Last active
October 7, 2025 10:20
-
-
Save sunmeat/6005a18468f4f71389d12b7d4c386f13 to your computer and use it in GitHub Desktop.
IClonable example C#
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Text; | |
namespace MakeClone | |
{ | |
class Monster : ICloneable // public object Clone() | |
{ | |
public string Name { get; set; } | |
public int Health { get; set; } | |
public int Mana { get; set; } | |
public int Ammo { get; set; } | |
public Monster(int health, int mana, int ammo, string name) | |
{ | |
Health = health; | |
Mana = mana; | |
Ammo = ammo; | |
Name = name; | |
} | |
public Monster ShallowClone() | |
{ | |
// повертає поверхневу копію через MemberwiseClone (копіює поля, але референси на об'єкти лишаються спільними) | |
return (Monster)this.MemberwiseClone(); | |
} | |
public object Clone() | |
{ | |
// повертає користувацьку копію (створює новий об'єкт з копійованими значеннями) | |
return new Monster(Health, Mana, Ammo, "Клон " + Name); | |
} | |
virtual public void Passport() | |
{ | |
Console.WriteLine("Монстр {0} з здоров'ям = {1}, манною = {2} та боєприпасами = {3}", Name, Health, Mana, Ammo); | |
} | |
} | |
class Program | |
{ | |
static void Main() | |
{ | |
Console.OutputEncoding = Encoding.UTF8; | |
Monster original = new Monster(70, 50, 80, "Шепетуха"); | |
Monster referenceCopy = original; | |
Monster shallowCopy = original.ShallowClone(); | |
Monster customCopy = (Monster)original.Clone(); | |
// демонстрація: зміна значень для ілюстрації типів копіювання | |
original.Health = 100; | |
shallowCopy.Health = 200; | |
customCopy.Health = 300; | |
Console.WriteLine("Інформація про монстрів:"); | |
original.Passport(); | |
Console.WriteLine("Копія за посиланням:"); | |
referenceCopy.Passport(); | |
Console.WriteLine("Поверхнева копія:"); | |
shallowCopy.Passport(); | |
Console.WriteLine("Користувацька копія:"); | |
customCopy.Passport(); | |
} | |
} | |
} | |
/* | |
Реалізація клонування за допомогою інтерфейсу ICloneable в коді вище все ще технічно працює в .NET 9, | |
оскільки інтерфейс не позначено як obsolete (застарілий) у фреймворку. Однак Microsoft не рекомендує його використовувати | |
в публічних API, бо метод Clone() не уточнює, чи це поверхневе (shallow) чи глибоке (deep) клонування, що призводить | |
до непередбачуваної поведінки та помилок у коді. Це проблема існує ще з часів .NET Framework і досі не виправлена в .NET 9, | |
де прийнято робити акцент на сучасні, безпечніші підходи до даних, такі наприклад як records та immutable об'єкти. | |
ShallowClone() з MemberwiseClone() — це класичний shallow-клон, який копіює тільки значення полів першого рівня | |
(референси лишаються спільними). Він актуальний для простих сценаріїв, але не масштабується для складних об'єктів. | |
Ручна реалізація Clone() — це deep-клон для примітивних типів, але для вкладених об'єктів (наприклад, колекцій) потребує рекурсії, | |
що зробить код громіздким. | |
На сьогодні така реалізація вважається застарілою для нових проєктів: вона порушує принципи ясності коду, тестування та продуктивності. | |
Краще уникати ICloneable взагалі, особливо в enterprise-додатках, де важлива передбачуваність !!! | |
=================================================================================================================== | |
Ось основні сучасні альтернативи для клонування об'єктів у C# 13 / .NET 9. | |
1) Records з with-експрешенами (рекомендовано для immutable даних): | |
Введено в C# 9, удосконалено в C# 12/13. Records — це reference-типи, але з автоматичним equals/hashcode/to-string | |
і вбудованим клонуванням через with. | |
Переваги: Автоматичний shallow-клон за замовчуванням, легко зробити deep для вкладених records. | |
Недоліки: Не для mutable станів (як у класі Monster, де змінюється Health). | |
2) Copy-конструктори: | |
Простий клас з конструктором, що приймає інший екземпляр того ж типу. Легко реалізувати shallow або deep. | |
Переваги: Явний, без інтерфейсів. Підходить для mutable класів. | |
Недоліки: Треба писати вручну для кожного класу. | |
3) Serialization (для deep-клону): | |
Використовуйте System.Text.Json або його альтернативи. Серіалізуйте об'єкт у потік і десеріалізуйте. | |
Переваги: Автоматичний deep-клон для складних графів. | |
Недоліки: Overhead на продуктивність, не для частих операцій | |
=================================================================================================================== | |
приклад з рекордсами: | |
using System.Text; | |
namespace MakeClone | |
{ | |
// monster як record: автоматично immutable, з вбудованим клонуванням через 'with' | |
public record Monster(int Health, int Mana, int Ammo, string Name); | |
class Program | |
{ | |
static void Main() | |
{ | |
Console.OutputEncoding = Encoding.UTF8; | |
Monster original = new(70, 50, 80, "Шепетуха"); | |
Monster referenceCopy = original; // копія за посиланням (mutable зміни вплинуть на обидва) | |
Monster shallowCopy = original with { }; // shallow-клон через with (копіює значення, референси спільні) | |
Monster customCopy = original with { Name = "Клон " + original.Name, Health = original.Health }; // користувацький клон з модифікаціями | |
// демонстрація: records immutable, тож зміни створюють нові об'єкти | |
Monster modifiedOriginal = original with { Health = 100 }; | |
Monster modifiedShallow = shallowCopy with { Health = 200 }; | |
Monster modifiedCustom = customCopy with { Health = 300 }; | |
Console.WriteLine("Інформація про монстрів:"); | |
Console.WriteLine($"Оригінал: {original}"); | |
Console.WriteLine("Копія за посиланням:"); | |
Console.WriteLine($"Копія за посиланням: {referenceCopy}"); | |
Console.WriteLine("Поверхнева копія:"); | |
Console.WriteLine($"Поверхнева копія: {shallowCopy}"); | |
Console.WriteLine("Користувацька копія:"); | |
Console.WriteLine($"Користувацька копія: {customCopy}"); | |
// перевірка незмінності: оригінал не змінився | |
Console.WriteLine("\nПісля модифікацій:"); | |
Console.WriteLine($"Модифікований оригінал: {modifiedOriginal}"); | |
Console.WriteLine($"Модифікована поверхнева: {modifiedShallow}"); | |
Console.WriteLine($"Модифікована користувацька: {modifiedCustom}"); | |
} | |
} | |
} | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment