.NET框架的ICloneable接口通常提供了一种支持类实例的cloning的方法。
但是如果我有多个第三方类,并且不想单独关注每个属性,我该如何有效地克隆这些类的对象? (这些类的源代码不可用)。 有没有办法使用generics和extension methods?
我需要的是一个深度克隆,它可以创建包含所有属性和(子)对象的精确副本。
示例:假设您要在LinqPad中克隆UserQuery
对象:
void Main()
{
UserQuery uc=this;
var copy=uc.CreateCopy(); // clone it
}
我正在寻找的是CreateCopy()
扩展,允许创建副本而无需处理此类的详细信息,因为我不拥有UerQuery
的来源。
(请注意,UserQuery
只是一个示例,用于显示我需要的内容,它也可以是PDF文档类,用户控件类,ADO.NET类或其他任何内容。)
答案 0 :(得分:6)
我目前有 2个不同的解决方案,一个有一个没有使用反射:
1。)使用通用扩展方法(加上 reflection ),您可以在C#中这样做:< / p>
public static class Extension
{
public static T CreateCopy<T>(this T src)
where T: new()
{
if (src == null) return default(T); // just return null
T tgt = new T(); // create new instance
// then copy all properties
foreach (var pS in src.GetType().GetProperties())
{
foreach (var pT in tgt.GetType().GetProperties())
{
if (pT.Name != pS.Name) continue;
(pT.GetSetMethod()).Invoke(tgt, new object[] {
pS.GetGetMethod().Invoke(src, null) });
}
};
return tgt;
} // method
} // class
这非常强大,因为现在您可以克隆每个对象,而不仅仅是您编写的类中的对象,而是来自所有类包括系统类 .NET Framework。感谢反思,你不需要知道它的属性,它们会被自动复制。
要使用方法CreateCopy()
,假设您有一个Customer
类和一个Order
类,您需要使用新ID创建副本(不是引用)。然后,您可以执行以下操作:
Order CopyOrderWithNewPK(Order item)
{
Order newItem = item.CreateCopy(); // use ext. method to copy properties
newItem.OrderId = new Guid(); // create new primary key for the item
return newItem;
}
毫不奇怪,对于Customer类来说,它看起来是一样的:
Customer CopyCustomerWithNewPK(Customer item)
{
Customer newItem = item.CreateCopy(); // use ext. method to copy properties
newItem.CustomerId = new Guid(); // create new primary key for the item
return newItem;
}
注意,自动复制示例类中定义的所有属性的值。如果您不拥有源代码,甚至可以克隆第三方程序集的对象。权衡的是,反思方法较慢。
2。)还有另一种方法可以做到这一点没有反思,受到this问题的启发。一个优点是它甚至能够克隆实体框架的对象(例如,将实体对象附加和重新附加到不同的数据上下文):
// using System.Runtime.Serialization;
public class Cloner<T>
{
readonly DataContractSerializer _serializer
= new DataContractSerializer(typeof(T));
/// <summary>
/// Clone an object graph
/// </summary>
/// <param name="graph"></param>
/// <returns></returns>
public T Clone(T graph)
{
MemoryStream stream = new MemoryStream();
_serializer.WriteObject(stream, graph);
stream.Seek(0, SeekOrigin.Begin);
return (T)_serializer.ReadObject(stream);
}
}
为了不破坏上面的示例,您可以按如下方式更改扩展方法CreateCopy
:
public static class Extension
{
public static T CreateCopy<T>(this T src)
where T: new()
{
return (new Cloner<T>()).Clone(src);
}
}
注意:虽然Cloner
正在使用System.Runtime.Serialization
,但正在克隆的对象不需要可序列化。这可能是一个优势,我见过的其他解决方案只能克隆可序列化的对象。
答案 1 :(得分:1)
如果您实施ICloneable
,则不必关心每个属性!你的答案很好但对我来说它的代码和反射都很低(!),我更喜欢使用
class Test : ICloneable
{
public int A { get; set; }
public int B { get; set; }
#region ICloneable-Member
public object Clone()
{
return base.MemberwiseClone();
}
#endregion
}