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

性能优化--string 字符串拼接(超详细),第1张

目录
      • 前言
      • 测试
        • 1. int -> object
        • 2. int.ToString()
        • 3. 空string + int
        • 4. 非空string + int
        • 5. 非空string + int.ToString
      • 非空string + struct
      • 非空string + struct.ToString
      • 结论

前言

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

测试 1. int -> object
private void Update()
{
    int num = 1;
    Profiler.BeginSample("int -> object");
    object o = num;
    Profiler.EndSample();
}


得出: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();
}


得出: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. 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. 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;

等价于

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

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

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

string tmp_str_3 = "123";
tmp_str_3 = tmp_str_3 + num;

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. 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();
    }
}

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. 34B:tmp_str_4所占的空间:34B
  2. 28B:a.ToString方法中的age.ToString需要28B

结论

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

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/2889266.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-09-14
下一篇 2022-09-14

发表评论

登录后才能评论

评论列表(0条)

保存