I want a true deep copy. In Java, this was easy, but how do you do it in 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;
[Serializable]
. so [Serializable]public class Foo { }
will make Foo
marked as serializable. –
Plenary if (!typeof(T).IsSerializable) { throw new ArgumentException("Type {0} is not serializable",typeof(T).Name); }
–
Valuate ms.Position = 0
saved the day for me! Without it really weird stuff was happening. –
Terminator public static T DeepClone<T>(this T obj) { using (MemoryStream memoryStream = new MemoryStream()) { DataContractSerializer serializer = new DataContractSerializer(typeof(T)); serializer.WriteObject(memoryStream, obj); memoryStream.Seek(0, SeekOrigin.Begin); return (T)serializer.ReadObject(memoryStream); } }
–
Companionship 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;
}
}
}
}
ReferenceEqualityComparer.GetHashCode(object obj)
you should be using RuntimeHelpers.GetHashCode(obj)
, otherwise it will be using the objects real hashcode. See https://mcmap.net/q/10197/-what-does-runtimehelpers-gethashcode-do –
Polacca 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);
? –
Polacca Delegates
. Do you know if the other approaches do/don't work with Delegates as well? I would assume they do not. –
Polacca 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. –
Sticktight StackOverflowException
when cloning objects that mutually reference each other... Anybody know of a workaround? –
Mcwhirter .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 :) –
Mcwhirter PropertyInfo
. Is there any way to mark properties of this type as excluded or allowed to reference? –
Specter List<BsonElement> { 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("=") }
–
Undaunted if (typeToReflect == typeof(LocalDate) || typeToReflect == typeof(OffsetDateTime)) return originalObject;
–
Ladanum interface IDeepCopy { object DeepCopy(IDictionary<object, object> visited); }
Each implementor does object DeepCopy(visited) { ..check-visited-add-to-visited..; MyType clone = MemberwiseClone; ... }
, where "..." recurses as needed. E.g. clone.myField1 = myField1.DeepCopy(visited);
or for fields that aren't IDeepCopy
, do the InternalCopy
call shown in answer. –
Fransen 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. –
Dictation 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. –
Gelid 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);
–
Rugby System.InvalidCastException
s if you use this to deep copy ExpandoObject
s, see https://mcmap.net/q/10198/-weakreference-returns-wrong-object and https://mcmap.net/q/21228/-system-invalidcastexception-unable-to-cast-object-of-type-39-system-data-sqlclient-sqltransaction-39-to-type-39-system-transactions-bucketset-39/8479. Still great on plainer objects but as always, beware when copying objects you don't fully understand. Or code. –
Jurisdiction 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();
this
.. Power of extension methods :) –
Qualify 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 MemberwiseClone vs. 39.93 seconds for Serialization). Using Nested MemberwiseClone 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 Nested MemberwiseClone, 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 Nested MemberwiseClone:\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
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. –
Fransen 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);
}
}
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.
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];
}
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
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.
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.
Consts.AppConsts.FullBindingList
? –
Ronnaronnholm Consts.AppConsts.FullBindingList
–
Handsaw © 2022 - 2024 — McMap. All rights reserved.