我写了一些代码,发现两个类(即下面的Fish和Mammal)有相同的模式,所以我决定用泛型来总结。
问题是,我需要从 base 类部分复制一个构造函数。
此外,使用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 and Mammal是源自Animal和
的唯一类我知道他们都实现了复制构造函数。
编辑:不需要深层复制。
(类型)心脏和肺是单身并且在动物/鱼/哺乳动物之间共享。
答案 0 :(得分:1)
实现ICloneable
的一个问题是可变引用类型的存储位置(字段,变量,数组槽等)可用于封装标识,可变状态,两者或两者,但都不是.NET或其任何语言都包含任何标准约定,以指示上述哪个存储位置应该封装。
如果字段Foo1
封装了身份但不包含可变状态,则George.Clone().Foo1
应引用与George.Foo1
相同的实例。
如果字段Foo2
封装了可变状态但不包含身份,那么George.Clone().Foo2
应该引用一个新对象,该对象被初始化为与George.Foo2
具有相同的状态。
如果字段Foo3
既不包含可变状态也不包含标识[即只有{identity}之外的George.Clone().Foo3
可以引用George.Foo3
,具有相同状态的新对象,或者具有相同状态的任何其他方便对象。
如果字段Foo4
封装了可变状态和标识,则无法使用Clone
方法有意义地克隆该类型的实例。
如果存在封装身份,可变状态,两者或两者的事物的不同存储位置类型,则由于任何引用类型字段或由类型封装的其他存储位置,因此几乎没有深层次与浅层区别。应该通过递归地应用上述规则克隆(请注意,对于正确构造的对象,这不会导致无限递归,因为两个对象无法有意义地封装彼此的可变状态,而其中至少有一个也封装了另一个的身份)。不幸的是,因为在类型系统中不存在这样的区别,除了实现ad-hoc克隆方法之外没有实际的解决方案,或者实现使用属性来决定它应该做什么的克隆方法,并使用硬编码行为来构建-in .NET类型,不包含任何特殊指标。
答案 1 :(得分:0)
如果没有通过在导出的构造函数声明中给出:base(...)来区别对,则无论如何都会调用默认的基础构造函数。
您不必实施ICloneable,因为它可能会让您在问题很深或者是浅拷贝方面遇到麻烦。但无论如何,您可以遵循克隆模式(仅以不同方式调用它)。
这是一个可以引导您实现具有或不具有ICloneable接口的克隆工具的资源:How to Implement ICloneable Interface in Derivable Classes in .NET。
我们的想法是创建一个受保护的复制构造函数,而不是默认的构造函数。这样可以保留new()约束,并且仍然为派生类提供了在需要时克隆基础和派生内容的选项。