204

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

4

4 回答 4

159

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

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

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

我从CodeProject 的这篇方便的文章中找到了这些信息。

于 2009-02-03T17:24:23.577 回答
25

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

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

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

即使常量被认为是静态成员,常量声明既不需要也不允许静态修饰符。

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

鉴于此,以下代码行在创建新对象方面执行完全相同的操作。

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

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

readonly 关键字与 const 关键字不同。const 字段只能在字段声明时进行初始化。只读字段可以在声明或构造函数中初始化。因此,只读字段可以具有不同的值,具体取决于使用的构造函数。此外,虽然 const 字段是编译时常量,但 readonly 字段可用于运行时常量,...

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

于 2009-02-03T17:43:28.747 回答
5
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 

编辑:更正错字

于 2013-05-18T11:02:47.823 回答
0

这个答案是出于历史目的而存在的。

起初:

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

扩展讨论:

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

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

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

是的,对象必须使用只读。只有结构可以做常量。我认为当您使用string而不是String编译器时,会将 const 更改为您的只读。一切都是为了让 C 程序员开心。– 加里·舒特勒 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

头疼。我仍然认为我是对的,但现在我不太确定了。今晚需要研究!– 加里·舒特勒 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 运算符,因此引用类型常量的唯一可能值字符串以外的为空。前面的两条评论直接取自 C# 语言规范,并重申了 Joel Mueller 提到的内容。– senfo 2009 年 2 月 4 日 15:05 5

于 2009-02-03T16:51:06.060 回答