001. 正确操作字符串
- 避免不必要的装箱
自定义结构体
struct MyStruct { }
FCL,String自带的拼接字符串方法
public static String Concat(params object[] args);
示例
string str1 = "str1" + 9 + false + new MyStruct() + new object();
等价于
string str1 = string.Concat("str1", 9, false, new MyStruct(), new object());
以上,发生3次装箱,1次Concat
改为
string str1 = "str1" + 9.ToString() + false.ToString() + new MyStruct().ToString() + new object();
未发生装箱,性能较高
结论:
拼接字符串时(+ 或 Concat),以 + 值类型.ToString()方式,不要以 + 值类型方式
-
避免分配额外的内存空间
拼接"abc"和"123"的三种方法
const string cs123 = "123"; string s123 = "123"; string str = "abc" + "123"; // 方法1 str = "abc" + s123; // 方法2 str = "abc" + cs123; // 方法3
方法1因为都是字面值,所以编译时相当于
string str = "abc123"
,未调用Concat(),未分配内存空间方法2调用1次Concat(),分配1次内存空间
方法3等价于方法1,因为cs123是constant字面值,在编译期就被替换成“123”。未调用Concat(),未分配内存空间
-
使用StringBuilder拼接字符串
string是不可变类型,任何修改字符串的操作都会导致开辟新的内存空间,频繁修改字符串或拼接字符串时,使用StringBuilder更合适。
a. StringBuilder功能表现就像是一个List
b. 非托管方式分配内存
c. 默认容量16,但可以指定初始容量,不够用时就翻倍
拓展
装箱为什么影响性能?
引入了内存开销 + 时间开销
步骤:
a. 开辟堆中内存:值本身 + 类型对象指针 + 同步索引块
b. 栈中值复制到堆
c. 实例地址返回到栈中引用变量
9.ToString()
结构体调用它的方法时并不需要先装箱。微软为int提供的ToString()实际调用的是非委托代码直接操作内存返回一个字符串,而非通过CLR.
String.Format()
String.Format("{0}{1}{2}{3}",a,b,c,d);
$"{a}{b}{c}{d}"
$插值法和String.Format()底层都是使用StringBuilder拼接
测试题
StringBuilder sb = new StringBuilder();
sb.Append("t");
sb.Append("e");
sb.Append("s");
sb.Append("t");
烂代码。使用StringBuilder拼接字面值多此一举,采用"t" + "e" + "s" + "t"形式拼接,编译时直接是"test",内存和时间开销最佳。
string a = "t";
string b = "e";
string c = "s";
string d = "t";
StringBuilder sb = new StringBuilder();
sb.Append(a);
sb.Append(b);
sb.Append(c);
sb.Append(d);
好代码。StringBuilder适合拼接运行时字符串变量。