从baseclass创建子类的克隆副本

时间:2012-01-25 20:54:01

标签: c# reflection

考虑这种情况:

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

我认为应该可以使用反射递归克隆所有Base - 成员和属性。但是有一些工作。

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

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

5 个答案:

答案 0 :(得分:13)

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

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

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

Mapper.CreateMap<MyBase, MyClass>();

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

下载AutoMapper

答案 1 :(得分: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你正在处理什么类型。一旦你这样做,它就非常简单。

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

答案 2 :(得分:3)

按照&#34;四人帮&#34; :&#34;赞成作品而非继承&#34;这是完美的理由...

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

public class SuperClass : Person 

SuperClass可以轻松地修饰Person类,添加Person类中找不到的属性。 但是,如果Superclass装饰仅用于GUI,会发生什么?例如,bool值表示&#34; Selected&#34;。我们仍然能够从列表中的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;  

确实非常乏味。但是有更好的方法......不要让超级类继承人

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

这给了我们&#34;遏制&#34; Superclass现在包含一个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;
}

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

答案 3 :(得分:1)

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

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

我想我会绕过整个事情。如果我们关心的是sb的基础上具有相同的状态,并且它没有我们将关心的其他状态(否则我们必须通过它一直到CloneAndUpcast方法),那么我们需要s吗?

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

答案 4 :(得分:1)

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

public Base(){}

public Base(Base pSource){}

public Sub(){}

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

public Sub(Sub pSource){}