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

发布于 2024-07-06 22:44:55 字数 136 浏览 12 评论 0 原文

在 .NET 中,String.Empty"" 之间有什么区别,它们是否可以互换,或者是否存在一些关于等式的潜在引用或本地化问题 String.Empty会确保没有问题吗?

In .NET, what is the difference between String.Empty and "", and are they interchangeable, or is there some underlying reference or Localization issues around equality that String.Empty will ensure are not a problem?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(18

洋洋洒洒 2024-07-13 22:44:55

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

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

.Length == 0 是最快的选项,但 .Empty 可以使代码稍微干净一些。

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

In .NET prior to version 2.0, "" creates an object while string.Empty creates no objectref, which makes string.Empty more efficient.

In version 2.0 and later of .NET, all occurrences of "" refer to the same string literal, which means "" is equivalent to .Empty, but still not as fast as .Length == 0.

.Length == 0 is the fastest option, but .Empty makes for slightly cleaner code.

See the .NET specification for more information.

羁拥 2024-07-13 22:44:55

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

what is the difference between String.Empty and "", and are they
interchangable

string.Empty is a read-only field whereas "" is a compile time constant. Places where they behave differently are:

Default Parameter value in C# 4.0 or higher

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

Case expression in switch statement

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

    case "":
        break;

}

Attribute arguments

[Example(String.Empty)]
// Error: An attribute argument must be a constant expression, typeof expression 
//        or array creation expression of an attribute parameter type
花落人断肠 2024-07-13 22:44:55

之前的答案对于 .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 的帖子评论中提到了这一点

综上所述,“” vs. String.Empty 的实际结果是 nil。 JIT 最终会解决这个问题。

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

The previous answers were correct for .NET 1.1 (look at the date of the post they linked: 2003). As of .NET 2.0 and later, there is essentially no difference. The JIT will end up referencing the same object on the heap anyhow.

According to the C# specification, section 2.4.4.5:
http://msdn.microsoft.com/en-us/library/aa691090(VS.71).aspx

Each string literal does not necessarily result in a new string instance. When two or more string literals that are equivalent according to the string equality operator (Section 7.9.7) appear in the same assembly, these string literals refer to the same string instance.

Someone even mentions this in the comments of Brad Abram's post

In summary, the practical result of "" vs. String.Empty is nil. The JIT will figure it out in the end.

I have found, personally, that the JIT is way smarter than me and so I try not to get too clever with micro-compiler optimizations like that. The JIT will unfold for() loops, remove redundant code, inline methods, etc better and at more appropriate times than either I or the C# compiler could ever anticipate before hand. Let the JIT do its job :)

(り薆情海 2024-07-13 22:44:55

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

String.Empty is a readonly field while "" is a const. This means you can't use String.Empty in a switch statement because it is not a constant.

长伴 2024-07-13 22:44:55

我倾向于使用 String.Empty 而不是 "" ,原因很简单,但并不明显:
"""" 不一样,第一个实际上有 16其中的零宽度字符。 显然,没有称职的开发人员会将零宽度字符放入他们的代码中,但如果他们确实这样做,这可能会成为维护的噩梦。

注意:

I tend to use String.Empty rather than "" for one simple, yet not obvious reason:
"" and "" are NOT the same, the first one actually has 16 zero width characters in it. Obviously no competent developer is going to put and zero width characters into their code, but if they do get in there, it can be a maintenance nightmare.

Notes:

  • I used U+FEFF in this example.

  • Not sure if SO is going to eat those characters, but try it yourself with one of the many zero-width characters

  • I only came upon this thanks to https://codegolf.stackexchange.com/

梅倚清风 2024-07-13 22:44:55

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

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 
}

Another difference is that String.Empty generates larger CIL code. While the code for referencing "" and String.Empty is the same length, the compiler doesn't optimize string concatenation (see Eric Lippert's blog post) for String.Empty arguments. The following equivalent functions

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

generate this 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 
}
故事和酒 2024-07-13 22:44:55

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

The above answers are technically correct, but what you may really want to use, for best code readability and least chance of an exception is String.IsNullOrEmpty(s)

凹づ凸ル 2024-07-13 22:44:55

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

All instances of "" are the same, interned string literal (or they should be). So you really won't be throwing a new object on the heap every time you use "" but just creating a reference to the same, interned object. Having said that, I prefer string.Empty. I think it makes code more readable.

梦过后 2024-07-13 22:44:55

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

String.Empty does not create an object whereas "" does. The difference, as pointed out here, is trivial, however.

滴情不沾 2024-07-13 22:44:55

使用String.Empty而不是""

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

参考: String.Empty 与 <代码>“”

Use String.Empty rather than "".

This is more for speed than memory usage but it is a useful tip. The
"" is a literal so will act as a literal: on the first use it is
created and for the following uses its reference is returned. Only one
instance of "" will be stored in memory no matter how many times we
use it! I don't see any memory penalties here. The problem is that
each time the "" is used, a comparing loop is executed to check if the
"" is already in the intern pool.
On the other side, String.Empty
is a reference to a "" stored in the .NET Framework memory zone.
String.Empty is pointing to same memory address for VB.NET and C#
applications. So why search for a reference each time you need ""
when you have that reference in String.Empty?

Reference: String.Empty vs ""

从此见与不见 2024-07-13 22:44:55
string mystring = "";
ldstr ""

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

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

ldsfld 将静态字段的值推送到计算堆栈上

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

string mystring = "";
ldstr ""

ldstr pushes a new object reference to a string literal stored in the metadata.

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

ldsfld pushes the value of a static field onto the evaluation stack

I tend to use String.Empty instead of "" because IMHO it's clearer and less VB-ish.

丑丑阿 2024-07-13 22:44:55

当您以视觉方式扫描代码时,“”会以与字符串着色的方式显示颜色。 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;

When you're visually scanning through code, "" appears colorized the way strings are colorized. string.Empty looks like a regular class-member-access. During a quick look, its easier to spot "" or intuit the meaning.

Spot the strings (stack overflow colorization isn't exactly helping, but in VS this is more obvious):

var i = 30;
var f = Math.Pi;
var s = "";
var d = 22.2m;
var t = "I am some text";
var e = string.Empty;
谜兔 2024-07-13 22:44:55

埃里克·利珀特 写道(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   +   { 默认(字符串),   "",   字符串.空}

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


Test details

Microsoft (R) Visual C# Compiler version 2.10.0.0 (b9fb1610)
AMD64 Release
[MethodImpl(MethodImplOptions.NoInlining)]
'SuppressJitOptimization' = false

Eric Lippert wrote (June 17, 2013):
"The first algorithm I ever worked on in the C# compiler was the optimizer that handles string concatenations. Unfortunately I did not manage to port these optimizations to the Roslyn codebase before I left; hopefully someone will get to that!"

Here are some Roslyn x64 results as of January 2019. Despite the consensus remarks of the other answers on this page, it does not appear to me that the current x64 JIT is treating all of these cases identically, when all is said and done.

Note in particular, however, that only one of these examples actually ends up calling String.Concat, and I'm guessing that that's for obscure correctness reasons (as opposed to an optimization oversight). The other differences seem harder to explain.


default(String)   +   { default(String),   "",   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

""   +   { default(String),   "",   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


Test details

Microsoft (R) Visual C# Compiler version 2.10.0.0 (b9fb1610)
AMD64 Release
[MethodImpl(MethodImplOptions.NoInlining)]
'SuppressJitOptimization' = false
早乙女 2024-07-13 22:44:55

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

string.Empty 出于验证目的被视为空值,如果在必需(属性)字段上使用它,则会引发验证错误; 其中“”将通过验证并且不会引发错误。

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

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

Coming at this from an Entity Framework point of view: EF versions 6.1.3 appears to treat String.Empty and "" differently when validating.

string.Empty is treated as a null value for validation purposes and will throw a validation error if it's used on a Required (attributed) field; where as "" will pass validation and not throw the error.

This problem may be resolved in EF 7+. Reference:
- https://github.com/aspnet/EntityFramework/issues/2610 ).

Edit: [Required(AllowEmptyStrings = true)] will resolve this issue, allowing string.Empty to validate.

晨与橙与城 2024-07-13 22:44:55

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

public void test(int i=0,string s="")
    {
      // Function Body
    }

Since String.Empty is not a compile-time constant you cannot use it as a default value in function definition.

public void test(int i=0,string s="")
    {
      // Function Body
    }
爱的那么颓废 2024-07-13 22:44:55

在大多数情况下使用 string.Empty 来提高可读性。 某些开发人员的视力可能无法区分 """ ",其中 string.Empty 很清楚。

对于参数默认值,仍然必须使用 "",但在这种情况下,我想知道为什么该方法不使用 string? = null 而不是默认参数。(对于视觉障碍者来说也更具可读性)

Use string.Empty in most cases for readability. Some developer's vision might not be able to tell the difference of "" and " ", where string.Empty is clear.

For parameter defaults will still have to use "", but in that case, I wonder why the method does not use string? = null instead for a defaulted param.(also more readable for the visually impared)

心是晴朗的。 2024-07-13 22:44:55

感谢您提供非常有用的答案。

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

if string + "" = ""

Thanks for a very informative answer.

Forgive my ignorance if I'm wrong. I'm using VB but I think if you test the length of an unassigned string (i.e. IS Nothing), it returns an error. Now, I started programming in 1969, so I've been left well behind, however I have always tested strings by concatenating an empty string (""). E.g. (in whatever language): -

if string + "" = ""

羁绊已千年 2024-07-13 22:44:55

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

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”意味着它包含某种值,但它是空的。

Everybody here gave some good theoretical clarification. I had a similar doubt. So I tried a basic coding on it. And I found a difference. Here's the difference.

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


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

So it seems "Null" means absolutely void & "String.Empty" means It contains some kind of value, but it is empty.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文