最近刚开始研究IL,起源是看到Odin内部源码创建一个Type使用了这种做法,当时好奇为什么要这么做。
先丢出代码例子:
1 class TestClass 2 { 3 public TestClass() 4 { 5 mylist = new List<int>(); 6 for(int i=0;i<100;i++) 7 { 8 mylist.Add(i); 9 } 10 } 11 12 public int x = 5; 13 public List<int> mylist; 14 }测试类代码
1 // Do Some Test 2 int needCnt = 10000; 3 var preTime = EditorApplication.timeSinceStartup; 4 for(int i=0;i<needCnt;i++) 5 { 6 TestClass t = new TestClass(); 7 } 8 var nowTime = EditorApplication.timeSinceStartup; 9 Debug.Log("通常创建的时间 " + (nowTime - preTime)); 10 11 var type = typeof(TestClass); 12 var constructor = type.GetConstructor(Type.EmptyTypes); 13 var method = new DynamicMethod(type.FullName + "_FastCreator", type, Type.EmptyTypes); 14 15 var il = method.GetILGenerator(); 16 17 il.Emit(OpCodes.Newobj, constructor); 18 il.Emit(OpCodes.Ret); 19 20 var fastCreator = (Func<TestClass>)method.CreateDelegate(typeof(Func<TestClass>)); 21 22 preTime = EditorApplication.timeSinceStartup; 23 for(int i=0;i<needCnt;i++) 24 { 25 var t = constructor.Invoke(new object[] {}); 26 } 27 nowTime = EditorApplication.timeSinceStartup; 28 Debug.Log("构造函数Invoke并且不转型创建的时间 " + (nowTime - preTime)); 29 30 preTime = EditorApplication.timeSinceStartup; 31 for(int i=0;i<needCnt;i++) 32 { 33 TestClass t = constructor.Invoke(new object[] {}) as TestClass; 34 } 35 nowTime = EditorApplication.timeSinceStartup; 36 Debug.Log("构造函数Invoke创建的时间 " + (nowTime - preTime)); 37 38 preTime = EditorApplication.timeSinceStartup; 39 for(int i=0;i<needCnt;i++) 40 { 41 TestClass t = fastCreator(); 42 } 43 nowTime = EditorApplication.timeSinceStartup; 44 Debug.Log("Emit创建的时间 " + (nowTime - preTime));测试代码逻辑
测试结果如下(直接在Unity上跑的):
看起来的结果就是,通过IL Emit的时间跟直接new一个的时间是同一个数量级别的,通过method.Invoke的时间要慢上一倍。
个人猜测Odin选择这种做法来创建种种类型的原因如下:1. 时间上比Invoke要快 2. 只有Type信息无法调用new(猜测?)3. 方便制造成一个委托,下次再用。
以后有更多心得了再补充。。。