Почему? does.NET foreach loop throw NullRefException когда коллекция равна null?
поэтому я часто сталкиваюсь с этой ситуацией... где Do.Something(...) возвращает пустую коллекцию, например, так:
int[] returnArray = Do.Something(...);
затем я пытаюсь использовать эту коллекцию следующим образом:
foreach (int i in returnArray)
{
// do some more stuff
}
мне просто любопытно, почему цикл foreach не может работать с нулевой коллекцией? Мне кажется логичным, что 0 итераций будут выполняться с нулевой коллекцией... вместо этого он бросает!--3-->. Кто-нибудь знает почему это может быть?
это раздражает, так как я работаю с API, которые не являются ясно, что именно они возвращаются, так что я в конечном итоге с if (someCollection != null) везде...
Edit: спасибо всем за пояснив, что foreach использует GetEnumerator и если нет перечислителя, чтобы получить, foreach не удастся. Я думаю, я спрашиваю, почему язык / среда выполнения не может или не будет выполнять проверку null перед захватом перечислителя. Мне кажется, что поведение все равно будет хорошо определено.
11 ответов:
ну, короткий ответ: "потому что так проектировали его разработчики компиляторов."Реально, однако, ваш объект коллекции равен нулю,поэтому компилятор не может заставить перечислитель перебирать коллекцию.
Если вам действительно нужно сделать что-то вроде этого, попробуйте оператор null coalescing:
int[] array = null; foreach (int i in array ?? Enumerable.Empty<int>()) { System.Console.WriteLine(string.Format("{0}", i)); }
A
foreachпетли называетGetEnumeratorметод.
Если коллекцияnull, этот вызов метода приводит кNullReferenceException.Это плохая практика, чтобы вернуть
nullколлекция; ваши методы должны возвращать пустую коллекцию.
существует большая разница между пустой коллекцией и нулевой ссылкой на коллекцию.
при использовании
foreach, внутренне, это вызов IEnumerable в GetEnumerator() метод. Если ссылка имеет значение null, это вызовет это исключение.однако, вполне допустимо иметь пустой
IEnumerableилиIEnumerable<T>. В этом случае foreach не будет" перебирать " что-либо (так как коллекция пуста), но и не будет бросать, так как это совершенно правильный сценарий.
Edit:
лично, если вам нужно обойти это, я бы рекомендовал метод расширения:
public static IEnumerable<T> AsNotNull<T>(this IEnumerable<T> original) { return original ?? Enumerable.Empty<T>(); }вы можете просто позвонить:
foreach (int i in returnArray.AsNotNull()) { // do some more stuff }
другой метод расширения, чтобы обойти это:
public static void ForEach<T>(this IEnumerable<T> items, Action<T> action) { if(items == null) return; foreach (var item in items) action(item); }потреблять несколькими способами:
(1) с помощью метода, который принимает
T:returnArray.ForEach(Console.WriteLine);(2) с выражением:
returnArray.ForEach(i => UpdateStatus(string.Format("{0}% complete", i)));(3) с многострочным анонимным методом
int toCompare = 10; returnArray.ForEach(i => { var thisInt = i; var next = i++; if(next > 10) Console.WriteLine("Match: {0}", i); });
просто написать метод расширения, чтобы помочь вам:
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); } } }
потому что нулевая коллекция-это не то же самое, что пустая коллекция. Пустая коллекция-это объект коллекции без элементов; нулевая коллекция-это несуществующий объект.
вот что нужно попробовать: объявить две коллекции любого рода. Инициализируйте один обычно так, чтобы он был пуст, и назначьте другому значение
null. Затем попробуйте добавить объект в обе коллекции и посмотреть, что произойдет.
это ответ давно, но я попытался сделать это следующим образом, чтобы просто избежать исключения нулевого указателя и может быть полезно для кого-то, кто использует оператор проверки c# null ?.
//fragments is a list which can be null fragments?.ForEach((obj) => { //do something with obj });
Это вина
Do.Something(). Лучшей практикой здесь было бы вернуть массив размером 0 (что возможно) вместо null.
потому что за кулисами
foreachполучает перечислитель, эквивалентный этому:using (IEnumerator<int> enumerator = returnArray.getEnumerator()) { while (enumerator.MoveNext()) { int i = enumerator.Current; // do some more stuff } }
Я думаю, что объяснение того, почему исключение очень понятно, с ответами здесь. Я просто хочу дополнить то, как я обычно работаю с этими коллекциями. Потому что, несколько раз, я использую коллекцию более одного раза и должен проверить, если null каждый раз. Чтобы избежать этого, я делаю следующее:
var returnArray = DoSomething() ?? Enumerable.Empty<int>(); foreach (int i in returnArray) { // do some more stuff }таким образом, мы можем использовать коллекцию столько, сколько мы хотим, не опасаясь исключения, и мы не загрязняем код чрезмерным условным заявления.
С помощью оператора проверки null
?.это также отличный подход. Но, в случае массивов (как пример в вопросе), он должен быть преобразован в список перед:int[] returnArray = DoSomething(); returnArray?.ToList().ForEach((i) => { // do some more stuff });
SPListItem item; DataRow dr = datatable.NewRow(); dr["ID"] = (!Object.Equals(item["ID"], null)) ? item["ID"].ToString() : string.Empty;
Comments