Click here to Skip to main content
15,867,308 members
Articles / Programming Languages / C#
Article

CTM - Clone To Modify Model

Rate me:
Please Sign up or sign in to vote.
5.00/5 (9 votes)
14 May 2013CPOL13 min read 28.3K   154   16   18
Create data structures that implement the right Clone To Modify Pattern the easy way.

Background

I recently published the article CTM - Clone To Modify Pattern in which I discuss different kinds of immutability and present a pattern that is really thread safe and avoids excessive copying.

But I always say that a pattern only exists because we aren't capable of automating it. So, this time I am presenting the Clone To Modify Model, which is the component model that implements the pattern for you.

How it works? - Overview

The CTM Model is implemented to emit code at run-time. It will implement the property getters and setters for abstract classes that inherit from the CtmObject class or interfaces that depend on the ICtmObject.

Following the pattern, for each abstract class or interface two classes are generated, one mutable and one immutable. The immutable class will implement getters to read from a field directly and setters to throw ReadOnlyExceptions. The mutable class will be implemented to store the creator thread during construction and then all the methods do a thread-check before getting or setting a field value.

The clone methods are implemented accordingly (that is, the AsImmutable() creates a read-only clone on the mutable type and returns this on the immutable type, the AsMutable() is effectively the opposite and the MutableClone() always make a mutable clone).

The Equals() method is implemented by first comparing if the given instance is the same reference as this. If it is not, then it checks if the given instance is at least of the right type. If it is not, it returns false directly. Then, if you implemented Equals(), your implementation will be called and if the return is false, then the final return is also false. Considering the Equals() was not implemented by you or that it returned true, then it will compare all the fields using the EqualityComparer<T>.Default comparer.

Note: At this moment, the Equals() is implemented for you, but the GetHashCode() is not. I don't really consider this a problem, as I usually only use a single field (like an Id) to calculate the hash code and I think you will do the same if you need it.

I consider that there is nothing really hard to understand conceptually, but writing IL code is not that easy and emitting it at run-time is yet harder (and my code needs some refactoring to be at least less hard to understand).

Using the Code

To use the code, there are somethings that you must know:

  • Your type must be abstract and inherit from CtmObject or it must be an interface that depends on ICtmObject;
  • Considering that you are using an abstract class, only the abstract properties will be auto-implemented following the model. It's up to you to do the right validations on methods or properties that you write by hand;
  • As the type will be abstract, you can't simply call new. You should use the CtmGenerator class to create an instance directly or to return a constructor delegate (this is preferrable if you plan to create many instances);
  • To be consistent, all the types used in the properties must be already immutable. For example, if you put a property of type array (instead of ImmutableArray<>) then the immutable instance can't replace the array, yet the content inside such array can be changed;
  • Properties that are of type ICtmObject (or of any type that implements it) can only be set to already immutable instances. The idea is that you can replace the value of the property with another instance, not that you can change the inner contents. This also simplifies the process of clonning but, unfortunately, such strong rule forbids circular references.

Example:

C#
public abstract class Person:
  CtmObject
{
  public abstract string Name { get; set; }
  public abstract Date Birthdate { get; set; }
  public abstract Sex Sex { get; set; }

  public abstract Person BiologicalMother { get; set; }
  public abstract Person BiologicalFather { get; set; }

  protected override void Validate()
  {
    if (string.IsNullOrEmpty(Name))
      throw new InvalidOperationException("The name is mandatory.");

    if (BiologicalMother != null && BiologicalMother.Sex != Sex.Female)
      throw new InvalidOperationException("The biological mother must be a Female.");

    if (BiologicalFather != null && BiologicalFather.Sex != Sex.Male)
      throw new InvalidOperationException("The biological father must be a Male.");

    // I am ommitting other validations as the idea is already presented.
    // For example, the son can't be older than his parents.
  }
}

And we can create instances of the Person class using something like:

Func<Person> createPerson = CtmGenerator.GetConstructor<Person>();
Person mutableFather = createPerson();
mutableFather.Name = "Father";
mutableFather.Birthdate = new Date(2000, 1, 1);
mutableFather.Sex = Sex.Male;
Person father = mutableFather.AsImmutable(false);

Person mutableMother = createPerson();
mutableMother.Name = "Mother";
mutableMother.Birthdate = new Date(2001, 2, 2);
mutableMother.Sex = Sex.Female;
Person mother = mutableMother.AsImmutable(false);

Person mutableSon = createPerson();
mutableSon.Name = "Son";
mutableSon.Birthdate = new Date(2013, 3, 3);
mutableSon.Sex = Sex.Male;
mutableSon.BiologicalMother = mother; // trying to set the mutable mother or
mutableSon.BiologicalFather = father; // father in those lines throws an exception.
Person son = mutableSon.AsImmutable(false);
// Now we can return the son instance and, with it, any thread will be able to access
// its properties and get his parents.

As you can see, with such code we create a class Person, in which all the properties will be implemented at run-time to follow the Clone To Modify Pattern.

If you look at the code of the ICtmObject, you will see that it has methods like UntypedAsImmutable(), UntypedAsMutable(), UntypedMutableClone() and UntypedGetOldInstance(), and then the methods AsImmutable(), AsMutable(), MutableClone and GetOldInstance() are extension methods.

That's something I don't really like, as I will prefer normal methods rather than extension methods, but it is impossible to create a method on a base class that will return values of the actual child type of the object. But the extension methods can be generic and return objects of the same type as the entering parameter, so a Person.AsImmutable() will return an object of type Person and not an object of type ICtmObject.

Unfortunately, to use such method you should add the using Pfz.Extensions; clause to your unit.

Understanding the methods and classes you will need to use

To use the CTM Model you should use at least these types and methods:

  • ICtmObject or CtmObject;
  • CtmGenerator.Create() or CtmGenerator.GetConstructor();
  • ICtmObject.AsImmutable() [the extension method] or ICtmObject.UntypedAsImmutable().

And you will probably also use the:

  • ICtmObject.AsMutable() when you decide to apply modifications to an object that may be immutable (if the object is already mutable, then the actual object is returned without making a copy);
  • ICtmObject.MutableClone() if you want to create a mutable clone, even if the actual object is already mutable;
  • ICtmObject.GetOldInstance() if you decide to compare the modifications of a mutable instance that was generated from an immutable one.

Of all those methods, I think that only one really needs an explanation. The AsImmutable() method receives a boolean parameter named keepOldInstance.

Everytime you use a immutable instance to generate a mutable instance, such mutable instance keeps the "old instance" that generated it. If you use such mutable instance that already holds an old instance to create a new immutable instance, you have the option to give the "old instance" to the new immutable instance (the old instance will be the old immutable one, a mutable instance is never stored as the old one).

This is useful if you want to keep the history of all changes. But when the mutable object was just created, there is no old instance to keep, so the parameter will be ignored in such situation. Yet I didn't made it have a default value as that usually leads to errors because some people simply forget that such parameter exists.

INotifyPropertyChanged

The ICtmObject interface depends on the INotifyPropertyChanged and the CtmGenerator will implement that interface for you.

Considering that only the mutable type will ever call the PropertyChanged event, such event on the immutable instances do nothing. Asking to add a handler to a read-only instance does not throws exceptions but it also does not registers the given delegate, so a reference to such delegate is not kept alive.

I though about throwing exceptions if users tried to call register to the PropertyChanged event on the read-only instances, but as many frameworks simply register to that event if the class implements the interface, I decided it was better to do nothing in the immutable case. For the mutable version the event will be called for you and the property set methods will be implemented using the following pattern:

  • If the parameter is directly or indirectly implementing ICtmObject, it is checked to guarantee it is immutable (or null);
  • The event variable is read and verified. If it is null, then a direct set is done and the code returns;
  • If the event is set, then the code checks if the value was really modified. If you are setting the same value that is already held, nothing is done (this is very useful if the change of property A changes property B, and the change of property B changes property A);
  • Considering the value was changed, the field that holds the value is set;
  • The PropertyChanged event will be invoked for that property and for any calculated property based on it. Also, all the PropertyChangedEventArgs are created on static readonly fields only once, so when invoking the event there aren't new event args instances being created.

So, to explain it with code, let's see how the Father property is implemented in the mutable instance.

C#
private string _father;
private static readonly PropertyChangedEventArgs _fatherChangedArgs = new PropertyChangedEventArgs("Father");
public IPerson Father
{
  get
  {
    _CheckThread();
    return _father;
  }
  set
  {
    _CheckThread();
    CheckCtmReferencesMustBeReadOnly(value);
    
    var propertyChanged = PropertyChanged;
    if (propertyChanged == null)
    {
      _father = value;
      return;
    }

    // Note, the EqualityComparer<T>.Default.Equals()
    // is used for value types.
    if (value == _father)
      return;

    _father = value;
    propertyChanged(_fatherChangedArgs);
  }
}

Calculated Properties

I just talked about calculated properties. Well, the CtmGenerator does not analyses IL code, so it is not capable of discovering that a property you implemented is a calculated property (and which properties it uses to do its calculation).

For example, if you generate a property Total, that is calculated by multiplying the NumberOfItems by ItemPrice, the CtmGenerator will not discover that by magic. But you can still tell the CtmGenerator that when modifying the NumberOfItems or the ItemPrice the PropertyChanged should be called to inform that the Total has changed.

To do that, you should use the [CtmCalculatedProperty] attribute over the calculated property, which receives as parameter the names of all properties used by such property.

The attribute is used on the calculated property, not on the properties that generate the event, on purpose. This way, when modifying the calculated property you don't need to go to the code of the other properties. The change is becomes more localized this way. Also, if you declare the Total property in a sub-class, while the original properties are on the base class, the base class simply doesn't need to know that such calculated property exists at all.

And the code is smart enough to know that if you make a calculated property based on another calculated property, that it should notify the change of both properties when a property used by the first calculated property is changed.

If that's not clear enough, imagine that:

You have the NumberOfItems and the ItemPrice as the real properties.

Then you have the Total calculated on the NumberOfItems and ItemPrice.

Then you have a RoundedTotal, which is only based on Total.

When you change either the NumberOfItems or the ItemPrice, the Total will change and, consequently, the RoundedTotal will change. But the only methods that invoke all the PropertyChanges are the property setters of both NumberOfItems and ItemPrice, as the calculated properties don't need to have a setter at all.

[CtmNonUpdatable]

By applying the [CtmNonUpdatable] attribute on a property you effectively say that such property can't be changed on a mutable instance that was created as a clone of an immutable instance.

The idea is that such property can only be set when "creating" a record, and not when "updating" a record (and you may imagine that this is especially useful on ORMs that use the CTM Model).

If you apply the [CtmNonUpdatable] to a type, you are effectively telling that all properties are non-updatable and so you can't generate mutable clones of immutable instances. Considering those objects are used to represent database records, that means that records can be inserted but never changed. This is specially true for log records.

[CtmBaseClass] Attribute

With the CtmGenerator you can already implement any interface that depends on the ICtmObject. But, by default, the CtmGenerator will implement such interfaces inheriting directly from the CtmObject class.

But imagine the situation:

You originally have only the IPerson interface.

Then you decide to create the Person class (which inherits from CtmObject and implements IPerson, still letting the properties as abstract) only to be able to validate it.

You can now create Person instances and return them as IPerson.

Then you want to create the IUser interface, representing a person that can use the system. But if you use the ICtmGenerator to implement the IUser, it will not have the validations done at the Person class. So, normally the solution would be to also create an User class, inheriting from the Person class and also implementing the IUser interface. Now you can create an User and return it as an IUser.

Well, doing that we are requiring too many steps and too many code, effectively loosing the advantage of using the CTM Model. In fact we can't request the CtmGenerator to create an IPerson anymore or else it will create an IPerson instance that does not do any validation.

So, to solve such situation, you can use the [CtmBaseClass] attribute to tell which class should be used as the base class when generating the code to implement the interface. That is, you can mark the IPerson as using the Person class as its base class. So, you can ask the CtmGenerator to create an IPerson, and you will still have the validation done at the Person class. Better yet, you can create the IUser interface depending only on IPerson and, when you ask the CtmGenerator to create an instance of the IUser type, you will have an user instance with the validation done at the Person class, without requiring to create an User class and without requiring some kind of factory to be able to ask for an IUser to receive an implemented object that is based on a User class.

This way you are also able to create base types with many different methods and still tell developers to only create interfaces. In fact, I use this model with another "personal pattern". I declare all the interfaces without the initial I on their names, as if they were classes and, as most of the time I am only adding new properties to the types, I keep creating only new interfaces.

For example:

C#
public interface Person
{
  string Name { get; set; }
  Date Birthdate { get; set; }
  Sex Sex { get; set; }

  Person BiologicalMother { get; set; }
  Person BiologicalFather { get; set; }
}

I consider this better than creating abstract classes by default, because when you create an abstract class you can easily write:

C#
public string Name { get; set; }

Instead of:

C#
public abstract string Name { get; set; }

And such property will become always mutable from any thread by simply forgetting a single keyword.

Then, if you really need to do a validation during clone you will implement the class, naming it differently (like ImplementedPerson) and you mark the interface as using such class as the base class. Even if this adds work to write the Person type, any sub-type of Person can continue to be written as an interface, using the reduced syntax of interfaces (and greatly helping you if you decide to add new aspects to your objects or to create mocks).

The Sample

The sample is more like a "Unit Test" to prove the pattern is working correctly. The Ctm classes are part of my personal library and they use a lot of my other classes. I tried to remove big parts that aren't interesting for the article (like the Remoting namespace) but I am still giving the library with tons of classes that you may not want to use, so I am sorry if you only want to use the CTM classes isolated from everything else, but in the Threading namespace, for example, I have the locks used by my dictionaries which are used by the CtmGenerator and have a better performance than the .NET equivalents.

Future

In the future I plan to present my ORM framework that is based on the ADO+.NET and on the CTM Model. Such ORM library already exists but I must document it, do more tests and write an article to present it accordingly. In fact, the CTM Model was created to support the pattern I wanted to use in the ORM.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Microsoft
United States United States
I started to program computers when I was 11 years old, as a hobbyist, programming in AMOS Basic and Blitz Basic for Amiga.
At 12 I had my first try with assembler, but it was too difficult at the time. Then, in the same year, I learned C and, after learning C, I was finally able to learn assembler (for Motorola 680x0).
Not sure, but probably between 12 and 13, I started to learn C++. I always programmed "in an object oriented way", but using function pointers instead of virtual methods.

At 15 I started to learn Pascal at school and to use Delphi. At 16 I started my first internship (using Delphi). At 18 I started to work professionally using C++ and since then I've developed my programming skills as a professional developer in C++ and C#, generally creating libraries that help other developers do their work easier, faster and with less errors.

Want more info or simply want to contact me?
Take a look at: http://paulozemek.azurewebsites.net/
Or e-mail me at: paulozemek@outlook.com

Codeproject MVP 2012, 2015 & 2016
Microsoft MVP 2013-2014 (in October 2014 I started working at Microsoft, so I can't be a Microsoft MVP anymore).

Comments and Discussions

 
QuestionOne issue Pin
FatCatProgrammer24-Jun-13 5:38
FatCatProgrammer24-Jun-13 5:38 
AnswerRe: One issue Pin
Paulo Zemek24-Jun-13 11:35
mvaPaulo Zemek24-Jun-13 11:35 
GeneralRe: One issue Pin
FatCatProgrammer24-Jun-13 16:37
FatCatProgrammer24-Jun-13 16:37 
GeneralRe: One issue Pin
Paulo Zemek25-Jun-13 1:59
mvaPaulo Zemek25-Jun-13 1:59 
I can say that if you put the mutable version into the dictionary you already did the wrong thing.

You can say: "-Hey, but I wanted to put the writeable one in the dictionary."
Ok, so, why did you call the IsImmutable?
"- Because now it does not need to be immutable anymore."
So, why don't you replace the item in the dictionary?

In fact, if you start with a mutable instance that is public and then you make it immutable at a random moment you are creating a bug (or a possible bug) as someone that expects it to be always mutable will not see an immutable object.

And you are seeing too many problems in my opinion: Do you have a problem that a string and that a StringBuilder don't share the same hashcode? Surely you have two different classes, but the principle is the same: One class is completely mutable and the other is completely immutable... if you do a ToString() over a StringBuilder you will receive the immutable string. You can continue to do ToString in the string itself and it will simply continue to return itself. This is very similar to calling mutableInstance.AsImmutable().AsImmutable();

But apparently you only want to discuss my solution for the sake of discussing something. So, to conclude: Yes, there will be two instances, with the same content and a different Id, and it is by design. You should never make the mutable instance public. If you do, it is your problem.
GeneralRe: One issue Pin
FatCatProgrammer25-Jun-13 6:09
FatCatProgrammer25-Jun-13 6:09 
GeneralRe: One issue Pin
Paulo Zemek25-Jun-13 6:24
mvaPaulo Zemek25-Jun-13 6:24 
GeneralMy vote of 5 Pin
Ivaylo Slavov15-May-13 23:54
Ivaylo Slavov15-May-13 23:54 
GeneralRe: My vote of 5 Pin
Paulo Zemek16-May-13 1:48
mvaPaulo Zemek16-May-13 1:48 
General5 stars Pin
bitterskittles15-May-13 3:10
bitterskittles15-May-13 3:10 
GeneralRe: 5 stars Pin
Paulo Zemek15-May-13 3:52
mvaPaulo Zemek15-May-13 3:52 
GeneralRe: 5 stars Pin
bitterskittles16-May-13 2:12
bitterskittles16-May-13 2:12 
GeneralRe: 5 stars Pin
Paulo Zemek16-May-13 3:43
mvaPaulo Zemek16-May-13 3:43 
GeneralRe: 5 stars Pin
bitterskittles16-May-13 4:25
bitterskittles16-May-13 4:25 
GeneralRe: 5 stars Pin
Paulo Zemek16-May-13 5:11
mvaPaulo Zemek16-May-13 5:11 
GeneralRe: 5 stars Pin
bitterskittles16-May-13 6:19
bitterskittles16-May-13 6:19 
GeneralRe: 5 stars Pin
bitterskittles16-May-13 23:20
bitterskittles16-May-13 23:20 
GeneralMy vote of 5 Pin
Rodiney Branta14-May-13 13:41
Rodiney Branta14-May-13 13:41 
GeneralRe: My vote of 5 Pin
Paulo Zemek14-May-13 13:42
mvaPaulo Zemek14-May-13 13:42 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.