Clonación profunda de objetos


Quiero hacer algo como:

MyObject myObj = GetMyObj(); // Create and fill a new object
MyObject newObj = myObj.Clone();

Y luego realice cambios en el nuevo objeto que no se reflejen en el objeto original.

A menudo no necesito esta funcionalidad, por lo que cuando ha sido necesario, he recurrido a crear un nuevo objeto y luego copiar cada propiedad individualmente, pero siempre me deja con la sensación de que hay una forma mejor o más elegante de manejar la situación.

¿Cómo puedo clonar o copiar profundamente un objeto para que el objeto clonado pueda ser modificado sin que ningún cambio se refleje en el objeto original?

 1883
Author: poke, 2008-09-17

30 answers

Mientras que la práctica estándar es implementar el ICloneable interfaz (descrita aquí , así que no regurgitaré), aquí hay una bonita copiadora de objetos clon profunda que encontré en El Proyecto de Código hace un tiempo y la incorporé a nuestras cosas.

Como se mencionó en otra parte, requiere que sus objetos sean serializables.

using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

/// <summary>
/// Reference Article http://www.codeproject.com/KB/tips/SerializedObjectCloner.aspx
/// Provides a method for performing a deep copy of an object.
/// Binary Serialization is used to perform the copy.
/// </summary>
public static class ObjectCopier
{
    /// <summary>
    /// Perform a deep Copy of the object.
    /// </summary>
    /// <typeparam name="T">The type of object being copied.</typeparam>
    /// <param name="source">The object instance to copy.</param>
    /// <returns>The copied object.</returns>
    public static T Clone<T>(T source)
    {
        if (!typeof(T).IsSerializable)
        {
            throw new ArgumentException("The type must be serializable.", "source");
        }

        // Don't serialize a null object, simply return the default for that object
        if (Object.ReferenceEquals(source, null))
        {
            return default(T);
        }

        IFormatter formatter = new BinaryFormatter();
        Stream stream = new MemoryStream();
        using (stream)
        {
            formatter.Serialize(stream, source);
            stream.Seek(0, SeekOrigin.Begin);
            return (T)formatter.Deserialize(stream);
        }
    }
}

La idea es serializar tu objeto y luego deserializarlo en un objeto nuevo. El beneficio es que usted no tiene que preocuparse usted mismo acerca de la clonación de todo cuando un objeto se vuelve demasiado complejo.

Y con el uso de métodos de extensión (también de la fuente referenciada originalmente):

En caso de que prefiera utilizar los nuevos métodos de extensión de C# 3.0, cambie el método para tener la siguiente firma:

public static T Clone<T>(this T source)
{
   //...
}

Ahora la llamada al método simplemente se convierte en objectBeingCloned.Clone();.

EDIT (10 de enero de 2015) Pensé en volver a esto, para mencionar que recientemente empecé a usar (Newtonsoft) Json para hacer esto, debe ser más ligero, y evita la sobrecarga de etiquetas [Serializables]. (NB @atconway ha señalado en los comentarios que los miembros privados no se clonan utilizando el método JSON)

/// <summary>
/// Perform a deep Copy of the object, using Json as a serialisation method. NOTE: Private members are not cloned using this method.
/// </summary>
/// <typeparam name="T">The type of object being copied.</typeparam>
/// <param name="source">The object instance to copy.</param>
/// <returns>The copied object.</returns>
public static T CloneJson<T>(this T source)
{            
    // Don't serialize a null object, simply return the default for that object
    if (Object.ReferenceEquals(source, null))
    {
        return default(T);
    }

    // initialize inner objects individually
    // for example in default constructor some list property initialized with some values,
    // but in 'source' these items are cleaned -
    // without ObjectCreationHandling.Replace default constructor values will be added to result
    var deserializeSettings = new JsonSerializerSettings {ObjectCreationHandling = ObjectCreationHandling.Replace};

    return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source), deserializeSettings);
}
 1495
Author: johnc,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:18:36

Quería un clonador para objetos muy simples de primitivas y listas en su mayoría. Si su objeto está fuera de la caja JSON serializable entonces este método hará el truco. Esto no requiere ninguna modificación o implementación de interfaces en la clase clonada, solo un serializador JSON como JSON.NET.

public static T Clone<T>(T source)
{
    var serialized = JsonConvert.SerializeObject(source);
    return JsonConvert.DeserializeObject<T>(serialized);
}

También puede utilizar este método de extensión

public static class SystemExtension
{
    public static T Clone<T>(this T source)
    {
        var serialized = JsonConvert.SerializeObject(source);
        return JsonConvert.DeserializeObject<T>(serialized);
    }
}
 192
Author: craastad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-14 06:16:54

La razón para no usar ICloneable es no porque no tiene una interfaz genérica. La razón para no usarlo es porque es vago. No deja claro si está obteniendo una copia superficial o profunda; eso depende del implementador.

Sí, MemberwiseClone hace una copia superficial, pero lo contrario de MemberwiseClone no es Clone; sería, quizás, DeepClone, que no existe. Cuando se utiliza un objeto a través de su interfaz ICloneable, no se puede saber qué tipo de se realiza la clonación del objeto subyacente. (Y los comentarios XML no lo dejarán claro, porque obtendrás los comentarios de la interfaz en lugar de los del método de clonación del objeto.)

Lo que normalmente hago es simplemente hacer un método Copy que hace exactamente lo que quiero.

 152
Author: Ryan Lundy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-07 18:37:12

Después de mucho leer sobre muchas de las opciones enlazadas aquí, y las posibles soluciones para este problema, creo que todas las opciones se resumen bastante bien en Ian P ' s link (todas las demás opciones son variaciones de esas) y la mejor solución es proporcionada por Pedro77 enlace sobre los comentarios de la pregunta.

Así que solo copiaré las partes relevantes de esas 2 referencias aquí. De esa manera podemos tener:

Lo mejor que se puede hacer para clonar objetos en c sharp!

En primer lugar, esas son todas nuestras opciones:

El artículo Fast Deep Copy by Expression Trees también tiene comparación de rendimiento de la clonación por Serialización, Reflexión y Árboles de Expresión.

Por qué elijo ICloneable (i. e. manualmente)

El Sr. Venkat Subramaniam (enlace redundante aquí) explica con mucho detalle por qué .

Todo su artículo gira en torno a un ejemplo que intenta ser aplicable a la mayoría de los casos, utilizando 3 objetos: Persona, Cerebro y Ciudad. Queremos clonar a una persona, que tendrá su propio cerebro pero la misma ciudad. Puede imaginar todos los problemas que cualquiera de los otros métodos anteriores puede traer o leer el artículo.

Esta es mi versión ligeramente modificada de su conclusión:

Copiar un objeto especificando New seguido del nombre de la clase a menudo conduce a un código que no es extensible. El uso de clone, la aplicación del patrón prototipo, es una mejor manera de lograr esto. Sin embargo, usar clon como se proporciona en C# (y Java) también puede ser bastante problemático. Es mejor proporcionar un constructor de copia protegido (no público) e invocarlo desde el método clone. Esto nos da la capacidad de delegar la tarea de crear un objeto a una instancia de una clase misma, proporcionando así extensibilidad y también, creando de forma segura los objetos usando el constructor de copia protegida.

Esperemos que esta implementación pueda aclarar las cosas:

public class Person : ICloneable
{
    private final Brain brain; // brain is final since I do not want 
                // any transplant on it once created!
    private int age;
    public Person(Brain aBrain, int theAge)
    {
        brain = aBrain; 
        age = theAge;
    }
    protected Person(Person another)
    {
        Brain refBrain = null;
        try
        {
            refBrain = (Brain) another.brain.clone();
            // You can set the brain in the constructor
        }
        catch(CloneNotSupportedException e) {}
        brain = refBrain;
        age = another.age;
    }
    public String toString()
    {
        return "This is person with " + brain;
        // Not meant to sound rude as it reads!
    }
    public Object clone()
    {
        return new Person(this);
    }
    …
}

Ahora considere tener una clase derivada de Persona.

public class SkilledPerson extends Person
{
    private String theSkills;
    public SkilledPerson(Brain aBrain, int theAge, String skills)
    {
        super(aBrain, theAge);
        theSkills = skills;
    }
    protected SkilledPerson(SkilledPerson another)
    {
        super(another);
        theSkills = another.theSkills;
    }

    public Object clone()
    {
        return new SkilledPerson(this);
    }
    public String toString()
    {
        return "SkilledPerson: " + super.toString();
    }
}

Puede intentar ejecutar el siguiente código:

public class User
{
    public static void play(Person p)
    {
        Person another = (Person) p.clone();
        System.out.println(p);
        System.out.println(another);
    }
    public static void main(String[] args)
    {
        Person sam = new Person(new Brain(), 1);
        play(sam);
        SkilledPerson bob = new SkilledPerson(new SmarterBrain(), 1, "Writer");
        play(bob);
    }
}

La salida producida será:

This is person with Brain@1fcc69
This is person with Brain@253498
SkilledPerson: This is person with SmarterBrain@1fef6f
SkilledPerson: This is person with SmarterBrain@209f4e

Observe que, si mantenemos un recuento del número de objetos, el clon implementado aquí mantenga un recuento correcto del número de objetos.

 90
Author: cregox,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-23 20:30:46

Prefiero un constructor de copia a un clon. La intención es más clara.

 70
Author: Nick,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-12-03 17:21:32

Método de extensión simple para copiar todas las propiedades públicas. Funciona para cualquier objeto y no requiere que la clase sea [Serializable]. Se puede ampliar para otro nivel de acceso.

public static void CopyTo( this object S, object T )
{
    foreach( var pS in S.GetType().GetProperties() )
    {
        foreach( var pT in T.GetType().GetProperties() )
        {
            if( pT.Name != pS.Name ) continue;
            ( pT.GetSetMethod() ).Invoke( T, new object[] 
            { pS.GetGetMethod().Invoke( S, null ) } );
        }
    };
}
 35
Author: Konstantin Salavatov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-04-07 12:59:49

Bueno estaba teniendo problemas usando ICloneable en Silverlight, pero me gustó la idea de seralización, puedo seralizar XML, así que hice esto:

static public class SerializeHelper
{
    //Michael White, Holly Springs Consulting, 2009
    //[email protected]
    public static T DeserializeXML<T>(string xmlData) where T:new()
    {
        if (string.IsNullOrEmpty(xmlData))
            return default(T);

        TextReader tr = new StringReader(xmlData);
        T DocItms = new T();
        XmlSerializer xms = new XmlSerializer(DocItms.GetType());
        DocItms = (T)xms.Deserialize(tr);

        return DocItms == null ? default(T) : DocItms;
    }

    public static string SeralizeObjectToXML<T>(T xmlObject)
    {
        StringBuilder sbTR = new StringBuilder();
        XmlSerializer xmsTR = new XmlSerializer(xmlObject.GetType());
        XmlWriterSettings xwsTR = new XmlWriterSettings();

        XmlWriter xmwTR = XmlWriter.Create(sbTR, xwsTR);
        xmsTR.Serialize(xmwTR,xmlObject);

        return sbTR.ToString();
    }

    public static T CloneObject<T>(T objClone) where T:new()
    {
        string GetString = SerializeHelper.SeralizeObjectToXML<T>(objClone);
        return SerializeHelper.DeserializeXML<T>(GetString);
    }
}
 28
Author: Michael White,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-21 14:11:33

Si ya estás usando una aplicación de terceros como ValueInjecter o Automapper , puedes hacer algo como esto:

MyObject oldObj; // The existing object to clone

MyObject newObj = new MyObject();
newObj.InjectFrom(oldObj); // Using ValueInjecter syntax

Usando este método no tienes que implementar ISerializable o ICloneable en tus objetos. Esto es común con el patrón MVC / MVVM, por lo que se han creado herramientas simples como esta.

Ver la solución de clonación profunda valueinjecter en CodePlex.

 26
Author: Michael Cox,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-19 22:57:37

Acabo de crear CloneExtensions biblioteca proyecto. Realiza clon rápido y profundo utilizando operaciones de asignación simples generadas por la compilación de código de tiempo de ejecución del árbol de expresiones.

¿Cómo usarlo?

En lugar de escribir sus propios métodos Clone o Copy con un tono de asignaciones entre campos y propiedades, haga que el programa lo haga por sí mismo, utilizando el Árbol de expresiones. GetClone<T>() método marcado como método de extensión le permite simplemente llamarlo en su instancia:

var newInstance = source.GetClone();

Puede elegir lo que se debe copiar de source a newInstance usando CloningFlags enumeración:

var newInstance 
    = source.GetClone(CloningFlags.Properties | CloningFlags.CollectionItems);

¿Qué se puede clonar?

  • Primitivo (int, uint, byte, doble, char, etc.), conocido inmutable tipos (DateTime, TimeSpan, String) y delegados (incluyendo Acción, Func, etc)
  • Nullable
  • T [] matrices
  • Clases y estructuras personalizadas, incluyendo clases y estructuras genéricas.

Los siguientes miembros de clase/estructura son clonado internamente:

  • Valores de los campos public, no readonly
  • Valores de las propiedades públicas con los accesores get y set
  • Elementos de colección para tipos que implementan ICollection

Qué tan rápido es?

La solución es más rápida que la reflexión, porque la información de los miembros debe recopilarse solo una vez, antes de que GetClone<T> se use por primera vez para un tipo dado T.

También es más rápido que la solución basada en serialización cuando clona más instancias de pareja del mismo tipo T.

Y más...

Lea más sobre las expresiones generadas en la documentación .

Lista de depuración de expresiones de ejemplo para List<int>:

.Lambda #Lambda1<System.Func`4[System.Collections.Generic.List`1[System.Int32],CloneExtensions.CloningFlags,System.Collections.Generic.IDictionary`2[System.Type,System.Func`2[System.Object,System.Object]],System.Collections.Generic.List`1[System.Int32]]>(
    System.Collections.Generic.List`1[System.Int32] $source,
    CloneExtensions.CloningFlags $flags,
    System.Collections.Generic.IDictionary`2[System.Type,System.Func`2[System.Object,System.Object]] $initializers) {
    .Block(System.Collections.Generic.List`1[System.Int32] $target) {
        .If ($source == null) {
            .Return #Label1 { null }
        } .Else {
            .Default(System.Void)
        };
        .If (
            .Call $initializers.ContainsKey(.Constant<System.Type>(System.Collections.Generic.List`1[System.Int32]))
        ) {
            $target = (System.Collections.Generic.List`1[System.Int32]).Call ($initializers.Item[.Constant<System.Type>(System.Collections.Generic.List`1[System.Int32])]
            ).Invoke((System.Object)$source)
        } .Else {
            $target = .New System.Collections.Generic.List`1[System.Int32]()
        };
        .If (
            ((System.Byte)$flags & (System.Byte).Constant<CloneExtensions.CloningFlags>(Fields)) == (System.Byte).Constant<CloneExtensions.CloningFlags>(Fields)
        ) {
            .Default(System.Void)
        } .Else {
            .Default(System.Void)
        };
        .If (
            ((System.Byte)$flags & (System.Byte).Constant<CloneExtensions.CloningFlags>(Properties)) == (System.Byte).Constant<CloneExtensions.CloningFlags>(Properties)
        ) {
            .Block() {
                $target.Capacity = .Call CloneExtensions.CloneFactory.GetClone(
                    $source.Capacity,
                    $flags,
                    $initializers)
            }
        } .Else {
            .Default(System.Void)
        };
        .If (
            ((System.Byte)$flags & (System.Byte).Constant<CloneExtensions.CloningFlags>(CollectionItems)) == (System.Byte).Constant<CloneExtensions.CloningFlags>(CollectionItems)
        ) {
            .Block(
                System.Collections.Generic.IEnumerator`1[System.Int32] $var1,
                System.Collections.Generic.ICollection`1[System.Int32] $var2) {
                $var1 = (System.Collections.Generic.IEnumerator`1[System.Int32]).Call $source.GetEnumerator();
                $var2 = (System.Collections.Generic.ICollection`1[System.Int32])$target;
                .Loop  {
                    .If (.Call $var1.MoveNext() != False) {
                        .Call $var2.Add(.Call CloneExtensions.CloneFactory.GetClone(
                                $var1.Current,
                                $flags,


                         $initializers))
                } .Else {
                    .Break #Label2 { }
                }
            }
            .LabelTarget #Label2:
        }
    } .Else {
        .Default(System.Void)
    };
    .Label
        $target
    .LabelTarget #Label1:
}

}

Lo que tiene el mismo significado como el siguiente código c#:

(source, flags, initializers) =>
{
    if(source == null)
        return null;

    if(initializers.ContainsKey(typeof(List<int>))
        target = (List<int>)initializers[typeof(List<int>)].Invoke((object)source);
    else
        target = new List<int>();

    if((flags & CloningFlags.Properties) == CloningFlags.Properties)
    {
        target.Capacity = target.Capacity.GetClone(flags, initializers);
    }

    if((flags & CloningFlags.CollectionItems) == CloningFlags.CollectionItems)
    {
        var targetCollection = (ICollection<int>)target;
        foreach(var item in (ICollection<int>)source)
        {
            targetCollection.Add(item.Clone(flags, initializers));
        }
    }

    return target;
}

¿No es exactamente como escribirías tu propio método Clone para List<int>?

 25
Author: MarcinJuraszek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-09-08 15:26:38

La respuesta corta es que hereda de la interfaz ICloneable y luego implementa la .función clonar. Clone debe hacer una copia de memberwise y realizar una copia profunda en cualquier miembro que lo requiera, luego devolver el objeto resultante. Esta es una operación recursiva (requiere que todos los miembros de la clase que desea clonar sean tipos de valor o implementen ICloneable y que sus miembros sean tipos de valor o implementen ICloneable, y así sucesivamente).

Para una explicación más detallada sobre Clonación usando ICloneable, echa un vistazo a este artículo.

La respuesta long es "depende". Como han mencionado otros, ICloneable no está soportado por genéricos, requiere consideraciones especiales para referencias de clase circulares, y en realidad es visto por algunos como un "error" en.NET Framework. El método de serialización depende de que sus objetos sean serializables, lo que puede no ser y que usted no tenga control sobre ellos. Todavía hay mucho debate en la comunidad sobre que es la "mejor" práctica. En realidad, ninguna de las soluciones son las mejores prácticas de talla única para todas las situaciones como ICloneable se interpretó originalmente.

Consulte el artículo de this Developer's Corner para ver algunas opciones más (crédito para Ian).

 20
Author: Zach Burlingame,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-09 22:46:07

Si desea una clonación verdadera a tipos desconocidos, puede echar un vistazo a fastclone.

Eso es la clonación basada en expresiones que funciona aproximadamente 10 veces más rápido que la serialización binaria y mantiene la integridad completa del gráfico de objetos.

Eso significa: si hace referencia varias veces al mismo objeto en su hierachy, el clon también tendrá una sola instancia referenciada.

No hay necesidad de interfaces, atributos o cualquier otra modificación a los objetos que se clonar.

 15
Author: Michael Sander,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-12 20:07:36

Lo mejor es implementar un método de extensión como

public static T DeepClone<T>(this T originalObject)
{ /* the cloning code */ }

Y luego usarlo en cualquier parte de la solución por

var copy = anyObject.DeepClone();

Podemos tener las siguientes tres implementaciones:

  1. Por Serialización (el código más corto)
  2. Por la Reflexión - 5 veces más rápido
  3. Por Árboles de Expresión - 20 veces más rápido

Todos los métodos vinculados están funcionando bien y fueron profundamente probado.

 15
Author: frakon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:55:19
  1. Básicamente necesita implementar la interfaz ICloneable y luego realizar la copia de la estructura de objetos.
  2. Si es una copia profunda de todos los miembros, debe asegurarse (no relacionándose con la solución que elija) de que todos los niños también sean clonables.
  3. A veces debe tener en cuenta alguna restricción durante este proceso, por ejemplo, si copia los objetos OR la mayoría de los frameworks solo permiten un objeto adjunto a la sesión y NO DEBE hacer clones de este objeto, o si es es posible que deba preocuparse por la fijación de sesiones de estos objetos.

Saludos.

 15
Author: dimarzionist,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-07-03 07:25:13

Se me ocurrió esto para superar un defecto . NET tener que copiar manualmente la lista profunda.

Yo uso esto:

static public IEnumerable<SpotPlacement> CloneList(List<SpotPlacement> spotPlacements)
{
    foreach (SpotPlacement sp in spotPlacements)
    {
        yield return (SpotPlacement)sp.Clone();
    }
}

Y en otro lugar:

public object Clone()
{
    OrderItem newOrderItem = new OrderItem();
    ...
    newOrderItem._exactPlacements.AddRange(SpotPlacement.CloneList(_exactPlacements));
    ...
    return newOrderItem;
}

Traté de llegar a un oneliner que hace esto, pero no es posible, debido a que el rendimiento no funciona dentro de bloques de métodos anónimos.

Mejor aún, use lista genérica cloner:

class Utility<T> where T : ICloneable
{
    static public IEnumerable<T> CloneList(List<T> tl)
    {
        foreach (T t in tl)
        {
            yield return (T)t.Clone();
        }
    }
}
 10
Author: Daniel Mošmondor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-12-03 17:23:13

Mantenga las cosas simples y use AutoMapper como otros mencionaron, es una pequeña biblioteca simple para mapear un objeto a otro... Para copiar un objeto a otro con el mismo tipo, todo lo que necesita son tres líneas de código:

MyType source = new MyType();
Mapper.CreateMap<MyType, MyType>();
MyType target = Mapper.Map<MyType, MyType>(source);

El objeto de destino es ahora una copia del objeto de origen. No lo suficientemente simple? Cree un método de extensión para usar en cualquier lugar de su solución:

public static T Copy<T>(this T source)
{
    T copy = default(T);
    Mapper.CreateMap<T, T>();
    copy = Mapper.Map<T, T>(source);
    return copy;
}

Usando el método de extensión, las tres líneas se convierten en una línea:

MyType copy = source.Copy();
 9
Author: Stacked,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-28 11:23:05

En general, usted implementa la interfaz ICloneable e implementa el Clon usted mismo. Los objetos C# tienen un método MemberwiseClone incorporado que realiza una copia superficial que puede ayudarlo con todas las primitivas.

Para una copia profunda, no hay manera de que pueda saber cómo hacerlo automáticamente.

 7
Author: HappyDude,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-17 00:09:02

También lo he visto implementado a través de la reflexión. Básicamente, había un método que habría de recorrer los miembros de un objeto y adecuadamente copiarlos en el nuevo objeto. Cuando llegó a tipos de referencia o colecciones creo que hizo una llamada recursiva sobre sí mismo. La reflexión es cara, pero funcionó bastante bien.

 7
Author: xr280xr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-10-19 13:01:19

Aquí hay una implementación de copia profunda:

public static object CloneObject(object opSource)
{
    //grab the type and create a new instance of that type
    Type opSourceType = opSource.GetType();
    object opTarget = CreateInstanceOfType(opSourceType);

    //grab the properties
    PropertyInfo[] opPropertyInfo = opSourceType.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

    //iterate over the properties and if it has a 'set' method assign it from the source TO the target
    foreach (PropertyInfo item in opPropertyInfo)
    {
        if (item.CanWrite)
        {
            //value types can simply be 'set'
            if (item.PropertyType.IsValueType || item.PropertyType.IsEnum || item.PropertyType.Equals(typeof(System.String)))
            {
                item.SetValue(opTarget, item.GetValue(opSource, null), null);
            }
            //object/complex types need to recursively call this method until the end of the tree is reached
            else
            {
                object opPropertyValue = item.GetValue(opSource, null);
                if (opPropertyValue == null)
                {
                    item.SetValue(opTarget, null, null);
                }
                else
                {
                    item.SetValue(opTarget, CloneObject(opPropertyValue), null);
                }
            }
        }
    }
    //return the new item
    return opTarget;
}
 7
Author: dougajmcdonald,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-09-06 07:38:32

P. ¿Por qué elegiría esta respuesta?

  • Elija esta respuesta si desea la velocidad más rápida de la que.NET es capaz.
  • Ignora esta respuesta si quieres un método de clonación muy, muy fácil.

En otras palabras, ve con otra respuesta a menos que tengas un cuello de botella de rendimiento que necesite arreglarse, y puedes probarlo con un perfilador.

10 veces más rápido que otros métodos

El siguiente método para realizar un clon profundo is:

  • 10 veces más rápido que cualquier cosa que implique serialización/deserialización;
  • Bastante cerca de la velocidad máxima teórica de la que.NET es capaz.

Y el método ...

Para la velocidad máxima, puede usar MemberwiseClone anidado para hacer una copia profunda. Es casi la misma velocidad que copiar una estructura de valor, y es mucho más rápido que (a) reflexión o (b) serialización (como se describe en otras respuestas en esta página).

Tenga en cuenta que si utiliza MemberwiseClone anidado para una copia profunda, tiene que implementar manualmente un ShallowCopy para cada nivel anidado en la clase, y un DeepCopy que llama a todos los métodos ShallowCopy para crear un clon completo. Esto es simple: solo unas pocas líneas en total, vea el código de demostración a continuación.

Aquí está la salida del código que muestra la diferencia de rendimiento relativa para 100.000 clones:

  • 1.08 segundos para MemberwiseClone anidado en anidado estructuras
  • 4.77 segundos para MemberwiseClone anidado en clases anidadas
  • 39,93 segundos para Serialización / Deserialización

Usar MemberwiseClone anidado en una clase casi tan rápido como copiar una estructura, y copiar una estructura está bastante cerca de la velocidad máxima teórica de la que.NET es capaz.

Demo 1 of shallow and deep copy, using classes and MemberwiseClone:
  Create Bob
    Bob.Age=30, Bob.Purchase.Description=Lamborghini
  Clone Bob >> BobsSon
  Adjust BobsSon details
    BobsSon.Age=2, BobsSon.Purchase.Description=Toy car
  Proof of deep copy: If BobsSon is a true clone, then adjusting BobsSon details will not affect Bob:
    Bob.Age=30, Bob.Purchase.Description=Lamborghini
  Elapsed time: 00:00:04.7795670,30000000

Demo 2 of shallow and deep copy, using structs and value copying:
  Create Bob
    Bob.Age=30, Bob.Purchase.Description=Lamborghini
  Clone Bob >> BobsSon
  Adjust BobsSon details:
    BobsSon.Age=2, BobsSon.Purchase.Description=Toy car
  Proof of deep copy: If BobsSon is a true clone, then adjusting BobsSon details will not affect Bob:
    Bob.Age=30, Bob.Purchase.Description=Lamborghini
  Elapsed time: 00:00:01.0875454,30000000

Demo 3 of deep copy, using class and serialize/deserialize:
  Elapsed time: 00:00:39.9339425,30000000

Para entender cómo hacer una copia profunda usando MemberwiseCopy, aquí está el proyecto de demostración que se utilizó para generar los tiempos anteriores:

// Nested MemberwiseClone example. 
// Added to demo how to deep copy a reference class.
[Serializable] // Not required if using MemberwiseClone, only used for speed comparison using serialization.
public class Person
{
    public Person(int age, string description)
    {
        this.Age = age;
        this.Purchase.Description = description;
    }
    [Serializable] // Not required if using MemberwiseClone
    public class PurchaseType
    {
        public string Description;
        public PurchaseType ShallowCopy()
        {
            return (PurchaseType)this.MemberwiseClone();
        }
    }
    public PurchaseType Purchase = new PurchaseType();
    public int Age;
    // Add this if using nested MemberwiseClone.
    // This is a class, which is a reference type, so cloning is more difficult.
    public Person ShallowCopy()
    {
        return (Person)this.MemberwiseClone();
    }
    // Add this if using nested MemberwiseClone.
    // This is a class, which is a reference type, so cloning is more difficult.
    public Person DeepCopy()
    {
            // Clone the root ...
        Person other = (Person) this.MemberwiseClone();
            // ... then clone the nested class.
        other.Purchase = this.Purchase.ShallowCopy();
        return other;
    }
}
// Added to demo how to copy a value struct (this is easy - a deep copy happens by default)
public struct PersonStruct
{
    public PersonStruct(int age, string description)
    {
        this.Age = age;
        this.Purchase.Description = description;
    }
    public struct PurchaseType
    {
        public string Description;
    }
    public PurchaseType Purchase;
    public int Age;
    // This is a struct, which is a value type, so everything is a clone by default.
    public PersonStruct ShallowCopy()
    {
        return (PersonStruct)this;
    }
    // This is a struct, which is a value type, so everything is a clone by default.
    public PersonStruct DeepCopy()
    {
        return (PersonStruct)this;
    }
}
// Added only for a speed comparison.
public class MyDeepCopy
{
    public static T DeepCopy<T>(T obj)
    {
        object result = null;
        using (var ms = new MemoryStream())
        {
            var formatter = new BinaryFormatter();
            formatter.Serialize(ms, obj);
            ms.Position = 0;
            result = (T)formatter.Deserialize(ms);
            ms.Close();
        }
        return (T)result;
    }
}

Entonces, llame a la demo desde main:

void MyMain(string[] args)
{
    {
        Console.Write("Demo 1 of shallow and deep copy, using classes and MemberwiseCopy:\n");
        var Bob = new Person(30, "Lamborghini");
        Console.Write("  Create Bob\n");
        Console.Write("    Bob.Age={0}, Bob.Purchase.Description={1}\n", Bob.Age, Bob.Purchase.Description);
        Console.Write("  Clone Bob >> BobsSon\n");
        var BobsSon = Bob.DeepCopy();
        Console.Write("  Adjust BobsSon details\n");
        BobsSon.Age = 2;
        BobsSon.Purchase.Description = "Toy car";
        Console.Write("    BobsSon.Age={0}, BobsSon.Purchase.Description={1}\n", BobsSon.Age, BobsSon.Purchase.Description);
        Console.Write("  Proof of deep copy: If BobsSon is a true clone, then adjusting BobsSon details will not affect Bob:\n");
        Console.Write("    Bob.Age={0}, Bob.Purchase.Description={1}\n", Bob.Age, Bob.Purchase.Description);
        Debug.Assert(Bob.Age == 30);
        Debug.Assert(Bob.Purchase.Description == "Lamborghini");
        var sw = new Stopwatch();
        sw.Start();
        int total = 0;
        for (int i = 0; i < 100000; i++)
        {
            var n = Bob.DeepCopy();
            total += n.Age;
        }
        Console.Write("  Elapsed time: {0},{1}\n\n", sw.Elapsed, total);
    }
    {               
        Console.Write("Demo 2 of shallow and deep copy, using structs:\n");
        var Bob = new PersonStruct(30, "Lamborghini");
        Console.Write("  Create Bob\n");
        Console.Write("    Bob.Age={0}, Bob.Purchase.Description={1}\n", Bob.Age, Bob.Purchase.Description);
        Console.Write("  Clone Bob >> BobsSon\n");
        var BobsSon = Bob.DeepCopy();
        Console.Write("  Adjust BobsSon details:\n");
        BobsSon.Age = 2;
        BobsSon.Purchase.Description = "Toy car";
        Console.Write("    BobsSon.Age={0}, BobsSon.Purchase.Description={1}\n", BobsSon.Age, BobsSon.Purchase.Description);
        Console.Write("  Proof of deep copy: If BobsSon is a true clone, then adjusting BobsSon details will not affect Bob:\n");
        Console.Write("    Bob.Age={0}, Bob.Purchase.Description={1}\n", Bob.Age, Bob.Purchase.Description);                
        Debug.Assert(Bob.Age == 30);
        Debug.Assert(Bob.Purchase.Description == "Lamborghini");
        var sw = new Stopwatch();
        sw.Start();
        int total = 0;
        for (int i = 0; i < 100000; i++)
        {
            var n = Bob.DeepCopy();
            total += n.Age;
        }
        Console.Write("  Elapsed time: {0},{1}\n\n", sw.Elapsed, total);
    }
    {
        Console.Write("Demo 3 of deep copy, using class and serialize/deserialize:\n");
        int total = 0;
        var sw = new Stopwatch();
        sw.Start();
        var Bob = new Person(30, "Lamborghini");
        for (int i = 0; i < 100000; i++)
        {
            var BobsSon = MyDeepCopy.DeepCopy<Person>(Bob);
            total += BobsSon.Age;
        }
        Console.Write("  Elapsed time: {0},{1}\n", sw.Elapsed, total);
    }
    Console.ReadKey();
}

De nuevo, tenga en cuenta que si utiliza MemberwiseClone anidado para una copia profunda, tiene que implementar manualmente un ShallowCopy para cada nivel anidado en la clase, y un DeepCopy que llama a todos los métodos ShallowCopy mencionados para crear un clon completo. Esto es simple: solo unas pocas líneas en total, vea el código de demostración anterior.

Tipos de valor vs. Tipos de referencias

Tenga en cuenta que cuando se trata de clonar un objeto, hay una gran la diferencia entre una "struct" y "clase":

  • Si tienes una "struct", es un tipo de valor así que simplemente puedes copiarla, y el contenido será clonado (pero solo hará un clon superficial a menos que uses las técnicas en este post).
  • Si tiene una "clase", es un tipo de referencia , por lo que si lo copia, todo lo que está haciendo es copiar el puntero a él. Para crear un verdadero clon, tienes que ser más creativo, y utilice diferencias entre los tipos de valor y los tipos de referencias que crean otra copia del objeto original en memoria.

Ver diferencias entre los tipos de valores y los tipos de referencias.

Sumas de comprobación para ayudar en la depuración

  • Clonar objetos incorrectamente puede llevar a errores muy difíciles de identificar. En código de producción, tiendo a implementar una suma de comprobación para verificar que el objeto se ha clonado correctamente y no ha sido dañado por otro referencia a ella. Esta suma de comprobación se puede desactivar en el modo Release.
  • Encuentro este método bastante útil: a menudo, solo quieres clonar partes del objeto, no todo.

Realmente útil para desacoplar muchos hilos de muchos otros hilos

Un excelente caso de uso para este código es alimentar clones de una clase o estructura anidada en una cola, para implementar el patrón productor / consumidor.

  • Podemos tener uno (o más) hilos modificando una clase que ellos poseen, luego empujan una copia completa de esta clase en un ConcurrentQueue.
  • Luego tenemos uno (o más) hilos que extraen copias de estas clases y tratan con ellas.

Esto funciona extremadamente bien en la práctica, y nos permite desacoplar muchos hilos (los productores) de uno o más hilos (los consumidores).

Y este método también es cegadoramente rápido: si usamos estructuras anidadas, es 35 veces más rápido que serializar / deserializar clases anidadas, y nos permite aprovechar de todos los hilos disponibles en la máquina.

Actualizar

Aparentemente, ExpressMapper es tan rápido, si no más rápido, que la codificación manual como la anterior. Podría tener que ver cómo se comparan con un perfilador.

 7
Author: Contango,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-09-18 18:12:33

Como no pude encontrar un clonador que cumpla con todos mis requisitos en diferentes proyectos, creé un clonador profundo que se puede configurar y adaptar a diferentes estructuras de código en lugar de adaptar mi código para cumplir con los requisitos de los clonadores. Se consigue añadiendo anotaciones al código que se clonará o simplemente dejando el código como es para tener el comportamiento por defecto. Utiliza reflexión, escribe cachés y se basa en fasterflect . El proceso de clonación es muy rápido para una gran cantidad de datos y una alta jerarquía de objetos (en comparación con otros algoritmos basados en reflexión / serialización).

Https://github.com/kalisohn/CloneBehave

También disponible como paquete nuget: https://www.nuget.org/packages/Clone.Behave/1.0.0

Por ejemplo: El siguiente código hará deepClone Address, pero solo realizará una copia superficial del campo _currentJob.

public class Person 
{
  [DeepClone(DeepCloneBehavior.Shallow)]
  private Job _currentJob;      

  public string Name { get; set; }

  public Job CurrentJob 
  { 
    get{ return _currentJob; }
    set{ _currentJob = value; }
  }

  public Person Manager { get; set; }
}

public class Address 
{      
  public Person PersonLivingHere { get; set; }
}

Address adr = new Address();
adr.PersonLivingHere = new Person("John");
adr.PersonLivingHere.BestFriend = new Person("James");
adr.PersonLivingHere.CurrentJob = new Job("Programmer");

Address adrClone = adr.Clone();

//RESULT
adr.PersonLivingHere == adrClone.PersonLivingHere //false
adr.PersonLivingHere.Manager == adrClone.PersonLivingHere.Manager //false
adr.PersonLivingHere.CurrentJob == adrClone.PersonLivingHere.CurrentJob //true
adr.PersonLivingHere.CurrentJob.AnyProperty == adrClone.PersonLivingHere.CurrentJob.AnyProperty //true
 7
Author: kalisohn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-27 12:53:06

Este método resolvió el problema para mí:

private static MyObj DeepCopy(MyObj source)
        {

            var DeserializeSettings = new JsonSerializerSettings { ObjectCreationHandling = ObjectCreationHandling.Replace };

            return JsonConvert.DeserializeObject<MyObj >(JsonConvert.SerializeObject(source), DeserializeSettings);

        }

Úsalo así: MyObj a = DeepCopy(b);

 6
Author: JerryGoyal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-04-12 13:43:36

Me gustan los Copyconstructors así:

    public AnyObject(AnyObject anyObject)
    {
        foreach (var property in typeof(AnyObject).GetProperties())
        {
            property.SetValue(this, property.GetValue(anyObject));
        }
        foreach (var field in typeof(AnyObject).GetFields())
        {
            field.SetValue(this, field.GetValue(anyObject));
        }
    }

Si tienes más cosas que copiar añádelas

 5
Author: LuckyLikey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-06 15:39:15

Generador de código

Hemos visto un montón de ideas desde la serialización sobre la implementación manual a la reflexión y quiero proponer un enfoque totalmente diferente utilizando el Generador de Código CGbR . El método generate clone es eficiente en memoria y CPU y, por lo tanto, 300 veces más rápido que el DataContractSerializer estándar.

Todo lo que necesita es una definición de clase parcial con ICloneable y el generador hace el resto:

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.Add(value);
            }
            copy.Numbers = tempNumbers;
        }
        else
        {
            // In a shallow clone only references are copied
            copy.Partials = Partials; 
            copy.Numbers = Numbers; 
        }
        return copy;
    }
}

Nota: La última versión tiene un valor más nulo cheques, pero los dejé fuera para una mejor comprensión.

 5
Author: Toxantron,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-09 21:24:27

Aquí una solución rápida y fácil que funcionó para mí sin retransmitir en Serialización/Deserialización.

public class MyClass
{
    public virtual MyClass DeepClone()
    {
        var returnObj = (MyClass)MemberwiseClone();
        var type = returnObj.GetType();
        var fieldInfoArray = type.GetRuntimeFields().ToArray();

        foreach (var fieldInfo in fieldInfoArray)
        {
            object sourceFieldValue = fieldInfo.GetValue(this);
            if (!(sourceFieldValue is MyClass))
            {
                continue;
            }

            var sourceObj = (MyClass)sourceFieldValue;
            var clonedObj = sourceObj.DeepClone();
            fieldInfo.SetValue(returnObj, clonedObj);
        }
        return returnObj;
    }
}

EDITAR : requiere

    using System.Linq;
    using System.Reflection;

Así es como lo usé

public MyClass Clone(MyClass theObjectIneededToClone)
{
    MyClass clonedObj = theObjectIneededToClone.DeepClone();
}
 5
Author: Daniele D.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-29 14:15:45

Siga estos pasos:

  • Define un ISelf<T> con una propiedad de solo lectura Self que devuelve T, y ICloneable<out T>, que deriva de ISelf<T> e incluye un método T Clone().
  • Luego defina un tipo CloneBase que implementa un protected virtual generic VirtualClone casting MemberwiseClone al tipo pasado.
  • Cada tipo derivado debe implementar VirtualClone llamando al método clon base y luego haciendo lo que sea necesario para clonar correctamente aquellos aspectos del tipo derivado que el método padre VirtualClone aún no lo ha manejado.

Para una versatilidad de herencia máxima, las clases que exponen la funcionalidad de clonación pública deben ser sealed, pero derivan de una clase base que es idéntica excepto por la falta de clonación. En lugar de pasar variables del tipo clonable explícito, tome un parámetro de tipo ICloneable<theNonCloneableType>. Esto permitirá que una rutina que espera un derivado cloneable de Foo funcione con un derivado cloneable de DerivedFoo, pero también permitirá la creación de no cloneable derivados de Foo.

 4
Author: supercat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-06-04 14:29:45

Creo que puedes probar esto.

MyObject myObj = GetMyObj(); // Create and fill a new object
MyObject newObj = new MyObject(myObj); //DeepClone it
 4
Author: Sudhanva Kotabagi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-19 16:47:21

He creado una versión de la respuesta aceptada que funciona con '[Serializable]' y '[DataContract]'. Ha pasado un tiempo desde que lo escribí, pero si no recuerdo mal [DataContract] necesitaba un serializador diferente.

Requiere Sistema, System.IO, Sistema.Ejecución.Serialización, Sistema.Ejecución.Serialización.Formateadores.Binario, Sistema.Xml ;

public static class ObjectCopier
{

    /// <summary>
    /// Perform a deep Copy of an object that is marked with '[Serializable]' or '[DataContract]'
    /// </summary>
    /// <typeparam name="T">The type of object being copied.</typeparam>
    /// <param name="source">The object instance to copy.</param>
    /// <returns>The copied object.</returns>
    public static T Clone<T>(T source)
    {
        if (typeof(T).IsSerializable == true)
        {
            return CloneUsingSerializable<T>(source);
        }

        if (IsDataContract(typeof(T)) == true)
        {
            return CloneUsingDataContracts<T>(source);
        }

        throw new ArgumentException("The type must be Serializable or use DataContracts.", "source");
    }


    /// <summary>
    /// Perform a deep Copy of an object that is marked with '[Serializable]'
    /// </summary>
    /// <remarks>
    /// Found on http://stackoverflow.com/questions/78536/cloning-objects-in-c-sharp
    /// Uses code found on CodeProject, which allows free use in third party apps
    /// - http://www.codeproject.com/KB/tips/SerializedObjectCloner.aspx
    /// </remarks>
    /// <typeparam name="T">The type of object being copied.</typeparam>
    /// <param name="source">The object instance to copy.</param>
    /// <returns>The copied object.</returns>
    public static T CloneUsingSerializable<T>(T source)
    {
        if (!typeof(T).IsSerializable)
        {
            throw new ArgumentException("The type must be serializable.", "source");
        }

        // Don't serialize a null object, simply return the default for that object
        if (Object.ReferenceEquals(source, null))
        {
            return default(T);
        }

        IFormatter formatter = new BinaryFormatter();
        Stream stream = new MemoryStream();
        using (stream)
        {
            formatter.Serialize(stream, source);
            stream.Seek(0, SeekOrigin.Begin);
            return (T)formatter.Deserialize(stream);
        }
    }


    /// <summary>
    /// Perform a deep Copy of an object that is marked with '[DataContract]'
    /// </summary>
    /// <typeparam name="T">The type of object being copied.</typeparam>
    /// <param name="source">The object instance to copy.</param>
    /// <returns>The copied object.</returns>
    public static T CloneUsingDataContracts<T>(T source)
    {
        if (IsDataContract(typeof(T)) == false)
        {
            throw new ArgumentException("The type must be a data contract.", "source");
        }

        // ** Don't serialize a null object, simply return the default for that object
        if (Object.ReferenceEquals(source, null))
        {
            return default(T);
        }

        DataContractSerializer dcs = new DataContractSerializer(typeof(T));
        using(Stream stream = new MemoryStream())
        {
            using (XmlDictionaryWriter writer = XmlDictionaryWriter.CreateBinaryWriter(stream))
            {
                dcs.WriteObject(writer, source);
                writer.Flush();
                stream.Seek(0, SeekOrigin.Begin);
                using (XmlDictionaryReader reader = XmlDictionaryReader.CreateBinaryReader(stream, XmlDictionaryReaderQuotas.Max))
                {
                    return (T)dcs.ReadObject(reader);
                }
            }
        }
    }


    /// <summary>
    /// Helper function to check if a class is a [DataContract]
    /// </summary>
    /// <param name="type">The type of the object to check.</param>
    /// <returns>Boolean flag indicating if the class is a DataContract (true) or not (false) </returns>
    public static bool IsDataContract(Type type)
    {
        object[] attributes = type.GetCustomAttributes(typeof(DataContractAttribute), false);
        return attributes.Length == 1;
    }

} 
 3
Author: Jeroen Ritmeijer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-11 16:06:55

Para clonar su objeto de clase puede usar el Objeto.Método MemberwiseClone,

Simplemente agregue esta función a su clase :

public class yourClass
{
    // ...
    // ...

    public yourClass DeepCopy()
    {
        yourClass othercopy = (yourClass)this.MemberwiseClone();
        return othercopy;
    }
}

Luego, para realizar una copia independiente profunda, simplemente llame al método DeepCopy:

yourClass newLine = oldLine.DeepCopy();

Espero que esto ayude.

 3
Author: Chtiwi Malek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-25 09:39:22

Ok, hay algunos ejemplos obvios con reflexión en este post, PERO la reflexión suele ser lenta, hasta que empiezas a almacenarla en caché correctamente.

Si lo cacheas correctamente, entonces clonará profundamente 1000000 objeto por 4,6 s (medido por Watcher).

static readonly Dictionary<Type, PropertyInfo[]> ProperyList = new Dictionary<Type, PropertyInfo[]>();

Luego toma las propiedades en caché o agrega nuevas al diccionario y las usa simplemente

foreach (var prop in propList)
{
        var value = prop.GetValue(source, null);   
        prop.SetValue(copyInstance, value, null);
}

Código completo comprobar en mi post en otra respuesta

Https://stackoverflow.com/a/34365709/4711853

 3
Author: Roma Borodov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:47:36

Si su Árbol de objetos es Serializable, también podría usar algo como esto

static public MyClass Clone(MyClass myClass)
{
    MyClass clone;
    XmlSerializer ser = new XmlSerializer(typeof(MyClass), _xmlAttributeOverrides);
    using (var ms = new MemoryStream())
    {
        ser.Serialize(ms, myClass);
        ms.Position = 0;
        clone = (MyClass)ser.Deserialize(ms);
    }
    return clone;
}

Tenga en cuenta que esta solución es bastante fácil, pero no es tan eficaz como otras soluciones pueden ser.

Y asegúrese de que si la Clase crece, todavía habrá solo aquellos campos clonados, que también se serializan.

 3
Author: LuckyLikey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-21 14:14:04