C# 如何将对象“克隆”为子类对象?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1093056/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 08:03:14  来源:igfitidea点击:

How to "clone" an object into a subclass object?

c#objectcopysubclass

提问by Vizu

I have a class Aand a class Bthat inherits class Aand extends it with some more fields.

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

Having an object aof type A, how can I create an object bof type Bthat contains all data that object acontained?

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

I have tried a.MemberwiseClone()but that only gives me another type Aobject. And I cannot cast Ainto Bsince the inheritance relationship only allows the opposite cast.

我试过了,a.MemberwiseClone()但这只会给我另一种类型的A对象。而且我不能A转换成,B因为继承关系只允许相反的转换。

What is the right way to do this?

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

采纳答案by Reed Copsey

There is no means of doing this automatically built into the language...

没有办法自动内置到语言中......

One option is to add a constructor to class B that takes a class A as an argument.

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

Then you could do:

那么你可以这样做:

B newB = new B(myA);

The constructor can just copy the relevant data across as needed, in that case.

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

回答by Michael Todd

Create a ctor in B that allows one to pass in an object of type A, then copy the A fields and set the B fields as appropriate.

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

回答by Brandon

You could make a Convert method on class B that takes in the base class.

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

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

You could also have a constructor for ClassB take in an object of ClassA.

您还可以让 ClassB 的构造函数接收 ClassA 的对象。

回答by Bryan

I would add a copy constructor to A, and then add a new constructor to B that takes an instance of A and passes it to the base's copy constructor.

我会向 A 添加一个复制构造函数,然后向 B 添加一个新构造函数,该构造函数采用 A 的实例并将其传递给基类的复制构造函数。

回答by Razzie

No, you can't do that. One way to achieve this is to add a constructor on class B that accepts a parameter of type B, and add data manually.

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

So you could have something like this:

所以你可以有这样的事情:

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

回答by JoanComasFdz

Using Factory Method Pattern:

使用工厂方法模式

    private abstract class A
    {
        public int P1 { get; set; }

        public abstract A CreateInstance();

        public virtual A Clone()
        {
            var instance = CreateInstance();
            instance.P1 = this.P1;
            return instance;
        }
    }

    private class B : A
    {
        public int P2 { get; set; }

        public override A CreateInstance()
        {
            return new B();
        }

        public override A Clone()
        {
            var result = (B) base.Clone();
            result.P2 = P2;
            return result;
        }
    }

    private static void Main(string[] args)
    {
        var b = new B() { P1 = 111, P2 = 222 };

        var c = b.Clone();
    }

回答by Nilzor

You can achieve this by using reflection.

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

Advantage:Maintainability. No need for changing copy-constructor or similar, adding or removing properties.

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

Disadvantage: Performance. Reflection is slow. We're still talking milliseconds on average sized classes though.

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

Here's a reflection-based shallow copy implementation supporting copy-to-subclass, using extension methods:

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

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

This method will copy all properties - private and public, as well as all fields. Properties are copied by reference, making it a shallow copy.

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

Unit tests:

单元测试:

[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 {}

回答by Hoots

In your base class add the CreateObject virtual method below...

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

    public virtual T CreateObject<T>()
    {
        if (typeof(T).IsSubclassOf(this.GetType()))
        {
            throw new InvalidCastException(this.GetType().ToString() + " does not inherit from " + typeof(T).ToString());
        }

        T ret = System.Activator.CreateInstance<T>();

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

then say you want to create a real life subclass object from the super class just call

然后说你想从超类创建一个现实生活中的子类对象,只需调用

this.CreateObject<subclass>();

That should do it!

应该这样做!

回答by Jazimov

While no one suggested this (and this won't work for everyone, admittedly), it should be said that if you have the option of creating object b from the get-go, do that instead of creating object a then copying to object b. For example, imagine that you are in the same function and have this code:

虽然没有人建议这样做(不可否认,这不会对每个人都有效),但应该说,如果您可以选择从一开始就创建对象 b,请这样做,而不是创建对象 a 然后复制到对象 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...

Instead of worrying about that last commented step, just start with b and set the values:

不用担心最后评论的步骤,只需从 b 开始并设置值:

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

Please don't downvote me because you think the above is stupid! I have encountered many programmers who are so focused on their code that they didn't realize a simpler solution is staring them in the face. :)

请不要贬低我,因为你认为以上是愚蠢的!我遇到过许多程序员,他们如此专注于他们的代码,以至于他们没有意识到一个更简单的解决方案正在面对他们。:)