2009-06-23 32 views
21

¿Hay alguna manera de copiar una clase en C#? Algo así como var dupe = MyClass (original).copiar una clase, C#

+2

La pregunta no está nada clara. Estás hablando de objetos, no de clases, y debes aclarar qué semántica quieres, como Groo dijo http://stackoverflow.com/questions/184710/what-is-the-difference-between-a-deep-copy- and-a-shallow-copy –

+0

Una posibilidad es utilizar la reflexión para crear un método genérico para hacer esto por usted: http://pmichaels.net/2016/03/04/dynamically-copy-a-class-from-one -instance-to-another-using-reflection/ –

Respuesta

18

No todas las clases tienen esta funcionalidad. Probablemente, si una clase lo hace, proporciona un método Clone. Para ayudar a implementar ese método para sus propias clases, existe un método protegido MemberwiseClone definido en System.Object que hace una copia superficial de la instancia actual (es decir, los campos se copian, si son tipos de referencia, la referencia apuntará a la ubicación original).

1

Una posibilidad es clone. Debe implementar la interfaz ICloneable y el método Clone.

+2

Tenga en cuenta que la interfaz en cuestión ha quedado en desuso. Nunca estuvo claro si significaba "copia profunda" o "copia superficial"; una interfaz con un contrato poco claro no es una que se puede implementar con precisión o usar de manera confiable. (La próxima vez que diseñe una biblioteca de clase base de framework, ¡no repita este error! :)) –

2

No hay ninguna manera directa que siempre funcione. Si su clase es [Serializable] o implementa ISerializable, puede hacer una serialización de ida y vuelta para crear una copia idéntica. Lo mismo funciona para [DataContract]

Si solo desea una copia poco profunda, puede intentar Object.MemberwiseClone(). Sin embargo, es un método protegido y solo puede usarlo desde dentro de la clase.

Si tiene suerte, la clase implementa ICloneable y puede simplemente llamar al método Clone().

4

Creo que el autor está preguntando por constructores de copia ...

La respuesta es "sí, pero sólo si se implementa por sí mismo", no hay manera 'automática' de hacerlo sin algunos cludges pesados ​​(lee : Reflexión):

class MyClass { 
    private string _name; 

    // standard constructor, so that we can make MyClass's without troubles 
    public MyClass(string name) {_name = name} 
    public MyClass(MyClass old) { 
     _name = old._name; 
     // etc... 
    } 
} 

la otra cosa de la nota a este respecto es la interfaz de IClonable, y el método .Clone() esto proporciona. También el método protegido .MemberwiseClone() proporcionado por la clase Object puede ayudar a implementar estos dos métodos.

1

¿Quiere decir un constructor de copia, como existe en C++?

No existe en C#. Lo que puede hacer es escribir el suyo (lo cual es tedioso), o puede escribir un método 'Clonar' que usa la serialización para crear una nueva instancia que tenga exactamente los mismos valores que la clase original.

Puede serializar la instancia actual, deserializarla y devolver el resultado deserializado. La desventaja es que tu clase tiene que ser serializable.

35

Probablemente esté hablando de una copia en profundidad (deep copy vs shallow copy)?

Usted tiene que:

  1. implemento (codificar) un método propio,
  2. tratar de poner en práctica (o encontrar) una implementación que utiliza la reflexión o emitir hacerlo de forma dinámica (que se explica here),
  3. usan serialización y deserialización para crear una copia profunda, si el objeto está marcado con un atributo [Serializable].
public static T DeepCopy<T>(T other) 
{ 
    using (MemoryStream ms = new MemoryStream()) 
    { 
     BinaryFormatter formatter = new BinaryFormatter(); 
     formatter.Serialize(ms, other); 
     ms.Position = 0; 
     return (T)formatter.Deserialize(ms); 
    } 
} 

Para obtener una copia superficial, puede utilizar el método Object.MemberwiseClone(), pero es un método protegido, lo que significa que sólo se puede utilizar desde el interior de la clase.

Con todos los métodos de copia profunda, es importante considerar cualquier referencia a otros objetos, o referencias circulares que pueden dar como resultado la creación de una copia más profunda que la que usted quería.

+0

La serialización es una buena idea :-). –

+0

@Martin: Sí, esa es la manera más fácil, aunque para usar la clase de serialización .NET nativa debe marcarse como '[Serializable]'. De lo contrario, debe usar algunas otras implementaciones. – Groo

+1

Por algún motivo, falta el fragmento de código anterior . Por ejemplo, debería estar viendo: public static T DeepCopy (T other) – sapbucket

4

Sí y no. Esta es un área donde debes ser un poco cuidadoso porque hay algunas trampas (y luego otra vez, no es difícil).

Antes que nada, si hurgas un poco en la Base Class Library (BCL), puedes descubrir el ICloneable interface. No todos los tipos implementan esta interfaz, pero los que sí tienen un método Clone que devolverá una nueva instancia del mismo tipo con (presumiblemente) los mismos valores.

Sin embargo, aquí hay una trampa: la interfaz ICloneable no especifica suficientemente si se espera un clon profundo o un clon superficial, por lo que algunas implementaciones hacen una cosa, y otras implementaciones la otra. Por esta razón, ICloneable no se usa mucho, y se desaconseja su uso posterior; consulte el excelente Framework Design Guidelines para obtener más detalles.

Si profundiza en el BCL, puede descubrir que System.Object tiene el método MemberwiseClone protegido. Si bien no puede llamar a este método directamente desde otro tipo, puede usar este método para implementar la clonación en sus propios objetos.

Un patrón de clonación común es definir un constructor protegido de la clase que desea clonar y pasar una instancia ya existente como parámetro. Algo como esto:

public class MyClass() 
{ 
    public MyClass() {} 

    protected MyClass(MyClass other) 
    { 
     // Cloning code goes here... 
    } 

    public MyClass Clone() 
    { 
     return new MyClass(this); 
    } 
} 

Sin embargo, eso obviamente solo funciona si controla el tipo que desea clonar.

Si desea clonar un tipo que no puede modificar, y que no proporciona un método de clonación, deberá escribir código para copiar explícitamente cada dato de la instancia anterior a la nueva instancia.

1

Una manera fácil de clonar un objeto está escribiendo en un arroyo y leerlo de nuevo:

public object Clone() 
{ 
    object clonedObject = null; 
    BinaryFormatter formatter = new BinaryFormatter(); 
    using (Stream stream = new MemoryStream()) 
    { 
     formatter.Serialize(stream, this); 
     stream.Seek(0, SeekOrigin.Begin); 
     clonedObject = formatter.Deserialize(stream); 
    } 

    return clonedObject; 
} 

Pero tener en cuenta, que esto puede causar problemas con el llamado objeto agregado.

7

Si su clase sólo tiene propiedades, se puede hacer algo como esto:

SubCentreMessage actual; 
actual = target.FindSubCentreFullDetails(120); //for Albany 
SubCentreMessage s = new SubCentreMessage(); 

//initialising s with the same values as 
foreach (var property in actual.GetType().GetProperties()) 
{ 
    PropertyInfo propertyS = s.GetType().GetProperty(property.Name); 
    var value = property.GetValue(actual, null); 
    propertyS.SetValue(s, property.GetValue(actual, null), null); 
} 

Si tiene campos y métodos, estoy seguro de que puede volver a crear en nuevas clases usando reflexiones.Espero que esto ayude

1

¿Qué tal algo como:

public class MyClass 
{ 
    int i; 
    double d; 
    string something; 

    public MyClass(int i, double d) {} 

    public MyClass Clone() 
    { 
     return (new MyClass(i, d)); 
    } 
} 

O si también necesita copiar algo más por lo general no se conoce cuando se construye un nuevo objeto:

public MyClass CloneMore() 
    { 
     MyClass obj = new MyClass(i, d); 
     obj.something = this.something; 
     return (obj); 
    } 

llamada usando:

MyClass us = them.Clone(); 

~~~

+0

Creo que escribí una clase que amplía el objeto llamado memberwiseclone que utiliza la reflexión para obtener/establecer todos los campos/propiedades. –

3

¿Qué tal con JSON?

Usted puede obtener el paquete de JSON: https://www.nuget.org/packages/Newtonsoft.Json/

using Newtonsoft.Json; 
public static List<T> CopyAll<T>(this List<T> list) { 
    List<T> ret = new List<T>(); 
    string tmpStr = JsonConvert.SerializeObject(list); 
    ret = JsonConvert.DeserializeObject<List<T>>(tmpStr); 
    return ret; 
} 
+0

Gracias @ saud-khan, esto me ayuda a lograr lo que estaba buscando. – MatVAD

+0

esto es ... muy necesario ... –

1

Algunas soluciones razonables aquí serialización es una forma válida de hacerlo, así como un método de clon para cada clase .. batida esto y parece funciona para las pocas pruebas que hice en ella

using System.Reflection; using System.Text.StringBuilder(); 

public static T CopyClass2<T>(T obj){ 
    T objcpy = (T)Activator.CreateInstance(typeof(T)); 
    obj.GetType().GetProperties().ToList() 
    .ForEach(p => objcpy.GetType().GetProperty(p.Name).SetValue(objcpy, p.GetValue(obj))); 
    return objcpy; 

}

Y aquí es la versión más prolija supongo, anterior sugiere que entiende Lambda Expressions que no es común. Si respetas la legibilidad (que es totalmente válido) más que yo usaría a continuación

public static T CopyClass<T>(T obj) 
{ 
    T objcpy = (T)Activator.CreateInstance(typeof(T)); 
    foreach (var prop in obj.GetType().GetProperties()) 
    { 
     var value = prop.GetValue(obj); 
     objcpy.GetType().GetProperty(prop.Name).SetValue(objcpy, value); 
    } 
    return objcpy; 

}

Utilice este método para enumerar la propiedad de para ver si se copia en nueva referencia .. simple que no muestra propiedades de ninguna primitiva (tipos estructurados) ... por lo que obtendrá un nombre de clase

public static string ListAllProperties<T>(T obj) 
{ 
     StringBuilder sb = new System.Text.StringBuilder(); 
     PropertyInfo[] propInfo = obj.GetType().GetProperties(); 
     foreach (var prop in propInfo) 
     { 
      var value = prop.GetValue(obj) ?? "(null)"; 
      sb.AppendLine(prop.Name + ": " + value.ToString()); 
     } 
     return sb.ToString(); 
} 
Cuestiones relacionadas