有没有什么可以使T4代码更干净...干净?

时间:2012-10-29 05:04:47

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

我把一个相当复杂的东西塞进了T4模板。基本上我采取像

这样的东西
  

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

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

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

生成的代码当然比这复杂得多。无论如何,T4模板现在超过600行代码,并且真正变得难以管理。我认为主要的问题是混合代码和内容"(即静态代码)。我不确定如何干净地解决这个问题(当然不会影响生成的代码)。我也没有看到任何可行的单元测试我的T4代码的方法,除了简单地测试它的T4执行错误。当然,似乎几乎不可能测试它生成的代码。

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

2 个答案:

答案 0 :(得分:5)

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

  1. 分离模型和视图 - 这有助于将模板逻辑保持在最低限度(通常是导致维护问题)。我个人认为这不需要框架,只需要你这样做。
  2. 部分是您的朋友 - 我通常使用T4从模型生成框架代码。特定的行为可能不值得投入模型,更好的做法是通过使用部分类或方法来实现这种行为。
  3. 不那么重要但很好

    1. 让代码可搜索 - 我不依赖于T4 Addons,因为我发现它们都不够好,缺少IntelliSense以便导航代码我必须使代码可搜索。它可以很简单,而不是调用Column属性Name,我称之为ColumnName。
    2. 在输出中插入“tags” - 可以更轻松地找到生成该部分输出的代码。
    3. 分离模型/视图的示例:

      <#
          // 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是优雅的,但它仍然很有用。

答案 1 :(得分:1)

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

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

我写了一篇完整的代码示例(之前/之后)和示例单元测试on my blog的文章,如果你想了解如何做的冗长细节。