我有这个对象:
public class Foo {
public string MyOwnId { get; set; }
public Guid FooGuid { get; } = Guid.NewGuid();
}
我希望Equals()
只关心那些MyOwnId
的人,否则他们永远不会相等。如果Foo
有MyOwnId
,我会尝试使用它,否则我想使用FooGuid
。
由于FooGuid
可能永远不会相同,我做了类似的事情:
public bool Equals(Foo foo) {
if (foo== null) return false;
return MyOwnId.Equals(foo.MyOwnId);
}
public override bool Equals(object obj) {
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((Foo)obj);
}
public override int GetHashCode() {
int hash = 13;
hash = (hash*7) + (!string.IsNullOrEmpty(MyOwnId) ? MyOwnId.GetHashCode() : FooGuid.GetHashCode());
return hash;
}
这是做我想要的正确方法吗?或者我还需要更改我的Equals
方法,以便它看起来像GetHashCode
一样?例如:
public bool Equals(Foo foo) {
if (foo == null) return false;
if (string.IsNullOrEmpty(MyOwnId) || string.IsNullOrEmpty(foo.MyOwnId)) return false;
return MyOwnId.Equals(foo.MyOwnId);
}
答案 0 :(得分:2)
好吧,让我们看看。您对Equals
和GetHashCode
的实施错误。
Equals
和GetHashCode
都不得抛出异常;反例是
Foo A = new Foo();
Foo B = new Foo() {
MyOwnId = "bla-bla-bla",
};
// Throws an exception
if (A.Equals(B)) {}
如果两个实例通过Equals
相等,则这些实例必须具有相同的哈希码;反例是
Foo A = new Foo() {
MyOwnId = "",
};
Foo B = new Foo() {
MyOwnId = "",
};
if (A.Equals(B)) {
// Hashcodes must be equal and they are not
Console.Write(String.Format("{0} != {1}", A.GetHashCode(), B.GetHashCode()));
}
可能(最简单)的实施
// since you've declared Equals(Foo other) let others know via interface implementation
public class Foo: IEquatable<Foo> {
public string MyOwnId { get; set; }
public Guid FooGuid { get; } = Guid.NewGuid();
public bool Equals(Foo other) {
if (Object.ReferenceEquals(this, other))
return true;
else if (Object.ReferenceEquals(null, other))
return false;
else
return String.Equals(MyOwnId, other.MyOwnId);
}
public override bool Equals(object obj) {
return Equals(obj as Foo); // do not repeat youself: you've got Equals already
}
public override int GetHashCode() {
// String.GetHashCode is good enough, do not re-invent a wheel
return null == MyOwnId ? 0 : MyOwnId.GetHashCode();
}
}
答案 1 :(得分:1)
或者我是否还需要更改我的Equals方法,使它看起来像我的GetHashCode一样?
您更改Equals
以匹配您希望解决相等的方式。你做到了。
您将GetHashCode()
更改为键入相同的信息。在这种情况下:
public override int GetHashCode()
{
return MyOwnId == null ? 0 : MyOwnId.GetHashCode();
}
顺便提一下,您的Equals(object)
有点过于复杂。我会用:
public override bool Equals(object obj)
{
return Equals(obj as Foo);
}
这将obj
为空的情况传递给特定的Equals()
(必须处理它),处理obj
不是{{1}通过传递Foo
一个null(无论如何都是假的)并将Equals()
的案例处理从obj
传递给更具体的事物(这也是必须的处理那个)。
Foo
的快捷方式在这里不值得做,因为只有一个字段被比较,并且它的比较将具有相同的ReferenceEquals
快捷方式。您不会将ReferenceEquals
作为专用foo
中的派生类型处理。如果Foo
未被封存,则应包括:
Foo
如果密封public bool Equals(Foo foo)
{
return (object)foo != null &&
foo.GetType() == GetType() &&
MyOwnId.Equals(foo.MyOwnId);
}
,则应省略Foo
比较。
如果GetType()
的逻辑比这更复杂,那么就像:
Equals()
确实是有益的,但同样应该是特定的过载,而不是一般的覆盖。
(在public bool Equals(Foo foo)
{
if ((object)foo == (object)this)
return true;
return (object)foo != null &&
foo.GetType() == GetType() &&
// Some more complicated logic here.
}
重载中执行引用相等性检查更有利,因为它们必须考虑两个操作数都为null的可能性,因此它们也可能认为它们都是相同的,它们隐含地包含那个案子)。
答案 2 :(得分:0)
哈希函数必须具有以下属性:
请参阅https://msdn.microsoft.com/en-us/library/system.object.gethashcode(v=vs.110).aspx