Posts

Array-Empty

Array.Empty<T>() 对每个封闭泛型类型只缓存一个共享实例,重复调用返回引用相等的同一个数组;这是 CLR 泛型静态类带来的实现行为,官方 API 文档只写了 “Returns an empty array”,完全没提这一层。

原文链接

官方文档没写什么

Learn 上的 API 页面非常简短:方法说明只有一句 “Returns an empty array.”,返回值描述也只有 “An empty array.”,连 Remarks 段都没有。没有提到:

这个行为只能从源码和测试里确认。

测试用例

[TestClass]
public sealed class ArrayEmptyTest
{
    [TestMethod]
    public void SameTypeReturnsSharedInstance()
    {
        var a = Array.Empty<int>();
        var b = Array.Empty<int>();

        Assert.IsTrue(ReferenceEquals(a, b));
    }

    [TestMethod]
    public void SameReferenceTypeReturnsSharedInstance()
    {
        var a = Array.Empty<string>();
        var b = Array.Empty<string>();

        Assert.IsTrue(ReferenceEquals(a, b));
    }

    [TestMethod]
    public void DifferentTypesReturnDifferentInstances()
    {
        var ints = Array.Empty<int>();
        var strings = Array.Empty<string>();

        Assert.IsFalse(ReferenceEquals(ints, strings));
    }

    [TestMethod]
    public void EmptyArrayIsEmpty()
    {
        Assert.HasCount(0, Array.Empty<int>());
    }
}

四个测试覆盖的行为可以总结成一张表:

调用结果
Array.Empty<int>() 多次调用返回同一个 int[] 实例(引用相等)
Array.Empty<string>() 多次调用返回同一个 string[] 实例(引用相等)
Array.Empty<int>() 与 Array.Empty<string>()不同实例(不同封闭泛型类型各自缓存)
数组长度恒为 0

runtime 源码

行为来自 runtime 源码 中一个嵌套的泛型静态类:

private static class EmptyArray<T>
{
#pragma warning disable CA1825, IDE0300 // this is the implementation of Array.Empty<T>()
    internal static readonly T[] Value = new T[0];
#pragma warning restore CA1825, IDE0300
}

public static T[] Empty<T>()
{
    return EmptyArray<T>.Value;
}

关键在 CLR 对封闭泛型类型的实例化规则:EmptyArray<int> 和 EmptyArray<string> 是两个不同的封闭类型,各自的 Value 静态字段只初始化一次。于是每个 T 天然得到一份零长度数组单例,Array.Empty<T>() 每次只是把这个字段读出来返回。

#pragma 压制的是 CA1825 / IDE0300 分析器建议——实现本身就是 Array.Empty<T>(),照建议写会无限递归。

ref