什么时候使用String.Format和string连接更好?

我有一小段代码parsing一个索引值,以确定一个单元格input到Excel中。 这让我想到…

有什么区别

xlsSheet.Write("C" + rowIndex.ToString(), null, title); 

 xlsSheet.Write(string.Format("C{0}", rowIndex), null, title); 

这个比那个好吗? 为什么?

在C#之前

说实话,我觉得第一个版本更简单 – 虽然我简化为:

 xlsSheet.Write("C" + rowIndex, null, title); 

我怀疑其他的答案可能会谈论性能打击,但说实话,它是最小的, 如果目前 – 而且这个串联版本不需要parsing格式string。

格式化string对于本地化等是非常有用的,但是在这种情况下,这种连接更简单,而且工作起来也是如此。

用C#6

string插值使得在C#6中读取很多东西变得更简单。在这种情况下,第二个代码变成:

 xlsSheet.Write($"C{rowIndex}", null, title); 

这可能是IMO的最佳select。

我最初的喜好(来自C + +背景)是为String.Format。 我稍后放弃了,原因如下:

  • string连接可以说是“更安全”的。 它发生在我身上(而且我看到它发生在其他几个开发人员身上)删除一个参数,或弄错了参数顺序。 编译器不会根据格式string检查参数,并最终产生一个运行时错误(也就是说,如果你足够幸运的话,不要使用一个难懂的方法,比如logging一个错误)。 通过串联,删除参数不太容易出错。 你可能会认为错误的可能性非常小,但可能会发生。

– string连接允许空值, String.Format不允许。 写入“ s1 + null + s2 ”不会中断,只是将null值视为String.Empty。 那么,这可能取决于你的具体情况 – 有些情况下,你想要一个错误,而不是默默地忽略空名字。 但即使在这种情况下,我个人更喜欢自己检查空值,并抛出特定的错误,而不是从String.Format中获取标准的ArgumentNullException。

  • string连接执行得更好。 上面的一些post已经提到这(没有真正解释为什么,这决定了我写这篇文章:)。

想法是.NET编译器是足够聪明的转换这段代码:

 public static string Test(string s1, int i2, int i3, int i4, string s5, string s6, float f7, float f8) { return s1 + " " + i2 + i3 + i4 + " ddd " + s5 + s6 + f7 + f8; } 

对此:

 public static string Test(string s1, int i2, int i3, int i4, string s5, string s6, float f7, float f8) { return string.Concat(new object[] { s1, " ", i2, i3, i4, " ddd ", s5, s6, f7, f8 }); } 

在String.Concat的引擎下会发生什么很容易猜测(使用reflection器)。 数组中的对象通过ToString()被转换为它们的string。 然后计算总长度,只分配一个string(总长度)。 最后,通过wstrcpy将每个string复制到一个不安全的代码片段中。

原因String.Concat速度更快吗? 那么,我们都可以看看String.Format在做什么 – 你会惊讶于处理格式string所需的代码量。 在此之上(我已经看到有关内存消耗的意见), String.Format内部使用StringBuilder。 就是这样:

StringBuilder builder = new StringBuilder(format.Length + (args.Length * 8));

因此,对于每一个通过的论点,它保留8个字符。 如果论点是一位数字,那么太糟糕了,我们有一些浪费的空间。 如果参数是一个在ToString()上返回一些长文本的自定义对象,那么甚至可能需要一些重新分配(当然最坏的情况)。

与此相比,连接只会浪费对象数组的空间(不要太多,考虑到它是一个引用数组)。 没有格式说明符的parsing,也没有中间的StringBuilder。 拳击/拆箱开销在两种方法中都存在。

我会去String.Format的唯一原因是涉及到本地化。 将格式string放在资源中可以让你支持不同的语言,而不会混淆代码(考虑格式化值根据语言而改变顺序的情况,例如“{0}小时和{1}分钟”在日语中可能看起来完全不同: )。


总结我的第一篇(也是相当长的)文章:

  • 最好的方式(就性能而言可维护性/可读性)我使用string连接,没有任何ToString()调用
  • 如果你在表演之后,让ToString()自己调用来避免装箱(我有点偏向于可读性) – 和你问题中的第一个选项一样
  • 如果您向用户显示本地化的string(不是这里的情况), String.Format()有一个边缘。

我认为第一个select是更可读的,这应该是你的主要关注。

 xlsSheet.Write("C" + rowIndex.ToString(), null, title); 

string.Format在底层使用了一个StringBuilder(与reflection器一起检查),所以它不会有任何性能上的好处,除非你做了大量的连接。 对于你的情况来说,它会变慢,但事实上这个微型性能优化的决定在大多数情况下是不合适的,除非你处于循环之中,否则你应该把重点放在代码的可读性上。

无论哪种方式,首先写可读性,然后使用性能分析器来确定你的热​​点,如果你真的认为你有性能问题。

对于一个简单的单一连接的情况,我觉得这是不值得string.Format的复杂性(我没有testing,但我怀疑,对于这样一个简单的例子, string.Format 可能会稍微慢一点,什么与格式stringparsing和所有)。 像Jon Skeet一样,我更喜欢不显式调用.ToString() ,因为这会由string.Concat(string, object)重载隐式完成,我认为代码更清晰,更容易阅读。

但是对于多个连接(有多less是主观的),我绝对更喜欢string.Format 。 在某个时候,我认为可读性和性能都会不必要地连接在一起。

如果格式string有很多参数(同样,“many”是主观的),我通常更倾向于在replace参数中包含注释索引,以免丢失哪个值到哪个参数。 一个人为的例子:

 Console.WriteLine( "Dear {0} {1},\n\n" + "Our records indicate that your {2}, \"{3}\", is due for {4} {5} shots.\n" + "Please call our office at 1-900-382-5633 to make an appointment.\n\n" + "Thank you,\n" + "Eastern Vetinary", /*0*/client.Title, /*1*/client.LastName, /*2*/client.Pet.Animal, /*3*/client.Pet.Name, /*4*/client.Pet.Gender == Gender.Male ? "his" : "her", /*5*/client.Pet.Schedule[0] ); 

如果你的string更复杂,连接了许多variables,那么我会selectstring.Format()。 但是对于string的大小和variables的数量在你的情况下,我会去你的第一个版本,这是更斯巴达 。

我看了一下String.Format(使用Reflector),它实际上创build了一个StringBuilder,然后调用AppendFormat。 所以它比concat更快速的多重stirngs。 最快(我相信)将创build一个StringBuilder和手动调用Append。 当然,“许多”的数量是猜测。 我会使用+(实际上,因为我主要是一个VB程序员)像你的例子一样简单。 因为它变得更复杂,我使用String.Format。 如果有大量的variables,那么我会去一个StringBuilder和附加,例如,我们有代码构build代码,我用一行实际代码输出一行生成的代码。

似乎有一些关于每个操作创build了多less个string的猜测,所以我们举几个简单的例子。

 "C" + rowIndex.ToString(); 

“C”已经是一个string。
rowIndex.ToString()创build另一个string。 (@manohard – 不会出现rowIndex的装箱)
然后我们得到最后一个string。
如果我们以这个为例

 String.Format("C(0)",rowIndex); 

那么我们有一个string“C {0}”
rowIndex被装箱以传递给函数
一个新的stringbuilder被创build
在string生成器上调用AppendFormat – 我不知道AppendFormat函数的细节,但是假设它是超高效的,它仍然需要将装箱的rowIndex转换为string。
然后将stringbuilder转换成一个新的string。
我知道StringBuilders试图防止无意义的内存拷贝发生,但String.Format仍然以相对于纯连接的额外开销结束。

如果我们现在举一个例子再多一些string

 "a" + rowIndex.ToString() + "b" + colIndex.ToString() + "c" + zIndex.ToString(); 

我们有6个string开始,这将是所有情况下相同。
使用连接我们也有4个中间string加上最后的结果。 这是通过使用String,Format(或StringBuilder)消除的中间结果。
请记住,要创build每个中间string,必须将前一个string复制到新的内存位置,这不仅仅是内存分配可能很慢。

这个例子可能太微不足道了。 事实上,我认为在大多数情况下,编译器可以优化任何差异。

但是,如果我不得不猜测我会给string.Format()一个更复杂的场景的边缘。 但是这更多的是直觉,它可能会更好地利用缓冲区而不是生成多个不可变的string,而不是基于任何实际的数据。

我喜欢String.Format,因为可以使你的格式化文本更容易遵循和阅读比内联,也更灵活,让你格式化你的参数,但是像你这样的短使用我没有看到连接问题。

对于循环内部或大string中的连接,你应该总是尝试使用StringBuilder类。

我同意上面的许多观点,我相信应该提到的另一点是代码可维护性。 string.Format允许更改代码。

即我有一条消息"The user is not authorized for location " + location"The User is not authorized for location {0}"

如果我曾经想改变消息说: location + " does not allow this User Access""{0} does not allow this User Access"

与string.Format我所要做的就是改变string。 为了连接,我必须修改那个消息

如果在多个地方使用可以节省时间的分配。

当格式模板(“C {0}”)存储在configuration文件(如Web.config / App.config)中时,string.Format可能是更好的select。

我做了一些分析各种string方法,包括string.Format,StringBuilder和string连接。 string连接几乎总是胜过构buildstring的其他方法。 所以,如果性能是关键,那么它更好。 但是,如果性能不重要,那么我个人发现string.Format在代码中更容易遵循。 (但这是一个主观原因)然而,StringBuilder在内存利用率方面可能是最高效的。

我更喜欢关于性能的String.Format

我的印象是string.format比这个testing快了3倍

 string concat = ""; System.Diagnostics.Stopwatch sw1 = new System.Diagnostics.Stopwatch (); sw1.Start(); for (int i = 0; i < 10000000; i++) { concat = string.Format("{0}{1}{2}{3}{4}{5}{6}{7}{8}{9}{10}","1", "2" , "3" , "4" , "5" , "6" , "7" , "8" , "9" , "10" , i); } sw1.Stop(); Response.Write("format: " + sw1.ElapsedMilliseconds.ToString()); System.Diagnostics.Stopwatch sw2 = new System.Diagnostics.Stopwatch(); sw2.Start(); for (int i = 0; i < 10000000; i++) { concat = "1" + "2" + "3" + "4" + "5" + "6" + "7" + "8" + "9" + "10" + i; } sw2.Stop(); 

string.format花了4.6秒,当使用“+”花了1.6秒。

与String.Format相比,string连接需要更多的内存。 所以连接string的最好方法是使用String.Format或System.Text.StringBuilder对象。

我们来看第一种情况:“C”+ rowIndex.ToString()我们假设rowIndex是一个值types,所以ToString()方法必须将Box转换为String,然后CLR为包含两个值的新string创build内存。

在string.Format需要对象参数,并将rowIndex作为一个对象,并将其转换为内部stringoffboards会有拳击,但它是内在的,也不会像第一种情况占用尽可能多的内存。

对于简短的string,我猜这不重要