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:
-
A
es una clase grande y contiene muchos tipos de referencia - No puedo marcar
B
como[Serializable]
ya que no tengo acceso al código fuente deB
Los siguientes métodos para realizar una copia profunda no funcionan:
- No puedo usar
ICloneable
oMemberwiseClone
ya que la claseA
contiene muchos tipos de referencia - No puedo escribir un constructor de copia para
A
, ya que la clase es grande y se agrega continuamente, y contiene clases (comoB
) que no se pueden copiar en profundidad - 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
?
¿No puedes hacer esto?
[Serializable]
class A
{
...
[NonSerialized]
public List<B> ListB;
....
}
Y luego consulte ¿Cómo se hace una copia en profundidad de un objeto en .NET (C # específicamente)? para una función de clonación
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;
}
}
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