zoukankan      html  css  js  c++  java
  • C# 正确操作字符串,规避字符串转换所带来的额外开销

    一、确保尽量少的装箱

    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个建议》陆敏技

  • 相关阅读:
    Mac014--Sourcetree安装(Git client)
    SSM003/构建Maven单模块项目(二)
    Git016--Work
    Mac013--Docker安装
    前端002/常用标签属性(工作应用)
    Python 38 初识数据库
    Python 38 sql基础
    Python 39 数据库的数据类型
    Python 39 数据库
    Python 37 进程池与线程池 、 协程
  • 原文地址:https://www.cnblogs.com/xuyouyou/p/13143973.html
Copyright © 2011-2022 走看看