在上一篇博客中我提出了一个问题:如何用.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.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)

tA.CreateType()
tB.CreateType()
tI.CreateType()

dasm.Save(name.Name + ".dll")
End Sub
End Module

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

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

因此我们分别解决这两个问题。首先既然无法自动生成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作为编译器后端的同学可以参考一下这种方法。

艾伟_转载:趣味问题:你能用Reflection.Emit生成这段代码吗?(答案)相关推荐

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

    众所周知,Reflection.Emit是非常强大的工具,可以在运行时动态生成各种程序集.类型和方法的IL代码,几乎无所不能.原先我也是这样认为的,但是看了某个人的博客之后我发现想要用Reflecti ...

  2. 艾伟_转载:探索.Net中的委托

    废话 我本来以为委托很简单,本来只想简简单单的说说委托背后的东西,委托的使用方法.原本只想解释一下那句:委托是面向对象的.类型安全的函数指针.可没想到最后惹出一堆的事情来,越惹越多,罪过,罪过.本文后 ...

  3. 艾伟_转载:学习 ASP.NET MVC (第五回)理论篇

    本系列文章导航 学习 ASP.NET MVC (第一回)理论篇 学习 ASP.NET MVC (第二回)实战篇 学习 ASP.NET MVC (第三回)实战篇 学习 ASP.NET MVC (第四回) ...

  4. 艾伟_转载:C#语言基础常见问题汇总

    概述 1.什么是C#? C#是Microsoft公司设计的一种编程语言.它松散地基于C/C++,并且有很多方面和Java类似. Microsoft是这样描述C#的:"C#是从C和C++派生来 ...

  5. 艾伟_转载:.NET设计模式:观察者模式(Observer Pattern)

    概述 在软件构建过程中,我们需要为某些对象建立一种"通知依赖关系" --一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知.如果这样的依赖关系过于紧密,将 ...

  6. 艾伟_转载:ASP.NET MVC数据验证

    关于ASP.NET MVC的验证,用起来很特别,因为MS的封装,使人理解起来很费解.也可能很多人都在Scott Guthrie等人写的一本<ASP.NET MVC 1.0>书中,见过Ner ...

  7. 艾伟_转载:.NET Discovery 系列之二--string从入门到精通(勘误版下)

    本系列文章导航 .NET Discovery 系列之一--string从入门到精通(上) .NET Discovery 系列之二--string从入门到精通(勘误版下) .NET Discovery ...

  8. 艾伟_转载:.NET 4.0中数组的新增功能

    1.两数组是否"相等"? 在实际开发中,有时我们需要比对两个数组是否拥有一致的元素,例如,以下两个数组由于拥有相同的元素,因此被认为是相等的: int[] arr1 = new i ...

  9. 艾伟_转载:C# Design Patterns (3) - Decorator

    Decorator Pattern (装饰模式) 装饰模式可「动态」地给一个对象添加一些额外的职责,提供有别于「继承」的另一种选择.就扩展功能而言,Decorator Pattern 透过 Aggre ...

最新文章

  1. 【opencv】(2) 图像处理:边界填充、图像融合、图像阈值、数值计算
  2. 步入DevExpress的使用(VS)
  3. Python中的正则表达式(基础)
  4. numpy元素特定条件查找过滤
  5. 全网首发:WINDOWS主机与MAC虚拟机文件访问的解决办法
  6. web前端实战小游戏两则(附源码)
  7. 运放输入偏置电流方向_运算放大器+仪表放大器:如何为偏置电流提供直流回路?...
  8. cpu架构之体系架构
  9. 阿里云企业飞天会员是什么,如何申请?
  10. 高手进阶,终极内存技术指南——完整/进阶版 I(转)
  11. 计算机网络和internet选项,大师为你详解win10系统设置Internet选项的处理
  12. 声学模型训练-LDA算法
  13. linux c语言编程(转)
  14. 基于JAVA图书馆座位预约管理系统计算机毕业设计源码+数据库+lw文档+系统+部署
  15. 一个jar包冲突引起的StackOverflowError
  16. iris接口返回json数据封装
  17. postgreSQL与postGIS安装过程中遇到的坑及解决
  18. 防止进入后含有edittext的界面自动弹出软件盘
  19. “程序员锁死服务器导致公司倒闭”案正式开庭审理
  20. DP1363F高度集成的非接触读写芯片 13.56M NFC/RFID读卡器芯片 兼容替代CLRC663

热门文章

  1. 为什么会存在TIME_WAIT socket-详细分析No buffer space available
  2. 查看、关闭SELinux
  3. 《统一沟通-微软-实战》-3-部署-Exchange 2010-2-自定义安装
  4. sync.Once简介
  5. OpenStack SFC 深入剖析
  6. 手把手教你学dsp_新课免费看| 手把手教你学DSP,C2000从入门到精通
  7. 在eclipse安卓sqlite怎么用_把安卓手机性能发挥到极致之-Aid Learning
  8. SpringCloud 从菜鸟到大牛之八 服务容错 Hystrix
  9. Spring实用系列-深入了解SpringMVC OncePerRequestFilter过滤器原理
  10. Onvif之设备发现-基于gsoap2.8.27