我有一小段代码正在解析索引值,以确定输入到Excel的单元格。我在想...
之间有什么区别
xlsSheet.Write("C" + rowIndex.ToString(), null, title);
和
xlsSheet.Write(string.Format("C{0}", rowIndex), null, title);
这个比那个好吗?又为什么呢
我有一小段代码正在解析索引值,以确定输入到Excel的单元格。我在想...
之间有什么区别
xlsSheet.Write("C" + rowIndex.ToString(), null, title);
和
xlsSheet.Write(string.Format("C{0}", rowIndex), null, title);
这个比那个好吗?又为什么呢
Answers:
在C#6之前
老实说,我认为第一个版本更简单-尽管我将其简化为:
xlsSheet.Write("C" + rowIndex, null, title);
我怀疑其他答案可能涉及性能下降,但是老实说,如果存在的话,它将是最小的-而且此串联版本不需要解析格式字符串。
格式字符串对于本地化等而言非常有用,但是在这种情况下,这种连接更为简单,并且效果也很好。
使用C#6
字符串插值使许多事情在C#6中更易于阅读。在这种情况下,第二个代码变为:
xlsSheet.Write($"C{rowIndex}", null, title);
这可能是最好的选择,IMO。
xlsSheet.Write($"C{rowIndex}", null, title);
我最初的偏好(来自C ++背景)是String.Format。由于以下原因,我稍后将其删除:
-字符串串联允许空值,String.Format
不允许。编写“ s1 + null + s2
”不会中断,只是将null值视为String.Empty。好吧,这可能取决于您的特定情况-在某些情况下,您想要一个错误,而不是静默忽略空的FirstName。但是,即使在这种情况下,我个人更喜欢自己检查null并抛出特定错误,而不是从String.Format获得的标准ArgumentNullException。
想法是.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()
则具有优势。string.Format
使用ReSharper时“安全”;也就是说,它与[错误使用]的任何其他代码一样安全。2)string.Format
确实允许“安全” null
:string.Format("A{0}B", (string)null)
结果为“ AB”。3)我很少关心这种性能水平(为此,我很少抽出一天StringBuilder
)……
string s = "This " + MyMethod(arg) + " is a test";
将其编译为String.Concat()
发布模式的调用),也使用String.Concat 。
对于一个简单的单连接的简单情况,我觉得这不值得string.Format
(而且我还没有进行测试,但是我怀疑对于像这样的简单情况,它string.Format
可能会稍微慢一些,使用格式字符串解析会怎样)和所有)。像乔恩·斯凯特(Jon Skeet)一样,我宁愿不显式调用.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]
);
在我看来,我给出的示例有点令人困惑,因为看来我在这里同时使用了串联string.Format
。是的,在逻辑上和词汇上,这就是我所做的。但是串联将全部由编译器1进行优化,因为它们都是字符串文字。因此,在运行时,将只有一个字符串。因此,我想应该说我宁愿在运行时避免许多串联。
当然,除非您仍然使用C#5或更早版本,否则本主题的大部分内容已过时。现在,我们已经插入了字符串,出于可读性考虑,字符串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"
);
这样您确实会丢失编译时级联。 每个内插字符串string.Format
都由编译器转换为对它们的调用,并且它们的结果在运行时连接在一起。这意味着为了牺牲可读性而牺牲了运行时性能。在大多数情况下,这是一个值得的牺牲,因为运行时的损失可以忽略不计。但是,在性能关键的代码中,您可能需要分析不同的解决方案。
1 您可以在C#规范中看到这一点:
...在常量表达式中允许以下构造:
...
- 预定义的+ ...二进制运算符...
您还可以通过一些代码来验证它:
const string s =
"This compiles successfully, " +
"and you can see that it will " +
"all be one string (named `s`) " +
"at run time";
String.Format()
如果您的字符串比较复杂,并且串联了许多变量,那么我将选择string.Format()。但是对于您的情况而言,字符串的大小和要连接的变量的数量,我会选择您的第一个版本,它更简单。
我看过String.Format(使用Reflector),它实际上创建了一个StringBuilder,然后在其上调用AppendFormat。因此,对于多个搅拌,它比concat更快。最快(我相信)是创建一个StringBuilder并手动执行Append的调用。当然,“很多”的数目尚待猜测。我会使用+(实际上,因为我主要是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)消除了那些中间结果。
请记住,要创建每个中间字符串,必须将前一个字符串复制到新的内存位置,而不仅仅是内存分配可能会变慢。
我同意以上几点,我认为应该提到的另一点是代码的可维护性。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.format更快,在此测试中似乎慢了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秒。
"1" + "2" + "3" + "4" + "5" + "6" + "7" + "8" + "9" + "10"
为一个字符串文字,因此该行实际上变得比上一行"12345678910" + i
更快string.Format(...)
当格式模板(“ C {0}”)存储在配置文件(例如Web.config / App.config)中时,string.Format可能是更好的选择。
与String.Format相比,字符串串联需要更多的内存。因此,连接字符串的最佳方法是使用String.Format或System.Text.StringBuilder对象。
让我们以第一种情况为例:“ C” + rowIndex.ToString()假设rowIndex是一个值类型,因此ToString()方法必须Box将该值转换为String,然后CLR为包含两个值的新字符串创建内存。
如果string.Format需要对象参数,并将rowIndex作为对象并内部将其转换为字符串,那么Boxing就是它的固有功能,并且不会像第一种情况那样占用太多内存。
对于短字符串,我猜并没有那么重要...