我注意到了
Console.WriteLine((object) new string(' ', 0) == (object) new string(' ', 0));
打印true
,表示CLR保留空字符串并重新使用相同的实例。 (它会为false
以外的任何其他数字打印0
。)
但是,对于数组来说也是如此:
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不这样做?有什么我想念的吗?
答案 0 :(得分:9)
字符串可以使用实习,这使它们成为一个不同的故事(来自所有其他类型的对象)。
数组本质上只是对象。重复使用语法或上下文中不明确的实例并非没有副作用或风险。
static int[] empty = new int[0];
...
lock (empty) { ... }
如果某些其他代码锁定在另一个(他们认为)为空int[]
,那么您可能会遇到很难找到的死锁。
其他方案包括使用数组作为词典中的键,或者其他任何地方的身份都很重要。框架不能只是改变规则。
答案 1 :(得分:0)
使用“new”创建对象将始终创建一个新实例,该实例可能与任何其他实例明显锁定,并且ReferenceEquals将报告与所有其他实例不同。如果存在系统定义的工厂方法或属性来创建空数组,类似于Enumerable&lt; T&gt; .Empty或String.Empty,那些属性可以返回共享对象实例,但是暴露的构造函数除了返回新实例或抛出之外不能执行任何操作例外。