【问题标题】:IEquatable<T> - Best practice override for .Equals(object obj) in C#IEquatable<T> - C# 中 .Equals(object obj) 的最佳实践覆盖
【发布时间】:2013-08-09 01:51:26
【问题描述】:

每当我编写一个新的classstruct 可能包含一些可能需要比较的数据时,我总是实现IEquatable&lt;T&gt;,因为这为class/struct 提供了一个强类型.Equals(T other)方法。

示例:

public struct Radius : IEquatable<Radius>
{
    public Int32 TopLeft { get; set; }
    public Int32 TopRight { get; set; }
    public Int32 BottomLeft { get; set; }
    public Int32 BottomRight { get; set; }

    public bool Equals(Radius other)
    {
        return this.TopLeft == other.TopLeft
            && this.TopRight == other.TopRight
            && this.BottomLeft == other.BottomLeft
            && this.BottomRight == other.BottomRight;
    }
}

除了为.Equals(Radius other) 提供一个实现之外,我真的应该重写默认实现 (.Equals(object obj))

我这里有两个选择,我的问题是,这些实现中哪个更好?

选项 1 是使用强制转换:

public override bool Equals(object obj)
{
    return this.Equals((Radius)obj);
}

选项 2 是使用“as”关键字:

public override bool Equals(object obj)
{
    return this.Equals(obj as Radius);
}

我问这个的原因是,如果不能将obj 转换为Radius,使用转换将引发异常,而如果不能转换as 将解析为null,因此它只检查@987654335 @反对null,不抛出异常;那么是抛出异常更好,还是只返回false

编辑:正如许多 SO'ers 所指出的,结构不能为空,因此第二个选项不适用于结构。因此另一个问题浮现在脑海中:.Equals(object obj) 的覆盖实现对于结构和类是否应该相同?

【问题讨论】:

  • 如果other 为空,您的Equals(Radius) 方法应返回false,否则将抛出空输入异常。
  • @cdhowie 是一个结构体,不能为空。
  • 您不能使用as 关键字,因为Radius 不是可空类型。您应该改用 is 为其他类型返回 false,然后进行强制转换。
  • @Servy 我错过了。在这种情况下,仍然可以通过使用as-cast 到可空类型来完成一次类型检查:var radius = obj as Radius?; return radius == null ? false : Equals(radius.Value); 人们应该始终更喜欢 as 而不是 is-followed-by-parenthetical-cast 为它只执行一种类型检查。
  • @cdhowie 可以执行的最快操作之一的速度提高了 30%。这在性能上的差异完全可以忽略不计,以至于在几乎任何应用程序中,即使提及它也不值得。如果类型本身可以为空,并且您想要可以为空的结果,则使用 as 是有意义的,但感觉它在逻辑上不可以为空,您正在做的将其翻译回来的工作有效地消除了所获得的优势。这两种解决方案是相当的,差异几乎完全是主观偏好,仅此而已。

标签: c# .net exception boolean equality


【解决方案1】:

Equals() 方法must never throw an exception

不同类型的对象只是不相等的。

引用documentation

Equals 的实现不能抛出异常;他们应该总是返回一个值。例如,如果 obj 为 null,则 Equals 方法应返回 false 而不是抛出 ArgumentNullException。

【讨论】:

  • “一个不同类型的对象”——或者根本没有对象 (null)。
  • 除非你调用 Equals 的对象为空
  • @chriswarner:在这种情况下,在输入Equals()之前抛出异常。
  • @chriswarner:消息来源说if (this == null) //this is necessary to guard against reverse-pinvokes and throw new NullReferenceException(); //other callers who do not use the callvirt instruction
  • @SLaks 当一个人使用指针时,这与它有很大关系。在安全代码中,不需要进行空测试,因为使用空对象的成员会引发 NullReferenceException,这在代码损坏时是完全可以接受的。由空对象上的指针算术导致的访问冲突是不可接受的。
【解决方案2】:

正如@SLaks 已经提到的,Equals() 永远不应该抛出。

在这种特殊情况下,我认为将 is 运算符与强制转换结合使用应该可以帮助您:

public override bool Equals(object obj)
{
     if(obj is Radius)
         return Equals((Radius)obj);

     return false;
}

如果您有 class,您应该简单地使用 as 运算符:

public override bool Equals(object obj)
{
     return Equals(obj as MyObj);
}

public bool Equals(MyObj obj)
{
     if(ReferenceEquals(obj, null))
         return false;

     // ToDo: further checks for equality.
}

【讨论】:

  • 谢谢,这给了我一个非常清晰、简洁的例子来说明我应该如何实现。
【解决方案3】:

我个人的意见是使用第二个选项,甚至事先检查对象是否为“半径”,然后返回 false,以便意图更明确

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-12-27
    • 2011-12-06
    • 1970-01-01
    • 1970-01-01
    • 2011-09-11
    • 2012-09-11
    • 2010-09-20
    相关资源
    最近更新 更多