从基类方法克隆派生类
本文关键字:派生 基类 类方法 | 更新日期: 2023-09-27 18:33:38
>我有一个抽象的基类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 { }
问题是,由于我无法知道 Base 类中派生类的类型,即使我在派生类上调用它,这是否会导致有一个
Base
类实例?(a.Clone();
((实际上在测试之后,这就是正在发生的事情,但也许我的测试设计得不好,这就是为什么我对此表示怀疑(有没有一种好方法(模式(来实现一个基本
Clone
方法,该方法可以按我的预期工作,或者我必须在每个派生类中编写相同的代码(我真的很想避免这种情况......
感谢您的帮助
可以将复制构造函数添加到基类:
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;
}
}
只需覆盖Clone
并有另一种方法来CreateInstance
然后做你的事情。
这样,您只能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();
}
}
我做了一些与亚历山大·西蒙诺夫类似的事情,但也许更简单。这个想法是(正如我在评论中所说(在基类中只有一个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
你可以做这样的事情:
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();
}
但我怀疑它会带来一些有用的东西。我将创建另一个示例..
我使用 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();
但我会选择亚历山大·西蒙诺夫的答案。
如果性能对你的情况并不重要,你可以通过创建一个通用的克隆方法来简化你的代码,该方法可以将任何内容克隆到属性相同的任何内容:
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;
}
}
}
}
在尝试解决这个确切问题时发现了这个问题,在 LINQPad 上玩得很开心。概念验证:
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; }
}