Почему 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, которая делает наш код короче и проще.