我在c#中有一个对象的通用列表,并希望克隆列表。列表中的项是可克隆的,但似乎没有做list. clone()的选项。
有什么简单的办法吗?
我在c#中有一个对象的通用列表,并希望克隆列表。列表中的项是可克隆的,但似乎没有做list. clone()的选项。
有什么简单的办法吗?
当前回答
对于深度复制,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使它简洁干净。同样地,你也可以像其他答案所建议的那样,创建扩展方法等等,但这些都不是必需的。
其他回答
在这种情况下,对于浅拷贝,使用强制转换可能会有所帮助:
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);
我已经为我自己做了一些扩展,它转换了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();
}
}
下面的代码应该以最小的更改转移到列表中。
基本上,它的工作原理是在每个连续的循环中插入一个更大范围的新随机数。如果已经存在与它相同或更高的数字,则将这些随机数向上移动1,以便它们转移到新的更大范围的随机索引中。
// 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;
}
我使用automapper来复制一个对象。我只是设置了一个映射,将一个对象映射到它自己。您可以按照您喜欢的任何方式来完成这个操作。
http://automapper.codeplex.com/
另一件事:你可以使用反射。如果你正确地缓存它,那么它将在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只是我的测试类,其中有十几个测试,包括表达式的使用。你可以用另一种形式在扩展中实现它。不要忘记缓存。
我还没有测试序列化,但我怀疑有一百万个类会有什么改进。我会尝试一些快速的东西。
附注:为了简化阅读,我在这里只使用了auto-property。我可以用FieldInfo进行更新,或者您应该自己轻松实现这一点。
我最近用DeepClone函数开箱测试了Protocol Buffers序列化器。它在处理一百万个简单对象时以4.2秒获胜,但在处理内部对象时,它以7.4秒的结果获胜。
Serializer.DeepClone(personList);
总结:如果您没有访问这些类的权限,那么这将有所帮助。否则,它取决于对象的计数。我认为你可以使用反射多达10,000个对象(可能会少一点),但对于超过这个,协议缓冲区序列化器将执行得更好。