Студопедия

КАТЕГОРИИ:


Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748)

Клас object 1 страница




 

Кореневий клас System.Object всій ієрархії об'єктів .NET, названий в С# object, забезпечує всіх спадкоємців декількома важливими методами. Похідні класи можуть використовувати ці методи безпосередньо або перевизначати їх.

Клас object часто використовується і безпосередньо при описі типу параметрів методів для додання ним спільності, а також для зберігання посилань на об'єкти різного типу - таким чином реалізується поліморфізм.

Відкриті методи класу System.Object перераховані нижче.

1. Метод Equals з одним параметром повертає значення true, якщо параметр об'єкту, що визивається, посилаються на одну і ту ж область пам'яті. Синтаксис:

 

public virtual bool Equals(object obj);

 

2. Метод Equals з двома параметрами повертає значення true, якщо обидва параметри посилаються на одну і ту ж область пам'яті. Синтаксис:

 

public static bool Equals(object ob1, object ob2);

 

3. Метод GetHashCode формує хеш-кодування-код об'єкту і повертає число, що однозначно ідентифікує об'єкт. Це число використовується в різних структурах і алгоритмах бібліотеки. Якщо перевизначається метод Equals, необхідно перенавантажувати і метод GetHashCode. Синтаксис:

 

public virtual int GetHashCode();

 

4. Метод GetType повертає поточний поліморфний тип об'єкту, тобто не тип посилання, а тип об'єкту, на який він в даний момент указує. Повертане значення має тип Турe. Це абстрактний базовий клас ієрархії, що використовується для отримання інформації про типи під час виконання. Синтаксис:

 

public Type GetТуре();

 

5. Метод ReferenceEquals повертає значення true, якщо обидва параметри посилаються на одну і ту ж область пам'яті. Синтаксис:

 

public static bool ReferenceEquals(object obi, object ob2);

 

6. Метод ToString за умовчанням повертає для посилальних типів повне ім'я класу у вигляді рядка, а для значущих - значення величини, перетворене в рядок. Цей метод перевизначають для того, щоб можна було виводити інформацію про стан об'єкту. Синтаксис:

 

public virtual string ToString();

 

У похідних об'єктах ці методи часто перевизначають. Наприклад, можна перевизначити метод Equals для того, щоб задати власні критерії порівняння об'єктів, тому що часто буває зручнішим використовувати для порівняння не посилальну семантику (рівність посилань), а значущу (рівність значень).

Приклад застосування і перевизначення методів класу object для класу Monster приведений в лістингу 8.5.

 

Лістинг 8.5. Перевантаження методів класу object

using System;

 

namespace ConsoleApplicationl

{

class Monster

{

public Monster(int health, int ammo, string name)

{

this.health = health;

this.ammo = ammo;

this.name = name;

}

public override bool Equals(object obj)

{

if (obj == null || GetType()!= obj.GetType()) return false;

 

Monster temp = (Monster) obj;

return health == temp.health &&

ammo == temp.ammo &&

name == temp.name;

}

 

 

public override int GetHashCode()

{

return name.GetHashCode();

}

 

public override string ToString()

{

return string.Format("Monster {0} \t health = {1} ammo = {2}",

name, health, ammo);

}

string name;

int health, ammo;

}

 

class Class1

{

static void Main()

{

Monster X = new Monster(80, 80, "Вася");

Monster Y = new Monster(80, 80, "Вася");

Monster Z = X;

if (X == Y) Console.WriteLine("X == Y ");

else Console.WriteLine("X!= Y ");

if (X == Z) Console.WriteLine(" X == Z ");

else Console.WriteLine("X!= Z ");

if (X.Equals(Y)) Console.WriteLine(" X Equals Y ");

else Console.WriteLine(" X not Equals Y ");

Console.WriteLine(X.GetType());

}

}

}

 

Результат роботи програми:

 

X!= Y

X = = Z

X Equals Y

ConsoleApplicationl.Monster

 

У методі Equals спочатку перевіряється переданий в нього аргумент. Якщо він рівний null або його тип не відповідає типу об'єкту, що викликав метод, повертається значення false. Значення true формується у разі попарної рівності всіх полів об'єктів.

Метод GetHashCode просто делегує свої функції відповідному методу одного з полів. Метод ToString формує рядок, що містить значення полів об'єкту.

Аналізуючи результат роботи програми, можна побачити, що в операції порівняння на рівність порівнюються посилання, а в перевантаженому методі Equals - значення.

 

8.6. Рекомендації по програмуванню

 

Спадкоємство класів надає програмістові великі можливості організації коду і його багатократного використання. Спадкоємство класу Y від класу X означає, що Y є різновидом класу X, тобто конкретнішу, приватну концепцію. Базовий клас є більш загальним поняттям, ніж Y. Скрізь, де можна використовувати X, можна використовувати і Y, але не навпаки (пригадайте, що на місце базового класу можна передавати будь-який з похідних). Необхідно пам'ятати, що під час виконання програми не існує ієрархії класів і передачі повідомлень об'єктам базового класу з похідних - є тільки конкретні об'єкти класів, поля яких формуються на основі ієрархії на етапі компіляції.

Головна перевага спадкоємства полягає в тому, що на рівні базового класу можна написати універсальний код, за допомогою якого працювати також з об'єктами похідного класу, що реалізується за допомогою віртуальних методів.

Як віртуальні мають бути описані методи, які виконують у всіх класах ієрархії одну і ту ж функцію, але, можливо, різними способами. Нехай, наприклад, всі об'єкти ієрархії повинні уміти виводити інформацію про себе. Оскільки ця інформація зберігається в різних полях похідних класів, функцію виводу не можна реалізувати в базовому класі. Природно назвати її у всіх класах однаково і оголосити як віртуальну з тим, щоб її можна було викликати залежно від фактичного типу об'єкту, з яким працюють через базовий клас.

Для представлення загальних понять, які передбачається конкретизувати в похідних класах, використовують абстрактні класи. Як правило, в абстрактному класі задається набір методів, тобто інтерфейс, який кожен з нащадків реалізовуватиме по-своєму.

Альтернативним спадкоємству механізмом використання одним класом іншого є вкладення, коли один клас є полем іншого. Вкладення представляє стосунки класів "Y містить X" або "Y реалізується за допомогою X".

Для вибору між спадкоємством і вкладенням служить відповідь на питання про те, чи може у Y бути декілька об'єктів класу X ("Y містить X"). Крім того, вкладення використовується замість спадкоємства тоді, коли про класи X і Y не можна сказати, що Y є різновидом X, але при цьому Y використовує частину функціональності X ("Y реалізується за допомогою X").


РОЗДІЛ 9. ІНТЕРФЕЙСИ І СТРУКТУРНІ ТИПИ

 

У цьому розділі розглядаються спеціальні види класів: інтерфейси, структури і перелічення.

 

9.1. Синтаксис інтерфейсу

Інтерфейс є “крайнім випадком” абстрактного класу. У ньому задається набір абстрактних методів, властивостей і індексаторів, які мають бути реалізовані в похідних класах. Основна ідея використання інтерфейсу полягає в тому, щоб до об'єктів таких класів можна було звертатися однаковим чином.

Кожен клас може визначати елементи інтерфейсу по-своєму. Так досягається поліморфізм: об'єкти різних класів по-різному реагують на виклики одного і того ж методу.

 

Синтаксис інтерфейсу аналогічний синтаксису класу:

 

[атрибути] [специфікатори] interface ім'я_інтерфейсу [:предки]

тіло_інтерфейсу [; ]

 

Для інтерфейсу можуть бути вказані специфікатори, new, publiс, protected, internal і private. Специфікатор new застосовується для вкладених інтерфейсів і має такий же сенс, як і відповідний модифікатор методу класу. Решта специфікаторів управляє видимістю інтерфейсу. У різних контекстах визначення інтерфейсу допускаються різні специфікатори. За умовчанням інтерфейс доступний тільки із збірки, в якій він описаний (internal).

Інтерфейс може успадковувати властивості декількох інтерфейсів, в цьому випадку предки перераховуються через кому. Тіло інтерфейсу складають абстрактні методи, шаблони властивостей і індексаторів, а також події.

Інтерфейс не може містити константи, поля, операції, конструктори, деструктори, типи і будь-які статичні елементи.

Як приклад розглянемо інтерфейс IAction, що визначає базову поведінку персонажів комп'ютерної гри, що зустрічалися в попередніх розділах. Допустимо, що будь-який персонаж повинен уміти виводити себе на екран, атакувати і красиво вмирати:

interface IAction

{

void Draw();

int Attack(int a);

void Die();

int Power { get; set}

}

У інтерфейсі IAction задані заголовки трьох методів і шаблон властивості Power.

Відмінності інтерфейсу від абстрактного класу:

· елементи інтерфейсу за умовчанням мають специфікатор доступу publiс і не можуть мати специфікаторів, заданих явним чином;

· інтерфейс не може містити полів і звичайних методів - всі елементи інтерфейсу мають бути абстрактними;

· клас, в списку предків якого задається інтерфейс, повинен визначати всі його елементи, тоді як нащадок абстрактного класу може не перевизначати частину абстрактних методів предка (в цьому випадку похідний клас також буде абстрактним);

· клас може мати в списку предків декілька інтерфейсів, при цьому він повинен визначати всі їх методи.

У бібліотеці. NET визначена велика кількість стандартних інтерфейсів, які описують поведінку об'єктів різних класів. Наприклад, якщо потрібно порівнювати об'єкти за принципом більше або менше, відповідні класи повинні реалізувати інтерфейс IComparable. Ми розглянемо найбільш споживані стандартні інтерфейси в подальших розділах посібника.

 

9.2. Реалізація інтерфейсу

 

У списку предків класу спочатку вказується його базовий клас, якщо він є, а потім через кому - інтерфейси, які реалізує цей клас. Таким чином, в С# підтримується одиночне спадкоємство для класів і множинне - для інтерфейсів. Це дозволяє додати похідному класу властивості декількох базових інтерфейсів, реалізовуючи їх на свій розсуд.

Наприклад, реалізація інтерфейсу IAction в класі Monster показана на лістингу 9.1:

 

Лістинг 9.1. Реалізація інтерфейсу

using System;

 

namespace Iterface

{

interface IAction

{

void Draw();

int Attack(int a);

void Die();

void Passport();

int Power { get; set;}

}

class Monster: IAction

{

public Monster (int health, int ammo, string name)

{

this.health = health;

this.ammo = ammo;

this.name = name;

}

public void Draw()

{

Console.WriteLine("Тут був " + name);

}

public int Attack(int ammo_)

{

ammo -= ammo_;

if (ammo > 0) Console.WriteLine("Ба-бах!");

else ammo = 0;

return ammo;

}

public void Die()

{

Console.WriteLine("Monster " + name + " RIP");

health = 0;

}

public int Power

{

get

{

return ammo * health;

}

set

{

if (value > 0)

ammo = health = value;

}

}

public void Passport()

{

Console.WriteLine("Monster {0} \t health = {1} ammo = {2}",

name, health, ammo);

}

string name;

int health, ammo;

}

class Class1

{

public static void Act(IAction A)

{

A.Passport();

}

static void Main()

{

Monster X = new Monster(80, 80, "Вася"); // 1 Об'єкт класу

X.Draw();

IAction Y = new Monster(80, 80, "Миша"); // 2 Об'єкт типу інтерфейсу

Y.Draw();

Console.WriteLine("Power - " + Y.Power);

Y.Power = 100;

Y.Attack(-500);

Y.Draw();

Y.Passport();

Act(Y);

}

}

}

 

Природно, що сигнатури методів в інтерфейсі і реалізації повинні повністю збігатися. Для елементів інтерфейсу, що реалізовуються, в класі слід указувати специфікатор public. До цих елементів можна звертатися двома способами: через об'єкт класу і через об'єкт типу відповідного інтерфейсу. Це показано відповідно в операторах 1 і 2.

 

Результат роботи програми:

Тут був Вася

Тут був Міша

Power - 6400

Ба – бах!

Тут був Міша

Monster Міша health = 100 ammo = 600

Monster Міша health = 100 ammo = 600

 

Існує другий спосіб реалізації інтерфейсу в класі: явна вказівка імені інтерфейсу перед елементом, що реалізовується. Специфікатори доступу при цьому не указуються. До таких елементів можна звертатися в програмі тільки через об'єкт типу інтерфейсу, наприклад:

 

class Monster: IAction

{

int IAction.Power

{

get

{

return ammo * health;

}

}

 

void IAction.Draw()

{

Console.WriteLine(" Тут був " + name);

}

}

IAction Actor = new Monster(10,10,"Maшa");

Actor.Draw(); // звернення через об'єкт типу інтерфейсу

// Monster Vasia = new Monster(50, 50, "Вася");

// Vasia.Draw(); помилка!

 

Таким чином, при явному завданні імені інтерфейсу, що реалізовується, відповідний метод не входить в інтерфейс класу. Це дозволяє спростити його в тому випадку, якщо якісь елементи інтерфейсу не потрібні кінцевому користувачеві класу.

Крім того, явне завдання імені інтерфейсу, що реалізовується, перед ім'ям методу дозволяє уникнути конфліктів при множинному спадкоємстві, якщо елементи з однаковими іменами або сигнатурою зустрічаються більш ніж в одному інтерфейсі. Хай, наприклад, клас Monster підтримує два інтерфейси: один для управління об'єктами, а інший для тестування:

 

interface ITest

{

void Draw();

}

 

interface IAction

{

void Draw();

int Attack(int a);

void Die();

int Power { get; }

}

 

class Monster: IAction, ITest

{

void ITest.Draw()

{

Console.Writel_ine("Testing " + name);

}

void IAction.Draw()

{

Console.WriteLine(" Тут був " + name);

}

}

 

Обидва інтерфейси містять метод Draw з однією і тією ж сигнатурою. Розрізняти їх допомагає явна вказівка імені інтерфейсу. Звертатися до цих методів можна, використовуючи операцію приведення типу, наприклад:

Monster Vasia = new MONSTER(50, 50, "Вася");

((Itest)Vasia).Draw(); // результат: Тут був Вася

((Iaction)Vasia).Draw(); // результат: Testing Вася

Втім, якщо від таких методів не потрібна різна поведінка, можна реалізувати метод першим способом (із специфікатором public), компілятор не заперечує

:

class Monster: IAction. ITest

{

public void Draw()

{

Console.WriteLine(" Тут був " + name):

}

}

До методу Draw, описаного таким чином, можна звертатися будь-яким способом: через об'єкт класу Monster, через інтерфейс IAction або ITest.

Конфлікт виникає в тому випадку, якщо компілятор не може визначити з контексту звернення до елементу, елемент якого саме з інтерфейсів, що реалізовуються, потрібно викликати. При цьому завжди допомагає явне завдання імені інтерфейсу.

 

9.3. Робота з об'єктами через інтерфейси. Операції is і as

 

При роботі з об'єктом через об'єкт типу інтерфейсу буває необхідно переконатися, що об'єкт підтримує даний інтерфейс. Перевірка виконується за допомогою бінарної операції is. Ця операція визначає, чи сумісний поточний тип об'єкту, що знаходиться зліва від ключового слова is, з типом, заданим праворуч.

Результат операції рівний true, якщо об'єкт можна перетворити до заданого типу, і false – в іншому випадку. Операція зазвичай використовується в наступному контексті:

 

if (об’єкт is тип)

{

// виконати перетворення "об'єкту" до "типу"

// виконати дії з перетвореним об'єктом

}

Допустимо, ми оформили якісь дії з об'єктами у вигляді методу з параметром типу object. Перш ніж використовувати цей параметр усередині методу для звернення до методів, описаних в похідних класах, потрібно виконати перетворення до похідного класу. Для безпечного перетворення слід перевірити, чи можливо воно, наприклад так:

 

static vod Act(object A)

{

if (A is IAction)

{

IAction Actor = (IAction) A;

Actor.Draw();

}

}

У метод Act можна передавати будь-які об'єкти, але на екран будуть виведені тільки ті, які підтримують інтерфейс IAction.

Недоліком використання операції is є те, що перетворення фактично виконується двічі: при перевірці і при власне перетворенні. Ефективнішою є інша операція - as. Вона виконує перетворення до заданого типу, а якщо це неможливо, формує результат null, наприклад:

 

static void Act(object A)

{

IAction Actor = A as IAction;

if (Actor!= null)Actor.Draw();

}

 

Обидві розглянуті операції застосовуються як до інтерфейсів, так і до класів.

9.4. Інтерфейси і спадкоємство

Інтерфейс може не мати або мати скільки завгодно інтерфейсів-предків, в останньому випадку він успадковує всі елементи всіх своїх базових інтерфейсів, починаючи з самого верхнього рівня. Базові інтерфейси мають бути доступні як і їх нащадки. Як і в звичайній ієрархії класів, базові інтерфейси визначають загальну поведінку, а їх нащадки конкретизують і доповнюють його. У інтерфейсі-нащадку можна також вказати елементи, які змінюють успадковані елементи з такою ж сигнатурою. В цьому випадку перед елементом указується ключове слово new, як і в аналогічній ситуації в класах. За допомогою цього слова відповідний елемент базового інтерфейсу приховується. Приклад:

interface IBase

{

void F(int i);

}

interface Ileft: IBase

{

new void F(int i); // перевизначення методу F

{

 

interface Iright: IBase

{

void G();

}

interface IDerived: ILeft, IRight {}

 

class A

{

void Test(IDerived d) {

d.F(1); // Викликається ILeft.F

((IBase)d). F(1): // Викликається IBase.F

((ILeft)d). F(1); //' Викликається ILeft. F

((IRight)d).F(1); // Викликається IBase.F

}

}

 

Метод F з інтерфейсу IBase прихований інтерфейсом ILeft, не дивлячись на те що в ланцюжку IDerived - IRight - IBase він не перевизначався.

Клас, що реалізовує інтерфейс, повинен визначати всі його елементи, зокрема успадковані. Якщо при цьому явно указується ім'я інтерфейсу, воно повинне посилатися на той інтерфейс, в якому був описаний відповідний елемент, наприклад:

 

class А: IRight

{

IRight.G() {... }

IBase.F(int i) {... } // IRight.F(int i) - не можна

}

Інтерфейс, на власні або успадковані елементи якого є явне посилання, має бути вказаний в списку предків класу, наприклад:

class В: А

{

// IRight.G() {... } не можна!

}

 

class С: A, IRight

{

IRight.G() {... } // можна

IBase.F(int i) {... } // можна

}

 

Клас успадковує всі методи свого предка, зокрема ті, які реалізовували інтерфейси. Він може перевизначити ці методи за допомогою специфікатора new, але звертатися до них можна буде тільки через об'єкт класу. Якщо використовувати для звернення посилання на інтерфейс, викликається не перевизначена версія:

interface IBase

{

void А();

}

 

class Base: IBase

{

public void A() {... }

}

class Derived: Base

{

new public void A() {... }

}

...

Derived d = new Derived ();

d.A(); // викликається Derived.A();

IBase id = d;

id.A(); // викликається Base.A();

 

Проте якщо інтерфейс реалізується за допомогою віртуального методу класу, після його перевизначення в нащадку будь-який варіант звернення (через клас або через інтерфейс) приведе до одного і того ж результату:

 

interface IBase

{

void А();

}

class Base: IBase

{

public virtual void A() {... }

}

class Derived: Base

{

Public override void A() {... }

}

...

Derived d = new Derived ();

d.A(); // викликається Derived.A();

IBase id = d;

id.A(); // викликається Base.A();

 

Метод інтерфейсу, реалізований явною вказівкою імені, оголошувати віртуальним забороняється. При необхідності перевизначити в нащадках його поведінку користуються наступним прийомом: з цього методу викликається інший, захищений метод, який оголошується віртуальним. У приведеному далі прикладі метод А інтерфейсу IВase реалізується за допомогою захищеного віртуального методу А, який можна перевизначати в нащадках класу Base:

 

interface IBase

{

void A();

}

 

class Base: IBase

{

void IBase.A() { A_(); }

protected virtual void A_() {... }

}

 

class Derived: Base

{

protected override void A() {... }

}

 

Існує можливість повторно реалізувати інтерфейс, вказавши його ім'я в списку предків класу разом з класом-предком, що вже реалізував цей інтерфейс. При цьому реалізація перевизначених методів базового класу до уваги не береться:

 

interface IBase

{

void А();

}

 

class Base: IBase

{

void IBase.A() {... } // не використовується в Derived

}

 

class Derived: Base, IBase

{

public void A() {... }

}

 

Якщо клас успадковує від класу і інтерфейсу, які містять методи з однаковими сигнатурами, успадкований метод класу сприймається як реалізація інтерфейсу, наприклад:

 

interface Interfacel

{

void F():

}

 

class Class1

{

public void F() {... }

public void G{} {... }

}

 

class Class2: Class1, Interface1

{

new public void G() {... }

}

 

Тут клас Class2 успадковує від класу Class1 метод F. Інтерфейс Interface1 також містить метод F. Компілятор не видає помилку, тому що клас Class2 містить метод, відповідний для реалізації інтерфейсу.

Взагалі при реалізації інтерфейсу враховується наявність “відповідних” методів в класі незалежно від їх походження. Це можуть бути методи (описані в поточному або базовому класі) які реалізують інтерфейс явним або неявним чином.

 

9.5. Стандартні інтерфейси.NET

У бібліотеці класів .NET визначена множина стандартних інтерфейсів, задаючих бажану поведінку об'єктів. Наприклад, інтерфейс IComparable задає метод порівняння об'єктів за принципом більше або менше, що дозволяє виконувати їх сортування. Реалізація інтерфейсів IEnumerable і IEnumerator дає можливість проглядати вміст об'єкту за допомогою конструкції foreach, а реалізація інтерфейсу ICloneable - клонувати об'єкти.

Стандартні інтерфейси підтримуються багатьма стандартними класами бібліотеки. Наприклад, робота з масивами за допомогою циклу foreach можлива саме тому, що тип Array реалізує інтерфейси IEnumerable і IEnumerator. Можна створювати і власні класи, що підтримують стандартні інтерфейси, що дозволить використовувати об'єкти цих класів стандартними способами.

 

9.5.1. Порівняння об'єктів (інтерфейс IComparable)

Інтерфейс IComparable визначений в просторі імен System. Він містить всього один метод CompareTo, що повертає результат порівняння двох об'єктів, - поточного і переданого йому як параметр:

 

interface IComparable

{

int CompareTo(object obj)

}

 

Метод повинен повертати:

· 0, якщо поточний об'єкт і параметр рівні;

· від’ємне число, якщо поточний об'єкт менше параметра;

· додатне число, якщо поточний об'єкт більше параметра.

Реалізуємо інтерфейс IComparable в знайомому нам класі Monster. Як критерій порівняння об'єктів виберемо поле health. У лістингу 9.2 приведена програма, що сортує масив монстрів за збільшенням величини, що характеризує їх здоров'я (елементи класу, що не використовуються в даній програмі, не приводяться).

 

Лістинг 9.2. Приклад реалізації інтерфейсу IComparable

 

using System;

 

// Стандартні інтерфейси.NET

 

namespace examp55

{

class Monster: IComparable

{

public Monster(int health, int ammo, string name)

{

this.health = health;

this.ammo = ammo;

this.name = name;

}

 

virtual public void Passport()

{

Console.WriteLine("Monster {0} \t healt h = {1} ammo = {2}",

name, health, ammo);

}

public int CompareTo(object obj) // реализація інтерфейсу

{

Monster temp = (Monster)obj;

if (this.health > temp.health) return 1;

if (this.health < temp.health) return -1;

return 0;

}

string name;

int health, ammo;

}

 

class Class1

{

static void Main()

{

const int n = 3;

Monster[] stado = new Monster[n];

stado[0] = new Monster(50, 50, "V");

stado[1] = new Monster(80, 80, "P");

stado[2] = new Monster(40, 10, "M");

Array.Sort(stado); // сортування стало можливим

foreach (Monster elem in stado) elem.Passport();

}

}

}

 

Результат роботи програми:

 

Monster Маша health = 40 ammo = 10

Monster Вася health = 50 ammo = 50

Monster Петя health = 80 ammo = 80

 

Якщо декілька об'єктів мають однакове значення критерію сортування, їх відносний порядок проходження після сортування не зміниться.

У багатьох алгоритмах потрібно виконувати сортування об'єктів по різних критеріях. У С# для цього використовується інтерфейс IComparer.

 

9.5.2 Сортування по різних критеріях (інтерфейс IComparer)

 

Інтерфейс IComparer визначений в просторі імен System.Collections. Він містить один метод CompareTo, що повертає результат порівняння двох об'єктів, переданих йому як параметри:

interface IComparer

{

int Compare (object ob1, object ob2)

}

 

Принцип застосування цього інтерфейсу полягає в тому, що для кожного критерію сортування об'єктів описується невеликий допоміжний клас, що реалізовує цей інтерфейс. Об'єкт цього класу передається в стандартний метод сортування масиву як другий аргумент (існує декілька перевантажених версій цього методу).

Приклад сортування масиву об'єктів з попереднього лістингу по іменах (властивість Name, клас Sortbyname) і кількості озброєнь (властивість Ammo, клас Sortbyammo) приведений в лістингу 9.3. Класи параметрів сортування оголошені вкладеними, оскільки вони потрібні тільки об'єктам класу Monster.




Поделиться с друзьями:


Дата добавления: 2014-12-27; Просмотров: 597; Нарушение авторских прав?; Мы поможем в написании вашей работы!


Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет



studopedia.su - Студопедия (2013 - 2024) год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! Последнее добавление




Генерация страницы за: 0.009 сек.