Литмир - Электронная Библиотека
A
A

using System;

namespace CloneablePoint

{

  // Этот класс описывает точку.

  public class PointDescription

  {

    public string PetName {get; set;}

    public Guid PointID {get; set;}

    public PointDescription()

    {

      PetName = "No-name";

      PointID = Guid.NewGuid();

    }

  }

}

Начальные изменения самого класса

Point
включают модификацию метода
ToString()
для учета новых данных состояния, а также определение и создание ссылочного типа
PointDescription
. Чтобы позволить внешнему миру устанавливать дружественное имя для
Point
, необходимо также изменить аргументы, передаваемые перегруженному конструктору:

public class Point : ICloneable

{

  public int X { get; set; }

  public int Y { get; set; }

  public PointDescription desc = new PointDescription();

  public Point(int xPos, int yPos, string petName)

  {

    X = xPos; Y = yPos;

    desc.PetName = petName;

  }

  public Point(int xPos, int yPos)

  {

    X = xPos; Y = yPos;

  }

  public Point() { }

  // Переопределить Object.ToString().

  public override string ToString()

     => $"X = {X}; Y = {Y}; Name = {desc.PetName};\nID = {desc.PointID}\n";

  // Возвратить копию текущего объекта.

  public object Clone() => this.MemberwiseClone();

}

Обратите внимание, что метод

Clone()
пока еще не обновлялся. Следовательно, когда пользователь объекта запросит клонирование с применением текущей реализации, будет создана поверхностная (почленная) копия. В целях иллюстрации модифицируйте вызывающий код, как показано ниже:

Console.WriteLine("***** Fun with Object Cloning *****\n");

...

Console.WriteLine("Cloned p3 and stored new Point in p4");

Point p3 = new Point(100, 100, "Jane");

Point p4 = (Point)p3.Clone();

Console.WriteLine("Before modification:");  // Перед модификацией

Console.WriteLine("p3: {0}", p3);

Console.WriteLine("p4: {0}", p4);

p4.desc.PetName = "My new Point";

p4.X = 9;

Console.WriteLine("\nChanged p4.desc.petName and p4.X");

Console.WriteLine("After modification:");   // После модификации

Console.WriteLine("p3: {0}", p3);

Console.WriteLine("p4: {0}", p4);

Console.ReadLine();

В приведенном далее выводе видно, что хотя типы значений действительно были изменены, внутренние ссылочные типы поддерживают одни и те же значения, т.к. они "указывают" на те же самые объекты в памяти (в частности, оба объекта имеют дружественное имя

Му new Point
):

***** Fun with Object Cloning *****

Cloned p3 and stored new Point in p4

Before modification:

p3: X = 100; Y = 100; Name = Jane;

ID = 133d66a7-0837-4bd7-95c6-b22ab0434509

p4: X = 100; Y = 100; Name = Jane;

ID = 133d66a7-0837-4bd7-95c6-b22ab0434509

Changed p4.desc.petName  and p4.X

After modification:

p3: X = 100; Y = 100; Name = My new Point;

ID = 133d66a7-0837-4bd7-95c6-b22ab0434509

p4: X = 9; Y = 100; Name = My new Point;

ID = 133d66a7-0837-4bd7-95c6-b22ab0434509

Чтобы заставить метод

Clone()
создавать полную глубокую копию внутренних ссылочных типов, нужно сконфигурировать объект, возвращаемый методом
MemberwiseClone()
, для учета имени текущего объекта
Point
(тип
System.Guid
на самом деле является структурой, так что числовые данные будут действительно копироваться). Вот одна из возможных реализаций:

// Теперь необходимо скорректировать код для учета члена.

public object Clone()

{

  // Сначала получить поверхностную копию.

  Point newPoint = (Point)this.MemberwiseClone();

  // Затем восполнить пробелы.

  PointDescription currentDesc = new PointDescription();

  currentDesc.PetName = this.desc.PetName;

  newPoint.desc = currentDesc;

  return newPoint;

}

179
{"b":"847442","o":1}