从基类创建子类的克隆副本

发布于 2024-12-29 03:57:30 字数 619 浏览 2 评论 0原文

考虑这种情况:

public class Base
{
    public int i;
}

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

然后我想,给定一个 Base 实例,获取 Sub 的克隆实例(在本例中 i=17),以便我可以调用 < code>foo 在子类中。

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

但是,如何创建CloneAndUpcast

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

有人有更好、更简洁的想法吗?

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

Consider this scenario:

public class Base
{
    public int i;
}

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

And then I want to, given an instance of Base get an cloned instance of Sub (with i=17 in this case) so that I can call foo in the subclass.

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

However, how can I create CloneAndUpcast?

I am thinking that is should be possible to recursively clone all of Base-members and properties using reflection. But quite some work.

Anyone with better, neater ideas?

PS. The scenario where I am thinking about using this is a set of "simple" classes in a tree-like structure (no cyclic graphs or similar here) and all the classes are simple value holders. The plan is to have a stupid layer holding all values and then an similar set of classes (the subclasses) that actually contains some business-logic the value-holders shouldn't be aware of. Generally bad practice yes. I think it works in this case.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

甜妞爱困 2025-01-05 03:57:30

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

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

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

Mapper.CreateMap<MyBase, MyClass>();

您可以从 https://github.com/AutoMapper/ 下载 AutoMapper自动映射器

You could use AutoMapper to avoid the tedium of writing the copy constructors.

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

and you need to run this once when your application starts up

Mapper.CreateMap<MyBase, MyClass>();

You can download AutoMapper from https://github.com/AutoMapper/AutoMapper

遗弃M 2025-01-05 03:57:30

这是一种方法(在多种可能性中),您可以做您所要求的事情。我不确定这是否非常漂亮,并且调试起来可能有点难看,但我认为它有效:

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 您正在处理什么类型。一旦你这样做了,事情就非常简单了。

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

Here's one way (out of many possibilities) that you could do something like you're asking. I'm not sure this is very pretty and can be kind of ugly to debug, but I think it works:

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

Basically, as you suggested, you use reflection to iterate through the object's properties (I set i and j as public properties) and set the values accordingly in the cloned object. The key is using generics to tell CloneAndUpcast what type you're dealing with. Once you do that, it's pretty straightforward.

Hope this helps. Good luck!

枫以 2025-01-05 03:57:30

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

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

public class SuperClass : Person 

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

    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;  

确实很乏味。但是有一个更好的方法......不要让超类继承 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;
}

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

Per the "Gang of Four" : "Favor composition over inheritance" and this is a perfect reason to do so...

If we have a SuperClass that looks like this:

public class SuperClass : Person 

The SuperClass can easily decorate the Person class adding properties not found in Person class.
But what happens if the Superclass decorations are only for the GUI? For example a bool value indicating "Selected". We are still able to get all Persons from the DB in a List but we run into trouble trying to create the Superclass and merge the DB results.

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

The compiler complains because Superclass is not a Person to the compiler it's a Superclass. The only way to fill in the properties of the Person subclass is to iterate and set each one... like this.

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

Pretty tedious indeed. But there's a better way.... Don't make Superclass inherit from Person

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

This gives us "Containment" The Superclass now contains a Person class.

Now we can do this:

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

The consumer of this class must now qualify the properties of the Superclass/Person like this...

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

The beauty of this is that in the future, you can add any other container you want and it will NOT affect any other containers. You can also morph the Superclass to anything it contains. If each of the contained classes become Interfaces, then that's one step futher down the road.

煮茶煮酒煮时光 2025-01-05 03:57:30

好吧,由于 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() 允许我们做的唯一一件事就是访问受保护的成员。

Well, since b isn't a Sub, we can't "clone" it as one.

If Base has an appropriate combination of constructor and public properties to let a constructor in Sub ensure that its base would therefore have the same state as b, then we could use that.

I think I'd by-pass the whole thing though. If all we care about is that s have the same state in its base as b, and it has not other state that we're going to care about (or else we'd have to be passing it through to the CloneAndUpcast method), then do we need s at all?

A static method could take a Base and we could just use static public void foo(Base bc). We could even define it as an extension method static public void foo(this Base bc) and then code the call as b.foo(). The only thing this won't let us do that CloneAndUpcast() lets us do is access protected members.

喵星人汪星人 2025-01-05 03:57:30

克隆是一种不好的做法,您的问题就是其原因(子类克隆)。
一般来说,您应该只使用 copy cotrs 并让子类接受父类作为参数。

public Base(){}

public Base(Base pSource){}

public Sub(){}

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

public Sub(Sub pSource){}

Clone is a bad practice and your question is the reason for that (subclass cloning).
In general, you should just use copy cotrs instead and have the subclass accept a parent as a parameter.

public Base(){}

public Base(Base pSource){}

public Sub(){}

public Sub(Base pSource, other parameters...){}

public Sub(Sub pSource){}

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文