【问题标题】:Using reflection for code gen?对代码生成使用反射?
【发布时间】:2010-11-13 09:04:48
【问题描述】:

我正在编写一个控制台工具来为类库中的对象生成一些 C# 代码。我可以实际生成代码的最佳/最简单的方法是在构建库后使用反射。它工作得很好,但这似乎充其量只是一个随意的方法。由于生成的代码将与库一起编译,因此在进行更改后,我需要构建解决方案两次以获得最终结果等。其中一些问题可以通过构建脚本来缓解,但它仍然感觉像是对我来说有点过分了。

我的问题是,对于这类事情是否有任何高级别的最佳实践?

【问题讨论】:

  • 所以你正在寻找一些方法来事先分析你的类库的源代码,然后在那个阶段生成代码?
  • 我并不是在寻找一个具体的解决方案,更像是“这个方法没问题,因为......”或“你绝对不应该这样做,因为......”。

标签: c# reflection code-generation


【解决方案1】:

您是否考虑过使用 T4 模板来执行代码生成?看起来它现在得到了更多的宣传和关注,并且在 VS2010 中得到了更多的支持。

本教程似乎以数据库为中心,但它可能会为您提供一些指导:http://www.olegsych.com/2008/09/t4-tutorial-creatating-your-first-code-generator/ 此外还有最近关于 T4 的 Hanselminutes:http://www.hanselminutes.com/default.aspx?showID=170

编辑:另一个很棒的地方是 StackOverflow 上的 T4 标签:https://stackoverflow.com/questions/tagged/t4

编辑:(通过提问者,新的发展)

从 VS2012 开始,T4 现在支持一步反射活动项目。这意味着您可以更改您的代码,并且 T4 模板的编译输出将反映最新版本,而无需您执行第二个反映/构建步骤。借助此功能,我将其标记为已接受的答案。

【讨论】:

    【解决方案2】:

    不清楚你在做什么,但似乎很清楚的是你有一些基线代码,并且基于它的一些属性,你想生成更多代码。

    所以这里的关键问题是,在给定基线代码的情况下,如何提取有趣的属性,以及如何从这些属性生成代码?

    反射是一种将运行(至少是加载)代码的属性提取到与反射用户代码相同的执行环境中的方法。反射的问题在于它只提供了一组非常有限的属性,通常是类、方法的列表,或者可能是参数的名称。如果您想要做的所有代码生成都可以用它来完成,那么反射似乎就好了。但是如果你想要关于代码的更详细的属性,反射不会削减它。

    事实上,可以从中提取真正任意代码属性的唯一工件是作为字符串的源代码(你怎么能回答,是加法运算符和变量中间的 T 之间的字符数名称是质数?)。实际上,您可以从字符串中获得的属性通常不是很有帮助(请参阅我刚刚给出的示例:)。

    编译器人员在过去 60 年中一直在研究如何提取有趣的程序属性,而如果你忽略他们在这半个世纪中学到的东西,那你就是个彻头彻尾的白痴。

    他们已经确定了一些相对标准的“编译器数据结构”:抽象语法树 (AST)、符号表 (ST)、控制流图 (CFG)、数据流事实 (DFF)、程序三元组、ponter 分析, 等等。 如果你想分析或生成代码,最好的办法是先将它处理成这样的标准编译器数据结构,然后再完成这项工作。如果您有 AST,则可以回答有关使用哪些运算符和操作数的各种问题。如果你有 ST,你可以回答关于 where-defined、where-visible 和 what-type 的问题。如果您有 CFG,您可以回答有关“this-before-that”、“语句 X 依赖于什么条件”的问题。如果您有 DFF,则可以确定哪些分配会影响代码中某个点的操作。恕我直言,反射永远不会提供此功能,因为它始终仅限于运行时系统开发人员在运行程序时愿意保留的内容。 (也许有一天他们会保留所有编译器数据结构,但它不会是反射;它最终会成为编译器支持)。

    现在,在您确定了感兴趣的属性之后,您会为代码生成做什么?在这里,编译器人员一直专注于机器代码的生成,以至于他们不提供标准答案。这样做的人是程序转换社区 (http://en.wikipedia.org/wiki/Program_transformation)。这里的想法是至少将程序的一种表示形式保留为 AST,并为匹配源代码语法提供特殊支持(通过从感兴趣的代码片段构造模式匹配 AST),并提供“重写”规则,如效果,“当你看到这个模式时,然后在这个条件下用那个模式替换它”。 通过将条件与编译器人员的各种属性提取机制联系起来,您可以相对容易地说出您想要的 50 年经验支持的内容。这样的程序转换系统具有读取源代码的能力, 进行分析和转换,一般是转换后重新生成代码。

    对于您的代码生成任务,您需要将基线代码读入 AST,应用分析以确定有趣的属性,使用转换生成新的 AST,然后吐出答案。

    要使这样的系统有用,它还必须能够解析和漂亮地打印各种源代码语言,以便 C# 爱好者以外的人也可以享受代码分析和生成的好处。

    这些想法都在 DMS Software Reengineering Toolkit。 DMS 可处理 C、C++、C#、Java、COBOL、JavaScript、PHP、Verilog 等多种语言。

    (我是 DMS 的架构师,所以我的观点比较偏颇。YMMV)。

    【讨论】:

    • 有偏见?看起来是这样 :) 实际上我所做的只是查看类型数据……虽然读起来很有趣
    【解决方案3】:

    您可能希望使用CodeDom,这样您只需构建一次。

    首先,我会阅读 this CodeProject article 以确保没有不使用 Reflection 就无法支持的特定语言功能。

    【讨论】:

      【解决方案4】:

      据我了解,您可以使用 Common Compiler Infrastructure (http://ccimetadata.codeplex.com/) 之类的工具来以编程方式分析您现有的 c# 源代码。

      不过,这对我来说似乎很复杂,而且 CCI 显然只完全支持 C# 语言规范 2。更好的策略可能是简化您现有的方法。

      【讨论】:

        【解决方案5】:

        我不确定最好的方法,但你可以这样做

        • 作为基础 dll 的构建后步骤,运行代码生成器
        • 作为另一个构建后步骤,运行 cscmsbuild 来构建生成的 dll

        • 其他依赖于生成的 dll 的东西也需要依赖于基础 dll,所以构建顺序保持正确

        【讨论】:

          猜你喜欢
          • 2012-05-04
          • 2014-05-19
          • 2011-02-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-12-02
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多