定义operator ==但没有定义Equals()或GetHashCode()有什么问题?

时间:2012-05-28 21:00:27

标签: c# warnings equals equals-operator

以下代码

public struct Person
{
    public int ID;
    public static bool operator ==(Person a, Person b) { return  a.Equals(b); }
    public static bool operator !=(Person a, Person b) { return !a.Equals(b); }
}

为什么编译器会给我这些警告?
没有定义下面的方法有什么问题?

warning CS0660: 'Person' defines operator == or operator != but
    does not override Object.Equals(object o)

warning CS0661: 'Person' defines operator == or operator != but
    does not override Object.GetHashCode()

8 个答案:

答案 0 :(得分:19)

编辑:此答案已得到纠正,除其他事项外,用户定义的值类型不会生成==,并提及{{3}的性能问题}。


一般来说,覆盖一个,但不是全部,令人困惑。用户希望不会被重写,或者两者都被覆盖,具有相同的语义。

微软此状态的ValueType.Equals(以及其他内容):

  
      
  • 每当实现Equals方法时,都要实现GetHashCode方法。这使得Equals和GetHashCode保持同步。

  •   
  • 无论何时实现等于运算符(==),都要覆盖Equals方法,并让它们做同样的事情。

  •   

在您的情况下,您有正当理由推迟Equals(编译器不会自动实现==)并仅覆盖这两个(== / {{1} })。但是,由于!=使用了反射:

,因此仍存在性能问题
  

“覆盖特定类型的Equals方法以改进   该方法的表现更贴切地代表了这一概念   类型相等。“

因此,我们仍建议最后覆盖所有(ValueType.Equals / == / !=)。当然,对于这个简单的结构,性能可能无关紧要。

答案 1 :(得分:3)

我的猜测是你收到这些警告,因为编译器不知道你在Equals方法中使用==

假设您有此实现

public struct  Person
{
    public int ID;
    public static bool operator ==(Person a, Person b) { return Math.Abs(a.ID - b.ID) <= 5; }
    public static bool operator !=(Person a, Person b) { return Math.Abs(a.ID - b.ID) > 5; }
}

然后

 Person p1 = new Person() { ID = 1 };
 Person p2 = new Person() { ID = 4 };

 bool b1 = p1 == p2;
 bool b2 = p1.Equals(p2);

b1将是 true ,但是b2 false

<强> - 编辑 -

现在假设您要这样做

Dictionary<Person, Person> dict = new Dictionary<Person, Person>();
dict.Add(p1, p1);
var x1 = dict[p2]; //Since p2 is supposed to be equal to p1 (according to `==`), this should return p1

但是这会抛出像KeyNotFound

这样的异常

但是如果你添加

public override bool Equals(object obj)
{
    return Math.Abs(ID - ((Person)obj).ID) <= 5; 
}
public override int GetHashCode()
{
    return 0;
}

你会得到你想要的东西。

编译器只是警告您可能面临类似的情况

答案 2 :(得分:2)

框架内普遍期望certain operations应始终产生相同的结果。原因是某些操作(特别是排序和搜索,构成任何应用程序的很大一部分)依赖于这些不同的操作,从而产生有意义且一致的结果。在这种情况下,您打破了以下几个假设:

  • 如果==a之间存在有效的操作b,则应生成与a.Equals(b)
  • 相同的结果
  • 类似地,如果!=a之间存在有效的操作b,则应该产生与!a.Equals(b)
  • 相同的结果
  • 如果存在两个ab对象a == b,则ab在存储在哈希表中时应生成相同的密钥

前两个,IMO,显而易见;如果要定义两个对象相等的含义,则应该包括检查两个对象是否相等的所有方法。请注意,编译器(通常不能)不强制您实际遵循这些规则。它不会对运算符的主体执行复杂的代码分析,看看它们是否已经模仿Equals,因为在最坏的情况下,这可能等同于solving the halting problem.

但它可以做的是检查您最有可能违反这些规则的情况,特别是您提供了自定义比较运算符并且未提供自定义Equals方法。这里的假设是,如果您不希望它们执行某些特殊操作,您就不会费心提供运算符,在这种情况下,您应该为所有方法提供自定义行为。同步。

如果你确实Equals实现了与==不同的东西,那么编译器就不会抱怨;你会达到C#愿意试图阻止你做一些愚蠢事情的极限。它愿意阻止你不小心在你的代码中引入微妙的错误,但如果这就是你想要的话,它会让你故意这样做。

第三个假设与框架中的许多内部操作使用哈希表的某些变体这一事实有关。如果我有两个对象,根据我的定义,“相等”,那么我应该能够做到这一点:

if (a == b)
{
    var tbl = new HashTable();
    tbl.Add(a, "Test");

    var s = tbl[b];
    Debug.Assert(s.Equals("Test"));
}

这是哈希表的一个基本属性,如果它突然不正确会导致非常奇怪的问题。

答案 3 :(得分:1)

您需要做的就是在结构中添加另一个成员,例如Forename。

所以,如果你有两个身份证号为63但姓名不同的人,他们是否相同?

全部取决于您想要实施的“相同”的定义。

使用一个更好的示例结构,编写一个执行各种方法的noddy应用程序,看看当你更改相等和/或等价的定义时会发生什么,如果它们不是一步到位,你最终会得到类似的东西!( a == b)!=(a!= b),这可能是真的,但是如果你没有覆盖所有使用过你的方法,代码会想知道你的意图是什么。

基本上编译器告诉你要成为好公民并明确你的意图。

答案 4 :(得分:0)

可能是因为预期默认Equals()方法对于真实系统来说不够好(例如在您的课程中它应该比较ID字段)。

答案 5 :(得分:0)

如果覆盖EqualsGetHashCode,您甚至不需要覆盖运算符,这是一种更简洁的方法。 编辑:它应该工作,因为这是一个结构。

答案 6 :(得分:0)

阅读MSDN页面。

CS0660

CS0661

编译器基本上是在说:“既然你说知道如何比较你的对象,你应该让它一直比较。”

答案 7 :(得分:0)

public struct Coord
{
    public int x;
    public int y;

    public Coord(int x, int y)
    {
        this.x = x;
        this.y = y;
    }

    public static bool operator ==(Coord c1, Coord c2)
    {
        return c1.x == c2.x && c1.y == c2.y;
    }

    public static bool operator !=(Coord c1, Coord c2)
    {
        return !(c1 == c2);
    }

    public bool Equals(Coord other)
    {
        return x == other.x && y == other.y;
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        return obj is Coord && Equals((Coord) obj);
    }

    public override int GetHashCode()
    {
        return 0;
    }
}

这是一个例子。希望它有用。