我有一小段代码正在解析索引值以确定输入Excel的单元格。这让我想到了......
之间有什么区别
xlsSheet.Write("C" + rowIndex.ToString(), null, title);
和
xlsSheet.Write(string.Format("C{0}", rowIndex), null, title);
一个人比另一个“更好”吗?为什么?
答案 0 :(得分:152)
我最初的偏好(来自C ++背景)是针对String.Format的。由于以下原因,我稍后放弃了这个:
- 字符串连接允许空值,String.Format
不允许。写“s1 + null + s2
”不会中断,它只是将null值视为String.Empty。嗯,这可能取决于您的具体情况 - 有些情况下您需要一个错误而不是默默地忽略null FirstName。然而,即使在这种情况下,我个人更喜欢自己检查空值并抛出特定错误,而不是我从String.Format获得的标准ArgumentNullException。 德尔>
Idea是.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的引擎下发生的事情很容易猜到(使用Reflector)。数组中的对象通过ToString()转换为其字符串。然后计算总长度并且仅分配一个字符串(具有总长度)。最后,在一些不安全的代码片段中,每个字符串都会通过wstrcpy复制到结果字符串中。
原因String.Concat
更快?好吧,我们都可以看看String.Format
正在做什么 - 您会对处理格式字符串所需的代码量感到惊讶。除此之外(我见过关于内存消耗的评论),String.Format
在内部使用了StringBuilder。方法如下:
StringBuilder builder = new StringBuilder(format.Length + (args.Length * 8));
因此,对于每个传递的参数,它保留8个字符。如果参数是一位数的值,那么太糟糕了,我们有一些浪费的空间。如果参数是在ToString()
上返回一些长文本的自定义对象,那么甚至可能需要一些重新分配(当然,最糟糕的情况)。
与此相比,连接只会浪费对象数组的空间(不要太多,考虑到它是一个引用数组)。格式说明符没有解析,也没有中间StringBuilder。两种方法都存在装箱/拆箱开销。
我选择String.Format的唯一原因是涉及本地化时。将格式字符串放在资源中允许您支持不同的语言而不会弄乱代码(考虑格式化值根据语言改变顺序的情况,即“{0}小时和{1}分钟后”在日语中看起来可能大不相同: )。
总结我的第一篇(也是很长篇)帖子:
ToString()
调用ToString()
电话以避免拳击(我有点偏向于可读性) - 与问题中的第一个选项相同String.Format()
有优势。答案 1 :(得分:105)
在C#6之前
老实说,我认为第一个版本更简单 - 尽管我将其简化为:
xlsSheet.Write("C" + rowIndex, null, title);
我怀疑其他答案可能谈论性能损失,但说实话,如果存在,它将是最小的 - 并且此级联版本不需要解析格式字符串。
格式化字符串非常适合本地化等目的,但在这种情况下,连接更简单,也可以正常工作。
使用C#6
字符串插值使得在C#6中读取的内容更加简单。在这种情况下,您的第二个代码变为:
xlsSheet.Write($"C{rowIndex}", null, title);
这可能是最好的选择,IMO。
答案 2 :(得分:5)
我认为第一个选项更具可读性,应该是您最关心的问题。
xlsSheet.Write("C" + rowIndex.ToString(), null, title);
string.Format在底层使用StringBuilder(使用reflector检查),因此除非您进行大量连接,否则它不会有任何性能优势。对于您的场景来说速度会慢一些,但实际情况是这种微观性能优化决策在大多数情况下是不合适的,除非您处于循环中,否则您应该专注于代码的可读性。
无论哪种方式,首先编写可读性,然后使用performance profiler标识您的热点,如果您确实认为自己存在性能问题。
答案 3 :(得分:5)
对于一个简单的单一连接的简单情况,我觉得它不值得string.Format
的复杂性(我没有测试过,但我怀疑对于像这样的简单情况,{{1 可能稍微慢一些,格式化字符串解析和所有)。和Jon Skeet一样,我更喜欢不明确地调用string.Format
,因为这将由.ToString()
重载隐式完成,我认为代码看起来更干净,更容易阅读而没有它。
但是对于多个连接(多少是主观的),我绝对更喜欢string.Concat(string, object)
。在某个时刻,我认为可读性和性能都会因连接而受到不必要的影响。
如果格式字符串有很多参数(同样,“很多”是主观的),我通常更喜欢在替换参数上包含注释索引,以免我忘记哪个值转到哪个参数。一个人为的例子:
string.Format
在我看来,我给出的示例有点令人困惑,因为我似乎在这里使用了两个连接和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 Veterinary",
/*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]
);
。是的,从逻辑上和词汇上来说,这就是我所做的。但是这些连接都将被编译器 1 优化掉,因为它们都是字符串文字。所以在运行时,会有一个字符串。所以我想我应该说我更喜欢在运行时避免许多连接。
当然,这个主题的大部分内容现在都已过时,除非你仍然坚持使用C#5或更早版本。现在我们有interpolated strings,为了便于阅读,几乎在所有情况下都优于string.Format
。这些天,除非我只是将值直接连接到字符串文字的开头或结尾,否则我几乎总是使用字符串插值。今天,我会像这样写下我之前的例子:
string.Format
这样就失去了编译时串联。编译器会将每个插值字符串转换为对Console.WriteLine(
$"Dear {client.Title} {client.LastName},\n\n" +
$"Our records indicate that your {client.Pet.Animal}, \"{client.Pet.Name}\", " +
$"is due for {(client.Pet.Gender == Gender.Male ? "his" : "her")} " +
$"{client.Pet.Schedule[0]} shots.\n" +
"Please call our office at 1-900-382-5633 to make an appointment.\n\n" +
"Thank you,\n" +
"Eastern Veterinary"
);
的调用,并且它们的结果在运行时连接在一起。这意味着这是对可读性的运行时性能的牺牲。大多数时候,这是值得的牺牲,因为运行时间的惩罚可以忽略不计。但是,在性能关键代码中,您可能需要分析不同的解决方案。
<子> 1 您可以在the C# specification:
中看到这一点...在常量表达式中允许使用以下结构:
...
- 预定义的+ ...二元运算符...
您也可以使用一些代码验证它:
string.Format
答案 4 :(得分:3)
如果您的字符串更复杂,并且连接了许多变量,那么我会选择string.Format()。但是对于字符串的大小和在你的情况下连接的变量的数量,我会使用你的第一个版本,它更多spartan。
答案 5 :(得分:3)
我看了一下String.Format(使用Reflector),它实际上创建了一个StringBuilder,然后在其上调用AppendFormat。因此,它比连续多次搅拌更快。最快(我相信)将创建一个StringBuilder并手动调用Append。当然,“很多”的数量可供猜测。 我会使用+(实际上&amp;因为我主要是VB程序员)来做一些像你的例子一样简单的事情。随着它变得越来越复杂,我使用String.Format。如果有很多变量,那么我会选择StringBuilder和Append,例如,我们有代码构建代码,在那里我使用一行实际代码输出一行生成的代码。
似乎有一些关于为每个操作创建了多少字符串的猜测,所以让我们举几个简单的例子。
"C" + rowIndex.ToString();
“C”已经是一个字符串
rowIndex.ToString()创建另一个字符串。 (@manohard - 不会发生rowIndex拳击)
然后我们得到最后的字符串。
如果我们以
String.Format("C(0)",rowIndex);
然后我们将“C {0}”作为字符串
rowIndex被装箱以传递给函数
创建一个新的stringbuilder
在字符串构建器上调用AppendFormat - 我不知道AppendFormat如何运行的细节,但我们假设它是超高效的,它仍然需要将盒装的rowIndex转换为字符串。
然后将stringbuilder转换为新的字符串
我知道StringBuilders试图阻止无意义的内存副本发生,但是与普通连接相比,String.Format仍然会产生额外的开销。
如果我们现在举几个字符串的例子
"a" + rowIndex.ToString() + "b" + colIndex.ToString() + "c" + zIndex.ToString();
我们有6个字符串开头,对所有情况都是一样的。
使用连接,我们还有4个中间字符串加上最终结果。通过使用String,Format(或StringBuilder)消除了那些中间结果
请记住,要创建每个中间字符串,必须将前一个字符串复制到新的内存位置,而不仅仅是内存分配可能很慢。
答案 6 :(得分:2)
这个例子可能太微不足道,无法发现差异。事实上,我认为在大多数情况下,编译器可以完全消除任何差异。
然而,如果我不得不猜测我会给string.Format()
更复杂的场景带来优势。但这更像是一种直觉,它可能会更好地利用缓冲区而不是产生多个不可变字符串,而不是基于任何真实数据。
答案 7 :(得分:1)
我喜欢String.Format,因为它可以使您的格式化文本比内联连接更容易跟踪和阅读,而且它更灵活,允许您格式化您的参数,但是对于像您这样的简短用途,我认为没有关于连接的问题。 / p>
对于循环内部或大字符串中的连接,您应该总是尝试使用StringBuilder类。
答案 8 :(得分:1)
我同意上面的很多观点,我认为应该提到的另一点是代码可维护性。 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所有我需要做的就是更改字符串。 对于连接,我必须修改该消息
如果在多个地方使用可以节省一些时间。
答案 9 :(得分:1)
我的印象是string.format更快,在这个测试中似乎慢了3倍
(function() {
window.addEventListener("load", foo, false);
function foo() {
var u = "beforeunload";
var v = unsafeWindow;
if (v._eventTypes && v._eventTypes[u]) {
var r=v._eventTypes[u];
for(var s=0;s<r.length;s++) {
v.removeEventListener(u,r[s],false);
}
v._eventTypes[u]=[];
}
}
})();
string.format耗时4.6秒,使用&#39; +&#39;花了1.6秒。
答案 10 :(得分:0)
string.Format可能是更好的选择
答案 11 :(得分:0)
我对各种字符串方法进行了一些分析,包括string.Format,StringBuilder和字符串连接。字符串连接几乎总是优于构建字符串的其他方法。所以,如果性能是关键,那就更好了。但是,如果性能不是很关键,那么我个人觉得string.Format在代码中更容易理解。 (但这是一个主观原因)但是,StringBuilder在内存利用率方面可能是最有效的。
答案 12 :(得分:0)
我更喜欢String.Format关于性能
答案 13 :(得分:-1)
与String.Format相比,字符串连接需要更多内存。因此,连接字符串的最佳方法是使用String.Format或System.Text.StringBuilder Object。
让我们来看第一种情况:“C”+ rowIndex.ToString() 假设rowIndex是一个值类型,因此ToString()方法必须将Box转换为String,然后CLR为包含两个值的新字符串创建内存。
当string.Format需要对象参数并将rowIndex作为对象并将其转换为字符串内部时,会有Boxing但它是内在的,并且它不会占用与第一种情况相同的内存。
对于短小的字符串,我猜不会那么重要......