How do you do a deep copy of an object in .NET?

C#.NetSerializationClone

C# Problem Overview


I want a true deep copy. In Java, this was easy, but how do you do it in C#?

C# Solutions


Solution 1 - C#

Important Note

BinaryFormatter has been deprecated, and will no longer be available in .NET after November 2023. See BinaryFormatter Obsoletion Strategy


I've seen a few different approaches to this, but I use a generic utility method as such:

public static T DeepClone<T>(this T obj)
{
 using (var ms = new MemoryStream())
 {
   var formatter = new BinaryFormatter();
   formatter.Serialize(ms, obj);
   ms.Position = 0;

   return (T) formatter.Deserialize(ms);
 }
}

Notes:

  • Your class MUST be marked as [Serializable] for this to work.

  • Your source file must include the following code:

     using System.Runtime.Serialization.Formatters.Binary;
     using System.IO;
    

Solution 2 - C#

I wrote a deep object copy extension method, based on recursive "MemberwiseClone". It is fast (three times faster than BinaryFormatter), and it works with any object. You don't need a default constructor or serializable attributes.

Source code:

using System.Collections.Generic;
using System.Reflection;
using System.ArrayExtensions;

namespace System
{
    public static class ObjectExtensions
    {
        private static readonly MethodInfo CloneMethod = typeof(Object).GetMethod("MemberwiseClone", BindingFlags.NonPublic | BindingFlags.Instance);

        public static bool IsPrimitive(this Type type)
        {
            if (type == typeof(String)) return true;
            return (type.IsValueType & type.IsPrimitive);
        }

        public static Object Copy(this Object originalObject)
        {
            return InternalCopy(originalObject, new Dictionary<Object, Object>(new ReferenceEqualityComparer()));
        }
        private static Object InternalCopy(Object originalObject, IDictionary<Object, Object> visited)
        {
            if (originalObject == null) return null;
            var typeToReflect = originalObject.GetType();
            if (IsPrimitive(typeToReflect)) return originalObject;
            if (visited.ContainsKey(originalObject)) return visited[originalObject];
            if (typeof(Delegate).IsAssignableFrom(typeToReflect)) return null;
            var cloneObject = CloneMethod.Invoke(originalObject, null);
            if (typeToReflect.IsArray)
            {
                var arrayType = typeToReflect.GetElementType();
                if (IsPrimitive(arrayType) == false)
                {
                    Array clonedArray = (Array)cloneObject;
                    clonedArray.ForEach((array, indices) => array.SetValue(InternalCopy(clonedArray.GetValue(indices), visited), indices));
                }

            }
            visited.Add(originalObject, cloneObject);
            CopyFields(originalObject, visited, cloneObject, typeToReflect);
            RecursiveCopyBaseTypePrivateFields(originalObject, visited, cloneObject, typeToReflect);
            return cloneObject;
        }

        private static void RecursiveCopyBaseTypePrivateFields(object originalObject, IDictionary<object, object> visited, object cloneObject, Type typeToReflect)
        {
            if (typeToReflect.BaseType != null)
            {
                RecursiveCopyBaseTypePrivateFields(originalObject, visited, cloneObject, typeToReflect.BaseType);
                CopyFields(originalObject, visited, cloneObject, typeToReflect.BaseType, BindingFlags.Instance | BindingFlags.NonPublic, info => info.IsPrivate);
            }
        }

        private static void CopyFields(object originalObject, IDictionary<object, object> visited, object cloneObject, Type typeToReflect, BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.FlattenHierarchy, Func<FieldInfo, bool> filter = null)
        {
            foreach (FieldInfo fieldInfo in typeToReflect.GetFields(bindingFlags))
            {
                if (filter != null && filter(fieldInfo) == false) continue;
                if (IsPrimitive(fieldInfo.FieldType)) continue;
                var originalFieldValue = fieldInfo.GetValue(originalObject);
                var clonedFieldValue = InternalCopy(originalFieldValue, visited);
                fieldInfo.SetValue(cloneObject, clonedFieldValue);
            }
        }
        public static T Copy<T>(this T original)
        {
            return (T)Copy((Object)original);
        }
    }

    public class ReferenceEqualityComparer : EqualityComparer<Object>
    {
        public override bool Equals(object x, object y)
        {
            return ReferenceEquals(x, y);
        }
        public override int GetHashCode(object obj)
        {
            if (obj == null) return 0;
            return obj.GetHashCode();
        }
    }

    namespace ArrayExtensions
    {
        public static class ArrayExtensions
        {
            public static void ForEach(this Array array, Action<Array, int[]> action)
            {
                if (array.LongLength == 0) return;
                ArrayTraverse walker = new ArrayTraverse(array);
                do action(array, walker.Position);
                while (walker.Step());
            }
        }

        internal class ArrayTraverse
        {
            public int[] Position;
            private int[] maxLengths;

            public ArrayTraverse(Array array)
            {
                maxLengths = new int[array.Rank];
                for (int i = 0; i < array.Rank; ++i)
                {
                    maxLengths[i] = array.GetLength(i) - 1;
                }
                Position = new int[array.Rank];
            }

            public bool Step()
            {
                for (int i = 0; i < Position.Length; ++i)
                {
                    if (Position[i] < maxLengths[i])
                    {
                        Position[i]++;
                        for (int j = 0; j < i; j++)
                        {
                            Position[j] = 0;
                        }
                        return true;
                    }
                }
                return false;
            }
        }
    }

}

Solution 3 - C#

Building on Kilhoffer's solution...

With C# 3.0 you can create an extension method as follows:

public static class ExtensionMethods
{
    // Deep clone
    public static T DeepClone<T>(this T a)
    {
        using (MemoryStream stream = new MemoryStream())
        {
            BinaryFormatter formatter = new BinaryFormatter();
            formatter.Serialize(stream, a);
            stream.Position = 0;
            return (T) formatter.Deserialize(stream);
        }
    }
}

which extends any class that's been marked as [Serializable] with a DeepClone method

MyClass copy = obj.DeepClone();

Solution 4 - C#

You can use Nested MemberwiseClone to do a deep copy. Its almost the same speed as copying a value struct, and its an order of magnitude faster than (a) reflection or (b) serialization (as described in other answers on this page).

Note that if you use Nested MemberwiseClone for a deep copy, you have to manually implement a ShallowCopy for each nested level in the class, and a DeepCopy which calls all said ShallowCopy methods to create a complete clone. This is simple: only a few lines in total, see the demo code below.

Here is the output of the code showing the relative performance difference (4.77 seconds for deep nested MemberwiseCopy vs. 39.93 seconds for Serialization). Using nested MemberwiseCopy is almost as fast as copying a struct, and copying a struct is pretty darn close to the theoretical maximum speed .NET is capable of, which is probably quite close to the speed of the same thing in C or C++ (but would have to run some equivalent benchmarks to check this claim).

	Demo of shallow and deep copy, using classes and MemberwiseClone:
	  Create Bob
	    Bob.Age=30, Bob.Purchase.Description=Lamborghini
	  Clone Bob >> BobsSon
	  Adjust BobsSon details
	    BobsSon.Age=2, BobsSon.Purchase.Description=Toy car
	  Proof of deep copy: If BobsSon is a true clone, then adjusting BobsSon details will not affect Bob:
	    Bob.Age=30, Bob.Purchase.Description=Lamborghini
	  Elapsed time: 00:00:04.7795670,30000000
	Demo of shallow and deep copy, using structs and value copying:
	  Create Bob
	    Bob.Age=30, Bob.Purchase.Description=Lamborghini
	  Clone Bob >> BobsSon
	  Adjust BobsSon details:
	    BobsSon.Age=2, BobsSon.Purchase.Description=Toy car
	  Proof of deep copy: If BobsSon is a true clone, then adjusting BobsSon details will not affect Bob:
	    Bob.Age=30, Bob.Purchase.Description=Lamborghini
	  Elapsed time: 00:00:01.0875454,30000000
	Demo of deep copy, using class and serialize/deserialize:
	  Elapsed time: 00:00:39.9339425,30000000

To understand how to do a deep copy using MemberwiseCopy, here is the demo project:

// Nested MemberwiseClone example. 
// Added to demo how to deep copy a reference class.
[Serializable] // Not required if using MemberwiseClone, only used for speed comparison using serialization.
public class Person
{
	public Person(int age, string description)
	{
		this.Age = age;
		this.Purchase.Description = description;
	}
	[Serializable] // Not required if using MemberwiseClone
	public class PurchaseType
	{
		public string Description;
		public PurchaseType ShallowCopy()
		{
			return (PurchaseType)this.MemberwiseClone();
		}
	}
	public PurchaseType Purchase = new PurchaseType();
	public int Age;
	// Add this if using nested MemberwiseClone.
	// This is a class, which is a reference type, so cloning is more difficult.
	public Person ShallowCopy()
	{
		return (Person)this.MemberwiseClone();
	}
	// Add this if using nested MemberwiseClone.
	// This is a class, which is a reference type, so cloning is more difficult.
	public Person DeepCopy()
	{
	        // Clone the root ...
		Person other = (Person) this.MemberwiseClone();
	        // ... then clone the nested class.
		other.Purchase = this.Purchase.ShallowCopy();
		return other;
	}
}
// Added to demo how to copy a value struct (this is easy - a deep copy happens by default)
public struct PersonStruct
{
	public PersonStruct(int age, string description)
	{
		this.Age = age;
		this.Purchase.Description = description;
	}
	public struct PurchaseType
	{
		public string Description;
	}
	public PurchaseType Purchase;
	public int Age;
	// This is a struct, which is a value type, so everything is a clone by default.
	public PersonStruct ShallowCopy()
	{
		return (PersonStruct)this;
	}
	// This is a struct, which is a value type, so everything is a clone by default.
	public PersonStruct DeepCopy()
	{
		return (PersonStruct)this;
	}
}
// Added only for a speed comparison.
public class MyDeepCopy
{
	public static T DeepCopy<T>(T obj)
	{
		object result = null;
		using (var ms = new MemoryStream())
		{
			var formatter = new BinaryFormatter();
			formatter.Serialize(ms, obj);
			ms.Position = 0;
			result = (T)formatter.Deserialize(ms);
			ms.Close();
		}
		return (T)result;
	}
}

Then, call the demo from main:

	void MyMain(string[] args)
	{
		{
			Console.Write("Demo of shallow and deep copy, using classes and MemberwiseCopy:\n");
			var Bob = new Person(30, "Lamborghini");
			Console.Write("  Create Bob\n");
			Console.Write("    Bob.Age={0}, Bob.Purchase.Description={1}\n", Bob.Age, Bob.Purchase.Description);
			Console.Write("  Clone Bob >> BobsSon\n");
			var BobsSon = Bob.DeepCopy();
			Console.Write("  Adjust BobsSon details\n");
			BobsSon.Age = 2;
			BobsSon.Purchase.Description = "Toy car";
			Console.Write("    BobsSon.Age={0}, BobsSon.Purchase.Description={1}\n", BobsSon.Age, BobsSon.Purchase.Description);
			Console.Write("  Proof of deep copy: If BobsSon is a true clone, then adjusting BobsSon details will not affect Bob:\n");
			Console.Write("    Bob.Age={0}, Bob.Purchase.Description={1}\n", Bob.Age, Bob.Purchase.Description);
			Debug.Assert(Bob.Age == 30);
			Debug.Assert(Bob.Purchase.Description == "Lamborghini");
			var sw = new Stopwatch();
			sw.Start();
			int total = 0;
			for (int i = 0; i < 100000; i++)
			{
				var n = Bob.DeepCopy();
				total += n.Age;
			}
			Console.Write("  Elapsed time: {0},{1}\n", sw.Elapsed, total);
		}
		{				
			Console.Write("Demo of shallow and deep copy, using structs:\n");
			var Bob = new PersonStruct(30, "Lamborghini");
			Console.Write("  Create Bob\n");
			Console.Write("    Bob.Age={0}, Bob.Purchase.Description={1}\n", Bob.Age, Bob.Purchase.Description);
			Console.Write("  Clone Bob >> BobsSon\n");
			var BobsSon = Bob.DeepCopy();
			Console.Write("  Adjust BobsSon details:\n");
			BobsSon.Age = 2;
			BobsSon.Purchase.Description = "Toy car";
			Console.Write("    BobsSon.Age={0}, BobsSon.Purchase.Description={1}\n", BobsSon.Age, BobsSon.Purchase.Description);
			Console.Write("  Proof of deep copy: If BobsSon is a true clone, then adjusting BobsSon details will not affect Bob:\n");
			Console.Write("    Bob.Age={0}, Bob.Purchase.Description={1}\n", Bob.Age, Bob.Purchase.Description);				
			Debug.Assert(Bob.Age == 30);
			Debug.Assert(Bob.Purchase.Description == "Lamborghini");
			var sw = new Stopwatch();
			sw.Start();
			int total = 0;
			for (int i = 0; i < 100000; i++)
			{
				var n = Bob.DeepCopy();
				total += n.Age;
			}
			Console.Write("  Elapsed time: {0},{1}\n", sw.Elapsed, total);
		}
		{
			Console.Write("Demo of deep copy, using class and serialize/deserialize:\n");
			int total = 0;
			var sw = new Stopwatch();
			sw.Start();
			var Bob = new Person(30, "Lamborghini");
			for (int i = 0; i < 100000; i++)
			{
				var BobsSon = MyDeepCopy.DeepCopy<Person>(Bob);
				total += BobsSon.Age;
			}
			Console.Write("  Elapsed time: {0},{1}\n", sw.Elapsed, total);
		}
		Console.ReadKey();
    }

Again, note that if you use Nested MemberwiseClone for a deep copy, you have to manually implement a ShallowCopy for each nested level in the class, and a DeepCopy which calls all said ShallowCopy methods to create a complete clone. This is simple: only a few lines in total, see the demo code above.

Note that when it comes to cloning an object, there is is a big difference between a "struct" and a "class":

  • If you have a "struct", it's a value type so you can just copy it, and the contents will be cloned.
  • If you have a "class", it's a reference type, so if you copy it, all you are doing is copying the pointer to it. To create a true clone, you have to be more creative, and use a method which creates another copy of the original object in memory.
  • Cloning objects incorrectly can lead to very difficult-to-pin-down bugs. In production code, I tend to implement a checksum to double check that the object has been cloned properly, and hasn't been corrupted by another reference to it. This checksum can be switched off in Release mode.
  • I find this method quite useful: often, you only want to clone parts of the object, not the entire thing. It's also essential for any use case where you are modifying objects, then feeding the modified copies into a queue.

Update

It's probably possible to use reflection to recursively walk through the object graph to do a deep copy. WCF uses this technique to serialize an object, including all of its children. The trick is to annotate all of the child objects with an attribute that makes it discoverable. You might lose some performance benefits, however.

Update

Quote on independent speed test (see comments below):

> I've run my own speed test using Neil's serialize/deserialize > extension method, Contango's Nested MemberwiseClone, Alex Burtsev's > reflection-based extension method and AutoMapper, 1 million times > each. Serialize-deserialize was slowest, taking 15.7 seconds. Then > came AutoMapper, taking 10.1 seconds. Much faster was the > reflection-based method which took 2.4 seconds. By far the fastest was > Nested MemberwiseClone, taking 0.1 seconds. Comes down to performance > versus hassle of adding code to each class to clone it. If performance > isn't an issue go with Alex Burtsev's method. > – Simon Tewsi

Solution 5 - C#

I believe that the BinaryFormatter approach is relatively slow (which came as a surprise to me!). You might be able to use ProtoBuf .NET for some objects if they meet the requirements of ProtoBuf. From the ProtoBuf Getting Started page (http://code.google.com/p/protobuf-net/wiki/GettingStarted):

Notes on types supported:

Custom classes that:

  • Are marked as data-contract
  • Have a parameterless constructor
  • For Silverlight: are public
  • Many common primitives, etc.
  • Single dimension arrays: T[]
  • List<T> / IList<T>
  • Dictionary<TKey, TValue> / IDictionary<TKey, TValue>
  • any type which implements IEnumerable<T> and has an Add(T) method

The code assumes that types will be mutable around the elected members. Accordingly, custom structs are not supported, since they should be immutable.

If your class meets these requirements you could try:

public static void deepCopy<T>(ref T object2Copy, ref T objectCopy)
{
    using (var stream = new MemoryStream())
    {
        Serializer.Serialize(stream, object2Copy);
        stream.Position = 0;
        objectCopy = Serializer.Deserialize<T>(stream);
    }
}

Which is VERY fast indeed...

Edit:

Here is working code for a modification of this (tested on .NET 4.6). It uses System.Xml.Serialization and System.IO. No need to mark classes as serializable.

public void DeepCopy<T>(ref T object2Copy, ref T objectCopy)
{
	using (var stream = new MemoryStream())
	{
		var serializer = new XS.XmlSerializer(typeof(T));

		serializer.Serialize(stream, object2Copy);
		stream.Position = 0;
		objectCopy = (T)serializer.Deserialize(stream);
	}
}

Solution 6 - C#

You can try this

    public static object DeepCopy(object obj)
    {
        if (obj == null)
            return null;
        Type type = obj.GetType();

        if (type.IsValueType || type == typeof(string))
        {
            return obj;
        }
        else if (type.IsArray)
        {
            Type elementType = Type.GetType(
                 type.FullName.Replace("[]", string.Empty));
            var array = obj as Array;
            Array copied = Array.CreateInstance(elementType, array.Length);
            for (int i = 0; i < array.Length; i++)
            {
                copied.SetValue(DeepCopy(array.GetValue(i)), i);
            }
            return Convert.ChangeType(copied, obj.GetType());
        }
        else if (type.IsClass)
        {

            object toret = Activator.CreateInstance(obj.GetType());
            FieldInfo[] fields = type.GetFields(BindingFlags.Public |
                        BindingFlags.NonPublic | BindingFlags.Instance);
            foreach (FieldInfo field in fields)
            {
                object fieldValue = field.GetValue(obj);
                if (fieldValue == null)
                    continue;
                field.SetValue(toret, DeepCopy(fieldValue));
            }
            return toret;
        }
        else
            throw new ArgumentException("Unknown type");
    }

Thanks to DetoX83 article on code project.

Solution 7 - C#

The best way is:

    public interface IDeepClonable<T> where T : class
    {
        T DeepClone();
    }

    public class MyObj : IDeepClonable<MyObj>
    {
        public MyObj Clone()
        {
            var myObj = new MyObj();
            myObj._field1 = _field1;//value type
            myObj._field2 = _field2;//value type
            myObj._field3 = _field3;//value type

            if (_child != null)
            {
                myObj._child = _child.DeepClone(); //reference type .DeepClone() that does the same
            }

            int len = _array.Length;
            myObj._array = new MyObj[len]; // array / collection
            for (int i = 0; i < len; i++)
            {
                myObj._array[i] = _array[i];
            }

            return myObj;
        }

        private bool _field1;
        public bool Field1
        {
            get { return _field1; }
            set { _field1 = value; }
        }

        private int _field2;
        public int Property2
        {
            get { return _field2; }
            set { _field2 = value; }
        }

        private string _field3;
        public string Property3
        {
            get { return _field3; }
            set { _field3 = value; }
        }

        private MyObj _child;
        private MyObj Child
        {
            get { return _child; }
            set { _child = value; }
        }

        private MyObj[] _array = new MyObj[4];
    }

Solution 8 - C#

Maybe you only need a shallow copy, in that case use Object.MemberWiseClone().

There are good recommendations in the documentation for MemberWiseClone() for strategies to deep copy: -

http://msdn.microsoft.com/en-us/library/system.object.memberwiseclone.aspx

Solution 9 - C#

    public static object CopyObject(object input)
    {
        if (input != null)
        {
            object result = Activator.CreateInstance(input.GetType());
            foreach (FieldInfo field in input.GetType().GetFields(Consts.AppConsts.FullBindingList))
            {
                if (field.FieldType.GetInterface("IList", false) == null)
                {
                    field.SetValue(result, field.GetValue(input));
                }
                else
                {
                    IList listObject = (IList)field.GetValue(result);
                    if (listObject != null)
                    {
                        foreach (object item in ((IList)field.GetValue(input)))
                        {
                            listObject.Add(CopyObject(item));
                        }
                    }
                }
            }
            return result;
        }
        else
        {
            return null;
        }
    }

This way is a few times faster than BinarySerialization AND this does not require the [Serializable] attribute.

Solution 10 - C#

The MSDN documentation seems to hint that Clone should perform a deep copy, but it is never explicitly stated:

The ICloneable interface contains one member, Clone, which is intended to support cloning beyond that supplied by MemberWiseClone… The MemberwiseClone method creates a shallow copy…

You can find my post helpful.

http://pragmaticcoding.com/index.php/cloning-objects-in-c/

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser18931View Question on Stackoverflow
Solution 1 - C#KilhofferView Answer on Stackoverflow
Solution 2 - C#Alex BurtsevView Answer on Stackoverflow
Solution 3 - C#NeilView Answer on Stackoverflow
Solution 4 - C#ContangoView Answer on Stackoverflow
Solution 5 - C#Kurt RichardsonView Answer on Stackoverflow
Solution 6 - C#Suresh Kumar VeluswamyView Answer on Stackoverflow
Solution 7 - C#alexView Answer on Stackoverflow
Solution 8 - C#David ThornleyView Answer on Stackoverflow
Solution 9 - C#BasilView Answer on Stackoverflow
Solution 10 - C#EugeneView Answer on Stackoverflow