C# 将字符串和字符组合在一起的性能
我想知道当我选择向常量字符串中添加一个字符或一个字符时,是否会对性能产生影响 因此,我编写了一个小型控制台应用程序:(.NET4)C# 将字符串和字符组合在一起的性能,c#,.net,types,C#,.net,Types,我想知道当我选择向常量字符串中添加一个字符或一个字符时,是否会对性能产生影响 因此,我编写了一个小型控制台应用程序:(.NET4) 静态类程序 { const string STR=“string”; 静态void Main() { var arr=新字符串[9999999]; 秒表计时器=新秒表(); Console.ReadLine(); timer.Start(); //对于(uint i=0;i
静态类程序
{
const string STR=“string”;
静态void Main()
{
var arr=新字符串[9999999];
秒表计时器=新秒表();
Console.ReadLine();
timer.Start();
//对于(uint i=0;i<9999999;i++)
//{
//arr[i]=STR+'C';
//}
对于(uint i=0;i<9999999;i++)
{
arr[i]=STR+“C”;
}
timer.Stop();
控制台写入线(计时器延时毫秒);
Console.ReadLine();
}
}
您必须为循环注释一个
因此,STR+“C”
大约需要1300毫秒
对于STR+'C'
我还没有看到结果。这花的时间太长了,似乎对我的电脑造成了很大的困扰
所以,我的问题是。这种性能影响如何可能?我知道在实际使用中,9999999值的数组不会经常出现,但它仍然是一个巨大的差异
提前谢谢 像这样的简单程序:
var val = "hello ";
val += 'r';
对对象执行char
值的装箱,我们可以从生成的IL
IL_0001: ldstr "hello "
IL_0006: stloc.0 // val
IL_0007: ldloc.0 // val
IL_0008: ldc.i4.s 72
IL_000A: box System.Char
IL_000F: call System.String.Concat
IL_0014: stloc.0 // val
IL_0015: ldloc.0 // val
相反,对于string
,它不涉及任何装箱,因此速度明显更快
那么为什么要执行拳击?因为在两个参数上调用(这是对二进制+
运算符调用的结果),其中只有一个参数是字符串
,会调用,因此,char
的值被装箱,以便能够传递到该方法调用中。这实际上很容易解释:您偶然发现C编译器将对字符串表达式执行常量折叠
由于您将STR
声明为const
,因此其效果是将对它的引用替换为文字字符串“string”
。然后,当编译器遇到“string”+“C”
时,它会用等价的“string C”
替换该表达式。因此,实际完成的循环将花费所有时间将该字符串分配到数组中的不同位置
相反地,char
串接并不是这样优化的,因此实际上您必须等待串接(包括分配新的string
对象)以及数组分配。同样,循环将生成大量垃圾,因此您也在等待收集器
如果您想公平地比较这两个操作,我会做两件事:
将STR
的声明更改为static readonly
,而不是const
减少迭代次数,这样您就可以真正获得完整的运行
你忘了解释为什么字符是盒装的,但是字符串不是。非常感谢,回答得很好!即使没有常量STR+“C”
的速度几乎是添加字符的两倍。现在我将使用单字符字符串。再次感谢@安迪很乐意帮忙。你的观察是有意义的,因为字符串
可以直接连接,而不必先将字符
转换为字符串
。@Andy这与Tigran的回答相结合是有意义的:+
操作符被调用string.Concat>的C#编译器取代。当您将它与字符串一起使用时,它可以直接调用接受字符串参数的重载。当您将它与char一起使用时,它必须将char装箱并调用接受对象参数的重载。装箱和拆箱操作相对昂贵,尤其是在紧密循环中执行时。但请注意,这是微优化的顶峰@科迪格雷,我支持!如果您实际上是在紧密循环中连接一个字符串,那么您肯定应该使用StringBuilder
。
IL_0001: ldstr "hello "
IL_0006: stloc.0 // val
IL_0007: ldloc.0 // val
IL_0008: ldc.i4.s 72
IL_000A: box System.Char
IL_000F: call System.String.Concat
IL_0014: stloc.0 // val
IL_0015: ldloc.0 // val