30

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

7 回答 7

40

基类需要定义一个拷贝构造函数:

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

这样,基类为所有派生类处理属性设置。

于 2013-12-30T15:14:28.953 回答
18

您可以使用反射来复制属性。

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);
        }
    }
}
于 2017-11-29T16:45:28.263 回答
7

不幸的是,没有简单的方法可以做到这一点。正如您所说,您要么必须使用反射,要么创建一个“克隆”方法,该方法将使用父对象作为输入生成一个新的子对象,如下所示:

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

无论如何,您要么最终编写反射代码(这很慢),要么手动编写每个属性。这完全取决于您是否想要可维护性(反射)或速度(手动属性副本)。

于 2013-12-30T15:12:43.347 回答
4

一个通用的解决方案是将其序列化为 json 并返回。在 json-string 中没有关于序列化的类名的信息。大多数人在 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;
    }
}
于 2017-09-01T07:27:18.927 回答
0

如果一个浅拷贝就足够了,你可以使用MemberwiseClone方法

例子:

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

如果需要深拷贝,可以像这样序列化/反序列化:https ://stackoverflow.com/a/78612/1105687

一个示例(假设您按照该答案中的建议编写了一个扩展方法,并将其称为 DeepClone)

MyObject deepClone = original.DeepClone();
于 2013-12-30T15:22:59.487 回答
0

基础对象似乎很自然地具有带有属性参数的构造函数:

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

那么,在您的后代对象中,您可以拥有:

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

这减少了与分配相关的重复。您可以使用反射来自动复制所有属性,但这种方式似乎更具可读性。

另请注意,如果您的类有太多属性,以至于您正在考虑自动复制属性,那么它们很可能违反单一职责原则,您应该考虑更改您的设计。

于 2013-12-30T15:13:53.477 回答
0

有图书馆可以处理这个问题;但是如果你只是想在几个地方快速实现,我肯定会像之前建议的那样选择“复制构造函数”。

没有提到的一个有趣的点是,如果一个对象是一个子类,那么它可以从父类内部访问子类的私有变量!

因此,在父级上添加一个CloneIntoChild方法。在我的例子中:

  • Order是父类
  • OrderSnapshot是子类
  • _bestPrice是 上的非只读私有成员Order。但Order可以设置为OrderSnapshot.

例子:

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

注意:必须在构造函数中设置只读成员变量,因此您必须使用子构造函数来设置这些...

虽然我一般不喜欢“扩大规模”,但我经常将这种方法用于分析快照......

于 2017-07-09T06:48:06.027 回答