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:"); // Перед модификацией
(window.adrunTag = window.adrunTag || []).push({v: 1, el: 'adrun-4-390', c: 4, b: 390})
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;
}