Console.WriteLine((object) new string(' ', 0) == (object) new string(' ', 0));
打印true
,这表明CLR保留空字符串并重用相同的实例。(对于0
以外的任何数字,显示false
)
但是,对于数组则不是这样:
Console.WriteLine(new int[0] == new int[0]); // False
现在,如果我们看一下Enumerable.Empty<T>()
的实现,我们发现它缓存和重用空数组:
public static IEnumerable<TResult> Empty<TResult>()
{
return EmptyEnumerable<TResult>.Instance;
}
[...]
public static IEnumerable<TElement> Instance
{
get
{
if (EmptyEnumerable<TElement>.instance == null)
EmptyEnumerable<TElement>.instance = new TElement[0];
return EmptyEnumerable<TElement>.instance;
}
}
所以框架团队认为为每种类型保留一个空数组是值得的。CLR可以,如果它想的话,走得更远一点,在本地做这件事,这样它不仅适用于对Enumerable.Empty<T>()
的调用,也适用于new T[0]
。如果Enumerable.Empty<T>()
的优化是值得的,那么这个肯定会更值得?
为什么CLR不这样做?我是不是漏掉了什么?
字符串可以使用实习,这使得它们(与所有其他类型的对象)是不同的。
数组本质上就是对象。在语法或上下文不清楚的情况下重用实例并非没有副作用或风险。
static int[] empty = new int[0];
...
lock (empty) { ... }
如果其他代码锁定了另一个(他们认为)空的int[]
,你可能会有一个死锁,这是很难发现的。
其他场景包括使用数组作为Dictionary中的键,或其他任何与其标识相关的地方。框架不能只是改变规则。
使用"new"创建对象将始终创建一个新实例,该实例可能与任何其他实例被锁定,并且ReferenceEquals将报告其与所有其他实例不同。如果有系统定义的工厂方法或属性来创建空数组,类似于Enumerable