.NET是否有官方标准的、采用值相等而非引用相等的数组类?
如何在增量生成中实现数组的值相等判断?
纠正ImmutableArray的误解
你之前测试ImmutableArray
- ImmutableArray
的实例Equals方法和静态ImmutableArray.Equals方法是基于值相等判断的,会逐元素比较。 - 而
==运算符默认是引用比较(因为ImmutableArray是结构,但它的 ==被重载为引用比较内部的数组实例)。
示例代码验证:
var arr1 = ImmutableArray.Create(new MyRecord { Id = 1 }, new MyRecord { Id = 2 }); var arr2 = ImmutableArray.Create(new MyRecord { Id = 1 }, new MyRecord { Id = 2 }); Console.WriteLine(arr1.Equals(arr2)); // 输出 True(值相等) Console.WriteLine(ImmutableArray.Equals(arr1, arr2)); // 输出 True(值相等) Console.WriteLine(arr1 == arr2); // 输出 False(引用比较内部数组)
在IIncrementalGenerator的缓存逻辑中,只要你使用ImmutableArray的相等性判断逻辑(比如在增量提供器中指定对应的比较器),就能正确实现记忆化。
公认的实现方案
1. 优先使用ImmutableArray(推荐)
ImmutableArray
- 将所有中间数据用ImmutableArray
存储,确保元素类型(比如你的Record)本身支持值相等。 - 在IncrementalValueProvider中,使用
WithComparer(ImmutableArray<T>.ValueComparer)来明确指定值相等比较逻辑,确保缓存能正确识别数组变化。
2. 为原生数组自定义相等比较器
如果坚持使用原生T[],无需编写包装类,只需实现一个通用的IEqualityComparer<T[]>,在缓存/记忆化环节注入即可:
public class ArrayValueEqualityComparer<T> : IEqualityComparer<T[]> { public static readonly ArrayValueEqualityComparer<T> Instance = new(); public bool Equals(T[]? x, T[]? y) { if (ReferenceEquals(x, y)) return true; if (x is null || y is null) return false; if (x.Length != y.Length) return false; var elementComparer = EqualityComparer<T>.Default; for (int i = 0; i < x.Length; i++) { if (!elementComparer.Equals(x[i], y[i])) return false; } return true; } public int GetHashCode(T[]? obj) { if (obj is null) return 0; var hash = new HashCode(); foreach (var item in obj) { hash.Add(item); } return hash.ToHashCode(); } }
在IIncrementalGenerator中使用:
// 假设你有一个返回T[]的增量提供器 var arrayProvider = context.SomeSource() .Select((data, ct) => data.ToArray()) .WithComparer(ArrayValueEqualityComparer<MyRecord>.Instance);
这种方案无需修改数组类型,仅在需要比较的环节注入逻辑,避免了包装类的方法遗漏问题。
3. 固定长度数组用ValueTuple替代
如果你的数组长度固定(比如总是2个元素),可以用ValueTuple(如(T, T))代替数组。ValueTuple是值类型,默认基于元素值相等判断,完全符合你的需求,且无需额外代码。
关键注意点
- 不要自己编写数组包装类:手动包装容易遗漏接口实现(如
IList<T>、ICollection<T>)或方法透传,导致使用时出现意外行为,增加维护成本。 - 确保元素类型支持值相等:无论使用哪种方案,数组中的元素(比如你的Record)必须正确实现值相等(Record默认实现,自定义类需重写
Equals和GetHashCode),否则数组的整体值相等判断会失效。
内容的提问来源于stack exchange,提问作者PreventRage
相关产品推荐
相关产品推荐

