ReSharper Equality成员生成Hashcode警告

时间:2017-04-12 10:35:34

标签: c# visual-studio refactoring resharper equals

自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);

如果删除GetHashCode()功能,这是R#给出的警告: The warning R# gives me

这是它给我GetHashCode()功能的警告: The other warning from R#

2 个答案:

答案 0 :(得分:5)

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

public class Test {
    public string First { get; set; }
    public string Second { get; set; }
}

现在,如果TestFirst属性相等,则确定两个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指出的那样,这显然是一个比较对象的全新策略