2013-06-05 200 views
0

我写了一些代码,发现两个类(即下面的Fish和Mammal)具有相同的模式,所以我决定总结泛型。我应该实施ICloneable吗?

问题是,我需要复制基类类构造函数。

此外,由于构造函数不是默认(0参数),所以无法使用new()约束(CS0304)修复此问题。

我写这个是因为我被告知实施ICloneable不是一个好的做法。

public class OneHeart {...} 
public class TwoHeart {...} 

public class Animal<TyHeart> /* : ICloneable ?*/ { 
    public Animal(TyHeart heart) { 
     if(heart==null) throw new ArgumentNullException(); 
     Heart = heart; 
    } 
    public Heart { get; set; } 
} 
public class Fish : Animal<OneHeart> { 
    public Fish(OneHeart heart) : base(heart) {} 
    public Fish(Fish fish) : base(fish.Heart) {} // copy ctor but no use? 
} 
public class Mammal : Animal<TwoHeart> { 
    public Mammal(TwoHeart heart, Organ lung) : base(heart) {Lungs=lung;} 
    public Mammal(Mammal mammal) : base(mammal.Heart) {Lungs=mammal.Lung;} 
    public Organ Lungs {get; set;} // Mammal-only member:) 
} 
// The Zoo collects animals of only one type: 
public class Zoo<TyHeart, TyAnimal> : LinkedList<TyAnimal> 
    where TyAnimal : Animal<TyHeart> { 
    public Zoo() : base() {} 
    public Zoo(Zoo<TyHeart, TyAnimal> srcZoo) { 
     foreach(var animal in srcZoo) { 
      // CS0304 compile error: 
      base.AddLast(new TyAnimal(animal)); 
     } 
    } 
    ... 
} 

鱼类和哺乳动物来自动物性和唯一的类

我知道他们都实现拷贝构造函数。

编辑:没有深层复制需要。

(类型)心脏和肺是单身和动物/鱼/哺乳动物共享。

回答

1

与实施ICloneable的一个问题是一个可变引用类型的存储位置(字段,变量,数组槽等)可被用于封装身份,可变的状态,两者的,或两者都不是,但既不.NET也不它的任何语言都包含任何标准约定,以指示上面哪个给定的存储位置应该封装。

如果一个字段Foo1封装身份,但没有可变的状态,然后George.Clone().Foo1应该是指同一个实例George.Foo1

如果一个字段Foo2封装了可变的状态,但不认同,那么George.Clone().Foo2应该指的是被初始化为具有相同的状态George.Foo2一个新的对象。

如果字段Foo3既没有包含可变状态也没有标识[即只有不同于身份的不可变状态],那么George.Clone().Foo3可能会引用George.Foo3,具有相同状态的新对象或具有相同状态的任何其他便利对象。

如果字段Foo4封装了可变状态和标识,则使用Clone方法不能有效克隆该类型的实例。

如果存在封装身份,可变状态,两者或两者都不同的存储位置类型,那么深度与浅度的差别几乎没有,因为任何引用类型字段或其他存储位置都被封装类型应该通过递归地应用上述规则来克隆(注意,对于正确构造的对象,这不会导致无限递归,因为两个对象不能有意义地封装彼此的可变状态而没有其中至少一个也封装另一个的身份)。不幸的是,因为类型系统中不存在这样的区别,所以除了实现临时克隆方法外,没有实际的解决方案,或者实现使用属性决定应该做什么的克隆方法,并且使用硬编码行为来构建 - 不包含任何特殊指标的.NET类型。

0

默认的基础构造函数将被调用,如果在派生的构造函数声明中给出base(...)不同的话。

您不必实施ICloneable,因为它可能会使您对问题的深度或是浅拷贝造成麻烦。但无论如何,您可以按照克隆模式(仅以不同的方式调用它)。

以下是可以引导您使用或不使用ICloneable接口来实施克隆设施的资源:How to Implement ICloneable Interface in Derivable Classes in .NET

这个想法是创建一个除默认构造函数外的受保护的副本构造函数。这样可以让你保持new()约束,并且仍然提供派生类,并提供了在需要时克隆基本内容和派生内容的选项。

+0

可继承类不应具有不会链接到MemberwiseClone的克隆成员。否则,在不覆盖它的派生类的实例上调用'Clone'将产生错误类型的实例。 – supercat

+0

@supercat是不是所有虚拟方法的情况?暂时忘掉ICloneable接口,想象您实现了自己的克隆机制(例如Copy方法)。这个方法自然会返回对基类型的引用。任何继承者将不得不重写它,否则它将无法正常工作。依赖MemberwiseClone不能解决深度克隆的问题。 –

+0

如果克隆成员形成可返回到'MemberwiseClone'的链(最好使用受保护的虚拟方法链接),并且派生类不创建任何私有的可变对象,则父类的克隆方法将正确地创建派生类型的实例。否则,每个派生类都必须定义一个克隆方法覆盖,即使它唯一的事情是链接到父代的拷贝构造函数,如果不这样做不会导致编译器诊断 - 只是破坏行为。 – supercat

相关问题