without memberwiseclone c# deep-copy cloning

c# - memberwiseclone - Cómo hacer una copia profunda de una clase sin marcarla como Serializable



clone object c# (7)

Dada la siguiente clase:

class A { public List<B> ListB; // etc... }

donde B es otra clase que puede heredar / contener algunas otras clases.

Ante este escenario:

  1. A es una clase grande y contiene muchos tipos de referencia
  2. No puedo marcar B como [Serializable] ya que no tengo acceso al código fuente de B

Los siguientes métodos para realizar una copia profunda no funcionan:

  1. No puedo usar ICloneable o MemberwiseClone ya que la clase A contiene muchos tipos de referencia
  2. No puedo escribir un constructor de copia para A , ya que la clase es grande y se agrega continuamente, y contiene clases (como B ) que no se pueden copiar en profundidad
  3. No puedo usar la serialización ya que no puedo marcar una clase contenida (como B , donde no hay código fuente disponible) como [Serializable]

¿Cómo puedo hacer una copia profunda de la clase A ?



De todos modos, dejé de usar la serialización para realizar copias en profundidad, porque no hay suficiente control (no es necesario copiar todas las clases de la misma manera). Entonces comencé a implementar mis propias interfaces de copia profunda y copiar cada propiedad de la forma en que debería copiarse.

Formas típicas de copiar un tipo referenciado:

  • usar copia constructor
  • utilizar el método de fábrica (por ejemplo, tipos inmutables)
  • usa tu propio "clon"
  • copia solo referencia (por ejemplo, otro tipo de raíz)
  • crear nuevas instancias y copiar propiedades (por ejemplo, tipos que no están escritos por usted mismo y que no tienen un constructor de copia)

Ejemplo:

class A { // copy constructor public A(A copy) {} } // a referenced class implementing class B : IDeepCopy { object Copy() { return new B(); } } class C : IDeepCopy { A A; B B; object Copy() { C copy = new C(); // copy property by property in a appropriate way copy.A = new A(this.A); copy.B = this.B.Copy(); } }

Usted puede pensar que esto es una gran cantidad de trabajo. Pero al final, es fácil y sencillo, puede ajustarse donde sea necesario y hace exactamente lo que necesita.


Intente usar un flujo de memoria para obtener una copia profunda de su objeto:

public static T MyDeepCopy<T>(this T source) { try { //Throw if passed object has nothing if (source == null) { throw new Exception("Null Object cannot be cloned"); } // Don''t serialize a null object, simply return the default for that object if (Object.ReferenceEquals(source, null)) { return default(T); } //variable declaration T copy; var obj = new DataContractSerializer(typeof(T)); using (var memStream = new MemoryStream()) { obj.WriteObject(memStream, source); memStream.Seek(0, SeekOrigin.Begin); copy = (T)obj.ReadObject(memStream); } return copy; } catch (Exception) { throw; } }

Aquí hay más.


Puedes probar esto. Esto funciona para mi

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"); }

Gracias al article DetoX83 en el proyecto de código.


Su interfaz IDeepCopy es exactamente lo que ICloneable especifica.

class B : ICloneable { public object Clone() { return new B(); } }

Y con una implementación más amigable:

class B : ICloneable { public B Clone() { return new B(); } // explicit implementation of ICloneable object ICloneable.Clone() { return this.Clone(); } }


Una answer de un hilo diferente de que el uso de la serialización json es lo mejor que he visto.

public static T CloneJson<T>(this T source) { if (Object.ReferenceEquals(source, null)) { return default(T); } return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source)); }


private interface IDeepCopy<T> where T : class { T DeepCopy(); } private class MyClass : IDeepCopy<MyClass> { public MyClass DeepCopy() { return (MyClass)this.MemberwiseClone(); } }

Pluss: Yoy puede controlar el proceso de copia (si su clase tiene propiedades de identificador, puede establecerlas o puede escribir otro código de lógica de negocios)

Menos: la clase puede ser marcada como sellada