Reflection.Emit 与 CodeDOM
Posted
技术标签:
【中文标题】Reflection.Emit 与 CodeDOM【英文标题】:Reflection.Emit vs CodeDOM 【发布时间】:2011-01-22 22:35:05 【问题描述】:使用 Reflection.Emit 库与 CodeDOM 在运行时动态生成代码有哪些优缺点?
我正在尝试根据运行时以 XML 形式提供的元数据在系统中生成一些(相对复杂的)动态类。我将生成扩展应用程序集中现有类的类,实现额外的接口,添加方法,并覆盖虚拟和抽象成员。
我想确保在深入实施之前选择合适的技术。任何有关这些不同代码生成技术有何不同的信息都会有所帮助。此外,任何有关简化或简化使用任一 API 的开源库的信息也会很有用。
【问题讨论】:
在阅读本文时,NHibernate 是第一个跳入我脑海的东西。值得看看他们是如何做到的吗? 其实我在看那个。他们使用 Reflection.Emit,但不清楚为什么他们选择了与 CodeDOM 相比。 今天最大的不同是CSharpCodeProvider
API 仅在 .NET Core 3.0 及更高版本中可用。它们甚至不是 .NET Standard 的一部分(与 Reflection.Emit
不同)
【参考方案1】:
您可能想查看ExpandoObject
。但是它只是 .NET 4.0。
【讨论】:
【参考方案2】:我认为 CodeDOM 和 Reflection.Emit 的关键点如下:
CodeDom 生成 C# 源代码,通常用于生成要包含在解决方案中并在 IDE 中编译的代码(例如,LINQ to SQL 类、WSDL、XSD一切都以这种方式工作)。在这种情况下,您还可以使用部分类来自定义生成的代码。它的效率较低,因为它生成 C# 源代码,然后运行编译器来解析它(再次!)并编译它。您可以使用循环等相对高级的构造(类似于 C# 表达式和语句)生成代码。
Reflection.Emit 生成一个 IL,因此它直接生成一个也只能存储在内存中的程序集。因此效率更高。您必须生成低级 IL 代码(值存储在堆栈中;必须使用跳转来实现循环),因此生成任何更复杂的逻辑有点困难。
总的来说,我认为 Reflection.Emit 通常被认为是在运行时生成代码的首选方式,而在编译时生成代码时首选 CodeDOM。在您的场景中,它们可能都可以正常工作(尽管 CodeDOM 可能需要更高的权限,因为它实际上需要调用 C# 编译器,这是任何 .NET 安装的一部分)。
另一种选择是使用Expression
class。在 .NET 4.0 中,它允许您生成与 C# 表达式和语句等效的代码。但是,它不允许您生成类。因此,您可以将其与 Reflection.Emit 结合使用(以生成将实现委托给使用 Expression
生成的代码的类)。对于某些场景,您可能也不需要完整的类层次结构 - 通常动态生成的委托字典(例如 Dictionary<string, Action>
)就足够了(当然,这取决于您的具体场景)。
【讨论】:
据我了解CodeDOM也可以generate code in memory? 您能否详细说明如何在 CodeDom 中使用部分类?如果我的研究表明什么,那就是你根本不能使用partial
:它甚至不是TypeAttributes
枚举中的一个选项。
一个完整的答案。谢谢。【参考方案3】:
以 CodeDom 为目标的代码往往更易于维护,因为您生成的是 C# 代码而不是 IL(阅读 C# 的人比阅读 IL 的人多)。此外,如果你的 CodeDom 代码错误,你会得到一个编译器错误;如果生成无效的 IL,则会出现致命异常或崩溃。
但是,由于 CodeDom 调用 csc.exe
编译器,因此准备好代码以供使用的速度要慢一些。使用 Reflection.Emit,您可以将代码直接生成到内存中。
CodeDom 可能适用于大多数事情; XmlSerializer
和 WinForms 设计者使用它。
【讨论】:
XmlSerializer 使用 CodeDOM 做什么?抱歉,我可以在 Reflector 中查找它,但我很懒,因为我的注意力分散在许多其他问题上,既然你回答了它,我假设你已经投入了时间,所以你可以简单地输入答案而无需花费太多时间要么。 @WaterCoolrv2 如果我不得不猜测,他们会构建一个程序集来读取/写入目标类型。我曾经被带到一个项目中,该项目有一个巨大的、基于反射的数据访问层。它很慢,所以用户讨厌它,但没有人知道如何修复它。我通过缓存和 Emit 的组合使它几乎是瞬间完成的。除了消除慢反射之外,您还可以通过首先检查类型并将适当的操作码直接发送到方法中来消除开销,而不是在序列化过程中分支或调用专门的委托。以上是关于Reflection.Emit 与 CodeDOM的主要内容,如果未能解决你的问题,请参考以下文章