从基类方法克隆派生类

我有一个抽象基类Base ,它有一些共同的属性,以及许多派生的实现不同逻辑但很少有其他字段。

 public abstract Base { protected int field1; protected int field2; .... protected Base() { ... } } 

有时我需要克隆派生类。 所以我的猜测是,只需在我的基类中创建一个虚拟Clone方法,并且只在具有其他字段的派生类中覆盖它,但当然我的Base类不再是抽象的(这不是问题,因为它只有protected构造函数)。

 public Base { protected int field1; protected int field2; .... protected Base() { ... } public virtual Base Clone() { return new Base(); } } public A : Base { } public B : Base { } 
  1. 问题是,因为我无法知道我的Base类中的派生类的类型,即使我在派生类上调用它,这也不会导致有一个Base类实例吗? ( a.Clone(); )(实际上在测试之后这是发生了什么,但也许我的测试没有很好地设计,这就是为什么我对它有疑问)

  2. 是否有一种很好的方法(模式)来实现一个基本的Clone方法,它可以按照我的预期工作,或者我必须在每个派生类中编写相同的代码(我真的想避免这种情况……)

谢谢你的帮助

只需覆盖Clone并为CreateInstance提供另一种方法,然后执行您的操作。

这样你就可以只有Base类来避免generics。

 public Base { protected int field1; protected int field2; .... protected Base() { ... } public virtual Base Clone() { var bc = CreateInstanceForClone(); bc.field1 = 1; bc.field2 = 2; return bc; } protected virtual Base CreateInstanceForClone() { return new Base(); } } public A : Base { protected int fieldInA; public virtual Base Clone() { var a = (A)base.Clone(); a.fieldInA =5; return a; } protected virtual Base CreateInstanceForClone() { return new A(); } } 

您可以将复制构造函数添加到基类:

 public abstract Base { protected int field1; protected int field2; protected Base() { ... } protected Base(Base copyThis) : this() { this.field1 = copyThis.field1; this.field2 = copyThis.field2; } public abstract Base Clone(); } public Child1 : Base { protected int field3; public Child1 () : base() { ... } protected Child1 (Child1 copyThis) : base(copyThis) { this.field3 = copyThis.field3; } public override Base Clone() { return new Child1(this); } } public Child2 : Base { public Child2 () : base() { ... } protected Child (Child copyThis) : base(copyThis) { } public override Base Clone() { return new Child2(this); } } public Child3 : Base { protected int field4; public Child3 () : base() { ... } protected Child3 (Child3 copyThis) : base(copyThis) { this.field4 = copyThis.field4; } public override Base Clone() { var result = new Child1(this); result.field1 = result.field2 - result.field1; } } 

你可以这样做:

 public class Base where T: Base, new() { public virtual T Clone() { T copy = new T(); copy.Id = this.Id; return copy; } public string Id { get; set; } } public class A : Base { public override A Clone() { A copy = base.Clone(); copy.Name = this.Name; return copy; } public string Name { get; set; } } private void Test() { A a = new A(); A aCopy = a.Clone(); } 

但我怀疑它会带来一些有用的东西。 我将创建另一个例子..

我有另一个想法使用Activator类:

 public class Base { public virtual object Clone() { Base copy = (Base)Activator.CreateInstance(this.GetType()); copy.Id = this.Id; return copy; } public string Id { get; set; } } public class A : Base { public override object Clone() { A copy = (A)base.Clone(); copy.Name = this.Name; return copy; } public string Name { get; set; } } A a = new A(); A aCopy = (A)a.Clone(); 

但我会选择亚历山大·西蒙诺夫的回答。

我做了类似Alexander Simonov的事情,但也许更简单。 这个想法是(正如我在评论中所说的Clone()在基类中只有一个 Clone()并将所有工作留给虚拟CloneImpl() ,每个类根据需要定义,依赖于基类的CloneImpl()类。

正确类型的创建留给C#的MemberwiseClone() ,它将对正在调用的对象执行任何操作。 这也消除了在任何类中都需要默认构造函数(没有调用过)。

 using System; namespace CloneImplDemo { // dummy data class class DeepDataT : ICloneable { public int i; public object Clone() { return MemberwiseClone(); } } class Base: ICloneable { protected virtual Base CloneImpl() { // Neat: Creates the type of whatever object is calling. // Also obviates the need for default constructors // (Neither Derived1T nor Derived2T have one.) return (Base)MemberwiseClone(); } public object Clone() { // Calls whatever CloneImpl the // actual calling type implements. return CloneImpl(); } } // Note: No Clone() re-implementation class Derived1T : Base { public Derived1T(int i) { der1Data.i = i; } public DeepDataT der1Data = new DeepDataT(); protected override Base CloneImpl() { Derived1T cloned = (Derived1T)base.CloneImpl(); cloned.der1Data = (DeepDataT)der1Data.Clone(); return cloned; } } // Note: No Clone() re-implementation. class Derived2T : Derived1T { public Derived2T(int i1, int i2) : base(i1) { der2Data.i = i2; } public string txt = string.Empty; // copied by MemberwiseClone() public DeepDataT der2Data = new DeepDataT(); protected override Base CloneImpl() { Derived2T cloned = (Derived2T)base.CloneImpl(); // base members have been taken care of in the base impl. // we only add our own stuff. cloned.der2Data = (DeepDataT)der2Data.Clone(); return cloned; } } class Program { static void Main(string[] args) { var obj1 = new Derived2T(1,2); obj1.txt = "this is obj1"; var obj2 = (Derived2T)obj1.Clone(); obj2.der1Data.i++; obj2.der2Data.i++; // changes value. obj2.txt = "this is a deep copy"; // replaces reference. // the values for i should differ because // we performed a deep copy of the DeepDataT members. Console.WriteLine("obj1 txt, i1, i2: " + obj1.txt + ", " + obj1.der1Data.i + ", " + obj1.der2Data.i); Console.WriteLine("obj2 txt, i1, i2: " + obj2.txt + ", " + obj2.der1Data.i + ", " + obj2.der2Data.i); } } } 

输出:

 obj1 txt, i1, i2: this is obj1, 1, 2 obj2 txt, i1, i2: this is a deep copy, 2, 3 

如果性能对您的情况不重要,您可以通过创建一个通用克隆方法来简化代码,如果属性相同,可以克隆任何内容:

 Base base = new Base(){...}; Derived derived = XmlClone.CloneToDerived(base); public static class XmlClone { public static D CloneToDerived(T pattern) where T : class { using (var ms = new MemoryStream()) { using (XmlWriter writer = XmlWriter.Create(ms)) { Type typePattern = typeof(T); Type typeTarget = typeof(D); XmlSerializer xmlSerializerIn = new XmlSerializer(typePattern); xmlSerializerIn.Serialize(writer, pattern); ms.Position = 0; XmlSerializer xmlSerializerOut = new XmlSerializer(typeTarget, new XmlRootAttribute(typePattern.Name)); D copy = (D)xmlSerializerOut.Deserialize(ms); return copy; } } } } 

在尝试解决这个问题的同时找到了这个问题,在使用LINQPad时有一些乐趣。 概念certificate:

 void Main() { Person p = new Person() { Name = "Person Name", Dates = new List() { DateTime.Now } }; new Manager() { Subordinates = 5 }.Apply(p).Dump(); } public static class Ext { public static TResult Apply(this TResult result, TSource source) where TResult: TSource { var props = typeof(TSource).GetProperties(BindingFlags.Public | BindingFlags.Instance); foreach (var p in props) { p.SetValue(result, p.GetValue(source)); } return result; } } class Person { public string Name { get; set; } public List Dates { get; set; } } class Manager : Person { public int Subordinates { get; set; } }