【问题标题】:Why isn't String.Empty a constant?为什么 String.Empty 不是常数?
【发布时间】:2010-10-05 05:00:15
【问题描述】:

在 .NET 中,为什么 String.Empty 是只读而不是常量?我只是想知道是否有人知道该决定背后的原因。

【问题讨论】:

  • This这个问题可以解决,简单的回答是,没人知道...
  • 是的,为 Eric Lippert 的回复 +1,谢谢!
  • 特别考虑到 Decimal.Zero 是 const (从用户的角度来看是......)

标签: .net string readonly constants


【解决方案1】:

使用static readonly 而不是const 的原因是与非托管代码一起使用,正如Microsoft 在Shared Source Common Language Infrastructure 2.0 Release 中所指出的那样。要查看的文件是sscli20\clr\src\bcl\system\string.cs

Empty 常量保存空 字符串值。我们需要调用 字符串构造函数,以便 编译器不会将此标记为 字面意思。

将其标记为文字意味着 它不会显示为字段 我们可以从本地访问。

我从this handy article at CodeProject找到了这个信息。

【讨论】:

  • 如果您能解释此评论,我将不胜感激(因为 Jon Skeet 无法...)请参见此处:stackoverflow.com/questions/8462697/…
  • @gdoron:我的猜测(而且是猜测)是这样的。当一个值被定义为文字(常量)时,它的值被插入到它被引用的地方,而当它没有被定义为文字时,引用值的源并在运行时检索实际值。我怀疑后者可能会确保在运行时在本机和 .NET 之间发生正确的字符串编组 - 如果它是文字,则本机编译器可能需要以某种方式将文字值拉入其本机代码,这可能不是可行的。不过,这都是我的猜测。
  • 这确实意味着必须使用 "",而不是 string.Empty 作为方法中的默认参数值。这有点烦人。
  • "" 可能看起来像一个错误,而 string.Empty 则表示故意
  • @JeffYates 我要补充一点,它不一致的事实已经很烦人了。人们会看到其余的代码并想知道“为什么他在这里使用“”而不是 String.Empty?仅出于这个原因,我正在认真考虑不再使用String.Empty
【解决方案2】:

我认为这里有很多混乱和不好的反应。

首先,const 字段是static 成员(不是实例成员)。

查看 C# 语言规范的第 10.4 节常量。

即使考虑了常量 静态成员,一个常量声明 既不需要也不允许静态 修饰符。

如果public const 成员是静态的,则不能认为常量会创建新对象。

鉴于此,以下代码行在创建新对象方面完全相同做同样的事情。

public static readonly string Empty = "";
public const string Empty = "";

这是来自 Microsoft 的注释,解释了两者之间的区别:

只读关键字不同于 const 关键字。一个 const 字段可以 仅在声明时初始化 领域的。只读字段可以是 在声明中初始化 或在构造函数中。所以, 只读字段可以有不同的 值取决于构造函数 用过的。此外,虽然 const 字段是 编译时常量,只读 字段可用于运行时 常数,...

所以我发现这里唯一合理的答案是 Jeff Yates 的。

【讨论】:

  • +1 表示关于 const 和 static readonly 的 C# 规范的客气话和说明。
  • 重读这篇文章,我不同意 const stringstatic readonly string 做同样的事情。常量值在链接代码中被替换,而静态只读值被引用。如果库 A 中有一个由库 B 使用的 const,则库 B 会将对该 const 变量的所有引用替换为其文字值;如果该变量是 static readonly,则会引用它并在运行时确定其值。
  • Jeff 的观点在引用库时很重要。如果您重新编译 A 并重新分发它,不重新编译 B,B 仍将使用旧值。
【解决方案3】:
String.Empty read only instead of a constant?

如果你将任何字符串设为常量,那么编译器会在你调用它的任何地方替换为实际的字符串,然后你会用相同的字符串填充你的代码。代码运行也需要从不同的内存数据中反复读取那个字符串。

如果您将字符串只保留在一个位置,因为它是String.Empty,程序只将相同的字符串保留在一个位置并读取它,或者引用它 - 将内存中的数据保持在最低限度。

此外,如果您使用 String.Empty 作为 const 编译任何 dll,并且由于任何原因 String.Empty 更改,则编译后的 dll 将不再工作,因为 cost 使内部代码实际在每次调用时保留字符串的副本。

例如看这段代码:

public class OneName
{
    const string cConst = "constant string";
    static string cStatic = "static string";
    readonly string cReadOnly = "read only string";

    protected void Fun()
    {
        string cAddThemAll ;

        cAddThemAll = cConst;
        cAddThemAll = cStatic ;
        cAddThemAll = cReadOnly;    
    }
}

将由编译器作为:

public class OneName
{
    // note that the const exist also here !
    private const string cConst = "constant string";
    private readonly string cReadOnly;
    private static string cStatic;

    static OneName()
    {
        cStatic = "static string";
    }

    public OneName()
    {
        this.cReadOnly = "read only string";
    }

    protected void Fun()
    {
        string cAddThemAll ;

        // look here, will replace the const string everywhere is finds it.
        cAddThemAll = "constant string";
        cAddThemAll = cStatic;
        // but the read only will only get it from "one place".
        cAddThemAll = this.cReadOnly;

    }
}

和程序集调用

        cAddThemAll = cConst;
0000003e  mov         eax,dword ptr ds:[09379C0Ch] 
00000044  mov         dword ptr [ebp-44h],eax 
        cAddThemAll = cStatic ;
00000047  mov         eax,dword ptr ds:[094E8C44h] 
0000004c  mov         dword ptr [ebp-44h],eax 
        cAddThemAll = cReadOnly;
0000004f  mov         eax,dword ptr [ebp-3Ch] 
00000052  mov         eax,dword ptr [eax+0000017Ch] 
00000058  mov         dword ptr [ebp-44h],eax 

编辑:更正错字

【讨论】:

  • 所以,这意味着 const 字符串必须始终使用包含该 const 的类来实例化?那似乎使用静态只读要好得多。
  • @theberserker 更好,但您可以使用所有选项。
  • > 那么编译后的 dll 将不再工作,因为成本使内部代码在每次调用时实际保留字符串的副本。 @Aristos 这不太对。编译代码后,字符串的“副本”将在可执行文件的 TEXT 块中引用,并且所有代码将仅引用同一块内存。您在第二步中引用的只是一个中间步骤。
  • @user1533523 感谢您的注意 - 我会在有时间检查时进行测试
  • 你是怎么得到那个汇编代码的? C# 不能编译成程序集!
【解决方案4】:

此答案出于历史目的而存在。

原文:

因为String 是一个类,因此不能是常量。

扩展讨论:

在审核这个答案的过程中敲出了很多有用的对话,而不是删除它,直接复制了这个内容:

在 .NET 中,(与 Java 不同)字符串和字符串完全相同。是的,您可以在 .NET 中使用字符串文字常量 – DrJokepu 2009 年 2 月 3 日 16:57

你是说一个类不能有常量吗? – StingyJack 2009 年 2 月 3 日 16:58

是的,对象必须使用只读。只有结构可以做常量。我认为当您使用 string 而不是 String 时,编译器会将 const 更改为您的只读。一切都是为了让 C 程序员开心。 – Garry Shutler 2009 年 2 月 3 日 16:59

tvanfosson 只是稍微详细地解释了一下。 “X 不能是一个常数,因为包含的 Y 是一个类”有点过于无上下文了 ;) – Leonidas 2009 年 2 月 3 日 17:01

string.Empty 是静态属性,它返回 String 类的实例,即空字符串,而不是字符串类本身。 – tvanfosson 2009 年 2 月 3 日 17:01

Empty 是 String 类的只读实例(它不是属性)。 – senfo 2009 年 2 月 3 日 17:02

头疼。我仍然认为我是对的,但现在我不太确定了。今晚需要研究! – Garry Shutler 2009 年 2 月 3 日 17:07

空字符串是字符串类的一个实例。 Empty 是 String 类上的静态字段(不是属性,我已更正)。基本上是指针和它所指向的东西之间的区别。如果它不是只读的,我们可以更改 Empty 字段引用的实例。 – tvanfosson 2009 年 2 月 3 日 17:07

加里,你不需要做任何研究。想想看。字符串是一个类。 Empty 是 String 的一个实例。 – senfo 2009 年 2 月 3 日 17:12

有些东西我不太明白:String 类的静态构造函数到底如何创建 String 类的实例?这不是某种“先有鸡还是先有蛋”的场景吗? – DrJokepu 2009 年 2 月 3 日 17:12 5

这个答案对于除 System.String 之外的几乎任何其他类都是正确的。 .NET 为字符串做了很多性能特例,其中之一就是你可以拥有字符串常量,试试吧。在这种情况下,杰夫耶茨有正确的答案。 – 乔尔·穆勒 2009 年 2 月 3 日 19:25

如第 7.18 节所述,常量表达式是可以在编译时完全计算的表达式。由于创建除字符串之外的引用类型的非空值的唯一方法是应用 new 运算符,并且由于常量表达式中不允许使用 new 运算符,因此引用类型常量的唯一可能值字符串以外的为空。前两个 cmets 直接取自 C# 语言规范,并重申 Joel Mueller 提到的内容。 – senfo 2009 年 2 月 4 日 15:05 5

【讨论】:

  • 请投下正确答案。如果您转到定义,您会发现它在 String 类上,并且是 String 的一个实例。它显示为小写的事实是编译器的魔法。
  • 不是我对你投了反对票,但在 .NET 中,(与 Java 不同)字符串和字符串完全相同。是的,您可以在 .NET 中使用字符串文字常量
  • 这个答案对于除 System.String 之外的几乎任何其他类都是正确的。 .NET 为字符串做了很多性能特例,其中之一就是你可以拥有字符串常量,试试吧。在这种情况下,杰夫耶茨的答案是正确的。
  • 我几乎删除了这个答案,因为出现了一个更好的答案,但是这些 cmets 中的讨论值得保留。
  • @Garry,你很幸运我读了你的最后一条评论,否则我也会投反对票。字符串在 .NET 中有一个特殊功能,即使它是一个 ref 类,它也可以是一个 const。
猜你喜欢
  • 2011-10-24
  • 2015-03-18
  • 2011-10-28
  • 1970-01-01
  • 2012-12-01
  • 1970-01-01
  • 2011-03-09
  • 1970-01-01
  • 2015-05-29
相关资源
最近更新 更多