ChatGPT解决这个技术问题 Extra ChatGPT

Why does .NET foreach loop throw NullRefException when collection is null?

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.

Something feels wrong about calling an array a collection. But maybe I'm just old school.
Yes, I agree... I'm not even sure why so many methods in this code base return arrays x_x
I suppose by the same reasoning it would be well-defined for all statements in C# to become no-ops when given a null value. Are you suggesting this for just foreach loops or other statements as well?
@ Ken... I'm thinking just foreach loops, because to me it seems apparent to the programmer that nothing would happen if the collection is empty or non-existent

t
theduck

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 ?? Enumerable.Empty<int>())
{
   System.Console.WriteLine(string.Format("{0}", i));
}

Please excuse my ignorance, but is this efficient? Does it not result in a comparison on each iteration?
I don't believe so. Looking at the generated IL, the loop is after the is null comparison.
Holy necro... Sometimes you have to look at the IL to see what the compiler is doing to figure out if there are any efficiency hits. User919426 had asked whether it did the check for each iteration. Though the answer might be obvious to some people, it is not obvious to everyone, and providing the hint that looking at the IL will tell you what the compiler is doing, helps people fish for themselves in the future.
@Robaticus (even why later) the IL looks that why because the specification says so. The expansion of the syntactic sugar (aka foreach) is to evaluate the expression on the right side of "in" and call GetEnumerator on the result
@RuneFS - exactly. Understanding the specification or looking at the IL is a way to figure out the "why." Or to evaluate whether two different C# approaches boil down to the same IL. That was, essentially, my point to Shimmy above.
S
SLaks

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.


I agree, empty collections should always be returned... however i didn't write these methods :)
@Polaris, null coalescing operator to the rescue! int[] returnArray = Do.Something() ?? new int[] {};
+1 Like the tip of returning empty collections instead of null. Thanks.
I disagree about a bad practice: see ⇒ if a function failed it could either return an empty collection — it is a call to constructor, memory allocation, and perhaps a bunch of a code to be executed. Either you could just return «null» → obviously there's only a code to return and a very short code to check is the argument is «null». It's just a performance.
I'd also add: ofc the coalescing operator creates an empty list either way. But that's already decision of a user: if one calls a function somewhere e.g. inside of a GUI, where a performance isn't significant, they may decide to do so. But if they're doing something that requires a performance, they just insert a check «if a result isn't null, do foreach». Though for high performance usually used C++ :Ь
h
huysentruitw

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 ?? Enumerable.Empty<T>();
}

You can then just call:

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

Yes, but WHY doesn't foreach do a null check before getting the enumerator?
@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...
@Polaris878: I would suggest rewording your question: "Why SHOULD the runtime do a null check before getting the enumerator?"
@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"...
Great solution, implemented here as an extension.
D
Devesh

It is being answer long back but i have tried to do this in the following way to just avoid null pointer exception and may be useful for someone using C# null check operator ?.

     //fragments is a list which can be null
     fragments?.ForEach((obj) =>
        {
            //do something with obj
        });

@kjbartel beat you to this by over a year (at " stackoverflow.com/a/32134295/401246 "). ;) This is the best solution, because it doesn't: a) involve performance degradation of (even when not null) generalizing the whole loop to the LCD of Enumerable (as using ?? would), b) require adding an Extension Method to every Project, and c) require avoiding null IEnumerables (Pffft! Puh-LEAZE! SMH.) to begin with.
J
Jay

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);
});

Just missing a closing parenthesis in the 3rd example. Otherwise, beautiful code that can be extended further in interesting ways (for loops, reversing, leaping, etc). Thanks for sharing.
Thanks for such a wonderful code, But i didn't understand the first methods,why you pass console.writeline as parameter,though its printing the array elements.but didnt understand
@AjaySingh Console.WriteLine is just an example of a method that takes one argument (an Action<T>). The items 1, 2 and 3 are showing examples of passing functions to the .ForEach extension method.
@kjbartel's answer (at " stackoverflow.com/a/32134295/401246 " is the best solution, because it doesn't: a) involve performance degradation of (even when not null) generalizing the whole loop to the LCD of Enumerable (as using ?? would), b) require adding an Extension Method to every Project, or c) require avoiding null IEnumerables (Pffft! Puh-LEAZE! SMH.) to begin with (cuz, null means N/A, whereas empty list means, it's appl. but is currently, well, empty!, i.e. an Empl. could have Commissions that's N/A for non-Sales or empty for Sales).
@Tom - but that answer applies only to List<T>, it's the only collection that has .ForEach<T>(), which is dumb, IMO. So you can't use that answer after string.Split() for example, without another perf trade-off of converting the array of strings returned to a list just to use .ForEach(). All these trade-offs are just dumb, they should fix it, or never return null from built-in methods (I don't think it's viable to always return List<T>.
J
JAB

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.


B
BFree

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);
      }
   }
}

H
Henk Holterman

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.


L
Lucero

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
    }
}

so? Why can't it simply check if it is null first and skip the loop? AKA, exactly what is shown in the extension methods? The question is, is it better to default to skip the loop if null or to throw an exception? I think it's better to skip! Seems likely that null containers are meant to be skipped rather than looped over since loops are meant to do something IF the container is non-null.
@AbstractDissonance You could argue the same with all null references, e.g. when accessing members. Typically this is an error, and if it isn't then it's simple enough to handle this for instance with the extension method which another user has provided as answer.
I don't think so. The foreach is meant to operate over the collection and is different than referencing a null object directly. While one could argue the same, I bet if you analyzed all the code in the world, you would have most foreach loops have null checks of some kind in front of them only to bypass the loop when the collection is "null"(which is hence treated the same as empty). I don't think anyone considers looping over a null collection as something they want to and would rather simply ignore the loop if the collection is null. Maybe, rather, a foreach?(var x in C) could be used.
The point I'm mainly trying to make is that it creates a bit of litter in the code since one has to check every time for no good reason. The extensions, of course, work but a language feature could be added to avoid these things without much issue. (mainly I think the current method produces hidden bugs since the programmer may forget to put the check and hence an exception... because either he expects the check to occur somewhere else before the loop or is thinking that it was pre-initialized(which it may or may have changed). But in either cause, the behavior would be the same as if empty.
@AbstractDissonance Well, with some proper static analysis you know where you could have nulls and where not. If you get a null where you don't expect one it's better to fail instead of silently ignoring problems IMHO (in the spirit of failing fast). Therefore I feel that this is the correct behavior.
L
Leniel Maccaferri

I think the explanation of why exception is thrown is very clear with the answers provided here. I just wish to complement with the way I usually work with these collections. Because, some times, I use the collection more then once and have to test if null every time. To avoid that, I do the following:

    var returnArray = DoSomething() ?? Enumerable.Empty<int>();

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

This way we can use the collection as much as we want without fear the exception and we don't polute the code with excessive conditional statements.

Using the null check operator ?. is also a great approach. But, in case of arrays (like the example in the question), it should be transformed into List before:

    int[] returnArray = DoSomething();

    returnArray?.ToList().ForEach((i) =>
    {
        // do some more stuff
    });

Converting to a list just to have access to the ForEach method is one of the things I hate in a codebase.
I agree... I avoid that as much as possible. :(
T
Tunaki
SPListItem item;
DataRow dr = datatable.NewRow();

dr["ID"] = (!Object.Equals(item["ID"], null)) ? item["ID"].ToString() : string.Empty;