【问题标题】:Why does the CLR re-use empty strings, but not empty arrays?为什么 CLR 重用空字符串,而不是空数组?
【发布时间】:2011-10-27 12:13:42
【问题描述】:

我注意到了

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&lt;T&gt;() 的调用,还适用于对 new T[0] 的调用。如果Enumerable.Empty&lt;T&gt;() 的优化值得,那肯定更值得?

为什么 CLR 不这样做?我有什么遗漏吗?

【问题讨论】:

  • 我认为最大的异常是new string(...) 可以返回一个现有的引用,而不是它不能用于数组。这对我来说总是很奇怪。
  • @sll 基本数组不能添加或删除项目。一旦确定尺寸,它们的尺寸就很好了。
  • @Timwi:该行为违反了 C# 语言规范。来自第 7.6.10 节:“new 运算符用于创建类型的新实例。”和“new 运算符意味着创建一个类型的实例。” (可能还有更多我可以引用的例子。)同时检查是否违反了 ECMA-335...
  • FWIW,你会经常在我的代码中发现private static readonly SomeType[] nix = new SomeType[0];;p
  • @Timwi:在我看来,它违反了 ECMA-335 分区 III 的第 4.21 节:“newobj 指令创建了一个新对象或值类型的新实例。”显然这里没有这样做。

标签: .net clr


【解决方案1】:

字符串可以使用实习,这使它们成为不同的故事(与所有其他类型的对象)。

数组本质上只是对象。重用从语法或上下文中不清楚的实例并非没有副作用或风险。

static int[] empty = new int[0];
...
   lock (empty) { ... }

如果某些其他代码锁定在另一个(他们认为)空的 int[] 上,您可能会遇到很难找到的死锁。

其他场景包括使用数组作为字典中的键,或者在其他任何与它们的身份相关的地方。框架不能只是四处改变规则。

【讨论】:

  • 好主意,但没有解决问题,因为同样适用于空字符串。
  • 只有字符串 literals 默认使用实习 - 字符串本质上只是对象。将您的示例更改为使用static string empty = new string(' ', 0) - 它看起来更有效吗?
  • Henk 的观点仍然是,如果 CLR 团队考虑到这个功能,这将是他们考虑的一点。
  • +1 你永远不应该锁定字符串的一个很好的理由。实际上,我以前曾帮助开发人员解决过涉及这种情况的问题。
  • @Jon heh;我重申我的主张(如果我们有绿色字段)object 应该是 abstract,而 Monitor 应该是基于实例的 - 你应该只能 lock 一个 Monitor 实例。哦,那艘船已经航行了。
【解决方案2】:

使用“new”创建对象将始终创建一个新实例,该实例可能与任何其他实例不同地锁定,并且 ReferenceEquals 将报告为与所有其他实例不同。如果有系统定义的工厂方法或属性来创建空数组,类似于 Enumerable.Empty 或 String.Empty,这些属性可以返回共享对象实例,但暴露的构造函数除了返回一个新实例或抛出之外不能做任何事情一个例外。

【讨论】:

    猜你喜欢
    • 2023-02-15
    • 1970-01-01
    • 2014-06-27
    • 2023-04-07
    • 2012-12-29
    • 1970-01-01
    • 2013-01-19
    • 1970-01-01
    • 2013-07-01
    相关资源
    最近更新 更多