Question

This relates to the "Watson et al: Beginning Visual C# Chapter 10: exercise 4": Implement the ICloneable interface on the People class to provide deep copying capability

class People : DictionaryBase: ICloneable

    public void DictAdd(Person newPerson)
    {
        Dictionary.Add(newPerson.Name, newPerson);

    public object Clone()
    {

        People newPeople = new People();


        foreach (Person myPerson in Dictionary.Values)
        {
            Person ClonePerson = (Person)myPerson.Clone();
            newPeople.DictAdd(ClonePerson);
        }

        return newPeople;
    }

In the Person class we have:

        public object Clone()
    {
        Person newPerson = new Person();
        newPerson = (Person)newPerson.MemberwiseClone();
        newPerson.Age = age;
        newPerson.Name = name;
        return newPerson;

    }

To test it in Program.cs:

People clonedPeople = (People)PeopleCollection.Clone();

        PeopleCollection.Remove("Mick");
        myPerson1.Name = "Jock";
        myPerson1.Age = 13;
        PeopleCollection.DictAdd(myPerson1);


        Console.WriteLine("In the current collection \"Mick\" is now: \"{0}\" and his age is: {1}", myPerson1.Name, myPerson1.Age);
        Console.WriteLine("But \"Mick\" should remain in the original collection, now cloned.");
        foreach (DictionaryEntry p in clonedPeople)
        {
            Console.WriteLine();
            Console.WriteLine("myPerson Name: {0} myPerson.Age: {1}", ((Person)p.Value).Name, ((Person)p.Value).Age);
        }

This works, and the original values of "Mick" are preserved. But the question is to the point of implementing ": ICloneable" on the Person and People class in the first place. The code works the same with or without it.

A related question is to the efficacy of what they call running a "recursive" implementation of ICloneable in their example

 public class Content
 {
 public int Val;
 }
 public class Cloner: ICloneable
 {
 public Content MyContent = new Content();
 public Cloner(int newVal)
 {
 MyContent.Val = newVal;
 }
     public object Clone()
     {
     Cloner clonedCloner = new Cloner(MyContent.Val);
     return clonedCloner;
     }
 }           

Have attempted miserably to get this recursion to work, but all we end up with is a StackOverflow. Short of using a global/static variable to quit the loop, does there exist an elegant way to implement this "recursively?"

Was it helpful?

Solution

  clonedCloner.MyContent = MyContent.Clone();

The Content class does not implement ICloneable so this statement cannot compile. You are getting a bit lost, I can not see which implementation of Cloner you actually used and how this class has anything to do with Person.

Do note that you did not actually test whether you got a deep clone. You only tested for the shallow clone case by checking that the collection wasn't being modified. A deep clone test would, say, alter a property of Mick and check that the original collection still has an unmodified Mick. Which is really the meaning of "deep". Which is okay in your code, you however lose elegance points for using MemberwiseClone(), it doesn't do anything useful and is the opposite of deep cloning.

Frankly, the book isn't teaching you very good practices. The ICloneable interface narrowly escaped being deprecated and should be avoided. It is a broken interface, it doesn't allow the caller to specify whether a deep or a shallow copy is desired. Too often, it is implemented as a shallow copy, because it is cheap and easy, while the caller really wanted a deep copy. Producing a nasty bug that is pretty hard to diagnose.

If you want to support deep-cloning then just add a Person DeepClone() method to your class. Now it is explicit and type-safe.

Don't dwell on this, move on in the book. Do consider finding a better one.

OTHER TIPS

/* Here is a simple program of Deep copy. This will help you to fully understand ICloneable Interface.

using System;

namespace ICloneableDemo
{

    class Program
    {
        class Demo : ICloneable
        {

            public int a, b;
            public Demo(int x, int y)
            {
                a = x;
                b = y;
            }

            public override string ToString()
            {
                return string.Format(" a : " + a + "  b: " + b);
            }


            public object Clone()
            {
                Demo d = new Demo(a, b);
                return d;
            }
        }


        static void Main(string[] args)
        {

            Demo d1 = new Demo(10, 20);
            Console.WriteLine(" d1 : "+d1);

            Demo d2 = (Demo)d1.Clone();
            Console.WriteLine(" d2 : " + d2);

            Demo d3 = (Demo)d2.Clone();
            Console.WriteLine(" d3 : " + d3);

            Console.WriteLine("Changing the value of d1");

            d1.a = 44; 
            d1.b = 33;


            Console.WriteLine(" d1 : " + d1);

            Console.WriteLine(" d2 : " + d2);

            Console.WriteLine(" d3 : " + d3);


            Console.WriteLine("Changing the value of d3");

            d3.a = 50;
            d3.b = 60;

            Console.WriteLine(" d1 : " + d1);

            Console.WriteLine(" d2 : " + d2);

            Console.WriteLine(" d3 : " + d3);


            Console.ReadKey();
        }
    }
}

/*Output:
 d1 :  a : 10  b: 20
 d2 :  a : 10  b: 20
 d3 :  a : 10  b: 20
Changing the value of d1
 d1 :  a : 44  b: 33
 d2 :  a : 10  b: 20
 d3 :  a : 10  b: 20
Changing the value of d3
 d1 :  a : 44  b: 33
 d2 :  a : 10  b: 20
 d3 :  a : 50  b: 60
*/

/* Notice the output, when one object's value is changed, it does not affect others. Thus when an object is cloned, it behaves like a separate object */

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top