C# 9.0 中记录与列表的相等性
Equality of records with lists in C# 9.0
我今天在试验 C# 9.0 功能时注意到,当 record
有 List<Point2d>
时,列表中具有相同项目的两条记录不再相等。 Point2d
这里也是record
。
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
如果我手动添加 record
的相等方法并重新定义 Equals()
方法,那么一切都很好:
public bool Equals(Polyline2d other)
{
return other != null && _corners.SequenceEqual(other._corners);
}
public override int GetHashCode()
{
return 42;
}
a == b => true
这是解决此问题的正确方法吗,还是我需要考虑其他有关记录数据类型的注意事项?
自动生成的记录相等比较对所有字段使用默认相等比较器(顺便说一句,值类型一直以来都是如此)。 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
return 两个实例的不同值然后 Equals
对它们必须 return false,所以你实际上需要重写 GetHashCode
以便序列相等列表做不是 return 不同的哈希码,默认实现就是这种情况。
免责声明:合成Equals
中的逻辑比这复杂得多,因为它实际上使用EqualityComparer<List<Point2d>>.Default
并考虑了记录继承,但在这种情况下无关紧要。
我今天在试验 C# 9.0 功能时注意到,当 record
有 List<Point2d>
时,列表中具有相同项目的两条记录不再相等。 Point2d
这里也是record
。
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
如果我手动添加 record
的相等方法并重新定义 Equals()
方法,那么一切都很好:
public bool Equals(Polyline2d other)
{
return other != null && _corners.SequenceEqual(other._corners);
}
public override int GetHashCode()
{
return 42;
}
a == b => true
这是解决此问题的正确方法吗,还是我需要考虑其他有关记录数据类型的注意事项?
自动生成的记录相等比较对所有字段使用默认相等比较器(顺便说一句,值类型一直以来都是如此)。 List<T>
的默认相等比较器只是引用相等。所以它或多或少等同于:
public bool Equals(Polyline2d other)
{
return other != null && _corners.Equals(other._corners);
}
没有意外,您的解决方案是正确的 - 如果您想使用 SequenceEqual
,您需要自己定义 Equals
。 另请注意,您无需重新定义 这是一些糟糕的建议,感谢 Jeremy Lakeman 指出。 如果 GetHashCode
,即使您指定了自己的 Equals
,编译器仍会生成自己的版本(但自动生成的 GetHashCode
仍将使用 List<T>
的默认比较器 GetHashCode
)。GetHashCode
return 两个实例的不同值然后 Equals
对它们必须 return false,所以你实际上需要重写 GetHashCode
以便序列相等列表做不是 return 不同的哈希码,默认实现就是这种情况。
免责声明:合成Equals
中的逻辑比这复杂得多,因为它实际上使用EqualityComparer<List<Point2d>>.Default
并考虑了记录继承,但在这种情况下无关紧要。