Лучший способ создать экземпляр дочернего объекта из родительского объекта

Я создаю дочерний объект из родительского объекта. Таким образом, сценарий заключается в том, что у меня есть объект и дочерний объект, который добавляет свойство расстояния для сценариев, в которых я хочу выполнить поиск. Я решил использовать наследование, так как мой пользовательский интерфейс работает эквивалентно либо с объектом поиска, либо с перечнем объектов, а не результатом поиска местоположения. Поэтому в этом случае наследование кажется разумным выбором.

В настоящее время мне нужно создать новый объект MyObjectSearch из экземпляра MyObject. В настоящее время я делаю это в конструкторе вручную, задавая свойства один за другим. Я мог бы использовать рефлексию, но это было бы медленным. Есть ли лучший способ добиться такого рода улучшения объекта?

Надеюсь, мой код ниже иллюстрирует сценарий.

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

И моя функция поиска:

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;

}

Ответ 1

Базовый класс должен определить конструктор копирования:

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

Таким образом, настройка свойств обрабатывается для всех производных классов базовым классом.

Ответ 2

Нет простого способа сделать это, к сожалению. Как вы сказали, вам придется либо использовать отражение, либо создать метод "Клонировать", который будет генерировать новый дочерний объект с использованием родительского объекта в качестве входного файла, например:

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

Независимо от того, вы или собираетесь в конечном итоге написать код отражения (который медленный) или написать каждую собственность вручную. Все зависит от того, хотите ли вы ремонтировать (отражение) или скорость (ручная копия свойств).

Ответ 3

Вы можете использовать отражение для копирования свойств.

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

Ответ 4

Кажется естественным, если базовый объект имеет конструктор с параметрами для его свойств:

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

Итак, в вашем потомком вы можете:

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

Это уменьшает дублирование, связанное с назначениями. Вы можете использовать отражение, чтобы автоматически копировать все свойства, но этот способ кажется более читаемым.

Обратите также внимание, что если ваши классы обладают настолько большим количеством свойств, что вы думаете об автоматизации копирования свойств, то они, вероятно, нарушают Single Принцип ответственности, и вам стоит подумать об изменении дизайна.

Ответ 5

Есть библиотеки для обработки этого; но если вам просто нужна быстрая реализация в нескольких местах, я бы определенно пошел за "конструктором копирования", как было предложено ранее.

Один интересный момент, который не упоминается, заключается в том, что если объект является подклассом, он может обращаться к дочерним закрытым переменным изнутри родителя!

Итак, в родительском добавить метод "CloneIntoChild". В моем примере: * Заказ - это родительский класс * OrderSnapshot - это дочерний класс * "_bestPrice" является частным пользователем, не являющимся постоянным пользователем, по заказу. Но Order может установить его для OrderSnapshot!

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

ПРИМЕЧАНИЕ: переменные-члены readonly ДОЛЖНЫ быть установлены в конструкторе, поэтому вам придется использовать дочерний конструктор, чтобы установить эти...

Хотя мне вообще не нравится "повышение размера", я часто использую этот подход для аналитических снимков...

Ответ 6

Общим решением было бы сериализовать его на json и обратно. В json-строке нет информации о имени класса, из которого он был сериализован. Большинство людей делают это в javascript.

Как вы видите, это хорошо работает для объектов pocco, но я не гарантирую, что он работает в каждом сложном случае. Но это событие для не унаследованных классов, когда свойства сопоставляются.

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

Ответ 7

Если достаточно мелкой копии, вы можете использовать метод MemberwiseClone.

Пример:

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

Если вам нужна глубокая копия, вы можете сериализовать/десериализовать вот так: fooobar.com/info/594/...

Пример (предполагая, что вы пишете метод расширения, предложенный в этом ответе, и называете его DeepClone)

MyObject deepClone = original.DeepClone();