sharpener
sharpener

Reputation: 1853

How to "change" immutable objects effectively?

Is there any clever design pattern or sort of generic method for "modification" of immutable objects?

Background:
Let's have set of different (not having common base) immutable objects, each having different set of {get; private set;} properties and one public constructor (accepting set of property values). These objects are and should remain immutable, but from time to time, in sort of special mode, their values are required to "change".

Such kind of modification means just creation of new object having the same values as the original one except the updated properties (like o = new c(o.a, updateB, updateC, o.d, ...)).

I can imagine calling the constructors in place, or defining an (e.g. extension) method returning new instance, accepting some parameters to identify the updated property/ies and modifying it/them via reflection, but everything seems to be very specific and not elegant to be used system wide for "any" immutable. I like the way linq handles e.g. the IEnumerables and the chain you can produce and completely re-transform the input collection. Any ideas?

Upvotes: 2

Views: 759

Answers (1)

Joey
Joey

Reputation: 354506

One example of something similar I'm aware of is Roslyn, Microsoft's current C# compiler, which uses immutable data structures extensively. The syntax tree classes have convenience methods for every property for returning a new instance with just one property changed, e.g.

var cu = Syntax.CompilationUnit()
            .AddMembers(
                Syntax.NamespaceDeclaration(Syntax.IdentifierName("ACO"))
                        .AddMembers(
                        Syntax.ClassDeclaration("MainForm")
                            .AddBaseListTypes(Syntax.ParseTypeName("System.Windows.Forms.Form"))
                            .WithModifiers(Syntax.Token(SyntaxKind.PublicKeyword))
                            .AddMembers(
                                Syntax.PropertyDeclaration(Syntax.ParseTypeName("System.Windows.Forms.Timer"), "Ticker")
                                        .AddAccessorListAccessors(
                                        Syntax.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration).WithSemicolonToken(Syntax.Token(SyntaxKind.SemicolonToken)),
                                        Syntax.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration).WithSemicolonToken(Syntax.Token(SyntaxKind.SemicolonToken))),
                                Syntax.MethodDeclaration(Syntax.ParseTypeName("void"), "Main")
                                        .AddModifiers(Syntax.Token(SyntaxKind.PublicKeyword))
                                        .AddAttributes(Syntax.AttributeDeclaration().AddAttributes(Syntax.Attribute(Syntax.IdentifierName("STAThread"))))
                                        .WithBody(Syntax.Block())
                                )
                        )
                );

or in your case:

o = o.WithB(updateB).WithC(updateC);

which I think reads quite nicely for the intended use case (updating only a few properties while keeping everything else the same). It especially beats the »always have to call the ctor« approach when there are many properties.

In Roslyn that's all auto-generated, I think. You can probably do something similar with T4, if warranted.

Upvotes: 3

Related Questions