2012-06-17 34 views
6

Estoy trabajando en algún código escrito en C#. En esta aplicación, tengo una colección personalizada define como sigue:Copia profunda de un objeto C#

public class ResultList<T> : IEnumerable<T> 
{ 
    public List<T> Results { get; set; } 
    public decimal CenterLatitude { get; set; } 
    public decimal CenterLongitude { get; set; } 
} 

El tipo utilizado por los resultados son uno de los tres tipos personalizados. Las propiedades de cada uno de los tipos personalizados son solo tipos primitivos (ints, strings, bools, int ?, bool?). He aquí un ejemplo de uno de los tipos personalizados:

public class ResultItem 
{ 
    public int ID { get; set; } 
    public string Name { get; set; } 
    public bool? isLegit { get; set; } 
} 

¿Cómo realizo una copia profunda de un objeto resultList que he creado. Encontré esta publicación: Generic method to create deep copy of all elements in a collection. Sin embargo, no puedo entender cómo hacerlo.

+0

¿Qué has intentado? ¿Qué mensaje de error tienes? El código de Jon Skeet que encontraste simplemente funciona hasta donde puedo ver. –

+0

¿Copia superficial o profunda? http://stackoverflow.com/questions/11073196/shallow-copy-of-a-custom-c-sharp-object –

+0

¿Por qué está usted y el OP de [este queston] (http://stackoverflow.com/questions/ 11073196/shallow-copy-of-a-custom-c-sharp-object) que parece utilizar exactamente la misma estructura de datos en su ejemplo? –

Respuesta

8

Una de las razones por las que su clase resultList no funcionará con El example de Jon Skeet se debe a que no implementa la interfaz ICloneable.

Implemente ICloneable en todas las clases que necesita clonar, p. Ej.

public class ResultItem : ICloneable 
{ 
    public object Clone() 
    { 
    var item = new ResultItem 
       { 
        ID = ID, 
        Name = Name, 
        isLegit = isLegit 
       }; 
    return item; 
    } 
} 

Y también en resultList:

public class ResultList<T> : IEnumerable<T>, ICloneable where T : ICloneable 
{ 
    public List<T> Results { get; set; } 
    public decimal CenterLatitude { get; set; } 
    public decimal CenterLongitude { get; set; } 

    public object Clone() 
    { 
    var list = new ResultList<T> 
       { 
        CenterLatitude = CenterLatitude, 
        CenterLongitude = CenterLongitude, 
        Results = Results.Select(x => x.Clone()).Cast<T>().ToList() 
       }; 
    return list; 
    } 
} 

A continuación, para hacer una copia profunda de su objeto:

resultList.clone(); 
+0

Gran explicación. Muchas gracias por tu ayuda. – user70192

12

El enfoque que implica el menor esfuerzo de codificación es el de serialización y deserialización a través de un BinaryFormatter.

Se podría definir el siguiente método de extensión (tomado de Kilhoffer’s answer):

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

... y luego simplemente llamar:

ResultList<T> clone = DeepClone(original); 
1

Aquí es algo que necesitaba y escribí, que utiliza la reflexión para copiar cada propiedad (y privadas si se especifica)

public static class ObjectCloner 
{ 
    public static T Clone<T>(object obj, bool deep = false) where T : new() 
    { 
     if (!(obj is T)) 
     { 
      throw new Exception("Cloning object must match output type"); 
     } 

     return (T)Clone(obj, deep); 
    } 

    public static object Clone(object obj, bool deep) 
    { 
     if (obj == null) 
     { 
      return null; 
     } 

     Type objType = obj.GetType(); 

     if (objType.IsPrimitive || objType == typeof(string) || objType.GetConstructors().FirstOrDefault(x => x.GetParameters().Length == 0) == null) 
     { 
      return obj; 
     } 

     List<PropertyInfo> properties = objType.GetProperties().ToList(); 
     if (deep) 
     { 
      properties.AddRange(objType.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic)); 
     } 

     object newObj = Activator.CreateInstance(objType); 

     foreach (var prop in properties) 
     { 
      if (prop.GetSetMethod() != null) 
      { 
       object propValue = prop.GetValue(obj, null); 
       object clone = Clone(propValue, deep); 
       prop.SetValue(newObj, clone, null); 
      } 
     } 

     return newObj; 
    } 
} 
+0

Manejar objetos que heredan List : Voy a crear una segunda respuesta ya que el código en los comentarios es una mierda. – jeromeyers

3

Ampliando @ Georgi-it, que tuvo que modificar su código para manejar propiedades cuyo tipo hereda de lista:

public static class ObjectCloner { 
    public static T Clone<T>(object obj, bool deep = false) where T : new() { 
     if (!(obj is T)) { 
      throw new Exception("Cloning object must match output type"); 
     } 

     return (T)Clone(obj, deep); 
    } 

    public static object Clone(object obj, bool deep) { 
     if (obj == null) { 
      return null; 
     } 

     Type objType = obj.GetType(); 

     if (objType.IsPrimitive || objType == typeof(string) || objType.GetConstructors().FirstOrDefault(x => x.GetParameters().Length == 0) == null) { 
      return obj; 
     } 

     List<PropertyInfo> properties = objType.GetProperties().ToList(); 
     if (deep) { 
      properties.AddRange(objType.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic)); 
     } 

     object newObj = Activator.CreateInstance(objType); 

     foreach (var prop in properties) { 
      if (prop.GetSetMethod() != null) { 
       var proceed = true; 
       if (obj is IList) { 
        var listType = obj.GetType().GetProperty("Item").PropertyType; 
        if (prop.PropertyType == listType) { 
         proceed = false; 
         foreach (var item in obj as IList) { 
          object clone = Clone(item, deep); 
          (newObj as IList).Add(clone);        
         }       
        }      
       } 

       if (proceed) { 
        object propValue = prop.GetValue(obj, null); 
        object clone = Clone(propValue, deep); 
        prop.SetValue(newObj, clone, null); 
       }     
      } 
     } 

     return newObj; 
    } 
} 
Cuestiones relacionadas