[英]Clone derived class from base class method
I have an abstract base class Base
which has some common properties, and many derived ones which implement different logic but rarely have additional fields. 我有一个抽象的基类
Base
,它有一些共同的属性,以及许多派生的,它们实现不同的逻辑,但很少有其他字段。
public abstract Base
{
protected int field1;
protected int field2;
....
protected Base() { ... }
}
Sometimes I need to clone the derived class. 有时我需要克隆派生类。 So my guess was, just make a virtual
Clone
method in my base class and only override it in derived classes that have additional fields, but of course my Base
class wouldn't be abstract anymore (which isn't a problem since it only has a protected
constructor). 所以我的猜测是,只需在我的基类中创建一个虚拟
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 { }
The thing is, since I can't know the type of the derived class in my Base one, wouldn't this lead to have a Base
class instance even if I call it on the derived ones ? 问题是,因为我无法知道我的Base类中派生类的类型,即使我在派生类上调用它,这也不会导致有一个
Base
类实例吗? ( a.Clone();
) (actually after a test this is what is happening but perhaps my test wasn't well designed that's why I have a doubt about it) (
a.Clone();
)(实际上在测试之后这是发生了什么,但也许我的测试设计得不好,这就是为什么我对它有疑问)
Is there a good way (pattern) to implement a base Clone
method that would work as I expect it or do I have to write the same code in every derived class (I'd really like to avoid that...) 有没有一种好方法(模式)来实现一个基本的
Clone
方法,它可以按照我的预期工作,或者我必须在每个派生类中编写相同的代码(我真的想避免这种情况......)
Thanks for your help 谢谢你的帮助
You can add a copy constructor to your base class: 您可以将复制构造函数添加到基类:
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;
}
}
Just override the Clone
and have another method to CreateInstance
then do your stuff. 只需覆盖
Clone
并使用另一种方法来创建CreateInstance
然后做你的东西。
This way you could have only Base
class avoiding generics. 这样你就可以只有
Base
类来避免泛型。
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 override Base Clone()
{
var a = (A)base.Clone();
a.fieldInA =5;
return a;
}
protected override Base CreateInstanceForClone()
{
return new A();
}
}
You could do something like this: 你可以这样做:
public class Base<T> where T: Base<T>, new()
{
public virtual T Clone()
{
T copy = new T();
copy.Id = this.Id;
return copy;
}
public string Id { get; set; }
}
public class A : Base<A>
{
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();
}
But i doubt that it will bring something useful. 但我怀疑它会带来一些有用的东西。 I'll create another example..
我将创建另一个例子..
I got another idea using the Activator class: 我有另一个想法使用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();
But i would go for the Alexander Simonov answer. 但我会选择亚历山大·西蒙诺夫的回答。
I did something similar as Alexander Simonov, but perhaps simpler. 我做了类似Alexander Simonov的事情,但也许更简单。 The idea is (as I said in a comment) to have just one
Clone()
in the base class and leave all the work to a virtual CloneImpl()
which each class defines as needed, relying on the CloneImpl()
s of the base classes. 这个想法是(正如我在评论中所说
Clone()
在基类中只有一个 Clone()
并将所有工作留给虚拟CloneImpl()
,每个类根据需要定义,依赖于基类的CloneImpl()
类。
Creation of the proper type is left to C#'s MemberwiseClone()
which will do whatever it takes for the object that's calling. 正确类型的创建留给C#的
MemberwiseClone()
,它将对正在调用的对象执行任何操作。 This also obviates the need for a default constructor in any of the classes (none is ever called). 这也消除了对任何类中的默认构造函数的需要(从未调用过任何类)。
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);
}
}
}
Output: 输出:
obj1 txt, i1, i2: this is obj1, 1, 2
obj2 txt, i1, i2: this is a deep copy, 2, 3
If performance is not important for your case, you can simplify your code by creating just one general clone method which can clone whatever to whatever if properties are same: 如果性能对您的情况不重要,您可以通过创建一个通用克隆方法来简化代码,如果属性相同,可以克隆任何内容:
Base base = new Base(){...};
Derived derived = XmlClone.CloneToDerived<Base, Derived>(base);
public static class XmlClone
{
public static D CloneToDerived<T, D>(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;
}
}
}
}
Found this question while trying to solve this exact problem, had some fun with LINQPad while at it. 在尝试解决这个问题时发现了这个问题,在使用LINQPad的时候有了一些乐趣。 Proof of concept:
概念证明:
void Main()
{
Person p = new Person() { Name = "Person Name", Dates = new List<System.DateTime>() { DateTime.Now } };
new Manager()
{
Subordinates = 5
}.Apply(p).Dump();
}
public static class Ext
{
public static TResult Apply<TResult, TSource>(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<DateTime> Dates { get; set; }
}
class Manager : Person
{
public int Subordinates { get; set; }
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.