Array-Empty
Array.Empty<T>()对每个封闭泛型类型只缓存一个共享实例,重复调用返回引用相等的同一个数组;这是 CLR 泛型静态类带来的实现行为,官方 API 文档只写了 “Returns an empty array”,完全没提这一层。
原文链接
- Array.Empty
() Method - .NET | Microsoft Learn - 测试用例:
Tests.Wpf.Tests/ArrayEmptyTest.cs
官方文档没写什么
Learn 上的 API 页面非常简短:方法说明只有一句 “Returns an empty array.”,返回值描述也只有 “An empty array.”,连 Remarks 段都没有。没有提到:
- 返回的是缓存共享的单例,不是每次新分配的数组;
- 同一类型参数下两次调用
ReferenceEquals成立; - 调用方共享同一实例这一契约隐含的”不要试图修改返回数组”约束。
这个行为只能从源码和测试里确认。
测试用例
[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>(),照建议写会无限递归。