C#中,我们一般情况下认为"+"操作符有两种功能,一种是做算术加,一种是做字符串的连接。今天看到一份文档说,深入解析C#中两个PLUS操作符执行的不同操作,想了想,也的确应该是这样,IL代码实例也表面这个观点是正确的:
我们先写一小段测试代码:
namespace MSILTest { class Program { static void Main(string[] args) { string a = "aaa"; string b = a + "bbb"; System.Console.WriteLine(b); int c = 1; int d = c + 1; System.Console.WriteLine(d); } } } |
反编译得到IL代码:
.method private hidebysig static void Main(string[] args) cil managed { .entrypoint // Code size 40 (0x28) .maxstack 2 .locals init ([0] string a, [1] string b, [2] int32 c, [3] int32 d) IL_0000: nop IL_0001: ldstr "aaa" IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: ldstr "bbb" IL_000d: call string [mscorlib]System.String::Concat(string, string) IL_0012: stloc.1 IL_0013: ldloc.1 IL_0014: call void [mscorlib]System.Console::WriteLine(string) IL_0019: nop IL_001a: ldc.i4.1 IL_001b: stloc.2 IL_001c: ldloc.2 IL_001d: ldc.i4.1 IL_001e: add IL_001f: stloc.3 IL_0020: ldloc.3 IL_0021: call void [mscorlib]System.Console::WriteLine(int32) IL_0026: nop IL_0027: ret } // end of method Program::Main |
从上面的代码中可以看到,在+连接字符串的时候,C#的Complier是把它转换成为了带两个参数的Concat()函数。这个函数可以反编译System.dll可以看到这个静态的带两个参数的方法。
而+在handle两个number的时候,是直接转换成为add操作指令的。
这“两个”操作指令,完全没有一点相似的地方。所以,我们需要把这不同功能的两个+当成是两个运算符来看待。
同时,我们还可以稍为引申一下,关于C#中的强制类型转换:
大家看这一句:
IL_0021: call void [mscorlib]System.Console::WriteLine(int32) |
如果我们把
System.Console.WriteLine(d); |
改成
System.Console.WriteLine('\u0041'); |
相应的IL代码就会转变成为:
IL_0020: ldc.i4.s 65 IL_0022: call void [mscorlib]System.Console::WriteLine(char) |
由此我们可以得到结论:
强制类型转换,只不过是调用了一些方法的不同的重载的方法,而这个值本身是没有变的。
这个值在Stack的顶部,转换前后都不变,只是编译器来根据强制类型转换相应的代码来选择不同方法的不同的重载版本。
跟踪堆栈顶部数值,得到的结果也支持我们的这个结论。