通过IL Emit来创建类型的探究

Posted zzyds

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了通过IL Emit来创建类型的探究相关的知识,希望对你有一定的参考价值。

  最近刚开始研究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. 方便制造成一个委托,下次再用。

  以后有更多心得了再补充。。。

  

 

以上是关于通过IL Emit来创建类型的探究的主要内容,如果未能解决你的问题,请参考以下文章

CodeDom

反射-Emit

.NET(C#):分析IL中的if-else,while和for语句并用Emit实现

Protobuf-net & IL2CPP - System.Reflection.Emit 不受支持

IL初步了解

.net动态代理-EMIT,AOP实现