Eu quero fazer algo como:
MyObject myObj = GetMyObj(); // Create and fill a new object
MyObject newObj = myObj.Clone();
E, em seguida, faça alterações no novo objeto que não são refletidas no objeto original.
Como muitas vezes não preciso dessa funcionalidade, quando é necessário, comecei a criar um novo objeto e a copiar cada propriedade individualmente, mas sempre me deixa com a sensação de que há uma maneira melhor ou mais elegante de lidar. a situação.
Como posso clonar ou copiar em profundidade um objeto para que o objeto clonado possa ser modificado sem que nenhuma alteração seja refletida no objeto original?
clone
método na classe e, em seguida, chame um construtor interno e privado que seja aprovadothis
. Portanto, copiar é permitido [sic], mas copiar com cuidado (e o artigo definitivamente vale a pena ler) não é. ; ^)Respostas:
Embora a prática padrão seja implementar a
ICloneable
interface (descrita aqui , para que eu não volte a regurgitar), aqui está uma boa copiadora profunda de objetos clone que encontrei no The Code Project há algum tempo e a incorporei em nossas coisas.Como mencionado em outro lugar, ele exige que seus objetos sejam serializáveis.
A idéia é que ele serialize seu objeto e o desserialize para um novo objeto. O benefício é que você não precisa se preocupar em clonar tudo quando um objeto fica muito complexo.
E com o uso de métodos de extensão (também da fonte originalmente referenciada):
Caso você prefira usar os novos métodos de extensão do C # 3.0, altere o método para ter a seguinte assinatura:
Agora a chamada do método simplesmente se torna
objectBeingCloned.Clone();
.EDIT (10 de janeiro de 2015) Pensei em revisitar isso, para mencionar que recentemente comecei a usar o (Newtonsoft) Json para fazer isso, deve ser mais leve e evita a sobrecarga das tags [Serializable]. ( NB @atconway indicou nos comentários que membros privados não são clonados usando o método JSON)
fonte
typeof(T).IsSerializable
também é verdadeiro se o tipo foi marcado com o[Serializable]
atributo. Não precisa implementar aISerializable
interface.Eu queria um clonador para objetos muito simples, principalmente de primitivas e listas. Se seu objeto estiver fora da caixa JSON serializável, esse método fará o truque. Isso não requer modificação ou implementação de interfaces na classe clonada, apenas um serializador JSON como o JSON.NET.
Além disso, você pode usar este método de extensão
fonte
Newtonsoft.Json.JsonConvert
, mas é o mesmoO motivo para não usar o ICloneable não é porque ele não possui uma interface genérica. A razão para não usá-lo é porque é vago . Não fica claro se você está recebendo uma cópia superficial ou profunda; isso depende do implementador.
Sim,
MemberwiseClone
faz uma cópia superficial, mas o oposto deMemberwiseClone
não éClone
; seria, talvezDeepClone
, o que não existe. Quando você usa um objeto por meio de sua interface ICloneable, não pode saber que tipo de clonagem o objeto subjacente executa. (E os comentários XML não deixarão claro, porque você obterá os comentários da interface, e não os do método Clone do objeto.)O que eu costumo fazer é simplesmente criar um
Copy
método que faça exatamente o que eu quero.fonte
Após muita leitura sobre muitas das opções vinculadas aqui e possíveis soluções para esse problema, acredito que todas as opções estão resumidas muito bem no link de Ian P (todas as outras opções são variações daquelas) e a melhor solução é fornecida por O link de Pedro77 nos comentários da pergunta.
Então, copiarei partes relevantes dessas 2 referências aqui. Dessa forma, podemos ter:
A melhor coisa a fazer para clonar objetos em C sharp!
Em primeiro lugar, essas são todas as nossas opções:
O artigo Fast Deep Copy by Expression Trees também tem comparação de desempenho de clonagem por serialização, reflexão e expressão.
Por que eu escolho o ICloneable (ou seja, manualmente)
Venkat Subramaniam (link redundante aqui) explica em detalhes o motivo .
Todo o artigo dele circula em torno de um exemplo que tenta ser aplicável na maioria dos casos, usando três objetos: Pessoa , Cérebro e Cidade . Queremos clonar uma pessoa, que terá seu próprio cérebro, mas a mesma cidade. Você pode imaginar todos os problemas que qualquer um dos outros métodos acima pode trazer ou ler o artigo.
Esta é a minha versão ligeiramente modificada de sua conclusão:
Esperamos que esta implementação possa esclarecer as coisas:
Agora considere ter uma classe derivada de Person.
Você pode tentar executar o seguinte código:
A saída produzida será:
Observe que, se mantivermos uma contagem do número de objetos, o clone, conforme implementado aqui, manterá uma contagem correta do número de objetos.
fonte
ICloneable
para membros públicos. "Como os chamadores do Clone não podem depender do método que executa uma operação de clonagem previsível, recomendamos que o ICloneable não seja implementado em APIs públicas". msdn.microsoft.com/en-us/library/… No entanto, com base na explicação dada por Venkat Subramaniam em seu artigo vinculado, acho que faz sentido usar nessa situação , desde que os criadores dos objetos ICloneable tenham uma profunda compreensão de quais propriedades devem ser profundas cópias vs. rasos (ou seja, copiar profunda do cérebro, cópia superficial Cidade)Eu prefiro um construtor de cópias a um clone. A intenção é mais clara.
fonte
Método de extensão simples para copiar todas as propriedades públicas. Funciona para qualquer objeto e não exige que a classe seja
[Serializable]
. Pode ser estendido para outro nível de acesso.fonte
Acabei de criar um projeto de
CloneExtensions
biblioteca . Ele executa um clone rápido e profundo usando operações de atribuição simples geradas pela compilação do código de tempo de execução da Expression Tree.Como usá-lo?
Em vez de escrever seus próprios métodos
Clone
ouCopy
com um tom de designação entre campos e propriedades, faça com que o programa faça você mesmo, usando a Expression Tree.GetClone<T>()
O método marcado como método de extensão permite simplesmente chamá-lo em sua instância:Você pode escolher o que deve ser copiado
source
paranewInstance
usarCloningFlags
enum:O que pode ser clonado?
Os seguintes membros de classe / estrutura são clonados internamente:
Quão rápido é?
A solução é mais rápida que a reflexão, porque as informações dos membros precisam ser coletadas apenas uma vez, antes de
GetClone<T>
serem usadas pela primeira vez em um determinado tipoT
.Também é mais rápido que a solução baseada em serialização quando você clona mais do que duas instâncias do mesmo tipo
T
.e mais...
Leia mais sobre expressões geradas na documentação .
Listagem de depuração de expressão de amostra para
List<int>
:}
o que tem o mesmo significado, como segue o código c #:
Não é exatamente como você escreveria seu próprio
Clone
métodoList<int>
?fonte
Bem, eu estava tendo problemas ao usar o ICloneable no Silverlight, mas gostei da idéia de seralização, posso seralizar XML, então fiz o seguinte:
fonte
Se você já estiver usando um aplicativo de terceiros como o ValueInjecter ou o Automapper , poderá fazer algo assim:
Usando esse método, você não precisa implementar
ISerializable
ouICloneable
em seus objetos. Isso é comum com o padrão MVC / MVVM, portanto, ferramentas simples como essa foram criadas.veja o exemplo de clonagem profunda ValueInjecter no GitHub .
fonte
O melhor é implementar um método de extensão como
e use-o em qualquer lugar da solução,
Podemos ter as três implementações a seguir:
Todos os métodos vinculados estão funcionando bem e foram profundamente testados.
fonte
A resposta curta é que você herda da interface ICloneable e implementa a função .clone. O clone deve fazer uma cópia de membro e executar uma cópia profunda em qualquer membro que a exija e, em seguida, retornar o objeto resultante. Esta é uma operação recursiva (requer que todos os membros da classe que você deseja clonar sejam tipos de valor ou implementem o ICloneable e que seus membros sejam tipos de valor ou implementem o ICloneable e assim por diante).
Para uma explicação mais detalhada sobre a clonagem usando o ICloneable, consulte este artigo .
A resposta longa é "depende". Conforme mencionado por outros, o ICloneable não é suportado por genéricos, requer considerações especiais para referências de classe circular e é realmente visto por alguns como um "erro" no .NET Framework. O método de serialização depende de seus objetos serem serializáveis, o que pode não ser e você pode não ter controle. Ainda há muito debate na comunidade sobre qual é a "melhor prática". Na realidade, nenhuma das soluções é a melhor prática única para todas as situações em que o ICloneable foi originalmente interpretado.
Consulte o artigo deste canto do desenvolvedor para mais algumas opções (crédito a Ian).
fonte
Felicidades.
fonte
EDIT: projeto é descontinuado
Se você deseja clonagem verdadeira para tipos desconhecidos, consulte o fastclone .
É uma clonagem baseada em expressão que funciona cerca de 10 vezes mais rápido que a serialização binária e mantém a integridade completa do gráfico de objetos.
Isso significa: se você se referir várias vezes ao mesmo objeto em sua hierarquia, o clone também terá uma única instância sendo referenciada.
Não há necessidade de interfaces, atributos ou qualquer outra modificação nos objetos que estão sendo clonados.
fonte
Mantenha as coisas simples e use o AutoMapper, como já mencionado, é uma pequena biblioteca simples 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:
O objeto de destino agora é uma cópia do objeto de origem. Não é simples o suficiente? Crie um método de extensão para usar em qualquer lugar da sua solução:
O método de extensão pode ser usado da seguinte maneira:
fonte
Eu vim com isso para superar uma falha do .NET que precisa copiar manualmente a Lista <T>.
Eu uso isso:
E em outro lugar:
Tentei criar o oneliner que faz isso, mas não é possível, devido ao rendimento não funcionar dentro de blocos de métodos anônimos.
Melhor ainda, use o clonador genérico List <T>:
fonte
Q. Por que eu escolheria esta resposta?
Em outras palavras, escolha outra resposta, a menos que você tenha um gargalo de desempenho que precise ser corrigido e possa provar isso com um criador de perfil .
10x mais rápido que outros métodos
O método a seguir para executar um clone profundo é:
E o método ...
Para velocidade máxima, você pode usar Nested MemberwiseClone para fazer uma cópia detalhada . É quase a mesma velocidade que copiar uma estrutura de valor e é muito mais rápido que (a) reflexão ou (b) serialização (conforme descrito em outras respostas nesta página).
Observe que se você usar o Nested MemberwiseClone para obter uma cópia detalhada , precisará implementar manualmente um ShallowCopy para cada nível aninhado na classe e um DeepCopy que chame todos os métodos ShallowCopy para criar um clone completo. Isso é simples: apenas algumas linhas no total, veja o código de demonstração abaixo.
Aqui está a saída do código que mostra a diferença de desempenho relativa para 100.000 clones:
Usar o Nested MemberwiseClone em uma classe quase tão rápido quanto copiar uma estrutura, e copiar uma estrutura é muito parecido com a velocidade máxima teórica em que o .NET é capaz.
Para entender como fazer uma cópia profunda usando MemberwiseCopy, aqui está o projeto de demonstração que foi usado para gerar os tempos acima:
Em seguida, chame a demonstração do main:
Novamente, observe que, se você usar Nested MemberwiseClone para uma cópia profunda , precisará implementar manualmente um ShallowCopy para cada nível aninhado na classe e um DeepCopy que chame todos os métodos ShallowCopy para criar um clone completo. Isso é simples: apenas algumas linhas no total, veja o código de demonstração acima.
Tipos de valor vs. tipos de referências
Observe que quando se trata de clonar um objeto, há uma grande diferença entre uma " estrutura " e uma " classe ":
Veja as diferenças entre tipos de valor e tipos de referência .
Soma de verificação para ajudar na depuração
Realmente útil para dissociar muitos threads de muitos outros threads
Um excelente caso de uso para esse código é alimentar clones de uma classe ou estrutura aninhada em uma fila, para implementar o padrão produtor / consumidor.
ConcurrentQueue
.Isso funciona muito bem na prática e permite dissociar muitos threads (os produtores) de um ou mais threads (os consumidores).
E esse método também é incrivelmente rápido: se usarmos estruturas aninhadas, será 35x mais rápido que serializar / desserializar classes aninhadas e nos permitirá tirar proveito de todos os threads disponíveis na máquina.
Atualizar
Aparentemente, o ExpressMapper é tão rápido, se não mais rápido, quanto a codificação manual, como acima. Talvez eu precise ver como eles se comparam com um criador de perfil.
fonte
Em geral, você implementa a interface ICloneable e o Clone. Os objetos C # têm um método MemberwiseClone interno que executa uma cópia superficial que pode ajudá-lo em todas as primitivas.
Para uma cópia profunda, não há como saber como fazê-lo automaticamente.
fonte
Eu já o vi implementado através da reflexão. Basicamente, havia um método que iria percorrer os membros de um objeto e copiá-los adequadamente para o novo objeto. Quando alcançou tipos ou coleções de referência, acho que fez uma chamada recursiva em si mesma. A reflexão é cara, mas funcionou muito bem.
fonte
Aqui está uma implementação de cópia profunda:
fonte
Como não consegui encontrar um clonador que atenda a todos os meus requisitos em diferentes projetos, criei um clonador profundo que pode ser configurado e adaptado a diferentes estruturas de código, em vez de adaptá-lo para atender aos requisitos dos clonadores. Isso é conseguido adicionando anotações ao código que deve ser clonado ou você apenas deixa o código como deve ter o comportamento padrão. Ele usa reflexão, digite caches e baseia-se no efeito mais rápido . O processo de clonagem é muito rápido para uma enorme quantidade de dados e uma alta hierarquia de objetos (em comparação com outros algoritmos baseados em reflexão / serialização).
https://github.com/kalisohn/CloneBehave
Também disponível como pacote nuget: https://www.nuget.org/packages/Clone.Behave/1.0.0
Por exemplo: O código a seguir fará o deepClone Address, mas apenas executará uma cópia superficial do campo _currentJob.
fonte
Gerador de código
Vimos muitas idéias desde a serialização, passando pela implementação manual até a reflexão, e quero propor uma abordagem totalmente diferente usando o CGbR Code Generator . O método generate clone é eficiente em memória e CPU e, portanto, 300x mais rápido que o DataContractSerializer padrão.
Tudo o que você precisa é de uma definição de classe parcial
ICloneable
e o gerador faz o resto:Nota: A versão mais recente possui verificações mais nulas, mas as deixei de fora para melhor entendimento.
fonte
Eu gosto de Copyconstructors assim:
Se você tiver mais coisas para copiar, adicione-as
fonte
Este método resolveu o problema para mim:
Use-o assim:
MyObj a = DeepCopy(b);
fonte
Aqui uma solução rápida e fácil que funcionou para mim sem retransmitir a serialização / desserialização.
EDIT : requer
Foi assim que eu o usei
fonte
Siga esses passos:
ISelf<T>
com umaSelf
propriedade somente leitura que retorneT
eICloneable<out T>
, que deriva deISelf<T>
e inclui um métodoT Clone()
.CloneBase
tipo que implementa umaprotected virtual generic VirtualClone
conversãoMemberwiseClone
para o tipo passado.VirtualClone
chamando o método de clone base e, em seguida, fazendo o que for necessário para clonar adequadamente os aspectos do tipo derivado que o método VirtualClone pai ainda não manipulou.Para máxima versatilidade de herança, as classes que expõem a funcionalidade de clonagem pública devem ser
sealed
, mas derivam de uma classe base que, de outra forma, é idêntica, exceto pela falta de clonagem. Ao invés de passar variáveis do tipo clonable explícita, ter um parâmetro do tipoICloneable<theNonCloneableType>
. Isso permitirá que uma rotina que espera que um derivado clonávelFoo
funcione com um derivado clonável deDerivedFoo
, mas também permita a criação de derivativos não clonáveis deFoo
.fonte
Eu acho que você pode tentar isso.
fonte
Eu criei uma versão da resposta aceita que funciona com '[Serializable]' e '[DataContract]'. Já faz um tempo desde que o escrevi, mas se bem me lembro, o [DataContract] precisava de um serializador diferente.
Requer System, System.IO, System.Runtime.Serialization, System.Runtime.Serialization.Formatters.Binary, System.Xml ;
fonte
Ok, há algum exemplo óbvio com reflexão neste post, mas a reflexão geralmente é lenta, até que você comece a armazená-la corretamente.
se você o armazenar em cache corretamente, clonará profundamente o objeto 1000000 em 4,6s (medido pelo Watcher).
do que você pega propriedades em cache ou adiciona novas ao dicionário e as usa simplesmente
código completo cheque no meu post em outra resposta
https://stackoverflow.com/a/34365709/4711853
fonte
prop.GetValue(...)
ainda é reflexo e não pode ser armazenada em cache. Em uma árvore de expressão sua compilado embora, por isso mais rápidoComo quase todas as respostas a essa pergunta foram insatisfatórias ou claramente não funcionam na minha situação, criei o AnyClone que é totalmente implementado com reflexão e resolvi todas as necessidades aqui. Não consegui fazer com que a serialização funcionasse em um cenário complicado com estrutura complexa e
IClonable
é menos do que o ideal - na verdade, nem deveria ser necessário.Padrão ignorar atributos são suportados usando
[IgnoreDataMember]
,[NonSerialized]
. Suporta coleções complexas, propriedades sem setters, campos somente leitura etc.Espero que ajude alguém por aí que tenha os mesmos problemas que eu.
fonte
Disclaimer: Eu sou o autor do pacote mencionado.
Fiquei surpreso em saber como as principais respostas para essa pergunta em 2019 ainda usam serialização ou reflexão.
A serialização é limitadora (requer atributos, construtores específicos etc.) e é muito lenta
BinaryFormatter
requer oSerializable
atributo,JsonConverter
requer um construtor sem parâmetros ou atributos, nem manipula campos ou interfaces somente de leitura muito bem e ambos são 10 a 30 vezes mais lentos que o necessário.Árvores de expressão
Em vez disso, você pode usar Expression Trees ou Reflection.Emit para gerar código de clonagem apenas uma vez e, em seguida, usar esse código compilado em vez de reflexão lenta ou serialização.
Depois de me deparar com o problema e não encontrar uma solução satisfatória, decidi criar um pacote que faz exatamente isso e funciona com todos os tipos e é quase tão rápido quanto um código escrito personalizado .
Você pode encontrar o projeto no GitHub: https://github.com/marcelltoth/ObjectCloner
Uso
Você pode instalá-lo a partir do NuGet. Pegue o
ObjectCloner
pacote e use-o como:ou se você não se importa de poluir o seu tipo de objeto com extensões, obtenha
ObjectCloner.Extensions
também e escreva:atuação
Um benchmark simples de clonagem de uma hierarquia de classes mostrou desempenho ~ 3x mais rápido que o Reflection, ~ 12x mais rápido que a serialização Newtonsoft.Json e ~ 36x mais rápido do que o sugerido
BinaryFormatter
.fonte