为什么 CLR 重用空字符串,而不重用空数组?
我注意到
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
,我们发现它缓存并重用空数组:
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
的调用,还适用于 new T[0 ]
。如果 Enumerable.Empty
为什么 CLR 不这样做?我有什么遗漏的吗?
I notice that
Console.WriteLine((object) new string(' ', 0) == (object) new string(' ', 0));
prints true
, which indicates that the CLR keeps the empty string around and re-uses the same instance. (It prints false
for any other number than 0
.)
However, the same is not true for arrays:
Console.WriteLine(new int[0] == new int[0]); // False
Now, if we look at the implementation of Enumerable.Empty<T>()
, we find that it caches and re-uses empty arrays:
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;
}
}
So the framework team felt that keeping an empty array around for every type is worth it. The CLR could, if it wanted to, go a small step further and do this natively so it applies not only to calls to Enumerable.Empty<T>()
but also new T[0]
. If the optimisation in Enumerable.Empty<T>()
is worth it, surely this would be even more worth it?
Why does the CLR not do this? Is there something I’m missing?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
字符串可以使用实习,这使它们成为一个不同的故事(与所有其他类型的对象)。
数组本质上只是对象。重用语法或上下文不清楚的实例并非没有副作用或风险。
如果其他一些代码锁定了另一个(他们认为)空的
int[]
,那么您可能会遇到很难发现的死锁。其他场景包括使用数组作为字典中的键,或者它们的身份重要的任何其他地方。框架不能只是改变规则。
Strings may use interning, that makes them a different story (from all other kind of objects).
Arrays are essentially just objects. Re-using instances where that is not clear from the syntax or context isn't without side effects or risks.
If some other code locked on another (they thought) empty
int[]
you might have a deadlock that is very hard to find.Other scenarios include using arrays as the key in a Dictionary, or anywhere else their identity matters. The framework can't just go around changing the rules.
使用“new”创建对象将始终创建一个新实例,该实例可能与任何其他实例不同地被锁定,并且 ReferenceEquals 将报告为与所有其他实例不同。如果有系统定义的工厂方法或属性来创建空数组,类似于 Enumerable.Empty 或 String.Empty,这些属性可以返回共享对象实例,但公开的构造函数除了返回新实例或抛出异常之外不能执行任何操作一个例外。
Creating an object with "new" will always create a new instance, which may be locked distinctly from any other instance, and which ReferenceEquals will report as distinct from all other instances. If there were system-defined factory methods or properties to create empty arrays, similar to Enumerable<T>.Empty or String.Empty, those properties could return shared object instances, but exposed constructors cannot do anything other than return a new instance or throw an exception.