12

我有一个Base具有一些通用属性的抽象基类,以及许多实现不同逻辑但很少有附加字段的派生类。

public abstract Base
{
    protected int field1;
    protected int field2;
    ....

    protected Base() { ... }
}

有时我需要克隆派生类。所以我的猜测是,只需在我的基类中创建一个虚拟Clone方法,并且只在具有附加字段的派生类中覆盖它,但当然我的Base类将不再是抽象的(这不是问题,因为它只有一个protected构造函数)。

public Base
{
    protected int field1;
    protected int field2;
    ....

    protected Base() { ... }

    public virtual Base Clone() { return new Base(); }
}

public A : Base { }
public B : Base { }
  1. 问题是,由于我不知道基类中派生类的类型,Base即使我在派生类上调用它,这不会导致类实例吗?( a.Clone();) (实际上在测试之后这是正在发生的事情,但也许我的测试设计得不好,这就是为什么我对此表示怀疑)

  2. 有没有一种好方法(模式)来实现一个Clone可以按我预期工作的基本方法,还是我必须在每个派生类中编写相同的代码(我真的很想避免这种情况......)

谢谢你的帮助

4

7 回答 7

14

您可以将复制构造函数添加到基类:

public abstract Base
{
    protected int field1;
    protected int field2;

    protected Base() { ... }

    protected Base(Base copyThis) : this()
    { 
        this.field1 = copyThis.field1;
        this.field2 = copyThis.field2;
    }

    public abstract Base Clone();
}

public Child1 : Base
{
    protected int field3;

    public Child1 () : base() { ... }

    protected Child1 (Child1  copyThis) : base(copyThis)
    {
        this.field3 = copyThis.field3;
    }

    public override Base Clone() { return new Child1(this); }
}

public Child2 : Base
{
    public Child2 () : base() { ... }

    protected Child (Child  copyThis) : base(copyThis)
    {  }

    public override Base Clone() { return new Child2(this); }
}

public Child3 : Base
{
    protected int field4;

    public Child3 () : base() { ... }

    protected Child3 (Child3  copyThis) : base(copyThis)
    {
        this.field4 = copyThis.field4;
    }

    public override Base Clone()
    {
        var result = new Child1(this);
        result.field1 = result.field2 - result.field1;
    }
}
于 2013-10-01T15:22:06.260 回答
13

只需覆盖Clone并有另一种方法CreateInstance 来做你的事情。

这样,您只能拥有Base避免泛型的类。

public Base
{
    protected int field1;
    protected int field2;
    ....

    protected Base() { ... }

    public virtual Base Clone() 
    { 
        var bc = CreateInstanceForClone();
        bc.field1 = 1;
        bc.field2 = 2;
        return bc;
    }

    protected virtual Base CreateInstanceForClone()
    {
        return new Base(); 
    }
}


public A : Base 
{     
    protected int fieldInA;
    public override Base Clone() 
    { 
        var a = (A)base.Clone();
        a.fieldInA =5;
        return a;
    }

    protected override Base CreateInstanceForClone()
    {
        return new A(); 
    }
}
于 2013-10-01T15:21:33.950 回答
3

我做了与 Alexander Simonov 类似的事情,但可能更简单。这个想法是(正如我在评论中所说)在基类中只有一个 Clone(),并将所有工作留给CloneImpl()每个类根据需要定义的虚拟,依赖于CloneImpl()基类的 s。

正确类型的创建留给 C# MemberwiseClone(),它将为调用的对象做任何事情。这也消除了在任何类中对默认构造函数的需要(从未调用过)。

using System;

namespace CloneImplDemo
{
    // dummy data class
    class DeepDataT : ICloneable 
    {
        public int i;
        public object Clone() { return MemberwiseClone(); } 
    }

    class Base: ICloneable
    {
        protected virtual Base CloneImpl()
        { 
            // Neat: Creates the type of whatever object is calling. 
            // Also obviates the need for default constructors
            // (Neither Derived1T nor Derived2T have one.)
            return (Base)MemberwiseClone();
        }

        public object Clone() 
        {
            // Calls whatever CloneImpl the  
            // actual calling type implements.
            return CloneImpl();
        }
    }

    // Note: No Clone() re-implementation
    class Derived1T : Base
    {
        public Derived1T(int i) { der1Data.i = i; }
        public DeepDataT der1Data = new DeepDataT();
        protected override Base CloneImpl()
        {
            Derived1T cloned = (Derived1T)base.CloneImpl();
            cloned.der1Data = (DeepDataT)der1Data.Clone();
            return cloned;
        }
    }

    // Note: No Clone() re-implementation.
    class Derived2T : Derived1T
    {
        public Derived2T(int i1, int i2) : base(i1)
        {
            der2Data.i = i2;
        }
        public string txt = string.Empty; // copied by MemberwiseClone()
        public DeepDataT der2Data = new DeepDataT();
        protected override Base CloneImpl()
        {
            Derived2T cloned = (Derived2T)base.CloneImpl();
            // base members have been taken care of in the base impl.
            // we only add our own stuff.
            cloned.der2Data = (DeepDataT)der2Data.Clone();
            return cloned;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var obj1 = new Derived2T(1,2);
            obj1.txt = "this is obj1";

            var obj2 = (Derived2T)obj1.Clone();
            obj2.der1Data.i++;
            obj2.der2Data.i++; // changes value.
            obj2.txt = "this is a deep copy"; // replaces reference.

            // the values for i should differ because 
            // we performed a deep copy of the DeepDataT members.
            Console.WriteLine("obj1 txt, i1, i2: " + obj1.txt + ", " + obj1.der1Data.i + ", " + obj1.der2Data.i);
            Console.WriteLine("obj2 txt, i1, i2: " + obj2.txt + ", " + obj2.der1Data.i + ", " + obj2.der2Data.i);
        }
    }
}

输出:

obj1 txt, i1, i2: this is obj1, 1, 2
obj2 txt, i1, i2: this is a deep copy, 2, 3
于 2017-03-21T10:22:08.730 回答
2

你可以这样做:

public class Base<T> where T: Base<T>, new()
{
    public virtual T Clone() 
    { 
        T copy = new T();
        copy.Id = this.Id;
        return copy;
    }

    public string Id { get; set; }
}

public class A : Base<A>
{
    public override A Clone()
    {
        A copy = base.Clone();
        copy.Name = this.Name;
        return copy;
    }

    public string Name { get; set; }
}

private void Test()
{
    A a = new A();
    A aCopy = a.Clone();
}

但我怀疑它会带来一些有用的东西。我将创建另一个示例..

于 2013-10-01T15:10:25.243 回答
1

我使用 Activator 类得到了另一个想法:

public class Base
{
    public virtual object Clone()
    {
        Base copy = (Base)Activator.CreateInstance(this.GetType());
        copy.Id = this.Id;
        return copy;
    }


    public string Id { get; set; }
}

public class A : Base
{
    public override object Clone()
    {
        A copy = (A)base.Clone();
        copy.Name = this.Name;
        return copy;
    }

    public string Name { get; set; }
}

A a = new A();
A aCopy = (A)a.Clone();

但我会选择亚历山大西蒙诺夫的答案。

于 2013-10-01T15:39:46.173 回答
0

如果性能对您的情况不重要,您可以通过仅创建一个通用克隆方法来简化代码,该方法可以将任何内容克隆到任何属性相同的情况下:

Base base = new Base(){...};
Derived derived = XmlClone.CloneToDerived<Base, Derived>(base);


public static class XmlClone
{
    public static D CloneToDerived<T, D>(T pattern)
        where T : class
    {
        using (var ms = new MemoryStream())
        {
            using (XmlWriter writer = XmlWriter.Create(ms))
            {
                Type typePattern = typeof(T);
                Type typeTarget = typeof(D);

                XmlSerializer xmlSerializerIn = new XmlSerializer(typePattern);
                xmlSerializerIn.Serialize(writer, pattern);
                ms.Position = 0;
                XmlSerializer xmlSerializerOut = new XmlSerializer(typeTarget, new XmlRootAttribute(typePattern.Name));
                D copy = (D)xmlSerializerOut.Deserialize(ms);                    
                return copy;
            }
        }
    }
}
于 2018-03-09T08:01:36.287 回答
0

在尝试解决这个确切问题时发现了这个问题,并在使用 LINQPad 时获得了一些乐趣。概念证明:

void Main()
{
    Person p = new Person() { Name = "Person Name", Dates = new List<System.DateTime>() { DateTime.Now } };

    new Manager()
    {
        Subordinates = 5
    }.Apply(p).Dump();
}

public static class Ext
{
    public static TResult Apply<TResult, TSource>(this TResult result, TSource source) where TResult: TSource
    {
        var props = typeof(TSource).GetProperties(BindingFlags.Public | BindingFlags.Instance);
        foreach (var p in props)
        {
            p.SetValue(result, p.GetValue(source));
        }

        return result;
    }
}

class Person 
{
    public string Name { get; set; }
    public List<DateTime> Dates { get; set; }
}

class Manager : Person
{
    public int Subordinates { get; set; }
}
于 2018-10-18T14:33:38.957 回答