• 性能优化--string 字符串拼接(超详细)


    前言

    本文章需要了解前缀知识
    C# string为什么可以与int相加? string字符串拼接深入分析

    测试

    1. int -> object
    private void Update()
    {
        int num = 1;
        Profiler.BeginSample("int -> object");
        object o = num;
        Profiler.EndSample();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这里插入图片描述
    得出:int -> object 需要20B的内存

    2. int.ToString()
    private void Update()
    {
        int num = 1;
        Profiler.BeginSample("int -> object");
        object o = num;
        Profiler.EndSample();
    
    	Profiler.BeginSample("int ToString()");
        string tmp_str_1 = num.ToString();
        Profiler.EndSample();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在这里插入图片描述
    得出:int 调用ToString 需要28B的内存

    Tips:ToString内部其实是new一个新的字符串,所以会有垃圾产生

    3. 空string + int
    private void Update()
    {
        int num = 1;
        Profiler.BeginSample("int -> object");
        object o = num;
        Profiler.EndSample();
    
    	Profiler.BeginSample("int ToString()");
        string tmp_str_1 = num.ToString();
        Profiler.EndSample();
    
    	Profiler.BeginSample("Empty string + int");
        string tmp_str_2 = "";
        tmp_str_2 += num;
        Profiler.EndSample();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    在这里插入图片描述

    1. 28B:ToString需要28B
    2. 20B:由于Concat需要object参数,所以int转object需要20B

    为了方便理解,我画了一个内存图
    在这里插入图片描述

    4. 非空string + int
    private void Update()
    {
        int num = 1;
        Profiler.BeginSample("int -> object");
        object o = num;
        Profiler.EndSample();
    
    	Profiler.BeginSample("int ToString()");
        string tmp_str_1 = num.ToString();
        Profiler.EndSample();
    
    	Profiler.BeginSample("Empty string + int");
        string tmp_str_2 = "";
        tmp_str_2 += num;
        Profiler.EndSample();
    
        Profiler.BeginSample("Not Empty string + int");
        string tmp_str_3 = "123";
        tmp_str_3 = tmp_str_3 + num;
        Profiler.EndSample();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    在这里插入图片描述

    1. 34B:tmp_str_3所占的空间:34B
    2. 28B:ToString需要28B
    3. 20B:由于Concat需要object参数,所以int转object需要20B

    注意
    为什么这里多了一个34B呢?之前空字符串为什么没有多?

    因为tmp_str_2是空字符串,所以编译器做了处理。

    string tmp_str_2 = "";
    tmp_str_2 += num;
    
    • 1
    • 2

    等价于

    string tmp_str_2 = num;//注意:这里是伪代码,因为int不能直接转string,底层应该是先装箱,再ToString
    
    • 1

    所以空字符串相加是 20 + 28 = 48B

    所以非空string + int的内存图应该是这样的

    string tmp_str_3 = "123";
    tmp_str_3 = tmp_str_3 + num;
    
    • 1
    • 2

    在这里插入图片描述

    5. 非空string + int.ToString
    private void Update()
    {
        int num = 1;
        Profiler.BeginSample("int -> object");
        object o = num;
        Profiler.EndSample();
    
    	Profiler.BeginSample("int ToString()");
        string tmp_str_1 = num.ToString();
        Profiler.EndSample();
    
    	Profiler.BeginSample("Empty string + int");
        string tmp_str_2 = "";
        tmp_str_2 += num;
        Profiler.EndSample();
    
        Profiler.BeginSample("Not Empty string + int");
        string tmp_str_3 = "123";
        tmp_str_3 = tmp_str_3 + num;
        Profiler.EndSample();
    
    	Profiler.BeginSample("Not Empty string + int.ToString");
        string tmp_str_4 = "123";
        tmp_str_4 = tmp_str_4 + num.ToString();
        Profiler.EndSample();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    1. 34B:tmp_str_4所占的空间:34B
    2. 28B:ToString需要28B
      在这里插入图片描述
      在这里插入图片描述
      我们对比下4和5,我们发现主动调用ToString可以避免装箱带来的GC。

    非空string + struct

    private void Update()
    {
        int num = 1;
        Profiler.BeginSample("int -> object");
        object o = num;
        Profiler.EndSample();
    
    	Profiler.BeginSample("int ToString()");
        string tmp_str_1 = num.ToString();
        Profiler.EndSample();
    
    	Profiler.BeginSample("Empty string + int");
        string tmp_str_2 = "";
        tmp_str_2 += num;
        Profiler.EndSample();
    
        Profiler.BeginSample("Not Empty string + int");
        string tmp_str_3 = "123";
        tmp_str_3 = tmp_str_3 + num;
        Profiler.EndSample();
    
    	Profiler.BeginSample("Not Empty string + int.ToString");
        string tmp_str_4 = "123";
        tmp_str_4 = tmp_str_4 + num.ToString();
        Profiler.EndSample();
    
    	A a = new A(5, "A");
        Profiler.BeginSample("Not Empty string + struct");
        string tmp_str_5 = "123";
        tmp_str_5 = tmp_str_5 + a;
        Profiler.EndSample();
    }
    
    public struct A
    {
        public int age;
        public string name;
        public A(int age, string name)
        {
            this.age = age;
            this.name = name;
        }
        public override string ToString()
        {
            return age.ToString();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    Tips:结构体是值类型,所以要先装箱,然后再调用内部实现的ToString方法

    1. 34B:tmp_str_4所占的空间:34B
    2. 28B:a.ToString方法中的age.ToString需要28B
    3. 32B:struct装箱需要32B

    在这里插入图片描述

    在这里插入图片描述

    非空string + struct.ToString

    private void Update()
    {
        int num = 1;
        Profiler.BeginSample("int -> object");
        object o = num;
        Profiler.EndSample();
    
    	Profiler.BeginSample("int ToString()");
        string tmp_str_1 = num.ToString();
        Profiler.EndSample();
    
    	Profiler.BeginSample("Empty string + int");
        string tmp_str_2 = "";
        tmp_str_2 += num;
        Profiler.EndSample();
    
        Profiler.BeginSample("Not Empty string + int");
        string tmp_str_3 = "123";
        tmp_str_3 = tmp_str_3 + num;
        Profiler.EndSample();
    
    	Profiler.BeginSample("Not Empty string + int.ToString");
        string tmp_str_4 = "123";
        tmp_str_4 = tmp_str_4 + num.ToString();
        Profiler.EndSample();
    
    	A a = new A(5, "A");
        Profiler.BeginSample("Not Empty string + struct");
        string tmp_str_5 = "123";
        tmp_str_5 = tmp_str_5 + a;
        Profiler.EndSample();
    
        Profiler.BeginSample("Not Empty string + struct.ToString");
        string tmp_str_6 = "123";
        tmp_str_6 = tmp_str_6 + a.ToString();
        Profiler.EndSample();
    }
    
    public struct A
    {
        public int age;
        public string name;
        public A(int age, string name)
        {
            this.age = age;
            this.name = name;
        }
        public override string ToString()
        {
            return age.ToString();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    1. 34B:tmp_str_4所占的空间:34B
    2. 28B:a.ToString方法中的age.ToString需要28B

    在这里插入图片描述

    结论

    我们在进行字符串拼接的时候,一定要手动调用ToString方法

  • 相关阅读:
    项目实战(依旧还是登录认证,JWT解析异常处理,授权信息处理)
    【JDBC】----综合案例(账号密码登录和SQL注入)
    数据结构之排序
    动环监控系统的主要功能,动环监控系统的监控对象有哪些
    PVT法碳化硅SIC单晶生长工艺真空压力控制装置的国产化替代解决方案
    中英文说明书丨艾美捷支原体检测试剂盒
    一种获得离散型周期数据的变化周期的算法
    js仿toDoList(待办事项)练习
    Django学习
    docker 常用指令
  • 原文地址:https://blog.csdn.net/qq_52855744/article/details/126818425