首页 > 解决方案 > 如何为由字符串和 int32 集合组成的对象编写 GetHashCode 方法?

问题描述

有一类产品:

public class ProductWithFeatures
{
    public string Name { get; set; }
    public ICollection<Feature> Features { get; set; }
}

public class Feature
{
    public int Id { get; set; }
    
    public Feature(int Id)
    {
        this.Id = Id;
    }
}

我想为此编写一个 IEqualityComparer(我已经有一个用于功能的)。

Feature 是这样的:

public class FeatureComparer : IEqualityComparer<Feature>
{
    public bool Equals(Feature x, Feature y)
    {
        return x.Id == y.Id;
    }

    public int GetHashCode(Feature obj)
    {
        return obj.Id;
    }
}

到目前为止,我在另一个上写的是这样的:

public class ProductComparer : IEqualityComparer<LinqHomework.ProductWithFeatures>
        {
            public bool Equals(ProductWithFeatures x, ProductWithFeatures y)
            {
                return x.Name == y.Name && LinqHomework.FeatureComparer.Equals(x.Features, y.Features);
            }

            public int GetHashCode(ProductWithFeatures obj)
            {
    
            }
        }

我在任何地方都找不到关于这个的答案。有人知道怎么写吗?

标签: c#gethashcode

解决方案


如果两个ProductWithFeaturess 具有相同的名称,并且按相同的顺序具有相同的特征,则它们是相等的。

public class ProductComparer : IEqualityComparer<LinqHomework.ProductWithFeatures>
{
    public bool Equals(ProductWithFeatures x, ProductWithFeatures y)
    {
        return x.Name == y.Name && x.Features.SequenceEqual(y.Features, new LinqHomework.FeatureComparer());
    }

    public int GetHashCode(ProductWithFeatures obj)
    {
        int hash = obj.Name.GetHashCode();
        var featureComparer = new LinqHomework.FeatureComparer();
        foreach (var feature in obj.Features)
        {
            hash = hash * 23 + featureComparer.GetHashCode(feature);
        }
        return hash;
    }
}

这是一种简单的方法,可以通过多种方式进行改进。

首先,让我们给我们FeatureComparer一个Default属性,所以我们不需要继续创建新实例:

public class FeatureComparer : IEqualityComparer<Feature>
{
    public static FeatureComparer Default { get; } = new FeatureComparer();
    // ... as before
}

这让我们可以写:

public class ProductComparer : IEqualityComparer<LinqHomework.ProductWithFeatures>
{
    public bool Equals(ProductWithFeatures x, ProductWithFeatures y)
    {
        return x.Name == y.Name && x.Features.SequenceEqual(y.Features, LinqHomework.FeatureComparer.Default);
    }

    public int GetHashCode(ProductWithFeatures obj)
    {
        int hash = obj.Name.GetHashCode();
        foreach (var feature in obj.Features)
        {
            hash = hash * 23 + LinqHomework.FeatureComparer.Default.GetHashCode(feature);
        }
        return hash;
    }
}

我们也没有处理我们的方法被传递的情况null,或者特性的名称是null,所以让我们来处理这些。我们还可以测试x和是否y是同一个对象Equals

我们还将在一个unchecked块中执行整数运算,以防它溢出(并且程序集是用 编译的/checked)。

请注意,我们使用ReferenceEquals而不是==,以防您最终==在类型中实现运算符。

public class ProductComparer : IEqualityComparer<LinqHomework.ProductWithFeatures>
{
    public bool Equals(ProductWithFeatures x, ProductWithFeatures y)
    {
        if (ReferenceEquals(x, y))
            return true;
        if (ReferenceEquals(x, null) || ReferenceEquals(y, null))
            return false;

        if (x.Name != y.Name)
            return false;

        if (ReferenceEquals(x.Features, y.Features))
            return true;
        if (ReferenceEquals(x.Features, null) || ReferenceEquals(y.Features, null))
            return false;
        if (!x.Features.SequenceEquals(y.Features, LinqHomework.FeatureComparer.Default))
            return false;

        return true;
    }

    public int GetHashCode(ProductWithFeatures obj)
    {
        if (ReferenceEquals(obj, null))
            return 0;

        unchecked
        {
            int hash = obj.Name?.GetHashCode() ?? 0;
            if (!ReferenceEquals(obj.Features, null))
            {
                foreach (var feature in obj.Features)
                {
                    hash = hash * 23 + LinqHomework.FeatureComparer.Default.GetHashCode(feature);
                }
                return hash;
            }
        }
    }
}

推荐阅读