我正在从父对象创建子对象。所以场景是我有一个对象和一个子对象,它为我想要搜索的场景添加了距离属性。我选择使用继承,因为我的 UI 与搜索对象或对象列表(而不是位置搜索的结果)等效。所以在这种情况下继承似乎是一个明智的选择。
目前我需要从
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;
}
基类需要定义一个复制构造函数:
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;
}
}
通过这种方式,基类可以处理所有派生类的属性设置。
也可以选择实施
ICloneable
public class MyObjectSearch : MyObject, ICloneable
{
public MyObjectSearch(MyObjectSearch other)
: base(other)
{
...
}
...
public MyObjectSearch Clone()
{
return new MyObjectSearch(this);
}
object ICloneable.Clone()
{
return Clone()
}
}
您可以使用反射来复制属性。
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);
}
}
}
不幸的是,没有简单的方法可以做到这一点。正如您所说,您要么必须使用反射,要么创建一个“克隆”方法,该方法将使用父对象作为输入生成新的子对象,如下所示:
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;
}
}
无论如何,您最终要么编写反射代码(速度很慢),要么手动编写每个属性。这完全取决于您是否想要可维护性(反射)或速度(手动属性副本)。
一个通用的解决方案是将其序列化为 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;
}
}
当我正在寻找这样做时,我第一次遇到这个问题。 如果您能够使用 C# 9 和
record
类。您只需在子类中创建一个新的构造函数,接收基类对象并将其交给子类即可:
public record MyObject {
...
}
public record MyObjectSearch :MyObject
{
public MyObjectSearch(MyObject parent) : base(parent) { }
...
}
然后你可以像这样创建子对象:
MyObject parent = new();
MyObjectSearch m = new MyObjectSearch(parentObj) { Distance = 1.1};
MemberwiseClone
方法。
示例:
MyObject shallowClone = (MyObject)original.MemberwiseClone();
如果需要深拷贝,可以像这样序列化/反序列化:https://stackoverflow.com/a/78612/1105687
一个示例(假设您按照该答案中的建议编写了一个扩展方法,并将其称为 DeepClone)
MyObject deepClone = original.DeepClone();
基础对象拥有带有其属性参数的构造函数似乎很自然:
public class MyObject
{
public MyObject(prop1, prop2, ...)
{
this.Prop1 = prop1;
this.Prop2 = prop2;
}
}
那么,在你的后代对象中你可以拥有:
public MyObjectSearch(MyObject obj)
:base(obj.Prop1, obj.Prop2)
这减少了与作业相关的重复。您可以使用反射来自动复制所有属性,但这种方式似乎更具可读性。
另请注意,如果您的类具有如此多的属性,以至于您正在考虑自动复制属性,那么它们很可能违反单一职责原则,您应该考虑更改您的设计。
有库可以处理这个问题;但如果你只是想在几个地方快速实现,我肯定会像之前建议的那样选择“复制构造函数”。
没有提到的一个有趣的点是,如果一个对象是子类,那么它可以从父类内部访问子类的私有变量!
因此,在父级上添加一个
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;
}
注意:只读成员变量必须在构造函数中设置,因此您必须使用子构造函数来设置这些...
虽然我一般不喜欢“放大”,但我经常使用这种方法来分析快照......