关于.NET:为什么string.empty不是一个常量?

Why isn't String.Empty a constant?

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


使用static readonly而不是const的原因是与非托管代码一起使用,正如微软在共享源公共语言基础结构2.0版本中指出的。要查看的文件是sscli20\clr\src\bcl\system\string.cs

The Empty constant holds the empty
string value. We need to call the
String constructor so that the
compiler doesn't mark this as a
literal.

Marking this as a literal would mean
that it doesn't show up as a field
which we can access from native.

我从codeproject的这篇便利的文章中找到了这个信息。


我觉得这里有很多困惑和不良反应。

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

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

Even though constants are considered
static members, a constant-declaration
neither requires nor allows a static
modifier.

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

考虑到这一点,下面的代码行在创建新对象方面做了完全相同的事情。

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

以下是微软的一条注释,解释了这两种方法的区别:

The readonly keyword is different from
the const keyword. A const field can
only be initialized at the declaration
of the field. A readonly field can be
initialized either at the declaration
or in a constructor. Therefore,
readonly fields can have different
values depending on the constructor
used. Also, while a const field is a
compile-time constant, the readonly
field can be used for runtime
constants, ...

所以我发现这里唯一合理的答案是杰夫·耶茨的。


1
String.Empty read only instead of a constant?

如果将任何字符串设置为常量,那么编译器将替换为调用它的所有地方的实际字符串,并且在代码运行时,需要从不同的内存数据中反复读取该字符串。

如果您将字符串保持为只读,就像它是String.Empty一样,那么程序只在一个地方保持相同的字符串并读取它,或者引用它—使内存中的数据保持最小。

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

请参见此代码,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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;    
    }
}

将由编译器提供,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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;

    }
}

还有集会电话

1
2
3
4
5
6
7
8
9
10
        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

编辑:更正的拼写错误


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

原来:

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

扩展讨论:

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

In .NET, (unlike in Java) string and String are exactly the same. And yes, you can have string literal constants in .NET – DrJokepu Feb 3 '09 at 16:57

Are you saying that a Class cannot have constants? – StingyJack Feb 3 '09 at 16:58

Yes, objects have to use readonly. Only structs can do constants. I think when you use string instead of String the compiler changes the const into a readonly for you. All to do with keeping C programmers happy. – Garry Shutler Feb 3 '09 at 16:59

tvanfosson just explained it a little bit more verbose."X cannot be a constant, because the containing Y is a class" was just a little bit too context-free ;) – Leonidas Feb 3 '09 at 17:01

string.Empty is static property that returns an instance of the String class, namely the empty string, not the string class itself. – tvanfosson Feb 3 '09 at 17:01

Empty is a readonly instance (it's not a property) of the String class. – senfo Feb 3 '09 at 17:02

Head hurting. I still think I'm right, but now I'm less certain. Research required tonight! – Garry Shutler Feb 3 '09 at 17:07

The empty string is an instance of the string class. Empty is a static field (not a property, I stand corrected) on the String class. Basically the difference between a pointer and the thing it points to. If it weren't readonly we could change which instance the Empty field refers to. – tvanfosson Feb 3 '09 at 17:07

Garry, you don't need to do any research. Think about it. String is a class. Empty is an instance of a String. – senfo Feb 3 '09 at 17:12

There is something I don't quite get: how on earth can the static constructor of the String class create an instance of the String class ? Isn't that some sort of"chicken or the egg" scenario? – DrJokepu Feb 3 '09 at 17:12
5

This answer would be correct for nearly any other class but System.String. .NET does a lot of performance special-casing for strings, and one of them is that you CAN have string constants, just try it. In this case, Jeff Yates has the correct answer. – Joel Mueller Feb 3 '09 at 19:25

As described in §7.18, a constant-expression is an expression that can be fully evaluated at compile-time. Since the only way to create a non-null value of a reference-type other than string is to apply the new operator, and since the new operator is not permitted in a constant-expression, the only possible value for constants of reference-types other than string is null. The previous two comments were taken directly from the C# language specification and reiterate what Joel Mueller mentioned. – senfo Feb 4 '09 at 15:05
5