3

Given a tree hierarchy, suppose it is the following:

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

I want to implement the ICloneable interface. In the Student.Clone method I wish to do something like:

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

Because Person is abstract I can't create a Person in the Person.Clone() method, so I can't return a cloned Person, so I can't clone a Person.

The best answer I have figured out is to overload the Clone() method in the Person class to receive a Person, clone and return it. Then in the Student.Clone implementation call this overload to clone the person's related fields. Something like this:

//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
}     

Of course if any of the above class required to build any logic in its constructors this solution is more than useless. Any idea to implement a better one?

I think this is a subproblem of a more general one, so the answer will fit very well into a large superset.

mdarefull
  • 829
  • 2
  • 14
  • 24
  • What makes your above solution "more than useless"? It seems like it is a good approach at first glance. – Enigmativity Jan 24 '15 at 01:06
  • @Enigmativity: For example, if the class Student in its constructor initialize some readonly fields? Calling the default constructor makes those fields to remain uninitialized, and cannot be initialized within the Clone() method because are readonly. That's just an example that I imagine, in a real production this will be cathastrophic – mdarefull Jan 24 '15 at 03:04
  • Why would you want duplicates of a mutable object to start with? The fact that this is not well supported in the language and library is because of a lack of demand. – H H Jan 24 '15 at 11:27
  • @HenkHolterman: I disagree with you. The fact that ICloneable and Objet.MemberwiseCopy exitst is that even if may not be a topic trending may have some application. In a particular case I would want to make an exact copy of an objetc in a given moment and after, treat both objects differents, something like a "fork". – mdarefull Jan 24 '15 at 23:12

1 Answers1

2

Since your Student class inherits the properties/fields from the base class (Person), it is not necessary to clone the person object.

The solution is to not implement the Clone() method within the abstract base class, but instead make it abstract and force the implementation in any sub-classes which inherit from it. In order to clone, you can simply instantiate a new instance of the Student class and populate the base properties using a clone constructor. See my example. I'm not sure if this helps in any way.

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;
        }
    }
Michael
  • 451
  • 2
  • 5
  • That was one of my first solutions. The problems are the following: 1 - If the base class (Person in this example) has privates fields used to process inner logic, thoses privates fields will be innaccessibles to its childs. 2 - If the base class has A LOT of fields and has A LOT of childs, or simply the hierarchy tree is big with a few of abstract classes, implements the Clone() method force us to initialize ALL the accessibles fields. That solution breaks the main principle of hierachy. – mdarefull Jan 24 '15 at 03:13
  • 3 - Like I answer to Enigmativity, what if, for example, the Person class execute some logic related to its own field when it's being initialized? Sometimes I will have to Ctrl+C, Ctrl+V the logic (wrong) and sometime I couldn't execute it leaving the object in a state of inconsistence. Thanks anyway for your effort! – mdarefull Jan 24 '15 at 03:14
  • Please see my updated code example. You can simply pass the original Student object to the base constructor when you instantiate the clone object. This will allow you to clone both public and private fields as well as execute any logic when the base is being initialised. – Michael Jan 24 '15 at 11:17
  • There's nothing more to add. That's the answer. A few hours after I read your original answer I was able to reach to that same conclusion, I was going to answer myself the question, giving you aknowledge for that, but this way is better, you find it by yourself. Thanks again! – mdarefull Jan 24 '15 at 23:08
  • It was early in the morning when I posted my original answer, but after some well deserved sleep the answer dawned on me. Hopefully I was of some assistance in getting your answer. No problem! – Michael Jan 26 '15 at 21:29