У меня есть общий список объектов в C #, и я хочу клонировать этот список. Элементы в списке являются клонируемыми, но, похоже, нет возможности сделать это list.Clone().
@orip clone()по определению не глубокая копия? В C # вы можете легко передавать указатели с помощью =, подумал я.
Крис
13
@ Кривая мелкая копия копирует на один уровень глубже, чем копия указателя. Например, мелкая копия списка будет иметь те же элементы, но будет другим списком.
Я думаю, что List.ConvertAll может сделать это быстрее, так как он может предварительно выделить весь массив для списка, вместо того, чтобы постоянно изменять размер.
MichaelGG
2
@MichaelGG, что если вы не хотите конвертировать, а просто клонировать / дублировать элементы в списке? Будет ли это работать? || var clonedList = ListOfStrings.ConvertAll (p => p);
ИбрарМумтаз
29
@IbrarMumtaz: то же самое, что и var clonedList = new List <string> (ListOfStrings);
Брэндон Арнольд
4
Отличное решение! Кстати, я предпочитаю публичные статические List <T> CLone <T> ... Это более полезно в подобных случаях, потому что дальнейшее приведение не требуется: List <MyType> cloned = listToClone.Clone ();
Плутос
2
это глубокое клонирование
Джордж Бирбилис
513
Если ваши элементы являются типами значений, то вы можете просто сделать:
Однако, если они являются ссылочными типами и вы хотите глубокое копирование (при условии правильной реализации ваших элементов ICloneable), вы можете сделать что-то вроде этого:
Лично я бы избежал ICloneableиз-за необходимости гарантировать глубокую копию всех участников. Вместо этого я бы предложил конструктор копирования или такой метод фабрики, YourType.CopyFrom(YourType itemToCopy)который возвращает новый экземпляр YourType.
Любой из этих параметров может быть заключен в метод (расширение или иным образом).
Я думаю, что List <T> .ConvertAll может выглядеть лучше, чем создание нового списка и выполнение foreach + add.
MichaelGG
2
@Dimitri: Нет, это не правда. Проблема в том, что когда оно ICloneableбыло определено, определение никогда не указывало, был ли клон глубоким или неглубоким, поэтому вы не можете определить, какой тип операции клонирования будет выполняться, когда объект реализует ее. Это означает, что если вы хотите сделать глубокий клон List<T>, вам придется делать это, не ICloneableбудучи уверенным, что это глубокая копия.
Джефф Йейтс
5
Почему бы не использовать метод AddRange? ( newList.AddRange(oldList.Select(i => i.Clone())или newList.AddRange(oldList.Select(i => new YourType(i))
фото
5
@phoog: я думаю, что это немного менее читаемо / понятно при сканировании кода, вот и все. Читаемость выигрывает для меня.
Джефф Йейтс
1
@JeffYates: Одна недостаточно продуманная проблема заключается в том, что вещи, как правило, нужно копировать только в том случае, если существует какой-либо путь выполнения, который может их изменить. Это очень распространено иметь неизменные типы содержат ссылку на экземпляр изменяемого типа, но никогда не подвергать этот экземпляр к чему - либо , что будет мутировать его. Излишнее копирование того, что никогда не изменится, иногда может привести к значительному снижению производительности, увеличивая использование памяти на порядок.
суперкат
84
Для поверхностной копии вы можете вместо этого использовать метод GetRange универсального класса List.
Вы также можете достичь этого, используя конструктор List <T>, чтобы указать List <T>, из которого копировать. например, var shallowClonedList = new List <MyObject> (originalList);
Arkiliknam
9
Я часто использую List<int> newList = oldList.ToList(). Тот же эффект. Однако, на мой взгляд, решение Arkiliknam лучше всего подходит для удобства чтения.
Дэн Бешард
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;}
Это один из способов сделать это с C # и .NET 2.0. Ваш объект должен быть [Serializable()]. Цель состоит в том, чтобы потерять все ссылки и построить новые.
+1 - мне нравится этот ответ - он быстрый, грязный, противный и очень эффективный. Я использовал Silverlight и использовал DataContractSerializer, так как BinarySerializer был недоступен. Кому нужно писать страницы кода для клонирования объектов, когда вы можете просто сделать это? :)
Слагстер
3
Мне это нравится. Хотя хорошо делать вещи «правильно», быстрые и грязные часто оказываются полезными.
Одраде
3
Быстро! но: почему грязный?
Райзер
2
Это глубокие клоны и быстрые и простые. Осторожно обращайтесь с другими предложениями на этой странице. Я пробовал несколько, и они не глубокий клон.
Рэндалл до
2
Единственный негативный аспект, если вы можете это так называть, это то, что ваши классы должны быть помечены как Serializable, чтобы это работало.
Туукка Хаапаниеми,
30
Чтобы клонировать список, просто вызовите .ToList (). Это создает мелкую копию.
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}>
Небольшое предупреждение, это мелкая копия ... Это создаст два объекта списка, но объекты внутри будут одинаковыми. Т.е. изменение одного свойства изменит тот же объект / свойство в исходном списке.
Марк Г
22
После небольшой модификации вы также можете клонировать:
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;}
Не забудьте, что T должен быть сериализуемым, иначе вы получите System.Runtime.Serialization.SerializationException.
Бенс Вегерт
Хороший ответ. Один совет: вы можете добавить if (!obj.GetType().IsSerializable) return default(T);в качестве первого утверждения, которое предотвращает исключение. И если вы измените его на метод расширения, вы можете даже использовать оператор Элвиса, как var b = a?.DeepClone();( var a = new List<string>() { "a", "b" }; например, приведенный).
Мэтт
15
Если вам не нужен фактический клон каждого отдельного объекта внутри List<T>, лучший способ клонировать список - создать новый список со старым списком в качестве параметра коллекции.
Дело не в разнице в скорости, а в удобочитаемости. Если бы я пришел к этой строке кода, я бы ударил себя по голове и удивился, почему они представили стороннюю библиотеку для сериализации, а затем десериализации объекта, который я бы не знал, почему это происходит. Кроме того, это не будет работать для списка моделей с объектами, которые имеют круглую структуру.
Джонатон Цвик,
1
Этот код отлично работал для меня для глубокого клонирования. Приложение переносит шаблонный документ с Dev на QA на Prod. Каждый объект представляет собой пакет из нескольких объектов шаблона документа, и каждый документ, в свою очередь, состоит из списка объектов абзаца. Этот код позволяет мне сериализовать «исходные» объекты .NET и немедленно десериализовать их в новые «целевые» объекты, которые затем сохраняются в базе данных SQL в другой среде. После множества исследований я нашел много вещей, большая часть которых была слишком громоздкой, и решил попробовать это. Этот короткий и гибкий подход был «просто правильным»!
Мой друг Грегор Мартинович и я придумали это простое решение с использованием JavaScript-сериализатора. Нет необходимости помечать классы как Serializable, и в наших тестах использование Newtonsoft JsonSerializer даже быстрее, чем использование BinaryFormatter. С методами расширения, применимыми к каждому объекту.
Стандартная опция .NET JavascriptSerializer:
publicstatic T DeepCopy<T>(this T value){JavaScriptSerializer js =newJavaScriptSerializer();string json = js.Serialize(value);return js.Deserialize<T>(json);}
Я буду счастлив, если кто-нибудь когда-нибудь прочтет это ... но чтобы не возвращать список объектов типа в моих методах Clone, я создал интерфейс:
publicinterfaceIMyCloneable<T>{
T Clone();}
Затем я указал расширение:
publicstaticList<T>Clone<T>(thisList<T> listToClone)where T :IMyCloneable<T>{return listToClone.Select(item =>(T)item.Clone()).ToList();}
И вот реализация интерфейса в моем программном обеспечении маркировки A / V. Я хотел, чтобы мой метод Clone () возвращал список VidMark (в то время как интерфейс ICloneable хотел, чтобы мой метод возвращал список объектов):
И, наконец, использование расширения внутри класса:
privateList<VidMark>_VidMarks;privateList<VidMark>_UndoVidMarks;//Other methods instantiate and fill the listsprivatevoidSetUndoVidMarks(){_UndoVidMarks=_VidMarks.Clone();}
Вы также можете просто преобразовать список в массив с помощью ToArray, а затем клонировать массив с помощью Array.Clone(...). В зависимости от ваших потребностей методы, включенные в класс Array, могут удовлетворить ваши потребности.
Это не работает; изменения значений в клонированном массиве все еще изменяют значения в исходном списке.
Ящерица Бернулли
Вы можете использовать var clonedList = ListOfStrings.ConvertAll (p => p); как дано @IbrarMumtaz .... Работает эффективно ... Изменения в одном списке хранятся при себе и не отражаются в другом
zainul
2
Вы можете использовать метод расширения:
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;}};}
Вы можете клонировать все объекты, используя их членов типа значения, например, рассмотрим этот класс:
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 }};
Примечание: если вы внесете какие-либо изменения в копию (или клон), это не повлияет на исходный объект.
Кажется, некоторые коллекции (например, SelectedItems DataGrid в Silverlight) пропускают реализацию CopyTo, что является проблемой при таком подходе
Джордж Бирбилис
1
Я использую Autopper для копирования объекта. Я просто настроил отображение, которое отображает один объект на себя. Вы можете обернуть эту операцию любым удобным вам способом.
Для глубокой копии ICloneable является правильным решением, но вот аналогичный подход к ICloneable с использованием конструктора вместо интерфейса 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();
вам понадобится следующая библиотека, где вы делаете копию
using System.Linq
Вы также можете использовать цикл for вместо System.Linq, но Linq делает его лаконичным и понятным. Точно так же вы могли бы делать, как предлагали другие ответы, и делать методы расширения и т. Д., Но ничего из этого не требуется.
Это называется «конструктор копирования». Это подход, подверженный ошибкам, всякий раз, когда вы добавляете новое поле в Student, вы должны помнить, чтобы добавить его в конструктор копирования. Основная идея «клона» состоит в том, чтобы избежать этой проблемы.
Кенно
2
Даже с ICloneable у вас должен быть метод «Клон» в вашем классе. Если вы не используете отражение (которое вы могли бы также использовать в вышеупомянутом подходе), этот метод Clone будет выглядеть очень похоже на подход конструктора копирования, описанный выше, и будет страдать от той же проблемы необходимости обновления для новых / измененных полей. Но это говорит о том, что «класс должен обновляться при изменении полей класса». Конечно, это так;)
ztorstri
0
Следующий код должен перейти в список с минимальными изменениями.
В основном это работает, вставляя новое случайное число из большего диапазона с каждым последующим циклом. Если уже существуют числа, которые совпадают или превышают его, сдвиньте эти случайные числа на единицу, чтобы они перешли в новый больший диапазон случайных индексов.
// 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;}
Другое дело: вы могли бы использовать отражение. Если вы кешируете это правильно, то он клонирует 1 000 000 объектов за 5,6 секунды (к сожалению, 16,4 секунды с внутренними объектами).
[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;}
Я измерил это простым способом, используя класс 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 - просто мой тестовый класс, где у меня есть дюжина тестов, включая использование выражений. Вы можете реализовать это в другой форме в расширении или как угодно. Не забывайте о кешировании.
Я еще не тестировал сериализацию, но сомневаюсь в улучшении с миллионом классов. Я попробую что-нибудь быстрое protobuf / newton.
PS: для простоты чтения я использовал здесь только авто-свойства. Я мог бы обновить с FieldInfo, или вы должны легко реализовать это самостоятельно.
Я недавно протестировал сериализатор Protocol Buffers с функцией DeepClone из коробки. Он выигрывает за 4,2 секунды на миллионе простых объектов, но когда дело доходит до внутренних объектов, он выигрывает с результатом 7,4 секунды.
Serializer.DeepClone(personList);
РЕЗЮМЕ: Если у вас нет доступа к классам, это поможет. В противном случае это зависит от количества объектов. Я думаю, что вы могли бы использовать отражение до 10 000 объектов (возможно, немного меньше), но для большего, чем этот, сериализатор Protocol Buffers будет работать лучше.
Существует простой способ клонирования объектов в C # с использованием сериализатора и десериализатора JSON.
Вы можете создать класс расширения:
using Newtonsoft.Json;staticclass typeExtensions
{[Extension()]publicstatic T jsonCloneObject<T>(T source){string json =JsonConvert.SerializeObject(source);returnJsonConvert.DeserializeObject<T>(json);}}
clone()
по определению не глубокая копия? В C # вы можете легко передавать указатели с помощью =, подумал я.Ответы:
Вы можете использовать метод расширения.
источник
Если ваши элементы являются типами значений, то вы можете просто сделать:
Однако, если они являются ссылочными типами и вы хотите глубокое копирование (при условии правильной реализации ваших элементов
ICloneable
), вы можете сделать что-то вроде этого:Очевидно, замените
ICloneable
в вышеприведенных обобщениях и приведите к какому-либо типу вашего элемента, который реализуетICloneable
.Если ваш тип элемента не поддерживает,
ICloneable
но имеет конструктор копирования, вы можете сделать это вместо этого:Лично я бы избежал
ICloneable
из-за необходимости гарантировать глубокую копию всех участников. Вместо этого я бы предложил конструктор копирования или такой метод фабрики,YourType.CopyFrom(YourType itemToCopy)
который возвращает новый экземплярYourType
.Любой из этих параметров может быть заключен в метод (расширение или иным образом).
источник
ICloneable
было определено, определение никогда не указывало, был ли клон глубоким или неглубоким, поэтому вы не можете определить, какой тип операции клонирования будет выполняться, когда объект реализует ее. Это означает, что если вы хотите сделать глубокий клонList<T>
, вам придется делать это, неICloneable
будучи уверенным, что это глубокая копия.newList.AddRange(oldList.Select(i => i.Clone())
илиnewList.AddRange(oldList.Select(i => new YourType(i)
)Для поверхностной копии вы можете вместо этого использовать метод GetRange универсального класса List.
Цитируется из: Рецепты дженериков
источник
List<int> newList = oldList.ToList()
. Тот же эффект. Однако, на мой взгляд, решение Arkiliknam лучше всего подходит для удобства чтения.Это один из способов сделать это с C # и .NET 2.0. Ваш объект должен быть
[Serializable()]
. Цель состоит в том, чтобы потерять все ссылки и построить новые.источник
Чтобы клонировать список, просто вызовите .ToList (). Это создает мелкую копию.
источник
После небольшой модификации вы также можете клонировать:
источник
if (!obj.GetType().IsSerializable) return default(T);
в качестве первого утверждения, которое предотвращает исключение. И если вы измените его на метод расширения, вы можете даже использовать оператор Элвиса, какvar b = a?.DeepClone();
(var a = new List<string>() { "a", "b" };
например, приведенный).Если вам не нужен фактический клон каждого отдельного объекта внутри
List<T>
, лучший способ клонировать список - создать новый список со старым списком в качестве параметра коллекции.Изменения,
myList
такие как вставка или удаление, не влияютcloneOfMyList
и наоборот.Однако фактические объекты, содержащиеся в двух списках, остаются прежними.
источник
Использовать AutoMapper (или любую другую библиотеку картирования, которую вы предпочитаете), чтобы клонировать, просто и много поддерживаемо.
Определите ваше отображение:
Сделай магию:
источник
Если вы заботитесь только о типах значений ...
И вы знаете тип:
Если вы не знаете тип раньше, вам понадобится вспомогательная функция:
Просто:
источник
Если вы уже ссылались на Newtonsoft.Json в своем проекте и ваши объекты сериализуемы, вы всегда можете использовать:
Возможно, это не самый эффективный способ сделать это, но если вы не сделаете это сотни тысяч раз, вы можете даже не заметить разницу в скорости.
источник
источник
источник
источник
Мой друг Грегор Мартинович и я придумали это простое решение с использованием JavaScript-сериализатора. Нет необходимости помечать классы как Serializable, и в наших тестах использование Newtonsoft JsonSerializer даже быстрее, чем использование BinaryFormatter. С методами расширения, применимыми к каждому объекту.
Стандартная опция .NET JavascriptSerializer:
Более быстрый вариант с использованием Newtonsoft JSON :
источник
источник
Я буду счастлив, если кто-нибудь когда-нибудь прочтет это ... но чтобы не возвращать список объектов типа в моих методах Clone, я создал интерфейс:
Затем я указал расширение:
И вот реализация интерфейса в моем программном обеспечении маркировки A / V. Я хотел, чтобы мой метод Clone () возвращал список VidMark (в то время как интерфейс ICloneable хотел, чтобы мой метод возвращал список объектов):
И, наконец, использование расширения внутри класса:
Кто-нибудь любит это? Какие-нибудь улучшения?
источник
Вы также можете просто преобразовать список в массив с помощью
ToArray
, а затем клонировать массив с помощьюArray.Clone(...)
. В зависимости от ваших потребностей методы, включенные в класс Array, могут удовлетворить ваши потребности.источник
Вы можете использовать метод расширения:
Вы можете клонировать все объекты, используя их членов типа значения, например, рассмотрим этот класс:
Примечание: если вы внесете какие-либо изменения в копию (или клон), это не повлияет на исходный объект.
источник
Если вам нужен клонированный список с той же емкостью, вы можете попробовать это:
источник
Я сделал для себя какое-то расширение, которое конвертирует ICollection элементов, которые не реализуют IClonable
источник
Я использую Autopper для копирования объекта. Я просто настроил отображение, которое отображает один объект на себя. Вы можете обернуть эту операцию любым удобным вам способом.
http://automapper.codeplex.com/
источник
Использование приведения может быть полезно, в этом случае, для мелкой копии:
применяется к общему списку:
источник
Для глубокой копии ICloneable является правильным решением, но вот аналогичный подход к ICloneable с использованием конструктора вместо интерфейса ICloneable.
вам понадобится следующая библиотека, где вы делаете копию
Вы также можете использовать цикл for вместо System.Linq, но Linq делает его лаконичным и понятным. Точно так же вы могли бы делать, как предлагали другие ответы, и делать методы расширения и т. Д., Но ничего из этого не требуется.
источник
Следующий код должен перейти в список с минимальными изменениями.
В основном это работает, вставляя новое случайное число из большего диапазона с каждым последующим циклом. Если уже существуют числа, которые совпадают или превышают его, сдвиньте эти случайные числа на единицу, чтобы они перешли в новый больший диапазон случайных индексов.
источник
Другое дело: вы могли бы использовать отражение. Если вы кешируете это правильно, то он клонирует 1 000 000 объектов за 5,6 секунды (к сожалению, 16,4 секунды с внутренними объектами).
Я измерил это простым способом, используя класс Watcher.
РЕЗУЛЬТАТ: С внутренним объектом PersonInstance - 16.4, PersonInstance = null - 5.6
CopyFactory - просто мой тестовый класс, где у меня есть дюжина тестов, включая использование выражений. Вы можете реализовать это в другой форме в расширении или как угодно. Не забывайте о кешировании.
Я еще не тестировал сериализацию, но сомневаюсь в улучшении с миллионом классов. Я попробую что-нибудь быстрое protobuf / newton.
PS: для простоты чтения я использовал здесь только авто-свойства. Я мог бы обновить с FieldInfo, или вы должны легко реализовать это самостоятельно.
Я недавно протестировал сериализатор Protocol Buffers с функцией DeepClone из коробки. Он выигрывает за 4,2 секунды на миллионе простых объектов, но когда дело доходит до внутренних объектов, он выигрывает с результатом 7,4 секунды.
РЕЗЮМЕ: Если у вас нет доступа к классам, это поможет. В противном случае это зависит от количества объектов. Я думаю, что вы могли бы использовать отражение до 10 000 объектов (возможно, немного меньше), но для большего, чем этот, сериализатор Protocol Buffers будет работать лучше.
источник
Существует простой способ клонирования объектов в C # с использованием сериализатора и десериализатора JSON.
Вы можете создать класс расширения:
Чтобы клонировать и возражать:
источник