趣味问题:你能用Reflection.Emit生成这段代码吗?(答案)

在上一篇博客中我提出了一个问题:如何用.NET的Reflection.Emit生成等价于下面VB代码的三个类型:

Class A

Implements B.I

End Class

Class B

Inherits A

Interface I

End
Interface

End Class

这个问题的难点在于三个类型有循环依赖关系:A实现了接口B.I,因此A依赖于I;B是A的子类,因此B依赖于A;接口I是B的嵌套类型,因此I依赖于B。使用Reflection.Emit的时候最大的问题就是不管以何种顺序调用CreateType方法总会出现依赖项有问题的错误。下面是一个简单的尝试但是无法成功:

Module Program

Sub Main()

Dim name = New AssemblyName("test")

Dim dasm = AppDomain.CurrentDomain.DefineDynamicAssembly(name, AssemblyBuilderAccess.RunAndSave)

Dim dmod = dasm.DefineDynamic
Module(name.Name, name.Name + ".dll")

Dim tA = dmod.DefineType("A", TypeAttributes.Public Or TypeAttributes.Class)

Dim tB = dmod.DefineType("B", TypeAttributes.Public Or TypeAttributes.Class, tA)

Dim tI = tB.DefineNestedType("I", TypeAttributes.NestedPublic Or TypeAttributes.Interface Or TypeAttributes.Abstract)

tA.AddInterfaceImplementation(tI)

tA.CreateType()

tB.CreateType()

tI.CreateType()

dasm.Save(name.Name + ".dll")

End Sub

End Module

我们发现,这里存在的问题主要是:

Reflection.Emit无法在这种情况下生成B类型的构造函数 创建类型的时候会发生类型解析错误

因此我们分别解决这两个问题。首先既然无法自动生成B的构造函数那么我们手工来创造它。这可以用TypeBuilder的DefineConstructor来做到。至于A类型的基类是Object所以直接用DefineDefaultConstructor即可。注意构造函数有很多必须的属性,包括PrivateScope、HideBySig、SpecialName、RTSpecialName等,必须都加上(否则使用它的时候会出很多错)。接下来就是循环引用导致找不到的问题,我们可以处理AppDomain的TypeResolve事件手动处理循环引用的问题。下面就是完整的方案:

Imports System.Reflection

Imports System.Reflection.Emit

Module Program

Sub Main()

Dim name = New AssemblyName("test")

Dim dasm = AppDomain.CurrentDomain.DefineDynamicAssembly(name, AssemblyBuilderAccess.RunAndSave)

Dim dmod = dasm.DefineDynamicModule(name.Name, name.Name + ".dll")

Dim tA = dmod.DefineType("A", TypeAttributes.Public Or TypeAttributes.Class)

Dim tB = dmod.DefineType("B", TypeAttributes.Public Or TypeAttributes.Class, tA)

Dim tI = tB.DefineNestedType("I", TypeAttributes.NestedPublic Or TypeAttributes.Interface Or TypeAttributes.Abstract)

tA.AddInterfaceImplementation(tI)

Const ctorAttr As MethodAttributes = MethodAttributes.Public Or MethodAttributes.PrivateScope Or

MethodAttributes.HideBySig Or MethodAttributes.SpecialName Or MethodAttributes.RTSpecialName

Dim ctorA = tA.DefineDefaultConstructor(ctorAttr)

Dim ctorB = tB.DefineConstructor(ctorAttr,

CallingConventions.Standard, {})

With ctorB.GetILGenerator

.Emit(OpCodes.Ldarg_0)

.Emit(OpCodes.Call, ctorA)

.Emit(OpCodes.Ret)

End With

AddHandler AppDomain.CurrentDomain.TypeResolve,

Function(sender As Object, e As ResolveEventArgs)

Select Case e.Name

Case "A" : Return tA.CreateType.Assembly

Case "B" : Return tB.CreateType.Assembly

Case "I" : Return tI.CreateType.Assembly

End Select

Return Nothing

End Function

tA.CreateType()

tB.CreateType()

tI.CreateType()

dasm.Save(name.Name + ".dll")

End Sub

End Module

这样生成的程序集无论是通过Reflector反编译还是使用C#/VB来引用都是正确的,和真正的VB编译器编译出来是一样的。

这个问题的缘由是VB Team的PM Lucian Wischick的博客上提出了这个问题。微软的VB Team正在将C++编写的VB编译器移植成VB代码。他们正在研究托管编译器的后端可不可以使用Reflection.Emit来实现。由于VB支持这种循环类型依赖的代码,所以他们很头疼遇到的问题。幸好一篇回复解决了这个问题。希望采用Reflection.Emit作为编译器后端的同学可以参考一下这种方法。

时间: 2014-12-25

趣味问题:你能用Reflection.Emit生成这段代码吗?(答案)的相关文章

趣味问题:你能用Reflection.Emit生成这段代码吗?

众所周知,Reflection.Emit是非常强大的工具,可以在运行时动态生成各种程序集.类型和方法的IL代码,几乎无所不能.原先我也是这样认为的,但是看了某个人的博客之后我发现想要用Reflection.Emit做一些特殊的事情还是很需要技巧性的.假设你还没有看过那个人的博客(暂时先不公开--)可以尝试一下这个问题.下面的代码可以用vbc.exe正确编译(当然等价C#程序也可以经试验C#编译器无法处理该逻辑,各位参照VB的行为吧)... Class A Implements B.I End C

艾伟_转载:趣味问题:你能用Reflection.Emit生成这段代码吗?

众所周知,Reflection.Emit是非常强大的工具,可以在运行时动态生成各种程序集.类型和方法的IL代码,几乎无所不能.原先我也是这样认为的,但是看了某个人的博客之后我发现想要用Reflection.Emit做一些特殊的事情还是很需要技巧性的.假设你还没有看过那个人的博客(暂时先不公开--)可以尝试一下这个问题.下面的代码可以用vbc.exe正确编译(当然等价C#程序也可以经试验C#编译器无法处理该逻辑,各位参照VB的行为吧)... Class AImplements B.IEnd Cla

通过wsdl2java工具生成客户端段代码(wsdl2java -p cn.com.css.misps.graph.webservice.impl -d F:\src -all http://10.)

首先当前是从官网下载cxf组件. Java代码 http://cxf.apache.org/download.html  http://cxf.apache.org/download.html 下载后解压,在这里主要是用到解压后的bin目录中的wsdl2java.bat该批处理文件. 可以直接进入bin目下,运行wsdl2java,需要注意的他的几个参数 我测试时直接运行的以下命令: 写道 wsdl2java -p cn.com.css.misps.graph.webservice.impl -

用 System.Reflection.Emit 来自动生成调用储存过程的实现

/**************************************************************** * * 用 System.Reflection.Emit 来自动生成调用储存过程的实现! * * By http://lostinet.com * * Copyrights : Not-Reversed * ****************************************************************///使用的例子namespac

Reflection.Emit的使用场景、工具包及示例总结

 最近处理一个业务需要动态的生成一些业务模型和库,使用到了Emit的处理,相关的资料整理一下供参考. Reflection.Emit目的 使用的场景: 应用中自定义一个自己的语言 运行中动态的创建类型.模块等,同时又需要提高效率(可以动态编译一次,然后就不用再处理了) 延迟绑定对象的使用,在和Office这类的软件时会用到 动态插件系统等 - System.Reflection.Emit主要的类: AssemblyBuilder 应用的初始点,反射发出代码.创建动态Modules ModuleB

struts2 cssclass:Struts2 checkboxlist标签 设置cssClass属性生成的html代码中check没有class属性问题

使用struts2 checkboxlist标签设置cssClass属性后,发现生成的html代码中 input 标签并没有class属性.打开checkboxlist.ftl看,内容如下:<input type="checkbox" name="${parameters.name?html}" value="${itemKeyStr?html}" id="${parameters.name?html}-${itemCount}&

连连看小游戏地图数据简单生成的AS代码

核心提示:flash教程,连连看小游戏地图数据简单生成的AS代码. 比如连连看的数据生成. 比较简单,这里不包括判断是否一定有解的部分,只是一个随意生成地图(给定行,列,每种图片的生成个数),没有思路的可以参考一下,高手也可以指正. //生成地图的类 package src.ww.llk.map { import mx.collections.ArrayCollection; public class MakeMap { private var mapParam:MapParam = null;

自动生成数据对象代码和CRUD操作的C#代码生成器

对象|数据 笔者近期在Vs2005平台和SqlServer2005平台之上研发了一个C#代码生成器,支持数据库表和C#数据对象的直接映射,以及CRUD操作代码的自动生成,用户只需要实现IDAL接口即可,该工具能够显著提高开发速度,让开发人员从繁重的数据库表对象封装编码中解脱出来(工具下载地址:http://itabby.com/index-5.asp),现特将核心处理代码进行发表,仅供参考: ///数据对象提取部分数据库类型和C#对象类型之间的转换过程private string Convert

GridView生成的HTML代码

 很多初学者都奇怪GridView生成的HTML代码到底是什么,下面有个示例对比,需要的朋友可以参考下  代码如下: <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="false" CssClass="tablecss">  <Columns>  <asp:TemplateField HeaderText="