323

在 .NET 中, 和 之间有什么区别String.Empty""它们是否可以互换,或者是否存在一些围绕相等性的潜在参考或本地化问题String.Empty可以确保不存在问题?

4

18 回答 18

330

在 .NET 2.0 之前的版本中,""创建一个对象而不string.Empty创建对象ref,这样string.Empty效率更高。

在 .NET 2.0 及更高版本中,所有出现的 都""指代相同的字符串文字,这意味着""等同于.Empty,但仍不如.Length == 0.

.Length == 0是最快的选择,但.Empty代码更简洁。

有关详细信息,请参阅.NET 规范

于 2008-09-30T02:02:38.607 回答
219

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
于 2012-12-04T12:39:12.817 回答
45

之前的答案对于 .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 的实际结果是 nil。JIT 最终会解决这个问题。

就我个人而言,我发现 JIT 比我聪明得多,所以我尽量不要对像这样的微编译器优化过于聪明。与我或 C# 编译器之前预期的相比,JIT 将在更合适的时间更好地展开 for() 循环、删除冗余代码、内联方法等。让 JIT 完成它的工作 :)

于 2008-09-30T02:19:32.123 回答
40

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

于 2008-10-01T03:18:03.957 回答
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 
}
于 2013-06-30T02:07:51.477 回答
17

我倾向于使用String.Empty而不是""出于一个简单但不明显的原因: ""并且""不一样,第一个实际上有 16 个零宽度字符。显然,没有一个称职的开发人员会在他们的代码中加入零宽度字符,但如果他们确实进入了那里,那可能是维护的噩梦。

笔记:

于 2015-11-25T11:42:56.350 回答
12

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

于 2008-09-30T04:06:11.977 回答
7

"" 的所有实例都是相同的内部字符串文字(或者它们应该是)。所以你真的不会每次使用 "" 时都在堆上抛出一个新对象,而只是创建对同一个内部对象的引用。话虽如此,我更喜欢 string.Empty。我认为它使代码更具可读性。

于 2008-09-30T02:08:05.693 回答
7

使用String.Empty而不是"".

这比内存使用更多的是速度,但它是一个有用的提示。这 ""是一个文字,因此将充当文字:在第一次使用时创建它,并在以下使用时返回它的引用。""无论我们使用多少次,只有一个实例会存储在内存中!我在这里看不到任何内存损失。问题是每次""使用时,都会执行一个比较循环来检查是否 ""已经在实习池中。另一方面,String.Empty 是对""存储在.NET Framework 内存区域中的引用。 String.Empty指向 VB.NET 和 C# 应用程序的相同内存地址。那么,当您有参考资料时,为什么每次需要"" 时都搜索参考资料String.Empty?

参考:String.Emptyvs""

于 2016-02-11T09:20:10.590 回答
6

String.Empty 不会创建对象,而 "" 会。然而,正如这里所指出的,差异是微不足道的。

于 2008-09-30T02:02:48.513 回答
4
string mystring = "";
ldstr ""

ldstr将新对象引用推送到存储在元数据中的字符串文字。

string mystring = String.Empty;
ldsfld string [mscorlib]System.String::Empty

ldsfld将静态字段的值推送到评估堆栈上

我倾向于使用String.Empty而不是""因为恕我直言,它更清晰,更少 VB-ish。

于 2015-04-17T16:34:26.920 回答
3

Eric Lippert 写道(2013 年 6 月 17 日):
“我在 C# 编译器中使用的第一个算法是处理字符串连接的优化器。不幸的是,在我离开之前我没有设法将这些优化移植到 Roslyn 代码库;希望有人会达到那个目的!

以下是截至 2019 年 1 月的一些Roslyn x64结果。尽管此页面上其他答案的一致意见,但在我看来,当前的 x64 JIT 并没有对所有这些情况进行相同的处理,当一切都说了又做了。

但是,请特别注意,这些示例中只有一个实际上最终调用了String.Concat,我猜这是出于模糊的正确性原因(与优化疏忽相反)。其他差异似乎更难解释。


默认(字符串)+ { 默认(字符串),“”,String.Empty }

static String s00() => default(String) + default(String);
    mov  rax,[String::Empty]
    mov  rax,qword ptr [rax]
    add  rsp,28h
    ret

static String s01() => default(String) + "";
    mov  rax,[String::Empty]
    mov  rax,qword ptr [rax]
    add  rsp,28h
    ret

static String s02() => default(String) + String.Empty;
    mov  rax,[String::Empty]
    mov  rax,qword ptr [rax]
    mov  rdx,rax
    test rdx,rdx
    jne  _L
    mov  rdx,rax
_L: mov  rax,rdx
    add  rsp,28h
    ret

"" + { 默认(字符串), "", String.Empty }

static String s03() => "" + default(String);
    mov  rax,[String::Empty]
    mov  rax,qword ptr [rax]
    add  rsp,28h
    ret

static String s04() => "" + "";
    mov  rax,[String::Empty]
    mov  rax,qword ptr [rax]
    add  rsp,28h
    ret

static String s05() => "" + String.Empty;
    mov  rax,[String::Empty]
    mov  rax,qword ptr [rax]
    mov  rdx,rax
    test rdx,rdx
    jne  _L
    mov  rdx,rax
_L: mov  rax,rdx
    add  rsp,28h
    ret

String.Empty + { default(String), "", String.Empty }

static String s06() => String.Empty + default(String);
    mov  rax,[String::Empty]
    mov  rax,qword ptr [rax]
    mov  rdx,rax
    test rdx,rdx
    jne  _L
    mov  rdx,rax
_L: mov  rax,rdx
    add  rsp,28h
    ret

static String s07() => String.Empty + "";
    mov  rax,[String::Empty]
    mov  rax,qword ptr [rax]
    mov  rdx,rax
    test rdx,rdx
    jne  _L
    mov  rdx,rax
_L: mov  rax,rdx
    add  rsp,28h
    ret

static String s08() => String.Empty + String.Empty;
    mov  rcx,[String::Empty]
    mov  rcx,qword ptr [rcx]
    mov  qword ptr [rsp+20h],rcx
    mov  rcx,qword ptr [rsp+20h]
    mov  rdx,qword ptr [rsp+20h]
    call F330CF60                 ; <-- String.Concat
    nop
    add  rsp,28h
    ret


测试详情

Microsoft (R) Visual C# Compiler version 2.10.0.0 (b9fb1610)
AMD64 Release
[MethodImpl(MethodImplOptions.NoInlining)]
'SuppressJitOptimization' = false
于 2019-01-31T04:42:34.297 回答
3

当您在视觉上扫描代码时,“”会像字符串一样被着色。string.Empty 看起来像一个常规的类成员访问。在快速浏览期间,更容易发现“”或直观含义。

找出字符串(堆栈溢出着色并不完全有帮助,但在 VS 中这更明显):

var i = 30;
var f = Math.Pi;
var s = "";
var d = 22.2m;
var t = "I am some text";
var e = string.Empty;
于 2019-05-07T13:58:22.157 回答
2

从实体框架的角度来看:EF 版本 6.1.3 在验证时似乎以不同的方式对待 String.Empty 和 ""。

string.Empty 出于验证目的被视为 null 值,如果在必需(属性)字段上使用它将引发验证错误;其中 "" 将通过验证而不抛出错误。

此问题可能在 EF 7+ 中得到解决。参考: -https: //github.com/aspnet/EntityFramework/issues/2610)。

编辑: [Required(AllowEmptyStrings = true)] 将解决此问题,允许 string.Empty 进行验证。

于 2017-03-09T11:59:29.850 回答
2

由于 String.Empty 不是编译时常量,因此您不能将其用作函数定义中的默认值。

public void test(int i=0,string s="")
    {
      // Function Body
    }
于 2017-03-14T07:11:07.173 回答
-1

感谢您提供非常丰富的答案。

如果我错了,请原谅我的无知。我正在使用 VB,但我认为如果您测试未分配字符串的长度(即 IS Nothing),它会返回错误。现在,我在 1969 年开始编程,所以我已经远远落后了,但是我总是通过连接一个空字符串 ("") 来测试字符串。例如(以任何语言): -

if string + "" = ""

于 2021-03-12T17:48:50.530 回答
-10

这里的每个人都给出了一些很好的理论解释。我也有类似的疑问。所以我尝试了一个基本的编码。我发现了不同之处。这就是区别。

string str=null;
Console.WriteLine(str.Length);  // Exception(NullRefernceException) for pointing to null reference. 


string str = string.Empty;
Console.WriteLine(str.Length);  // 0

所以看起来“Null”意味着绝对无效和“String.Empty”意味着它包含某种值,但它是空的。

于 2016-01-21T15:57:14.337 回答