У меня есть общий список объектов на C#, и я хочу его клонировать. Элементы в списке можно клонировать, но, похоже, нет возможности выполнить list.Clone().
Есть ли простой способ обойти это?
Что такое глубокие и мелкие копии?
@ColonelPanic en.wikipedia.org/wiki/Object_copy#Shallow_copy
@orip Разве clone() по определению не является глубокой копией? Я подумал, что в C# вы можете легко передавать указатели с помощью =.
@Chris мелкая копия копирует на один уровень глубже, чем указатель. Например, неглубокая копия списка будет иметь те же элементы, но будет другим списком.
Где глубокая копия будет новым списком с новыми элементами, но с тем же содержанием.
Отметьте этот Отвечать: stackoverflow.com/a/52097307/4707576 about: Клонирование объектов без сериализации





Для неглубокой копии вместо этого можно использовать метод GetRange универсального класса List.
List<int> oldList = new List<int>( );
// Populate oldList...
List<int> newList = oldList.GetRange(0, oldList.Count);
Цитируется по: Рецепты дженериков
Вы также можете добиться этого, используя конструктор List <T>, чтобы указать List <T>, из которого выполняется копирование. например, var shallowClonedList = новый список <MyObject> (originalList);
Я часто использую List<int> newList = oldList.ToList(). Тот же эффект. Однако, на мой взгляд, решение Arkiliknam лучше всего подходит для удобочитаемости.
@DanBechard Спустя годы, но я предпочитаю ToList, поскольку он избегает всех дублирований - интересно, что на самом деле более производительно ... посмотрел. Похоже, список ToList вызывает new List<T>, который в конечном итоге будет использовать Array.CopyTo, так что примерно то же самое.
Если ваши элементы являются типами значений, вы можете просто сделать:
List<YourType> newList = new List<YourType>(oldList);
Однако, если это ссылочные типы, и вам нужна глубокая копия (при условии, что ваши элементы правильно реализуют ICloneable), вы можете сделать что-то вроде этого:
List<ICloneable> oldList = new List<ICloneable>();
List<ICloneable> newList = new List<ICloneable>(oldList.Count);
oldList.ForEach((item) =>
{
newList.Add((ICloneable)item.Clone());
});
Очевидно, замените ICloneable в приведенных выше универсальных шаблонах и приведите их к любому типу вашего элемента, который реализует ICloneable.
Если ваш тип элемента не поддерживает ICloneable, но имеет конструктор копирования, вы можете сделать это вместо этого:
List<YourType> oldList = new List<YourType>();
List<YourType> newList = new List<YourType>(oldList.Count);
oldList.ForEach((item)=>
{
newList.Add(new YourType(item));
});
Лично я бы избегал ICloneable из-за необходимости гарантировать полную копию всех участников. Вместо этого я бы предложил конструктор копирования или фабричный метод, такой как YourType.CopyFrom(YourType itemToCopy), который возвращает новый экземпляр YourType.
Любой из этих параметров может быть обернут методом (расширением или другим способом).
Я думаю, что List <T> .ConvertAll может выглядеть лучше, чем создание нового списка и выполнение foreach + add.
Хорошая точка зрения. Должен признать, я все еще сам разбираюсь со всеми вызовами LINQ.
+1 Таким образом, невозможно обеспечить функцию глубокого клонирования для Generic.List. Это правильно?
@Dimitri: Нет, это неправда. Проблема в том, что при определении ICloneable в определении никогда не указывалось, был ли клон глубоким или неглубоким, поэтому вы не можете определить, какой тип операции клонирования будет выполняться, когда объект реализует ее. Это означает, что если вы хотите сделать глубокий клон List<T>, вам придется сделать это без ICloneable, чтобы быть уверенным, что это глубокая копия.
Почему бы не использовать метод AddRange? (newList.AddRange(oldList.Select(i => i.Clone()) или newList.AddRange(oldList.Select(i => new YourType(i))
@phoog: Конечно, это еще один вариант. Это не имеет большого значения.
@ Джефф Йейтс: имеет значение, если аргумент коллекции для AddRange реализует ICollection <T>. В этом случае список использует свойство count коллекции, поэтому ему не нужно увеличивать его емкость более одного раза. Это (потенциально) экономит выделение памяти. Конечно, в большинстве случаев экономия незначительна, так что по большей части вы правы, это не имеет большого значения.
@phoog: Я бы избегал такой оптимизации, если только профилирование не показало, что это необходимо.
@ Джефф Йейтс: Достаточно честно. Я работал с приложением, которое использует огромные списки, массивы которых достаточно велики, чтобы находиться в куче больших объектов, так что в этом случае это полезная оптимизация. Мне любопытно, почему вы этого избегаете.
@phoog: Я думаю, что при сканировании кода он немного менее читабелен / понятен, вот и все. Для меня удобочитаемость.
почему бы не использовать ToArray () - который действительно создает глубокую копию - он эффективно использует memmove
@Feanor: Это не совсем так. Он копирует значения непосредственных элементов, но как насчет дочерних значений? Как он узнает, должна ли каждая ссылка в моем типе быть собственной уникальной копией или общим экземпляром? Это не так. Глубокая копия - это копия, которая просматривает весь граф объектов для экземпляра и гарантирует, что все элементы, которые он содержит или ссылки, также являются копиями, а их ссылки являются копиями и т. д. И т. Д.
да, конечно вы правы, извините, очень маловероятно, что класс состоит только из типов значений
@JeffYates: Одна недостаточно продуманная проблема заключается в том, что обычно вещи нужно копировать только в том случае, если существует некоторый путь выполнения, который их мутирует. Обычно в очень неизменяемые типы содержат ссылку на экземпляр изменяемого типа, но никогда не подвергают этот экземпляр чему-либо, что может его изменить. Излишнее копирование того, что никогда не изменится, иногда может привести к снижению производительности крупный, увеличивая использование памяти на порядки.
@supercat: Действительно, вам решать, что нужно клонировать при клонировании объекта, что является еще одной причиной, по которой клонирование может быть настолько трудным.
@JeffYates: Прискорбно то, что правила не сложные - они просто зависят от чего-то, что система типов не имеет стандартного способа выражения. Если бы система типов могла украсить типы мест хранения парой атрибутов флагов, чтобы указать, инкапсулируют ли они идентичность, изменяемое состояние, оба или ни то, ни другое, эффективное и надежное глубокое клонирование было бы простым в 99% случаев, даже для объектов, которые по своей сути не сериализуемы. Обратите внимание, что это должны быть черты типов мест хранения, а не типы кучи и не поля.
Если Identity<T> рассматривался как ссылка на T, используемый только для инкапсуляции идентичности, а MutableState<T> использовался для ссылки на тот, который инкапсулирует изменяемое состояние, но не идентичность, копия MutableState<List<Identity<T>>> должна содержать ссылку на новый список со ссылками на одинаковые предметы; копия MutableState<List<MutableState<T>>> должна содержать ссылку на новый список с копиями элементов в нем.
На первый взгляд, это может показаться слишком сложным решением серьезной проблемы. Вместо этого реализация объектов для поддержки сериализации гарантирует, что сериализацию / десериализацию объектов можно будет использовать для их клонирования, а затем разработчик каждого объекта будет контролировать, что для них означает клонирование.
Мне просто нужно было напомнить о первой строке - поэтому я предпочитаю этот ответ, спасибо @JeffYates!
Я согласен с @phoog, но если вы думаете, что AddRange с Linq менее читабельны, то почему бы просто не использовать foreach. ForEachсложнее понять, сложнее отлаживать и вводит семантику закрытия
Я думаю, что продолжение @supercat состоит в том, что в вашей текущей таксономии есть дыра. У вас есть типы значений и ссылочные типы, которые вы можете изменить, но не ссылочные типы, для которых уместны мелкие копии. Возможно: «Если ваши элементы являются типами значений[или ссылочные типы, где вас беспокоит только список членство], тогда вы можете просто сделать [мелкий конструктор клонов]». С ссылочными типами вы можете захотеть отредактировать членство newList, не влияя на членство в oldList, хотя вы хотите редактировать общие элементы. TL; DR: Может быть, явным образом сделать мелкие и глубокие варианты использования с ссылочными типами.
Если вас интересуют только типы значений ...
И вы знаете тип:
List<int> newList = new List<int>(oldList);
Если вы раньше не знали тип, вам понадобится вспомогательная функция:
List<T> Clone<T>(IEnumerable<T> oldList)
{
return newList = new List<T>(oldList);
}
Справедливый:
List<string> myNewList = Clone(myOldList);
Это не клонирует элементы.
Вы можете использовать метод расширения.
static class Extensions
{
public static IList<T> Clone<T>(this IList<T> listToClone) where T: ICloneable
{
return listToClone.Select(item => (T)item.Clone()).ToList();
}
}
Я думаю, что List.ConvertAll может сделать это быстрее, поскольку он может предварительно выделить весь массив для списка, вместо того, чтобы постоянно изменять размер.
@MichaelGG, что, если вы не хотите конвертировать, а просто клонируете / дублируете элементы в списке? Это сработает? || var clonedList = ListOfStrings.ConvertAll (p => p);
@IbrarMumtaz: это то же самое, что и var clonedList = new List <string> (ListOfStrings);
Хорошее решение! Между прочим, я предпочитаю общедоступный статический List <T> CLone <T> ... Это более полезно в таких случаях, потому что никаких дополнительных приведений не требуется: List <MyType> cloned = listToClone.Clone ();
@BrandonArnold, вы ошибаетесь, а что, если T - объект, а не тип значения?
@BrianOgden Разве делегат ConvertAll в его примере также не сохраняет ссылочные типы? Стрелочной функции потребуется конструктор копирования или сериализатор клонирования (например, p => new PType (p)).
Извините, @BrandonArnold, вы правы, я думал, что ваш комментарий относится к ответу, вы комментируете комментарий от IbrarMumtaz, мое плохое, продолжайте, как вы были :)
Разве это не половина ответа, поскольку он основан на реализации ICloneable, что было бы важной частью вопроса?
Я согласен, что это только половина ответа, поскольку в нем отсутствует часть IClonable.
public static object DeepClone(object obj)
{
object objResult = null;
using (var ms = new MemoryStream())
{
var bf = new BinaryFormatter();
bf.Serialize(ms, obj);
ms.Position = 0;
objResult = bf.Deserialize(ms);
}
return objResult;
}
Это один из способов сделать это с помощью C# и .NET 2.0. Ваш объект должен быть [Serializable()]. Цель состоит в том, чтобы потерять все ссылки и создать новые.
+1 - мне нравится этот ответ - он быстрый, грязный, противный и очень эффективный. Я использовал в silverlight и использовал DataContractSerializer, поскольку BinarySerializer был недоступен. Кому нужно писать страницы кода клонирования объектов, если вы можете просто это сделать? :)
Мне это нравится. Хотя делать что-то «правильно» приятно, быстро и грязно часто бывает кстати.
Быстро! но: почему грязный?
Это глубокое клонирование, быстрое и легкое. Внимательно отнеситесь к другим предложениям на этой странице. Я пробовал несколько, и они не имеют глубокого клонирования.
Мне нравится гадкое и грязное ^^ Это похоже на JSON.parse (JSON.stringify (obj)). Блестяще!
Единственный отрицательный аспект, если его можно так назвать, заключается в том, что ваши классы должны быть помечены как Serializable, чтобы это работало.
public static Object CloneType(Object objtype)
{
Object lstfinal = new Object();
using (MemoryStream memStream = new MemoryStream())
{
BinaryFormatter binaryFormatter = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.Clone));
binaryFormatter.Serialize(memStream, objtype); memStream.Seek(0, SeekOrigin.Begin);
lstfinal = binaryFormatter.Deserialize(memStream);
}
return lstfinal;
}
После небольшой модификации вы также можете клонировать:
public static T DeepClone<T>(T obj)
{
T objResult;
using (MemoryStream ms = new MemoryStream())
{
BinaryFormatter bf = new BinaryFormatter();
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" };).
public class CloneableList<T> : List<T>, ICloneable where T : ICloneable
{
public object Clone()
{
var clone = new List<T>();
ForEach(item => clone.Add((T)item.Clone()));
return clone;
}
}
Использовать AutoMapper (или любую другую библиотеку сопоставления, которую вы предпочитаете) для клонирования просто и легко обслуживается.
Определите свое отображение:
Mapper.CreateMap<YourType, YourType>();
Творите волшебство:
YourTypeList.ConvertAll(Mapper.Map<YourType, YourType>);
Вы можете использовать метод расширения:
namespace extension
{
public class ext
{
public static List<double> clone(this List<double> t)
{
List<double> kop = new List<double>();
int x;
for (x = 0; x < t.Count; x++)
{
kop.Add(t[x]);
}
return kop;
}
};
}
Вы можете клонировать все объекты, используя их члены типа значения, например, рассмотрим этот класс:
public class matrix
{
public List<List<double>> mat;
public int 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 so
int 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 cloned
return copy; // and copy of original is returned
}
};
Примечание: если вы внесете какие-либо изменения в копию (или клон), это не повлияет на исходный объект.
Я сделал для себя какое-то расширение, которое преобразует ICollection элементов, которые не реализуют IClonable.
static class CollectionExtensions
{
public static ICollection<T> Clone<T>(this ICollection<T> listToClone)
{
var array = new T[listToClone.Count];
listToClone.CopyTo(array,0);
return array.ToList();
}
}
кажется, что некоторые коллекции (например, DataGrid SelectedItems в Silverlight) пропускают реализацию CopyTo, что является проблемой с этим подходом
Если вы уже ссылались на Newtonsoft.Json в своем проекте и ваши объекты сериализуемы, вы всегда можете использовать:
List<T> newList = JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(listToCopy))
Возможно, это не самый эффективный способ сделать это, но если вы не делаете это 100 из 1000 раз, вы можете даже не заметить разницу в скорости.
Дело не в разнице в скорости, а в читабельности. Если бы я подошел к этой строке кода, я бы хлопнул себя по голове и удивился, почему они ввели стороннюю библиотеку для сериализации, а затем десериализации объекта, а я бы понятия не имел, почему это происходит. Кроме того, это не сработает для списка моделей с объектами, имеющими круговую структуру.
Этот код отлично работал у меня при глубоком клонировании. Приложение переносит шаблон документа с Dev на QA на Prod. Каждый объект представляет собой пакет из нескольких объектов шаблона документа, а каждый документ, в свою очередь, состоит из списка объектов абзаца. Этот код позволяет мне сериализовать «исходные» объекты .NET и немедленно десериализовать их в новые «целевые» объекты, которые затем сохраняются в базе данных SQL в другой среде. После множества исследований я нашел много вещей, многие из которых были слишком громоздкими, и решил попробовать это. Этот короткий и гибкий подход был «в самый раз»!
Я использую automapper для копирования объекта. Я просто настраиваю отображение, которое сопоставляет один объект самому себе. Вы можете обернуть эту операцию как хотите.
Вы также можете просто преобразовать список в массив с помощью ToArray, а затем клонировать массив с помощью Array.Clone(...).
В зависимости от ваших потребностей методы, включенные в класс Array, могут удовлетворить ваши потребности.
Это не работает; изменения значений в клонированном массиве ВСЕ ЕЩЕ изменяют значения в исходном списке.
вы можете использовать var clonedList = ListOfStrings.ConvertAll (p => p); как предоставлено @IbrarMumtaz .... Работает эффективно ... Изменения в одном списке хранятся в себе и не отражаются в другом
Если вам не нужен фактический клон каждого отдельного объекта внутри вашего List<T>, лучший способ клонировать список - создать новый список со старым списком в качестве параметра коллекции.
List<T> myList = ...;
List<T> cloneOfMyList = new List<T>(myList);
Изменения в myList, такие как вставка или удаление, не повлияют на cloneOfMyList и наоборот.
Однако фактические объекты, содержащиеся в двух списках, остаются прежними.
Я согласен с user49126, я вижу, что это неглубокая копия, и изменения, внесенные в один список, отражаются в другом списке.
@Seidleroni, вы ошибаетесь. Изменения, внесенные в список itens, влияют на другой список, а изменения в самом списке - нет.
Это мелкая копия.
Как это мелкая копия?
@WellingtonZanelli Только что подтвердил, что удаление элемента из myList удаляет его и из cloneOfMyList.
Следующий код должен быть перенесен в список с минимальными изменениями.
В основном это работает путем вставки нового случайного числа из большего диапазона с каждым последующим циклом. Если уже существуют такие же или большие числа, сдвиньте эти случайные числа на единицу вверх, чтобы они перешли в новый больший диапазон случайных индексов.
// Example Usage
int[] indexes = getRandomUniqueIndexArray(selectFrom.Length, toSet.Length);
for(int i = 0; i < toSet.Length; i++)
toSet[i] = selectFrom[indexes[i]];
private int[] getRandomUniqueIndexArray(int length, int count)
{
if (count > length || count < 1 || length < 1)
return new int[0];
int[] toReturn = new int[count];
if (count == length)
{
for(int i = 0; i < toReturn.Length; i++) toReturn[i] = i;
return toReturn;
}
Random r = new Random();
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;
}
Другое дело: вы можете использовать отражение. Если вы правильно кэшируете это, то он клонирует 1000000 объектов за 5,6 секунды (к сожалению, 16,4 секунды с внутренними объектами).
[ProtoContract(ImplicitFields = ImplicitFields.AllPublic)]
public class Person
{
...
Job JobDescription
...
}
[ProtoContract(ImplicitFields = ImplicitFields.AllPublic)]
public class Job
{...
}
private static readonly Type stringType = typeof (string);
public static class CopyFactory
{
static readonly Dictionary<Type, PropertyInfo[]> ProperyList = new Dictionary<Type, PropertyInfo[]>();
private static readonly MethodInfo CreateCopyReflectionMethod;
static CopyFactory()
{
CreateCopyReflectionMethod = typeof(CopyFactory).GetMethod("CreateCopyReflection", BindingFlags.Static | BindingFlags.Public);
}
public static 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)
{
var value = prop.GetValue(source, null);
prop.SetValue(copyInstance,
value != null && prop.PropertyType.IsClass && prop.PropertyType != stringType ? CreateCopyReflectionMethod.MakeGenericMethod(prop.PropertyType).Invoke(null, new object[] { value }) : value, null);
}
return copyInstance;
}
Я измерил это простым способом, используя класс Watcher.
var person = new Person
{
...
};
for (var i = 0; i < 1000000; i++)
{
personList.Add(person);
}
var watcher = new Stopwatch();
watcher.Start();
var copylist = personList.Select(CopyFactory.CreateCopyReflection).ToList();
watcher.Stop();
var elapsed = watcher.Elapsed;
РЕЗУЛЬТАТ: С внутренним объектом PersonInstance - 16.4, PersonInstance = null - 5.6
CopyFactory - это просто мой тестовый класс, в котором у меня есть дюжина тестов, включая использование выражения. Вы можете реализовать это в другой форме в расширении или в чем-то еще. Не забывайте о кешировании.
Я еще не тестировал сериализацию, но сомневаюсь в улучшении с помощью миллиона классов. Попробую что-нибудь быстрое protobuf / newton.
P.S .: для простоты чтения я использовал здесь только auto-property. Я мог бы обновить с помощью FieldInfo, или вы должны легко реализовать это самостоятельно.
Недавно я протестировал сериализатор Буферы протокола с функцией DeepClone из коробки. Он выигрывает с результатом 4,2 секунды на миллионе простых объектов, но когда дело доходит до внутренних объектов, он выигрывает с результатом 7,4 секунды.
Serializer.DeepClone(personList);
РЕЗЮМЕ: Если у вас нет доступа к классам, то это поможет. В противном случае это зависит от количества объектов. Я думаю, вы могли бы использовать отражение до 10000 объектов (может быть, немного меньше), но для большего, чем это, сериализатор протокольных буферов будет работать лучше.
Если вам нужен клонированный список с такой же емкостью, вы можете попробовать следующее:
public static List<T> Clone<T>(this List<T> oldList)
{
var newList = new List<T>(oldList.Capacity);
newList.AddRange(oldList);
return newList;
}
public List<TEntity> Clone<TEntity>(List<TEntity> o1List) where TEntity : class , new()
{
List<TEntity> retList = new List<TEntity>();
try
{
Type sourceType = typeof(TEntity);
foreach(var o1 in o1List)
{
TEntity o2 = new TEntity();
foreach (PropertyInfo propInfo in (sourceType.GetProperties()))
{
var val = propInfo.GetValue(o1, null);
propInfo.SetValue(o2, val);
}
retList.Add(o2);
}
return retList;
}
catch
{
return retList;
}
}
Нет необходимости отмечать классы как Serializable, и в наших тестах с использованием Newtonsoft JsonSerializer даже быстрее, чем с использованием BinaryFormatter. С методами расширения, используемыми для каждого объекта.
Стандартный вариант .NET JavascriptSerializer:
public static T DeepCopy<T>(this T value)
{
JavaScriptSerializer js = new JavaScriptSerializer();
string json = js.Serialize(value);
return js.Deserialize<T>(json);
}
Более быстрый вариант с использованием Newtonsoft JSON:
public static T DeepCopy<T>(this T value)
{
string json = JsonConvert.SerializeObject(value);
return JsonConvert.DeserializeObject<T>(json);
}
Частные члены не клонируются с использованием метода JSON. stackoverflow.com/a/78612/885627
Грегор Мартинович?
Существует простой способ клонировать объекты в C# с помощью сериализатора и десериализатора JSON.
Вы можете создать класс расширения:
using Newtonsoft.Json;
static class typeExtensions
{
[Extension()]
public static T jsonCloneObject<T>(T source)
{
string json = JsonConvert.SerializeObject(source);
return JsonConvert.DeserializeObject<T>(json);
}
}
Чтобы клонировать и возражать:
obj clonedObj = originalObj.jsonCloneObject;
Чтобы клонировать список, просто вызовите .ToList (). Это создает неглубокую копию.
Microsoft (R) Roslyn C# Compiler version 2.3.2.62116
Loading context from 'CSharpInteractive.rsp'.
Type "#help" for more information.
> var x = new List<int>() { 3, 4 };
> var y = x.ToList();
> x.Add(5)
> x
List<int>(3) { 3, 4, 5 }
> y
List<int>(2) { 3, 4 }
>
Небольшое предупреждение, это неглубокая копия ... Это создаст два объекта списка, но объекты внутри будут такими же. Т.е. изменение одного свойства изменит тот же объект / свойство в исходном списке.
//try this
List<string> ListCopy= new List<string>(OldList);
//or try
List<T> ListCopy=OldList.ToList();
Мне повезет, если кто-нибудь когда-нибудь это прочитает ... но, чтобы не возвращать список типов объектов в моих методах Clone, я создал интерфейс:
public interface IMyCloneable<T>
{
T Clone();
}
Затем я указал расширение:
public static List<T> Clone<T>(this List<T> listToClone) where T : IMyCloneable<T>
{
return listToClone.Select(item => (T)item.Clone()).ToList();
}
А вот реализация интерфейса в моем программном обеспечении для A / V-маркировки. Я хотел, чтобы мой метод Clone () возвращал список VidMark (в то время как интерфейс ICloneable хотел, чтобы мой метод возвращал список объектов):
public class VidMark : IMyCloneable<VidMark>
{
public long Beg { get; set; }
public long End { get; set; }
public string Desc { get; set; }
public int Rank { get; set; } = 0;
public VidMark Clone()
{
return (VidMark)this.MemberwiseClone();
}
}
И, наконец, использование расширения внутри класса:
private List<VidMark> _VidMarks;
private List<VidMark> _UndoVidMarks;
//Other methods instantiate and fill the lists
private void SetUndoVidMarks()
{
_UndoVidMarks = _VidMarks.Clone();
}
Кому-нибудь это нравится? Есть улучшения?
В этом случае для неглубокой копии может быть полезно использовать приведение:
IList CloneList(IList list)
{
IList result;
result = (IList)Activator.CreateInstance(list.GetType());
foreach (object item in list) result.Add(item);
return result;
}
применяется к общему списку:
List<T> Clone<T>(List<T> argument) => (List<T>)CloneList(argument);
Для глубокой копии ICloneable - правильное решение, но здесь аналогичный подход к ICloneable с использованием конструктора вместо интерфейса ICloneable.
public class Student
{
public Student(Student student)
{
FirstName = student.FirstName;
LastName = student.LastName;
}
public string FirstName { get; set; }
public string LastName { get; set; }
}
// wherever you have the list
List<Student> students;
// and then where you want to make a copy
List<Student> copy = students.Select(s => new Student(s)).ToList();
вам понадобится следующая библиотека, в которой вы сделаете копию
using System.Linq
вы также можете использовать цикл for вместо System.Linq, но Linq делает его кратким и понятным. Точно так же вы могли бы сделать, как предлагали другие ответы, и создать методы расширения и т.д., но ничего из этого не требуется.
Это называется «конструктор копирования». Это подход, подверженный v ошибкам: всякий раз, когда вы добавляете новое поле в Student, вы должны не забывать добавлять его в конструктор копирования. Основная идея «клона» - избежать этой проблемы.
Даже с ICloneable в вашем классе должен быть метод «Clone». Если вы не используете отражение (которое вы также можете использовать в вышеупомянутом подходе), этот метод Clone будет выглядеть очень похоже на подход конструктора копирования, описанный выше, и будет страдать от той же проблемы, связанной с необходимостью обновления для новых / измененных полей. Но при этом говорится: «Класс должен обновляться при изменении полей класса». Конечно, есть;)
Для глубокого клона я использую отражение следующим образом:
public List<T> CloneList<T>(IEnumerable<T> listToClone) {
Type listType = listToClone.GetType();
Type elementType = listType.GetGenericArguments()[0];
List<T> listCopy = new List<T>();
foreach (T item in listToClone) {
object itemCopy = Activator.CreateInstance(elementType);
foreach (PropertyInfo property in elementType.GetProperties()) {
elementType.GetProperty(property.Name).SetValue(itemCopy, property.GetValue(item));
}
listCopy.Add((T)itemCopy);
}
return listCopy;
}
Вы можете использовать List или IEnumerable как взаимозаменяемые.
Вы должны сказать, ищете ли вы глубокую копию или мелкую копию