【问题标题】:Result of calling IEquatable<T>.Equals(T obj) when this == null and obj == null?this == null 和 obj == null 时调用 IEquatable<T>.Equals(T obj) 的结果?
【发布时间】:2011-12-27 01:15:51
【问题描述】:

this == nullobj == nullIEquatable&lt;T&gt;.Equals(T obj)应该怎么做?

1) 此代码由 F# 编译器在实现IEquatable&lt;T&gt; 时生成。可以看到当两个对象都是null时它返回true

公共密封覆盖 bool Equals(T obj) { 如果(这 == 空) { 返回 obj == null; } 如果(obj == null) { 返回假; } // this 和 obj 都不为 null 时的代码。 }

2) 类似的代码可以在问题“in IEquatable implementation is reference check necessary”或问题“Is there a complete IEquatable implementation reference?”中找到。当两个对象都是null 时,此代码返回false

公共密封覆盖 bool Equals(T obj) { 如果(obj == null) { 返回假; } // obj 不为 null 时的代码。 }

3)最后一个选项是说this == null时没有定义方法的行为。

【问题讨论】:

  • this == null 的情况应该永远 (!!!) 发生。不确定 F# 的特性,但在 C# 中是这样。
  • call操作码调用方法时没有检查this != null
  • @RadekMicek,这就是 C# 编译器为非静态静态方法调用发出 callvirt 的原因。但也许 F# 只发出 call...
  • @Radek 即便如此,以上内容在 C# 中永远不会是正确的语义,并且在这种情况下永远不需要 null 检查。即使在 VB 中,有一个 MyClass 关键字发出 call 而不是 callvirt,这种情况也不会出现。
  • @KonradRudolph:但在 CLR 中仍有可能......

标签: c# .net f# equals equality


【解决方案1】:

我肯定会选择选项 1:

    if (this == null)
    {
        return obj == null;
    }
    if (obj == null)
    {
        return false;
    }

空对象总是等于空对象。

【讨论】:

  • 但这绝不等于null。
  • @JonHanna,在 C# 中这是真的,但是 OP 显示的代码是由 F# 生成的(并且可能通过 Reflector 或类似工具反编译为 C#)
  • @ThomasLevesque 是的,这就是他们提出这个问题的原因,但将其留在工程 C# 中是没有意义的。在 C# 中(如标签所示),测试不可能是真的。
  • @JonHanna,当然可以,但是如果该方法是用 C# 编写的,并被另一种语言的代码调用,而该语言在调用该方法之前不检查 null 怎么办? (例如,如果它发出 call 而不是 callvirt
  • @ThomasLevesque 那么它不会在检查成员以更详细地检查相等性时立即抛出吗?就像在不能保证在对象为 null 时表现良好的方法上使用 call 的任何情况一样,无论使用什么语言,都可以做到这一点。
【解决方案2】:

【讨论】:

  • IMO 该代码示例非常可疑。可变身份,而不是比较所有公共属性都是我要避免的模式。
【解决方案3】:

如果 this==null,您将在该对象上调用 Equals() 时获得运行时异常。

【讨论】:

  • this 永远不能为空;要访问this,您必须在该类型的实例中
  • 我当然知道,但是这个 == null 永远没有意义,因为在到达那一步之前您会遇到运行时异常。
  • @Jobo - 唯一可能的异常是 StackOverflowException。你指的是这个吗?
  • this!=null 只有在直接调用 C#(可能还有大多数其他 .net 语言)的方法时才能得到保证。可以使用其他语言的this==null 或使用委托来调用实例方法。
  • this 可以是 null,只是不能在 C# 中。
【解决方案4】:

如果this 为空,则无法调用代码,因此无需考虑这种情况(无论如何,在 C# 中,有些语言允许空对象具有取消引用的方法,但显然如果它在内部检查它会出错的任何不存在的字段。考虑:

return x.Equals(y);

如果 x 为空,我们甚至无法调用 Equals 来进行空检查。

因此我们只需要考虑:

public bool Equals(T obj)
{
  if(obj == null)
    return false;
  //logic defining equality here.
}

当我们从静态== 运算符覆盖或IEqualityComparer&lt;T&gt; 实现中检查它们时,确实会出现两个对象为空的可能性:

public bool Equals(T x, T y)
{
  if(x == null)
    return y == null;
  if(y == null)
    return false;
  //logic defining equality here.
}

请注意,如果相等可以很长地确定(例如比较长字符串),那么这里有用的捷径,那么我们可以利用身份需要相等这一事实 - 即某物总是等于它自己,甚至 Ayn Rand 也可以计算那出来 ;) 还有一些算法可以将一个项目与它本身进行比较,这使得这个快捷方式非常值得包括在内。在这种情况下,身份比较已经包括检查两者是否为空,因此我们再次将其省略:

public bool Equals(T x, T y)
{
  if(ReferenceEquals(x, y))
    return true;
  if(x == null || y == null)
    return false;
  //logic defining equality here.
}

【讨论】:

    【解决方案5】:

    对于大多数方法,当使用this==null 调用时,我假设行为未定义。这是因为大多数程序员在编写代码时都假设this!=null,如果调用代码是用 C# 编写的,则 C# 规范保证了这一点。

    这就是为什么x.Equals(y) 的每个理智调用者都应该确定x 不是null,或者添加手动null 检查。

    在大多数情况下,我根本不会直接调用Equals,而是使用EqualityComparer&lt;T&gt;.Default

    【讨论】:

    • C# 保证它,F# 不我假设。看我的回答。
    【解决方案6】:

    F# 这样做的原因(我怀疑)是为了将空列表优化为null

    通过添加此检查,它允许在null 实例上调用实例方法而不会出现任何问题。

    不久前见my blog post

    在 C# 中,这无关紧要。

    回答问题:

    它应该返回 true,因为两个实例都是 null 并被视为相等。

    【讨论】:

    • 但是F#中的空列表被定义为非空internal static readonly FSharpList&lt;T&gt; _unique_Empty = new FSharpList&lt;T&gt;();
    • 正如其他人所指出的,null 不用作 F# 中空列表的表示形式。但是,它被用作选项值None的表示。
    【解决方案7】:

    leppie 是对的。只是为了详细说明他的答案(并确认他对 F# 不保证 this != null) 的怀疑:可区分的联合可以用属性 [&lt;CompilationRepresentation(CompilationRepresentationFlags.UseNullAsTrueValue)&gt;] 标记,允许用 null 值表示案例。Option&lt;'T&gt; 就是这样一种类型。 None 的情况在运行时由 null 表示。(None : option&lt;int&gt;).Equals(None) 在语法上是有效的。这是一个有趣的例子:

    [<CompilationRepresentation(CompilationRepresentationFlags.UseNullAsTrueValue)>]
    type Maybe<'T> =
      | Just of 'T
      | Nothing
      [<CompilationRepresentation(CompilationRepresentationFlags.Instance)>]
      member this.ThisIsNull() = match this with Nothing -> true | _ -> false
    

    使用 Reflector 反编译 ThisIsNull 显示

    public bool ThisIsNull()
    {
        return (this == null);
    }
    

    结果:

    Nothing.ThisIsNull() //true
    

    【讨论】:

      猜你喜欢
      • 2013-08-09
      • 1970-01-01
      • 2023-04-02
      • 1970-01-01
      • 1970-01-01
      • 2016-04-24
      • 2011-09-11
      • 1970-01-01
      • 2019-10-07
      相关资源
      最近更新 更多