c# - otro - copia de objetos
Clonación profunda más rápida (9)
¿Alguien quiere un marco / clase que me permita clonar por objetos .Net de valores? Solo me interesan las propiedades de lectura / escritura públicas (es decir, los Contratos de datos), y no me importa si las referencias se resuelven correctamente (es decir, las recopilaciones que contienen la misma instancia del elemento dos veces).
Intenté el truco de serialización a través de DataContractSerializer
(serializar a XML y viceversa), escribí la clase de clonación basada en la reflexión (a veces más rápida / a veces más lenta), y me preguntaba si alguien escribió una clase auxiliar que pueda hacerlo a través de Emit y no la reflexión. En cuanto a ahora, emitir IL es un poco demasiado para mi pequeño cerebro, pero creo que esta sería la solución definitiva. A menos que alguien conozca un método alternativo que sea más rápido que DataContractSerializer.
¡Bien! puede escribir su propio método de clonación que puede especificar para ignorar o incluir propiedades por sus atributos. Mi nueva biblioteca en el enlace hacia abajo, utiliza la reflexión y FieldInfo para clonar el objeto de forma recursiva. Lo he agregado a CodeProject para que tenga acceso a su código pronto, lo que podría modificarlo según sus necesidades.
Pruébalo es muy rápido y limpio, te encantará.
https://www.nuget.org/packages/FastDeepCloner/1.0.1
o
PM> Install-Package FastDeepCloner
El generador de código CGbR puede generar una implementación de ICloneable
para usted. Todo lo que necesita es el paquete nuget y una definición de clase parcial que implementa ICloneable
. El generador hará el resto por ti:
public partial class Root : ICloneable
{
public Root(int number)
{
_number = number;
}
private int _number;
public Partial[] Partials { get; set; }
public IList<ulong> Numbers { get; set; }
public object Clone()
{
return Clone(true);
}
private Root()
{
}
}
public partial class Root
{
public Root Clone(bool deep)
{
var copy = new Root();
// All value types can be simply copied
copy._number = _number;
if (deep)
{
// In a deep clone the references are cloned
var tempPartials = new Partial[Partials.Length];
for (var i = 0; i < Partials.Length; i++)
{
var value = Partials[i];
value = value.Clone(true);
tempPartials[i] = value;
}
copy.Partials = tempPartials;
var tempNumbers = new List<ulong>(Numbers.Count);
for (var i = 0; i < Numbers.Count; i++)
{
var value = Numbers[i];
tempNumbers[i] = value;
}
copy.Numbers = tempNumbers;
}
else
{
// In a shallow clone only references are copied
copy.Partials = Partials;
copy.Numbers = Numbers;
}
return copy;
}
}
Hay muchas bibliotecas que hacen esta operación. Puedes ver los resultados del benchmark here :
En pocas palabras, si necesita un rendimiento, hágalo manualmente, realmente más rápido. Además, algunas bibliotecas permiten realizar una clonación superficial (según la pregunta, es una buena variante para usted), que es más rápida. Y no utilice BinaryFormatter
si necesita algún rendimiento.
Además, @frakon menciona que los árboles de Expresiones tienen la misma velocidad que IL Emit, es un poco incorrecto. El árbol de expresiones es un poco más lento, pero se puede usar en aplicaciones de confianza parcial.
Manual 13ms
DeepCloner (IL Emit) 167ms
DeepCloner (Expresiones) 267ms
Extensiones Clonales (Expresiones) 560ms
NClone 901ms
Clone.Behave! 8551ms
GeorgeCloney 1996ms
Nuclex.Cloning n / a (Crashed)
FastDeepCloner 1882ms
BinaryFormatter 15000ms
He escrito tres métodos de clonación profunda para .NET hace algún tiempo:
Uno utiliza la conocida técnica
BinaryFormatter
(aunque laBinaryFormatter
para que los objetos no necesiten ser serializables para poder clonarlos). Esto fue, con mucho, el más lento.Por el segundo utilicé la reflexión pura. Fue al menos 6 veces más rápido que la clonación con
BinaryFormatter
. Este también podría usarse en Silverlight y .NET Compact Framework.El tercero usa Linq Expression Trees (para la generación de MSIL en tiempo de ejecución). Es 60 veces más rápido que la técnica
BinaryFormatter
pero tiene un tiempo de configuración de aproximadamente 2 milisegundos por primera vez que se encuentra cada clase.
Publiqué los tres métodos de clonación como código abierto aquí:
La serialización basada en el método dinámico será la más rápida. (Genere un método dinámico utilizando código ligero y utilícelo para la serialización)
Puede hacer 1 método por propiedad / campo o un método para todo el objeto. Desde mi punto de referencia, hacer 1 por propiedad no le da demasiado impacto en el rendimiento.
Vea el siguiente código, para ver cómo hago esto en Media Browser: http://code.google.com/p/videobrowser/source/browse/trunk/MediaBrowser/Library/Persistance/Serializer.cs
También hay algunas pruebas unitarias allí.
Hay una muestra de reflexión rápida en el límite de instrucción que hace exactamente lo que usted quiere.
ver:
No sé si esto se ajusta exactamente a sus requisitos, pero también puede crear un clon profundo utilizando un BinaryFormatter
. Vea esta respuesta a una pregunta relacionada (por Binoj Antony ):
public static class GenericCopier<T>
{
public static T DeepCopy(object objectToCopy)
{
using (MemoryStream memoryStream = new MemoryStream())
{
BinaryFormatter binaryFormatter = new BinaryFormatter();
binaryFormatter.Serialize(memoryStream, objectToCopy);
memoryStream.Seek(0, SeekOrigin.Begin);
return (T) binaryFormatter.Deserialize(memoryStream);
}
}
}
Probablemente no haya un código completo de clonación en funcionamiento hecho por IL Emit en Internet.
Pero IL Emit es de la misma velocidad que el código de los árboles de expresión, porque ambos métodos terminan con funciones de copia lambda compiladas similares. Los árboles de expresión son aproximadamente 4 veces más rápidos que la reflexión . Lo mejor es que la función de clonación general de Expression Trees está disponible en Internet .
Cygon ya mencionó una implementación mediante árboles de expresión. Se puede encontrar una implementación completamente probada en el artículo de CodeProject Fast Deep Copy por Expression Trees (C #) .
Usa el método de extensión por
var copy = originalObject.DeepCopyByExpressionTree();
Pruebe AutoMapper o BLToolkit Mapping
Si estás hablando de un árbol de objetos / gráfico:
Escribir IL específico para serializar un objeto es complicado. En mi opinión, lo mejor es observar una serialización completa, como la forma en que funcionaría DataContractSerializer
, pero no necesariamente con ese motor.
Por ejemplo, protobuf-net tiene un método Serializer.DeepClone<T>
que podría ayudar. Debería ser más rápido que DataContractSerializer
, al menos. En el momento actual, debe agregar algunas pistas para el serializador (incluso si solo [ProtoContract(ImplicitFields=ImplicitFields.AllPublic)]
) - sin embargo, el trabajo en curso (incompleto) ofrece soporte de POCO sin atributos.
Si estás hablando de objetos individuales:
Hay cosas bastante simples que puedes hacer aquí con Expression
en .NET 3.5; construir una Expression
dinámica basada en la reflexión y llamar a .Compile()
. MiscUtil ya tiene esto:
DestType clone = PropertyCopy<DestType>.CopyFrom(original);
Con .NET 2.0 / 3.0 (sin Expression
) puede considerar HyperDescriptor para propósitos similares.