.net “x is null”和“x == null”有什么区别?

ctehm74n  于 2023-11-20  发布在  .NET
关注(0)|答案(3)|浏览(126)

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

if (x is null) return;

字符串
而不是

if (x == null) return;


使用新方法(前一个例子)比使用旧方法有什么优点吗?
语义有什么不同吗?
这仅仅是口味的问题吗?如果不是,我什么时候应该用一种而不是另一种?
参考:What’s New in C# 7.0 .

m4pnthwp

m4pnthwp1#

**更新:**Roslyn编译器已更新,使两个运算符的行为相同 * 当没有重载相等运算符 *。请参阅当前编译器结果中的代码(代码中的M1M2),该代码显示了当没有重载相等比较器时会发生什么。它们现在都具有性能更好的==行为。如果有重载相等比较器,代码仍然不同。

有关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调用System.Object::Equals(object, object),而==调用ceq

  • is的IL:*
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没有区别。当你重载了相等运算符时,这可能会改变。

a64a0gku

a64a0gku2#

重载equals运算符

当你将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


因此,不同之处在于,如果使用==,则有运行用户代码的风险(这可能会产生意外行为或性能问题)。
您还可以使用SharpLab在线工具查看C#编译器如何转换Main方法:

private void Main()
{
    Foo foo = null;
    if ((object)foo == null)
    {
        Console.WriteLine("foo is null");
    }
    if (foo == null)
    {
        Console.WriteLine("foo == null");
    }
}

8zzbczxx

8zzbczxx3#

当你试图比较一个非空变量和一个空值时,也有一个区别。当使用==时,编译器会发出一个警告,而当使用is时,编译器会发出一个错误。最有可能的是,99%的情况下,你希望编译器为这样一个基本的错误向你大喊大叫。对于is null,+1。


的数据



P.S.在https://dotnetfiddle.net/上使用NetCore3.1进行了测试

相关问题