“ x为空”和“ x ==空”有什么区别?


277

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

if (x is null) return;

代替

if (x == null) return;

与旧方法相比,使用新方法(以前的示例)有什么优势?

语义有什么不同吗?

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

参考:C#7.0的新增功能


4
那就是我刚才正在查看的链接,但是它没有提供很多信息,这就是为什么我猜OP在问这个问题。页面上最重要的部分是此测试是否为Operator(运算符)“ is”运算符用于检查对象的运行时类型是否与给定类型兼容。换句话说,我们使用“ is”运算符来验证对象的类型是否符合我们的预期。让我们看一下它的语法:
Simon Price

2
@SimonPrice这与C#的当前版本有关:C#6。此问题与C#7有关,它具有模式匹配
Patrick Hofman

@bigown您想要什么样的细节?
帕特里克·霍夫曼

@派翠克·霍夫曼(PatrickHofman)例如,回答了那种过错
Maniero

Answers:


231

更新: 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考虑比较另一侧的类型。太棒了!

我认为== 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

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,就没有区别,因为这仅对实例有所不同。当重载了相等运算符时,这可能会改变。


15
@PatrickHofman 它看起来像is调用object.Equals(x, null),而==编译为ceq但是结果应该和您说的一样。
svick

16
请始终谨记,这==是一个可重载的运算符。您可以使用它进行任何操作。例如,这个怪异的实现==不会告诉您您的实例是否真正为空。is null另一方面,对于真正的null引用,总会返回true :)另外,如果您ReferenceEquals的代码中包含VS 2017灯泡,建议将其更改为is null,而不是== null(正确)。
nawfal

2
@PatrickHofman @svick现在可以将两个空检查编译为同一事物,因此is在用于检查空值时不再具有函数调用的开销。为了证明,请参阅@svick在评论中发布的链接。
AndreasHassing

1
@AndreasBjørnHassingNielsen更新了我的答案。
Patrick Hofman

2
@PatrickHofman应该不是IL吗?==调用System.Object :: Equals(object,object)并且为null调用ceq
ZbigniewLedwoń19'Apr 23'19

67

重载等于运算符

实际上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

感谢David Augusto Villa指出了这一点。


2
另外,如果x是泛型类型,则注意(x为null)需要类约束,而(x == null)和object.ReferenceEquals(x,null)则不需要类约束。
戴维·奥古斯托·维拉
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.