使用对象的HashSet检查相等性

本文关键字:检查 HashSet 对象 | 更新日期: 2023-09-27 18:26:53

我正在尝试将Definition类型的两个哈希集与EqualityComparer<T>.Default.Equals(value, oldValue)进行比较。Definition定义如下

public class Definition
{
    public string Variable { get; set; }
    public HashSet<Location> LocationList { get; set; }
    public override bool Equals(object obj)
    {
        Definition other = obj as Definition;
        return other.Variable.Equals(this.Variable) && other.LocationList!= null &&this.LocationList != null
            && other.LocationList.Count == this.LocationList.Count
            && other.LocationList == this.LocationList;
    }
    public override int GetHashCode()
    {
        return this.Variable.GetHashCode() ^ this.LocationList.Count.GetHashCode();// ^ this.LocationList.GetHashCode();
    }
}
public class Location
{
    public int Line { get; set; }
    public int Column { get; set; }
    public int Position { get; set; }
    public string CodeTab { get; set; }
    public Location(int line, int col, int pos, string tab)
    {
        Line = line;
        Column = col;
        Position = pos;
        CodeTab = tab;
    }
    public override bool Equals(object obj)
    {
        Location other = obj as Location;
        return this.CodeTab == other.CodeTab
            && this.Position == other.Position
            && this.Column == other.Column
            && this.Line == other.Line;
    }
    public override int GetHashCode()
    {
        return this.CodeTab.GetHashCode() ^ this.Position.GetHashCode() 
            ^ this.Column.GetHashCode() ^ this.Line.GetHashCode();
    }
}

不知何故,对于类似的集合,尽管所有信息都保持不变,但结果还是作为false返回。唯一的区别是一些元素的位置是互换的,但我知道HashSet在比较时不会保留或检查顺序。有人能告诉我这里出了什么问题吗?

PS:我也尝试过取消对this.LocationList.GetHashCode()的注释,但没有成功。

使用对象的HashSet检查相等性

您需要为集合创建一个比较器:

var setComparer = HashSet<Location>.CreateSetComparer();
return other.Variable.Equals(this.Variable) && setComparer.Equals(this.LocationList, other.LocationList);

EqualityComparer<T>.Default将查找实现IEquatable<T>的对象。否则,它将服从ObjectEqualityComparer,后者只是检查引用相等性。这就是为什么在比较引用时会看到错误的原因。

您实际想要做的是显式地实现IEquatable<Location>。注意,你应该真正让你的属性不可变,这样才能正常工作:

public class Location : IEquatable<Location>
{       
    public Location(int line, int col, int pos, string tab)
    {
        Line = line;
        Column = col;
        Position = pos;
        CodeTab = tab;
    }
    public int Line { get; private set; }
    public int Column { get; private set; }
    public int Position { get; private set; }
    public string CodeTab { get; private set; }
    public bool Equals(Location other)
    {
        if (ReferenceEquals(null, other)) return false;
        if (ReferenceEquals(this, other)) return true;
        return string.Equals(CodeTab, other.CodeTab) && Column == other.Column && Line == other.Line && Position == other.Position;
    }
    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        if (obj.GetType() != this.GetType()) return false;
        return Equals((Location) obj);
    }
    public static bool operator ==(Location left, Location right)
    {
        return Equals(left, right);
    }
    public static bool operator !=(Location left, Location right)
    {
        return !Equals(left, right);
    }
    public override int GetHashCode()
    {
        unchecked
        {
            var hashCode = (CodeTab != null ? CodeTab.GetHashCode() : 0);
            hashCode = (hashCode*397) ^ Column;
            hashCode = (hashCode*397) ^ Line;
            hashCode = (hashCode*397) ^ Position;
            return hashCode;
        }
    }
}

现在,如果您查看由Default创建的类型EqualityComparer,您将看到GenericEqualityComparer<Location>:

Console.WriteLine(EqualityComparer<Location>.Default.GetType())