String.Empty与""(空字符串)之间有什么区别?

344
在.NET中,String.Empty""有什么区别?它们是否可以互换使用,或者在相等性方面是否存在基础引用或本地化问题,String.Empty可以确保这些问题不会成为问题?

5
真正的问题不是“什么”,而是“为什么”。为什么 Microsoft 提出了 string.Empty,并且为什么将其声明为 readonly 而不是 const,背后的原因是什么。 - user1451111
几个答案似乎是针对检查字符串是否为空的,这是一个比通常使用空字符串更具体的问题。 - Stewart
这个回答解决了你的问题吗?在C#中,初始化字符串应该使用string.Empty还是String.Empty还是""? - Michael Freidgeim
19个回答

357

在.NET 2.0版本之前,""会创建一个对象,而string.Empty则不会参考,这使得string.Empty更高效。

在.NET 2.0及更高版本中,所有出现的""都指向同一个字符串字面量,这意味着""等同于.Empty,但仍然不如.Length == 0快。

.Length == 0是最快的选项,但.Empty使代码稍微更简洁。

有关更多信息,请参见.NET规范


105
由于字符串驻留机制,原文中的代码本身只会创建一个对象。基本上,这种性能权衡可以忽略不计,可读性更为重要。 - Jon Skeet
16
有趣的是,尽管这个问题并没有提到将字符串与""或string.Empty进行比较来检查空字符串,但很多人似乎会以那种方式解释这个问题... - peSHIr
14
要小心使用.Length == 0,因为如果你的字符串变量为空,则会引发异常。但是,如果你将其与""进行比较,它将只返回false而不会引发异常。 - Jeffrey Harmon
14
或者你可以使用 string.IsNullOrEmpty( stringVar ) - Flynn1179
3
如果有人想防止出现测试空字符串的不良实践,您可以在代码分析中启用CA1820。https://learn.microsoft.com/visualstudio/code-quality/ca1820-test-for-empty-strings-using-string-length - sean

235

什么是 String.Empty 和 "" 的区别,它们是否可以互换使用?

string.Empty 是一个只读字段,而""是一个编译时常量。它们的行为不同的情况有:

C# 4.0或更高版本中的默认参数值

void SomeMethod(int ID, string value = string.Empty)
// Error: Default parameter value for 'value' must be a compile-time constant
{
    //... implementation
}

在 switch 语句中使用 case 表达式

string str = "";
switch(str)
{
    case string.Empty: // Error: A constant value is expected. 
        break;

    case "":
        break;

}

属性参数

[Example(String.Empty)]
// Error: An attribute argument must be a constant expression, typeof expression 
//        or array creation expression of an attribute parameter type

27
有趣,我没想到这个老问题会有任何相关的新信息。我错了。 - johnc
我认为你的例子#1 C# 4.0或更高版本中的默认参数值 实质上是你的例子#3 属性参数 的重复,因为我相信.NET将默认参数部署到属性中。所以更基本地说,你不能把(运行时)“值”(在这种情况下,一个实例句柄,对于那些一丝不苟的人来说)放入(编译时)元数据中。 - Glenn Slayden
@GlennSlayden,我不同意你的观点。属性初始化与普通初始化是不同的。这是因为在大多数情况下,您可以将String.Empty作为参数传递。您是正确的,所有示例都表明“您无法将(运行时)“值”放入(编译时)元数据”,这就是示例的目的所在。 - Sasuke Uchiha
对我来说,它们看起来并不像它们的行为有所不同的方式。只是一个合法,另一个不合法。说它们的行为不同,对我来说,就是说两者都构成了合法的代码,但在运行时可能会有不同的行为取决于你使用哪个。 - Stewart
了解为什么string.Empty是只读字段会很有趣。它似乎是编译时常量的完美位置。毕竟,string.Empty永远不需要在运行时计算,对吧? - Clonkex

47
之前的回答是针对.NET 1.1的(请看他们链接帖子的日期:2003年)。从.NET 2.0开始,基本上没有区别。JIT最终会引用堆上的同一对象。
根据C#规范第2.4.4.5节: http://msdn.microsoft.com/en-us/library/aa691090(VS.71).aspx 每个字符串字面量不一定会导致一个新的字符串实例。当两个或多个字符串字面量根据字符串相等运算符(第7.9.7节)在同一程序集中是相等的时,这些字符串字面量指向同一字符串实例。
有人甚至在Brad Abram的文章评论中提到了这一点。
总之,“”与String.Empty的实际结果为零。JIT最终会找出来。
就我个人而言,我发现JIT比我聪明得多,所以我尽量不要过于聪明地进行微型编译器优化。JIT将展开for()循环,删除冗余代码,内联方法等等,比我或C#编译器预先想象的更好,更合适的时间。让JIT做它的工作:)

相关热门问题 https://dev59.com/NXVC5IYBdhLWcg3wihqv - Michael Freidgeim

41

String.Empty是一个只读字段,而""是一个常量。这意味着你不能在switch语句中使用String.Empty,因为它不是一个常量。


随着 default 关键字的出现,我们可以提高可读性,避免意外修改,并拥有编译时常量。不过,说实话,我仍然认为 String.Empty 比 default 更易读,只是打字速度较慢。 - Enzoaeneas
4
“default”字符串是空的,而不是“null”。所有引用对象的“default”值都是“null”。 - Sorensen

24
我倾向于使用String.Empty而不是"",原因很简单,但并不明显:""""并不相同,前者实际上有16个零宽字符。显然,没有一个称职的开发人员会在他们的代码中放置任何零宽字符,但如果这样做了,它可能会成为维护的噩梦。
注:
  • 我在此示例中使用了U+FEFF

  • 不确定SO是否会吞噬这些字符,但您可以尝试使用众多零宽字符之一。

  • 我之所以知道这一点,只是因为https://codegolf.stackexchange.com/


这个问题值得更多的赞。我曾经看到过这个问题在跨平台系统集成中发生过。 - EvilDr
对我来说,这是一种糟糕的代码风格。你应该使用“\xfeff”来添加不可见字符。 - Vasiliy Zverev
@VasiliyZverev 是的,在一般情况下,将零宽字符直接添加到代码中是不明智的。关键是从其他地方复制粘贴,并没有意识到其中包含了零宽字符。类似于StyleCop这样的工具可能可以配置为标记此类问题,但并非每个人/项目都会设置。 - Justinw

18

另一个区别是String.Empty会生成更大的CIL代码。虽然引用""和String.Empty的代码长度相同,但编译器不会对使用String.Empty作为参数进行字符串连接进行优化(请参见Eric Lippert的博客文章)。以下是等效函数。

string foo()
{
    return "foo" + "";
}
string bar()
{
    return "bar" + string.Empty;
}

生成这个IL

.method private hidebysig instance string foo() cil managed
{
    .maxstack 8
    L_0000: ldstr "foo"
    L_0005: ret 
}
.method private hidebysig instance string bar() cil managed
{
    .maxstack 8
    L_0000: ldstr "bar"
    L_0005: ldsfld string [mscorlib]System.String::Empty
    L_000a: call string [mscorlib]System.String::Concat(string, string)
    L_000f: ret 
}

1
有道理,但是谁会这么做呢?我为什么要故意将空字符串与某个东西连接起来呢? - Robert S.
也许第二个字符串在一个单独的函数中被内联了。我承认这很少见。 - Bruno Martinez
4
使用三元运算符并不罕见: "bar " + (ok ? "" : "error") 的意思是,如果 ok 为真,则返回空字符串,否则返回字符串 "error",并将其连接到字符串 "bar " 上。 - symbiont
但是在三元运算符的情况下,除非“ok”是编译时常量,否则生成的IL仍然需要使用string.Concat。 - Sorensen

12

上述答案在技术上是正确的,但为了获得最佳的代码可读性和最小的异常几率,您可能希望使用String.IsNullOrEmpty(s)


5
就平等比较而言,我完全同意。但问题也涉及到两个概念之间的区别以及比较。 - johnc
3
请注意,“最小异常机会”通常意味着“最大的继续运行但做错事情的机会”。例如,如果您正在解析命令行参数,并且有人使用--foo=$BAR调用您的应用程序,则可能希望区分他们是否忘记设置环境变量以及他们根本没有传递标志。string.IsNullOrEmpty通常是代码异味,表明您没有正确验证输入或正在进行奇怪的操作。当您真正想使用null或类似的Maybe / Option类型时,不应该通常接受空字符串。 - Alastair Maw

7

所有的""实例都是相同的,即内部化的字符串字面量(或者它们应该是)。因此,每次使用""时,您实际上不会在堆上抛出一个新对象,而只是创建对同一内部化对象的引用。话虽如此,我更喜欢string.Empty。我觉得这可以使代码更易读。


6
使用String.Empty而不是"",这与速度有关,而非内存使用,是一个很好的技巧。由于""是一个字面量,因此它将作为字面值:在第一次使用时创建,并返回其引用。无论我们使用多少次,只会在内存中存储一个""实例!我没有看到任何内存惩罚。问题在于每次使用""时,都会执行比较循环以检查""是否已经在内部池中。另一方面,String.Empty是对存储在.NET Framework内存区域中的""的引用。对于VB.NET和C#应用程序,String.Empty指向同一内存地址。因此,在需要""时为什么要每次搜索引用,当你可以在String.Empty中拥有该引用呢?
参考:String.Empty vs ""

6
自 .net 2.0 以来,这就不再是真实的情况了。 - nelsontruran
这种引用的搜索(如果需要)肯定会在编译时发生,因此运行时开销将为零。但我可能会期望更好的结果:将 "" 识别为与 string.Empty 相同,并因此能够立即发出正确的引用。 - Stewart

6

String.Empty不会创建对象,而""会创建对象。然而,如此处所指出的那样,这种区别微不足道。


4
如果你检查一个字符串是否为空(string.Empty 或 ""),这并不是一个简单的问题。正如 Eugene Katz 指出的那样,你应该真正使用 String.IsNullOrEmpty。否则你将得到意外的结果。 - Sigur
@Sigur 你如何使用 String.IsNullOrEmpty 将变量设置为空字符串,或将空字符串作为函数参数传递? - Stewart
要将字符串设置为空,请使用String.Empty。要检查空字符串,请使用String.IsNullOrEmpty。请注意,String.IsNullOrEmpty是双重检查,属于不同的领域。检查null是一种技术性检查,以避免崩溃,而检查空则是一种功能性检查。空字符串可能是有效值。因此,在代码中通常在不同的位置进行检查。 - Sigur

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接