如何克隆C#中的通用列表?
我有一个C#中的对象的通用列表,并希望克隆列表。 列表中的项目是可复制的,但似乎没有做list.Clone()
的选项。
有没有简单的方法呢?
您可以使用扩展方法。
static class Extensions { public static IList<T> Clone<T>(this IList<T> listToClone) where T: ICloneable { return listToClone.Select(item => (T)item.Clone()).ToList(); } }
如果你的元素是值types,那么你可以做:
List<YourType> newList = new List<YourType>(oldList);
但是,如果它们是引用types,并且您想要深度复制(假设您的元素正确实现了ICloneable
),则可以这样做:
List<ICloneable> oldList = new List<ICloneable>(); List<ICloneable> newList = new List<ICloneable>(oldList.Count); oldList.ForEach((item) => { newList.Add((ICloneable)item.Clone()); });
很显然,在上面的generics中replaceICloneable
,并且使用ICloneable
元素types进行ICloneable
。
如果你的元素types不支持ICloneable
但是有一个copy-constructor,你可以这样做:
List<YourType> oldList = new List<YourType>(); List<YourType> newList = new List<YourType>(oldList.Count); oldList.ForEach((item)=> { newList.Add(new YourType(item)); });
就个人而言,我会避免ICloneable
因为需要保证所有成员的深层副本。 相反,我会build议复制构造函数或像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()]
。 目标是失去所有的参考和build立新的。
对于浅拷贝,您可以使用genericsList类的GetRange方法。
List<int> oldList = new List<int>( ); // Populate oldList... List<int> newList = oldList.GetRange(0, oldList.Count);
引用自: generics食谱
稍作修改后,您还可以克隆:
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; }
如果你只关心价值types…
你知道这个types:
List<int> newList = new List<int>(oldList);
如果你以前不知道types,你需要一个辅助函数:
List<T> Clone<T>(IEnumerable<T> oldList) { return newList = new List<T>(oldList); }
正义:
List<string> myNewList = Clone(myOldList);
使用AutoMapper(或者你喜欢的任何映射库)来克隆是简单和可维护的。
定义你的映射:
Mapper.CreateMap<YourType, YourType>();
做魔术:
YourTypeList.ConvertAll(Mapper.Map<YourType, YourType>);
除非需要List<T>
中每个对象的实际克隆,否则克隆List<T>
的最佳方法是创build一个新列表,并将旧列表作为收集参数。
List<T> myList = ...; List<T> cloneOfMyList = new List<T>(myList);
对myList
更改(如插入或删除)不会影响cloneOfMyList
,反之亦然。
两个列表包含的实际对象仍然是相同的。
如果你已经在你的项目中引用了Newtonsoft.Json,并且你的对象是可序列化的,你可以使用:
List<T> newList = JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(listToCopy))
可能不是最有效的方法,但除非你这样做,否则你可能不会注意到速度的差异。
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; } }
您也可以简单地使用ToArray
将列表转换为数组,然后使用Array.Clone(...)
来克隆该数组。 根据您的需要,Array类中包含的方法可以满足您的需求。
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; } }
你可以使用扩展方法:
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; } }; }
您可以通过使用它们的值types成员克隆所有对象,例如,考虑这个类:
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; }
我已经为自己的一些扩展转换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来复制一个对象。 我只是设置一个映射,将一个对象映射到自己。 你可以用任何你喜欢的方式来包装这个操作。
我的朋友Gregor Martinovic和我使用JavaScript Serializer提出了这个简单的解决scheme。 没有必要将类标记为Serializable,并且在使用Newtonsoft JsonSerializer的testing中比使用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); }
要克隆一个列表只需调用.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 } >
下面的代码应该以最小的改变转移到列表上。
基本上它是通过在每个连续的循环中插入一个更大范围的新随机数来工作的。 如果已经存在的数字相同或更高,则将这些随机数字向上移动一个,以便将它们转移到新的更大范围的随机索引中。
// 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; }
另一件事:你可以使用reflection。 如果你能够正确caching,那么它会在5.6秒内克隆1,000,000个对象(不幸的是,内部对象是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只是我的testing类,我有十几个testing,包括expression式的使用。 你可以通过扩展或其他方式以另一种forms实现。 不要忘记caching。
我还没有testing序列化,但我怀疑在一百万级的改进。 我会尝试一些快速protobuf /牛顿。
PS:为了简单阅读,我只在这里使用了自动属性。 我可以使用FieldInfo进行更新,或者您应该轻松地自行实施。
我最近使用DeepClonefunctiontesting了Protocol Buffers序列化程序。 它在一百万个简单对象上以4.2秒获胜,但是当涉及内部对象时,它以7.4秒的结果获胜。
Serializer.DeepClone(personList);
总结:如果你没有访问类,那么这将有所帮助。 否则它取决于对象的数量。 我认为你可以使用reflection多达10,000个对象(可能less一些),但是对于比这更多的协议缓冲器序列化器将执行更好。
有一个简单的方法来使用JSON序列化器和反序列化器在C#中克隆对象。
你可以创build一个扩展类:
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;