在這片文章中,我們暫時放一放Activator.CreateInstance(Type)和Activator.CreateInstance<T>()之間的性能差異,去探索一下,為什麼使用泛型約束的速度和CreateInstance<T>()差不多(用屁股都能猜到應該是直接調用了CreateInstance<T>())。
首先我們寫一個小程序來驗證我們的猜想:
1 using System;
2
3 namespace GenericNew
4 {
5 public class PRogram
6 {
7 public static void Main(string[] args)
8 {
9 CreateInstance<Program>();
10 new Program();
11 }
12
13 public static T CreateInstance<T>() where T: new()
14 {
15 return new T();
16 }
17 }
18 }
編譯了以後用reflector來查看編譯的結果:
1 public static T CreateInstance<T>() where T: new()
2 {
3 return new T();
4 }
5
看起來沒有什麼問題啊,跟寫的時候一樣美妙,也沒有見到System.Activator.CreateInstance<T>()的蹤影。
那麼,讓我們切換到msil模式看看:
1 .method public hidebysig static !!T CreateInstance<.ctor T>() cil managed
2 {
3 .maxstack 2
4 .locals init (
5 [0] !!T local,
6 [1] !!T local2)
7 L_0000: nop
8 L_0001: ldloca.s local2
9 L_0003: initobj !!T
10 L_0009: ldloc.1
11 L_000a: box !!T
12 L_000f: brfalse.s L_001c
13 L_0011: ldloca.s local2
14 L_0013: initobj !!T
15 L_0019: ldloc.1
16 L_001a: br.s L_0021
17 L_001c: call !!0 [mscorlib]System.Activator::CreateInstance<!!T>()
18 L_0021: stloc.0
19 L_0022: br.s L_0024
20 L_0024: ldloc.0
21 L_0025: ret
22 }
恩,發現[mscorlib]System.Activator::CreateInstance<!!T>()了,通過分析這段代碼,我們會發現,其實ms對於這個的性能還是做了一定的優化的,這段代碼:
試圖采用值類型的初始化方法初始化對象,並對該對象進行裝箱操作
裝箱失敗的情況下,調用System.Activator.CreateInstance<T>()來創建對象
最後再返回創建好的對象。
那麼是不是.Net本來就是通過這種方式來創建對象的呢?
我們來對比一下new Program()這條語句的編譯結果:
1 newobj instance void GenericNew.Program::.ctor()
那麼為什麼c#編譯器沒有采用這個指令來創建泛型類的實例呢?
我們需要參考一下msdn上對於newobj的用法的定義:
http://msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes.newobj(VS.85).aspx
在這裡,我們看到,newobj指令的使用方法是 newobj <ctor>
也就是說,它的參數是構造器,而非類型本身; 原因就在於,.Net允許有不同參數的構造器存在,只有指定了構造器,clr才知道要使用哪個構造器來初始化對象。
而,對於一個泛型類,在編譯器是無法知道它的泛型參數的構造器信息的,自然就沒有辦法使用newobj指令了。
這就是事實的真相,在下一篇文章中,我們將去探究,事實的終極真相。