copiar una clase, C #

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

¿Probablemente estás hablando de una copia profunda ( copia profunda frente a copia superficial )?

O tienes que:

  1. implementar (código duro) un método propio,
  2. intente implementar (o encontrar) una implementación que use Reflection o Emit para hacerlo dinámicamente (se explica aquí ),
  3. use la serialización y la deserialización para crear una copia profunda, si el objeto está marcado con un atributo [Serializable] .
 public static T DeepCopy(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 usar el método Object.MemberwiseClone() , pero es un método protegido, lo que significa que solo puede usarlo dentro de la clase.

Con todos los métodos de copia profunda, es importante tener en cuenta cualquier referencia a otros objetos, o referencias circulares que puedan resultar en la creación de una copia más profunda de lo que desea.

No todas las clases tienen esta funcionalidad. Probablemente, si una clase lo hace, proporciona un método de Clone . Para ayudar a implementar ese método para sus propias clases, hay 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).

Si su clase acaba de obtener propiedades, podría 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 recrearlos en una nueva clase utilizando reflexiones. Espero que esto ayude

Si y no. Esta es un área en la que debes tener un poco de cuidado porque hay algunas trampas (y, de nuevo, no es difícil).

En primer lugar, si se asoma un poco en la Biblioteca de clases base (BCL), puede descubrir la interfaz ICloneable . No todos los tipos implementan esta interfaz, pero aquellos que sí tienen un método de clonación que devolverá una nueva instancia del mismo tipo con (probablemente) los mismos valores.

Sin embargo, aquí hay una trampa: la interfaz ICloneable no especifica suficientemente si se espera un clon profundo o un clon poco profundo, 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 activamente su uso posterior; consulte las excelentes Pautas de diseño de marcos para 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 usted controla el tipo que desea clonar.

Si desea clonar un tipo que no puede modificar y 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.

Creo que el autor está preguntando acerca de los constructores de copia …

La respuesta es “sí, pero solo si lo implementas tú mismo”, no hay una forma “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 a tener en cuenta a este respecto es la interfaz IClonable , y el método .Clone() que proporciona. También el método protegido .MemberwiseClone() proporcionado por la clase Object puede ayudar a implementar ambos métodos.

¿Qué tal con JSON?

Puede obtener el paquete JSON desde: https://www.nuget.org/packages/Newtonsoft.Json/

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

No hay una manera directa que siempre funcionará. 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 superficial, puede probar Object.MemberwiseClone () . Sin embargo, es un método protegido y solo puedes usarlo dentro de la clase.

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

Una posibilidad es clonarlo . Tienes que implementar la interfaz ICloneable y el método de Clone .

¿Quieres decir un constructor de copia, como si existiera en C ++?

No existe en C #. Lo que puede hacer es escribir el suyo propio (lo cual es tedioso), o puede escribir un método ‘Clonar’ que use 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 su clase tiene que ser serializable.

Una forma fácil de clonar un objeto es escribirlo en una secuencia 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 tenga en cuenta que esto puede causar problemas con el llamado objeto agregado.

¿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 que no se sabe al construir un nuevo objeto:

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

Llamar usando:

 MyClass us = them.Clone(); 

~~~

Algunas soluciones razonables aquí, la serialización son una forma válida de hacerlo, así como un método de clonación para cada clase … Preparé esto y parece funcionar para las pocas pruebas que hice en él

 using System.Reflection; using System.Text.StringBuilder(); public static T CopyClass2(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í está la versión más detallada, supongo, más arriba sugiere que usted entiende las expresiones Lambda, que no es común. Si respeta la legibilidad (que es totalmente válida) más usaría a continuación

 public static T CopyClass(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; 

}

Use este método para enumerar las propiedades para ver si se copió a una nueva referencia .. simple no lista propiedades de ninguna primitiva (tipos estructurados) .. así obtendrá un nombre de clase

 public static string ListAllProperties(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(); }