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

Viz*_*izu 24 c# copy subclass object

我有一个类A和一个B继承类的类A,并用更多的字段扩展它.

拥有a类型的对象A,如何创建包含对象包含的所有数据b的类型的B对象a

我试过a.MemberwiseClone()但只给了我另一个类型的A对象.我不能A投入,B因为继承关系只允许相反的演员.

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

Bry*_*yan 11

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

  • 接受的答案是我一直以来所做的,但这种扭曲简单而优雅. (2认同)

Ree*_*sey 10

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

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

然后你可以这样做:

B newB = new B(myA);
Run Code Online (Sandbox Code Playgroud)

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


Nil*_*zor 5

您可以通过使用反射来实现这一点。

优点:可维护性。无需更改复制构造函数或类似内容、添加或删除属性。

缺点:性能。反射很慢。不过,我们仍在讨论平均大小的类的毫秒数。

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

public static TOut GetShallowCopyByReflection<TOut>(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(objOut, property.GetValue(objIn, null), null);
        }
        catch (ArgumentException) { } // For Get-only-properties
    }
    foreach (FieldInfo field in fields)
    {
        field.SetValue(objOut, field.GetValue(objIn));
    }
    return objOut;
}
Run Code Online (Sandbox Code Playgroud)

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

单元测试:

[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>() { uri };

        var dest = source.GetShallowCopyByReflection<TestClassChild>();
        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<Uri> SomeList { get; set; }
    internal int SomeIntField;
}

internal class TestClassChild : TestClassParent {}
Run Code Online (Sandbox Code Playgroud)