首页 > 解决方案 > Equality of records with lists in C# 9.0

问题描述

I was experimenting with the C# 9.0 features today and noticed that when a record has a List<Point2d>, then two records with the same items in the list are not equal anymore. Point2d is also a record here.

public sealed record Point2d
{
    public Point2d(double x, double y)
    {
        X = x;
        Y = y;
    }

    public double X { get; }
    public double Y { get; }
}

public sealed record Polyline2d
{
    private readonly List<Point2d> _corners;

    public Polyline2d(IEnumerable<Point2d> corners)
    {
        _corners = new List<Point2d>(corners);
    }
}

        Polyline2d a = new (new Point2d[] { new(0, 0), new(1, 1), new(2, 2)});
        Polyline2d b = new (new Point2d[] { new(0, 0), new(1, 1), new(2, 2) });

        a == b => false

If I manually add the equality methods for the record and redefine the Equals() method then everything is fine again:

    public bool Equals(Polyline2d other)
    {
        return other != null && _corners.SequenceEqual(other._corners);
    }

    public override int GetHashCode()
    {
        return 42;
    }


    a == b => true

Is this the correct approach for addressing this problem, or are there other considerations of record data types that I need to factor in?

标签: c#recordequalityc#-9.0c#-record-type

解决方案


记录的自动生成的相等比较对所有字段使用默认的相等比较器(从一直以来,值类型也是如此,顺便说一句)。并且默认的相等比较器List<T>只是引用相等。所以它或多或少相当于:

public bool Equals(Polyline2d other)
{
    return other != null && _corners.Equals(other._corners);
}

没有什么意外的,你的解决方案是正确的——如果你想SequenceEqual使用你需要Equals自己定义。另请注意,您不需要重新定义GetHashCode,即使您指定了自己的版本,编译器仍会生成自己的版本Equals(但自动生成GetHashCode的仍将使用List<T>的默认比较器GetHashCode)。 这是一些糟糕的建议,感谢 Jeremy Lakeman 指出这一点。如果GetHashCode为两个实例返回不同的值,则Equals必须为它们返回 false,因此您实际上确实需要覆盖GetHashCode,以便序列相等的列表不会返回不同的哈希码,默认实现就是这种情况。


免责声明:综合中的逻辑Equals比这复杂得多,因为它实际上使用EqualityComparer<List<Point2d>>.Default并考虑了记录继承,但在这种情况下无关紧要。


推荐阅读