Должен ли я реализовать ICloneable?
Я написал несколько кодов и обнаружил, что два класса (а именно "Рыба" и "Млекопитающее" ниже) имеют один и тот же шаблон, поэтому я решил подвести итог с помощью дженериков.
Проблема в том, что мне нужно скопировать конструктор из части базового класса.
Кроме того, это нельзя исправить с помощью ограничений 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));
}
}
...
}
Рыба и млекопитающие являются единственными классами, полученными из животных и
Я знаю, что они оба реализуют конструкторы копирования.
РЕДАКТИРОВАТЬ: Нет необходимости глубокого копирования.
(Типы) Сердца и Легкие являются одноэлементными и разделяются между животными, рыбами и млекопитающими.
2 ответа
Проблема с реализацией ICloneable
является то, что место хранения (поле, переменная, слот массива и т. д.) изменяемого ссылочного типа может использоваться для инкапсуляции идентификатора, изменяемого состояния, обоих или ни того, ни другого, но ни.NET, ни какой-либо из его языков не включают в себя никакого стандартного соглашения для указать, что из вышеперечисленного должно быть инкапсулировано в любом месте хранения.
Если поле Foo1
инкапсулирует идентичность, но не изменяемое состояние, то George.Clone().Foo1
должен ссылаться на тот же экземпляр, что и George.Foo1
,
Если поле Foo2
инкапсулирует изменяемое состояние, но не идентичность, то George.Clone().Foo2
должен ссылаться на новый объект, который инициализирован, чтобы иметь то же состояние, что и George.Foo2
,
Если поле Foo3
не инкапсулирует ни изменяемое состояние, ни идентичность [т.е. только неизменяемое состояние, отличное от идентичности], затем George.Clone().Foo3
может относиться к George.Foo3
новый объект с тем же состоянием или любой другой удобный объект с тем же состоянием.
Если поле Foo4
инкапсулирует как изменяемое состояние, так и идентичность, экземпляры типа не могут быть осмысленно клонированы с использованием Clone
метод.
Если бы существовали разные типы мест хранения для вещей, которые инкапсулируют идентичность, изменяемое состояние, оба или ни того, ни другого, для глубокого и мелкого различия было бы мало, поскольку любое поле ссылочного типа или другое место хранения, инкапсулированное типом, должно быть клонировано. путем рекурсивного применения вышеприведенных правил (обратите внимание, что для правильно сконструированных объектов это не может привести к бесконечной рекурсии, поскольку два объекта не могут осмысленно инкапсулировать изменяемое состояние друг друга без того, чтобы хотя бы один из них также инкапсулировал идентичность другого). К сожалению, поскольку в системе типов нет таких различий, нет практического решения, кроме как реализовать специальные методы клонирования или внедрить метод клонирования, который использует атрибуты для определения того, что он должен делать, и использует жестко запрограммированное поведение для встроенного -в типах.NET, которые не содержат специальных индикаторов.
Базовый конструктор по умолчанию будет вызываться в любом случае, если не указано иное, передавая: base(...) в объявлении производного конструктора.
Вам не нужно реализовывать ICloneable, так как это может вызвать проблемы в вопросе, является ли он глубоким или мелким копированием. Но в любом случае вы можете следовать шаблону клонирования (только называть его по-другому).
Вот ресурс, который может помочь вам в реализации функции клонирования с интерфейсом ICloneable или без него: Как реализовать интерфейс ICloneable в классах Derivable в.NET.
Идея состоит в том, чтобы создать конструктор защищенной копии помимо конструктора по умолчанию. Это позволит вам сохранить ограничение new() и по-прежнему предоставлять производным классам возможность клонировать базовый и производный контент при необходимости.