I want a true deep copy. In Java, this was easy, but how do you do it in C#?
-
1What does a Deep Copy do? Does it copy the bitstream? – SpoiledTechie.com Sep 24 '08 at 19:41
-
49A deep copy is something that copies EVERY field of an object. A shallow copy will only create a new object and point all the fields to the original. – swilliams Sep 24 '08 at 19:46
-
2A deep copy creates a second instance of the object with the same values. A shallow copy (oversimplified) is like creating a second reference to an object. – Michael Blackburn Mar 30 '11 at 19:49
-
1A framework for copying/cloning .NET objects: https://github.com/havard/copyable – jgauffin Feb 18 '11 at 13:14
-
1Use a Mapper, I suggest UltraMapper https://github.com/maurosampietro/UltraMapper – Mauro Sampietro Apr 23 '17 at 09:15
-
check this post : https://stackoverflow.com/a/52490699/1404642 – Siavash Sep 25 '18 at 05:55
-
The BinaryFormatter is insecure, take a look on official docs: https://learn.microsoft.com/en-us/dotnet/api/system.runtime.serialization.formatters.binary.binaryformatter?view=net-5.0 – Arnold Vakaria Jan 26 '21 at 19:05
-
If perf is not an issue, newtonsoft to the rescue... JsonConvert.DeserializeObject
(JsonConvert.SerializeObject(MyInstance)); – SeanT Jun 09 '21 at 00:46 -
See the [Object.MemberwiseClone Method](https://learn.microsoft.com/en-us/dotnet/api/system.object.memberwiseclone?view=net-5.0) page on Microsoft.com. – Sheridan Aug 24 '21 at 08:24
10 Answers
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;

- 178,213
- 47
- 333
- 501

- 32,375
- 22
- 97
- 124
-
5What happen if the object have event, Do they lost everything because of the serialization? – Patrick Desjardins Sep 24 '08 at 19:56
-
9Event subscribes are included into serialization graph, since BinaryFormatter uses fields via reflection, and events are just fields of delegate types plus add/remove/invoke methods. You can use [field: NonSerialized] on event to avoid this. – Ilya Ryzhenkov Sep 24 '08 at 20:16
-
What is that undeclared "stream" variable? Or is it something just in C# and not VB.NET? I converted everything but that variable. – HardCode Mar 19 '09 at 20:41
-
ms.Location or ms.Position? or is this something to do with the Framework version? – dr. evil Mar 29 '09 at 21:08
-
-
5@Sean87: above the class declaration, add `[Serializable]`. so `[Serializable]public class Foo { }` will make `Foo` marked as serializable. – Dan Atkinson Aug 03 '11 at 22:51
-
25Recursive MemberwiseClone will do deep copy too, it works 3 times faster then BinaryFormatter, doesn't require default constructor or any attributes. See my answer: http://stackoverflow.com/a/11308879/235715 – Alex Burtsev Jul 12 '12 at 04:19
-
1This is creating a curious exception "assembly not found" while using this Utility code within the UserControlTestContainer. Its really weird because the assembly is loaded... – v.oddou May 21 '13 at 03:13
-
To address your first note programmatically, you can handle the exception a little better by using the following: `if (!typeof(T).IsSerializable) { throw new ArgumentException("Type {0} is not serializable",typeof(T).Name); }` – KyleMit Jul 16 '13 at 16:08
-
how would you respond to [-->this apparent memory leak issue with Deserialize()](http://blogs.msdn.com/b/psirr/archive/2009/11/13/interesting-memory-leak-in-net-3-5-binary-deserialization.aspx)? – Code Jockey Mar 06 '14 at 19:27
-
It's more helpful to use var formatter = new BinaryFormatter {Context = new StreamingContext(StreamingContextStates.Clone)}; – Chris Ward Nov 27 '14 at 08:06
-
while this is working in some scenarios - it seem like an overkill to serialize everything and deserialize everything just to create a copy of an object. I feel like this should be a example on how to do when you have to finish your project in the next 5 minutes. – Bogdan Apr 25 '15 at 02:17
-
-
For those who wonder, see this easy solution: http://stackoverflow.com/questions/222598/how-do-i-clone-a-generic-list-in-c/40508693#40508693 – F.H. Nov 09 '16 at 13:56
-
`ms.Position = 0` saved the day for me! Without it really weird stuff was happening. – GDS Feb 06 '17 at 03:47
-
6I know this post is old but it still comes up as a top hit when searching for deep cloning. Take note that according to Microsoft (https://aka.ms/binaryformatter) this is no longer a recommended solution as it is insecure. – pwnell Nov 23 '20 at 19:36
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;
}
}
}
}

- 22,270
- 6
- 85
- 105

- 12,418
- 8
- 60
- 87
-
I tried this, but got an error saying I needed to have serializable attributes in my class I am cloning... – theJerm Feb 18 '13 at 07:05
-
@theJerm I think you have called Clone, instead of Copy, there is a Clone method that uses BinaryFormatter as described in accepted answer for benchmark comparisson – Alex Burtsev Feb 18 '13 at 13:42
-
3
-
1For `ReferenceEqualityComparer.GetHashCode(object obj)` you should be using `RuntimeHelpers.GetHashCode(obj)`, otherwise it will be using the objects real hashcode. See http://stackoverflow.com/a/11240110/495262 – Matt Smith Apr 23 '14 at 16:46
-
4Regarding `IsPrimitive`: what is the reason you return true for a string. Also, is there any reason you use the single `&` rather than `&&` in the statement: `return (type.IsValueType & type.IsPrimitive);`? – Matt Smith Apr 23 '14 at 16:51
-
Based on reading the code, I assume this doesn't work for types containing `Delegates`. Do you know if the other approaches do/don't work with Delegates as well? I would assume they do not. – Matt Smith Apr 23 '14 at 16:54
-
2@MattSmith You are absolutely right about GetHashCode, checked it, yep StackOverflow exception - https://gist.github.com/Burtsev-Alexey/11227277 – Alex Burtsev Apr 23 '14 at 18:33
-
3@MattSmith It was working for delegates, but I intently disabled it (by setting null), see https://github.com/Burtsev-Alexey/net-object-deep-copy/issues/7, the subscribers were cloned, in the end if you had two object A and B connected (by event subscription) you would get objects A' and B' connected, this is correct but that's not what most people want when the clone objects. – Alex Burtsev Apr 23 '14 at 18:39
-
@AlexBurtsev, Regarding the delegates--okay, I changed it to not copy delegates, but copy the reference (similar to how you treat strings), since (like strings) delegates are immutable. The serialization approach does handle delegates, but I'm not sure if they have a similar problem to what you experienced with delegates. – Matt Smith Apr 23 '14 at 18:43
-
3@MattSmith Strings are treated as primitives because 1: they are immutable, 2: calling protected MemberwiseClone on string will result in memory corruption, string data will turn into random chars, and soon .NET runtime will crash with internal error saying theer is a but in .NET :-) – Alex Burtsev Apr 23 '14 at 18:45
-
1@MattSmith I believe you do understand that by reusing same delegate in original and cloned object they will be using the same objects which were referenced in delegate, like A has delegate which modifies B, by cloning A you would get A' which modifies the same B, if you want to have the true copy (snapshot) of object graphs (memory), then simply remove the line which sets delegate to null. You would get then A modifying B, and A' modifying B' – Alex Burtsev Apr 23 '14 at 19:19
-
@AlexBurtsev, Good points. What you want somewhat depends on what objects are being accessed within the delegate. If you're accessing objects that are being copied, then you probably want the copied delegate. If you're accessing object external to the copied object, then you probably want the delegate to not be copied. That said, I don't have a use case for delegates, so perhaps it's best to leave it as unimplemented for now (i.e. I'll throw an exception). – Matt Smith Apr 23 '14 at 19:23
-
BTW, When I enable Delegates for my simple test case it stackoverflows. Here's my sample object I attempt to copy: http://shar.es/T86JR – Matt Smith Apr 23 '14 at 19:43
-
1I've forked and updated your code for compatibility with the new TypeInfo-based reflection API (used in Windows Phone and Windows Store applications, and their respective PCL profiles). https://github.com/Gameleon12/net-object-deep-copy/blob/master/ObjectExtensions.cs – Leon Lucardie Oct 08 '14 at 12:08
-
Please add an example of how to use your extension method, it will help flesh out your answer. – Dean Jan 15 '15 at 04:25
-
1Even faster (for me) is to use JSON.NET and serialize and then deserialize. Might not work for everyone (I'm thinking private fields and such). – Peter Apr 01 '15 at 09:57
-
2@AlexBurtsev, your code using namespace System.ArrayExtensions, where can I get it? – Alex141 Apr 11 '15 at 21:37
-
4@Alex141 - just encountered the same puzzlement. All the relevant code is in the referenced file, there's an ArrayExtensions namespace lower down. – Arunas May 15 '15 at 03:24
-
10This is a very clever and powerful implementation, however you have to consider a few things before deciding whether it’s right for your data model. `Memberwiseclone()` is so fast because it does not invoke constructors. So if your constructors are doing heavy lifting such as event subscription, you are out of luck. It relies on copying private fields of the object, bypassing the business logic in the properties and methods. For example, I saw `hashCode` field being copied in a `HashSet` collection, even though all instances have changed. – kat May 26 '15 at 17:21
-
4I'm getting a `StackOverflowException` when cloning objects that mutually reference each other... Anybody know of a workaround? – yu_ominae May 27 '15 at 04:57
-
2@yu_ominae cyclic references are suported and works fine. Your problem is somewhere else. If you can share source code of the class you are trying to clone, open an issue at project website. Cccasionally I run into SO Exceptions, when cloning what is not supposed to be cloned, like .NET internal infrastructure, that might get referenced accidently, especially pointers and their reflections. As an example trying to clone NHibernate entity containing collection that is attached to session, will result in big mess, because NH collection implementing IList
contains DbConnection field inside. – Alex Burtsev May 27 '15 at 09:41 -
@AlexBurtsev I was trying to clone a collection objects mirroring objects in a different application. The access is done via com calls, so maybe I have a reference in there that is causing the thing to blow up. The `.Copy()` method worked fine if I set a breakpoint and inspected the list content in the watch before resuming execution. Weird. I found that calling `.ToList() ` actually achieves what I want for now, so it's all good. Thanks for the reply :) – yu_ominae May 27 '15 at 10:59
-
Is there anyway I can use this but still ensure that a certain field is only copied by reference? – David Mar 11 '16 at 09:56
-
1
-
Can you explain how this is deep vs. shallow? My understanding is that memberwiseClone is shallow, and that's what you invoke in here. Unless I am missing something. – g.t.w.d Dec 07 '16 at 16:44
-
1Hi Alex, Thanks for your extension. Does this also "DeepCopy" events/delegates? – Deepak Jan 05 '17 at 12:39
-
Seems to have issues with type `PropertyInfo`. Is there any way to mark properties of this type as excluded or allowed to reference? – Wobbles Jul 13 '17 at 12:31
-
3
-
-
you dont copy arrays if its primitive+string, but one can modify one copy then other copy sees that change which is not deep copy meant to be. – TakeMeAsAGuest Mar 24 '19 at 18:31
-
Warning: this approach brakes XDocuments. I can no longer access attributes by name, even thou there is no namespace defined. – Darek Mar 27 '19 at 15:47
-
It seems that List
is not properly reflected by fieldInfo. Out of: `List – darthkurak May 15 '19 at 15:42{ BsonElement ("status=Connected"), BsonElement("lastConnectedTimeUtc=2019-05-15T10:50:40.5570932Z") }` it makes: `BsonElement[] { BsonElement ("status=Connected"), BsonElement("lastConnectedTimeUtc=2019-05-15T10:50:40.5570932Z"), BsonElement("="), BsonElement("=") }` -
I used this in conjunction with fluentassertions to make a clone of an object and later validate originalObject.Should().BeEquivalentTo(objectClone). However this fails if the object contains Noda Time structs. The solution was to add a line like this to InternalCopy: `if (typeToReflect == typeof(LocalDate) || typeToReflect == typeof(OffsetDateTime)) return originalObject;` – Ralph Gonzalez Mar 09 '21 at 22:39
-
@g.t.w.d - "how this is deep vs. shallow". If it *stopped* after calling MemberwiseClone, then it would be shallow. It uses that as a first step, then uses reflection info to do some recursive changes to the clone. That converts the shallow clone into a deep clone. (I assume the reason for initial MemberwiseClone, is to avoid the need to process fields that are in fact shallow - field types that don't need any recursion.) – ToolmakerSteve Mar 12 '21 at 02:05
-
Note that this approach can be easily optimized to use a custom interface that you implement to speed up your own classes. `interface IDeepCopy { object DeepCopy(IDictionary – ToolmakerSteve Mar 12 '21 at 02:28
-
2I know this code is very old and not continuously managed, but it has serious problems. If you deep copy an object which has a `Type` object as a member, `Type` object itself will be copied too. And the new one seems to be the same with the old one, but type comparison between two `Type`s will get fail, because `Type.Equals(Type)` use reference comparison. And one more, if an object 'A' has a user-defined `struct` member 'B', and 'B' has a reference to an object 'C' as a member again, object 'C' will not get deep copied with 'A'. Please use this code with care. – Codeption May 07 '21 at 15:03
-
-
@Alic W When you are explaining to some one how to do something the idea is to take something that is hard and try to make it easier. It's hard and it should be hard is not helpful, this is a reason Documentation is used. Its also why Stack Overflow exists. – dannyhut Sep 15 '21 at 10:38
-
1@AlexBurtsev, after 9 years still best & only (as I'm aware) solution. Thanks a lot !!!!! – Lucy82 Nov 25 '21 at 09:48
-
1It seems to not work on the objects wit the cross-links. The very first attempt to copy one of my objects ended up in indefinite loop :( And my problem is that the objects that I need to copy I don't control. I'd love to implement extra functionality on them to support the deep copy approach, except I can't. – real4x Feb 07 '22 at 07:43
-
1@real4x probably a bit late for you, but this is because the `visited.Add()` statement needs to be directly before the `if (typeToReflect.IsArray)` block. This issue and more were fixed in my fork of Alex's project on github. – jmik Sep 20 '22 at 22:53
-
I'm getting a `Could not get function from a frame. The code is currently unavailable. The error code is CORDBG_E_CODE_NOT_AVAILABLE, or0x80131309.` error on the line `var cloneObject = CloneMethod.Invoke(originalObject, null);` – emilaz Oct 25 '22 at 12:16
-
1You can get some nasty seemingly-random `System.InvalidCastException`s if you use this to deep copy `ExpandoObject`s, see https://stackoverflow.com/a/67220901/8479 and https://stackoverflow.com/q/74262314/8479. Still great on plainer objects but as always, beware when copying objects you don't fully understand. Or code. – Rory Oct 31 '22 at 14:02
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();
-
38To that add "public static T DeepClone
(this T a) where T : ISerializable" – Amir Rezaei Feb 11 '11 at 14:35 -
19@Amir - it isn't necessary for the class to implement ISerializable, Marking with SerializableAttribute is sufficient. The attribute uses reflection to perform serialization, while the interface allows you to write a custom serializer – Neil Feb 14 '11 at 16:10
-
9I agree with your statement, but I like Amir's suggestion b/c it provides compile-time checking. Is there any way to reconcile the two? – Michael Blackburn Mar 30 '11 at 19:47
-
2Passed unit test var stringbuilder = new StringBuilder("TestData"); var copy = stringbuilder.DeepClone(); Assert.IsFalse(Equals(stringbuilder,copy)); Thanks a lot. – om471987 May 06 '12 at 18:17
-
3@Neil This method is 10x slower than the NestedMemberwiseClone method, see my post on this page. – Contango May 20 '12 at 18:02
-
Hi @Gravitas. It's no surprise that hand-coded methods can out-perform a solution that uses reflection. It would be interesting to compare the performance of your solution to a hand-coded serialization method (i.e. implementing ISerializable) – Neil May 23 '12 at 13:50
-
@Gravitas, hand-coded serialization was no better. Your hand-coded clone method is much faster for simple classes (maybe more like 100x) – Neil May 23 '12 at 14:34
-
-
1
-
Hi @nawfal - well at the time there was also a bug in Kilhoffer's code that I didn't have enough karma to fix. But yes - my best contribution so far really is this trivial – Neil May 01 '13 at 08:29
-
-
I wrote a DeepClone method based on Neil's answer. It worked great, until I called DeepClone on an object whose class was defined in a 'plug-in' ... an assembly that I loaded programmatically. That caused a SerializationException to be thrown with an error of 'Unable to find assembly xxx'. I solved that problem with a solution I posted [here](https://stackoverflow.com/a/54694008/2175233) – Brad Oestreicher Feb 14 '19 at 15:44
-
Important Note BinaryFormatter has been deprecated and will no longer be available in .NET after November 2023. See [BinaryFormatter Obsoletion Strategy](https://github.com/dotnet/designs/blob/main/accepted/2020/better-obsoletion/binaryformatter-obsoletion.md) – whihathac Jun 01 '23 at 11:19
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

- 76,540
- 58
- 260
- 305
-
1Good post. Any idea why serialization is so much slower? Also, how would your checksum work? Why not just have an equality checker? – user420667 Apr 01 '12 at 15:33
-
@user420667 The checksum works by manually converting all of the parameters within the class into ints, then adding up said ints to create a checksum. Its useful if you are feeding copies of objects into a queue in one thread, and reading out with another thread. You would have to methods: ChecksumWrite and ChecksumVerify. – Contango May 20 '12 at 17:59
-
@user420667 Could use an equality checker as well - but only if you had something else to compare it to. If you are feeding things into a queue, how do you work out if the items popping out the other end are valid or not? An equality checker will not work, whereas a checksum will verify internal consistency. Once the code has been proven to work with say 100 million items in the queue over a couple of weeks of deployment, you can be pretty sure that the code is solidly written, and you can remove the checksum. – Contango May 20 '12 at 18:01
-
4I can confirm that this is much faster than the serialization method. The cost is: writing more code; the maintenance risk of adding a field without adding it to the clone method; need to write helper classes for any 3rd party classes (such as Dictionary<>) – Neil May 23 '12 at 14:37
-
@Gravitas: Ah ok. I guess I had envisioned item1 compared to item2, so it seemed excessive to do item1.GetChecksum() only to compare it to item2.GetChecksum(), which would require more operations than a simple equality comparison. But if you're only comparing it to itself at a later time..., i don't know that sounds like an impossible task. My ChecksumVerify would probably call GetChecksum(), so the two would be almost certainly guaranteed to match. – user420667 May 23 '12 at 16:51
-
@Gravitas: I guess you're concerned about the integrity of the data over some sort of noisy transmission line, right? – user420667 May 23 '12 at 16:55
-
1You can create an extension method that works on any object, see my answer http://stackoverflow.com/a/11308879/235715 – Alex Burtsev Jul 12 '12 at 04:16
-
@user420667. No, I am more concerned with cloning objects incorrectly which leads to bugs (see my second to last bullet point in my answer above). – Contango Jan 28 '13 at 19:03
-
3It's too bad neither Java nor .NET distinguishes among references that encapsulate identity, mutable state, both, or neither. Conceptually, there should only be one type of "clone": a new object where each reference encapsulates the same thing as in the corresponding reference in the original. If a reference encapsulates identity, the clone's reference must refer to the *same* object. If it encapsulates mutable state but *not* identity, the clone must receive a reference to a different object with the same state [otherwise both references would erroneously... – supercat Sep 23 '13 at 19:53
-
3...encapsulate identity as well as state]. An object reference that encapsulates both identity and state cannot be cloned except by copying everything else *which holds a reference to that object*--a feat which is often difficult or impossible. While references to some types of object will usually be used to encapsulate identity, and references to others will usually encapsulate mutable state, knowing the type of an object is not sufficient to the purpose for which a reference is held. – supercat Sep 23 '13 at 20:01
-
4I'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 Elms Jun 07 '16 at 15:12
-
-
@vargonian There are no circular references if one designs a data structure with none. Generally, POCO data structures simple, nested 2 (or perhaps 3) levels deep. – Contango Sep 12 '19 at 19:29
-
`this.Purchase.ShallowCopy()`. This makes no sense, if Purchase itself needs a DeepCopy. The correct pattern for a true DeepCopy, is to call DeepCopy on each non-primitive field. Any type that contains only primitives, simply implements DeepCopy as a ShallowCopy `(Person)this.MemberwiseClone()`. **In a DeepCopy, the parent *does not know whether each non-primitive child is deep or shallow*, so it must call DeepCopy on the child. It is child's responsibility to continue deep, or do shallow.** To avoid mistakes, its best to *always* call DeepCopy. Let the child be responsible for itself. – ToolmakerSteve Mar 12 '21 at 02:14
-
@ToolmakerSteve Correct. Also, please keep in mind that this is demo code only, mainly aimed at showing the difference between Deep and Shallow copies. In production code, would also recommend everything is named DeepCopy right down the stack, even if it only executes a shallow copy. Fortunately, this is a self-limiting issue: having a ShallowCopy method is unlikely to produce any bugs, as the name makes it clear what it does, and it can be refactored to add a DeepCopy on demand. Fnally, this code is absolutely rock solid, and battle tested in many successful projects over the last 10 years. – Contango Mar 12 '21 at 08:58
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);
}
}

- 16,260
- 18
- 100
- 123

- 211
- 2
- 4
-
Wonder how fast it is compared to the Nested MemberwiseClone answer above? – Contango Sep 24 '13 at 20:15
-
2this won't work if your class has a Dictionary which has to be copied, as IDictionary cannot be serialized – Fabio Napodano Nov 15 '19 at 16:36
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.

- 4,193
- 2
- 20
- 35
-
4
-
It does not deep copy value types, but those can have fields with references to mutable objects. Also, it cannot handle object graphs with cycles. – jmik Sep 23 '22 at 23:01
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];
}

- 30,738
- 21
- 105
- 131

- 49
- 2
-
Using [CGbR Clone Generator](https://github.com/Toxantron/CGbR#cloneable) you get the same result without manually writing the code. – Toxantron Jun 09 '16 at 20:50
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

- 32,045
- 9
- 125
- 137

- 1,278
- 1
- 12
- 15
-
12
-
You can do a deep clone with MemberwiseClone, all you do is add nesting. See answer from @Gravitas above. – Contango Dec 30 '11 at 19:22
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.

- 19
- 1
-
The problem with ICloneable is that the Clone method does not explicitly specify whether it is performing a shallow or deep copy, so callers can never be sure. Hence, there is some [discussion|http://blogs.msdn.com/brada/archive/2004/05/03/125427.aspx] about making ICloneable obsolete in the .NET Framework. – Mahmoud Samy Aug 08 '16 at 18:54
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.

- 30,738
- 21
- 105
- 131

- 33
- 1
-
1You're not continuing the deep copy down your non-IList branch and I think you would have issues with ICollection/IEnumerable. – Rob McCready Jul 05 '11 at 07:04
-
Using the "Nested MemberwiseClone" technique is an order of magnitude faster again (see my post under @Gravitas). – Contango Jan 01 '12 at 23:29
-
4
-
Poor quality answer. Includes an unknown constant `Consts.AppConsts.FullBindingList` – stigzler Feb 10 '22 at 10:11