2015-01-24 3 views
1

Учитывая дерево иерархии, пусть это выглядит следующим образом:Как исправить реализацию ICloneable в иерархии дерева, внедренной абстрактным классом?

abstract class Person : ICloneable 
... 
sealed class Student : Person 
... 

Я хочу реализовать ICloneable интерфейс. В методе Student.Clone я хотел бы сделать что-то вроде:

{ 
    Student clonedStudent = base.Clone() as Student; 
    clonedStudent.x1 = this.x1; 
    return clonedStudent 
} 

Потому что Person является абстрактным, я не могу создать Человек в Person.Clone() метод, поэтому я не могу вернуться клонированными людьми, поэтому я не могу клонировать человека.

Лучший ответ, который я выяснил, - это перегрузить метод Clone() в классе Person, чтобы получить Person, clone и вернуть его. Затем в реализации Student.Clone вызовите эту перегрузку, чтобы клонировать связанные с ней поля. Что-то вроде этого:

//In the Person class: 
public abstract object Clone(); 
protected Person Clone(Person clonedPerson) 
{ 
    // Clone all person's fields 
    return clonedPerson: 
} 
//In the Student class: 
public override object Clone() 
{ 
    Student clonedStudent = base.Clone(new Student()) as Student; 
    // Clone all student's fields 
    return clonedStudent 
}  

Конечно, если какой-либо из указанного класса, необходимого для создания какой-либо логики в своих конструкторах это решение является более чем бесполезным. Любая идея реализовать лучшую?

Я думаю, что это подзадача более общего, поэтому ответ будет очень хорошо вписываться в большой надмножество.

+0

Что делает ваше решение выше «бесполезным»? Похоже, что это хороший подход на первый взгляд. – Enigmativity

+0

@Enigmativity: Например, если класс Student в его конструкторе инициализирует некоторые поля только для чтения? Вызов конструктора по умолчанию заставляет эти поля оставаться неинициализированными и не может быть инициализирован в методе Clone(), поскольку они являются readonly. Это просто пример, который я себе представляю, в реальной постановке это будет катастрофическим. – mdarefull

+0

Зачем вам нужны дубликаты изменяемого объекта? Тот факт, что это плохо поддерживается в языке и библиотеке, объясняется отсутствием спроса. –

ответ

2

Поскольку ваш класс Student наследует свойства/поля из базового класса (Person), нет необходимости клонировать объект person.

Решение заключается в том, чтобы не реализовать метод Clone() в абстрактном базовом классе, а вместо этого сделать его абстрактным и принудительно реализовать в любых подклассах, которые наследуются от него. Чтобы клонировать, вы можете просто создать экземпляр класса Student и заполнить базовые свойства с помощью конструктора клонирования. См. Мой пример. Я не уверен, что это помогает.

class Program 
    { 
     static void Main(string[] args) 
     { 
      Student studentA = new Student(1000, "Defense Against the Dark Arts", "Harry", "Potter", 25); 
      Student studentB = (Student)studentA.Clone(); 
     } 
    } 

    public abstract class Person : ICloneable 
    { 
     public string FirstName { get; set; } 
     public string Surname { get; set; } 

     private int SomePrivateVariable { get; set; } 

     public Person() 
     { 

     } 

     public Person(string firstName, string surname, int privateVariableDefault) 
     { 
      this.FirstName = firstName; 
      this.Surname = surname; 
      this.SomePrivateVariable = privateVariableDefault; 
     } 

     public Person(Person original) 
     { 
      this.FirstName = original.FirstName; 
      this.Surname = original.Surname; 
      this.SomePrivateVariable = original.SomePrivateVariable; 
     } 

     public abstract object Clone(); 
    } 

    public sealed class Student : Person 
    { 
     public int StudentId { get; set; } 
     public string CourseTitle { get; set; } 

     public Student() 
     { 

     } 

     //Constructor with all the fields, passed down to the base class 
     public Student(int studentId, string courseTitle, string firstName, string surname, int baseVariableDefault) 
      : base(firstName, surname, baseVariableDefault) 
     { 
      this.StudentId = studentId; 
      this.CourseTitle = courseTitle; 
     } 

     //A clone constructor which takes an object of the same type and populates internal 
     //and base properties during construction 
     public Student(Student original) 
      : base(original) 
     { 
      this.FirstName = original.FirstName; 
      this.Surname = original.Surname; 
      this.StudentId = original.StudentId; 
      this.CourseTitle = original.CourseTitle; 
     } 

     public override object Clone() 
     { 
      Student clone = new Student(this);  
      return clone; 
     } 
    } 
+0

Это было одно из моих первых решений. Проблемы заключаются в следующем: 1 - Если базовый класс (Лицо в этом примере) имеет приватные поля, используемые для обработки внутренней логики, тосы, заполненные полями, будут недоступны для его дочерних элементов. 2 - Если базовый класс содержит много полей и имеет много детей, или просто дерево иерархии является большим с несколькими абстрактными классами, реализует метод Clone(), заставляя нас инициализировать ВСЕ поля доступа. Это решение нарушает главный принцип иерархии. – mdarefull

+0

3 - Как я отвечаю Enigmativity, что, если, например, класс Person выполняет некоторую логику, связанную с ее собственным полем, когда он инициализируется? Иногда мне придется Ctrl + C, Ctrl + V логика (неправильная), а иногда я не мог ее выполнить, оставив объект в состоянии несогласованности. В любом случае, спасибо за ваши усилия! – mdarefull

+0

См. Мой обновленный пример кода. Вы можете просто передать исходный объект Student базовому конструктору при создании экземпляра объекта clone.Это позволит вам клонировать как государственные, так и частные поля, а также выполнять любую логику при инициализации базы. – Michael