26

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

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

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

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

4

10 回答 10

11

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

于 2009-07-07T15:40:20.123 回答
10

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

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

然后你可以这样做:

B newB = new B(myA);

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

于 2009-07-07T15:39:26.740 回答
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;
}

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

单元测试:

[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 {}
于 2014-01-13T11:47:30.150 回答
4

使用工厂方法模式

    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();
    }
于 2012-08-20T15:39:34.167 回答
1

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

于 2009-07-07T15:38:36.397 回答
0

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

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

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

于 2009-07-07T15:40:03.943 回答
0

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

所以你可以有这样的东西:

public class B
{
  public B(A a)
  {
    this.Foo = a.foo;
    this.Bar = a.bar;
    // add some B-specific data here
  }
}
于 2009-07-07T15:40:37.297 回答
0

在您的基类中添加下面的 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;
    }

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

this.CreateObject<subclass>();

应该这样做!

于 2014-04-17T16:11:59.763 回答
0

虽然没有人建议这样做(诚然,这对每个人都不起作用),但应该说,如果您可以选择从一开始就创建对象 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...

不必担心最后一个注释步骤,只需从 b 开始并设置值:

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

请不要对我投反对票,因为您认为以上内容很愚蠢!我遇到过很多程序员,他们如此专注于他们的代码,以至于他们没有意识到一个更简单的解决方案正盯着他们看。:)

于 2017-09-22T16:23:16.280 回答
0

这是一种使用构造函数对我有用的方法:

class ClassA():
    def __init__(self, **attrs):
        self.__dict__.update(**attrs)
        ...

b = ClassA(**other_class.__dict__)

也可以使用继承

class Fields(AbstractType):
    def __init__(self, **attrs):
        self.__dict__.update(**attrs)

    my_date = Field(Date, required=True)
    text_field = Field(String, required=True)
    ...

class MyClass(InputObjectType, Fields):
    pass


class MyClassWithError(ObjectType, Fields):
    error = Field(String, required=True)


error_class = MyClassWithError(**my_class.__dict__)
error_class.error = "my error description"
于 2021-07-23T12:56:09.580 回答