if (x is null) return;
代替
if (x == null) return;
使用新方式(以前的示例)是否有任何优势?
语义是否有所不同?
如果不是仅仅因为口味,那么我应该在什么时候使用一种而不是另一种方式?
参考:C# 7.0有什么新功能.
if (x is null) return;
代替
if (x == null) return;
使用新方式(以前的示例)是否有任何优势?
语义是否有所不同?
如果不是仅仅因为口味,那么我应该在什么时候使用一种而不是另一种方式?
参考:C# 7.0有什么新功能.
更新:Roslyn编译器已经更新,使得这两个运算符的行为在没有重载的等式运算符时相同。请查看当前编译器结果中的代码(代码中的M1
和M2
),展示了当没有重载的等式比较器时会发生什么。它们现在都具有更好的性能表现的==
行为。如果存在重载的等式比较器,则代码仍然不同。
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
与is null
的常量模式只是一些偶然非常熟悉的东西,其中is
运算符和等号运算符的语法产生了相同的结果。
正如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
,那么这没有区别,因为这 只对实例有影响。当您重载相等运算符时,情况可能会改变。
实际上,当你比较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_0016: ldloc.0 // foo
IL_0017: ldnull
IL_0018: call UserQuery+Foo.op_Equality
==
,你就有可能运行用户代码(这可能会导致意外行为或性能问题)。Main
方法。private void Main()
{
Foo foo = null;
if ((object)foo == null)
{
Console.WriteLine("foo is null");
}
if (foo == null)
{
Console.WriteLine("foo == null");
}
}
bool IsNull<T>(T item) where T : struct => item is null;
),它会失败,但是 bool IsNull<T>(T item) => item is null;
目前看起来是可以的 - Jcl当你尝试将非空变量与空值进行比较时,也存在区别。使用==
时,编译器会发出警告,而使用is
时,编译器会发出错误。很可能,在99%的情况下,您希望编译器因这种基本错误而指责您。对于is null
,加1。
P.S. 使用NetCore3.1在https://dotnetfiddle.net/上进行了测试。
is
运算符会调用object.Equals(x, null)
方法,而==
运算符编译成了ceq
操作码。” 但你之前说过结果应该是相同的。 - svick==
运算符是可以被重载的,你可以让它表现出任何你想要的行为。例如,这个奇怪实现的==
不能告诉你你的实例是否真正为空。另一方面,is null
永远返回 true 表示空引用 :) 如果你的代码中有ReferenceEquals
,VS 2017 会建议你将其更改为is null
,而不是== null
(这是正确的)。 - nawfal