"x is null"和"x == null"之间有什么区别?

Man*_*ero 219 .net c# null pattern-matching c#-7.0

在C#7中我们可以使用

if (x is null) return;
Run Code Online (Sandbox Code Playgroud)

代替

if (x == null) return;
Run Code Online (Sandbox Code Playgroud)

使用新方法(前一个例子)比旧语法有什么好处吗?

语义学有什么不同?

只是品味问题?如果没有,何时使用一个或另一个.

参考.

Pat*_*man 190

更新: 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");
}
Run Code Online (Sandbox Code Playgroud)

测试结果a.如果将它与o == (object)1正常情况下的情况进行比较,那确实会产生一些差异.is考虑到比较的另一个站点上的类型.太棒了!

我认为== nullvs. 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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

由于我们正在谈论null,没有区别,因为这只会对实例产生影响.当你重载了相等运算符时,这可能会改变.

  • @PatrickHofman [它看起来像`is`调用`object.Equals(x,null)`,而`==`编译为`ceq`.](http://tryroslyn.azurewebsites.net/#b:master/f :%3Eilr/K4Zwlgdg5gBAygTxAFwKYFsDcAoADsAIwBswBjGUogQxBBgGEYBvbGNmAge06JgFkAjAApOBAFapSyGAA8AlDAC8APlkwwdCMCJEcASC49 + AJhHjJ0 + UtUylimFp04AvkA ==)但结果应该是相同的,如你所说. (14认同)
  • 始终要记住`==`是一个可重载的运算符.您可以随意使用它.例如,这个[奇怪的实现`==`](https://dotnetfiddle.net/QTpdpT)不会告诉你你的实例是否真的为空.另一方面,`is null`对于真正的空引用将始终返回true :)此外,如果您的代码中有`ReferenceEquals`,VS 2017灯泡将建议更改为`is null`,而不是`== null`(正确). (14认同)
  • 从 C# 9.0 开始,引入了“is not”,您可以使用 if (obj is not null) 而不是 if (!(obj is null)) ,它会忽略 != 的使用 (4认同)
  • @PatrickHofman @svick现在两个空检查编译成同样的东西,所以`is`在用于检查null时不再有函数调用的开销.有关证明,请参阅评论中@svick发布的链接. (2认同)
  • @AndreasBjørnHassingNielsen 更新了我的答案。 (2认同)
  • @PatrickHofman应该不是IL吗?==调用System.Object :: Equals(object,object)并且为null调用ceq (2认同)

Tho*_*sen 54

事实上,两种比较之间在语义上存在差异.当您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);
    }

    // ...
}
Run Code Online (Sandbox Code Playgroud)

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

IL_0003:  ldloc.0     // foo
IL_0004:  ldnull      
IL_0005:  ceq
Run Code Online (Sandbox Code Playgroud)

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

IL_0016:  ldloc.0     // foo
IL_0017:  ldnull      
IL_0018:  call        UserQuery+Foo.op_Equality
Run Code Online (Sandbox Code Playgroud)

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

  • 还应该注意的是,像“is”这样的空合并运算符(??)和空合并赋值运算符(??=)也会忽略重载的等于运算符(==)。 (4认同)
  • 此外,如果 x 是泛型类型,请注意 (x is null) 需要类约束,而 (x == null) 和 object.ReferenceEquals(x, null) 则不需要。 (3认同)

Fre*_*ric 12

当您尝试将非空变量与空值进行比较时,也存在差异。使用时==,编译器会发出警告,而使用时is,编译器会发出错误。很可能,在 99% 的情况下,您希望编译器因为这样一个基本错误而对您大喊大叫。+1 为is null

在此处输入图片说明

在此处输入图片说明

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

  • @BenjaminSutas 实际上,您可以配置 VS 来更改默认行为。但默认行为通常是大多数用户所期望的。 (11认同)