简体   繁体   中英

How do you write a GetHashCode method for an object made of a string and a collection of int32?

There is a class of Products:

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;
    }
}

I want to write an IEqualityComparer for this (i already have one for Feature).

The one for Feature is like this:

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;
    }
}

And what i wrote so far on the other one is this:

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)
            {
    
            }
        }

I can't find an answer anywhere about this. Does anybody know how to write it?

Two ProductWithFeatures s are equal if they have the same name, and have the same features in the same order.

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;
    }
}

This is a simple approach, which can be improved in a number of ways.

First, let's give our FeatureComparer a Default property, so we don't need to keep creating new instances:

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

This lets us write:

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;
    }
}

We're also not handling the case where our methods are passed null , or the name of a feature is null , so let's deal with those. We can also test whether x and y are the same object in Equals .

We'll also do the integer operations in an unchecked block in case it overflows (and the assembly is compiled with /checked ).

Note that we use ReferenceEquals instead of == , in case you end up implementing the == operator in your types.

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;
            }
        }
    }
}

It's really up to you. I personally would go for something like

public int GetHashCode( ProductWithFeatures obj )
{
    string toHash = obj.Name;
    foreach( var feature in obj.Features )
        toHash += feature.GetHashCode();

    return toHash.GetHashCode();
}

It's not the nicest code ever, but it does what it's supposed to do.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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