有问题的List
#pragma warning disable CA1825, IDE0300 // avoid the extra generic instantiation for Array.Empty<T>()
private static readonly T[] s_emptyArray = new T[0];
#pragma warning restore CA1825, IDE0300
例如,当调用
List<T>.ToArray()
并且列表为空时,将使用此缓存的空数组。
现在,为什么存在空数组的 custom 缓存,以及为什么
Array.Empty<T>()
被视为需要避免的“额外通用实例化”?难道在运行时,如果 Array.Empty<T>()
在其他地方使用,s_emptyArray
会成为“额外”实例化吗,因为现在有两个缓存的空实例?或者这是为了避免 Array.Empty<T>()
产生的一些性能影响,它委托给包含实际空数组的静态类 EmptyArray<T>
?这种通用访问(我不会像代码注释那样将其称为“实例化”)对性能的影响是否足以证明在运行时拥有自定义缓存的空数组和可能的两个空数组实例是合理的?我们是否也应该考虑在高性能场景中创建和使用我们自己的缓存空数组?
这不仅避免了对
Array.Empty<T>()
的泛型调用,而且还为每个 EmptyArray<T>
创建了一个新的泛型类型 List<T>
。由于运行时引入的每个新泛型类型都会产生少量开销(JIT 和内存使用),因此这种开销可能会累积,因为 List'1
极其广泛地用于许多不同的 T
。
这可以避免在不需要时为相关类型参数
EmptyArray<T>
创建泛型类型 T
。
我认为这不会对性能产生任何影响。但它在程序集中保存了几个字节的元数据,并为每个
T
保存了一个 Type 实例。看起来像是一种微优化,只有在像List<T>
这样极其常用的类型中才合理。