0

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

问题是,我需要从基类部分复制一个构造函数。

此外,这不能使用 new() 约束 (CS0304) 来解决,因为构造函数不是默认的(0 参数)构造函数。

我写这个是因为有人告诉我实现 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));
        }
    }
    ...
}

Fish 和 Mammal 是唯一从 Animal 派生的类,

我知道他们都实现了复制构造函数。

编辑:不需要深拷贝。

(类型)心脏和肺是单例的,在动物/鱼/哺乳动物之间共享。

4

2 回答 2

1

实现的一个问题ICloneable是,可变引用类型的存储位置(字段、变量、数组槽等)可用于封装标识、可变状态,或者两者都不封装,但 .NET 及其任何语言都不包括任何标准约定,以指示上述任何给定存储位置应该封装的哪一个。

如果一个字段Foo1封装了身份而不是可变状态,那么George.Clone().Foo1应该引用与George.Foo1.

如果一个字段Foo2封装了可变状态而不是标识,那么George.Clone().Foo2应该引用一个新对象,该对象被初始化为具有与 相同的状态George.Foo2

如果一个字段Foo3既不封装可变状态也不封装身份[即除了身份之外的唯一不可变状态],则George.Clone().Foo3可以引用George.Foo3具有相同状态的新对象,或具有相同状态的任何其他方便对象。

如果一个字段Foo4同时封装了可变状态和标识,则无法使用Clone方法有意义地克隆该类型的实例。

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

于 2013-06-26T00:14:07.783 回答
0

如果没有通过在派生构造函数声明中给出 : base(...) 进行不同的说明,无论如何都会调用默认的基本构造函数。

您不必实施 ICloneable ,因为它可能会给您带来麻烦,因为它是深拷贝还是浅拷贝。但无论如何,你可以按照克隆模式(只是叫它不同)。

这里有一个资源可以引导您在有或没有 ICloneable 接口的情况下实现克隆工具:如何在 .NET 的派生类中实现 ICloneable 接口

这个想法是在默认构造函数之外创建一个受保护的复制构造函数。这将让您保留 new() 约束,并在需要时仍为派生类提供克隆基本内容和派生内容的选项。

于 2013-06-25T15:44:53.590 回答