存在哪些工具/库将采用结构并自动生成不可变包装器以及用于逐步构建新实例的“构建器”类?
示例输入:
struct Foo
{
public int apples;
public int oranges;
public Foo Clone() {return (Foo) base.MemberwiseClone();}
}
示例输出:
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;
}
}
这样的“工具”可以是IDE插件,也可以在运行时使用反射生成新类。
示例是在C#中,但我会对任何静态类型的OO语言(Java,Scala,C ++等)的解决方案感兴趣。
理想的功能:
Equals()
和GetHashCode()
以及任何接口方法)IFooReader
接口,其中包含每个struct成员的只读属性,由不可变和构建器实现。List
- > ReadOnlyCollection
或类似。Clone
方法“你不应该使用这样的工具,因为......”也欢迎回答。
答案 0 :(得分: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();}
}
4)使用内置的Reflection.Emit库生成具有不可变类型的新程序集。
答案 1 :(得分:2)
为什么要打扰建造者?
你有一个(讨厌的)可变结构,但如果你必须使用它直接使用它而不是创建一个繁琐且不必要的构建器。
令我感到困扰的是,你有足够数量的这些结构让你觉得你需要自动生成这种包装。我的直觉反应是你做错了......
如果不可变包装器的目的只是存储快照,那么只需使用以下内容:
public struct Snapshot<T> where t : struct
{
private readonly T data;
public Snapshot(T value) { this.data = value; }
public T Data { get { return data; } }
}
传入的结构保证永远不会再次更改,但您可以直接访问它上面的所有值(并且在调用底层get_Data函数时创建的副本上会对这些结果进行修改)