none
.NET是如何实现使用加号让两个字符串拼接的 RRS feed

  • 问题

  • string a = "hello ";
    string b = "world!";
    string c = a + b;
    


    我们能得到c的值是"hello world!",但是我不知道内部是如何实现这个加号的,我查看了System.String的源代码,没有+操作符重载。

    我知道使用+不会改变原来的string,但这个不是我要问的,谢谢


    Debug my heart...
    2011年8月11日 14:23

答案

  • 我写了一个简单的方法:

    public string Add(string a, string b)
    {
    	string c = a + b;
    	return c;
    }
    


    这是它的IL代码:

    .method public hidebysig instance string 
        Add(string a,
          string b) cil managed
    {
     // Code size    10 (0xa)
     .maxstack 2
     .locals init ([0] string c)
     IL_0000: ldarg.1
     IL_0001: ldarg.2
     IL_0002: call    string [mscorlib]System.String::Concat(string,
                                   string)
     IL_0007: stloc.0
     IL_0008: ldloc.0
     IL_0009: ret
    } // end of method Class1::Add
    


    可以看出, + 被编译为IL代码之后变成了对String类的静态方法Concat(string, string)的调用。

    而Concat(string, string)的代码(反编译的结果)为

    public static string Concat(string str0, string str1)
    {
    	if (string.IsNullOrEmpty(str0))
    	{
    		if (string.IsNullOrEmpty(str1))
    		{
    			return string.Empty;
    		}
    		return str1;
    	}
    	else
    	{
    		if (string.IsNullOrEmpty(str1))
    		{
    			return str0;
    		}
    		int length = str0.Length;
    		string text = string.FastAllocateString(length + str1.Length);
    		string.FillStringChecked(text, 0, str0);
    		string.FillStringChecked(text, length, str1);
    		return text;
    	}
    }
    


    可见,关键是“FastAllocateString”和"FillStringChecked"

    FastAllocateString的定义:

    [MethodImpl(MethodImplOptions.InternalCall)]
    internal static extern string FastAllocateString(int length);
    

    这是一个internal的extern方法,internal意味着咱们是没有指望可以调用了。extern意味着这个方法也不是在String类内部定义的,InternalCall意味着这方法是对CLR内部实现的调用。

    FillStringChecked方法的定义(反编译的结果):

    private unsafe static void FillStringChecked(string dest, int destPos, string src)
    {
    	if (src.Length > dest.Length - destPos)
    	{
    		throw new IndexOutOfRangeException();
    	}
    	fixed (char* ptr = &dest.m_firstChar)
    	{
    		fixed (char* ptr2 = &src.m_firstChar)
    		{
    			string.wstrcpy(ptr + (IntPtr)destPos, ptr2, src.Length);
    		}
    	}
    }
    

    首先,private意味着咱们甭想用了。另外它是unsafe的,因为玩起了指针。

    我想以上的内容足够说明一些情况了。



    理解的越多,需要记忆的就越少
    2011年8月13日 0:19
    版主
  • dear

    再补充有关string的特性,由于string类会重新分配内存所以他在动态处理文字上效能会不佳,会比较建议使用StringBuilder类

    http://www.dotblogs.com.tw/yc421206/archive/2010/10/26/18575.aspx


    秘訣無它,唯勤而已 http://www.dotblogs.com.tw/yc421206/
    2011年8月13日 13:35

全部回复

  • 我写了一个简单的方法:

    public string Add(string a, string b)
    {
    	string c = a + b;
    	return c;
    }
    


    这是它的IL代码:

    .method public hidebysig instance string 
        Add(string a,
          string b) cil managed
    {
     // Code size    10 (0xa)
     .maxstack 2
     .locals init ([0] string c)
     IL_0000: ldarg.1
     IL_0001: ldarg.2
     IL_0002: call    string [mscorlib]System.String::Concat(string,
                                   string)
     IL_0007: stloc.0
     IL_0008: ldloc.0
     IL_0009: ret
    } // end of method Class1::Add
    


    可以看出, + 被编译为IL代码之后变成了对String类的静态方法Concat(string, string)的调用。

    而Concat(string, string)的代码(反编译的结果)为

    public static string Concat(string str0, string str1)
    {
    	if (string.IsNullOrEmpty(str0))
    	{
    		if (string.IsNullOrEmpty(str1))
    		{
    			return string.Empty;
    		}
    		return str1;
    	}
    	else
    	{
    		if (string.IsNullOrEmpty(str1))
    		{
    			return str0;
    		}
    		int length = str0.Length;
    		string text = string.FastAllocateString(length + str1.Length);
    		string.FillStringChecked(text, 0, str0);
    		string.FillStringChecked(text, length, str1);
    		return text;
    	}
    }
    


    可见,关键是“FastAllocateString”和"FillStringChecked"

    FastAllocateString的定义:

    [MethodImpl(MethodImplOptions.InternalCall)]
    internal static extern string FastAllocateString(int length);
    

    这是一个internal的extern方法,internal意味着咱们是没有指望可以调用了。extern意味着这个方法也不是在String类内部定义的,InternalCall意味着这方法是对CLR内部实现的调用。

    FillStringChecked方法的定义(反编译的结果):

    private unsafe static void FillStringChecked(string dest, int destPos, string src)
    {
    	if (src.Length > dest.Length - destPos)
    	{
    		throw new IndexOutOfRangeException();
    	}
    	fixed (char* ptr = &dest.m_firstChar)
    	{
    		fixed (char* ptr2 = &src.m_firstChar)
    		{
    			string.wstrcpy(ptr + (IntPtr)destPos, ptr2, src.Length);
    		}
    	}
    }
    

    首先,private意味着咱们甭想用了。另外它是unsafe的,因为玩起了指针。

    我想以上的内容足够说明一些情况了。



    理解的越多,需要记忆的就越少
    2011年8月13日 0:19
    版主
  • dear

    再补充有关string的特性,由于string类会重新分配内存所以他在动态处理文字上效能会不佳,会比较建议使用StringBuilder类

    http://www.dotblogs.com.tw/yc421206/archive/2010/10/26/18575.aspx


    秘訣無它,唯勤而已 http://www.dotblogs.com.tw/yc421206/
    2011年8月13日 13:35
  • 谢谢你的回复,我可以在mono project里面学习一下处理方法。

    再有,从你这儿,我学到了IL的有一种用法,谢谢。


    Debug my heart...
    2011年8月26日 2:18
  • 谢谢,关于string和StringBuilder的区别,我以前也注意到了,所以对于大量的字符串连接,我也会使用StringBuilder。
    Debug my heart...
    2011年8月26日 2:19
  • 你是怎么看到CLR中FillStringChecked的源码的?
    2013年8月15日 6:20
  • 有趣的问题。功能的实现肯定是.NET Framework实现的,你说的不会是MSDN中没有+操作符重载的问题吧。那么MSDN也不会有int、float等类型的+-运算符重载的说明,不过在运算符重载那一章看到说明:

    为数值类型和字符串类型预定义了二元 + 运算符。 对于数值类型,+ 计算两个操作数之和。 当其中的一个操作数是字符串类型或两个操作数都是字符串类型时,+ 将操作数的字符串表示形式串联在一起。

    https://

    live.com/login.srf?wa=wsignin1.0&rpsnv=11&checkda=1&ct=1377363381&rver=6.0.5276.0&wp=MCLBI&wlcxt=msdn%24msdn%24msdn&wreply=http%3a%2f%2fmsdn.microsoft.com%2fZH-CN%2flibrary%2fk1a63xkz%2528v%3dVS.110%2cd%3dhv.2%2529.aspx&lc=1033&id=254354&mkt=en-US


    2013年8月24日 17:24