001. 正确操作字符串

  1. 避免不必要的装箱

    struct MyStruct { }
    
    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()方式,不要以 + 值类型方式

  2. 避免分配额外的内存空间

    拼接"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(),未分配内存空间

  3. 使用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适合拼接运行时字符串变量。


上一篇:别告诉我这是真的?goroutine 可能使程序变慢


下一篇:001.云桌面整体解决方案实施