提问人:Maniero 提问时间:11/18/2016 最后编辑:PangManiero 更新时间:11/16/2023 访问量:158732
“x is null”和“x == null”有什么区别?
What is the difference between "x is null" and "x == null"?
问:
在 C# 7 中,我们可以使用
if (x is null) return;
而不是
if (x == null) return;
与旧方法相比,使用新方法(以前的例子)有什么优势吗?
语义有什么不同吗?
这只是口味问题吗?如果没有,我应该在什么时候使用一个而不是另一个?
参考:C# 7.0 中的新增功能。
答:
更新:Roslyn 编译器已更新,使两个运算符在没有重载相等运算符时的行为相同。请参阅当前编译器结果(和代码)中的代码,该代码显示了没有重载相等比较器时会发生什么情况。他们现在都有更好的表现。如果存在重载的相等比较器,则代码仍然不同。M1
M2
==
有关旧版本的 Roslyn 编译器,请参阅以下分析。
因为这与我们习惯的 C# 6 没有区别。但是,当您更改为另一个常量时,事情会变得有趣。null
null
举个例子:
Test(1);
public void Test(object o)
{
if (o is 1) Console.WriteLine("a");
else Console.WriteLine("b");
}
测试结果为 。如果你把它与你平时写的东西进行比较,它确实有很大的不同。 考虑比较另一侧的类型。很酷!a
o == (object)1
is
我认为 vs. 常量模式只是“偶然”非常熟悉的东西,其中运算符和等于运算符的语法产生相同的结果。== null
is null
is
正如 svick 所评论的,是 null
调用 System.Object::Equals(object, object),
其中 ==
调用 ceq
。
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
评论
是
调用对象。Equals(x, null),
而 ==
编译为 ceq
。但结果应该是一样的,正如你所说。
==
不会告诉你你的实例是否真的是 null。 另一方面,对于 true null 引用,将始终返回 true:)此外,如果您的代码中有,VS 2017 灯泡将建议更改为 ,而不是(正确)。==
is null
ReferenceEquals
is null
== null
重载等于运算符
实际上,当您与使运算符重载的类型进行比较时,这两个比较之间的语义存在差异。 将使用直接引用比较来确定结果,而当然会运行重载运算符(如果存在)。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
因此,区别在于,如果您使用,则可能会运行用户代码(这可能会产生意外行为或性能问题)。==
您还可以使用 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");
}
}
评论
bool IsNull<T>(T item) where T : struct => item is null;
bool IsNull<T>(T item) => item is null;
当您尝试将非 null 变量与 null 值进行比较时,也存在差异。使用 时,编译器会发出警告,而使用 时,编译器会发出 Error。最有可能的是,在99%的情况下,你希望编译器因为你这样一个基本错误而对你大喊大叫。+1 代表 。==
is
is null
P.S. 在 NetCore3.1 的 https://dotnetfiddle.net/ 上进行了测试
评论