惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

N
News and Events Feed by Topic
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
月光博客
月光博客
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
大猫的无限游戏
大猫的无限游戏
T
Tailwind CSS Blog
S
SegmentFault 最新的问题
V
V2EX
阮一峰的网络日志
阮一峰的网络日志
C
Cisco Blogs
博客园 - 叶小钗
P
Privacy International News Feed
Jina AI
Jina AI
Apple Machine Learning Research
Apple Machine Learning Research
T
Threatpost
IT之家
IT之家
博客园 - 聂微东
Know Your Adversary
Know Your Adversary
Help Net Security
Help Net Security
罗磊的独立博客
I
Intezer
S
Schneier on Security
博客园_首页
C
CERT Recently Published Vulnerability Notes
雷峰网
雷峰网
Cisco Talos Blog
Cisco Talos Blog
宝玉的分享
宝玉的分享
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Webroot Blog
Webroot Blog
TaoSecurity Blog
TaoSecurity Blog
MyScale Blog
MyScale Blog
P
Privacy & Cybersecurity Law Blog
T
The Exploit Database - CXSecurity.com
PCI Perspectives
PCI Perspectives
Security Latest
Security Latest
H
Heimdal Security Blog
S
Secure Thoughts
Hacker News: Ask HN
Hacker News: Ask HN
Y
Y Combinator Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Microsoft Security Blog
Microsoft Security Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
SecWiki News
SecWiki News
The GitHub Blog
The GitHub Blog
A
Arctic Wolf
A
About on SuperTechFans
aimingoo的专栏
aimingoo的专栏
T
Threat Research - Cisco Blogs
Engineering at Meta
Engineering at Meta
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC

博客园 - 装配脑袋

Windows Azure一些小技巧集合 C++ AMP实战:绘制曼德勃罗特集图像 GPU并行计算版函数图像生成器 自己动手开发编译器(十二)生成托管代码 自己动手开发编译器(十一)语义分析 自己动手开发编译器(十)miniSharp语法分析器 自己动手开发编译器(九)CPS风格的解析器组合子 自己动手开发编译器(八)用Linq编写解析器组合子 自己动手开发编译器(七)递归下降的语法分析器 自己动手开发编译器(六)上下文无关语言和文法 自己动手开发编译器特别篇——用词法分析器解决背诵圣经问题 自己动手开发编译器(五)miniSharp语言的词法分析器 自己动手开发编译器(四)利用DFA转换表建立扫描器 自己动手开发编译器(三)有穷自动机 自己动手开发编译器(二)正则语言和正则表达式 自己动手开发编译器(一)编译器的模块化工程 自己动手开发编译器(零)序言 DirectCompute & DirectX 11 计算着色器编程简介(翻译) 趣味问题:你能用Reflection.Emit生成这段代码吗?
趣味问题:你能用Reflection.Emit生成这段代码吗?(答案)
装配脑袋 · 2009-12-01 · via 博客园 - 装配脑袋

在上一篇博客中我提出了一个问题:如何用.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作为编译器后端的同学可以参考一下这种方法。