8

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

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

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

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

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

是否有任何“模型视图”类型的框架或技术可以用来使我的 T4 模板代码更干净?

4

2 回答 2

5

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

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

不那么重要但很好

  1. 使代码可搜索- 我不依赖 T4 插件,因为我发现它们都不够好,由于缺少 IntelliSense 来导航代码,我必须使代码可搜索。它可以很简单,而不是调用 Column 属性名称,我将其称为 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 曾经很优雅,但它仍然非常有用。

于 2012-10-29T19:52:01.910 回答
1

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

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

我在博客上写了一篇包含代码示例(之前/之后)和示例单元测试的文章,如果你想详细了解如何做的话。

于 2012-11-21T03:53:42.987 回答