Почему Any() не работает на объекте aС# null
При вызове Any() на нулевом объекте он генерирует исключение ArgumentNullException в С#. Если объект имеет значение null, определенно не "any", и он должен, вероятно, возвращать false.
Почему С# ведет себя так?
Ответы
Ответ 1
При работе со ссылочными типами значение null
семантически отличается от "пустого" значения.
null
строка - это не то же string.Empty
, что и string.Empty
, а null
IEnumerable<T>
не совпадает с Enumerable.Empty<T>
(или любым другим "пустым" перечислением этого типа).
Если Any
не был методом расширения, вызов его на null
привел бы к NullReferenceException
. Поскольку это метод расширения, бросание некоторого исключения (хотя и не обязательно) является хорошей идеей, потому что оно сохраняет известную семантику попытки вызвать метод на null
: BOOM!
Ответ 2
Any()
спрашивает: "В этом поле содержатся какие-либо элементы?"
Если поле пусто, ответ явно нет.
Но если в первую очередь нет ящика, тогда вопрос не имеет смысла, и функция жалуется: "О чем, черт возьми, вы говорите? Нет коробки".
Ответ 3
С современным С# вы можете легко обработать сценарий 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
, как предлагается в комментариях. Это гораздо лучшее имя!
Ответ 4
Any()
представляет собой метод расширения, так что this
на самом деле передается в качестве первого аргумента метода. В этой ситуации, понятным для него, чтобы передать ArgumentNullException
, this
null
.
Вы можете выполнить проверку заранее:
bool hasAny = yourData == null ? false : yourData.Any(yourPredicate);
Ответ 5
Потому что Any() это метод расширения следующим образом:
public static bool Any(this IEnumerable enumerable)
{
if (enumerable == null)
throw ArgumentNullException("enumerable");
...
}
Ответ 6
Any
метод работает против IEnumerable
и сообщает вам, есть ли какие-либо элементы в Enumerable. Если вы не дадите ему что-либо, чтобы перечислить, то аргумент ArgumentNullException является разумным: коллекция без элементов (совпадающих) не отличается от коллекции.
Ответ 7
Как уже упоминалось, Any
проверяют, содержит или нет последовательность элементов. Это не мешает вам передавать null
значения (что может быть ошибкой в первую очередь).
Каждый метод расширения в классе Enumerable
генерирует ArgumentNullException
если source
имеет значение null
. Throwing ArgumentNullExceptions
в расширениях - это хорошая практика.
Ответ 8
Any()
- это метод расширения, который выбрасывает ArgumentNullException
, если источник нулевой. Будете ли вы выполнять действие ни на что? В общем, лучше получить какой-то явный индикатор того, что происходит в коде, а не значение по умолчанию.
Но это не значит, что так не может быть. Если вы знаете, что делаете, напишите свою собственную реализацию.
Я просто хотел поделиться с вами некоторыми практическими советами, которым следит моя компания.
Мы пишем наши пользовательские пакеты, предоставляемые частным NuGet, которые широко используются в наших продуктах. Проверка, является ли список пустым/пустым, очень часто, поэтому мы решили написать нашу реализацию Any
, которая делает наш код короче и проще.