Let's start off with a few words on extension methods. They are best explained through an example. Let's say we want to be able to calculate area given size. Wouldn't it be nice to be able to add GetArea to the already existing Size class? Well, let's do so!
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public static class ExtensionMethods | |
{ | |
public static int GetArea(this Size size) | |
{ | |
return size.Width * size.Height; | |
} | |
} |
As mentioned, I had the idea of extending the very base of the C# class hierarchy (System.Object) with a method for copying or cloning "any" object. Obviously, the method cannot automatically copy _any_ object, since it cannot possibly know how to construct an object from an arbitrary class. Hence, a small framework needed to be created. The goals were to:
- Enable copying of many objects automatically.
- Enable copying of virtually any object with very little effort.
- Automate and hide away as much as possible (The KISS Principle).
The Copyable framework
Copyable is a small framework for copying (or cloning, if you will) objects. The straightforward way of using it is to just reference the assembly it's in from your project, and start copying!
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
SomeType instance = new SomeType(); | |
// ...do lots of stuff to the object... | |
SomeType copy = instance.Copy(); // Create a deep copy |
For the automated copy to work, though, one of the following statements must hold for instance:
- Its type must have a parameterless constructor, or
- It must be a Copyable, or
- It must have an IInstanceProvider registered for its type.
Besides the Copy method, The Copyable class and IInstanceProvider interface are the two major building blocks of the Copyable framework. Each of these blocks enable copying of objects that cannot automatically be copied.
The Copyable base class
Copyable is an abstract base class for objects that can be copied. To create a copyable class, you simply subclass Copyable and call its constructor with the arguments of your constructor.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class MyClass : Copyable | |
{ | |
public MyClass(int a, double b, string c) | |
: base(a, b, c) | |
{ | |
} | |
} |
MyClass can now be copied just like the previous example, e.g. MyClass b = new MyClass(1, 2.0, "3").Copy().
The introduction of the Copyable base class solves many problems, but not all. Let's say you wanted to copy a System.Drawing.SolidBrush. This class does not have a parameterless constructor, which means it cannot be copied "automatically" by the framework. Also, you cannot alter it so that it subclasses Copyable. So, what do you do? You create an instance provider.
The IInstanceProvider interface
An instance provider is defined by the interface IInstanceProvider. As the name clearly states, the implementation is a provider of instances. One instance provider can provide instances of one given type. The Copyable framework automatically detects IInstanceProvider implementations in all assembies in its application domain, so all you need to do to create a working instance provider is to define it. No registration or other additional operations are required. To simplify the implementation of instance providers and the IInstanceProvider interface, an abstract class InstanceProvider is included in the framework.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class SolidBrushProvider : InstanceProvider | |
{ | |
public override SolidBrush CreateTypedCopy(SolidBrush s) | |
{ | |
return new SolidBrush(s.Color); | |
} | |
} |
The instance provider pattern does not solve the case where you want different initial states for your SolidBrush instances depending on which context you use them for copying. For those cases, an overload of Copy() exists which takes an already created instance as an argument. This argument will become the copy.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
SolidBrush instance = new SolidBrush(Color.Red); | |
instance.Color = Color.Black; | |
SolidBrush copy = new SolidBrush(Color.Red); | |
instance.Copy(copy); // Create a deep copy | |
// copy now has the color Color.Black. |
Limitations and pitfalls
Although this solution works in most cases, it's not a silver bullet. Be aware when you copy classes that hold unmanaged resources such as handles. If these classes are designed on the premise that their resources are exclusive to them, they will manage them as they see fit. Imagine if you copied a class which holds a handle, disposed one of the instances, and continued using the copy. The handle will (probably) be freed by the original instance, and the copy will generate an access violation by attempting reading or writing freed memory.That's it! The Copyable framework can be downloaded from Github. For those interested in reading more on extension methods, MSDN provides an excellent explanation in the C# Programming Guide, and Scott Guthrie has an introduction article here.
Enjoy Copyable, and please let me know if you find it useful or come across any problems with it.
UPDATE 2009-12-11: Due to popular demand, I have made the source code for Copyable available under the MIT license.
UPDATE 2010-01-31: The requirement of parameterless constructors has been removed in the latest version of Copyable available on Github. A new release will follow soon.