Best way to create instance of child object from parent object

拟墨画扇 提交于 2019-12-01 02:42:47

The base class needs to define a copy constructor:

public class MyObject
{
    protected MyObject(MyObject other)
    {
        this.Prop1=other.Prop1;
        this.Prop2=other.Prop2;
    }

    public object Prop1 { get; set; }
    public object Prop2 { get; set; }
}

public class MyObjectSearch : MyObject
{

    public double Distance { get; set; }

    public MyObjectSearch(MyObject obj)
         : base(obj)
    {
        this.Distance=0;
    }
    public MyObjectSearch(MyObjectSearch other)
         : base(other)
    {
        this.Distance=other.Distance;
    }
}

This way the setting of properties is handled for all derived classes by the base class.

There is no easy way to do this, unfortunately. As you said, you would either have to use reflection, or create a "Clone" method that would generate a new child object using a parent object as input, like so:

public class MyObjectSearch : MyObject {

    // Other code

    public static MyObjectSearch CloneFromMyObject(MyObject obj)
    {
        var newObj = new MyObjectSearch();

        // Copy properties here
        obj.Prop1 = newObj.Prop1;

        return newObj;
    }
}

No matter what, you're either going to end up writing reflection code (which is slow), or writing each property out by hand. It all depends on whether or not you want maintainability (reflection) or speed (manual property copy).

You can use reflection to copy properties.

public class ChildClass : ParentClass
{


    public ChildClass(ParentClass ch)
    {
        foreach (var prop in ch.GetType().GetProperties())
        {
            this.GetType().GetProperty(prop.Name).SetValue(this, prop.GetValue(ch, null), null);
        }
    }
}

Seems natural for the base object to have constructor with parameters for its properties:

public class MyObject 
{
    public MyObject(prop1, prop2, ...)
    {
        this.Prop1 = prop1;
        this.Prop2 = prop2;
    }
}

So then, in your descendant object you can have:

public MyObjectSearch(MyObject obj)
    :base(obj.Prop1, obj.Prop2)

This reduces duplication related to assignments. You could use reflection to automatically copy all properties, but this way seems more readable.

Note also, that if your classes have so much properties that you're thinking about automatizing of copying of the properties, then they are likely to violate the Single Responsibility Principle, and you should rather consider changing your design.

A generic solution would be to serialize it to json and back. In the json-string is no information about the class name from which it was serialized. Most people do this in javascript.

As you see it works well for pocco objects but i don't guarantee that it works in every complex case. But it does event for not-inherited classes when the properties are matched.

using Newtonsoft.Json;

namespace CastParentToChild
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var p = new parent();
            p.a=111;
            var s = JsonConvert.SerializeObject(p);
            var c1 = JsonConvert.DeserializeObject<child1>(s);
            var c2 = JsonConvert.DeserializeObject<child2>(s);

            var foreigner = JsonConvert.DeserializeObject<NoFamily>(s);

            bool allWorks = p.a == c1.a && p.a == c2.a && p.a == foreigner.a;
            //Your code goes here
            Console.WriteLine("Is convertable: "+allWorks + c2.b);
        }
    }

    public class parent{
        public int a;
    }

    public class child1 : parent{
     public int b=12345;   
    }

    public class child2 : child1{
    }

    public class NoFamily{
        public int a;
        public int b = 99999;
    }

    // Is not Deserializeable because
    // Error 'NoFamily2' does not contain a definition for 'a' and no extension method 'a' accepting a first argument of type 'NoFamily2' could be found (are you missing a using directive or an assembly reference?)
    public class NoFamily2{
        public int b;
    }
}
James Joyce

There are libraries to handle this; but if you just want a quick implementation in a few places, I would definitely go for a "copy constructor" as previously suggested.

One interesting point not mentioned is that if an object is a subclass, then it can access the child's private variables from the within the parent!

So, on the parent add a CloneIntoChild method. In my example:

  • Order is the parent class
  • OrderSnapshot is the child class
  • _bestPrice is a non-readonly private member on Order. But Order can set it for OrderSnapshot.

Example:

public OrderSnapshot CloneIntoChild()
{
    OrderSnapshot sn = new OrderSnapshot()
    {
        _bestPrice = this._bestPrice,
        _closed = this._closed,
        _opened = this._opened,
        _state = this._state       
    };
    return sn;
}

NOTE: Readonly member variables MUST be set in the constructor, so you will have to use the child constructor to set these...

Although I don't like "up-sizing" generally, I use this approach a lot for analytic snapshots...

lightbricko

If a shallow copy is enough, you can use the MemberwiseClone method.

Example:

MyObject shallowClone = (MyObject)original.MemberwiseClone();

If you need a deep copy, you can serialize/deserialize like this: https://stackoverflow.com/a/78612/1105687

An example (assuming you write an extension method as suggested in that answer, and you call it DeepClone)

MyObject deepClone = original.DeepClone();
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!