我有一位同事喜欢编写如下的空检查:
if (!ReferenceEquals(myObject, null))
另一方面,我发现这种语法阅读起来很麻烦并且更喜欢:
if (myObject != null)
我发现一些文章和堆栈溢出问题讨论了 ReferenceEquals 在运算符重载方面的优点,但在运算符重载场景之外,ReferenceEquals 与 == 有什么好处吗?
但是在运算符重载场景之外, ReferenceEquals vs == 有什么好处吗?
不 - 显式使用的唯一优势(我认为这不是什么优势)Object.ReferenceEquals
是它永远不会使用重载的运算符等于。在非重载的情况下,== 运算符被定义为“如果它的两个操作数引用同一个对象,则返回 true”,以用于所有“除字符串之外的引用类型”。因此,它的等价物(只要它没有重载)。
我个人也赞成使用第二种语法,并且发现它更易于维护以进行空检查。我还认为,任何重载operator==
也应该提供适当的检查null
,如果由于某种原因它没有(这很奇怪),那么该决定背后可能有一个特定的理由会导致你想要使用重载,而不是ReferenceEquals
.
使用 c# 7,您可以使用:
if ( !(myObject is null) )
相当于
if (!ReferenceEquals(myObject, null))
好吧,如果有人要覆盖 == 或 != 运算符,他们可以让他们为所欲为。甚至可以让它做一些真正的意思,比如return true;
or return false;
。此外,如果有一个重载的运算符,那么它很有可能不会表现得那么好ReferenceEquals
(不能保证,它可能还不够重要,但仍然如此)。
话虽如此,因为任何重载运算符的任何合理实现都不太可能成为问题。我个人不使用ReferenceEquals
,除非我有令人信服的理由不使用该==
类型或特定情况下的运算符。
在空值检查方面,两者应该总是返回相同的结果。如果非空引用曾经等于空(即使使用空对象模式),无论是使用 ReferenceEquals 还是 == 运算符,这都是一件非常糟糕的事情。所以,在那种情况下,我会使用 ==/!=。
我想说,如果 == 运算符重载,使用 ReferenceEquals可能会稍微快一些。重载 == 应该做的第一件事是查看两个变量是否指向同一个对象,因此在重载运算符的情况下,您最终会在调用堆栈上获得额外的帧。使用 ReferenceEquals 还可以保证这是唯一执行的检查。
我通常也会在几乎任何其他情况下使用 ==/!= 。整个想法是运算符定义“平等”;这并不总是引用的(事实上,大多数复合对象应该在结构上比较是否相等;如果它们的成员相等,它们就相等)。从理论上讲,对象知道如何最好地将自己与另一个对象进行比较,例如相等、相对顺序等,因此与其硬编码一个非常具体且可能不正确的逻辑,您应该使用语言的面向对象特性来让对象告诉你它是否等于其他任何东西。
VB.NET 标记可能不应该包含在这个问题中,因为它没有另外提及,但为了完整起见,Is
它等同于Object.ReferenceEquals
,因此总是可以用来代替那个调用。
所以,即使它已经有一百万年的历史了,我也想加入这个谈话。
假设我们要编写一个检查 null 的扩展方法。我们可以做到以下几点:
public static bool IsNull<T>(this T value) where T : class, new()
{
return value == null;
}
但是这段代码很无聊。
原因是 T 必须是一个类。为什么?因为我们无法将值类型与 null 进行比较。这使得编写体面的通用代码变得非常痛苦。
或者,您可以将相同的方法附加到对象,而不必关心该对象是什么。
public static bool IsNull(this object value)
{
return object.ReferenceEquals(value, null);
}
这打开了其他可能性,例如为 Linq 编写容错 monad 或您拥有的东西 - 而无需实际限制您的通用代码。
ReferenceEquals 可能会稍微快一些。如前所述,它确保没有调用重载的 Equals 运算符。ReferenceEquals 还确保仅执行一次比较,而不是可能执行 2 次,具体取决于重载的 Equals 运算符的实现。尽管重载运算符很可能将 ReferenceEquals 本身用作第一条语句。
我个人确实使用 ReferenceEquals,但仅在我真正调整以挤出最后一个时钟周期的地方(可能被称为每秒数百万次的地方)。或者当我无法控制类型时,比如泛型。......但只有当性能真的很关键时。
真的,真的很晚才回复——但我在阅读 EFCore 库时遇到了这篇文章并遇到了这种方法......
Microsoft.EntityFrameworkCore.Utilities 中有一个 Check 类,它使用此逻辑进行空检查....
internal static class Check
{
[ContractAnnotation("value:null => halt")]
public static T NotNull<T>([NoEnumeration] T value, [InvokerParameterName] [NotNull] string parameterName)
{
#pragma warning disable IDE0041 // Use 'is null' check
if (ReferenceEquals(value, null))
#pragma warning restore IDE0041 // Use 'is null' check
{
NotEmpty(parameterName, nameof(parameterName));
throw new ArgumentNullException(parameterName);
}
return value;
}
正如其他答案中所解释的,这两个调用的语义略有不同。
如果您想使用ReferenceEquals
简化语法的语义,对于引用类型,您还可以使用:
if (myObject is object)
在 C# 9 中,您可以使用if (myObject is not null)
. 该参考文件规定了以下安全保证:
当您将表达式与 null 匹配时,编译器保证不会调用用户重载的 == 或 != 运算符。