Reputation: 1853
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 IEnumerable
s and the chain you can produce and completely re-transform the input collection. Any ideas?
Upvotes: 2
Views: 759
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