从baseclass创建子类的克隆副本

lei*_*ren 4 c# reflection

考虑这种情况:

public class Base
{
    public int i;
}

public class Sub : Base
{
    public void foo() { /* do stuff */}
}
Run Code Online (Sandbox Code Playgroud)

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

Base b = new Base { i=17 };
Sub s = CloneAndUpcast(b);
s.foo();
Run Code Online (Sandbox Code Playgroud)

但是,我该如何创作CloneAndUpcast

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

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

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

小智 13

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

public class MyClass : MyBase
{
    public MyClass(MyBase source)
    {
        Mapper.Map(source, this);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

Mapper.CreateMap<MyBase, MyClass>();
Run Code Online (Sandbox Code Playgroud)

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


Dav*_*ter 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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


Joh*_*ers 5

根据“四人帮”:“优先考虑组合而不是继承”,这是这样做的完美理由......

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

public class SuperClass : Person 
Run Code Online (Sandbox Code Playgroud)

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

    foreach( var person in myPersonList){
    var sc = new SuperClass();
    sc.Selected = false;
    sc=person;
 }
Run Code Online (Sandbox Code Playgroud)

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

    SuperClass.Name = Person.Name;
    SuperClass.Id = Person.ID;  
Run Code Online (Sandbox Code Playgroud)

确实很乏味。但有一个更好的方法......不要让超类继承自 Person

public class SuperClass{
  public Person ThisPerson {get;set;}
  public bool Selected {get;set;}
}
Run Code Online (Sandbox Code Playgroud)

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

现在我们可以这样做:

foreach(var person in MyPersonList){
   var sc = new Superclass();
   sc.Selected = false;
   sc.Person = person;
}
Run Code Online (Sandbox Code Playgroud)

这个类的消费者现在必须像这样限定超类/人的属性......

forach(var sc in MySuperClassList){
  var selected = sc.Selected;
  var name = sc.Person.Name;
}
Run Code Online (Sandbox Code Playgroud)

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