سؤال

I'm creating a child object from a parent object. So the scenario is that I have an object and a child object which adds a distance property for scenarios where I want to search. I've chosen to use inheritance as my UI works equivalently with either a search object or a list of objects not the result of a location search. So in this case inheritance seems a sensible choice.

As present I need to generate a new object MyObjectSearch from an instance of MyObject. At present I'm doing this in the constructor manually by setting properties one by one. I could use reflection but this would be slow. Is there a better way of achieving this kind of object enhancement?

Hopefully my code below illustrates the scenario.

public class MyObject {

    // Some properties and a location.
}

public class MyObjectSearch : MyObject {

    public double Distance { get; set; }
    
    public MyObjectSearch(MyObject obj) {
         base.Prop1 = obj.Prop1;
         base.Prop2 = obj.Prop2;
    }
}

And my search function:

public List<MyObjectSearch> DoSearch(Location loc) { 
  var myObjectSearchList = new List<MyObjectSearch>();       

   foreach (var object in myObjectList) {
       var distance = getDistance();
       var myObjectSearch = new MyObjectSearch(object);
       myObjectSearch.Distance = distance;
       myObjectSearchList.add(myObjectSearch);
   } 
   return myObjectSearchList;
}
هل كانت مفيدة؟

المحلول

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.

نصائح أخرى

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

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).

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

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();

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.

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...

I first came accros this question when I was looking for doing this. If you are able to work with C# 9 and record-classes. You only have to create a new constructor in the sub-class taking in a base class object and hand it over to the subclass:

public record MyObject {
  ...
}

public record MyObjectSearch {
 public MyObjectSearch(MyObject parent) : base(parent) { }
 ...
}

Then you can create the child object like this:

MyObject parent = new();
MyObjectSearch m = new MyObjectSearch(parentObj) { Distance = 1.1};

Credits to https://stackoverflow.com/a/64573044/2582968

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top