I want a real deep copy. In Java, this is easy, but how to implement it in C?
#1 building
The best way is to:
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]; }
#2 building
I Wrote a Based on recursive "memberwise clone" Deep object replication extension method based on . It's fast (three times faster than BinaryFormatter) and can be used with any object. You do not need a default constructor or serializable property.
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; } } } }
#3 building
Kilhoffer based solution
With C 3.0, you can create the following extension methods:
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); } } }
Extended any class marked as [Serializable] using the DeepClone method
MyClass copy = obj.DeepClone();
#4 building
I've seen several different approaches, but I've used a common utility approach:
public static T DeepClone<T>(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 [Serializable] to work properly.
-
Your source file must contain the following code:
using System.Runtime.Serialization.Formatters.Binary; using System.IO;
#5 building
I have a simple idea. Use LINQ with the new selection.
public class Fruit { public string Name {get; set;} public int SeedCount {get; set;} } void SomeMethod() { List<Fruit> originalFruits = new List<Fruit>(); originalFruits.Add(new Fruit {Name="Apple", SeedCount=10}); originalFruits.Add(new Fruit {Name="Banana", SeedCount=0}); //Deep Copy List<Fruit> deepCopiedFruits = from f in originalFruits select new Fruit {Name=f.Name, SeedCount=f.SeedCount}; }