В чем разница между «x = ноль» и «x == ноль»?

277

В C # 7 мы можем использовать

if (x is null) return;

вместо того

if (x == null) return;

Есть ли преимущества использования нового способа (прежний пример) по сравнению со старым?

Семантика отличается?

Это просто вопрос вкуса? Если нет, когда я должен использовать один поверх другого?

Ссылка: Что нового в C # 7.0 .

Maniero
источник
4
это ссылка, на которую я только что посмотрел, однако она не дает вам много информации, поэтому я полагаю, что ОП задает вопрос. Наиболее важной частью страницы является этот тест Оператор. Оператор «is» используется для проверки совместимости типа времени выполнения объекта с данным типом или нет. Другими словами, мы используем оператор «is» для проверки того, что тип объекта соответствует ожидаемому. Давайте посмотрим на его синтаксис:
Саймон Прайс
2
@SimonPrice Это о текущей версии C #: C # 6. Этот вопрос о C # 7, который имеет сопоставление с шаблоном .
Патрик Хофман
@bigown какие детали ты ищешь?
Патрик Хофман
@PatrickHofman, вид свика, ответил на пример
Maniero

Ответы:

233

Обновление: компилятор Roslyn был обновлен, чтобы сделать поведение двух операторов одинаковым, когда нет перегруженного оператора равенства . Пожалуйста, смотрите код в результатах текущего компилятора ( M1и M2в коде), который показывает, что происходит, когда нет перегруженного компаратора равенства. Они оба теперь ведут себя лучше ==. Если имеется перегруженный компаратор равенства, код все равно будет другим .

Смотрите более ранние версии компилятора Roslyn ниже анализ.


Потому nullчто нет разницы с тем, к чему мы привыкли в C # 6. Однако, вещи становятся интересными, когда вы переходите nullна другую константу.

Возьмите это к примеру:

Test(1);

public void Test(object o)
{
    if (o is 1) Console.WriteLine("a");
    else Console.WriteLine("b");
}

Тест дает a. Если вы сравните это с o == (object)1тем, что написали бы, как обычно, это будет чертовски важно.isпринимает во внимание тип на другой стороне сравнения. Это круто!

Я думаю, что паттерн « == nullпротив is nullконстанты» просто очень знаком «случайно», где синтаксис isоператора и оператора равенства дают один и тот же результат.


Как прокомментировал свик , is nullзвонки System.Object::Equals(object, object)куда ==звонятceq .

IL для is:

IL_0000: ldarg.1              // Load argument 1 onto the stack
IL_0001: ldnull               // Push a null reference on the stack
IL_0002: call bool [mscorlib]System.Object::Equals(object, object) // Call method indicated on the stack with arguments
IL_0007: ret                  // Return from method, possibly with a value

IL для ==:

IL_0000: ldarg.1              // Load argument 1 onto the stack
IL_0001: ldnull               // Push a null reference on the stack
IL_0002: ceq                  // Push 1 (of type int32) if value1 equals value2, else push 0
IL_0004: ret                  // Return from method, possibly with a value

Поскольку мы говорим о том null, что нет никакой разницы, поскольку это имеет значение только в случаях . Это может измениться, если вы перегружаете оператор равенства.

Патрик Хофман
источник
16
@PatrickHofman Это выглядит как isвызовы object.Equals(x, null), а ==компилируется как ceq. Но результат должен быть таким же, как вы сказали.
svick
18
Всегда помните, что ==это перегружаемый оператор. Вы можете иметь любое поведение, которое вы хотите с ним. Например, эта странная реализация не== скажет вам, является ли ваш экземпляр действительно нулевым. is nullс другой стороны, всегда будет возвращать true для истинных нулевых ссылок :) Кроме того, если у вас есть ReferenceEqualsкод, лампочки VS 2017 предложат изменить на is null, а не == null(правильно).
nawfal
2
@PatrickHofman @svick две проверки на нуль теперь компилируются в одну и ту же вещь, поэтому isбольше нет накладных расходов на вызов функции при использовании для проверки на ноль. Для доказательства смотрите ссылку, размещенную @svick в комментариях.
AndreasHassing
1
@ AndreasBjørnHassingNielsen Обновил мой ответ.
Патрик Хофман
2
@PatrickHofman разве ИЛ не должен быть наоборот? == вызывает System.Object :: Equals (объект, объект), а null вызывает ceq
Zbigniew Ledwoń
68

Перегруженный оператор равенства

На самом деле существует разница в семантике между двумя сравнениями, когда вы сравниваете nullс типом, который перегружает ==оператор. foo is nullбудет использовать прямое сравнение ссылок для определения результата, тогда как foo == null, конечно, запустит перегруженный ==оператор, если он существует.

В этом примере я ввел «ошибку» в перегруженном ==операторе, заставляя его всегда генерировать исключение, если второй аргумент null:

void Main()
{
    Foo foo = null;

    if (foo is null) Console.WriteLine("foo is null"); // This condition is met
    if (foo == null) Console.WriteLine("foo == null"); // This will throw an exception
}

public class Foo
{
    public static bool operator ==(Foo foo1, Foo foo2)
    {
        if (object.Equals(foo2, null)) throw new Exception("oops");
        return object.Equals(foo1, foo2);
    }

    // ...
}

Код IL для foo is nullиспользования ceqинструкции для прямого сравнения ссылок:

IL_0003:  ldloc.0     // foo
IL_0004:  ldnull      
IL_0005:  ceq

В IL-коде foo == nullиспользуется вызов перегруженного оператора:

IL_0016:  ldloc.0     // foo
IL_0017:  ldnull      
IL_0018:  call        UserQuery+Foo.op_Equality

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

Ограничение на дженерики

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

bool IsNull<T>(T item) => item is null;                  // Compile error: CS0403
bool IsNull<T>(T item) => item == null;                  // Works
bool IsNull<T>(T item) where T : class => item is null;  // Works

Спасибо Дэвиду Аугусто Вилле за указание на это.

Торкил Холм-Якобсен
источник
2
Кроме того, note (x равен нулю) требует ограничения класса, если x является универсальным типом, тогда как (x == null) и object.ReferenceEquals (x, null) нет.
Давид Аугусто Вилла