При вызове Any() на нулевом объекте он генерирует исключение ArgumentNullException в С#. Если объект имеет значение null, определенно не "any", и он должен, вероятно, возвращать false.
Почему С# ведет себя так?
При вызове Any() на нулевом объекте он генерирует исключение ArgumentNullException в С#. Если объект имеет значение null, определенно не "any", и он должен, вероятно, возвращать false.
Почему С# ведет себя так?
При работе со ссылочными типами значение null
семантически отличается от "пустого" значения.
null
строка - это не то же string.Empty
, что и string.Empty
, а null
IEnumerable<T>
не совпадает с Enumerable.Empty<T>
(или любым другим "пустым" перечислением этого типа).
Если Any
не был методом расширения, вызов его на null
привел бы к NullReferenceException
. Поскольку это метод расширения, бросание некоторого исключения (хотя и не обязательно) является хорошей идеей, потому что оно сохраняет известную семантику попытки вызвать метод на null
: BOOM!
Any()
спрашивает: "В этом поле содержатся какие-либо элементы?"
Если поле пусто, ответ явно нет.
Но если в первую очередь нет ящика, тогда вопрос не имеет смысла, и функция жалуется: "О чем, черт возьми, вы говорите? Нет коробки".
С современным С# вы можете легко обработать сценарий OP с помощью простой проверки, например, такой:
List<string> foo = null;
if (foo?.Any() ?? false)
{
DoStuff();
}
Это похоже на неудачную реализацию AnyOrDefault(bool default)
, которую ОП ожидает от метода расширения Any()
.
Вы можете легко превратить это в расширение вроде этого:
public static bool HasItems<T>(this IEnumerable<T> source)
{
return (source?.Any() ?? false);
}
Честно говоря, мне не очень нравится имя Переименован в AnyOrDefault
для этого, так как не имеет смысла передавать значение по умолчанию (значение по умолчанию true, вероятно, было бы довольно плохо для людей, читающих код позже).HasItems
, как предлагается в комментариях. Это гораздо лучшее имя!
Any()
представляет собой метод расширения, так что this
на самом деле передается в качестве первого аргумента метода. В этой ситуации, понятным для него, чтобы передать ArgumentNullException
, this
null
.
Вы можете выполнить проверку заранее:
bool hasAny = yourData == null ? false : yourData.Any(yourPredicate);
Потому что Any() это метод расширения следующим образом:
public static bool Any(this IEnumerable enumerable)
{
if (enumerable == null)
throw ArgumentNullException("enumerable");
...
}
Any
метод работает против IEnumerable
и сообщает вам, есть ли какие-либо элементы в Enumerable. Если вы не дадите ему что-либо, чтобы перечислить, то аргумент ArgumentNullException является разумным: коллекция без элементов (совпадающих) не отличается от коллекции.
Как уже упоминалось, Any
проверяют, содержит или нет последовательность элементов. Это не мешает вам передавать null
значения (что может быть ошибкой в первую очередь).
Каждый метод расширения в классе Enumerable
генерирует ArgumentNullException
если source
имеет значение null
. Throwing ArgumentNullExceptions
в расширениях - это хорошая практика.
Any()
- это метод расширения, который выбрасывает ArgumentNullException
, если источник нулевой. Будете ли вы выполнять действие ни на что? В общем, лучше получить какой-то явный индикатор того, что происходит в коде, а не значение по умолчанию.
Но это не значит, что так не может быть. Если вы знаете, что делаете, напишите свою собственную реализацию.
Я просто хотел поделиться с вами некоторыми практическими советами, которым следит моя компания.
Мы пишем наши пользовательские пакеты, предоставляемые частным NuGet, которые широко используются в наших продуктах. Проверка, является ли список пустым/пустым, очень часто, поэтому мы решили написать нашу реализацию Any
, которая делает наш код короче и проще.