0

我有一个从另一个类 A 派生的 B 类。A 类实现了一个 Copy 方法。如何在使用 A.Copy() 的 B 类中实现 Copy 方法?

我没有使用 Clone 以及为什么我想要这种继承结构是有原因的(可能不是好的原因)。

class A 
{
    ... properties ...
    public A Copy()
    {
        ...copy properties....
    }
}

class B : A 
{
    ... one extra property ...
    public B Copy()
    {
        // how to copy base()??
        ...copy extra propertie....
    }
}
4

4 回答 4

3

一个常见的解决方案是定义一个从另一个实例复制所有属性的构造函数。然后派生类复制其类中定义的属性并调用基构造函数来复制基类的属性。

class A 
{
    // properties

    protected A(A other)
    {
        // copy properties
    }

    public A Clone()
    {
        A clone = CloneCore() as A;
        if (clone == null)
            throw new NotImplementedException("Clone Not Implemented Correctly");
        return clone;
    }

    protected virtual object CloneCore()
    {
        return new A(this);
    }
}
class B : A 
{
    // one extra property

    protected B(B other) : base(other)
    {
        // copy extra property
    }

    public new B Clone()
    {
        B clone = CloneCore() as B;
        if (clone == null)
            throw new NotImplementedException("Clone Not Implemented Correctly");
        return clone;
    }

    protected override object CloneCore()
    {
        return new B(this);
    }
}
于 2013-03-22T15:50:56.373 回答
1

您经常通过受保护的复制构造函数来实现这一点。

这是一个演示的示例程序(它使用术语“克隆”而不是“复制”,但效果是一样的。

我意识到你说过你不想克隆,但复制实际上是一回事,至少在这种情况下。

无论如何,也许这会给你一些想法。如果需要,将所有出现的“克隆”更改为“复制”... ;)

此外,这些天不赞成使用 ICloneable。不过,这显示了一般方法。

注意:这是一个完整的可编译示例,它还使用具有属性的抽象基类,并实现 GetHashCode() - 所以它看起来相当复杂。因为如果你做的一切都正确,它确实会变得复杂......

using System;
using System.IO;
using System.Diagnostics;

/*

This code demonstrates a cloning pattern that you can use for class hierarchies.

The abstract base class specifies an abstract Clone() method which must be implemented by all derived classes.
Every class except the abstract base class must have a protected copy constructor. 

This protected copy constructor will:

(1) call the base class' copy constructor, and 
(2) set any new fields introduced in the derived class.

This code also demonstrates an implementation of Equals() and CopyFrom().

*/

namespace CloningPattern
{
    static class Program
    {
        static void Main()
        {
            Derived2 test = new Derived2()
            {
                IntValue = 1,
                StringValue = "s",
                DoubleValue = 2,
                ShortValue = 3
            };

            Derived2 copy = Clone(test);
            Console.WriteLine(copy);
        }

        static Derived2 Clone(AbstractBase item)
        {
            AbstractBase abstractBase = (AbstractBase) item.Clone();
            Derived2 result = abstractBase as Derived2;
            Debug.Assert(result != null);
            return result;
        }
    }

    public abstract class AbstractBase: ICloneable
    {
        // Sample data field.

        public int IntValue { get; set; }

        // Canonical way of providing a Clone() operation
        // (except that this is abstract rather than virtual, since this class
        // is itself abstract).

        public abstract object Clone();

        // Default constructor.

        protected AbstractBase(){}

        // Copy constructor.

        protected AbstractBase(AbstractBase other)
        {
            if (other == null)
            {
                throw new ArgumentNullException("other");
            }

            this.copyFrom(other);
        }

        // Copy from another instance over the top of an already existing instance.

        public virtual void CopyFrom(AbstractBase other)
        {
            if (other == null)
            {
                throw new ArgumentNullException("other");
            }

            this.copyFrom(other);
        }

        // Equality check.

        public override bool Equals(object obj)
        {
            if (obj == null)
            {
                return false;
            }

            if (object.ReferenceEquals(this, obj))
            {
                return true;
            }

            if (this.GetType() != obj.GetType())
            {
                return false;
            }

            AbstractBase other = (AbstractBase)obj;

            return (this.IntValue == other.IntValue);
        }

        // Get hash code.

        public override int GetHashCode()
        {
            return this.IntValue.GetHashCode();
        }

        // ToString() for debug purposes.

        public override string ToString()
        {
            return "IntValue = " + IntValue;
        }

        // Implement copying fields in a private non-virtual method, called from more than one place.

        private void copyFrom(AbstractBase other)  // 'other' cannot be null, so no check for nullness is made.
        {
            this.IntValue = other.IntValue;
        }
    }

    public abstract class AbstractDerived: AbstractBase
    {
        // Sample data field.

        public short ShortValue{ get; set; }

        // Default constructor.

        protected AbstractDerived(){}

        // Copy constructor.

        protected AbstractDerived(AbstractDerived other): base(other)
        {
            this.copyFrom(other);
        }

        // Copy from another instance over the top of an already existing instance.

        public override void CopyFrom(AbstractBase other)
        {
            base.CopyFrom(other);
            this.copyFrom(other as AbstractDerived);
        }

        // Comparison.

        public override bool Equals(object obj)
        {
            if (object.ReferenceEquals(this, obj))
            {
                return true;
            }

            if (!base.Equals(obj))
            {
                return false;
            }

            AbstractDerived other = (AbstractDerived)obj;  // This must succeed because if the types are different, base.Equals() returns false.

            return (this.IntValue == other.IntValue);
        }

        // Get hash code.

        public override int GetHashCode()
        {
            // "Standard" way of combining hash codes from subfields.

            int hash = 17;

            hash = hash * 23 + base.GetHashCode();
            hash = hash * 23 + this.ShortValue.GetHashCode();

            return hash;
        }

        // ToString() for debug purposes.

        public override string ToString()
        {
            return base.ToString() + ", ShortValue = " + ShortValue;
        }

        // This abstract class doesn't need to implement Clone() because no instances of it
        // can ever be created, on account of it being abstract and all that.
        // If you COULD, it would look like this (but you can't so this won't compile):

        // public override object Clone()
        // {
        //     return new AbstractDerived(this);
        // }

        // Implement copying fields in a private non-virtual method, called from more than one place.

        private void copyFrom(AbstractDerived other)  // Other could be null, so check for nullness.
        {
            if (other != null)
            {
                this.ShortValue = other.ShortValue;
            }
        }
    }

    public class Derived1: AbstractDerived
    {
        // Must declare a default constructor.

        public Derived1(){}

        // Sample data field.

        public string StringValue{ get; set; }

        // Implement Clone() by simply using this class' copy constructor.

        public override object Clone()
        {
            return new Derived1(this);
        }

        // Copy from another instance over the top of an already existing instance.

        public override void CopyFrom(AbstractBase other)
        {
            base.CopyFrom(other);
            this.copyFrom(other as Derived1);
        }

        // Equality check.

        public override bool Equals(object obj)
        {
            if (object.ReferenceEquals(this, obj))
            {
                return true;
            }

            if (!base.Equals(obj))
            {
                return false;
            }

            Derived1 other = (Derived1)obj;  // This must succeed because if the types are different, base.Equals() returns false.

            return (this.StringValue == other.StringValue);
        }

        // Get hash code.

        public override int GetHashCode()
        {
            // "Standard" way of combining hash codes from subfields.

            int hash = 17;

            hash = hash * 23 + base.GetHashCode();
            hash = hash * 23 + this.StringValue.GetHashCode();

            return hash;
        }

        // ToString() for debug purposes.

        public override string ToString()
        {
            return base.ToString() + ", StringValue = " + StringValue;
        }

        // Protected copy constructor. Used to implement Clone().
        // Also called by a derived class' copy constructor.

        protected Derived1(Derived1 other): base(other)
        {
            this.copyFrom(other);
        }

        // Implement copying fields in a private non-virtual method, called from more than one place.

        private void copyFrom(Derived1 other)  // Other could be null, so check for nullness.
        {
            if (other != null)
            {
                this.StringValue = other.StringValue;
            }
        }
    }

    public class Derived2: Derived1
    {
        // Must declare a default constructor.

        public Derived2(){}

        // Sample data field.

        public double DoubleValue{ get; set; }

        // Implement Clone() by simply using this class' copy constructor.

        public override object Clone()
        {
            return new Derived2(this);
        }

        // Copy from another instance over the top of an already existing instance.

        public override void CopyFrom(AbstractBase other)
        {
            base.CopyFrom(other);
            this.copyFrom(other as Derived2);
        }

        // Equality check.

        public override bool Equals(object obj)
        {
            if (object.ReferenceEquals(this, obj))
            {
                return true;
            }

            if (!base.Equals(obj))
            {
                return false;
            }

            Derived2 other = (Derived2)obj;  // This must succeed because if the types are different, base.Equals() returns false.

            return (this.DoubleValue == other.DoubleValue);
        }

        // Get hash code.

        public override int GetHashCode()
        {
            // "Standard" way of combining hash codes from subfields.

            int hash = 17;

            hash = hash * 23 + base.GetHashCode();
            hash = hash * 23 + this.DoubleValue.GetHashCode();

            return hash;
        }

        // ToString() for debug purposes.

        public override string ToString()
        {
            return base.ToString() + ", DoubleValue = " + DoubleValue;
        }

        // Protected copy constructor. Used to implement Clone().
        // Also called by a derived class' copy constructor.

        protected Derived2(Derived2 other): base(other)
        {
            // Canonical implementation: use ":base(other)" to copy all
            // the base fields (which recursively applies all the way to the ultimate base)
            // and then explicitly copy any of this class' fields here:

            this.copyFrom(other);
        }

        // Implement copying fields in a private non-virtual method, called from more than one place.

        private void copyFrom(Derived2 other)  // Other could be null, so check for nullness.
        {
            if (other != null)
            {
                this.DoubleValue = other.DoubleValue;
            }
        }
    }
}
于 2013-03-22T15:50:07.417 回答
0

您可以使用base关键字,该关键字用于访问父类的成员。

class B : A 
{
    ... extra properties ...
    public B Copy()
    {
        base.Copy();
        ... copy extra properties ...
    }
}
于 2013-03-22T15:49:14.453 回答
0

简短的回答:你不能。

由于A.Copy()返回类型A,它不能更改为返回 a B

您的对象上已经有一个方法可以将所有字段复制到一个新对象中。它被称为object.MemberwiseClone()。不过要小心,因为它会执行浅拷贝。(意味着所有引用类型成员都将指向现有对象)。

根据我的发现,您通常需要一个深拷贝,不幸的是,这意味着您必须在每个可以包含的类中编写一个“深拷贝”方法A

方法Clone()通常被理解为深拷贝。(评论总是有帮助的)而且,它返回object. 如果你想要一个B背部,这将需要一个演员。由于您无法更改继承方法的签名,因此没有真正的解决方法。

new还有一件事:定义函数时不要使用关键字。它破坏了多态性并可能导致难以发现的错误。

public class A : IClonable {
   public ReferenceType member1;
   public int member2;

   public object Clone() {
       var clone = this.MemberwiseClone() as A;  //shallow copy
       clone.CloneReferenceMembers();            //deep copy
       return clone;
   }
   public virtual void CloneReferenceMembers(){
      this.member1 = this.member1.Clone();
   }

}

public class B : A {
   public AnotherReferenceType member3;
   public int member4;

    public override void CloneReferenceMembers(){
       base.CloneReferenceMembers();
       this.member3 = this.member3.Clone();
   }
}
于 2013-03-22T16:29:55.463 回答