【问题标题】:How to generate code based on another class?如何基于另一个类生成代码?
【发布时间】:2011-07-20 12:53:43
【问题描述】:

为了创建我们的测试数据,我们使用了 Builder 模式的以下变体(简化示例!):

示例类:

public class Person
{
   public string Name { get; set; }
   public string Country { get; set; }
}

建造者:

public class PersonBuilder
{
    private string name;
    private string country;

    public PersonBuilder()
    {
        SetDefaultValues();
    }

    private void SetDefaultValues()
    {
        name = "TODO";
        country = "TODO";
    }

    public Person Build()
    {
        return new Person
                   {
                       Name = name,
                       Country = country
                   };
    }

    public PersonBuilder WithName(string  name)
    {
        this.name = name;            
        return this;
    }

    public PersonBuilder WithCountry(string country)
    {
        this.country = country;
        return this;
    }
}

注意:示例本身的上下文不相关。这里重要的是,在示例中,如何通过查看实体类 (Person) 并应用相同的模式来完全生成像 PersonBuilder 这样的构建器类 - 见下文。

现在假设 person 类有 15 个属性而不是 2 个。实现 builder 类需要一些功夫,而理论上,它可以从 Person 类自动生成。我们可以使用代码生成来快速设置构建器类,并在需要时添加自定义代码。

代码生成过程必须了解上下文(person 类的名称和属性),因此简单的基于文本的代码生成或正则表达式魔法在这里并不合适。首选动态解决方案,而不是基于文本,并且可以从 Visual Studio 内部快速触发。

我正在寻找针对此类场景执行代码生成的最佳方式。 反射?密码匠? T4 模板?带有宏的 Resharper Live 模板?

我期待看到一些很棒的答案:)

【问题讨论】:

    标签: visual-studio reflection code-generation resharper codesmith


    【解决方案1】:

    T4 解决方案是一个很好的 Visual Studio 集成选项。您可以在 T4 模板中使用反射来实际生成代码。

    【讨论】:

      【解决方案2】:

      如果仅用于测试,请考虑使用类似 RhinoMocks 的模拟框架:

      internal class PersonBuilder
      {
          private MockRepository _mockRepository;
          private IPerson _person;
      
          public PersonBuilder()
          {
              _mockRepository = new MockRepository();
              _person = _mockRepository.Stub<IPerson>();
          }
      
          public PersonBuilder WithName(string name)
          {
              _person.Name = name;
              return this; 
          }
      
          public PersonBuilder WithCountry(string Country)
          {
              _person.Country= Country;
              return this;
          }
      
          public IPerson Build()
          {
              _mockRepository.ReplayAll();
              return _person; 
          }
      
      }
      

      通过这种方式,您的构建器可以随着您的需要而发展。此外,您不需要更改 Build 方法。只需添加“WithX”方法。

      【讨论】:

      • 我们已经在我们的单元测试中广泛使用了模拟,但我从来没有想过为此使用模拟:)。我认为这将是解决这个特定问题的好方法,但不是一般问题,它的范围比测试更广泛。不过还是非常感谢这个答案!
      【解决方案3】:

      我们在CodeSmith Generator 5.x 中添加了一项功能,允许您从现有代码生成。请看一下documentation here。您还可以在 CodeSmith 生成器模板中使用反射或任何 .NET 库。

      谢谢 -布莱克·涅米斯基

      【讨论】:

        【解决方案4】:

        查看ABSE 建模方法及其 IDE,AtomWeaver。 ABSE 是一个基于模板的建模和代码生成框架,其中模型(与 UML 或 MDA 无关)是通过应用“构建块”(Atoms)来创建的。这些 Atom 是模板/程序混合体,并且是上下文感知的:Atom 可以根据其在树上的位置以及某些 Atom 的存在/不存在来生成代码。

        模型宿主(在本例中为 AtomWeaver)将“执行”模型以获得所需的源代码。模型可以是“源”:更改模型并根据需要多次重新生成。

        AtomWeaver 没有集成到 Visual Studio 中,但可以毫无问题地与它一起工作。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-12-11
          • 2013-04-21
          • 1970-01-01
          • 2019-11-09
          • 1970-01-01
          • 1970-01-01
          • 2011-11-27
          • 1970-01-01
          相关资源
          最近更新 更多