Как клонировать общий список в C#?
23 ответов
вы можете использовать метод расширения.
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<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
В приведенных выше дженериках и cast с любым типом элемента, который реализует 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
.
любой из этих параметров может быть обернут методом (расширением или иным способом).
public static object DeepClone(object obj)
{
object objResult = null;
using (MemoryStream ms = new MemoryStream())
{
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(ms, obj);
ms.Position = 0;
objResult = bf.Deserialize(ms);
}
return objResult;
}
Это один из способов сделать это с C# и .NET 2.0. Ваш объект должен быть [Serializable()]
. Цель состоит в том, чтобы потерять все ссылки и строить новые.
для неглубокой копии вместо этого можно использовать метод GetRange универсального класса List.
List<int> oldList = new List<int>( );
// Populate oldList...
List<int> newList = oldList.GetRange(0, oldList.Count);
ЦИТ. по: Универсальные Рецепты
после небольшой модификации вы также можете клонировать:
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;
}
Если вам не нужен фактический клон каждого отдельного объекта внутри вашего List<T>
, лучший способ клонировать список-создать новый список со старым списком в качестве параметра коллекции.
List<T> myList = ...;
List<T> cloneOfMyList = new List<T>(myList);
изменения myList
такие как insert или remove не повлияют на cloneOfMyList
и наоборот.
фактические объекты, которые содержат два списка, по-прежнему одинаковы.
используйте AutoMapper (или любое отображение lib, которое вы предпочитаете) для клонирования просто и много ремонтопригодно.
определите свое отображение:
Mapper.CreateMap<YourType, YourType>();
сделать волшебство:
YourTypeList.ConvertAll(Mapper.Map<YourType, YourType>);
клонировать список просто позвоните .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 }
>
Если вы заботитесь только о типах значений...
и вы знаете, типа:
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);
Если вы уже ссылались на Newtonsoft.Json в вашем проекте и ваши объекты сериализуемы, вы всегда можете использовать:
List<T> newList = JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(listToCopy))
возможно, не самый эффективный способ сделать это, но если вы не делаете это 100 раз из 1000 раз, вы можете даже не заметить разницу в скорости.
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 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;
}
}
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;
}
}
вы также можете просто преобразовать список в массив с помощью ToArray
, а затем клонировать массив, используя Array.Clone(...)
.
В зависимости от ваших потребностей методы, включенные в класс Array, могут удовлетворить ваши потребности.
Вы можете использовать метод расширения:
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
}
};
Примечание: Если вы сделаете какие-либо изменения в копии (или клонировании), это не повлияет на исходный объект.
Если вам нужен клонированный список с той же емкостью, вы можете попробовать следующее:
public static List<T> Clone<T>(this List<T> oldList)
{
var newList = new List<T>(oldList.Capacity);
newList.AddRange(oldList);
return newList;
}
мой друг Грегор Мартинович и я придумали это простое решение, используя сериализатор JavaScript. Нет необходимости помечать классы как сериализуемые и в наших тестах с использованием 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);
}
Я сделал для своего собственного расширения, которое преобразует 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();
}
}
Я использую automapper для копирования объекта. Я просто настраиваю отображение, которое сопоставляет один объект с самим собой. Вы можете завершить эту операцию любым способом.
следующий код следует перенести в список с минимальными изменениями.
в основном он работает, вставляя новое случайное число из большего диапазона с каждым последовательным циклом. Если уже существуют числа, которые совпадают или превышают его, сдвиньте эти случайные числа на единицу, чтобы они перешли в новый больший диапазон случайных индексов.
// 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;
}
еще одна вещь: вы можете использовать отражение. Если вы правильно кэшируете это, то он клонирует 1 000 000 объектов за 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);
резюме: если у вас нет доступа к классам, то это поможет. В противном случае это зависит от количества объектов. Я думаю, вы можете использовать отражение до 10 000 объектов (может быть, немного меньше), но для большего, чем это, сериализатор буферов протокола будет работать лучше.
существует простой способ клонирования объектов в 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;
//try this
List<string> ListCopy= new List<string>(OldList);
//or try
List<T> ListCopy=OldList.ToList();