Extension methods for copying or cloning objects

C# 3.0 includes a new feature known as extension methods, and fiddling with it triggered the idea of creating a mechanism for copying or cloning (virtually) any .NET object or graph of objects. The manifestation of that idea has become a rather decent little framework for copying objects. It performs a deep copy as automatically as it possibly can, and provides mechanisms to easily solve many of the cases which cannot be covered automatically. It is great for copying your custom object hierarchies, and saves you the pain of a solution like implementing ICloneable for an entire hierarchy of objects. Click here to grab it now, and read on for a presentation.

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!

public static class ExtensionMethods
{
  public static int GetArea(this Size size)
  {
    return size.Width * size.Height;
  }
}
As you can see, the new syntax simply allows you to tell the compiler that the this of this method is a Size. This means that the method is an extension of the Size class.

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 result is Copyable (pun intended).

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!

SomeType instance = new SomeType();
// ...do lots of stuff to the object...
SomeType copy = instance.Copy(); // Create a deep copy

The instance copy is now a deep copy of instance, no matter how complex the object graph for instance is. The relations in the copy graph is the same as in instance, but all objects in the copy object graph are copies of those in instance.

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.

class MyClass : Copyable
{
  public MyClass(int a, double b, string c)
    : base(a, b, c)
  {
  }
}

This code above makes MyClass a copyable class. Note that if MyClass had had a parameterless constructor, subclassing Copyable would not be necessary.

MyClass can now be copied just like the previous example.

MyClass a = new MyClass(1, 2.0, "3");
MyClass b = a.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.

public class SolidBrushProvider</dt>
<dd>InstanceProvider<SolidBrush>
{
public override SolidBrush CreateTypedCopy(SolidBrush s)
{
return new SolidBrush(s.Color);
}
}

This implementation will be used automatically by the Copyable framework. NOTE: To be usable, the instance provider MUST have a parameterless constructor.

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.

SolidBrush instance = new SolidBrush(Color.Red);
instance.Color = Color.Black;
SolidBrush copy = new SolidBrush(Color.Red);
instance.Copy(copy); // Create a deep copy

In this example, copy is now of 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 here. For those interested in reading more on extension methods, For additional information, 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. The source can be downloaded here.

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.

  • André Müller

    I get the same exception for each unit test which is in the original package. I think the copy framework has problems on .NET 4.0 (the original files where all on .NET 3.5). In addition to the information above the TypeInitializationException.InnerException.LoaderExceptions property shows the following strange dependency: {“Could not load file or assembly ‘Microsoft.Web.Administration, Version=7.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35′ or one of its dependencies. Das System kann die angegebene Datei nicht finden.”:”Microsoft.Web.Administration, Version=7.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35″} I hope this helps (I could not manage to resolve this dependency).

  • André Müller

    Ok. I think I solved the problem. The class initializer of ObjectExtensions throws when it hits the Microsoft.VisualStudio.Enterprise.ASPNetHelper assembly and calls GetTypes(). So another try catch around that and everything is fine.

    private static IEnumerable GetInstanceProviders(Assembly assembly) { System.Type[] types = null; try { types = assembly.GetTypes(); } catch { }

            if (types != null)
            { 
                foreach (Type t in types)
                {
                    if (typeof(IInstanceProvider).IsAssignableFrom(t))
                    {
                        IInstanceProvider instance = null;
                        try
                        {
                            instance = (IInstanceProvider)Activator.CreateInstance(t);
                        }
                        catch { } // Ignore provider if it cannot be created
                        if (instance != null)
                            yield return instance;
                    }
                }
            }
        }
    

  • http://ox.no Håvard

    André,

    thanks a lot for noticing the issue, and sharing a solution. It would be great if you could fork Copyable on Github, implement your changes, and send me a pull request so we can get it into the master branch (preferably also with a unit test that reproduces the specific problem you had).

  • http://www.bluesky-information.com Marc

    Thank You Havard, it was a great introduction to deep copy. For others – please remove the link to your old project in this post:http://ox.no/software/copyable. It is an old version, for me only a shallow copy was possible and it failed with arrays. The version of Github works. I know this is a silly mistake, but stuff like this happens :-). Again, very valueable framework. Cheers, Marc

  • Daniele Fusi

    Great code, thanks! I’d like to use it in Silverlight (4), where it would be especially useful as deep-cloning objects using serialization is excluded by the lack of SerializableAttribute. Anyway, I tried to compile it under SL 4 and I get an error about AssemblyLoadEventArgs, which seems not public under this platform. Do you plan a SL compatible version? Or can you suggest any workaround for this?

  • Daniele Fusi

    Sorry guys, I had not seen another post like mine in comments, they were shown all only after I posted. So it seems there is no support for SL. Thanks anyway.

  • Mike FitzPatrick

    I had an issue when the class to be copied had either DataSets or DataTables as member properties. I was able to modify the ObjectExtensions.cs file and add support for these to object types.

        private static object Clone(this object instance, VisitedGraph visited, object copy)
        {
            if (visited.ContainsKey(instance))
                return visited[instance];
            else
                visited.Add(instance, copy);

        Type type = instance.GetType();
    
        if (instance.GetType().Name == "DataTable")
        {
            DataTable dt = (DataTable)instance;
            copy = dt.Copy();
        }
        else if (instance.GetType().Name == "DataSet")
        {
            DataSet ds = (DataSet)instance;
            copy = ds.Copy();
        }
        else
        {
            while (type != null)
            {
                foreach (FieldInfo field in type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
                {
                    object value = field.GetValue(instance);
                    if (visited.ContainsKey(value))
                        field.SetValue(copy, visited[value]);
                    else
                        field.SetValue(copy, value.Clone(visited));
                }
    
                type = type.BaseType;
            }
        }
    
        return copy;
    }
    

  • Gravitas

    Is it possible to write custom type converters, as described by AutoMapper here: http://automapper.codeplex.com/wikipage?title=Custom%20Type%20Converters&referringTitle=Home

  • http://ox.no Håvard

    Gravitas: No, type converters are not supported. The concept of type conversion is different from the concept of copying, so there really is no obvious way of supporting conversion or “mapping”. AutoMapper and similar are a better match for conversion than Copyable is.

    Support for custom “copyers” could be desirable. I haven’t seen any real use for it in my applications, but I would welcome any suggestions. I would expect a suggestion to include an interface that can be implemented to support custom copying, as well as modification of the Copyable base class to support custom copy behavior.

    Just fork on GitHub and get on with it! :)

  • MOhan

    Hi,

    I am trying to use this framework for an entity class which is passed to a client via wcf service, it’s giving following error

    “Type ‘OX.Copyable.Copyable’ cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. If the type is a collection, consider marking it with the CollectionDataContractAttribute. See the Microsoft .NET Framework documentation for other supported types. “

    Please help me out in solving this problem.

    Thanks, Mohan

  • Pingback: .NET 对象深层克隆开源框架 | 哈哈808,开心呵呵网

  • Pingback: Cloning objects in C# « .Net « {%Pragmatic Coding%}

  • Pingback: Object copy | Michael's Excerpts

  • Pingback: How to: Deep cloning objects | SevenNet

  • Pingback: How to: Deep cloning objects | Technical information for you

  • Pingback: Fixed Deep cloning objects #dev #it #asnwer | Good Answer