自动生成不可变类和匹配构建器类

fin*_*nnw 12 c# language-agnostic design-patterns immutability builder-pattern

存在哪些工具/库将采用结构并自动生成不可变包装器以及用于逐步构建新实例的"构建器"类?

输入示例:

struct Foo
{
    public int apples;
    public int oranges;
    public Foo Clone() {return (Foo) base.MemberwiseClone();}
}
Run Code Online (Sandbox Code Playgroud)

示例输出:

public class ImmutableFoo // could probably be a struct
{
    private Foo snapshot;
    internal ImmutableFoo(Foo value) { this.snapshot = value; }
    public FooBuilder Builder() { return new FooBuilder(snapshot); }
    public int Apples { get { return snapshot.apples; } }
    public int Oranges { get { return snapshot.oranges; } }
}

public class FooBuilder
{
    private Foo state;

    public int Apples { get { return state.apples; } set { state.apples = value; } }
    public int Oranges { get { return state.oranges; } set { state.oranges = value; } }

    public FooBuilder() { }

    internal FooBuilder(Foo toCopy) { state = toCopy.Clone(); }

    public ImmutableFoo Build()
    {
        ImmutableFoo result = new ImmutableFoo(state);
        state = state.Clone();
        return result;
    }
}
Run Code Online (Sandbox Code Playgroud)

这样的"工具"可以是IDE插件,也可以在运行时使用反射生成新类.

这个例子是在C#中,但我会对任何静态类型的OO语言(Java,Scala,C++等)的解决方案感兴趣.

理想的功能:

  • 从构建器类中的struct重新创建方法
  • 重新创建自结构中不可变类非破坏性的方法(尤其是Equals()GetHashCode()和任何接口方法)
  • 还生成一个IFooReader包含每个struct成员的只读属性的接口,由不可变和构建器实现.
  • 如果字段的类具有不可变等效,则在不可变类中使用不可变版本(另请参阅如何在C#中为具有参考类型属性的对象创建构建器?),例如List- > ReadOnlyCollection或类似.
  • 或者将构建器类作为输入(构建器使用自动属性而不是委托给结构.)
  • 不要求Clone预定义方法

"你不应该使用这样的工具,因为......"答案也是受欢迎的.

Bra*_*uff 4

这里有四种可能的解决方案。

1) 使用CodeDOM生成C#或VB代码。这还允许您使用 Visual Studio 扩展在设计器文件中生成代码。类似于 Visual Studio 已经提供的一些内置工具 - 例如为 Web 服务调用生成包装器的工具等。不幸的是,我对扩展 Visual Studio 不太了解。

  • 优点 - 您可以在构建之前生成源代码。这使得根据任何程序集生成的类型编写代码变得更加容易。
  • 缺点 - 与语言无关。您被支持的语言所困扰。

2) 使用Mono.Cecil库分析构建后的程序集。然后,您可以使用包含的新类型重新编写程序集。

  • 优点 - 与语言无关。
  • 缺点 - 如果将类型添加到定义结构的同一程序集中,您将无法针对同一程序集中生成的不可变结构类型编写代码。如果将生成的类型放入新程序集中,则这是可能的。

3)使用PostSharp。我对这个库了解不多,因此您可能无法向程序集中添加新类型,但我知道您可以将 IL 注入到方法中。它还具有许多好东西,可以轻松地使用属性来完成此操作。所以你可以这样做 -

[GenerateImmutable]
struct Foo
{
    public int apples;
    public int oranges;
    public Foo Clone() {return (Foo) base.MemberwiseClone();}
}
Run Code Online (Sandbox Code Playgroud)
  • 优点 - 与语言无关,AOP在 PostSharp 中更容易实现。
  • 缺点 - 与 Mono.Cecil 相同,并且不确定是否可以使用 PostSharp 生成新类型。

4) 使用内置的Reflection.Emit库生成具有不可变类型的新程序集。

  • 优点 - 与语言无关,没有第三方的东西。
  • 缺点 - 必须将生成的类型放入新程序集中。无法将它们添加到原始类型所在的同一程序集中。