C# 9.0中含列表成员的record相等性判断问题及解决方法咨询
问题解答
现有解决方案是否正确?
C# record默认的值相等判断逻辑,会对类型的所有字段依次调用默认Equals方法做比较。List<T>作为引用类型,默认Equals实现仅比较实例引用是否相同,不会校验集合内部的元素值,因此当两个Polyline2d实例持有不同的List<Point2d>实例时,就算集合内部元素完全一致,默认的相等判断也会返回false。
你提供的修复方案中:
Equals的实现逻辑是合理的:SequenceEqual会按顺序对比两个集合的每个元素,而Point2d本身是record类型,会基于值语义做相等判断,因此可以正确识别内容一致的集合。- 但
GetHashCode直接返回固定值42的实现是严重错误的:Dictionary、HashSet等依赖哈希码的集合类型,会通过哈希码快速定位元素,如果所有Polyline2d实例的哈希码完全相同,会导致100%的哈希碰撞,集合操作性能会退化至线性遍历级别,极端场景下还会引发逻辑错误。
正确的GetHashCode实现参考:
public override int GetHashCode() { var hashCode = new HashCode(); foreach (var corner in _corners) { hashCode.Add(corner); } return hashCode.ToHashCode(); }
使用record的注意事项
- 引用类型成员的相等性逻辑:如果record包含集合、自定义引用类型的成员,默认相等判断只会比较引用地址,不会做深度内容对比。如果需要值语义的深度相等,必须自行重写
Equals方法实现对应逻辑。 Equals和GetHashCode必须配套修改:只要重写了Equals方法,就必须同步重写GetHashCode,保证两个相等的实例返回的哈希码完全一致,同时要尽量降低不同实例的哈希碰撞概率,不能返回固定值。- 配合不可变性使用:record的设计初衷是承载不可变数据,如果record包含可变成员(比如可增删元素的
List<T>),一旦成员内容被修改,实例的哈希码也会发生变化,会导致已经存入HashSet、Dictionary的实例无法被正确找到。建议对集合类成员使用ImmutableList<T>等不可变集合类型,避免意外修改。 - with表达式是浅拷贝:record的
with语法生成的拷贝是浅拷贝,引用类型成员只会拷贝引用地址,新实例和原实例会共享同一个引用对象。如果需要深度拷贝,需要自行实现拷贝逻辑。 - 避免和class的相等性逻辑混用:record默认是值相等,class默认是引用相等,二者互相转换或者做相等判断时要注意逻辑差异,避免出现预期外的结果。
内容的提问来源于stack exchange,提问作者Vahid
相关产品推荐
相关产品推荐

