it-roy-ru.com

В чем разница между "x is null" и "x == null"?

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

if (x is null) return;

вместо

if (x == null) return;

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

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

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

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

177
Maniero

Обновление: Компилятор 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 vs. is null просто очень знаком "случайно", где синтаксис оператора is и оператора equals дают одинаковый результат.


Как svick прокомментировано, 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, нет никакой разницы, поскольку это имеет значение только в случаях . Это может измениться, если вы перегружаете оператор равенства.

150
Patrick Hofman

На самом деле существует разница в семантике между двумя сравнениями. Случай Edge проявляется, когда вы сравниваете 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

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

43
Thorkil Holm-Jacobsen