Search code examples
c#.net.net-corebase-class-library

Why List<T> uses its own cached empty array instead of Array.Empty<T>?


The List<T> code in question is:

#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

This cached empty array is used, for instance, when List<T>.ToArray() is called and the list is empty.

Now, why does this custom caching of an empty array exist, and why is Array.Empty<T>() seen as an "extra generic instantiation" that needs to be avoided? Wouldn't it be the case that, during runtime, if Array.Empty<T>() is used elsewhere, s_emptyArray would become the "extra" instantiation because there would now be two cached empty instances? Or is this about avoiding some performance impact arising from Array.Empty<T>(), which delegates to the static class EmptyArray<T> that contains the actual empty array? Is the performance impact of this generic access (I wouldn't quite call it "instantiation," as the code comment does) significant enough to justify having a custom cached empty array and potentially two instances of empty arrays during runtime? Should we also consider creating and using our own cached empty arrays in high-performance scenarios?


Solution

  • This not only avoids the generic call to Array.Empty<T>(), but also creating a new generic type EmptyArray<T> for every List<T>. Since every new generic type introduced at runtime comes with a small overhead (JIT and memory usage) this can accumulate, because List'1 is extremely widely used for many different T's.