ICloneable интерфейс

Это относится к "Watson et al. Начало Visual C# Глава 10: упражнение 4": Реализация интерфейса ICloneable в классе People для обеспечения возможности глубокого копирования.

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

В классе Person мы имеем:

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

    }

Чтобы проверить это в 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);
        }

Это работает, и оригинальные значения "Мик" сохраняются. Но вопрос заключается в том, чтобы в первую очередь реализовать класс ": ICloneable" для класса "Человек и люди". Код работает одинаково с или без него.

С этим связан вопрос об эффективности того, что они называют "рекурсивной" реализацией ICloneable в их примере.

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

Мы с треском пытались заставить эту рекурсию работать, но все, что у нас получается, это Stackru. Если не использовать глобальную / статическую переменную для выхода из цикла, существует ли элегантный способ реализовать это "рекурсивно?"

2 ответа

Решение
  clonedCloner.MyContent = MyContent.Clone();

Класс Content не реализует ICloneable, поэтому этот оператор не может быть скомпилирован. Вы немного заблудились, я не могу понять, какую реализацию Cloner вы на самом деле использовали и как этот класс имеет какое-либо отношение к Person.

Обратите внимание, что вы на самом деле не проверяли, есть ли у вас глубокий клон. Вы проверяли только случай мелкого клона, проверяя, что коллекция не изменялась. Глубокий тест на клонирование, скажем, изменил бы свойство Мика и проверил бы, что оригинальная коллекция все еще содержит неизмененный Мика. Что на самом деле означает "глубокий". Что хорошо в вашем коде, однако вы теряете очки элегантности за использование MemberwiseClone(), это не делает ничего полезного и является противоположностью глубокого клонирования.

Честно говоря, книга не учит вас хорошим практикам. Интерфейс ICloneable избежал, будучи устаревшим, и его следует избегать. Это неработающий интерфейс, он не позволяет вызывающей стороне указывать, нужна ли глубокая или мелкая копия. Слишком часто это реализуется как мелкая копия, потому что это дешево и просто, в то время как вызывающая сторона действительно хотела глубокую копию. Создание неприятной ошибки, которую довольно сложно диагностировать.

Если вы хотите поддерживать глубокое клонирование, просто добавьте Person DeepClone() метод для вашего класса. Теперь это явно и безопасно для типов.

Не зацикливайтесь на этом, идите дальше в книге. Подумайте о поиске лучшего.

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

/* Обратите внимание на вывод: когда значение одного объекта изменяется, оно не влияет на другие. Таким образом, когда объект клонируется, он ведет себя как отдельный объект */

Другие вопросы по тегам