C# 从父对象创建子对象实例的最佳方法

C# 从父对象创建子对象实例的最佳方法,c#,.net,inheritance,objectinstantiation,C#,.net,Inheritance,Objectinstantiation,我正在从父对象创建子对象。所以场景是,我有一个对象和一个子对象,它为我想要搜索的场景添加了一个距离属性。我选择使用继承,因为我的UI与搜索对象或不是位置搜索结果的对象列表等效。因此,在这种情况下,继承似乎是一个明智的选择 目前,我需要从MyObject的实例生成一个新对象MyObjectSearch。目前,我在构造函数中通过逐个设置属性手动执行此操作。我可以使用反射,但这将是缓慢的。有没有更好的方法来实现这种对象增强 希望下面的代码能够说明这个场景 public class MyObject {

我正在从父对象创建子对象。所以场景是,我有一个对象和一个子对象,它为我想要搜索的场景添加了一个距离属性。我选择使用继承,因为我的UI与搜索对象或不是位置搜索结果的对象列表等效。因此,在这种情况下,继承似乎是一个明智的选择

目前,我需要从
MyObject
的实例生成一个新对象
MyObjectSearch
。目前,我在构造函数中通过逐个设置属性手动执行此操作。我可以使用反射,但这将是缓慢的。有没有更好的方法来实现这种对象增强

希望下面的代码能够说明这个场景

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;
}
公共列表搜索(位置loc){
var myObjectSearchList=新列表();
foreach(myObjectList中的var对象){
var-distance=getDistance();
var myObjectSearch=新的myObjectSearch(对象);
myObjectSearch.Distance=距离;
myObjectSearchList.add(myObjectSearch);
} 
返回myObjectSearchList;
}

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

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

不管怎样,您要么编写反射代码(速度很慢),要么手工写出每个属性。这完全取决于您是否需要可维护性(反射)或速度(手动属性复制)。

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

public class MyObject 
{
    public MyObject(prop1, prop2, ...)
    {
        this.Prop1 = prop1;
        this.Prop2 = prop2;
    }
}
因此,在子体对象中,可以有:

public MyObjectSearch(MyObject obj)
    :base(obj.Prop1, obj.Prop2)
这减少了与工作分配相关的重复。您可以使用反射自动复制所有属性,但这种方式看起来更具可读性


也请注意,如果你的类有很多属性,你在考虑复制属性的自动化,那么它们很可能会违反,并且你应该考虑改变你的设计。

基类需要定义一个复制构造函数:

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

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

如果浅拷贝足够,可以使用

例如:

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

如果需要深度拷贝,可以按如下方式序列化/反序列化:

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


有图书馆来处理这个问题;但是,如果您只想在一些地方快速实现,我肯定会选择前面建议的“复制构造函数”

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

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

  • Order
    是父类
  • ordersnashot
    是子类
  • \u bestPrice
    订单
    上的非只读私人会员。但是
    Order
    可以将其设置为
    ordersnashot
例如:

MyObject shallowClone = (MyObject)original.MemberwiseClone();
public OrderSnapshot CloneIntoChild()
{
    OrderSnapshot sn = new OrderSnapshot()
    {
        _bestPrice = this._bestPrice,
        _closed = this._closed,
        _opened = this._opened,
        _state = this._state       
    };
    return sn;
}
注意:只读成员变量必须在构造函数中设置,因此您必须使用子构造函数来设置这些变量


虽然我一般不喜欢“放大”,但我在分析快照时经常使用这种方法

通用解决方案是将其序列化为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;
    }
}
使用Newtonsoft.Json;
命名空间CastParentToChild
{
公共课程
{
公共静态void Main(字符串[]args)
{
var p=新的父对象();
p、 a=111;
var s=JsonConvert.SerializeObject(p);
var c1=JsonConvert.DeserializeObject;
var c2=JsonConvert.DeserializeObject;
var=JsonConvert.DeserializeObject;
bool-allWorks=p.a==c1.a&&p.a==c2.a&&p.a==foreigne.a;
//你的密码在这里
Console.WriteLine(“可转换:+allWorks+c2.b”);
}
}
公共类父类{
公共INTA;
}
公共类child1:父类{
公共int b=12345;
}
公共类child2:child1{
}
公共类家庭{
公共INTA;
公共int b=99999;
}
//不可反序列化,因为
//错误“NoFamily2”不包含“a”的定义,并且找不到接受“NoFamily2”类型的第一个参数的扩展方法“a”(是否缺少using指令或程序集引用?)
公共类NoFamily2{
公共int b;
}
}

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

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

如果只有两类对象(具有和不具有Distance属性的对象),为什么不使用具有Distance属性的单一类型并将其初始化为NaN或其他类型,直到特定实例上的位置搜索为其设置值?在一些重要的地方,您可以显式地测试未初始化的值。@Jeroen对此我有两种想法。我选择继承的原因是,UI与MyObject和MyObject搜索的工作方式完全相同,如果已经进行了搜索,则只显示距离。我需要修改UI,这取决于它是用于距离搜索还是简单列表。最后一个角度是,这是一个将被JSON序列化的长列表,如果它使用组合或空值,则效率会降低