有什么区别" x是null"和" x == null"?

时间:2016-11-18 11:49:34

标签: c# .net null pattern-matching c#-7.0

在C#7中,我们可以使用

if (x is null) return;

而不是

if (x == null) return;

使用新方式(前一个例子)是否有任何优势?

语义是否有所不同?

只是品味问题?如果没有,我何时应该使用另一个?

参考:What’s New in C# 7.0

3 个答案:

答案 0 :(得分:147)

更新:当没有重载的相等运算符时,Roslyn编译器已更新为使两个运算符的行为相同。请参阅代码中的code in the current compiler resultsM1M2),其中显示了没有重载的相等比较器时会发生什么。他们现在都有更好的==行为。如果存在重载的等式比较器the code still differs

请参阅以下分析的旧版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考虑到比较另一侧的类型。那很酷!

我认为== nullis null常量模式只是非常熟悉的事情'其中is运算符的语法和equals运算符产生相同的结果。

svick评论,is null calls System.Object::Equals(object, object) where == calls 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,因此only makes a difference on instances没有区别。当你重载了相等运算符时,这可能会改变。

答案 1 :(得分:41)

事实上,两种比较之间存在语义差异。当您将null与重载==运算符的类型进行比较时,边缘情况会出现。

foo is null将使用直接引用比较来确定结果,而foo == null当然会运行重载的==运算符(如果存在)。

在这个例子中,我在重载的==运算符中引入了一个“bug”,如果第二个参数是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);
    }

    // ...
}

foo is null的IL代码使用ceq指令执行直接引用比较:

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

foo == null的IL代码使用对重载运算符的调用:

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

所以区别在于,如果您使用==,则存在运行用户代码(可能会出现意外行为或性能问题)的风险。

答案 2 :(得分:2)

当您尝试将非null变量与null值进行比较时,也存在差异。使用==时,编译器将发出警告,而使用is时,编译器将发出错误。最有可能在99%的时间里,您希望编译器针对这种基本错误大喊大叫。为is null +1。

enter image description here

enter image description here

P.S。已通过NetCore3.1在https://dotnetfiddle.net/上进行了测试