Ух ты ... Думаю, я был постыдно неправ
Я не согласен. Я думаю, что вы все еще делаете хорошую мысль.
Компилятор знает, собирается ли сравнение перейти к пользовательскому оператору сравнения или нет, а компилятор знает, что если это не так, то this никогда не будет нулевым.
И на самом деле, компилятор отслеживает, может ли данное выражение юридически быть нулевым или нет, чтобы реализовать небольшую оптимизацию для вызовов не виртуальных методов. Если у вас есть не виртуальный метод M, и вы говорите foo.M();
, то компилятор генерирует это как «сделать виртуальный вызов M с получателем foo». Зачем? Потому что мы хотим выбросить, если foo равно нулю, а виртуальный вызов всегда выполняет проверку нуля на получателе. Не виртуальный вызов не делает; нам нужно сгенерировать его как «проверьте foo на ноль, а затем сделайте не виртуальный вызов M», что является более длинным, медленным и более раздражающим кодом.
Теперь, если мы сможем уйти без проверки нуля, мы это сделаем. Если вы говорите this.M()
или (new Foo()).M()
, то мы НЕ генерируем виртуальный вызов. Мы генерируем не виртуальный вызов без проверки нуля, потому что знаем, что он не может быть нулем.
Таким образом, компилятор имеет отличные данные о том, будет ли конкретное сравнение с нулем иногда, всегда или никогда не удастся.
Тогда возникает вопрос: «Если компилятор знает, что конкретное сравнение никогда не будет успешным, почему бы не сгенерировать для него предупреждение?»
И ответ «иногда мы делаем, а иногда нет».
Мы делаем в этой ситуации:
int x = 123;
if (x == null) ...
Существует оператор равенства, определенный для двух обнуляемых целых чисел. x преобразуется в обнуляемый int. NULL конвертируется в NULL. Таким образом, этот оператор равенства действителен, а потому используется и, конечно, всегда ложен. Компилятор выдает предупреждение, что выражение всегда ложно.
Однако из-за ошибки, которую мы случайно ввели в C # 3, этот код НЕ выдает это предупреждение:
Guid x = whatever;
if (x == null) ...
Та же сделка. Обнуляемый оператор равенства guid действителен, но предупреждение подавлено. Я не уверен, исправили ли мы эту ошибку для C # 4 или нет. Если нет, то, надеюсь, мы получим его в сервисный пакет.
Что касается "if (this == null)", я не знаю, почему мы не предупреждаем об этом. Это, конечно, кажется хорошим кандидатом на предупреждение. Наиболее вероятным объяснением является следование этому логическому силлогизму:
- предупреждения являются функциями компилятора
- Функции компилятора должны быть (1) продуманы, (2) разработаны, (3) реализованы, (4) протестированы, (5) документированы и (6) отправлены вам, прежде чем вы сможете воспользоваться этой функцией.
- Никто не сделал никаких из этих шести необходимых вещей для этой функции; мы не можем отправлять функции, о которых мы никогда не думали.
- поэтому такой функции нет.
Существует бесконечно много функций компилятора, о которых мы еще не думали; мы не реализовали ни один из них.
Еще одна причина, по которой мы не предупреждаем, состоит в том, что мы пытаемся выдавать предупреждения по коду, который может быть напечатан случайно и почти наверняка неверно по неочевидной причине . «this == null» вряд ли будет введен случайно, и, хотя вы почти наверняка ошибаетесь, как вы заметили в формулировке вашего вопроса, это очевидно неправильно.
Сравните это с нашим "guid == null" - это может произойти случайно, потому что разработчик может случайно подумать, что Guid является ссылочным типом. Руководства обычно передаются по ссылке в C ++, так что это простая ошибка. Код почти наверняка неправильный, но он не очевиден. Так что это хороший кандидат на предупреждение. (Вот почему так прискорбно, что это предупреждение в C # 2, а не в C # 3 из-за введенной нами ошибки.)