Почему в C # часто встречается «null! = Variable» вместо «variable! = Null»?

103

В C # есть ли разница в скорости выполнения для порядка, в котором вы указываете условие?

if (null != variable) ...
if (variable != null) ...

С недавнего времени я довольно часто видел первый, и он привлек мое внимание, так как ко второму я привык.

Если нет разницы, в чем преимущество первого?

mr_georg
источник
Некоторые языки программирования поддерживают инициализацию значения в условии if. В таких языках, если мы хотим сравнить LValue с RValue, рекомендуется использовать литерал слева, например if (1 == i). поэтому случайно, если мы положим = скорее ==, это даст ошибку компилятора.
скуловой Panchal

Ответы:

161

Это задержка от C. В C, если вы либо используете плохой компилятор, либо предупреждения не выставлены достаточно высоко, это будет компилироваться без каких-либо предупреждений (и действительно является допустимым кодом):

// Probably wrong
if (x = 5)

когда вы на самом деле, вероятно, имели в виду

if (x == 5)

Вы можете обойти это в C, выполнив:

if (5 == x)

Опечатка здесь приведет к неверному коду.

Теперь, в C # это все чепуха. Если вы не сравниваете два логических значения (что бывает редко, IME), вы можете написать более читаемый код, поскольку для оператора «if» требуется логическое выражение для начала, а тип « x=5» - Int32нет Boolean.

Я предлагаю, если вы заметите это в коде своих коллег, обучите их современным языкам и предложите им писать в более естественной форме в будущем.

Джон Скит
источник
6
Все мои коллеги сводят меня с ума от этого и хотят использовать фигурные скобки даже для одного оператора после if ... (на случай, если вы добавите что-то позже, «не осознавая»). Включите F # и Boo (и YAML), если ваш язык нечитаем без отступов, сделайте его частью языка, я говорю.
Benjol
48
Добавление фигурных скобок является разумным, IMO - C #, конечно, не делает никаких попыток предотвратить его появление проблемы. Это сильно отличается от проблемы «константа == переменная».
Джон Скит,
6
а если (это! = то) {performAsSuch (); } на самом деле довольно здорово. Аргумент «позднего добавления» кажется мне столь же хрупким, как
отказ
3
@jpinto: Я не совсем понимаю, что вы здесь хотите сказать - что вы делаете , как скобки вокруг отдельных операторов, или что у вас нет? Разница между этим и переменным бизнесом в вопросе заключается в том, что в C # код с опечаткой является недопустимым, поэтому компилятор остановит его. То же самое нельзя сказать о том, чтобы не ставить брекеты.
Джон Скит,
3
@Benjol Не забывайте всегда указывать пустое else { }предложение на случай, если кому-то понадобится что-то добавить туда позже, и он забудет написать elseключевое слово самостоятельно. (Шутка)
Йеппе Стиг Нильсен
11

Есть веская причина использовать сначала null: if(null == myDuck)

Если вы class Duckпереопределяете ==оператор, то if(myDuck == null)можете войти в бесконечный цикл.

Использование nullfirst использует компаратор равенства по умолчанию и фактически делает то, что вы намеревались.

(Я слышал, что со временем ты привыкаешь читать код, написанный таким образом - я просто еще не испытал этого преобразования).

Вот пример:

public class myDuck
{
    public int quacks;
    static override bool operator ==(myDuck a, myDuck b)
    {
        // these will overflow the stack - because the a==null reenters this function from the top again
        if (a == null && b == null)
            return true;
        if (a == null || b == null)
            return false;

        // these wont loop
        if (null == a && null == b)
            return true;
        if (null == a || null == b)
            return false;
        return a.quacks == b.quacks; // this goes to the integer comparison
    }
}
DanW
источник
11
Это не верно. Проголосовали против. Просто наведите указатель мыши на ==оператора и оставьте его там, и вы увидите, что определяемая пользователем перегрузка используется в обоих случаях. Конечно, это может иметь небольшое значение, если вы проверите aперед этим, bа затем поменяете позицию aс правого операнда на левый. Но вы также можете проверить bраньше a, и тогда b == nullбудет тот, который изменит порядок. Все это сбивает с толку, когда оператор вызывает сам себя. Вместо этого приведите один из операндов (или оба) к, objectчтобы получить желаемую перегрузку. Вроде if ((object)a == null)или if (a == (object)null).
Йеппе Стиг Нильсен
10

Как все уже отмечали, это более или менее связано с языком C, где вы можете получить ложный код, если случайно забудете второй знак равенства. Но есть еще одна причина, которая также соответствует C #: читаемость.

Возьмем этот простой пример:

if(someVariableThatShouldBeChecked != null
   && anotherOne != null
   && justAnotherCheckThatIsNeededForTestingNullity != null
   && allTheseChecksAreReallyBoring != null
   && thereSeemsToBeADesignFlawIfSoManyChecksAreNeeded != null)
{
    // ToDo: Everything is checked, do something...
}

Если вы просто переставляете все нулевые слова в начало, вы можете намного легче обнаружить все проверки:

if(null != someVariableThatShouldBeChecked
   && null != anotherOne
   && null != justAnotherCheckThatIsNeededForTestingNullity
   && null != allTheseChecksAreReallyBoring
   && null != thereSeemsToBeADesignFlawIfSoManyChecksAreNeeded)
{
    // ToDo: Everything is checked, do something...
}

Так что этот пример может быть плохим (см. Рекомендации по кодированию), но просто подумайте о том, как быстро пролистать весь файл кода. Просто увидев образец

if(null ...

вы сразу знаете, что будет дальше.

Если бы все было наоборот, вам всегда нужно сканировать до конца строки, чтобы увидеть проверку на недействительность, просто позволяя вам на секунду задержаться, чтобы узнать, какой тип проверки там делается. Так что, возможно, подсветка синтаксиса может вам помочь, но вы всегда будете медленнее, когда эти ключевые слова находятся в конце строки, а не в начале.

Оливер
источник
9

Думаю, это программист на C, который переключил языки.

На C вы можете написать следующее:

int i = 0;
if (i = 1)
{
    ...
}

Обратите внимание на использование единственного знака равенства, что означает, что код присвоит 1 переменной i, затем вернет 1 (присвоение является выражением) и использует 1 в операторе if, который будет обработан как истина. Другими словами, это ошибка.

Однако в C # это невозможно. Между ними действительно нет разницы.

Лассе В. Карлсен
источник
1
«Однако в C # это невозможно» без жалоб компилятора, поскольку для оператора if требуется логическое выражение, а предоставленное имеет тип int.
Роберт Харви,
4

Раньше люди забывали знак "!" (или лишнее '=' для равенства, которое труднее обнаружить) и выполнять присваивание вместо сравнения. размещение нуля впереди исключает возможность ошибки, поскольку null не является l-значением (IE не может быть присвоено).

Большинство современных компиляторов выдают предупреждение, когда вы выполняете присваивание в условном выражении, а C # фактически выдает ошибку. Большинство людей просто придерживаются схемы var == null, так как некоторым людям ее легче читать.

Рик
источник
Все компиляторы C # (обратите внимание, что это вопрос C #) не должны компилировать оператор «if», хотя выражение не является логическим ...
Джон Скит,
4

Я не вижу никаких преимуществ в том, чтобы следовать этому соглашению. В C, где логические типы не существуют, полезно написать

if( 5 == variable)

скорее, чем

if (variable == 5)

потому что если вы забудете один из знаков равенства, вы получите

if (variable = 5)

который присваивает переменной 5 и всегда возвращает значение true. Но в Java логическое значение является логическим. А с! = Вообще нет причин.

Тем не менее, один хороший совет - написать

if (CONSTANT.equals(myString))

скорее, чем

if (myString.equals(CONSTANT))

потому что это помогает избежать исключений NullPointerExceptions.

Я бы посоветовал попросить обоснования правила. Если его нет, зачем ему следовать? Не улучшает читаемость

Гоккула Судан Р
источник
0

Для меня всегда было то, какой стиль ты предпочитаешь

@Shy - Опять же, если вы путаете операторы, вы должны получить ошибку компиляции, или вы будете запускать код с ошибкой - ошибка, которая возвращается и укусит вас позже, поскольку она произвела неожиданное поведение

TheCodeJunkie
источник
Я не думаю , что я когда - либо слышал, чтобы кто предпочитая «постоянную == переменную» вид другой , чем по соображениям безопасности, которые уже имели дело с в C #.
Джон Скит
Я сам предпочитаю «переменная == константа», но я видел, как программисты применяют обратное, потому что они считают, что для них это более естественно.
TheCodeJunkie
1
Все ли они были людьми с годами «промывания мозгов» на языке Си, или это был искренний выбор?
Джон Скит
0

Как многие отметили, это в основном в старом коде C, он использовался для выявления ошибки компиляции, поскольку компилятор принял это как законное

Новый язык программирования, такой как java, go, достаточно умен, чтобы фиксировать такие ошибки компиляции.

Не следует использовать в коде условия типа "null! = Variable", так как это очень нечитабельно.

Ананд Шах
источник
-4

Еще одна вещь ... Если вы сравниваете переменную с константой (например, целым числом или строкой), размещение константы слева - хорошая практика, потому что вы никогда не столкнетесь с NullPointerExceptions:

int i;
if(i==1){        // Exception raised: i is not initialized. (C/C++)
  doThis();
}

в то время как

int i;
if(1==i){        // OK, but the condition is not met.
  doThis();
}

Теперь, поскольку по умолчанию C # инстанцирует все переменные, у вас не должно быть этой проблемы на этом языке.

Кони
источник
1
Я не уверен, какой компилятор вы используете для первого бита кода, но он должен компилироваться (обычно с предупреждением). Значение i не определено, но имеет НЕКОТОРЫЕ значения.
Dolphin,
Конечно, оба кода компилируются! В этом и проблема. Попробуйте запустить первый код, и вы поймете, что я имел в виду под «
Возникло
также нет разницы между ними. Вы умеете разбираться?
mfazekas
Нет исключения, если вы не объявите int * i в C / C ++, и даже в этом случае он будет сравнивать указатели и не генерировать никаких исключений ... Как и заявил Dolphin, значение undefined, но оно не будет генерировать никаких исключений.
Cobusve
без исключений, допустим, iэто случайное значение без надлежащей инициализации. выражение имеет полностью ту же семантику в c / c ++
Raffaello