代码之家  ›  专栏  ›  技术社区  ›  Vizu

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

  •  21
  • Vizu  · 技术社区  · 16 年前

    我有一节课 A B 继承类的 A. 并用更多的字段扩展它。

    a 类型 ,如何创建对象 b 类型 包含该对象的所有数据的 A. 包含?

    我试过了 a.MemberwiseClone() A. A. 进入 B 因为继承关系只允许相反的强制转换。

    9 回复  |  直到 10 年前
        1
  •  11
  •   Bryan    16 年前

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

        2
  •  10
  •   Reed Copsey    16 年前

    在语言中没有自动实现这一点的方法。。。

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

    B newB = new B(myA);
    

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

        3
  •  5
  •   Nilzor    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 {}
    
        4
  •  4
  •   JoanComasFdz    6 年前

    使用 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();
        }
    
        5
  •  1
  •   Michael Todd    16 年前

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

        6
  •  0
  •   Brandon    16 年前

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

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

    您还可以在ClassA的对象中为ClassB创建一个构造函数。

        7
  •  0
  •   Razzie    16 年前

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

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

    public class B
    {
      public B(A a)
      {
        this.Foo = a.foo;
        this.Bar = a.bar;
        // add some B-specific data here
      }
    }
    
        8
  •  0
  •   Hoots    11 年前

    在基类中,在下面添加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>();
    

    那就够了!

        9
  •  0
  •   Jazimov    7 年前

    虽然没有人提出这一点(诚然,这并不适用于所有人),但应该说,如果您可以选择从一开始就创建对象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";
    

    请不要因为你认为上面的话很愚蠢而投我的反对票!我遇到过许多程序员,他们太专注于自己的代码,以至于没有意识到一个更简单的解决方案就在眼前

        10
  •  0
  •   Sonic Soul    3 年前

    下面是一种使用构造函数的方法:

    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"