我在c#中有一个对象的通用列表,并希望克隆列表。列表中的项是可克隆的,但似乎没有做list. clone()的选项。
有什么简单的办法吗?
我在c#中有一个对象的通用列表,并希望克隆列表。列表中的项是可克隆的,但似乎没有做list. clone()的选项。
有什么简单的办法吗?
当前回答
如果你的元素是值类型,那么你可以这样做:
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实例。
这些选项中的任何一个都可以通过方法(扩展或其他方式)包装。
其他回答
在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;
您可以使用扩展方法:
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
}
};
注意:如果你在复制(或克隆)上做了任何改变,它不会影响原始对象。
//try this
List<string> ListCopy= new List<string>(OldList);
//or try
List<T> ListCopy=OldList.ToList();
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;
}
}
另一件事:你可以使用反射。如果你正确地缓存它,那么它将在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个对象(可能会少一点),但对于超过这个,协议缓冲区序列化器将执行得更好。