Immutable object template in C # - what do you think?

Over the course of several projects, I developed a template for creating readonly objects and immutable object graphs. Immutable objects carry a 100 percent thread and, therefore, can be reused by thread. In my work, I very often use this template in web applications for configuration settings and other objects that I load and cache in memory. Cached objects should always be immutable, since you want to ensure that they are not unexpectedly changed.

Now you can easily create immutable objects, as in the following example:

public class SampleElement { private Guid id; private string name; public SampleElement(Guid id, string name) { this.id = id; this.name = name; } public Guid Id { get { return id; } } public string Name { get { return name; } } } 

This is good for simple classes, but for more complex classes, I cannot imagine the concept of passing all values ​​through a constructor. It is desirable to have property setters, and your code creating a new object becomes easier to read.

So how do you create immutable objects with setters?

Well, in my template objects begin to change completely until you freeze them with one method call. Once an object is frozen, it will remain unchanged forever - it cannot be turned back into a mutable object. If you need a modified version of an object, you simply clone it.

Ok, now for some kind of code. In the following code snippets, I tried to weld a template to its simplest form. IElement is the basic interface that should ultimately implement all immutable objects.

 public interface IElement : ICloneable { bool IsReadOnly { get; } void MakeReadOnly(); } 

The Element class is the default implementation of the IElement interface:

 public abstract class Element : IElement { private bool immutable; public bool IsReadOnly { get { return immutable; } } public virtual void MakeReadOnly() { immutable = true; } protected virtual void FailIfImmutable() { if (immutable) throw new ImmutableElementException(this); } ... } 

Let us reorganize the SampleElement class above to implement an immutable object template:

 public class SampleElement : Element { private Guid id; private string name; public SampleElement() {} public Guid Id { get { return id; } set { FailIfImmutable(); id = value; } } public string Name { get { return name; } set { FailIfImmutable(); name = value; } } } 

Now you can change the Id property and the Name property until the object has been marked as immutable by calling the MakeReadOnly () method. Once it becomes immutable, calling setter will throw an ImmutableElementException.

Final note: The complete template is more complex than the code snippets given here. It also contains support for sets of immutable objects and complete object graphs of immutable object graphs. The full template allows you to turn the entire object graph immutable by calling the MakeReadOnly () method on the most distant object. When you start creating larger object models using this template, the risk of leaking objects increases. An object leak is an object that cannot call the FailIfImmutable () method before making changes to the object. To check for leaks, I also developed a common class of leak detectors for use in unit tests. It uses reflection to check if all properties and methods throw an ImmutableElementException in an immutable state. In other words, TDD is used here.

I loved this template very much and found great advantages in it. So what would I like to know if any of you use similar patterns? If so, do you know of any good resources that document this? I am essentially looking for potential improvements and any standards that may already exist in this thread.

+44
immutability c # design-patterns functional-programming
Nov 04 '08 at 21:49
source share
15 answers

For information, the second approach is called the "immutability of the eater."

Eric Lippert has a series of immutability blog posts starting here . I am still facing CTP (C # 4.0), but it looks interesting what optional / named parameters (in .ctor) can do here (when matching with read-only fields) ... [update: I wrote here ]

For information, I probably won’t do these virtual methods — we probably don’t want subclasses to be able to make this non-freezing. If you want them to be able to add additional code, I would suggest something like:

 [public|protected] void Freeze() { if(!frozen) { frozen = true; OnFrozen(); } } protected virtual void OnFrozen() {} // subclass can add code here. 

Also - AOP (e.g. PostSharp) may be a viable option to add all of these ThrowIfFrozen () checks.

(apologies if I changed the names of the terms / methods - SO does not keep the original message visible when compiling the answers)

+29
Nov 04 '08 at 22:28
source share

Another option is to create a Builder class.

For example, in Java (and C # and many other languages) String is immutable. If you want to do several operations to create a String, you use StringBuilder. This can be changed, and then, once you are done, you will return the last String object to it. From this moment it is unchanged.

You can do something similar for your other classes. You have your immutable Element, and then ElementBuilder. All that the builder would do is save the parameters that you set, and then when you complete it, it builds and returns an immutable element.

This is a bit more code, but I think it's cleaner than creating setters for a class that should be immutable.

+17
Nov 04 '08 at 21:59
source share

After my initial discomfort due to the fact that I had to create a new System.Drawing.Point for each modification, I completely embraced the concept a few years ago. In fact, I now create each field as readonly by default and only modify it to be mutable if there is a good reason - which is surprisingly rare.

I don't care about cross-threading problems though (I rarely use code where relevant). I just find it much, much better because of semantic expressiveness. Consistency is the embodiment of an interface that is difficult to use incorrectly.

+9
Nov 04 '08 at 21:58
source share

You are still dealing with the state and therefore can still be bitten if your objects are parallelized before you make them immutable.

A more functional way could be to return a new instance of the object with each installer. Or create a mutable object and pass it to the constructor.

+8
Nov 04 '08 at 21:53
source share

The (relatively) new software development paradigm called Domain Driven design makes a distinction between entity objects and value objects.

Object Objects are defined as everything that should be mapped to a key-managed object in a permanent data warehouse, for example, with an employee or client, or with an invoice, etc ... when changing the properties of an object, it is understood that you need to save the changes to the data store is somewhere, and the existence of several instances of the class with the same "key" implies the need to synchronize them or coordinate their persistence with the data store so that changes to one instance do not overwrite the rest. Changing the properties of an entity object implies that you are changing something about the object - without changing the WHICH object you are referring to ...

Otoh value objects are objects that can be considered immutable, whose utility is strictly defined by their property values ​​and for which several instances do not need to be coordinated in any way ... for example, addresses or phone numbers, or wheels on a car, or letters in a document ... these things are completely determined by their properties ... the upper case "A" in a text editor can be transformed transparently with any other uppercase word "A" in the document you do not need a key to distinguish it from everything else. "In this sense, it unmeasured en, because if you change it to "B" (just like changing the line of a phone number in a phone number object, you do not change the data associated with any mutable object, you switch from one value to another ... same as changing string value ...

+6
Nov 04 '08 at 22:27
source share

System.String is a good example of an immutable class with installers and mutated methods, only each mutation method returns a new instance.

+4
Nov 04 '08 at 21:56
source share

Extension at the point of @Cory Foy and @Charles Bretana, where there is a difference between entities and values. While object values ​​should always be immutable, I really don't think that an object should be able to freeze or allow itself to be arbitrarily frozen in a codebase. It has a very bad smell, and I worry that it can be difficult to track exactly where the object was frozen, and why it was frozen, and the fact that between calls to the object it could change state from thawed to frozen,

This does not mean that sometimes you want to transfer something (mutable) to ensure that it will not be changed.

So, instead of freezing the object itself, another possibility is to copy the semantics of ReadOnlyCollection <T>

 List<int> list = new List<int> { 1, 2, 3}; ReadOnlyCollection<int> readOnlyList = list.AsReadOnly(); 

Your object can take on a role as mutable when it needs it, and then be immutable when you want it.

Note that ReadOnlyCollection <T> also implements ICollection <T>, which has an Add( T item) interface in the interface. However, there is also bool IsReadOnly { get; } bool IsReadOnly { get; } defined in the interface so that consumers can check before calling the method that throws the exception.

The difference is that you cannot just set IsReadOnly to false. A collection is either read-only or non-read-only, and never changes for the collection's lifetime.

It would be nice if you had the const-correctness that C ++ gives you at compile time, but this one has its own set of problems, and I'm glad that C # doesn't go there.




ICloneable . I thought I would just go back to the following:

Do not implement ICloneable

Do not use ICloneable in public APIs

Brad Abrams - Design Guide, Managed Code, and the .NET Framework

+4
Nov 04 '08 at 23:48
source share

This is an important issue, and I am very pleased to see that solving it requires more direct support for the framework / language. The solution you have requires a lot of templates. It can be easy to automate some of the patterns using code generation.

You must generate an incomplete class containing all property-dependent properties. It would be quite simple to make a reusable T4 template for this.

The template will accept this for input:

  • Namespace
  • class name
  • list of tuple properties / types

And it will output a C # file containing:

  • Namespace declaration
  • partial class
  • each of the properties, with the corresponding types, support field, getter and setter, which calls the FailIfFrozen method

AOP tags on freezable properties may also work, but this will require more dependencies, while T4 is built into newer versions of Visual Studio.

Another scenario that is very similar to this is the INotifyPropertyChanged interface. The solutions to this problem are likely to be applicable to this problem.

+4
Nov 03 '10 at 21:05
source share

My problem with this pattern is that you do not impose any compilation time limits on immutability. The encoder is responsible for ensuring that the object is set to be immutable before, for example, adding it to the cache or other insecure structure.

This is why I would extend this encoding pattern with a compilation time limit as a general class, for example:

 public class Immutable<T> where T : IElement { private T value; public Immutable(T mutable) { this.value = (T) mutable.Clone(); this.value.MakeReadOnly(); } public T Value { get { return this.value; } } public static implicit operator Immutable<T>(T mutable) { return new Immutable<T>(mutable); } public static implicit operator T(Immutable<T> immutable) { return immutable.value; } } 

Here is an example of how you will use this:

 // All elements of this list are guaranteed to be immutable List<Immutable<SampleElement>> elements = new List<Immutable<SampleElement>>(); for (int i = 1; i < 10; i++) { SampleElement newElement = new SampleElement(); newElement.Id = Guid.NewGuid(); newElement.Name = "Sample" + i.ToString(); // The compiler will automatically convert to Immutable<SampleElement> for you // because of the implicit conversion operator elements.Add(newElement); } foreach (SampleElement element in elements) Console.Out.WriteLine(element.Name); elements[3].Value.Id = Guid.NewGuid(); // This will throw an ImmutableElementException 
+3
Feb 06
source share

I do not like the idea of ​​changing an object from a mutable to an unchanging state, it looks like it amazes me. When do you need it? Only objects that represent VALUES should be immutable

+2
Nov 04 '08 at 21:56
source share

Just tell me how to simplify the properties of an element. Use automatic properties with a private set and avoid explicitly declaring a data field. eg.

 public class SampleElement { public SampleElement(Guid id, string name) { Id = id; Name = name; } public Guid Id { get; private set; } public string Name { get; private set; } } 
+2
Nov 05 '08 at 12:37
source share

Here's a new video on channel 9, where Anders Halesberg from 36:30 in an interview starts talking about immutability in C #. It provides a very good precedent for the immutability of the Eskimos and explains how this is what you currently need to implement. It was music for my ears when he said that it’s worth thinking about better support for creating immutable object graphs in future versions of C #

Expert Advisor: Anders Halesberg - The Future of C #

+2
Mar 08 '09 at 9:37
source share

Two other options for your specific problem that were not discussed:

  • Create your own deserializer that can invoke the private property configuration tool. Although the effort to create a deserializer in the beginning will be much greater, it makes things cleaner. The compiler will not let you even try to call setters, and the code in your classes will be easier to read.

  • Put a constructor in every class that takes an XElement (or some other taste of the XML object model) and populates it. Obviously, as the number of classes increases, this quickly becomes less desirable as a solution.

+2
Jun 23 '09 at 20:23
source share

How about having an abstract ThingBase class, with subclasses MutableThing and ImmutableThing? ThingBase will contain all the data in a secure structure, providing read-only public properties for fields and a secure read-only property for its structure. It would also provide an AsImmutable avoidable method that would return ImmutableThing.

MutableThing will hide properties with read / write properties and provide both a default constructor and a constructor that accepts ThingBase.

An immutable thing is a sealed class that overrides AsImmutable to simply return itself. It will also provide a constructor that accepts ThingBase.

+2
Nov 30 '10 at 20:51
source share

You can use optional named arguments along with nullables to make an immutable setter with a very small pattern. If you really want to set the null property, then you may have problems.

 class Foo{ ... public Foo Set ( double? majorBar=null , double? minorBar=null , int? cats=null , double? dogs=null) { return new Foo ( majorBar ?? MajorBar , minorBar ?? MinorBar , cats ?? Cats , dogs ?? Dogs); } public Foo ( double R , double r , int l , double e ) { .... } } 

Would you use it like that

 var f = new Foo(10,20,30,40); var g = f.Set(cat:99); 
+2
Apr 10 '13 at 2:56
source share



All Articles