Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Automatic generation of immutable class and matching builder class

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.

like image 776
finnw Avatar asked Feb 09 '10 18:02

finnw


1 Answers

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.
like image 169
Brandon Cuff Avatar answered Oct 21 '22 12:10

Brandon Cuff