Оскільки класи представляють посилальні типи, то це накладає деякі обмеження на їх використання. Зокрема:
class Program
{
static void Main(string[] args)
{
Person p1 = new Person { Name="Tom", Age = 23 };
Person p2 = p1;
p2.Name = "Alice";
Console.Writeline(p1.Name); // Alice
Console.Read();
}
}
class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
У цьому випадку об'єкти p1 і p2 будуть указувати на той самий об'єкт у пам'яті, тому зміни властивостей у p2 торкнуться також і p1. Щоб змінна p2 указувала на новий об'єкт, але зі значеннями з p1, ми можемо застосувати клонування за допомогою реалізації інтерфейсу Icloneable:
public interface Icloneable
{
object Clone();
}
Реалізація інтерфейсу в класі Person могла б виглядати в такий спосіб:
class Person : Icloneable
{
public string Name { get; set; }
public int Age { get; set; }
public object Clone()
{
return new Person { Name = this.Name, Age = this.Age };
}
}
Використання:
class Program
{
static void Main(string[] args)
{
Person p1 = new Person { Name="Tom", Age = 23 };
Person p2 = (Person)p1.Clone();
p2.Name = "Alice";
Console.Writeline(p1.Name); // Tom
Console.Read();
}
}
Тепер усі нормально копіюється, зміни у властивостях p2 не позначаються на властивостях в p1. Для скорочення коду копіювання ми можемо використовувати спеціальний метод Memberwiseclone(), який повертає копію об'єкта:
class Person : Icloneable { public string Name { get; set; } public int Age { get; set; } public object Clone() { return this.Memberwiseclone(); } }Цей метод реалізує поверхневе (неглибоке) копіювання. Однак даного копіювання може бути недостатньо. Наприклад, нехай клас Person містить посилання на об'єкт Company:
class Person : Icloneable
{
public string Name { get; set; }
public int Age { get; set; }
public Company Work { get; set; }
public object Clone()
{
return this.Memberwiseclone();
}
}
class Company
{
public string Name { get; set; }
}
У цьому випадку при копіюванні нова копія буде вказувати на той же об'єкт Company:
Person p1 = new Person { Name="Tom", Age = 23, Work= new Company { Name = "Microsoft" } };
Person p2 = (Person)p1.Clone();
p2.Work.Name = "Google";
p2.Name = "Alice";
Console.Writeline(p1.Name); // Tom
Console.Writeline(p1.Work.Name); // Google - а повинне бути Microsoft
Поверхневе копіювання працює тільки для властивостей, що представляють примітивні типи, але не для складних об'єктів. І в цьому випадку треба застосовувати глибоке копіювання:
class Person : Icloneable
{
public string Name { get; set; }
public int Age { get; set; }
public Company Work { get; set; }
public object Clone()
{
Company company = new Company { Name = this.Work.Name };
return new Person
{
Name = this.Name,
Age = this.Age,
Work = company
};
}
}
class Company
{
public string Name { get; set; }
Більшість вбудованих в.NET класів колекцій і масиви підтримують сортування. За допомогою одного методу, який як правило, називається Sort() можна відразу відсортувати по зростанню весь набір даних. Наприклад:
int[] numbers = new int[] { 97, 45, 32, 65, 83, 23, 15 };
Array.Sort(numbers);
foreach (int n in numbers)
Console.Writeline(n);
Однак метод Sort за замовчуванням працює тільки для наборів примітивних типів, як int або string. Для сортування наборів складних об'єктів застосовується інтерфейс Icomparable. Він має всього один метод:
public interface Icomparable
{
int Compareto(object o);
}
Метод Compareto призначений для порівняння поточного об'єкта з об'єктом, який передається як параметр object o. На виході він повертає ціле число, яке може мати одне із трьох значень:
Наприклад, є клас Person:
class Person : Icomparable
{
public string Name { get; set; }
public int Age { get; set; }
public int Compareto(object o)
{
Person p = o as Person;
if (p != null)
return this.Name.Compareto(p.Name);
else
throw new Exception("Неможливо зрівняти два об'єкти");
}
}
Тут як критерій порівняння вибрано властивість Name об'єкта Person. Тому при порівнянні тут фактично йде порівняння значення властивості Name поточного об'єкта і властивості Name об'єкта, переданого через параметр. Якщо раптом об'єкт не вдасться привести до типу Person, то викидається виключення
Застосування:
Person p1 = new Person { Name = "Bill", Age = 34 };
Person p2 = new Person { Name = "Tom", Age = 23 };
Person p3 = new Person { Name = "Alice", Age = 21 };
Person[] people = new Person[] { p1, p2, p3 };
Array.Sort(people);
foreach(Person p in people)
{
Console.Writeline("{0} - {1}", p.Name, p.Age);
}
Інтерфейс Icomparable має узагальнену версію, тому ми могли б скоротити і спростити його застосування в класі Person:
class Person : Icomparable{ public string Name { get; set; } public int Age { get; set; } public int Compareto(Person p) { return this.Name.Compareto(p.Name); } }
interface Icomparer
{
int Compare(object o1, object o2);
}
Метод Compare призначено для порівняння двох об'єктів р1 і р2. Він також повертає три значення, залежно від результату порівняння: якщо перший об'єкт більше другого, то вертається число більше 0, якщо менше - то число менше нуля; якщо обоє об'єкта рівні, вертається нуль.
Створимо компаратор об'єктів Person. Нехай він порівнює об'єкти залежно від довжини рядка - значення властивості Name:
class Peoplecomparer : Icomparer{ public int Compare(Person p1, Person p2) { if (p1.Name.Length > p2.Name.Length) return 1; else if (p1.Name.Length < p2.Name.Length) return -1; else return 0; } }
У цьому випадку використовується узагальнена версія інтерфейсу Icomparer, щоб не робити зайвих перетворень типів. Застосування компаратора:
Person p1 = new Person { Name = "Bill", Age = 34 };
Person p2 = new Person { Name = "Tom", Age = 23 };
Person p3 = new Person { Name = "Alice", Age = 21 };
Person[] people = new Person[] { p1, p2, p3 };
Array.Sort(people, new Peoplecomparer());
foreach(Person p in people)
{
Console.Writeline("{0} - {1}", p.Name, p.Age);
}
Об'єкт компаратора вказується в якості другого параметра методу Array.Sort (). При цьому не важливо, чи реалізує клас Person інтерфейс IComparable чи ні. Правила сортування, встановлені компаратором, , будуть мати більший пріоритет. На початку будуть об'єкти Person, у яких імена менші, а в кінці - у яких імена довші:
Tom - 23 Bill - 34 Alice - 21
Додамо в цей же проект стандартні інтерфейси Icloneable і Icomparable.Необхідно дати можливість сортувати квадрати по зростанню сторони, а також дати можливість створювати копії кіл.
Створимо клас Point, що містить координати центру.
Створимо інтерфейс Ishape, який будє реалізувати два класи - Circle (клас кіл) і Square (клас квадратів).
Відпрацьовування