Pull to refresh

Comments 65

Забавно то, что если items == null, то items.Count() == 0 — не возникнет NullReferenceException, что выглядет как-то слегка неестественно :-)
Компилятор оптимизирует.
Первое сравнение выдает true
true || что-угодно = true
поэтому items.Count() == 0 не вызывается.
Я не писал, что не знаю почему, я говорил о том, как это выглядет в коде. Я знаю что такое extension methods.
Причем здесь extension methods? Если в бинарной операции `A || B` значение первого операнда A истино, значение B заведомо не будет вычисляться, это специфика оператора `||`, что нельзя сказать об операторе `|`, в котором будут вычислены значения обоих операндов вне зависимости от значения первого, то есть следующее выражение породит исключение в случае когда items = null:
if(items == null | !items.Any()) { /*… */ }
Попробуйте перечитать комментарии, может до вас дойдет. (где вы там увидели то, что я не понимаю как работает оператор || — для меня загадка).Не моя вина, что для вас совершенно нормально, что у null можно вызвать метод и не получить NullReferenceException (из-за «сахарного» синтаксиса расширяющих методов)
Попытка номер 4:
1) Utility.IsNullOrEmpty(null as List<int>) — нормально
2) (null as List<int>).IsNullOrEmpty() — выглядет ненормально
Хотя в IL обе эти строчки будут выглядеть _абсолютно_ одинаково.
Простите за безграмотность, расстроился из-за налетевших школьников.
Да я понял, что вы хотели изложить, но получился обратный эффект, потому как даже если бы в примерах поста стоял бы одинарный «или», NullReferenceException в .Count() произошел бы все равно (вы написали, что исключения не возникнет, отсюда и минусы), но уже внутри метода расширения Count по проверке входящего аргумента, а не непосредственно перед попыткой вызова метода объекта null.
>>NullReferenceException в .Count() произошел бы все равно
Там возник бы ArgumentNullException, но возможно вы и правы — слишком коротко выразил мысль.
Да действительно ошибся, суббота вечер, все дела )
Другими словами большинство программистов увидя такой код:
List items = null;
bool r = items.IsNullOrEmpty();

скажут, что тут возникнет исключение (если не знать, про extension method)
Вообще-то, было бы очень странно, если бы метод с названием IsNullOrEmpty() выкидывал исключение в такой ситуации.
как раз для обычных методов это совсем не странно
если в данном случае метод IsnullOrEmpty() на самом деле не относится к экземпляру IEnumerable, а является статическим методом-расширением, у которого просто вызов удобен для пользователя, то у обычного класса такое не сработает
например, метод IsNullOrEmpty у класса string вызывается не у экземпляра класса string, а как статический string.isNullOrEmpty(someString)
Не совсем понял Ваш комментарий. Т.е. это совсем не странно, что метод IsNull..() в случае null не возвращает true, а выкидывает исключение?
Вы хотите сказать, что написать такой метод для экземпляра невозможно? Естественно. Но при чем тут IsNullOrEmpty()?
при том, что IsNullOrEmpty для экземпляра тоже нельзя написать
именно поэтому строчка
bool r = items.IsNullOrEmpty();
выглядит нелогично для тех, кто не знает extension-методы
и для них же фраза «было бы очень странно, если бы метод с названием IsNullOrEmpty() выкидывал исключение в такой ситуации» некорректна

вот все, что я хотел сказать
Мое мнение заключается вот в чем: абсолютно логично, когда метод с названием IsNull...() возвращает true у null. И абсолютно нелогично, если метод с таким названием не может обработать null. Если он так делает, он не может так называться. И неважно, как этот метод реализован (очевидно, что как extension, но дело не в этом).

Вы рассматриваете программиста, который вообще не знает об extension-методах, но при этом догадывается, что у экземпляра не написать проверку на null и не поверит названию? Это очень странный .NET-разработчик, по-моему.
ок, переформулирую: для .NET-программиста это выглядит нормально, но для программирования в целом это выглядит нелогично, потому что экземпляр сам себя на null проверить не может
и теоретически, даже для .NET-программиста данная строчка будет первые несколько раз сильно привлекать к себе внимание
«Программирование в целом»? Лол. О чем Вы? Что это за «программирование»?
Думаю, дальше спорить бессмысленно.
С точки зрения «программирования в целом» вызывается процедура объекта. В данном случае, объект не существует, а значит и процедура существовать не может.
> программирования в целом
Что Вы имеете в виду? ООП?

> вызывается процедура объекта
Объекту посылается сообщение.

> В данном случае, объект не существует
В данном случае, объект — это null.
Именно об этом я и писал, но большинство почему то подумало, что я не знаю как работает оператор ||… Видемо пишут быстрее чему думают или для них совершенно нормально, что у null можно вызвать какой-то метод (причем тут только IsNullOrEmpty ?? в любом extension method'е автор может реализовать проверку на null, в результате которой (null as Class1).SomeMethod(); не будет падать. к примеру с помощь. extension method можно написать вот так:
(null as string).Replace(); — и этот код нормально выполнится.
Жаль, что тут оказалось так мало адекватных людей.
Ну согласитесь, что ваш первый комментарий как раз больше и похож на то, что вы как бы не знали про работу ||.
Всё это потому, что extension method был только вторичной темой топика, а вы его в своём сообщении забыли упомянуть, что и изменило смысл комментария.
Хотя последующий комментарий про items.IsNullOrEmpty() всё и поставил на свои места, обезьянкам, судя по всему, было всё равно что минусовать.
Ух ты. я только сейчас заметил, что там items.Count(); в 1ом комментарии. Хотел же ведь IsNullOrEmpty написать…
Неправильно. Поведение и правда, не совсем стандартное.

// Нормальное поведение
string x = null;
x.Trim(); // Тут у нас NullReferenceException, т.к. Trim - экземплярный метод

// Поведение описанного extension-метода
IEnumerable<object> y = null;
y.IsNullOrEmpty(); // никаких эксепшнов

// Как правильно
string.IsNullOrEmpty(x); // И никаких эксепшнов, и выглядит логично

Я с Вами полностью согласен. Я, пожалуй, не совсем правильно выразился.

Я говорю о том, что если уж есть метод «instance.IsNullOrEmpty()», то ждать от него NullReferenceException довольно странно. Представьте, что Вы вызываете number.IsZero(), а он возвращает ZeroArgumentException.

Писать такой метод неправильно (именно в данном контексте, хотя тут все делают вид, что это какой-то неписаный закон ООП) — но это другой вопрос.
Обычно, вызов любого не статического метода у null-reference выкидывает исключение.
Советую капитанам-комментаторам и минусующим перечитать внимательно комментарий. || тут не при чем, вопрос в том, что [null].Count() не выкидывает NullReferenceException, а возвращает ноль. Тут я согласен, неочевидное поведение.
Спасибо, Наконец-то хоть кто-то понял, что я хотел сказать :-)
Черт, я все перепутал. .Count() же выкинет исключение. Вы меня извините, в общем, но я беру свои слова назад. В этом вопросе я с Вами тоже не согласен :)
Сount — да, а я имел ввиду общий случай: (null as Class1).SomeMethod();
Любой SomeMethod() кроме IsNull..() должен выкидывать исключение, да. Это очевидно.
Кому должен? если автор реализует проверку на null — он не упадет.
капец, short-circuit evaluation, первый класс вторая четверть
// if(items.IsNullOrEmpty()) {
а вот есть items == null? не упадет ли NullPointerException? ;)
по сути — вызов метода-расширение — это вызов статического метода с первым параметром, тем объектом над которым он вызывается.
я у себя использую такое:
public void NullableToString(this object obj) {
return obj == null? string.Empty: obj.ToString();
}
Если вызвать у IQueryable метод Count(), то в случае с EF и Linq2Sql выполнится запрос «select count(*) ...». Никаких пробеганий по всем элементам там нет.

А вот для коллекций, не поддерживающих свойство Count Ваш подход, конечно, даёт существенный прирост производительности.
Способ не совсем мой, это — Phil Haack (перевод), но хотелось бы отметить, что при применении Any делается sql-запрос exists вместо count(*), что на больших БД и при сложных запросах тоже может оказаться очень полезно (СУБД внутри тоже проходит по элементам, чтобы их посчитать, и не всегда на это есть индексы).

С IQueryable вообще всегда приходится быть внимательным к мелочам.
MySQL с движком MyISAM удалённые записи не удаляет физически, а помечает, как удалённые. Подсчёт count(*) может быть очень долгим :)
Вы пишете запросы без индексов?
UFO just landed and posted this here
Просто совет — уберите пожалуйста информацию о подсветке кода. Если есть желание — оставьте в одном блоке.
Подобный метод не использую, но для больших массивов всегда вызываю Any, а не Count, один раз уже убедился, что это очень важно.
Расскажите пожалуйста, откуда у Вас берутся переменные IEnumerable равные null? За всю свою практику ни разу с таки не сталкивался.
Класс! Вы хороший программист!

Эти переменные ничем не отличаются ото всех остальных, которые могут быть null.
Да, любая переменная может быть null, но приведите пример когда вместо пустой коллекции Вы будете возвращать/передавать значение null. Я не вижу в этом никакого смысла.
Тут вопрос в перестраховке. Есть люди которые все входящие параметры проверяют. А есть те кторые нет. При правильном подходе оба варианта работают.
Давайте еще параметры типа int на null проверять, на всякий случай. :) В действительности, ни один из методов фреймворка, возвращающий коллекцию, не вазвращает null.
int это валью тип, он не может быть нулл. И не может проверятся на нулл.
Я знаю :) Это была шутка, там смайлик стоял…

Проверяться может, компилятор позволяет, попробуйте.
Первое что пришло в голову это ASP.NET MVC фреймворк, там если Акшен принимает IEnumerable, Array или ченить подобное. То будет null. Ну и сам фреймворк всегда проверяет на null.

[ComVisible(false)]
public static string Concat(IEnumerable values)
{
if (values == null)
{
throw new ArgumentNullException(«values»);
}
Это не противоречит тому что «ни один из методов фреймворка, возвращающий коллекцию, не вазвращает null.»

А вообще всегда можно найти исключения из правил. :)
Любой ValueType не может быть null.

Что касается вашего вопроса — вы видимо никогда не писали чего-то рассчитанного на публичное использование, когда вы не можете делать предположения о корректности входных параметров.
Любой ValueType не может быть null.


Я знаю :) Это была шутка, там смайлик стоял…

Что касается вашего вопроса — вы видимо никогда не писали чего-то рассчитанного на публичное использование, когда вы не можете делать предположения о корректности входных параметров.


И такое писали, только это частный случай.
глотать ошибки — не самая верная стратегия
Ну а кто, собственно, говорит, что это ошибки? null передать в некоторых случаях намного проще, чем какой-либо объект. Особенно когда этот объект не нужен.

Foo(new Foo(), new Bar());
Foo(null, null);
ещё проще — не передавать ничего
FooBar(new Foo(), new Bar());
Foo(new Foo());
а полиморфные методы — зло
Айенде расписал почему такой метод проверки не подходит для любых коллекций.
ayende.com/Blog/archive/2010/06/10/checking-for-empty-enumerations.aspx
Проблема решения предложенного Филом Хааком в том, что предпологается что всякая коллекция может быть итерирована дважды.
Там надо коментарии читать. В них самое интересное.
Точно.

Я его пост читал, когда там было 0 комментов.
Что интересно пост Айенде появился минут через 30 после поста Хаака.

Спасибо.
chaliy верно заметил… там в комментариях верную штуку пишут, про то что Any() в любом случае создает новый Enumerator. И проблемы с потерей первого элемента коллекции — нет.

Видимо Айенде написал предположение, не потестировав сначала код. :)
В случае IQueryable и LINQ2SQL/EF перечисления не будет, а в СУБД уйдет запрос вида «SELECT COUNT(*) ..» и перечисления не будет. Могу предположить, что ANY здесь может работать менее эффективно.
UFO just landed and posted this here
Sign up to leave a comment.

Articles