一段时间过去了,但我还没有看到满意的回复,所以我将写下我过去所做的事情。我仍然对其他方法也很感兴趣,因为我觉得我已经取得的并不理想,我自己也想做得更好。
在过去的几年里,我尝试了四种列出的“丰富”类代码生成方法中的三种:
- 反射.发射
- T4
- CodeDom(但我很早就放弃了这个,因为它是在我的项目时,不完整!)
加上其他三个:
- IL重写使用Profiler API Here和here(实际使用)
- 在这里使用 ContextBoundObject 进行拦截(仅测试过)
- 嵌入 C# 编译器 (Roslyn/MonoCSharp)在这里和现在还有脚本(仅研究,未实际使用)
一般来说,我研究了所有这些工具,以 AOP 方式获得一些代码“编织”,从类似于 OP 示例的东西开始:制作带有“特殊”属性标记的类和方法,修改它们的行为。在我第一个项目的时候,C# 的 AOP 解决方案还不够成熟(但同时这可能已经改变了),所以我不能说任何关于 PostSharp 的事情,例如。正如我所说,我的目的非常相似:基于类和方法的属性,向类添加一些额外的代码(以及类成员)。我最终使用 Reflection.Emit 作为原型,最后使用 Profiler API 重写 IL。
我最终使用 T4 的第二个项目略有不同。它已经使用了代码生成器(更具体地说,是解析器生成器),但我们需要进一步(自动!)修改生成的源代码。
反射.发射
我使用了以下方法:一个类(我们称之为 MakeProxy)遍历一个给定的程序集,搜索属性,然后发出一个新的 dll(一个“代理”),它在后台调用原始程序集,提供所需的接口和行为给用户。
我实际上在一个工具中使用了这个类,一个 .NET 可执行文件,它在编译通过后由 MS Build 运行。
主要缺点:引用可能难以处理。您需要引用自动生成的 dll,因此您必须将代码拆分到不同的项目中,并且您甚至无法在解决方案中引用原始项目......这可能是不可接受的。此外,您也“看到”(在 IDE 中)原始代码,而不是生成的代码;这可能会使调试变得困难。
探查器 API
在这种情况下,您执行与 Reflection.Emit 相同的工作,但您不需要预先构建任何“代理”;代码在 IL 生成时注入(即一次,这在性能方面非常好)。您只需编写(非托管)探查器 DLL,并在其下启动程序。您的“分析器”将收到有关执行哪些功能(或者更好的是,哪些功能是 JITed)的通知,您可以采取相应的行动。
主要缺点:您不能修改类的“结构”(方法、字段、...)(不容易);使用非托管 API 生成 IL 是相当困难的(你必须处理很多事情,当出现问题时调试是一场真正的噩梦!)主要优点:不需要修改原始代码(尤其是不需要修改调用方 - 考虑到该特定项目的要求,这就是最终导致我走向这个解决方案的原因)。
T4
我将 T4 用于另一个项目,但我可以在这里分享我的发现。它适用于转换文本,但不适用于转换代码:语法变得“奇怪”(您有代码,并且代码中的代码可以生成代码......您很容易迷失方向)。
主要优点:与 IDE 和构建系统很好地集成;你得到智能感知支持;很容易上手;你实际上看到你在调试什么
就个人而言,我希望拥有(或自己构建)像 T4 这样的东西,但它执行 C# -> C# 代码转换;这样,您就可以拥有 IDE 的优势(包括对“原始”和“生产”事物的智能感知)和 Reflection.Emit 的强大功能。您可能需要同时构建 C#/C# 编译器和 VS 插件才能获得所有这些好东西。
最后一点:今天,我会使用比 Reflection.Emit 更“高级”的东西,比如 IKVM emit 或Sigil