自R#2017.1.1以来,我在自动生成的GetHashCode()
函数中收到警告。让我解释一下如何创建这个函数:
如果覆盖类的Equals
函数,R#建议您创建相等的成员。如果让R#生成那些相等的成员,它也会覆盖GetHashCode()
函数。然而,在那里它使用我的类的所有属性。由于这些属性并非都是只读的,因此R#告诉我应该只读取它们并显示警告。
所以我的问题是,我应该将GetHashCode()
函数留空(或删除那些带警告的部分)或者我应该尝试将属性设置为只读,以便R#不再警告我了。 / p>
这是我的代码:
public override bool Equals(object obj) => obj is RamEntry && Equals((RamEntry) obj);
public override int GetHashCode()
{
unchecked
{
var hashCode = (Value != null ? Value.GetHashCode() : 0); //In those 5 lines the warnings are being displayed
hashCode = (hashCode * 397) ^ Unimportant.GetHashCode(); //-------------------------------------------------
hashCode = (hashCode * 397) ^ (Comment?.GetHashCode() ?? 0);//-------------------------------------------------
hashCode = (hashCode * 397) ^ (Address?.GetHashCode() ?? 0);//-------------------------------------------------
hashCode = (hashCode * 397) ^ LastUpdated.GetHashCode(); //-------------------------------------------------
return hashCode;
}
}
private bool Equals(RamEntry other)
=> string.Equals(Value, other.Value) && Unimportant == other.Unimportant &&
string.Equals(Comment, other.Comment) && string.Equals(Address, other.Address);
答案 0 :(得分:5)
我认为更好地解释为什么在覆盖相等成员时应该覆盖GetHashCode
的原因,以及在计算哈希代码时不应该使用可变字段\属性的原因。考虑这个简单的类:
public class Test {
public string First { get; set; }
public string Second { get; set; }
}
现在,如果Test
和First
属性相等,则确定两个Second
个对象相等。您生成平等成员但不覆盖GetHashCode
:
public class Test {
public string First { get; set; }
public string Second { get; set; }
public override bool Equals(object obj) {
return obj is Test && Equals((Test)obj);
}
protected bool Equals(Test other) {
return string.Equals(First, other.First) && string.Equals(Second, other.Second);
}
public static bool operator ==(Test left, Test right) {
return Equals(left, right);
}
public static bool operator !=(Test left, Test right) {
return !Equals(left, right);
}
}
之后您将Test
对象用于Hashset
。
var test1 = new Test();
test1.First = "a";
test1.Second = "b";
var set = new HashSet<Test>();
set.Add(test1);
var test2 = new Test();
test2.First = "a";
test2.Second = "b";
bool equal = test1 == test2; // true
bool contains = set.Contains(test2); // nope, false
事情出错 - 设置明确包含一个等于test2
的项目,但是
我们没有找到它,因为我们没有覆盖GetHashCode
(这里是一个最近的例子,说明违反这些事情时出现了严重错误:https://stackoverflow.com/a/43356217/5311735)。所以我们实施它:
public override int GetHashCode() {
unchecked {
return ((First != null ? First.GetHashCode() : 0) * 397) ^ (Second != null ? Second.GetHashCode() : 0);
}
}
现在我们的问题已修复,set.Contains
返回true。到目前为止一切都很好,但我们使用可变属性来计算哈希码。所以考虑一下:
var test1 = new Test();
test1.First = "a";
test1.Second = "b";
var set = new HashSet<Test>();
set.Add(test1);
test1.First = "c";
var contains = set.Contains(test1); // nope, false
所以我们将item添加到hashset,修改了一个属性,现在set.Contains
返回false,因为我们之前添加了几行。从我们将项目放入集合的那一刻起,以及我们调用Contains
的那一刻,哈希码已经发生了变化。
如果你不想要这样的惊喜 - 当你覆盖相等的成员并且不在那里使用可变字段\属性时总是覆盖GetHashCode
。
答案 1 :(得分:2)
如果您需要其功能,您应该删除整个GetHashCode()
Stuff或正确实现它。
如果你只是想测试平等,你就不需要它(Source):
不测试哈希码的相等性以确定两个对象是否相等。 (不等的对象可以具有相同的哈希码。)要测试相等性,请调用ReferenceEquals或Equals方法
如果您想知道何时应该实施,请考虑此link,其中还包含基本的实施指南。
GetHashCode()
中的成员发出警告一般来说GetHashCode()
在Hashtables或Dictionarys中用于 - 哦多么奇迹 - 。这就是为什么你应该确保,只要对象在一个集合中并且可能依赖于该值的东西,哈希码至少不会改变。这就是你获得Warning on not readonly members的原因。
您可以为不可变引用类型覆盖GetHashCode。通常,对于可变引用类型,只有在以下情况下才应覆盖GetHashCode:
- 您可以从不可变的字段计算哈希码;要么 - 当对象包含在依赖于其哈希码的集合中时,您可以确保可变对象的哈希码不会更改。
GetHashCode()
发出警告?我的意思是这很简单,想象如果你真的已经实现了一个全新的策略,当一个Object等于另一个时,你可能会有一个特殊的哈希码创建。
实际上,Equals Method中的代码部分确实需要GetHashCode()
的实现。因为现在不再依赖于Heap上的Object-Instance。正如@Evk指出的那样,这显然是一个比较对象的全新策略。