为什么是字符串。空一个常数?

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

22535 次浏览

从历史角度来看,这个答案是存在的。

原:

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

扩展的讨论:

在审查这个答案的过程中,许多有用的对话框被敲出,而不是删除它,以下内容是直接复制的:

在。net中(与Java不同),string和string是完全相同的。是的,你可以在。net中使用字符串常量- DrJokepu Feb 3 '09 at 16:57

你是说类不能有常量吗?09年2月3日16:58

是的,对象必须使用只读。只有结构体可以做常量。我认为当你使用string而不是String时,编译器会为你将const转换为只读类型。都是为了让C程序员开心。- Garry Shutler 09年2月3日16:59

Tvanfosson解释得有点啰嗦。“X不能是一个常数,因为包含Y的是一个类”有点与上下文无关;)——列奥尼达2009年2月3日17:01

字符串。Empty是一个静态属性,返回String类的实例,即空字符串,而不是字符串类本身。- tvanfosson 09年2月3日17:01

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

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

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

加里,你不需要做任何调查。想想看。String是一个类。Empty是String对象的一个实例。- senfo 2009年2月3日17:12

有一些事情我不太明白:String类的静态构造函数究竟如何创建String类的实例?这难道不是“先有鸡还是先有蛋”的问题吗?- jokepu博士09年2月3日17:12 5 < / p >

这个答案对于几乎任何其他类都是正确的,但是System.String. net为字符串做了很多性能特殊的大小写处理,其中之一就是你可以有字符串常量,试试吧。在这种情况下,杰夫·耶茨的答案是正确的。- Joel Mueller 09年2月3日19:25

如§7.18所述,常量表达式是可以在编译时完全求值的表达式。由于创建字符串以外的引用类型的非空值的唯一方法是应用new操作符,并且由于在常量表达式中不允许使用new操作符,因此对于字符串以外的引用类型的常量,唯一可能的值是null。前面的两条评论直接取自c#语言规范,重申了Joel Mueller提到的内容。- senfo 2009年2月4日15:05 5 < / p >

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

Empty常量保存空值 字符串值。我们需要打电话给 字符串构造函数 编译器没有将此标记为 文字。< / p > 将此标记为文字将意味着 它不会显示为一个字段

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

我认为这里有很多困惑和糟糕的回应。

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

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

即使考虑常量 静态成员,常量声明 既不要求也不允许静态 修饰符。< / p >

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

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

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

下面是微软的一份说明,解释了两者之间的区别:

只读关键字与 const关键字。const字段可以 只在声明时初始化 这个领域。只读字段可以是 在声明时进行初始化 或者在构造函数中。因此, 只读字段可以有不同的 值,取决于构造函数 使用。另外,const字段是 编译时常数,即read - only 字段可用于运行时 常数,…< / p >

所以我发现唯一可信的答案是杰夫·耶茨的。

String.Empty read only instead of a constant?

如果你让任意字符串为常数,那么编译器将在你调用它的任何地方替换为实际上是字符串,你用相同的字符串填充你的代码,当代码运行时,也需要一遍又一遍地从不同的内存数据中读取该字符串。

如果你让你的字符串只在一个地方读,就像String.Empty一样,程序只在一个地方保留相同的字符串并读取它,或引用它-保持内存中的数据最小。

同样,如果你编译任何dll使用字符串。const是空的,String是空的。空更改,则编译后的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

编辑:更正的错别字