Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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

like image 369
user18931 Avatar asked Sep 24 '08 19:09

user18931


People also ask

What is deep clone in C#?

Deep Copy: It is a process of creating a new object and then copying the fields of the current object to the newly created object to make a complete copy of the internal reference types. If the specified field is a value type, then a bit-by-bit copy of the field will be performed.

Does clone () make a deep copy?

clone() is indeed a shallow copy. However, it's designed to throw a CloneNotSupportedException unless your object implements Cloneable . And when you implement Cloneable , you should override clone() to make it do a deep copy, by calling clone() on all fields that are themselves cloneable.


2 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; 
like image 176
Kilhoffer Avatar answered Sep 28 '22 03:09

Kilhoffer


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;             }         }     }  } 
like image 24
Alex Burtsev Avatar answered Sep 28 '22 02:09

Alex Burtsev