如何将对象“克隆”到子类对象中?

我有一个A类和一个inheritanceA类的BA并用更多的字段扩展它。

如果有一个A类的对象,我怎样才能创建一个B类型的对象b ,它包含对象包含的所有数据?

我试过了一个a.MemberwiseClone()但只给了我另一个类型A对象。 而且我不能将A转换为B因为inheritance关系只允许相反的转换。

这样做的正确方法是什么?

没有办法在语言中自动构建这个…

一种选择是向B类添加一个构造函数,该构造函数将类A作为参数。

然后你可以这样做:

 B newB = new B(myA); 

在这种情况下,构造函数可以根据需要复制相关数据。

我将向A添加一个复制构造函数,然后向B添加一个新构造函数,该构造函数接受A的实例并将其传递给base的复制构造函数。

您可以通过使用reflection来实现此目的。

优势:可维护性。 无需更改复制构造函数或类似工具,添加或删除属性。

缺点 :性能。 反思很慢。 我们仍在谈论平均大小的课程毫秒。

这是一个基于reflection的浅拷贝实现,支持使用扩展方法复制到子类:

 public static TOut GetShallowCopyByReflection(this Object objIn) { Type inputType = objIn.GetType(); Type outputType = typeof(TOut); if (!outputType.Equals(inputType) && !outputType.IsSubclassOf(inputType)) throw new ArgumentException(String.Format("{0} is not a sublcass of {1}", outputType, inputType)); PropertyInfo[] properties = inputType.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy); FieldInfo[] fields = inputType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy); TOut objOut = (TOut)Activator.CreateInstance(typeof(TOut)); foreach (PropertyInfo property in properties) { try { property.SetValue(objIn, property.GetValue(objIn, null), null); } catch (ArgumentException) { } // For Get-only-properties } foreach (FieldInfo field in fields) { field.SetValue(objOut, field.GetValue(objIn)); } return objOut; } 

此方法将复制所有属性 – 私有和公共,以及所有字段。 属性通过引用复制,使其成为浅表副本。

unit testing:

 [TestClass] public class ExtensionTests { [TestMethod] public void GetShallowCloneByReflection_PropsAndFields() { var uri = new Uri("http://www.stackoverflow.com"); var source = new TestClassParent(); source.SomePublicString = "Pu"; source.SomePrivateString = "Pr"; source.SomeInternalString = "I"; source.SomeIntField = 6; source.SomeList = new List() { uri }; var dest = source.GetShallowCopyByReflection(); Assert.AreEqual("Pu", dest.SomePublicString); Assert.AreEqual("Pr", dest.SomePrivateString); Assert.AreEqual("I", dest.SomeInternalString); Assert.AreEqual(6, dest.SomeIntField); Assert.AreSame(source.SomeList, dest.SomeList); Assert.AreSame(uri, dest.SomeList[0]); } } internal class TestClassParent { public String SomePublicString { get; set; } internal String SomeInternalString { get; set; } internal String SomePrivateString { get; set; } public String SomeGetOnlyString { get { return "Get"; } } internal List SomeList { get; set; } internal int SomeIntField; } internal class TestClassChild : TestClassParent {} 

使用工厂方法模式 :

  private abstract class A { public int A1 { get; set; } public abstract A CreateInstance(); public virtual A Clone() { var instance = CreateInstance(); instance.A1 = A1; return instance; } } private class B : A { public int A3 { get; set; } public override A CreateInstance() { return new B(); } public override A Clone() { var result = (B) base.Clone(); result.A3 = A3; return result; } } private static void Main(string[] args) { var b = new B() { A1 = 1, A3 = 2 }; var c = b.Clone(); } 

在B中创建一个允许传入A类对象的ctor,然后复制A字段并根据需要设置B字段。

您可以在B类上创建一个接受基类的Convert方法。

 public ClassB Convert(ClassA a) { ClassB b = new ClassB(); // Set the properties return b; } 

你也可以让ClassB的构造函数接受ClassA的对象。

不,你做不到。 实现此目的的一种方法是在B类上添加一个接受B类参数的构造函数,并手动添加数据。

所以你可以这样:

 public class B { public B(A a) { this.Foo = a.foo; this.Bar = a.bar; // add some B-specific data here } } 

在您的基类中添加下面的CreateObject虚拟方法…

  public virtual T CreateObject() { if (typeof(T).IsSubclassOf(this.GetType())) { throw new InvalidCastException(this.GetType().ToString() + " does not inherit from " + typeof(T).ToString()); } T ret = System.Activator.CreateInstance(); PropertyInfo[] propTo = ret.GetType().GetProperties(); PropertyInfo[] propFrom = this.GetType().GetProperties(); // for each property check whether this data item has an equivalent property // and copy over the property values as neccesary. foreach (PropertyInfo propT in propTo) { foreach (PropertyInfo propF in propFrom) { if (propT.Name == propF.Name) { propF.SetValue(ret,propF.GetValue(this)); break; } } } return ret; } 

然后说你想从超级类中调用创建一个真实的子类对象

 this.CreateObject(); 

应该这样做!

虽然没有人建议这一点(并且这不会对每个人都有效),但是应该说如果你可以选择从一开始就创建对象b,那么就这样做而不是创建对象然后复制到对象b 。 例如,假设您使用相同的函数并拥有此代码:

 var a = new A(); a.prop1 = "value"; a.prop2 = "value"; ... // now you need a B object instance... var b = new B(); // now you need to copy a into b... 

而不是担心最后一个评论步骤,只需从b开始并设置值:

 var b = new B(); b.prop1 = "value"; b.prop2 = "value"; 

请不要因为你认为以上是愚蠢的而投票给我! 我遇到过许多程序员,他们如此专注于他们的代码,他们没有意识到一个简单的解决方案是盯着他们。 🙂