一、确保尽量少的装箱
Microsoft Visual Studio Community 2019
版本 16.6.2
VisualStudio.16.Release/16.6.2+30204.135
Microsoft .NET Framework
版本 4.8.03752(此版本下编译是string str1 = "str1" + 9;会变为string str2 = "str2" + 9.ToString();
即不加ToString方法也不会装箱。
)
在自己编写的代码中,应当尽可能地避免编写不必要的装箱代码
1 string str1 = "str1" + 9;//装箱(vs2010版本,.net framework 4.0) 2 string str2 = "str2" + 9.ToString();
第一行代码对应的IL代码:
.maxstack 2 .entrypoint .locals init ( [0] string str1 ) IL_0000: nop IL_0001: ldstr "abc" IL_0006: ldc.i4.s 9 IL_0008: box [mscorlib]System.Int32 IL_000D: call string [mscorlib]System.String::Concat(object, object) IL_0012: stloc.0 IL_0013: ret
在运行时会完成一次装箱行为box
第二行代码对应的IL代码:
.maxstack 2 .entrypoint .locals init ( [0] string str2, [1] int32 ) IL_0000: nop IL_0001: ldstr "str2" IL_0006: ldc.i4.s 9 IL_0008: stloc.1 IL_0009: ldloca.s V_1 IL_000B: call instance string [mscorlib]System.Int32::ToString() IL_0010: call string [mscorlib]System.String::Concat(string, string) IL_0015: stloc.0 IL_0016: ret
没有装箱行为,通过直接操作内存来完成从int到string的转换,效率比装箱高很多。
装箱需要完成一下三个步骤:
1、为值类型在托管堆中分配内存。除了值类型本身所分配的内存外,内存总量还要加上类型对象指针和同步块索引所占用的内存。
2、将值类型的值复制到新分配的堆内存中。
3、返回已经成为引用类型的对象的地址。
二、避免分配额外的内存空间
对于CLR,string对象一旦被赋值就不可改变,运行时调用System.String类中的任何方法或进行任何运算(如“=”赋值、“+”拼接等),
都会在内存中创建一个新的字符串对象,所以要为新的对象分配新的内存空间,带来运行时的额外开销。
string a = "a"; string b = "b"; string c = "c"; string d = "d"; a += b;//调用一次string.Contact方法 a += c; a += d; string str = a + b + c + d;//创建了4个字符串对象,调用一次string.Contact方法 string str1 = 9 + "abc";//发生一次装箱,调用一次string.Contact方法 string str2 = "123" +"abc" + "456";//该代码等效于 string str2 = "123abc456"; const string mConst = "c"; string str3 = "abc" + mConst;//因为mConst是常量,所以该代码等效于string str3="abc" + "c"; 最终等效于string str3=“abcc”;
微软提供了StringBuilder来弥补String的不足,它会预先以非托管的方式分配内存。
StringBuilder str4 = new StringBuilder();//默认分配的长度为16
str4默认分配的长度为16,当str4的字符长度小于等于16时,不会重新分配内存;
当字符长度大于16小于32时,StringBuilder会重新分配长度32(按照上次的容量加倍进行分配)。
可以指定分配长度,要注意分配合理,太小了需要频繁分配内存;太大了浪费空间。
string a = "a"; string b = "b"; string c = "c"; string d = "d"; StringBuilder str5 = new StringBuilder(a); str1.Append(b);//字符串的拼接 str1.Append(c); str1.Append(d); string str6 = string.Format("{0}{1}{2}{3}", a, b, c, d);//也可以使用string.Format方法在内部使用StringBuilder进行字符串格式化
参考:《编写高质量代码改善C#程序的157个建议》陆敏技