C# 什么';定义运算符==但不定义Equals()或GetHashCode()有什么错?
对于下面的代码C# 什么';定义运算符==但不定义Equals()或GetHashCode()有什么错?,c#,warnings,equals,equals-operator,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
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()
可能是因为默认的
Equals()
方法对于实际系统来说不够好(例如,在您的类中,它应该比较ID
字段)。编辑:这个答案已经更正,需要注意的是,用户定义的值类型不会生成=
,并提及性能问题
一般来说,覆盖一个,但不是全部,是令人困惑的。用户希望两者都不被覆盖,或者两者都被覆盖,语义相同 Microsoft对此状态的支持(除其他外):
- 无论何时实现Equals方法,都要实现GetHashCode方法。这使Equals和GetHashCode保持同步
- 每当实现相等运算符(=)时,重写Equals方法,并使它们执行相同的操作
Equals
(编译器不会自动实现==
)并仅重写这两个(=
/!=
)。但是,仍然存在性能问题,因为ValueType.Equals
使用反射:
“重写特定类型的Equals方法以改进
该方法的性能和更紧密地代表了
类型的相等性。”
因此,仍然建议在最后覆盖所有(
=
/!=
/等于
)。当然,对于这个简单的结构,性能可能并不重要。如果覆盖Equals
和GetHashCode
,甚至不需要覆盖操作符,这是一种更干净的方法。
已编辑:它应该可以工作,因为这是一个结构 阅读MSDN页面
编译器基本上是这样说的:“既然你说知道如何比较你的对象,你就应该让它一直这样比较。”我猜你会得到这些警告,因为编译器不知道你在
=
方法中使用等于
假设您有这个实现
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; }
}
b1为真,b2为假
--编辑--
现在假设你想这样做
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
Dictionary dict=new Dictionary();
dict.Add(p1,p1);
var x1=dict[p2]//因为p2应该等于p1(根据`=`),所以应该返回p1
但这会抛出一个类似KeyNotFound的异常
但是如果你加上
public override bool Equals(object obj)
{
return Math.Abs(ID - ((Person)obj).ID) <= 5;
}
public override int GetHashCode()
{
return 0;
}
public override bool Equals(对象对象对象)
{
return Math.Abs(ID-((Person)obj.ID)您只需向结构中添加另一个成员,比如Forename
那么,如果你有两个ID为63但名字不同的人,他们是否相等
这一切都取决于您想要实现的“相同”的定义
使用一个更好的示例结构,编写一个noddy applictaion来执行各种方法,并查看当您更改相等和/或相等的定义时会发生什么,如果它们不一致,那么您将得到以下结果!(a==b)!=(a!=b),这可能是真的,但是如果您不重写所有使用您的代码的方法,那么使用您的代码的人会想知道您的意图是什么
基本上,编译器是在告诉你要做一个好公民,把你的意图说清楚。框架中有一个普遍的期望,它应该总是产生相同的结果。原因是某些操作(特别是排序和搜索,它们在任何应用程序中占很大一部分)依靠这些不同的操作产生有意义和一致的结果。在这种情况下,你打破了一些假设:
- 如果在
a
和b
之间存在有效的操作=
,则应产生与a.Equals(b)
相同的结果
- 类似地,如果在
a
和b
之间有一个有效的操作!=
,它应该产生与!a.Equals(b)
相同的结果
- 如果存在两个对象
a
和b
,其中a==b
,则a
和b
在存储在哈希表中时应产生相同的键
前两个,依我看,是显而易见的;如果你要定义两个对象相等意味着什么,你应该包括所有可以检查两个对象相等的方法强制您实际遵守这些规则。它不会对运算符主体执行复杂的代码分析,以查看它们是否已经模拟了等于
,因为在最坏的情况下,这可能相当于
但是,它可以做的是检查您最有可能违反这些规则的情况,特别是您提供了自定义比较运算符,但没有提供自定义的Equals
方法。这里的假设是,如果您不想让运算符做特殊的事,您不会费心提供运算符,在这种情况下,您应该为所有需要同步的方法提供自定义行为
如果您确实实现了Equals
与=
不同的东西,编译器不会抱怨;您将达到C#试图阻止您做蠢事的极限。C#愿意阻止您意外地在代码中引入微妙的错误,但它会让您有目的地避免如果这是你想要的,就这么做
第三个假设与以下事实有关:框架中的许多内部操作使用哈希表的某个变体。如果根据我的定义,我有两个“相等”的对象,那么我应该能够做到这一点:
if (a == b)
{
var tbl = new HashTable();
tbl.Add(a, "Test");
var s = tbl[b];
Debug.Assert(s.Equals("Test"));
}
这是哈希表的一个基本属性,如果
if (a == b)
{
var tbl = new HashTable();
tbl.Add(a, "Test");
var s = tbl[b];
Debug.Assert(s.Equals("Test"));
}
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;
}
}