从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来遍历对象的属性(我将i
和j
设置为公共属性)并在克隆对象中相应地设置值。 关键是使用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){}