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