无法将String.Empty用作可选参数的默认值

98
我正在阅读Bill Wagner的《Effective C#》。在“Item 14 - Minimize Duplicate Initialization Logic”中,他展示了使用构造函数中新的可选参数特性的以下示例: public MyClass(int initialCount = 0, string name = "") 请注意,他使用了""而不是string.Empty
他评论道:
“您会注意到(在上面的示例中)第二个构造函数在name参数的默认值上指定了“”,而不是更常用的string.Empty。那是因为string.Empty不是编译时常量。它是在string类中定义的静态属性。因为它不是编译常量,所以不能将其用作参数的默认值。”
如果我们无法在所有情况下使用string.Empty静态,则这是否会破坏其目的?我认为我们会使用它来确保我们拥有一个系统独立的引用空字符串的方法。我的理解错了吗?谢谢。
更新
只是跟进一下评论。根据MSDN:
“每个可选参数都有一个默认值作为其定义的一部分。如果未为该参数发送任何参数,则使用默认值。默认值必须是常量。”
那么我们也无法使用System.Environment.NewLine,也不能使用新实例化的对象作为默认值。我还没有使用VS2010,这令人失望!

2
我不知道在不同平台上空字符串的表示方式是否有任何差异。这与换行符不同。 - Tom Cabanski
1
CLR而不是“System”是决定“”是否为空字符串的因素。因此,我认为您可以安全地假设“”是在符合CLR实现的系统独立方式下引用字符串的方法。 - Chris Taylor
我只想指出字符串中表示换行的方式在不同平台上是相同的;它总是"\n"。不同的是如何在文件中表示它,你可以在任何系统上的文件中获得任何行尾样式,因此它会被自动检测并在文件读取代码中处理,就像字符编码一样。System.Environment.NewLine常量建议写入文件的内容,但是如果您始终如一地使用"\n",那么是安全的。 - jimrandomh
@jimrandomh:“我只想指出的是,在字符串中表示换行符的方式在平台之间并没有区别”——这完全与此讨论无关。“不同的是如何表示在文件中”——这就是System.Environment.NewLine的值。“您可以在任何系统上的文件中获得任何行尾样式”——再次无关(也不正确,除非您认为唯一的系统是Windows系统)… System.Environment.NewLine是默认表示,这是写入时的表示。 - Jim Balter
可能是为什么String.Empty不是常量?的重复问题。 - Saul
显示剩余5条评论
7个回答

72

自C# 2.0编译器以来,String.Empty已经没有太多意义,实际上在许多情况下它是一种悲观化,因为编译器可以内联引用""但无法做到同样的事情使用String.Empty

在C#1.1中,它很有用,可以避免创建许多包含空字符串的独立对象,但那些日子已经过去了。直接使用""即可。


7
即使在.NET 1.1中,它也不会创建“很多”独立的对象。我记不清1.1和2.0之间在这方面的差异细节,但字符串字面量插值并不是仅在2.0中引入的。 - Jon Skeet
谢谢澄清。我查了一下,没有找到C# 2.0变化的好总结,虽然我确信以前读过一个。不过我找到了一个来自2008年的StackOverflow答案,其中包含更多技术信息的链接。https://dev59.com/XXVC5IYBdhLWcg3w51hv - Andy Mortimer
1
我会点头赞同,尽管我不喜欢说string.Empty没有什么意义,因为我经常使用它。我觉得这样看起来更清晰,但这只是我的个人意见。有很多地方不能使用string.Empty,在这些情况下我使用""也没有问题。 - xximjasonxx
我之前使用string.Empty仅仅是因为它被“推荐”使用,但我从来没有真正喜欢它。直接使用""字面量看起来更直观和清晰,并且由于字符串池化,也更经济。 - tames
17
我发现使用 String.Empty 来快速识别空字符串比仔细检查 "" 中是否隐藏了撇号或其他字符要容易得多。尽管解释很好,但我还是要点个赞。 - NotMe
11
+1 Chris。在VS中,您无法搜索""的用法(除了进行标准查找外,它还将返回每个文本匹配项,包括注释和标记)。但您可以使用string.Empty搜索代码特定的用法。 - MutantNinjaCodeMonkey

55

如果您确实想将空字符串用作可选参数的值,那么没有什么阻止您定义自己的常量:

const string String_Empty = "";

public static void PrintString(string s = String_Empty)
{
    Console.WriteLine(s);
}

[顺便提一下,从总体上讲,更喜欢使用String.Empty而不是""的一个原因(其他答案中没有提到)是存在各种Unicode字符(零宽连接器等),这些字符实际上对肉眼来说是看不见的。因此,看起来像""的东西不一定是空字符串,然而使用String.Empty,你就知道你使用的是什么。我认识到这不是常见的错误来源,但确实可能存在。]


2
还有一些不可见的标识符字符,因此看起来像String_Empty的东西并不一定是。Unicode标准中有一个被大多数人忽略的安全注意事项章节。 - Jim Balter

26

原始问题如下:

我认为我们将使用它来确保我们有一种系统独立的方式来引用空字符串。

空字符串在哪些方面可能因系统而异?它总是一个没有字符的字符串!如果我发现一个实现中string.Empty == ""返回false,我会非常害怕:) 这与Environment.NewLine之类的东西不同。

关于Counter Terrorist发布的奖赏:

我希望String.Empty可以在下一个C#版本中用作默认参数。:D

好吧,这肯定不会发生。

尽管我个人也希望有一个非常不同的默认机制,但可选参数的工作方式从.NET开始就一直存在-并且始终意味着将常量嵌入到元数据中,以便调用代码可以将该常量复制到调用站点,如果没有提供相应的参数。

对于string.Empty来说,这真的毫无意义-使用""将做到您想要的;使用字符串字面量是否那么难受?(我在任何地方都使用字面量-我从不使用string.Empty,但这是另一个论点。)

这就是让我对这个问题感到惊讶的原因-抱怨围绕着一些实际上不会导致真正问题的事情。在需要默认值在执行时计算的情况下,它更为重要,因为它可能实际上会有所不同。例如,我可以想象某些情况,您希望能够调用具有DateTime参数的方法,并使其默认为“当前时间”。目前,我所知道的唯一近似优雅的解决方法是:

public void RecordTime(string message, DateTime? dateTime = null)
{
    var realDateTime = dateTime ?? DateTime.UtcNow;
}

...但这并不总是合适的。

总之:

  • 我非常怀疑这会成为C#的一部分
  • 对于string.Empty来说,这毫无意义
  • 对于其他实际上并不总是有相同值的值,这确实可能会很麻烦

这实际上是解决问题的好方法。同样的方法可以用来携带/设置其他设备相关变量,比如“Environment.Newline”..你的示例中唯一缺少的是对变量为空的检查,并向开发人员抛出异常,告诉他虽然它是可空的,但不被接受。“if(dateTime == null){ throw new ArgumentException("The dateTime parameter must be set. Nullable type used for device independent variable set.");}”或类似的东西。但我真的很喜欢这个!你的方法还有什么需要注意的地方吗? - MaxOvrdrv
1
@MaxOvrdrv:你不希望它为空时成为一个错误——整个意义在于当它为空时,你计算一个默认值。但是需要注意的是,它不允许将空传递作为有效值本身。 - Jon Skeet
你说得完全正确。我的错。-- 是的,那将是唯一真正的警告... 这也不算太糟糕。再次感谢您发布这个解决方案! :) - MaxOvrdrv
但是使用 'string.Empty` 比 "" 字符串字面常量更易读。其他人是否同意从可读性的角度来看,使用 string.Empty 更好? - jbob77435
@jbob77435:我完全不同意 - 我认为""更易读。 - Jon Skeet
@JonSkeet 嗯,我想这个问题是主观的。但是由于你通过你出色的书《C#深度剖析》教给了我几乎所有的知识,所以我发现自己在这个问题上对自己的品味产生了质疑。 - jbob77435

7

我从不使用string.Empty,我看不出它有什么意义。也许对于编程新手来说更容易理解,但我甚至怀疑它对他们是否有用。


2
也许它可以避免混淆 """ ",但我不能说 " " 是很常见的。 - Greg
8
我建议那些无法区分它们之间差别的人,要么需要更好的眼镜,要么降低屏幕分辨率。我视力不好,但我也必须处理包含这两者的大量代码,却从未犯过这种错误。 - Hans Olsson
4
string.Empty 是帮助程序员准确表达意图的实用工具。"" 对意图不做任何说明,如果程序员的意图是像这样初始化变量 "lol",但却忘记了... 在这种情况下,有无限的可能性,而 string.Empty 可以派上用场并且能够更好地完成工作。 - usefulBee

4

我认为string.Empty背后的想法是增强可读性。 它不像换行符,不同平台之间表示方式有所差异。很遗憾它不能用作默认参数。但是,如果在Windows和类似Linux上的Mono之间移植,它不会引起任何问题。


5
我认为你说的很有道理,一些人认为String.Empty更易读……不过我个人觉得这有点儿疯狂。 "" 可能是最常见的字符串了,每个人都看过无数次,怎么会不可读呢? String.Empty 就像 Int32.Zero 一样没什么用。 - Tim Goodman

3

提醒一下,看起来对于传递给属性构造函数的值也施加了同样的限制 - 它们必须是常量。由于string.empty被定义为:

public static readonly string Empty

由于它不是实际上的常数,因此不能使用。


2

我使用string.Empty只是为了代码可读性好。如果其他人需要阅读/更改我的代码,他们会知道我想要检查或设置某些内容为空字符串。仅使用""有时会导致错误和混淆,因为我可能只是忘记把我想要的字符串放在那里。

例如:

if(someString == string.Empty)
{

}

vs

if(someString == "")
{

}

第一个if语句对我来说更加明确和易读。因为这只是个人偏好,所以我并不认为必须使用string.Empty而不是""

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