ReSharper Equality成员生成Hashcode警告

Met*_*lon 2 c# resharper refactoring equals visual-studio

自R#2017.1.1起,我在自动生成的GetHashCode()函数中收到警告.让我解释一下如何创建这个函数:

如果覆盖Equals类的功能,R#建议您创建相等的成员.如果让R#生成那些相等的成员,它GetHashCode()也会覆盖该函数.然而,在那里它使用我的类的所有属性.由于这些属性并非都是只读的,因此R#告诉我应该只读取它们并显示警告.

所以我的问题是我应该将GetHashCode()函数留空(或删除那些带警告的部分)或者我应该尝试将属性设置为只读,以便R#不再警告我了.

这是我的代码:

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);
Run Code Online (Sandbox Code Playgroud)

这是R#给我的警告,如果删除该GetHashCode()功能: 警告R#给了我

这是它给我的GetHashCode()功能警告: 来自R#的另一个警告

Evk*_*Evk 5

我认为最好解释一下为什么GetHashCode在覆盖相等成员时应该覆盖的原因,以及为什么在计算哈希代码时不应该使用可变字段\属性.考虑这个简单的类:

public class Test {
    public string First { get; set; }
    public string Second { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

现在你决定两个Test对象是否相等,如果它们FirstSecond属性相等.您生成相等成员但不覆盖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);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后你会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
Run Code Online (Sandbox Code Playgroud)

事情出错了 - 设置明确包含一个等于的项目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);
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我们的问题已修复并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
Run Code Online (Sandbox Code Playgroud)

所以我们将item添加到hashset,修改了一个属性,现在set.Contains为我们之前添加几行的完全相同的项返回false.从我们将项目放入集合和我们调用的那一刻起,哈希码就已经发生了变化Contains.

如果你不想要这样的惊喜 - GetHashCode在覆盖相等成员时总是覆盖,并且不要在那里使用可变字段\属性.