Point p3 = new Point(100, 100);
Point p4 = (Point)p3.Clone();
// Изменить р4.Х (что не приводит к изменению р3.Х).
p4.X = 0;
// Вывести все объекты.
Console.WriteLine(p3);
Console.WriteLine(p4);
Console.ReadLine();
Несмотря на то что текущая реализация типа Point удовлетворяет всем требованиям, есть возможность ее немного улучшить. Поскольку Point не содержит никаких внутренних переменных ссылочного типа, реализацию метода Clone() можно упростить:
// Копировать все поля Point по очереди.
public object Clone() => this.MemberwiseClone();
Тем не менее, учтите, что если бы в типе Point содержались любые переменные-члены ссылочного типа, то метод MemberwiseClone() копировал бы ссылки на эти объекты (т.е. создавал бы
Более сложный пример клонирования
Теперь предположим, что класс Point содержит переменную-член ссылочного типа PointDescription. Данный класс представляет дружественное имя точки, а также ее идентификационный номер, выраженный как System.Guid (глобально уникальный идентификатор (globally unique identifier — GUID), т.е. статистически уникальное 128-битное число). Вот как выглядит реализация:
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);