2012-01-25 40 views
4

考虑这种情况:从基类创建子类的克隆副本

public class Base 
{ 
    public int i; 
} 

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

然后我想,鉴于Base一个实例得到的Sub的克隆实例(与我在这种情况下= 17),使我可以在子类中调用foo

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

但是,如何创建CloneAndUpcast

我在想,是应该可以递归克隆所有的使用反射Base - 成员和属性。但相当一些工作。

任何人有更好的,整洁的想法?

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

+3

你不能完全按照你所要求的。有很多原因,但即使可以,也足以说,这通常是一种不好的做法。如果您可以给我们提供您想要解决的更广泛的问题,我们可以帮助您找到正确的模式。 –

+1

这听起来像是一个具体的问题,表明一个更普遍的问题正在被错误地解决。旧鞋和玻璃瓶都不合适。请参阅:http://weblogs.asp.net/alex_papadimoulis/archive/2005/05/25/408925.aspx – FMM

+0

我认为你试图达到的目标*可能*表示设计问题。另外,由于'i'对于'base'类是私有的,所以'sub'无论如何都不能使用它,所以根据'Base'的工作原理,不需要复制该字段。 – tobsen

回答

7

这里有一种方法(出于多种可能性),你可以做你喜欢的事情。我不知道这是非常漂亮的,可以是一种丑陋的调试,但我认为它的工作原理:

class BaseClass 
{ 
    public int i { get; set; } 

    public BaseClass Clone(BaseClass b) 
    { 
     BaseClass clone = new BaseClass(); 
     clone.i = b.i; 
     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<SubClass>(b1); 
     SubClass sub2 = CloneAndUpcast<SubClass>(b2); 

     sub1.foo(); 
     sub2.foo(); 
    } 

    static T CloneAndUpcast<T>(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; 
    } 
} 

基本上,如你所说,你可以使用反射通过对象的属性来遍历(我设置ij作为公共属性),并在克隆对象中相应地设置值。关键是使用泛型来告诉CloneAndUpcast你正在处理的是什么类型。一旦你这样做,这非常简单。

希望这会有所帮助。祝你好运!

+0

一个静态泛型,T由它的参数定义。那很完美!我得到了一个内部甚至不需要反思的例子;它是由超类定义的列表,其处理根据子类而不同,所以我要做的就是复制它。 – Nyerguds

1

好吧,既然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()让我们做的就是访问受保护的成员。

1

克隆是一个不好的做法,你的问题是(克隆子类)的原因。 一般而言,您应该使用copy cotrs,并让子类接受父级作为参数。

公共基地(){}

公共碱(碱的PSource){}

公共子(){}

公共子(碱的PSource,其他参数...){}

公众子(分的PSource){}

12

你可以使用AutoMapper避免编写拷贝构造函数的单调乏味。

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

,你需要在你的应用程序启动时运行一次这个

Mapper.CreateMap<MyBase, MyClass>(); 

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

+0

整洁而精确。这应该是问题的答案。 –

3

下载AutoMapper每对“四人帮”:“有利于对继承组成”这是一个完美的理由......

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

public class SuperClass : Person 

SuperClass可以很容易地装饰Person类,添加Person类中找不到的属性。 但是,如果超类装饰仅用于GUI,会发生什么情况?例如指示“选定”的布尔值。我们仍然可以从列表中获得所有人员,但是我们遇到了麻烦,试图创建超类并合并数据库结果。

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

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

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

的确很乏味。但是有一个更好的办法....不要超类从Person

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; 
} 

这样做的好处是,在将来,您可以添加任何其他您想要的容器,并且不会影响任何其他容器。你也可以将超类变形为它包含的任何东西。如果每个包含的类都成为接口,那么这就是更进一步的道路。