(This blog post is copied from my old blog at www.lordzoltan.org, it was originally written 1st August 2008)
Somebody asked me the other day if there was a generic way of providing a property-based clone operation for classes. The operation didn't have to recurse into complex objects (just copy the references), but the value types had to be copied.
The first part of the solution was a little bit of reflection:
All great, so now we have a static method we can call - but, however nice it is, it's not that gawk-able.
I wanted to have this method appear in the member list of objects that we wanted to be able to clone - which meant either a base or an interface. Interfaces are less intrusive, because you can still apply them to classes with bases, of course, and you can have multiple interfaces - but having the method as an interface member didn't make any sense. However, if you've read my previous post: http://lordzoltan.blogspot.com/2010/09/pseudo-template-meta-programming-in-c_10.html, which uses some generic extensions alongside static generics to provide meta info, the solution becomes easy. However you have to do something that a lot of people would seriously moan about... an empty interface.
Notice that the reflection is performed on typeof(T) instead of source.GetType() - which should be faster at runtime.
There is a gotcha here, though, and it's to do with the T that is passed when this method called for base classes of a superclass, consider the following:
Now some method declared in another static class, no attempt made to contrive a complicated scenario, instead we just have a rather pointless method, but it demonstrates the issue at hand:
Now let's test the method for an instance of Cloneable1 and for Cloneable2:
The second test will fail, because the GetClone method implicitly passes Cloneable1 as the <T> parameter for the Clone<T> extension method, since T is taken from the left-hand side of the '.' operator which selects the method.
One way to get around this problem is to write another extension method for IGenericCloneable in a new IGenericCloneableExtensions2 class (simplifies the reflection), this time which takes IGenericCloneable and is not generic, we can give it the same name because the compiler will intelligently select the method that matches the MOST derived version of the input parameters when it is called:
This is a useful practise anyway, because it means you can make the IGenericCloneable(2) extensions available to code that only has instances of the interface (which, after all, is what most people will expect of an interface). However, we can bypass the need to regress the Utility.GetClone method to using IGenericCloneable instance, by tightening up our original generic version with a little bit of reflection and dynamic generic compilation magic:
Now, we can return our original GetClone method to take instances of Cloneable1, and the two tests should pass.
Don't be worried about the MakeGenericMethod call - the type engine will only actually compile the method once - after that, it will simply go and obtain a handle to the compiled method. [Note – with the advent of expression trees I now do things like this by dynamically compiling methods and then caching them – the result is far faster and much tidier :)]
There are other things we could do here, for example make another version of Clone which takes two type parameters:
This method automatically allows us to clone an instance of T1 to T2 because T1 is inherited from T2, and it'll still appear as an extension method to the instance of T1 because T2 is constrained to implement the IGenericCloneablee interface.
We could then modify the reflection code at the head of the modified generic Clone<T> method so that it picks up this generic method when the instance type of the destination object is a base class of the instance type of the source.
We could write a CloneNew<T> method, by specifying an additional constraint on <T>:
And if you started stacking these empty interfaces on top of others, you can start building whole libraries of useful generic extension methods that provide a whole host of common functionality to otherwise method-less types - and still be able to inherit those types from concrete classes (because the interface list is unbounded).