var customers = new List<BECustomer>();
customers = GetCustomers();
But I don't understand why customers[0] == null even if
customers.Any()==true or
customers.Count == 1 or
customers==null is false
How do I check for null customers?
This is a difference.
You list is not null since you instantiated it with new List() and then assign getCustomers(). Maybe this is returning null.
But an element in the list can be null. For example:
customers[0] = null
Then you have set the first element in the array to null.
So to summarize:
if (customers == null)
Checks if the customerzs variable points to null
if (customers[0] == null)
Checks if the first element in the array is null
Try the following:
customers.Any(c => c == null)
You can check that with customers.Contains(null). See more here.
If you want to check if any item of a collection is null, you can use this extension method.
public static bool AnyNull<T>(this IEnumerable<T> items)
where T : class
{
return items.Any(item => item == null);
}
Usage:
var customers = GetCustomers();
bool anyCustomerNull = customers.AnyNull();
new List<BECustomer().Add(null) will have the same effect. Non-null, non-empty list whch contains null element./
Because there is one customer, that is set to null.
Related
I am comparing two excel sheets using C#.
I have assigned the sheets to datatables and I hope to compare the values in each cell. I have converted the datatables to lists and I'm iterating through them comparing them. How do I avoid a NullReferenceException if one of the values contains a null?
I am hitting a NullReferenceException at this point in the code:
if (lst1[i].ToString() != lst2[j].ToString())
Is there a way to guard against nulls here or do I need to handle null values earlier in the code?
Thanks
One good way to do this is to ask if the value is null before call .ToString().
So, would be like:
if (lst1[i] != null && lst2[j] != null && lst1[i].ToString() != lst2[j].ToString())
if (lst1[i]?.ToString() != lst2[j]?.ToString())
More info on the ?. operator
Just test for null before dereferencing lst1[i] and lst2[j]
if(lst1[i] is not null and lst2[j] is not null)
{
if (lst1[i].ToString() != lst2[j].ToString())
{
...
}
}
Assuming
if both items are null then they are equal;
if both items are not null and their string representation are equal then they are equal;
in any other case they are not equal;
you can make comparison of your lists more readable With following LINQ query.
var query = list1
.Zip(list2, (a, b) => new { a, b })
.Any(m => !((m.a == null && m.b == null) || (m.a != null && m.b != null && m.a.ToString() == m.b.ToString())));
if (query)
{
Console.WriteLine("Lists are not equal.");
}
else
{
Console.WriteLine("Lists are equal.");
}
Since you're doing ToString() it looks like both lists are typed as objects. Since they're coming from Excel they're presumably all either strings or simple value types.
In that case you can do
if(lst1.SequenceEquals(lst2))
That compares each item in both lists and ensure that they're equal. It accounts for nulls. You wouldn't need to compare each item one at a time. This compares the entire list. I'd write unit tests to make sure it doesn't throw any curves.
If for some reason you wanted to do a text-based comparison ("2" == 2) then you could create a comparer like this:
public class ObjectTextComparer : IEqualityComparer<object>
{
public bool Equals(object x, object y)
{
return x?.ToString() == y?.ToString();
}
public int GetHashCode(object obj)
{
return obj?.GetHashCode() ?? 0;
}
}
and then this would return true:
var x = new object[] { "x", 2, null, "y" };
var y = new object[] { "x", "2", null, "y" };
var comparer = new ObjectTextComparer();
var listsAreEqual = x.SequenceEqual(y, comparer);
That second option is a can of worms, though, if the values in one list are stored in Excel as text and the other ones as numbers or dates.
For example if one list contains a date and the other list contains the same date stored as text, the comparer is going to convert the first one to a string, but it might not be formatted the same way as the other one stored as text, and the two strings won't match.
I have a list of a class object, I need to filter that list with element which starts with these letters "GHB" and then set a listview control dataconext to it to display the elements
if(myList.ToList().FindIndex(x=> x.Name !=null)!=-1 )
{
listview1.DataContext = myList.ToList().where(x=> x.Name.StarstWith("GHB"))
}
But it gives me an error when an element is null
It gives you the error because your if condition is actually useless. You check whether Name in at least one element is not null, if so you try to access the variable. This of course will fail, because you need only 1 element with a valid name and the rest still can have null values which will lead to the NullReferenceException
What you can do is: check in the where clause additionaly whether Name is not null and if so only then check whether it StartsWith("GHB"):
listview1.DataContext = myList.Where(x => x?.Name != null && x.Name.StartsWith("GHB")).ToList();
this way you can save yourself the if condition.
I guess what you where trying to check is if Name in all elements is not null. In this case you can use the All method:
if (myList.All(x=>x.Name != null)
EDIT: using the ? will avoid that Name is checked if an element in the List is entirely null:
myList.Where(x => x?.Name != null && x.Name.StartsWith("GHB")).ToList();
Try this:
listview1.DataContext = myList
.Where(x => x != null
&& !string.IsNullOrEmpty(x.Name)
&& x.Name.StarstWith("GHB"))
.ToList();
...and remove the if statement.
I was working on some code when I discovered something I don't fully understand so I created the following test code:
static void Main(string[] args)
{
string myKey = "bla";
var list = new List<KeyValuePair<string, string>>();
var list2 = new List<Test>();
var bla1 = (from a in list where a.Key == myKey select a.Value).FirstOrDefault();
var bla2 = list.FirstOrDefault(x => x.Key == myKey).Value;
var bla3 = (from a in list2 where a.Key == myKey select a.Value).FirstOrDefault();
var bla4 = list2.FirstOrDefault(x => x.Key == myKey).Value;
}
struct Test
{
public string Key { get; set; }
public string Value { get; set; }
public Test(string key, string value)
{
Key = key;
Value = value;
}
}
Now as long as Test is a struct all of the 4 bla-lines work fine. But as soon as I change Test to be a class instead of a struct bla4 fails. How can this be? Isn't bla4 just another (lambda) way of doing bla3 and therefore should behave the same? Why does it make a difference whether the list contains a struct's or classes? And is there a way to have a lambda version of bla4 that doesn't throw an exception and instead returns null like the others?
The default of a class type is null. The default of a struct is an empty struct. That's why FirstOrDefault(..).Value will fail with a NullReferenceException.
You should check that FirstOrDefault() did return something before trying to access the result.
var item = list2.FirstOrDefault(x => x.Key == myKey);
var bla4 = (item == null)?null:item.Value;
In C# 6 and later you can use the ?. operator, to perform the same check:
var item = list2.FirstOrDefault(x => x.Key == myKey)?.Value;
Another option is to use ?? to replace null with a default value.
var item = list2.FirstOrDefault(x => x.Key == myKey) ?? new Test();
var bla4 = item.Value;
It's common to create a special Empty object to use in such cases, eg:
class Test
{
//...
public static readonly Empty =new Test();
}
var item = list2.FirstOrDefault(x => x.Key == myKey) ?? Test.Empty;
var bla4 = item.Value;
A struct can never be null.
Therefore your call to FirstOrDefault() will actually cause a new struct to be constructed, because default(struct) is effectively new(struct).
However if you change it to a class, then default(class) is null.
Thus the result of your call
list2.FirstOrDefault(x => x.Key == myKey)
will actually result in null being returned once it is a class. You should therefore recieve a NullReferenceException when trying to access .Value.
In layman terms what your bla3 acutally does is check if there is a matching item within the collection. If there is, it will select the property Value of that item, otherwise it will return the default of that property's type.
Your bla4 instead looks if it finds an item that matches the Key property. If it does, it selects the first of those items, if it does not, it creates a default of that item's type and returns that. You then try to acces that return value's Value property.
Isn't bla4 just another (lambda) way of doing bla3 and therefore should behave the same?
No. bla3 extracts some strings from the objects contained in a list. You then call FirstOrDefault on that set of strings and obtain a null value, because that set was empty.
bla4 calls FirstOrDefault directly on the set of objects and you then obtain the string property Value from the defaulted object.
When the objects contained in your lists are structs, then the defaulted value will contain a null Value property. That's why they superficially seem similar. But be very clear - the order in which Value is being accessed and FirstOrDefault is being called is different, and FirstOrDefault is operating on different types of things.
Of course, exactly the same can be said for bla2 vs bla1.
I'm getting some unexpected behavior in my process. I'm doing the following.
IEnumerable<Thing> things = ...;
IEnumerable<Thing> subset = things.Where(a => a.SomeFlag);
String info = "null: " + (subset == null);
The above works and info tells me that the object isn't null. So I wish to check the number of the elements in subset by this.
IEnumerable<Thing> things = ...;
IEnumerable<Thing> subset = things.Where(a => a.SomeFlag);
String info = "null: " + (subset == null);
String count = subset.Count();
Now I get an exception giving me the error message:
Object reference not set to an instance of an object.
What do I miss?!
It's possible that one of the Thing's in subset is null. You can try this:
IEnumerable<Thing> subset = things.Where(a => a != null && a.SomeFlag);
Note that due to the way Linq's lazy evaluation, you won't get any exception you call .Where because all it's doing at that point is setting up a condition for filtering the elements of things. Only later when you call .Count is it actually evaluating the results.
Update: With the new null-condition operator in C# 6 (also called the safe navigation or 'Elvis' operator), we can do the same thing a bit more succinctly:
IEnumerable<Thing> subset = things.Where(a => a?.SomeFlag);
An IEnumerable<Thing> implies deferred execution.
In your first fragment subset and things are never enumerated.
In the second fragment, it is the call to Count() that enumerates the lists and only then it comes to light that one of the a is null in a => a.SomeFlag.
You can see what really happens here with a bit simplified example.
Test class:
public class Test
{
public int Value { get; set; }
}
And LINQ query on IEnumerable<Test>:
IEnumerable<Test> source = new List<Test>() {
new Test { Value = 10 },
null,
new Test { Value = 20 }
};
IEnumerable<Test> filteredSource = source.Where(x => x.Value > 10);
// return false
Console.WriteLine(filteredSource == null);
// throws NullReferenceException
Console.WriteLine(filteredSource.Count());
Why does it happens? Because filteredSource == null does not cause collection enumeration, so Where predicate is not being fired on any source collection element.
However, when you call Count() on filteredSource the predicate is being called on every item from source collection, and when it comes to an item which is null: null.Value > 10 throws the exception.
How to make it work? Extend the predicate with x != null check:
IEnumerable<Test> filteredSource = source.Where(x => x != null && x.Value > 10);
Ok, so suppose you had the following items in things:
Thing A SomeFlag = true
Thing B SomeFlag = false
null
Thing C SomeFlag = true
First you count all the items in things. So you iterate over 4 objects, find 4 objects, and know that the result is 4. Easy.
Now you want to count all of the items in subset which means you need to figure out which items are in subset in the first place. So you go about counting them:
Thing A .... A.SomeFlag is true, so count it
Thing B .... B.SomeFlag is not true, so don't count it
null .... null.SomeFlag NULLREFERENCEEXCEPTION
And that's where your error comes from.
Note that even if all of the elements in things are not null, you can still get a NullReferenceException if the .SomeFlag get accessor has a side effect that could cause a NullReferenceException.
Is this code good enough:
if (MyCollection.Where(p => p.SomeID == idstring).Any())
{
selectedval = MyCollection.Where(p => p.SomeID == idstring).FirstOrDefault().MyField;
}
My doubt is about the fact that I make the same query twice: first for null check and then to actually get data.
Maybe there is a better way to do this type of things?
Yes.
var item = MyCollection.FirstOrDefault(p => p.SomeID == idstring);
if (item != null)
selectval = item.MyField;
This avoids double querying the collection, which will certainly make a difference in big collections or if your collection performs a DB query.
There is. You can use the FirstOrDefault method which takes a predicate and returns null if the item is not found.
var result = MyCollection.FirstOrDefault(p => p.SomeID == idstring);
if( result != null )
{
// item was found
}