2010-03-30 19 views
16

Dada la siguiente clase:Cómo copiar una clase profunda sin marcar como Serializable

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] como no tengo acceso al código fuente de B

Lo siguiente métodos para realizar una copia completa no funcionan:

  1. no puedo usar ICloneable o MemberwiseClone como clase A contiene muchos tipos de referencia
  2. no puedo escribir un constructor de copia para A, como la clase es grande y continua que se añade a , y contiene clases (como B) que no se pueden copiar profunda
  3. no puedo utilizar la serialización ya que no puedo marcar una clase contenidos (como B, donde no hay código fuente disponible) como [Serializable]

¿Cómo puedo copiar profundamente la clase A?

+0

@Will: Mi sympathie, yo estaba tratando de cambiar el formato, también, lo que es un desastre! lexu

+0

Gracias amigo, en realidad soy nuevo en este sitio, he formateado mientras escribía, pero cuando lo publiqué como tht – Gaddigesh

+0

esto lo hace sin serialización: http://valueinjecter.codeplex.com/wikipage?title=Deep%20Cloning&referringTitle= Inicio – Omu

Respuesta

7

he dejado de utilizar la serialización de una copia completa de todos modos, porque no hay suficiente control (no cada clase tiene que ser copiado de la misma manera). Luego comencé a implementar mis propias interfaces de copia profunda y copié todas las propiedades de la manera en que deberían copiarse.

formas típicas para copiar un tipo referenciado:

  • uso constructor de copia
  • el uso de métodos de fábrica (. Por ejemplo, tipos inmutables)
  • utilizar su propio "clon"
  • copia única referencia (por ejemplo, .otros Root-Type)
  • crear nuevas propiedades de instancia y copia (p.tipos no escritas por sí mismo carece de 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 una enorme cantidad de trabajo. Pero al final, es fácil y directo, puede ajustarse donde sea necesario y hace exactamente lo que necesita.

+0

¿Qué pasa si tienes un objeto grande? Por favor, no me diga que tener un gran objeto es el problema porque eso crea uno más grande. –

+0

@Bomboca ;: Por supuesto, necesita el código para una copia profunda explícita en alguna parte. Pero no hay otra solución limpia. Si hubiera otra solución genérica, sería muy inestable o requeriría mucho esfuerzo de configuración, lo que la hace menos transparente y mucho más difícil de controlar. Entonces, ¿cuál es el problema con simplemente copiar los valores, la forma exacta en que desea copiarlos? –

+0

Agradezco su punto de vista, pero es demasiado trabajo configurar la copia de un objeto grande. Luego, alguien más agrega un nuevo campo y olvida o ni siquiera sabe acerca de lo de la copia.El QA luego viene y dice que no funciona. Por supuesto, lo solucionaremos, pero hacerlo genéricamente garantiza más pruebas de futuro. Ser menos transparente es el precio a pagar por las convenciones y las formas genéricas de hacer las cosas, pero esto es necesario para abstraer las operaciones de nivel inferior para que podamos centrarnos en la solución de problemas de manera eficiente. Aun así, acepto la solución para clases muy pequeñas y pequeñas. –

0

¿No puede la serialización XML ayudarle?

+0

Suena como si su diseño fuera tan jodido que se ahogaría. Un simple diccionario en el árbol de objetos y la serialización xml (bueno, el XmlSerializer) se ahogaría. – Will

1

¿No puedes hacer esto?

[Serializable] 
class A 
{ 
    ... 
    [NonSerialized] 
    public List<B> ListB; 
    .... 
} 

Y luego referirse a How do you do a deep copy of an object in .NET (C# specifically)? para una función de clonación

+0

@m_oLogin: OP lo quiere sin marcarlo 'Serializable'. –

+0

Según tengo entendido, el autor declara que no puede marcar la clase interna como [Serializable] porque no tiene acceso al código fuente ... Todavía puede marcar el miembro interno "ListB" como [NonSerialized] y luego use la serialización para copiar profundamente. – karlipoppins

1

su interfaz IDeepCopy es exactamente lo que especifica ICloneable.

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

y con una aplicación más amigable:

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

-1 ICloneable no significa que sea una copia profunda. Hay clases en .Net que están marcadas con ICloneable pero no hacen una copia profunda. – Amir

4

puede probar esta. Funciona para mí

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 a DetoX83 article en el proyecto de código.

+0

no funciona con controladores de eventos. –

+0

Lote de "trampas" aquí. Enumerados, nulables, etc ... – OnResolve

+0

La línea Tipo elementType = Type.GetType ( type.FullName.Replace ("[]", string.Empty)); debe reemplazarse por: Tipo elementType = type.GetElementType(); – Karrok

2
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 la propiedad identificadora puede establecerlos, o puede escribir otro código de lógica de negocio)


Menos: clase puede ser marcado como sellado


+4

¿Cómo se convierte MemberwiseClone() en una copia profunda? ¿Es solo código de muestra? Si es así, ¿por qué no está marcado como? ¿Por qué proporcionar un código de muestra malo y sin marcar, si ya hay una respuesta con un buen código de muestra? ¿Quién diablos votó esto? –

-1

Un answer de un hilo diferente que nosotros La serialización json es la 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)); 
} 
0

Trate de usar una secuencia 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; 
       } 
      } 

Here is more.

+0

El título es "Cómo copiar profundamente una clase sin marcarla como Serializable". Tu clase debe ser serializable para que la serialices con un MemoryStream. –

+0

@Andy_Vullhop Lo probé con varias clases (no serializables) y también con mi propia clase de ejemplo simple. En ninguno de los casos requirió el clausula "Serializable". Sin embargo, lo admito, usted obtiene una excepción a menos que su clase serializada no esté marcada como 'DataContractAttribute'. Y todos sus miembros que desea serializar deben estar marcados con 'DataMemberAttribute'. Si la clase es una colección, debe marcarla como 'CollectionDataContractAttribute'. En realidad obtienes un mensaje de excepción que dice esto. Creo que eso es justo. –

+0

Sí, y marcando la clase original cualquier cosa, lo trae al principio de nuevo ...: o / –

Cuestiones relacionadas