【问题标题】:Is there anything out there to make T4 code more... clean?有什么东西可以让 T4 代码更……干净吗?
【发布时间】:2012-10-18 10:37:15
【问题描述】:

我在 T4 模板中塞入了一个相当复杂的东西。基本上我会采取类似

{=foo=} 更多文字...

并将其转换为类(视图),如下所示:

public class MyView
{
  public string foo{get;set;}
  public string Write()
  {
    return foo+@" more text...";
  }
}

生成的代码当然比这复杂得多。无论如何,T4 模板现在有超过 600 行代码,而且很快就变得难以管理。我认为主要问题是混合代码和“内容”(即静态代码)。我不太确定如何彻底解决这个问题(当然不会影响生成的代码)。除了简单地测试 T4 执行错误之外,我也没有看到任何可行的方法来对我的 T4 代码进行单元测试。当然,测试它生成的代码似乎是几乎不可能完成的任务。

是否有任何“模型-视图”类型的框架或技术可以让我的 T4 模板代码更简洁?

【问题讨论】:

  • 这可能不是一个选项,但我更喜欢Resharper Templates 而不是 T4 模板。此外,Resharper 也是用于其他用途的绝佳工具。物有所值。
  • Resharper 是一个很好的工具,但恕我直言,比较 Resharper 模板与 T4 是比较苹果和橘子。 Resharper Templates 正在推广带有工具支持的复制粘贴反模式,导致您必须维护越来越多的冗余代码,从而增加了维护成本。 T4(和其他工具)将冗余最小化,因为您编写了一个生成代码工件(具有大量冗余)的元程序(具有低冗余)。关键是元程序和生成的代码之间的随意连接不会丢失。

标签: c# unit-testing refactoring code-generation t4


【解决方案1】:

恕我直言,编写复杂模板时最重要的两个概念是

  1. 分离模型和视图 - 这有助于将模板逻辑保持在最低限度(通常是维护问题的原因)。我个人认为这不需要一个框架,它只需要你去做。
  2. Partial 是你的朋友 - 我一般使用 T4 从模型生成骨架代码。特定行为可能不值得在模型中投入精力,最好通过使用部分类或方法来允许该行为进入。

没那么重要,但很好

  1. 使代码可搜索 - 我不依赖 T4 插件,因为我发现它们都不够好,缺少 IntelliSense 来导航代码,我必须使代码可搜索。它可以很简单,而不是调用 Column 属性 Name,我将其称为 ColumnName。
  2. 在输出中插入“标签” - 更容易找到生成该部分输出的代码。

分离模型/视图的示例:

<#
    // Model for Dependency Pooperties
    Model = new []
    {
        new ClassDefinition ("MyDataGrid")
            {
                P ("CultureInfo"            , "CultureInfo"),
                P ("Pen"                    , "CellBorderPen"),
                P ("IEnumerable<object>"    , "Rows"),
                C ("WColumnDefinition"      , "Columns"),
            },
    };
#>
// Include the view
<#@ include file="..\T4\DependencyProperties.ttinclude" #>

视图然后迭代模型并生成依赖属性。

依赖属性的行为随后被实现为部分方法

    partial void Changed_Columns(
        ObservableCollection<WColumnDefinition> oldValue, 
        ObservableCollection<WColumnDefinition> newValue
        )
    {
        HookUpColumns(oldValue, null);
        HookUpColumns(newValue, this);            
    }

请注意,将这种特定行为放入模型中会使模型显着复杂化。

最后;即使是一个称职的程序员也需要时间来胜任地编写元程序。我尝试了几次之后才找到了一种我认为可以维护的风格,但对我来说这是值得的,因为我能够更快地交付质量。

我希望这会有所帮助...

PS。我认为没有人会说 T4 曾经很优雅,但它仍然非常有用。

【讨论】:

  • 我实际上考虑过将“模型”分离到一个常规程序集,只使用一个简单的 T4“视图”来实际处理代码生成,然后将 T4 代码链接到常规程序集。 . 这也解决了大多数单元测试的复杂性
  • 好吧,我想我找到了至少进行单元测试的“好”方法,这也迫使“视图”和“模型”严格分离。当我整理好所有细节后,我会在这里发布,但基本上它涉及将“模型”放在不同的.cs 文件中,该文件包含在生成的 T4 输出中。它并不完美,但它是迄今为止我发现的最好的
  • 您可能想查看我的self-answer,了解我最终是如何做到这一点的,使智能感知、编译器错误和单元测试对我的 T4 模板使用的逻辑起作用
  • 我在你的另一个问题中看到了你的“技巧”,我很喜欢。
【解决方案2】:

经过漫长的旅程,我终于在我的 T4 模板中签入了第一个单元测试。基本上,我所做的是抽象出一个“视图”(实际的 T4 模板)和“逻辑”(生成代码,但实际上并不输出它并且不依赖于 T4)

然后我更进一步,并使用了一个大的hack 来制作它,以便我的逻辑文件在 T4 之外编译。这具有很好的效果,使智能感知和编译器错误起作用。它还允许我通过简单地引用项目来从单元测试中访问我的逻辑类。

我写了一篇完整的文章,其中包含代码示例(之前/之后)和示例单元测试on my blog,如果您想详细了解如何操作的话。

【讨论】:

    猜你喜欢
    • 2021-03-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-12
    • 1970-01-01
    • 2022-08-13
    • 2020-03-05
    • 1970-01-01
    相关资源
    最近更新 更多