Eu tenho uma lista genérica de objetos em C # e desejo clonar a lista. Os itens da lista são clonáveis, mas não parece haver uma opção a fazer list.Clone().
@orip Por clone()definição, não é uma cópia profunda? Em C #, você pode passar ponteiros facilmente com =, pensei.
22412 Chris
13
@Chris uma cópia superficial copia um nível mais profundo do que a cópia do ponteiro. Por exemplo, uma cópia superficial de uma lista terá os mesmos elementos, mas será uma lista diferente.
Eu acho que List.ConvertAll pode fazer isso em um tempo mais rápido, pois pode pré-alocar toda a matriz para a lista, em vez de precisar redimensionar o tempo todo.
MichaelGG
2
@ MichaelGG, e se você não quiser converter, mas apenas clonar / duplicar os itens da lista? Isso funcionaria? || var clonedList = ListOfStrings.ConvertAll (p => p);
precisa saber é o seguinte
29
@IbrarMumtaz: É o mesmo que var clonedList = new List <string> (ListOfStrings);
Brandon Arnold
4
Ótima solução! A propósito, eu prefiro List estático público <T> CLone <T> ... É mais útil em casos como este, porque não é necessário converter mais: List <MyType> cloned = listToClone.Clone ();
Plutoz 15/05
2
esta é a clonagem profunda
George Birbilis
513
Se seus elementos são tipos de valor, você pode simplesmente fazer:
No entanto, se eles são tipos de referência e você deseja uma cópia profunda (supondo que seus elementos sejam implementados corretamente ICloneable), você pode fazer algo assim:
Pessoalmente, eu evitaria ICloneablepor causa da necessidade de garantir uma cópia profunda de todos os membros. Em vez disso, sugiro que o construtor de cópias ou um método de fábrica YourType.CopyFrom(YourType itemToCopy)retorne uma nova instância de YourType.
Qualquer uma dessas opções pode ser agrupada por um método (extensão ou não).
Eu acho que List <T> .ConvertAll pode parecer melhor do que criar uma nova lista e fazer um foreach + add.
MichaelGG
2
@ Dimitri: Não, isso não é verdade. O problema é que, quando ICloneablefoi definida, a definição nunca declarou se o clone era profundo ou superficial, portanto, você não pode determinar que tipo de operação de Clone será realizada quando um objeto a implementar. Isso significa que, se você quiser fazer um clone profundo List<T>, precisará fazê-lo sem ICloneableter certeza de que é uma cópia profunda.
Jeff Yates
5
Por que não usar o método AddRange? ( newList.AddRange(oldList.Select(i => i.Clone())or newList.AddRange(oldList.Select(i => new YourType(i))
phoog
5
@ Phoho: Eu acho que é um pouco menos legível / compreensível ao digitalizar o código, isso é tudo. A legibilidade vence para mim.
Jeff Yates
1
@JeffYates: Uma ruga insuficientemente considerada é que as coisas geralmente só precisam ser copiadas se existir algum caminho de execução que as modifique. É muito comum que tipos imutáveis mantenham uma referência a uma instância do tipo mutável, mas nunca a exponha a algo que a modifique. A cópia desnecessária de coisas que nunca mudarão às vezes pode ser uma grande perda de desempenho, aumentando o uso da memória em ordens de magnitude.
Supercat 23/09/13
84
Para uma cópia superficial, você pode usar o método GetRange da classe List genérica.
Você também pode conseguir isso usando o contratador da Lista <T> para especificar uma Lista <T> da qual copiar. por exemplo, var shallowClonedList = new List <MyObject> (originalList);
Arkiliknam 16/02/2012
9
Eu costumo usar List<int> newList = oldList.ToList(). O mesmo efeito. No entanto, a solução da Arkiliknam é melhor para facilitar a leitura na minha opinião.
Dan Bechard
82
publicstaticobjectDeepClone(object obj){object objResult =null;
using (MemoryStream ms =newMemoryStream()){BinaryFormatter bf =newBinaryFormatter();
bf.Serialize(ms, obj);
ms.Position=0;
objResult = bf.Deserialize(ms);}return objResult;}
Essa é uma maneira de fazer isso com C # e .NET 2.0. Seu objeto precisa ser [Serializable()]. O objetivo é perder todas as referências e criar novas.
+1 - gosto desta resposta - é rápida, suja, desagradável e muito eficaz. Eu usei no silverlight e usei o DataContractSerializer, pois o BinarySerializer não estava disponível. Quem precisa escrever páginas de código de clonagem de objetos quando você pode fazer isso? :)
slugster
3
Eu gosto disso. Embora seja bom fazer as coisas "direito", rápido e sujo geralmente são úteis.
Odrade
3
Rápido! mas: por que sujo?
raiserle
2
Isso clona profundamente e é rápido e fácil. Cuidado com outras sugestões nesta página. Eu tentei vários e eles não clonam profundamente.
RandallTo
2
O único aspecto negativo, se você pode chamar assim, é que suas aulas precisam ser marcadas como serializáveis para que isso funcione.
Tuukka Haapaniemi
30
Para clonar uma lista, basta chamar .ToList (). Isso cria uma cópia superficial.
Microsoft(R)Roslyn C# Compiler version 2.3.2.62116Loading context from'CSharpInteractive.rsp'.Type"#help"for more information.>var x =newList<int>(){3,4};>var y = x.ToList();> x.Add(5)> x
List<int>(3){3,4,5}> y
List<int>(2){3,4}>
Um pequeno aviso de que é uma cópia superficial ... Isso criará dois objetos de lista, mas os objetos dentro serão os mesmos. Ou seja, alterar uma propriedade alterará o mesmo objeto / propriedade na lista original.
Mark G
22
Após uma ligeira modificação, você também pode clonar:
publicstatic T DeepClone<T>(T obj){
T objResult;
using (MemoryStream ms =newMemoryStream()){BinaryFormatter bf =newBinaryFormatter();
bf.Serialize(ms, obj);
ms.Position=0;
objResult =(T)bf.Deserialize(ms);}return objResult;}
Não esqueça que o T deve ser serializável; caso contrário, você obtém System.Runtime.Serialization.SerializationException.
Bence Végert
Boa resposta. Uma dica: você pode adicionar if (!obj.GetType().IsSerializable) return default(T);como a primeira instrução que impede a exceção. E se você alterá-lo para um método de extensão, você pode até usar o operador Elvis como var b = a?.DeepClone();(dado var a = new List<string>() { "a", "b" }; por exemplo).
Matt
15
A menos que você precise de um clone real de cada objeto dentro do seu List<T>, a melhor maneira de clonar uma lista é criar uma nova lista com a lista antiga como parâmetro de coleção.
Possivelmente não é a maneira mais eficiente de fazê-lo, mas, a menos que você esteja fazendo isso centenas de milhares de vezes, você nem perceberá a diferença de velocidade.
Não é sobre a diferença de velocidade, é sobre a legibilidade. Se eu chegasse a essa linha de código, daria um tapa na cabeça e me perguntaria por que eles introduziram uma biblioteca de terceiros para serializar e desserializar um objeto que eu não teria idéia do por que está acontecendo. Além disso, isso não funcionaria para uma lista de modelos com objetos que possuem uma estrutura circular.
Jonathon Cwik
1
Esse código funcionou excelentemente para mim na clonagem profunda. O aplicativo está migrando o clichê de documentos do Dev para o QA para o Prod. Cada objeto é um pacote de vários objetos de modelo de documento e cada documento, por sua vez, é composto de uma lista de objetos de parágrafo. Esse código me permite serializar os objetos "origem" do .NET e desserializá-los imediatamente para novos objetos "destino", que são salvos em um banco de dados SQL em um ambiente diferente. Após toneladas de pesquisa, encontrei muitas coisas, muitas das quais eram muito pesadas, e decidi tentar isso. Essa abordagem curta e flexível foi "perfeita"!
Meu amigo Gregor Martinovic e eu criamos essa solução fácil usando um serializador de JavaScript. Não há necessidade de sinalizar classes como Serializable e, em nossos testes, o Newtonsoft JsonSerializer é ainda mais rápido do que o BinaryFormatter. Com métodos de extensão utilizáveis em todos os objetos.
Opção .NET JavascriptSerializer padrão:
publicstatic T DeepCopy<T>(this T value){JavaScriptSerializer js =newJavaScriptSerializer();string json = js.Serialize(value);return js.Deserialize<T>(json);}
Serei sortudo se alguém ler isso ... mas, para não retornar uma lista do objeto de tipo nos meus métodos Clone, criei uma interface:
publicinterfaceIMyCloneable<T>{
T Clone();}
Então eu especifiquei a extensão:
publicstaticList<T>Clone<T>(thisList<T> listToClone)where T :IMyCloneable<T>{return listToClone.Select(item =>(T)item.Clone()).ToList();}
E aqui está uma implementação da interface no meu software de marcação A / V. Eu queria que meu método Clone () retornasse uma lista do VidMark (enquanto a interface ICloneable desejava que meu método retornasse uma lista de objetos):
E, finalmente, o uso da extensão dentro de uma classe:
privateList<VidMark>_VidMarks;privateList<VidMark>_UndoVidMarks;//Other methods instantiate and fill the listsprivatevoidSetUndoVidMarks(){_UndoVidMarks=_VidMarks.Clone();}
Você também pode simplesmente converter a lista em uma matriz usando ToArraye depois clonar a matriz usando Array.Clone(...). Dependendo das suas necessidades, os métodos incluídos na classe Array podem atender às suas necessidades.
Isso não funciona; alterações nos valores na matriz clonada AINDA alteram os valores na lista original.
Bernoulli Lizard
você pode usar var clonedList = ListOfStrings.ConvertAll (p => p); como dado por @IbrarMumtaz .... funciona de forma eficaz ... Mudanças a uma lista são mantidos para si e não para não refletir em outro
Zainul
2
Você pode usar o método de extensão:
namespace extension
{publicclass ext
{publicstaticList<double> clone(thisList<double> t){List<double> kop =newList<double>();int x;for(x =0; x < t.Count; x++){
kop.Add(t[x]);}return kop;}};}
Você pode clonar todos os objetos usando seus membros do tipo de valor, por exemplo, considere esta classe:
publicclass matrix
{publicList<List<double>> mat;publicint rows,cols;public matrix clone(){// create new object
matrix copy =new matrix();// firstly I can directly copy rows and cols because they are value types
copy.rows =this.rows;
copy.cols =this.cols;// but now I can no t directly copy mat because it is not value type soint x;// I assume I have clone method for List<double>for(x=0;x<this.mat.count;x++){
copy.mat.Add(this.mat[x].clone());}// then mat is clonedreturn copy;// and copy of original is returned }};
Nota: se você fizer alguma alteração na cópia (ou clone), isso não afetará o objeto original.
Parece que algumas coleções (por exemplo SelectedItems de DataGrid no Silverlight) ignorar a implementação de CopyTo que é um problema com esta abordagem
George Birbilis
1
Eu uso o automapper para copiar um objeto. Acabei de configurar um mapeamento que mapeia um objeto para si mesmo. Você pode quebrar esta operação da maneira que desejar.
Para uma cópia profunda, o ICloneable é a solução correta, mas aqui está uma abordagem semelhante ao ICloneable usando o construtor em vez da interface do ICloneable.
publicclassStudent{publicStudent(Student student){FirstName= student.FirstName;LastName= student.LastName;}publicstringFirstName{get;set;}publicstringLastName{get;set;}}// wherever you have the listList<Student> students;// and then where you want to make a copyList<Student> copy = students.Select(s =>newStudent(s)).ToList();
você precisará da seguinte biblioteca onde você faz a cópia
using System.Linq
você também pode usar um loop for em vez do System.Linq, mas o Linq o torna conciso e limpo. Da mesma forma, você poderia fazer o que outras respostas sugeriram e criar métodos de extensão etc., mas nada disso é necessário.
Isso é chamado de "construtor de cópias". É uma abordagem propensa a erros, sempre que você adiciona um novo campo ao Student, lembre-se de adicioná-lo ao construtor de cópias. A principal idéia por trás do "clone" é evitar esse problema.
Kenno
2
Mesmo com o ICloneable, você precisa ter um método "Clone" em sua classe. A menos que você use a reflexão (que você também pode usar na abordagem acima), esse método Clone será realmente semelhante à abordagem do construtor de cópias acima e sofrerá com o mesmo problema de ter que atualizar para campos novos / alterados. Mas isso está dizendo "A classe precisa ser atualizada quando os campos da classe mudarem". Claro que sim;)
ztorstri
0
O código a seguir deve ser transferido para uma lista com alterações mínimas.
Basicamente, ele funciona inserindo um novo número aleatório de um intervalo maior a cada loop sucessivo. Se já existem números iguais ou superiores a ele, mude esses números aleatórios um para cima para que eles sejam transferidos para o novo intervalo maior de índices aleatórios.
// Example Usageint[] indexes = getRandomUniqueIndexArray(selectFrom.Length, toSet.Length);for(int i =0; i < toSet.Length; i++)
toSet[i]= selectFrom[indexes[i]];privateint[] getRandomUniqueIndexArray(int length,int count){if(count > length || count <1|| length <1)returnnewint[0];int[] toReturn =newint[count];if(count == length){for(int i =0; i < toReturn.Length; i++) toReturn[i]= i;return toReturn;}Random r =newRandom();int startPos = count -1;for(int i = startPos; i >=0; i--){int index = r.Next(length - i);for(int j = startPos; j > i; j--)if(toReturn[j]>= index)
toReturn[j]++;
toReturn[i]= index;}return toReturn;}
Outra coisa: você pode usar a reflexão. Se você armazenar isso em cache corretamente, clonará 1.000.000 de objetos em 5,6 segundos (infelizmente, 16,4 segundos com objetos internos).
[ProtoContract(ImplicitFields=ImplicitFields.AllPublic)]publicclassPerson{...JobJobDescription...}[ProtoContract(ImplicitFields=ImplicitFields.AllPublic)]publicclassJob{...}privatestaticreadonlyType stringType =typeof(string);publicstaticclassCopyFactory{staticreadonlyDictionary<Type,PropertyInfo[]>ProperyList=newDictionary<Type,PropertyInfo[]>();privatestaticreadonlyMethodInfoCreateCopyReflectionMethod;staticCopyFactory(){CreateCopyReflectionMethod=typeof(CopyFactory).GetMethod("CreateCopyReflection",BindingFlags.Static|BindingFlags.Public);}publicstatic T CreateCopyReflection<T>(T source)where T :new(){var copyInstance =new T();var sourceType =typeof(T);PropertyInfo[] propList;if(ProperyList.ContainsKey(sourceType))
propList =ProperyList[sourceType];else{
propList = sourceType.GetProperties(BindingFlags.Public|BindingFlags.Instance);ProperyList.Add(sourceType, propList);}foreach(var prop in propList){varvalue= prop.GetValue(source,null);
prop.SetValue(copyInstance,value!=null&& prop.PropertyType.IsClass&& prop.PropertyType!= stringType ?CreateCopyReflectionMethod.MakeGenericMethod(prop.PropertyType).Invoke(null,newobject[]{value}):value,null);}return copyInstance;}
Eu a medi de maneira simples, usando a classe Watcher.
var person =newPerson{...};for(var i =0; i <1000000; i++){
personList.Add(person);}var watcher =newStopwatch();
watcher.Start();var copylist = personList.Select(CopyFactory.CreateCopyReflection).ToList();
watcher.Stop();var elapsed = watcher.Elapsed;
CopyFactory é apenas a minha classe de teste, onde tenho dezenas de testes, incluindo o uso de expressão. Você pode implementar isso de outra forma em uma extensão ou qualquer outra coisa. Não se esqueça do cache.
Ainda não testei a serialização, mas duvido de uma melhoria com um milhão de classes. Vou tentar algo rápido protobuf / newton.
PS: para simplificar a leitura, usei apenas propriedades automáticas aqui. Eu poderia atualizar com o FieldInfo ou você deve implementá-lo facilmente.
Recentemente, testei o serializador de buffers de protocolo com a função DeepClone pronta para uso. Ele vence com 4,2 segundos em um milhão de objetos simples, mas quando se trata de objetos internos, vence com o resultado 7,4 segundos.
Serializer.DeepClone(personList);
RESUMO: Se você não tiver acesso às aulas, isso ajudará. Caso contrário, isso depende da contagem dos objetos. Eu acho que você pode usar reflexão de até 10.000 objetos (talvez um pouco menos), mas, para mais do que isso, o serializador de buffers de protocolo terá um desempenho melhor.
Existe uma maneira simples de clonar objetos em C # usando um serializador e desserializador JSON.
Você pode criar uma classe de extensão:
using Newtonsoft.Json;staticclass typeExtensions
{[Extension()]publicstatic T jsonCloneObject<T>(T source){string json =JsonConvert.SerializeObject(source);returnJsonConvert.DeserializeObject<T>(json);}}
clone()
definição, não é uma cópia profunda? Em C #, você pode passar ponteiros facilmente com =, pensei.Respostas:
Você pode usar um método de extensão.
fonte
Se seus elementos são tipos de valor, você pode simplesmente fazer:
No entanto, se eles são tipos de referência e você deseja uma cópia profunda (supondo que seus elementos sejam implementados corretamente
ICloneable
), você pode fazer algo assim:Obviamente, substitua
ICloneable
os genéricos acima e faça a conversão por qualquer que seja o tipo de elemento que implementeICloneable
.Se o seu tipo de elemento não suporta,
ICloneable
mas possui um construtor de cópias, você pode fazer isso:Pessoalmente, eu evitaria
ICloneable
por causa da necessidade de garantir uma cópia profunda de todos os membros. Em vez disso, sugiro que o construtor de cópias ou um método de fábricaYourType.CopyFrom(YourType itemToCopy)
retorne uma nova instância deYourType
.Qualquer uma dessas opções pode ser agrupada por um método (extensão ou não).
fonte
ICloneable
foi definida, a definição nunca declarou se o clone era profundo ou superficial, portanto, você não pode determinar que tipo de operação de Clone será realizada quando um objeto a implementar. Isso significa que, se você quiser fazer um clone profundoList<T>
, precisará fazê-lo semICloneable
ter certeza de que é uma cópia profunda.newList.AddRange(oldList.Select(i => i.Clone())
ornewList.AddRange(oldList.Select(i => new YourType(i)
)Para uma cópia superficial, você pode usar o método GetRange da classe List genérica.
Citado de: Generics Recipes
fonte
List<int> newList = oldList.ToList()
. O mesmo efeito. No entanto, a solução da Arkiliknam é melhor para facilitar a leitura na minha opinião.Essa é uma maneira de fazer isso com C # e .NET 2.0. Seu objeto precisa ser
[Serializable()]
. O objetivo é perder todas as referências e criar novas.fonte
Para clonar uma lista, basta chamar .ToList (). Isso cria uma cópia superficial.
fonte
Após uma ligeira modificação, você também pode clonar:
fonte
if (!obj.GetType().IsSerializable) return default(T);
como a primeira instrução que impede a exceção. E se você alterá-lo para um método de extensão, você pode até usar o operador Elvis comovar b = a?.DeepClone();
(dadovar a = new List<string>() { "a", "b" };
por exemplo).A menos que você precise de um clone real de cada objeto dentro do seu
List<T>
, a melhor maneira de clonar uma lista é criar uma nova lista com a lista antiga como parâmetro de coleção.Alterações em
myList
inserir ou remover não afetarãocloneOfMyList
e vice-versa.Os objetos reais contidos nas duas listas ainda são os mesmos.
fonte
Use o AutoMapper (ou qualquer outra biblioteca de mapeamento que você preferir) para clonar é simples e muito sustentável.
Defina seu mapeamento:
Faça a mágica:
fonte
Se você se importa apenas com os tipos de valor ...
E você sabe o tipo:
Se você não conhece o tipo antes, precisará de uma função auxiliar:
O justo:
fonte
Se você já fez referência ao Newtonsoft.Json no seu projeto e seus objetos são serializáveis, você sempre pode usar:
Possivelmente não é a maneira mais eficiente de fazê-lo, mas, a menos que você esteja fazendo isso centenas de milhares de vezes, você nem perceberá a diferença de velocidade.
fonte
fonte
fonte
fonte
Meu amigo Gregor Martinovic e eu criamos essa solução fácil usando um serializador de JavaScript. Não há necessidade de sinalizar classes como Serializable e, em nossos testes, o Newtonsoft JsonSerializer é ainda mais rápido do que o BinaryFormatter. Com métodos de extensão utilizáveis em todos os objetos.
Opção .NET JavascriptSerializer padrão:
Opção mais rápida usando o Newtonsoft JSON :
fonte
fonte
Serei sortudo se alguém ler isso ... mas, para não retornar uma lista do objeto de tipo nos meus métodos Clone, criei uma interface:
Então eu especifiquei a extensão:
E aqui está uma implementação da interface no meu software de marcação A / V. Eu queria que meu método Clone () retornasse uma lista do VidMark (enquanto a interface ICloneable desejava que meu método retornasse uma lista de objetos):
E, finalmente, o uso da extensão dentro de uma classe:
Alguém gosta disso? Alguma melhoria?
fonte
Você também pode simplesmente converter a lista em uma matriz usando
ToArray
e depois clonar a matriz usandoArray.Clone(...)
. Dependendo das suas necessidades, os métodos incluídos na classe Array podem atender às suas necessidades.fonte
Você pode usar o método de extensão:
Você pode clonar todos os objetos usando seus membros do tipo de valor, por exemplo, considere esta classe:
Nota: se você fizer alguma alteração na cópia (ou clone), isso não afetará o objeto original.
fonte
Se você precisar de uma lista clonada com a mesma capacidade, tente:
fonte
Criei por mim próprio uma extensão que converte ICollection de itens que não implementam o IClonable
fonte
Eu uso o automapper para copiar um objeto. Acabei de configurar um mapeamento que mapeia um objeto para si mesmo. Você pode quebrar esta operação da maneira que desejar.
http://automapper.codeplex.com/
fonte
Usar um elenco pode ser útil, neste caso, para uma cópia superficial:
aplicado à lista genérica:
fonte
Para uma cópia profunda, o ICloneable é a solução correta, mas aqui está uma abordagem semelhante ao ICloneable usando o construtor em vez da interface do ICloneable.
você precisará da seguinte biblioteca onde você faz a cópia
você também pode usar um loop for em vez do System.Linq, mas o Linq o torna conciso e limpo. Da mesma forma, você poderia fazer o que outras respostas sugeriram e criar métodos de extensão etc., mas nada disso é necessário.
fonte
O código a seguir deve ser transferido para uma lista com alterações mínimas.
Basicamente, ele funciona inserindo um novo número aleatório de um intervalo maior a cada loop sucessivo. Se já existem números iguais ou superiores a ele, mude esses números aleatórios um para cima para que eles sejam transferidos para o novo intervalo maior de índices aleatórios.
fonte
Outra coisa: você pode usar a reflexão. Se você armazenar isso em cache corretamente, clonará 1.000.000 de objetos em 5,6 segundos (infelizmente, 16,4 segundos com objetos internos).
Eu a medi de maneira simples, usando a classe Watcher.
RESULTADO: Com objeto interno PersonInstance - 16.4, PersonInstance = null - 5.6
CopyFactory é apenas a minha classe de teste, onde tenho dezenas de testes, incluindo o uso de expressão. Você pode implementar isso de outra forma em uma extensão ou qualquer outra coisa. Não se esqueça do cache.
Ainda não testei a serialização, mas duvido de uma melhoria com um milhão de classes. Vou tentar algo rápido protobuf / newton.
PS: para simplificar a leitura, usei apenas propriedades automáticas aqui. Eu poderia atualizar com o FieldInfo ou você deve implementá-lo facilmente.
Recentemente, testei o serializador de buffers de protocolo com a função DeepClone pronta para uso. Ele vence com 4,2 segundos em um milhão de objetos simples, mas quando se trata de objetos internos, vence com o resultado 7,4 segundos.
RESUMO: Se você não tiver acesso às aulas, isso ajudará. Caso contrário, isso depende da contagem dos objetos. Eu acho que você pode usar reflexão de até 10.000 objetos (talvez um pouco menos), mas, para mais do que isso, o serializador de buffers de protocolo terá um desempenho melhor.
fonte
Existe uma maneira simples de clonar objetos em C # usando um serializador e desserializador JSON.
Você pode criar uma classe de extensão:
Para clonar e objetar:
fonte