Pergunta

Eu quero fazer algo como:

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

E, em seguida, fazer alterações para o novo objeto que não são refletidas no objeto original.

Eu muitas vezes não precisar dessa funcionalidade, de modo que, quando necessário, recorri a criação de um novo objeto e, em seguida, copiando cada propriedade individualmente, mas ele sempre me deixa com a sensação de que existe um melhor ou de forma mais elegante de resolver a situação.

Como posso clonar ou profunda copiar um objeto para que o objeto clonado pode ser modificado sem que as alterações sejam refletidas no objeto original?

Foi útil?

Solução

Enquanto a prática padrão é implementar o ICloneable interface (descrito aqui, então eu não vou repetir), aqui está um bom clone profundo objeto copiadora eu encontrei na O Projeto De Código De há um tempo atrás e a incorporou em nosso material.

Como mencionado em outro lugar, ele requer que seus objetos sejam serializáveis.

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);
        }
    }
}

A ideia é que ele serializa o objeto e, em seguida, desserializa-lo em um novo objeto.A vantagem é que você não tem que preocupar-se sobre a clonagem de tudo quando um objeto se torna muito complexo.

E com o uso de métodos de extensão (também do originalmente referenciado fonte):

No caso de você preferir usar o novo métodos de extensão do C# 3.0, altere o método que tem a seguinte assinatura:

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

Agora a chamada do método torna-se simplesmente objectBeingCloned.Clone();.

EDITAR (10 de janeiro de 2015) Pensei em rever isso, falar que eu recentemente comecei a usar (Newtonsoft) Json para fazer isso, ele deve ser mais leve e evita a sobrecarga de [Serializable] tags.(NB @atconway tem apontado nos comentários que os membros privados não são clonados usando o 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);
}

Outras dicas

Eu queria um clone para muito simples objetos, principalmente primitivos e listas.Se o objeto está fora da caixa JSON serializable, em seguida, este método irá fazer o truque.Isso requer nenhuma modificação ou implementação de interfaces no clonado classe, apenas um serializador JSON como JSON.NET.

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

Além disso, você pode usar este método de extensão

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

A razão para não usar ICloneable é não porque ele não tem uma interface genérica. A razão para não usá-lo é porque é vago.Ele não esclarece se você está recebendo um raso ou uma cópia de profundidade;até para o implementador.

Sim, MemberwiseClone faz uma cópia superficial, mas o oposto de MemberwiseClone não Clone;seria, talvez, DeepClone, que não existe.Quando você usa um objeto através de sua interface ICloneable, você não pode saber que tipo de clonagem do objeto subjacente executa.(E XML comentários não deixar claro, porque você vai obter a interface comentários, ao invés de incluir aqueles em que o objeto do método Clone.)

O que eu costumo fazer é simplesmente fazer um Copy o método que faz exatamente o que eu quero.

Depois de muito ler sobre muitas das opções ligados aqui, e possíveis soluções para esse problema, eu acredito todas as opções estão resumidas muito bem no Ian P's link (todas as outras opções são variações desses) e a melhor solução é fornecida pela Pedro77's link sobre a questão comentários.

Então eu vou apenas copiar partes relevantes do que aqueles 2 referências aqui.Dessa forma, podemos ter:

A melhor coisa a fazer para a clonagem de objetos em c sharp!

Em primeiro lugar, essas são todas as nossas opções:

O artigo Rápida Cópia de Profundidade por Árvores de Expressão tem também a comparação de desempenho de clonagem de Serialização, a Reflexão e a Expressão de Árvores.

Por isso que eu escolher ICloneable (i.é.manualmente)

Mr Venkat Subramaniam (redundante link aqui), explica em detalhes o porquê.

Todo o seu artigo círculos em torno de um exemplo que tenta ser aplicável para a maioria dos casos, através de 3 objetos: Pessoa, Cérebro e Cidade.Queremos clonar uma pessoa, que vai ter o seu próprio cérebro, mas da mesma cidade.Você pode imagem de todos os problemas de qualquer um dos outros métodos descritos acima podem trazer ou leia o artigo.

Esta é a minha versão ligeiramente modificada de sua conclusão:

Copiar um objeto, especificando New seguido pelo nome da classe, muitas vezes leva ao código que não é extensível.Usando o clone, a aplicação do padrão de protótipo, é uma forma melhor de conseguir isso.No entanto, usando o clone como ele é fornecido em C# (e em Java) pode ser bastante problemático também.É melhor fornecer uma protegido (não públicas) construtor de cópia e invocar que a partir do método clone.Isso nos dá a capacidade de delegar a tarefa de criar um objeto a uma instância de uma classe em si, proporcionando, assim, a extensibilidade e também, com segurança, a criação de objetos usando o protegido construtor de cópia.

Espero que essa implementação pode tornar as coisas claras:

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);
    }
    …
}

Agora, considere a possibilidade de ter uma classe deriva Pessoa.

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();
    }
}

Você pode tentar executar o código a seguir:

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);
    }
}

A saída produzida 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, se tivermos uma contagem do número de objetos, o clone, como implementado aqui vai manter uma correta contagem do número de objetos.

Eu prefiro um construtor de cópia para um clone.A intenção é clara.

Simples método de extensão para copiar todas as propriedades públicas.Funciona para todos os objetos e não requerem classe para ser [Serializable].Pode ser estendido para outro nível de acesso.

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 ) } );
        }
    };
}

Bem, eu estava tendo problemas usando o ICloneable no Silverlight, mas eu gostei da idéia de seralization, eu posso seralize XML, então eu fiz isso:

static public class SerializeHelper
{
    //Michael White, Holly Springs Consulting, 2009
    //michael@hollyspringsconsulting.com
    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);
    }
}

Acabei de criar CloneExtensions biblioteca projeto.Ele executa rápido e profundo clone através de simples operações de cessão gerados pela Árvore de Expressão de tempo de execução de compilação do código.

Como utilizar?

Em vez de escrever o seu próprio Clone ou Copy métodos com um tom de atribuições entre campos e propriedades fazer o programa de fazê-lo por si mesmo, usando Árvore de Expressão. GetClone<T>() método marcado como método de extensão permite que você simplesmente chamá-lo em sua instância:

var newInstance = source.GetClone();

Você pode escolher o que deve ser copiado a partir de source para newInstance usando CloningFlags enum:

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

O que pode ser clonado?

  • Primitivo (int, uint, byte, double, char, etc.), conhecido imutável tipos de (data / hora, Duração, Seqüência de caracteres) e delegados (incluindo Ação, Funções, etc.)
  • Anulável
  • T[] matrizes
  • Classes personalizadas e estruturas, incluindo os medicamentos genéricos classes e structs.

A seguir classe/estrutura membros são clonados internamente:

  • Valores do público, não só de leitura campos
  • Valores de propriedades públicas com get e set acessadores
  • Itens de coleção para os tipos de execução de ICollection

O quão rápido ele é?

A solução é mais rápido, em seguida, a reflexão, porque os membros a informação tem de ser recolhido apenas uma vez, antes GetClone<T> é usado pela primeira vez para um dado tipo de T.

É também mais rápido do que a serialização de uma solução baseada quando você clone, em seguida, mais algumas instâncias de um mesmo tipo de T.

e mais...

Leia mais sobre gerado expressões em documentação.

Exemplo de expressão de depuração de listagem 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:
}

}

o que tem o mesmo significado como o seguinte 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;
}

Não é muito, como se estivesse a escrever o seu próprio Clone método para List<int>?

Se você já estiver usando um aplicativo de terceiros 3ª gosto ValueInjecter ou Automapper, você pode fazer algo como isto:

MyObject oldObj; // The existing object to clone

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

Usando este método você não precisa implementar ISerializable ou ICloneable em seus objetos.Isso é comum com o MVC/padrão MVVM, portanto, de ferramentas simples, como este ter sido criado.

ver o valueinjecter profunda clonagem solução no CodePlex.

A resposta curta é que você herda a interface ICloneable e, em seguida, implementar a .clone função.Clone deve fazer uma cópia memberwise e executar uma cópia profunda a qualquer membro, que requer, em seguida, retornar o objeto resultante.Esta é uma operação recursiva ( requer que todos os membros da classe que você deseja clonar são tipos de valor ou implementar ICloneable e que seus membros são tipos de valor ou implementar ICloneable, e assim por diante).

Para uma explicação mais detalhada sobre a Clonagem usando ICloneable, confira este artigo.

O longo a resposta é "depende".Como mencionado por outros, ICloneable não é suportado pelo genéricos, requer considerações especiais para circular referências de classe, e, na verdade, é visto por alguns como um "erro" no .NET Framework.A serialização método depende de sua objetos serializáveis, que não pode ser-e você pode ter nenhum controle.Ainda há muito debate na comunidade sobre o que é o "melhor" prática.Na realidade, nenhuma das soluções são de tamanho único melhores práticas para todas as situações como ICloneable foi originalmente interpretados.

Ver a este Do desenvolvedor do Canto artigo para um pouco mais de opções de crédito para Ian).

O melhor é implementar uma método de extensão como

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

e, em seguida, usá-lo em qualquer lugar na solução por

var copy = anyObject.DeepClone();

Podemos ter as seguintes implementações:

  1. Por Serialização (o menor código)
  2. Reflexão - 5x mais rápido
  3. Por Árvores De Expressão - 20x mais rápido

Todos ligados métodos estão a trabalhar bem e foram profundamente testados.

  1. Basicamente o que você precisa implementar a interface ICloneable e, em seguida, perceber a estrutura do objecto a copiar.
  2. Se é profunda cópia de todos os membros, você precisará garantir que (não relacionados na solução que você escolher) que todas as crianças são clonable bem.
  3. Às vezes, você precisará estar ciente de algumas restrições durante este processo, por exemplo, se você copiar o ORM objetos a maioria dos quadros de permitir que apenas um objeto anexado à sessão e que você NÃO DEVE fazer clones do objeto, ou se é possível você precisa de se preocupar com a sessão de anexar um desses objetos.

Cheers.

Se você deseja verdadeira clonagem de tipos desconhecidos, você pode dar uma olhada em fastclone.

Que é baseado em expressão clonagem de trabalho de cerca de 10 vezes mais rápido do que a serialização binária e manutenção completa do objeto gráfico integridade.

Que significa:se você se referir várias vezes para o mesmo objeto em sua hierarquia, o clone também tem uma única instância passou a ser referenciado.

Não há necessidade de interfaces, atributos ou qualquer outra modificação para os objetos que estão sendo clonados.

Manter as coisas simples e usar AutoMapper como os outros mencionados, é uma pequena biblioteca para mapear um objeto para outro...Para copiar um objeto para outro com o mesmo tipo, tudo o que você precisa é de três linhas de código:

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

O objeto de destino é agora uma cópia do objeto de origem.Não é simples o suficiente?Criar um método de extensão para usar em qualquer lugar em sua solução:

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 o método de extensão, as três linhas se tornar uma linha:

MyType copy = source.Copy();

Eu vim com esta superar um .NET lacuna ter de manualmente cópia profunda Lista<T>.

Eu uso esse:

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

E em outro lugar:

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

Eu tentei subir com oneliner que faz isso, mas não é possível, devido ao rendimento de não trabalhar no interior método anônimo blocos.

Melhor ainda, use uma Lista genérica<T> 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();
        }
    }
}

P.Por que eu iria escolher essa resposta?

  • Escolha esta resposta, se você quiser a velocidade mais rápida .NET é capaz de fazer.
  • Ignorar esta resposta, se você quer um muito, muito fácil método de clonagem.

Em outras palavras, vá com outra resposta, a menos que você tem um afunilamento de desempenho que precisa de conserto, e você pode prová-lo com um profiler.

10x mais rápido do que outros métodos

O seguinte método de execução de um clone profundo é:

  • 10x mais rápido do que qualquer coisa que envolve a serialização/desserialização;
  • Muito perto da velocidade máxima teórica .NET é capaz de fazer.

E o método ...

Para o último em velocidade, você pode usar Aninhado MemberwiseClone para fazer uma cópia de profundidade.Sua quase a mesma velocidade como copiar uma estrutura do valor, e é muito mais rápido que o (a) reflexão (b) a serialização (como descrito em outras respostas nesta página).

Note que se você usa Aninhado MemberwiseClone para uma cópia profunda, você tem que manualmente implementar um ShallowCopy para cada aninhadas nível da classe, e um DeepCopy que chama a todos, disse ShallowCopy métodos para criar um clone completo.Isso é simples:apenas algumas linhas no total, veja a demonstração código abaixo.

Aqui está a saída do código mostrando a relativa diferença de desempenho para 100.000 clones:

  • 1,08 segundos para Aninhados MemberwiseClone em estruturas aninhadas
  • 4.77 segundos para Aninhados MemberwiseClone em classes aninhadas
  • 39.93 segundos para Serialização/Desserialização

Usando Aninhadas MemberwiseClone em uma classe quase tão rápido como copiar uma estrutura, e copiar uma estrutura, é muito perto da velocidade máxima teórica .NET é capaz de fazer.

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 como fazer uma cópia de profundidade usando MemberwiseCopy, aqui é o projeto de demonstração que foi utilizado para gerar os tempos acima:

// 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;
    }
}

Em seguida, chame o demo a partir do principal:

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();
}

Novamente, note que se você usa Aninhado MemberwiseClone para uma cópia profunda, você tem que manualmente implementar um ShallowCopy para cada aninhadas nível da classe, e um DeepCopy que chama a todos, disse ShallowCopy métodos para criar um clone completo.Isso é simples:apenas algumas linhas no total, veja a demonstração código acima.

Tipos de valor vs.Tipos De Referências

Note que quando se trata de clonagem de um objeto, existe uma grande diferença entre um "estrutura"e um "classe":

  • Se você tem um "estrutura"é uma tipo do valor assim você pode simplesmente copiá-lo, e o conteúdo será clonado (mas ele só vai fazer uma rasa clone, a menos que você use as técnicas neste post).
  • Se você tem um "classe"é uma tipo de referência, então, se você copiá-lo, tudo o que você está fazendo é copiar o ponteiro para ele.Para criar um verdadeiro clone, você tem que ser mais criativo, e usar diferenças entre os tipos de valores e referências de tipos de o que cria uma cópia do objeto original na memória.

Ver diferenças entre os tipos de valores e referências de tipos de.

Somas de verificação para ajudar na depuração

  • A clonagem de objetos incorretamente pode resultar muito difícil-a-pino para baixo bugs.No código de produção, que tendem a implementar uma soma de verificação para verificar que o objeto tenha sido clonado corretamente, e não foi corrompido por uma outra referência a ele.Essa soma de verificação pode ser desligado no modo de edição.
  • Acho que este método é bastante útil:muitas vezes, você só quer clonar partes do objeto, e não a coisa toda.

Muito útil para separação de muitos segmentos de muitos outros segmentos

Um excelente caso de uso para este código é a alimentação de clones de uma classe aninhada ou estrutura em uma fila, para implementar o produtor / consumidor padrão.

  • Podemos ter um (ou mais) threads modificar uma classe que eles próprios, em seguida, empurrando uma cópia completa desta classe em um ConcurrentQueue.
  • Nós, então, temos um (ou mais) threads puxando cópias dessas classes e a lidar com eles.

Isso funciona muito bem na prática, e nos permite separar a muitos segmentos (produtores) de um ou mais segmentos (os consumidores).

E esse método é extremamente rápido demais:se usamos estruturas aninhadas, é 35x mais rápido do que serialização/desserialização de classes aninhadas, e nos permite tirar vantagem de todos os threads disponíveis na máquina.

Atualização

Aparentemente, ExpressMapper é tão rápido, se não mais rápido do que a mão de codificação, como acima.Eu posso ver como eles se comparam com um profiler.

Em geral, você implementa a interface ICloneable e implementar Clonar a si mesmo.C# objectos têm um built-in MemberwiseClone método que efectua uma cópia superficial que pode ajudá-lo de todas as formas primitivas.

Para uma cópia de profundidade, não existe nenhuma forma ele pode saber como o fazer automaticamente ele.

Eu tenho visto isso implementado através de reflexão também.Basicamente, foi um método que iterar através de membros de um objeto e devidamente copiá-los para o novo objeto.Quando chegou tipos de referência ou coleções eu acho que ele fez uma chamada recursiva em si.A reflexão é caro, mas funcionou muito bem.

Aqui está uma cópia profunda implementação:

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;
}

Como eu não poderia encontrar um cloner que atenda todas as minhas necessidades em diferentes projetos, criei um profundo cloner, que pode ser configurado e adaptado para diferentes estruturas de código, em vez de adaptar o código para atender os clonadores requisitos.Sua conseguido através da adição de anotações ao código que deve ser clonado, ou você simplesmente deixar o código como é o comportamento padrão.Ele usa a reflexão, tipo de caches e é baseado no fasterflect.O processo de clonagem é muito rápido para uma enorme quantidade de dados e um de alta hierarquia de objetos (em comparação a outros reflexão/serialização de algoritmos de base).

https://github.com/kalisohn/CloneBehave

Também disponível como um pacote do nuget:https://www.nuget.org/packages/Clone.Behave/1.0.0

Por exemplo:O código a seguir irá deepClone Endereço, mas apenas efectuar uma cópia superficial do _currentJob campo.

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

Este método resolve o problema para mim:

private static MyObj DeepCopy(MyObj source)
        {

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

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

        }

Usá-lo assim: MyObj a = DeepCopy(b);

Eu gosto Copyconstructors assim:

    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));
        }
    }

Se você tem mais coisas para copiar adicioná-los

Gerador De Código

Já vimos um monte de idéias de serialização sobre o manual de implementação para a reflexão e quero propor uma abordagem totalmente diferente, utilizando o CGbR Gerador De Código.A gerar método clone é de CPU e de memória eficiente, e por isso 300x mais rápido que o padrão DataContractSerializer.

Tudo o que você precisa é de uma classe parcial definição com ICloneable e o gerador faz o 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: Última versão tem um null mais verificações, mas deixei-los para uma melhor compreensão.

Aqui uma solução rápida e fácil que trabalhou para mim, sem retransmissão de Serialização/Desserialização.

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:requer

    using System.Linq;
    using System.Reflection;

É assim que eu usei

public MyClass Clone(MyClass theObjectIneededToClone)
{
    MyClass clonedObj = theObjectIneededToClone.DeepClone();
}

Siga estes passos:

  • Definir um ISelf<T> com um só de leitura Self propriedade que retorna T, e ICloneable<out T>, que deriva de ISelf<T> e inclui um método T Clone().
  • Em seguida, defina um CloneBase tipo que implementa um protected virtual generic VirtualClone fundição MemberwiseClone para o passado-em tipo.
  • Cada tipo derivado deve implementar VirtualClone chamando a base do método clone e, em seguida, fazendo o que precisa ser feito corretamente clone aqueles aspectos de tipo derivado, que o pai VirtualClone método ainda não tratados.

Para o máximo de herança versatilidade, classes de expor o público a clonagem funcionalidade deve ser sealed, mas derivam de uma classe base, que é o contrário idênticos, exceto para a falta de clonagem.Ao invés de passar variáveis explícita, clonable tipo, tomar um parâmetro do tipo ICloneable<theNonCloneableType>.Isto irá permitir uma rotina que espera um cloneable derivados de Foo para trabalhar com um cloneable derivados de DerivedFoo, mas também permitir a criação de não-cloneable derivados de Foo.

Eu acho que você pode tentar este.

MyObject myObj = GetMyObj(); // Create and fill a new object
MyObject newObj = new MyObject(myObj); //DeepClone it

Eu criei uma versão do aceitas respostas que funciona tanto com '[Serializable]' e '[DataContract]'.Tem sido um tempo desde que eu escrevi, mas se eu me lembro corretamente [DataContract] precisava de um diferente serializador.

Requer Sistema De Sistema.IO, do Sistema.O tempo de execução.A Serialização, Do Sistema.O tempo de execução.A serialização.Formatadores.Binário, System.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;
    }

} 

Para clonar o seu objeto de classe você pode usar o Objeto.MemberwiseClone método,

basta adicionar esta função à classe :

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

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

em seguida, realizar uma profunda independente copiar, basta chamar o DeepCopy método :

yourClass newLine = oldLine.DeepCopy();

espero que isso ajude.

Ok, existem algumas exemplo óbvio de uma reflexão neste post, MAS a reflexão é geralmente lenta, até você começar a cache corretamente.

se você armazená-lo corretamente, que ele vai clone profundo 1000000 objeto de 4,6 s (medido pelo Observador).

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

do que você tomar em cache de propriedades ou adicionar novas ao dicionário e usá-los basta

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

completo código de verificação no meu post em outra resposta

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

Se o Objeto Árvore é Serializeable você também pode usar algo como isto

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;
}

ser informado de que esta Solução é muito fácil, mas não é tão funcionais como outras soluções podem ser.

E certifique-se de que, se o crescimento da Classe, ainda haverá apenas os campos clonado, que também obter serializado.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top