tags:

views:

377

answers:

8

So I frequently run into this situation... where Do.Something(...) returns a null collection, like so:

int[] returnArray = Do.Something(...);

Then, I try to use this collection like so:

foreach (int i in returnArray)
{
    // do some more stuff
}

I'm just curious, why can't a foreach loop operate on a null collection? It seems logical to me that 0 iterations would get executed with a null collection... instead it throws a NullReferenceException. Anyone know why this could be?

This is annoying as I'm working with APIs that aren't clear on exactly what they return, so I end up with if (someCollection != null) everywhere...

Edit: Thank you all for explaining that foreach uses GetEnumerator and if there is no enumerator to get, the foreach would fail. I guess I'm asking why the language/runtime can't or won't do a null check before grabbing the enumerator. It seems to me that the behavior would still be well defined.

+28  A: 

A foreach loop calls the GetEnumerator method.
If the collection is null, this method call results in a NullReferenceException.

It is bad practice to return a null collection; your methods should return an empty collection instead.

SLaks
I agree, empty collections should always be returned... however i didn't write these methods :)
Polaris878
@Polaris, null coalescing operator to the rescue! `int[] returnArray = Do.Something() ?? new int[] {};`
JSBangs
lol I love those double question marks
Polaris878
Or: `... ?? new int[0]`.
Ken
+1  A: 

Because behind the scenes the foreach acquires an enumerator, equivalent to this:

using (IEnumerator<int> enumerator = returnArray.getEnumerator()) {
    while (enumerator.MoveNext()) {
        int i = enumerator.Current;
        // do some more stuff
    }
}
Lucero
+5  A: 

There is a big difference between an empty collection and a null reference to a collection.

When you use foreach, internally, this is calling the IEnumerable's GetEnumerator() method. When the reference is null, this will raise this exception.

However, it is perfectly valid to have an empty IEnumerable or IEnumerable<T>. In this case, foreach will not "iterate" over anything (since the collection is empty), but it will also not throw, since this is a perfectly valid scenario.


Edit:

Personally, if you need to work around this, I'd recommend an extension method:

public static IEnumerable<T> AsNotNull<T>(this IEnumerable<T> original)
{
     return original ?? new T[0];
}

You can then just call:

foreach (int i in returnArray.AsNotNull())
{
    // do some more stuff
}
Reed Copsey
Yes, but WHY doesn't foreach do a null check before getting the enumerator?
Polaris878
@Polaris878: Because it was never intended to be used with a null collection. This is, IMO, a good thing - since a null reference and an empty collection should be treated separately. If you want to work around this, there are ways.. .I'll edit to show one other option...
Reed Copsey
@Polaris878: I would suggest rewording your question: "Why SHOULD the runtime do a null check before getting the enumerator?"
Reed Copsey
I guess I'm asking "why not?" lol it seems like the behavior would still be well defined
Polaris878
@Polaris878: I guess, the way I think of it, returning null for a collection is an error. The way it is now, the runtime gives you a meaningful exception in this case, but it's easy to work around (ie: above) if you don't like this behavior. If the compiler hid this from you, you'd lose the error checking at runtime, but there'd be no way to "turn it off"...
Reed Copsey
+1  A: 

Just write an extension method to help you out:

public static class Extensions
{
   public static void ForEachWithNull<T>(this IEnumerable<T> source, Action<T> action)
   {
      if(source == null)
      {
         return;
      }

      foreach(var item in source)
      {
         action(item);
      }
   }
}
BFree
+1  A: 

Because a null collection is not the same thing as an empty collection. An empty collection is a collection object with no elements; a null collection is a nonexistent object.

Here's something to try: Declare two collections of any sort. Initialize one normally so that it's empty, and assign the other the value null. Then try adding an object to both collections and see what happens.

JAB
+1  A: 

It is the fault of Do.Something(). The best practice here would be to return an array of size 0 (that is possible) instead of a null.

Henk Holterman
+10  A: 

Well, the short answer is "because that's the way the compiler designers designed it." Realistically, though, your collection object is null, so there's no way for the compiler to get the enumerator to loop through the collection.

If you really need to do something like this, try the null coalescing operator:

    int[] array = null;

    foreach (int i in array ?? new int[0])
    {
        System.Console.WriteLine(string.Format("{0}", i));
    }
Robaticus
+1  A: 

Another extension method to work around this:

public static void ForEach<T>(this IEnumerable<T> items, Action<T> action)
{
    if(items == null) return;
    foreach (var item in items) action(item);
}

Consume in several ways:

(1) with a method that accepts T:

returnArray.ForEach(Console.WriteLine);

(2) with an expression:

returnArray.ForEach(i => UpdateStatus(string.Format("{0}% complete", i)));

(3) with a multiline anonymous method

int toCompare = 10;
returnArray.ForEach(i =>
{
    var thisInt = i;
    var next = i++;
    if(next > 10) Console.WriteLine("Match: {0}", i);
};
Jay