开发者

Automatic generation of immutable class and matching builder class

开发者 https://www.devze.com 2022-12-19 12:24 出处:网络
What tools/libraries exist that will take a struct and automatically generate an immutable wrapper and also a \"builder\" class for incrementally building new instances?

What tools/libraries exist that will take a struct and automatically generate an immutable wrapper and also a "builder" class for incrementally building new instances?

Example input:

struct Foo
{
    public int apples;
    public int oranges;
    public Foo Clone() {return (Foo) base.MemberwiseClone();}
}

Example output:

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;
    }
}

Such a "tool" could be an IDE plugin or could generate the new class at run-time using reflection.

The example is in C# but I would be interested in a solution for any statically-typed OO language (Java开发者_高级运维, Scala, C++ etc.)

Desirable features:

  • Re-creates methods from the struct in the builder class
  • Re-creates nondestructive methods from the struct in the immutable class (esp. Equals() and GetHashCode() and any interface methods)
  • Also generates a IFooReader interface containing read-only properties for each struct member, implemented by both the immutable and the builder.
  • If a field's class has an immutable equivalent, uses the immutable version in the immutable class (see also How do I create a builder in C# for an object that has properties that are referenc types?) e.g. List -> ReadOnlyCollection or similar.
  • Alternatively take the builder class as input (where the builder uses automatic properties instead of delegating to a struct.)
  • Does not require the Clone method to be predefined

"You should not use a tool like this because..." answers are also welcome.


Here are four possible solutions.

1) Use CodeDOM to generate C# or VB code. This would also allow you to use visual studio extensions to generate your code in designer files. Similar to some of the built in tools that visual studio already offers - like the ones that generate wrappers for web service calls etc. Unfortunately I don't know much about extending Visual Studio.

  • Pros - You can generate source prior to building. This makes it easier to write code against the generated types from any assembly.
  • Cons - Not language agnostic. You're stuck with the languages that are supported.

2) Use the Mono.Cecil library to analyze your assembly post-build. You can then re-write the assembly with the new types included.

  • Pros - Language agnostic.
  • Cons - If you add the types to same assembly in which your structs are defined you won't be able to write code against the generated immutable struct types in the same assembly. If you put the generated types in a new assembly then this is possible.

3) Use PostSharp. I don't know as much about this library so you might not be able to add new types to your assembly but I know you can inject IL into methods. It also has a lot of nice stuff that makes it easy to do this with attributes. So you could do this -

[GenerateImmutable]
struct Foo
{
    public int apples;
    public int oranges;
    public Foo Clone() {return (Foo) base.MemberwiseClone();}
}
  • Pros - Language agnostic, AOP is easier to do in PostSharp.
  • Cons - Same as with Mono.Cecil and also not sure if you can generate new types using PostSharp.

4) Use built in Reflection.Emit libraries to generate a new assembly with your immutable types.

  • Pros - Language agnostic, No 3rd party stuff.
  • Cons - Must put generated types in new assemblies. Can't add them to the same assembly that the original type is in.


Why bother with the builder?

You have a (nasty) mutable struct, but if you must have it use that directly rather than creating a cumbersome and unnecessary Builder.

It bothers me somewhat that you have sufficient number of these structs for you to feel you need to autogenerate wrappers of this kind. My gut reaction is that you are doing it wrong...

If the purpose of the immutable wrapper is just to store a snapshot then just use something like this:

public struct Snapshot<T> where t : struct
{
    private readonly T data;
    public Snapshot(T value) { this.data = value; }
    public T Data { get { return data; } }
}

The struct passed in is guaranteed to never change again, but you can access all the values on it directly (and modifications on these results happen on the copy created when calling the underlying get_Data function)

0

精彩评论

暂无评论...
验证码 换一张
取 消