为什么要使用String.Format?

176
为什么有人会在使用C#和VB .NET时,选择使用String.Format而不是连接运算符(VB中的&和C#中的+)呢?
这两者之间的主要区别是什么?为什么大家都对使用String.Format感兴趣?我很好奇。

相关网站:https://dev59.com/tnVD5IYBdhLWcg3wU5-H - Nate Kohl
1
因为认为 + 运算符是邪恶的并且总是使用 string.Format 的人是错误的。 - Cheng Chen
1
在此还请参考我的被采纳的答案:https://dev59.com/mHA75IYBdhLWcg3w899J#3019525 - Mark Rushakoff
7个回答

411
我可以看到几个原因:
可读性
string s = string.Format("Hey, {0} it is the {1}st day of {2}.  I feel {3}!", _name, _day, _month, _feeling);

对比:

string s = "Hey," + _name + " it is the " + _day + "st day of " + _month + ".  I feel " + feeling + "!";

格式说明符 (这包括您可以编写自定义格式化程序的事实)

string s = string.Format("Invoice number: {0:0000}", _invoiceNum);

对比:

string s = "Invoice Number = " + ("0000" + _invoiceNum).Substr(..... /*can't even be bothered to type it*/)

字符串模板持久化

如果我想要将字符串模板存储在数据库中怎么办?使用字符串格式化:

_id         _translation
  1         Welcome {0} to {1}.  Today is {2}.
  2         You have {0} products in your basket.
  3         Thank-you for your order.  Your {0} will arrive in {1} working days.

对比:

_id         _translation
  1         Welcome
  2         to
  3         .  Today is
  4         . 
  5         You have
  6         products in your basket.
  7         Someone
  8         just shoot
  9         the developer.

201
+1 表示对某人或某事的赞同或支持,而 "Someone just shoot the developer." 则是一句幽默话,意思是希望某个开发者能够被枪击。注意,这只是一种玩笑,不应该被当作真正的威胁或暴力行为。 - Tim Schmelter
13
针对你的 'substr' 示例,你应该实际使用 _invoiceNum.ToString("00000")。ToString() 支持与 string.Format() 相同的复合格式设置。当你只有一个需要格式化的值时,ToString() 通常比 Format() 更简单/更清晰。 - Ash
28
我不知道你怎么想,但我觉得没有格式的那个更好读。特别是当我想知道应该放在哪里时,我必须来回查看文本和参数,并且这还没有包括错误计数和出错的可能性(如果参数少于10个,可能不是问题,但超过10个会变得不好玩)。正如艾什利所指出的,如果使用ToString方法内置的格式化,则第二点无效。我同意第三点,但我不认为我会在除了那个特定情况之外的其他情况下使用它。 - Yandros
8
+1 - 同样地,使用String.Format会更快且开销更小。每次使用连接符+/ 都需要重新构建字符串,这在大型字符串对象中可能非常糟糕。 string 的定义非常明确,它应该被定义一次并多次重复使用。(许多书籍以 concat 为例子来教我们,但从未提到过性能!!) 构建字符串应该使用StringBuilder,它比String.Format还要快!非常好的答案-开发人员需要学会编写易读的代码,并考虑性能! - Piotr Kula
10
@ppumkin 这不是真的。当您在单个表达式中指定一定数量的+运算符时,编译器会将其转换为对string.Concat的单个调用,并且不会创建任何中间字符串。实际上,使用StringBuilder会降低性能,而不是提高性能,因为Concat可以从一开始就创建一个恰好合适的内部缓冲区。当您有某种循环或其他控制块时,使得编译器无法在编译时知道正在连接的字符串数量时,才会使用SB。 - Servy
显示剩余13条评论

92

使用String.Format不仅让代码更易读且增加了一些操作符,而且如果您的应用程序具有国际化功能,也是非常有益的。 许多时候,变量是数字或关键字,这些数字或关键字在不同语言中的顺序可能会不同。 通过使用String.Format,您的代码可以保持不变,而不同的字符串将进入资源文件中。 因此,代码最终将是:

String.Format(resource.GetString("MyResourceString"), str1, str2, str3);

尽管你的资源字符串最终变成了

英语: "blah blah {0} blah blah {1} blah {2}"

俄语: "{0} blet blet blet {2} blet {1}"

在俄语中可能有不同的规则来处理事物的位置,因此顺序或句子结构可能会有所不同。


7
我以前从未考虑过String.Format在本地化方面的应用,直到我看到了这个。 - Pharap

27

首先,我找到

string s = String.Format(
    "Your order {0} will be delivered on {1:yyyy-MM-dd}. Your total cost is {2:C}.",
    orderNumber,
    orderDeliveryDate,
    orderCost
);

比起其他方式,更加易读、易写和易维护

string s = "Your order " +
           orderNumber.ToString() +
           " will be delivered on " +
           orderDeliveryDate.ToString("yyyy-MM-dd") +
           "." +
           "Your total cost is " +
           orderCost.ToString("C") + 
           ".";

看看下面的代码,它更易于维护

string s = String.Format(
    "Year = {0:yyyy}, Month = {0:MM}, Day = {0:dd}",
    date
);

相比于需要重复三次使用date的替代方法,String.Format提供的格式说明符使得字符串输出更加灵活,易读、易写、易于维护。此外,String.Format能够更容易地处理语言文化上的问题。

第三,当性能很重要时,String.Format会优于字符串拼接。在幕后,它使用了StringBuilder并避免了Schlemiel the Painter problem问题。


1
请注意:在一行中重复使用“ +”并不会出现Schlemiel the Painter问题。它们会被编译为单个的 string.Concat 调用。 - porges
实际上,string.Concat 在性能上优于 String.Format(至少对于单个短字符串)。然而,有许多因素涉及其中(这些因素可能使一个比另一个更有效)。在执行时间很重要的情况下(重复执行相同操作无数次),使用 string.Concat 节省的速度将是微不足道的。 - jahu
你不必显式地调用ToString()方法。"Your order " + orderNumber.ToString()"Your order " + orderNumber是相同的。 - Arthur Stankevich
1
实际上,+ 运算符确实存在 Schlemiel 画家问题的问题,只是有点延迟。 String.Concat 只接受最多四个参数,因此通过 + 运算符连接五个或更多字符串的方法是将它们分组为最多四个,然后连接这些字符串,以此类推。 - octothorpentine

17

有几个原因:

  1. String.Format()非常强大。您可以直接在格式字符串中使用简单的格式指示符(如固定宽度、货币、字符长度等)。您甚至可以为诸如扩展枚举、将特定输入映射到更复杂的输出或本地化等内容创建自己的格式提供程序。
  2. 通过将格式字符串放入配置文件中,您可以做一些强大的事情。
  3. String.Format()通常更快,因为它在幕后使用StringBuilder和高效的状态机,而在.Net中进行字符串连接相对较慢。对于小字符串,差异可以忽略不计,但随着字符串大小和替换值数量的增加,差异可能会变得明显。
  4. String.Format()实际上对许多程序员来说更熟悉,特别是那些来自使用旧版C printf()函数的背景的程序员。

最后,不要忘记StringBuilder.AppendFormat()。实际上,String.Format()在幕后使用此方法*,直接转到StringBuilder可以给您一种混合方法:对于大字符串的某些部分明确使用.Append()(类似于连接),在其他部分使用.AppendFormat()


* [编辑]原始答案现在已有8年之久,我后来看到一种迹象表明,在.Net中添加字符串插值时可能已经发生了变化。但是,我还没有回到参考源以验证更改。


3
通常情况下,string.Format 不会比几个字符串连接运算更快,因为格式化字符串还需要被解析和复制。 - Dirk Vollmar
谢谢更新。实际上,与连接和 ToString() 相比,string.Format 还有另一个注意事项:值类型会被装箱,如此处所述:http://jeffbarnes.net/blog/post/2006/08/08/Avoid-Boxing-When-Using-StringFormat-with-Value-Types.aspx。尽管如此,`string.Format` 是一个非常强大和优秀的工具,性能通常不是最重要的标准。 - Dirk Vollmar
6
关于#3的参考:字符串构建器并不比连接更快。支持它的“测试”实际上在不同行(或循环中)进行连接,这种错误被认为是真的。当所有连接在单行上完成时,编译器将其转换为string.append(paramaarray)调用,这是所有选项中最有效的。在string.append调用期间,目标字符串的确切大小提前已知,并且可以立即分配每个字符只复制一次。 - csauve

5
String.Format提供了许多选项,除了拼接运算符之外,还能够指定添加到字符串中的每个项目的特定格式。
有关可能性的详细信息,请阅读MSDN上名为复合格式的部分。它解释了String.Format(以及支持复合格式的xxx.WriteLine和其他方法)相对于常规连接操作符的优势。

2
字符串格式化虽然好看,但比拼接慢。话不多说。 - roblem

4

这个问题中,有关于性能方面的有趣内容。

然而出于可读性的原因,我个人仍然建议使用string.Format,除非性能对您至关重要。

string.Format("{0}: {1}", key, value);

比起“更可读”
key + ": " + value

例如,这也提供了良好的关注点分离。这意味着您可以具有<html>(结构)、<css>(样式)和<js>(交互)的不同类型文件。
string.Format(GetConfigValue("KeyValueFormat"), key, value);

将您的键值格式从"{0}: {1}"更改为"{0} - {1}"成为配置更改而不是代码更改。

string.Format还具有许多内置的格式提供,例如整数、日期格式等。


如果您对性能方面感兴趣,您可能会发现这个问题很有意思:https://dev59.com/GnRA5IYBdhLWcg3w_C8w - Joel Coehoorn

2

不推荐像'string +"Value"+ string'这样编写字符串的原因之一是本地化。在进行本地化的情况下,我们希望本地化字符串格式正确,这可能与编码语言非常不同。

例如,我们需要用不同的语言显示以下错误:

MessageBox.Show(String.Format(ErrorManager.GetError("PIDV001").Description, proposalvalue.ProposalSource)

在此处,

'ErrorCollector.GetError("ERR001").ErrorDescription'返回一个字符串,例如"Your ID {0} is not valid"。这个消息必须在很多语言中进行本地化。如果是这种情况,我们不能在C#中使用+号,我们需要使用string.format。


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