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的頂部,轉換前後都不變,只是編譯器來根據強制類型轉換相應的代碼來選擇不同方法的不同的重載版本。
跟蹤堆棧頂部數值,得到的結果也支持我們的這個結論。