简体   繁体   English

实现正确的GetHashCode

[英]Implementing correct GetHashCode

I have the following class 我有以下课程

public class ResourceInfo
{
    public string Id { get; set; }
    public string Url { get; set; }
}

which contains information about some resource. 其中包含有关某些资源的信息。 Now I need the possibility to check if two such resources are equal by the following scenario (I`ve implemented IEquatable interface) 现在我需要通过以下方案检查两个这样的资源是否相等(我实现了IEquatable接口)

public class ResourceInfo : IEquatable<ResourceInfo>
{
    public string Id { get; set; }
    public string Url { get; set; }

    public bool Equals(ResourceInfo other)
    {
        if (other == null)
            return false;

        // Try to match by Id
        if (!string.IsNullOrEmpty(Id) && !string.IsNullOrEmpty(other.Id))
        {
            return string.Equals(Id, other.Id, StringComparison.InvariantCultureIgnoreCase); 
        }

        // Match by Url if can`t match by Id
        return string.Equals(Url, other.Url, StringComparison.InvariantCultureIgnoreCase);
    }
}

Usage: oneResource.Equals(otherResource) . 用法: oneResource.Equals(otherResource) And everything is just fine. 一切都很好。 But some time have passed and now I need to use such eqaulity comparing in some linq query. 但是一段时间过去了,现在我需要在一些linq查询中使用这样的eqaulity比较。

As a result I need to implement separate Equality comparer which looks like this: 因此,我需要实现单独的Equality comparer,如下所示:

class ResourceInfoEqualityComparer : IEqualityComparer<ResourceInfo>
{
    public bool Equals(ResourceInfo x, ResourceInfo y)
    {
        if (x == null || y == null)
            return object.Equals(x, y);

        return x.Equals(y);
    }

    public int GetHashCode(ResourceInfo obj)
    {
        if (obj == null)
            return 0;

        return obj.GetHashCode();
    }
}

Seems to be ok: it makes some validation logic and uses the native equality comparing logic. 似乎没问题:它产生一些验证逻辑并使用本机相等比较逻辑。 But then I need to implement GetHashCode method in the ResourceInfo class and that is the place where I have some problem. 但后来我需要在ResourceInfo类中实现GetHashCode方法,这就是我遇到问题的地方。

I don`t know how to do this correctly without changing the class itself. 我不知道如何在不改变类本身的情况下正确地做到这一点。

At first glance, the following example can work 乍一看,以下示例可以正常工作

public override int GetHashCode()
{
    // Try to get hashcode from Id
    if(!string.IsNullOrEmpty(Id))
        return Id.GetHashCode();
    // Try to get hashcode from url
    if(!string.IsNullOrEmpty(Url))
        return Url.GetHashCode();

    // Return zero
    return 0;
}

But this implementation is not very good. 但这种实施并不是很好。

GetHashCode should match the Equals method : if two objects are equal, then they should have the same hashcode, right? GetHashCode应该与Equals方法匹配:如果两个对象相等,那么它们应该具有相同的哈希码,对吧? But my Equals method uses two objects to compare them. 但我的Equals方法使用两个对象来比较它们。 Here is the usecase, where you can see the problem itself: 这是用例,您可以在其中查看问题本身:

var resInfo1 = new ResourceInfo()
{
    Id = null,
    Url = "http://res.com/id1"
};
var resInfo2 = new ResourceInfo()
{
    Id = "id1",
    Url = "http://res.com/id1"
};

So, what will happen, when we invoke Equals method: obviously they will be equal, because Equals method will try to match them by Id and fail, then it tries matching by Url and here we have the same values. 那么,当我们调用Equals方法时会发生什么:显然它们将是相同的,因为Equals方法将尝试通过Id匹配它们并失败,然后它尝试通过Url进行匹配,这里我们有相同的值。 As intended. 如预期。

resInfo1.Equals(resInfo1 ) -> true

But then, if they are equal, they should have the same hash codes: 但是,如果它们相等,它们应该具有相同的哈希码:

var hash1 = resInfo.GetHashCode(); // -263327347
var hash2 = resInfo.GetHashCode(); // 1511443452

hash1.GetHashCode() == hash2.GetHashCode() -> false

Shortly speaking, the problem is that Equals method decides which field to use for equality comparing by looking at two different objects, while GetHashCode method have access only to one object. 简而言之,问题是Equals方法通过查看两个不同的对象来决定使用哪个字段进行相等比较,而GetHashCode方法只能访问一个对象。

Is there a way to implement it correctly or I just have to change my class to avoid such situations? 有没有办法正确实现它或我只需要改变我的类来避免这种情况?

Many thanks. 非常感谢。

Your approach to equality fundamentally breaks the specifications in Object.Equals . 您的平等方法从根本上打破了Object.Equals的规范。

In particular, consider: 特别要考虑:

var x = new ResourceInfo { Id = null, Uri = "a" };
var y = new ResourceInfo { Id = "yz", Uri = "a" };
var z = new ResourceInfo { Id = "yz", Uri = "b" };

Here, x.Equals(y) would be true, and y.Equals(z) would be true - but x.Equals(z) would be false. 这里, x.Equals(y)将为真, y.Equals(z)将为真 - 但x.Equals(z)将为false。 That is specifically prohibited in the documentation: 文档中明确禁止这样做:

  • If (x.Equals(y) && y.Equals(z)) returns true, then x.Equals(z) returns true. 如果(x.Equals(y) && y.Equals(z))返回true,则x.Equals(z)返回true。

You'll need to redesign, basically. 基本上你需要重新设计。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM