C# 9记录类型与集合属性:具备值语义的集合类型问询
解决C# Record中非字符串引用类型的值语义相等性问题
这个问题我之前也踩过坑!确实C# 9+的record类型默认是对每个成员用EqualityComparer<T>.Default做相等性判断,而对于非字符串的引用类型(比如int[]),默认逻辑是比引用地址——哪怕两个数组内容一模一样,只要不是同一个实例,就会被判定为不相等。
下面给你几个靠谱的解决思路,从.NET自带方案到第三方库,再到自定义实现:
一、修正ImmutableArray的用法(.NET自带)
你说试过ImmutableArray<T>没效果,大概率是用法不对。ImmutableArray<T>本身是支持值语义相等性的,只要正确使用:
- 一定要用
ImmutableArray.Create()方法创建实例,别直接用数组初始化 - record的默认相等性会自动调用
ImmutableArray<T>的内容比较逻辑
来看看示例代码:
using System.Collections.Immutable; public record SomeRecord(int SomeInt, string SomeString, ImmutableArray<int> SomeArray); var r1 = new SomeRecord(0, "test", ImmutableArray.Create(1, 2)); var r2 = new SomeRecord(0, "test", ImmutableArray.Create(1, 2)); Console.WriteLine(r1 == r2); // 这里会输出true,完美符合预期!
原理是ImmutableArray<T>的默认比较器会对比数组内容而非引用地址,所以record在做成员比较时自然会得到正确的结果。另外像ImmutableList<T>、ImmutableHashSet<T>这些不可变集合也都支持值语义,直接放进record里就行。
二、用第三方值语义集合库(适合可变场景)
如果不可变集合不符合你的需求(比如需要随时修改集合内容),可以试试专门的第三方库:
- ValueCollections(NuGet直接搜就能装):提供了
ValueArray<T>、ValueList<T>、ValueDictionary<TKey, TValue>这些值类型集合,它们天生就基于内容做相等性判断,和record的逻辑完美适配。
用起来也很简单:using ValueCollections; public record SomeRecord(int SomeInt, string SomeString, ValueArray<int> SomeArray); var r1 = new SomeRecord(0, "test", new[] {1,2}.ToValueArray()); var r2 = new SomeRecord(0, "test", new[] {1,2}.ToValueArray()); Console.WriteLine(r1 == r2); // 输出true
三、自己写个值语义包装结构体(零依赖)
要是不想引入第三方库,也可以自己撸个简单的结构体,实现IEquatable<T>来提供值语义:
public struct ValueArray<T> : IEquatable<ValueArray<T>> { private readonly T[] _array; public ValueArray(T[] array) { // 这里克隆一份数组,避免外部修改影响内部状态 _array = array?.Clone() as T[]; } public bool Equals(ValueArray<T> other) { // 用.NET自带的结构比较器来简化数组内容对比 return StructuralComparisons.StructuralEqualityComparer.Equals(_array, other._array); } public override bool Equals(object obj) { return obj is ValueArray<T> other && Equals(other); } public override int GetHashCode() { return StructuralComparisons.StructuralEqualityComparer.GetHashCode(_array); } public static bool operator ==(ValueArray<T> left, ValueArray<T> right) { return left.Equals(right); } public static bool operator !=(ValueArray<T> left, ValueArray<T> right) { return !left.Equals(right); } }
然后在record里用这个结构体就行:
public record SomeRecord(int SomeInt, string SomeString, ValueArray<int> SomeArray); var r1 = new SomeRecord(0, "test", new ValueArray<int>(new[] {1,2})); var r2 = new SomeRecord(0, "test", new ValueArray<int>(new[] {1,2})); Console.WriteLine(r1 == r2); // 输出true
内容的提问来源于stack exchange,提问作者jeroenh
相关产品推荐
相关产品推荐

