从baseclass创建子类的克隆副本

考虑这种情况:

public class Base { public int i; } public class Sub : Base { public void foo() { /* do stuff */} } 

然后我想,给定一个Base的实例得到一个Sub的克隆实例(在这种情况下i = 17),这样我就可以在子类中调用foo

 Base b = new Base { i=17 }; Sub s = CloneAndUpcast(b); s.foo(); 

但是,我该如何创建CloneAndUpcast

我认为应该可以使用reflection递归克隆所有Base -members和属性。 但相当一些工作。

谁有更好,更整洁的想法?

PS。 我正在考虑使用它的场景是一组树状结构中的“简单”类(这里没有循环图或类似的),所有类都是简单的值持有者。 计划是拥有一个包含所有值的愚蠢层,然后是一组类似的类(子类),它们实际上包含了值持有者不应该注意的一些业务逻辑。 一般不好的做法是的。 我认为它适用于这种情况。

这是一种方式(在许多可能性中),你可以做你喜欢的事情。 我不确定这是非常漂亮的,并且可能有点难以调试,但我认为它的工作原理:

 class BaseClass { public int i { get; set; } public BaseClass Clone(BaseClass b) { BaseClass clone = new BaseClass(); clone.i = bi; return clone; } } class SubClass : BaseClass { public int j { get; set; } public void foo() { Console.WriteLine("in SubClass with value of i = {0}", i.ToString()); } } class Program { static void Main(string[] args) { BaseClass b1 = new BaseClass() { i = 17 }; BaseClass b2 = new BaseClass() { i = 35 }; SubClass sub1 = CloneAndUpcast(b1); SubClass sub2 = CloneAndUpcast(b2); sub1.foo(); sub2.foo(); } static T CloneAndUpcast(BaseClass b) where T : BaseClass, new() { T clone = new T(); var members = b.GetType().GetMembers(BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance); for (int i = 0; i < members.Length; i++) { if (members[i].MemberType== MemberTypes.Property) { clone .GetType() .GetProperty(members[i].Name) .SetValue(clone, b.GetType().GetProperty(members[i].Name).GetValue(b, null), null); } } return clone; } } 

基本上,正如您所建议的那样,您使用reflection来遍历对象的属性(我将ij设置为公共属性)并在克隆对象中相应地设置值。 关键是使用generics来告诉CloneAndUpcast你正在处理什么类型。 一旦你这样做,它就非常简单。

希望这可以帮助。 祝好运!

您可以使用AutoMapper来避免编写复制构造函数的繁琐。

 public class MyClass : MyBase { public MyClass(MyBase source) { Mapper.Map(source, this); } } 

并且您需要在应用程序启动时运行一次

 Mapper.CreateMap(); 

您可以从https://github.com/AutoMapper/AutoMapper下载AutoMapper

根据“四人帮”:“赞成过inheritance的构成”,这是这样做的完美理由……

如果我们有一个看起来像这样的SuperClass:

 public class SuperClass : Person 

SuperClass可以轻松地修饰Person类,添加Person类中找不到的属性。 但是,如果Superclass装饰仅用于GUI,会发生什么? 例如,指示“已选择”的布尔值。 我们仍然能够从列表中的DB中获取所有人员,但是我们在尝试创建超类并合并数据库结果时遇到了麻烦。

  foreach( var person in myPersonList){ var sc = new SuperClass(); sc.Selected = false; sc=person; } 

编译器抱怨因为Superclass不是编译器的Person,它是Superclass。 填写Person子类属性的唯一方法是迭代并设置每个子类……就像这样。

  SuperClass.Name = Person.Name; SuperClass.Id = Person.ID; 

确实很乏味。 但是有更好的方法……不要让超类从Personinheritance

 public class SuperClass{ public Person ThisPerson {get;set;} public bool Selected {get;set;} } 

这给了我们“遏制”超类现在包含一个Person类。

现在我们可以这样做:

 foreach(var person in MyPersonList){ var sc = new Superclass(); sc.Selected = false; sc.Person = person; } 

这个类的消费者现在必须符合这样超类/人的属性……

 forach(var sc in MySuperClassList){ var selected = sc.Selected; var name = sc.Person.Name; } 

这样做的好处是,将来您可以添加任何其他容器,它不会影响任何其他容器。 您还可以将超类变形为它包含的任何内容。 如果每个包含的类成为接口,那么这就是未来的一步。

好吧,由于b不是Sub ,我们不能将其“克隆”为一个。

如果Base具有构造函数和公共属性的适当组合,以使Sub的构造函数确保其基础因此具有与b相同的状态,那么我们可以使用它。

我想我会绕过整个事情。 如果我们关心的是s在它的基础上具有与b相同的状态,并且它没有我们将要关心的其他状态(或者我们必须将它传递给CloneAndUpcast方法),然后我们需要s吗?

静态方法可以使用Base ,我们可以使用static public void foo(Base bc) 。 我们甚至可以将它定义为扩展方法static public void foo(this Base bc) ,然后将调用编码为b.foo() 。 唯一不允许我们这样做的CloneAndUpcast()让我们做的就是访问受保护的成员。

克隆是一种不好的做法,你的问题是其原因(子类克隆)。 通常,您应该只使用复制cotrs并让子类接受父级作为参数。

public Base(){}

公共基地(Base pSource){}

public Sub(){}

public Sub(Base pSource,其他参数…){}

public Sub(Sub pSource){}