Home > Back-end >  C# HashSet not using defined IEqualityComparer<>
C# HashSet not using defined IEqualityComparer<>

Time:04-02

I am trying to compare two HashSets of objects by using .SetEquals(). I looked at some guides and saw that I needed to implement IEqualityComparer<> as part of the class in order for it to work properly. However, I have found that .SetEquals() is failing when I use it.

I have verified that the data I am attempting to compare is the same by using Lists and comparing them one element at a time. I am suspecting that I have implemented the IEqualityComparer<> incorrectly and it's using the default Equals() and GetHashCode().

Here's the code I am using:

public class MemberDataRow : IEqualityComparer<MemberDataRow>
{
    public string ID { get; }
    public string FirstName { get; }
    public string MI { get; }
    public string LastName { get; }
    public decimal Premium { get; }
    public string MemberCount { get; }
    public string RateCell { get; }
    public string RateCellDescription { get; }
    public string Month { get; }
    public bool Kickback { get; }
    public decimal PrivateBalance { get; }
    public decimal StateBalance { get; }
    public decimal PrivateAssets { get; }
    public decimal StateAssets { get; }
    public decimal MUG { get; }
    public DateTime Date { get; }
 
    // Constructor Stuff is here. Removed for easier reading

    bool IEqualityComparer<MemberDataRow>.Equals(MemberDataRow? x, MemberDataRow? y)
    {
        if ((x.ID == y.ID) &&
            (x.FirstName == y.FirstName) &&
            (x.MI == y.MI) &&
            (x.LastName == y.LastName) &&
            (x.Premium == y.Premium) &&
            (x.MemberCount == y.MemberCount) &&
            (x.RateCell == y.RateCell) &&
            (x.RateCellDescription == y.RateCellDescription) &&
            (x.Month == y.Month) &&
            (x.Kickback == y.Kickback) &&
            (x.PrivateBalance == y.PrivateBalance) &&
            (x.PrivateAssets == y.PrivateAssets) &&
            (x.StateBalance == y.StateBalance) &&
            (x.StateAssets == y.StateAssets) &&
            (x.MUG == y.MUG) &&
            (x.Date == y.Date))
            return true;
        else
            return false;
    }

    int IEqualityComparer<MemberDataRow>.GetHashCode(MemberDataRow obj)
    {
        // HashCode.Combine() only allows 7 arguments, so we have to chain them together.
        return HashCode.Combine(
            HashCode.Combine(obj.ID, obj.FirstName, obj.MI, obj.LastName, obj.Premium, obj.MemberCount, obj.RateCell),
            HashCode.Combine(obj.RateCellDescription, obj.Month, obj.Kickback, obj.PrivateBalance, obj.PrivateAssets, obj.StateBalance),
            HashCode.Combine(obj.StateAssets, obj.MUG, obj.Date));
        }
    }

CodePudding user response:

I think what you want to do here is implement IEquatable<MemberDataRow>. The interface that you're implementing now is meant to be implemented by a separate "comparer" class that you pass in the constructor of a HashSet object instead.

See also here.

CodePudding user response:

If you're going to define Equals and GetHashCode by using every property of the object to calculate them, you might as well just switch to using a record:

public record MemberDataRow(
  string ID,
  string FirstName,
  string MI,
  string LastName,
  decimal Premium,
  string MemberCount,
  string RateCell,
  string RateCellDescription,
  string Month,
  bool Kickback,
  decimal PrivateBalance,
  decimal StateBalance,
  decimal PrivateAssets,
  decimal StateAssets,
  decimal MUG, 
  DateTime Date
);

records automatically override Equals and GetHashCode based on all constituent properties, they're immutable when declared thus and a lot cleaner/less to type. They have a constructor you can call to set all props in a compact fashion

  • Related