You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 04:46:21