Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

ЛабРаб_ООП

.pdf
Скачиваний:
145
Добавлен:
27.05.2015
Размер:
1.37 Mб
Скачать

Клас Stack

Клас колекції, призначений для підтримки стека, називається Stack. Він реалізує інтерфейси ICollection, IEnumerable та IСloneable. Стек – це динамічна колекція, яка при необхідності збільшується, щоб прийняти для зберігання нові елементи, причому кожного разу, коли стек повинен розширитися, його місткість подвоюється.

У класі Stack визначені наступні конструктори:

public Stack()

public Stack(int capacity)

public Stack(ICollection c)

Перший конструктор призначений для створення порожнього стека з початковою місткістю, рівною 10 елементам. Другий створює порожній стек з початковою місткістю, заданою параметром capacity. Третій конструктор служить для побудови стека, який ініціалізувався елементами і місткістю колекції, заданої параметром c.

Крім методів, визначених в інтерфейсах, які реалізує клас Stack, в нім визначені також власні методи, перераховані в Таблиці 8.

Таблиця 8. Методи, визначені в класі Stack.

Метод

Опис

public virtual bool Contains(object

Повертає значення true, якщо об'єкт v міститься

в стеку. Інакше повертає значення false.

v)

 

public virtual void Clear()

Встановлює властивість Count рівною нулю, тим

самим ефективно очищаючи стек.

 

public virtual object Peek()

Повертає елемент, розташований у вершині стека,

але не видаляє його

 

public virtual object Pop()

Повертає елемент, розташований у вершині стека,

і видаляє його

 

public virtual void Push(object v)

Поміщає об'єкт v в стек

 

public static Stack

Повертає синхронізовану версію Stack-списку,

переданого в параметрі stk

Synchronized(Stack stk)

 

public virtual object[] ToArray()

Повертає масив, який містить копії елементів

стека

 

Розглянемо приклад створення і використання стека.

using System;

 

using System.Collections;

 

using System.Collections.Generic;

 

using System.Text;

 

namespace Stack2

 

{

 

class Program

 

{

 

static void Main(string[] args)

{

Stack alphabet = new Stack(); alphabet.Push("A"); alphabet.Push("B"); alphabet.Push("C");

Console.Write("First Iteration: ");

foreach (string item in alphabet)

{

Console.Write(item);

}

51

Console.WriteLine("\nItem pulled from collection: " + alphabet.Pop().ToString()); Console.Write("Second iteration: ");

foreach (string item in alphabet)

{

Console.Write(item);

}

Console.ReadLine();

}

}

}

Клас Queue

Клас колекції, призначений для підтримки черги, називається Queue. Він реалізує інтерфейси ICollection, IEnumerable та IСloneable. Черга – це динамічна колекція, яка при необхідності збільшується, щоб прийняти для зберігання нові елементи, причому кожного разу, коли така необхідність виникає, поточний розмір черги умножається на коефіцієнт зростання, який за умовчанням дорівнює значенню 2,0.

У класі Queue визначені наступні конструктори:

public Queue()

public Queue(int capacity)

public Queue(int capacity, float growFact)

public Queue(ICollection c)

Перший конструктор призначений для створення порожньої черги з початковою місткістю, рівною 32 елементам, і коефіцієнтом зростання 2,0. Другий створює порожню черга з початковою місткістю, заданою параметром capacity, і коефіцієнтом зростання 2,0. Третій відрізняється від другого тим, що дозволяє задати коефіцієнт зростання за допомогою параметра growFact. Четвертий конструктор служить для створення черги, яка ініціалізувалася елементами і місткістю колекції, заданої параметром c.

Крім методів, визначених в інтерфейсах, які реалізує клас Queue, в нім визначені також власні методи. Щоб помістити об'єкт в чергу, викличте метод Enqueue(). Щоб витягнути верхній елемент і видалити його з черги, використовуйте метод Dequeue(). Метод Peek() дозволяє повернути наступний об'єкт, не видаляючи його з черги.

Розглянемо приклад, в якому демонструється використання класу Queue.

using System;

using System.Collections;

using System.Collections.Generic; using System.Text;

namespace Queue1

{

class Program

{

static void Main(string[] args)

{

Queue alphabet = new Queue(); alphabet.Enqueue("A"); alphabet.Enqueue("B"); alphabet.Enqueue("C");

Console.Write("First Iteration: ");

foreach (string item in alphabet)

{

52

Console.Write(item);

}

Console.WriteLine("\nItem pulled from collection: " + alphabet.Dequeue().ToString());

Console.Write("Second iteration: ");

foreach (string item in alphabet)

{

Console.Write(item);

}

Console.ReadLine();

}

}

}

Лабораторна робота №5. Класи в C#. Спадкування.

Мета роботи: засвоїти основи побудови ієрархії класів на мові C#, набути практичних навичок використання прийомів об'єктно-орієнтованого програмування.

Варіанти завдання.

1.Перелік класів: Студент, Викладач, Персона, Зав. кафедрою.

2.Перелік класів: Службовець, Персона, Робочий, Інженер.

3.Перелік класів: Робочий, Кадри, Інженер, Адміністрація.

4.Перелік класів: Деталь, Механізм, Виріб, Вузол.

5.Перелік класів: Організація, Страхова компанія, Суднобудівельна компанія, Завод.

6.Перелік класів: Журнал, Книга, Друкарське видання, Підручник.

7.Перелік класів: Тест, Іспит, Випускний іспит, Випробування.

8.Перелік класів: Місце, Область, Місто, Мегаполіс.

9.Перелік класів: Іграшка, Продукт, Товар, Молочний продукт.

10.Перелік класів: Квитанція, Накладна, Документ, Чек.

11.Перелік класів: Автомобіль, Потяг, Транспортний засіб, Експрес.

12.Перелік класів: Двигун, Двигун внутрішнього згорання, Дизель, Турбореактивний двигун.

13.Перелік класів: Республіка, Монархія, Королівство, Держава.

14.Перелік класів: Ссавці, Парнокопитні, Птахи, Тварина.

15.Перелік класів: Корабель, Пароплав, Парусник, Корвет.

Хід роботи.

1.Виконати Вправу 1.

2.Виконати Вправу 2.

3.Визначити ієрархію класів (відповідно до варіанту завдання).

4.Для визначення ієрархії класів потрібно зв'язати відношенням спадкоємства класи, приведені у варіанті завдання лабораторної роботи. З перерахованих класів вибрати один, який стоятиме на чолі ієрархії. Це буде абстрактний клас.

5.Визначити в класах всі необхідні конструктори і деструкцію.

53

6.Визначити в класі статичну компоненту - покажчик на початок зв'язаного списку об'єктів і статичну функцію для проглядання списку. Для додавання об'єкту в список слід передбачити метод класу, тобто об'єкт сам додає себе в список. Наприклад, а.Add() - об'єкт а додає себе в список. Включення об'єкту в список можна виконувати при створенні об'єкту, тобто помістити операторів включення в конструктор. У разі ієрархії класів, включення об'єкту в список повинен виконувати тільки конструктор базового класу. Ви повинні продемонструвати обидва ці способи.

7.Реалізувати класи.

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

Вправа 1.

Приклад #1: Class + new operator

using System; class Car

{

private String mName;

//Constructor

public Car(){ mName="No Name"; }

//Constructor

public Car(String name) { mName=name; }

//Destructor

~Car()

Console.WriteLine("Disposing of "+mName); }

// Member Method public void Print()

{

Console.WriteLine("Name : " + mName);

}

}

class TestMain

{

public static void Main( )

{

for(int i=0;i<9000;++i)

{

Car a=new Car("Name:"+i); а.Print();

}

}

}

Вправа 2.

Приклад #1: Пізнє скріплення і віртуальні функції

using System;

public class Base

{

public void PrintItem()

{

for(int i=0;i<10;++i) // Is There Late Binding????

54

Console.WriteLine("Item: "+this.GetItem(i) ); }

}

public virtual int GetItem(int index)

{

return 0;

}

}

public class Derived:Base

{

public override int GetItem(int index) //*override*

{

return index*index;

}

}

public class TestMain

{

public static void Main()

{

new Derived().PrintItem();

}

}

Приклад #2 Абстрактні методи

using System;

public abstract class Base

{

public void PrintItem()

{

for(int i=0;i<10;++i) // Is There Late Binding???? Console.WriteLine("Item: "+this.GetItem(i) ); }

}

 

abstract public int GetItem(int index);

// No

}

 

public class Derived:Base

 

{

 

override public int GetItem(int index)

 

{

 

return index*index;

 

}

 

}

 

public class TestMain

 

{

 

public static void Main()

 

{

 

new Derived().PrintItem();

 

}

 

}

 

Методичні вказівки та теоретичні відомості.

Клас – це шаблон, який визначає форму об'єкту. Він задає як дані, так і код, який оперує цими даними.

55

Визначення класу

Клас - це тип даних, який задає реалізацію деякої абстракції даних, характерної для завдання, на користь якого створюється програмна система [30]. Клас створюється за допомогою ключового слова class. Загальна форма визначення класу, який містить тільки змінні і методи, має наступний вигляд:

class имя_класса {

//Оголошення змінних екземплярів. доступ тип переменная1; доступ тип переменная2;

//. . .

доступ тип переменнаяN;

// Оголошення методів.

доступ тип_возврата метод1(параметри){

//тіло методу

}

доступ тип_возврата метод2(параметри){

//тіло методу

}

// . . .

доступ тип_возврата методN(параметри){ // тіло методу

}

}

Загальний синтаксис опису класу можна представити у вигляді:

[атрибути][модифікатори]class им’я_класу[:список_батьків] { тіло_класу}

Атрибути будуть розглянуті дещо пізніше. Можливими модифікаторами в оголошенні класу можуть бути модифікатори new, abstract, sealed, про яких говоритимемо при розгляді спадкоємства, та чотири модифікатори доступу (private, protected, public, internal), два з яких - private і protected - можуть бути задані тільки для вкладених класів. Зазвичай клас має атрибут доступу public, що є значенням за умовчанням.

У тілі класу можуть бути оголошені:

oконстанти;

oполя;

oконструктори і деструкції;

oметоди;

oподії;

oделегати;

oкласи (структури, інтерфейси, перерахування).

Поля класу.

Основу будь-якого класу складають його конструктори, поля і методи. Поля класу синтаксично є звичайними змінними (об'єктами) мови. Їх опис задовольняє звичайним правилам оголошення змінних. Змістовно поля задають представлення тієї самої абстракції даних, яку реалізує клас. Поля характеризують властивості об'єктів класу.

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

56

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

Якщо деякі поля класу A повинні бути доступні для методів класу B, що є нащадком класу A, то ці поля слід забезпечити атрибутом protected. Такі поля називаються захищеними. Якщо деякі поля повинні бути доступні для методів класів B1, B2 і так далі, дружніх по відношенню до класу A, то ці поля слід забезпечити атрибутом internal, а всі дружні класи B помістити в один проект (assembly). Такі поля називаються дружніми. Нарешті, якщо деякі поля повинні бути доступні для методів будь-якого класу B, якому доступний сам клас A, то ці поля слід забезпечити атрибутом public. Такі поля називаються загальнодоступними або відкритими.

Методи класу

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

Кожен метод має модифікатор доступу, що приймає одне з чотирьох значень: public, private, protected, internal. Атрибутом доступу за умовчанням є атрибут private. Незалежно від значення атрибуту доступу, всі методи доступні для виклику при виконанні методу класу. Якщо методи мають атрибут доступу private, можливо, опущений, то тоді вони доступні тільки для виклику лише усередині методів самого класу. Такі методи вважаються закритими. Зрозуміло, що клас, у якого всі методи закриті, абсурдний, оскільки ніхто не зміг би викликати жоден з його методів. Як правило, у класу є відкриті методи, що задають інтерфейс класу, і закриті методи. Інтерфейс - це обличчя класу і саме він визначає, чим клас цікавий своїм клієнтам, що він може робити, які сервіси надає клієнтам. Закриті методи складають важливу частину класу, дозволяючи клієнтам не вникати в багато деталей реалізації. Ці методи клієнтам класу недоступні, вони про них можуть нічого не знати, і, найголовніше, зміни в закритих методах ніяк не відбиваються на клієнтах класу за умови коректної роботи відкритих методів.

Якщо деякі методи класу A повинні бути доступні для викликів в методах класу B, що є нащадком класу A, то такі методи слід забезпечити атрибутом protected. Якщо деякі методи повинні бути доступні тільки для методів класів B1, B2 і так далі, дружніх по відношенню до класу A, то такі методи слід забезпечити атрибутом internal, а всі дружні класи B помістити в один проект. Нарешті, якщо деякі методи повинні бути доступні для методів будь-якого класу B, якому доступний сам клас A, то такі методи забезпечуються атрибутом public.

Методи-властивості

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

oчитання, запис (Read, Write);

oчитання, запис при першому зверненні (Read, Write-once);

oтільки читання (Read-only);

oтільки запис (Write-only);

oні читання, ні запис (Not Read, Not Write).

Загальнодоступність властивостей (атрибут public) дозволяє реалізувати тільки першу стратегію. У мові C# прийняте, як і в інших об'єктних мовах, властивості оголошувати закритими, а потрібну стратегію доступу організовувати через методи. Для ефективності цього процесу і введені спеціальні методи-властивості.

Розглянемо, наприклад, клас Person, у якого п'ять полий: fam, status, salary, age, health, що характеризують відповідно прізвище, статус, зарплату, вік і здоров'я персони. Для кожного з цих

57

полів може бути розумною своя стратегія доступу. Вік доступний для читання і запису, прізвище можна задати тільки один раз, статус можна тільки читати, зарплата недоступна для читання, а здоров'я закрите для доступу і лише спеціальні методи класу можуть повідомляти деяку інформацію про здоров'я персони. От як на C# можна забезпечити ці стратегії доступу до закритих полів класу [30]:

public class Person

{

//поля (всі закрито)

string fam="", status="", health=""; int age=0, salary=0;

//методи - властивості

/// <summary>

///стратегія: Read,Write-once (Читання, запис при першому зверненні)

/// </summary> public string Fam

{

set {if (fam == "") fam = value;} get {return(fam);}

}

/// <summary>

///стратегія: Read-only(Тільки читання)

/// </summary>

public string Status

{

get {return(status);}

}

/// <summary>

///стратегія: Read,Write (Читання, запис)

/// </summary> public int Age

{

set

{

age = value;

if(age < 7) status ="дитина";

else if(age <17) status ="школяр"; else if (age < 22) status = "студент"; else status = "службовець";

}

get {return(age);}

}

/// <summary>

///стратегія: Write-only (Тільки запис)

/// </summary> public int Salary

{

set {salary = value;}

}

}

Розглянемо тепер загальний синтаксис методів-властивостей. Хай name - це закрита властивість. Тоді для нього можна визначити відкритий метод-властивість (функцію), що повертає той же тип, що і поле name. Ім'я методу звичайно вибирається близьким до імені поля (наприклад, Name). Тіло властивості містить два методи - get і set, один з яких може бути опущений. Метод get повертає значення закритого поля, метод set - встановлює у момент виклику значення, використовуючи передаване йому значення, що зберігається в службовій змінній із стандартним ім'ям value. Оскільки get і set - це звичайні процедури мови, то програмно можна реалізувати скільки завгодно складні стратегії доступу. У нашому прикладі прізвище міняється, тільки якщо її значення рівне порожньому рядку і це означає, що прізвище персони жодного разу ще не задавалося. Статус персони перераховується автоматично при всякій зміні віку, явно змінювати його не можна. Ось приклад, що показує, як деякий клієнт створює і працює з полями персони:

58

public void TestPersonProps()

{

Person pers1 = new Person(); pers1.Fam = "Петров"; pers1.Age = 21;

pers1.Salary = 1000;

Console.WriteLine ("Фам={0}, возраст={1}, статус={2}", pers1.Fam, pers1.Age, pers1.Status);

pers1.Fam = "Іванов"; pers1.Age += 1; Console.WriteLine ("Фам={0}, возраст={1}, статус={2}",

pers1.Fam, pers1 .Age, pers1.Status); }//TestPersonProps

Індексатори

Властивості є окремим випадком методу класу з особливим синтаксисом. Ще одним окремим випадком є індексатор. Метод-індексатор є узагальненням методу-властивості. Він забезпечує доступ до закритого поля, що представляє масив. Об'єкти класу індексуються по цьому полю.

Синтаксично оголошення індексатора - таке ж, як і у разі властивостей, але методів get і set набувають аргументи по числу розмірності масиву, задаючого індекси елементу, значення якого читається або оновлюється. Важливим обмеженням є те, що у класу може бути тільки один індексатор і у цього індексатора стандартне ім'я this. Отже якщо серед полів класу є декілька масивів, то індексація об'єктів може бути виконана тільки по одному з них.

Приклад #1: Індексатори

using System;

public class MyArray

{

private int[] mArray;

public MyArray(int size) { mArray=new int[size]; }

public int this[int index] // An Indexor

{

get

if(index<0 || index>=mArray.Length) { return 0; } return mArray[index];

}

set

if(index<0 || index>=mArray.Length) { return; } mArray[index]=value;

}

}

}

class TestMain

{

public static int Main()

{

MyArray ar=new MyArray(20);

for(int i=-10;i<30;++i) { ar[i]= i; }

for(int i=-10;i<30;++i) Console.WriteLine("Element: " + ar[i]); }

return 0;

}

}

59

Статичні поля і методи класу

Коли конструктор класу створює новий об'єкт, то в пам'яті створюється структура даних з полями, що визначаються класом. Але не всі поля відбиваються в структурі об'єкту. У класу можуть бути поля, пов'язані не з об'єктами, а з самим класом. Ці поля оголошуються як статичні з модифікатором static. Статичні поля доступні всім методам класу. Незалежно від того, який об'єкт викликав метод, використовуються одні і ті ж статичні поля, дозволяючи методу використовувати інформацію, створену іншими об'єктами класу. Статичні поля представляють загальний інформаційний пул для всіх об'єктів класу, дозволяючи витягувати і створювати загальну інформацію. Наприклад, у класу Person може бути статичне поле message, в якому кожен об'єкт може залишити повідомлення для інших об'єктів класу.

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

Конструктори класу

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

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

Конструктор може бути оголошений з атрибутом private. Зрозуміло, що в цьому випадку зовнішній користувач не може скористатися їм для створення об'єктів. Але це можуть робити методи класу, створюючи об'єкти для власних потреб із спеціальною ініціалізацією.

У класі можна оголосити статичний конструктор з атрибутом static. Він викликається автоматично - його не потрібно викликати стандартним чином. Такий конструктор може виконувати деяку попередню роботу, яку потрібно виконати один раз, наприклад, зв'язатися з базою даних, заповнити значення статичних полів класу, створити константи класу, виконати інші подібні дії

Деструктор класу

Якщо завдання створення об'єктів повністю покладається на програміста, то завдання видалення об'єктів, після того, як вони стали не потрібними, в Visual Studio .Net знята з програміста і покладена на відповідний інструментарій - складальник сміття. У класичному варіанті мови C++ деструктор так само необхідний класу, як і конструктор. У мові C# у класу може бути деструктор, але він не займається видаленням об'єктів і не викликається нормальним чином в ході виконання програми. Так само, як і статичний конструктор, деструктор класу, якщо він є, викликається автоматично в процесі збірки сміття. Його роль - в звільненні ресурсів, наприклад, файлів, відкритих об'єктом.

Ім'я деструктора будується з імені класу з передуванням йому символа ~ (тильда). Як і у статичного конструктора, у деструктора не указується модифікатор доступу.

Спадкоємство

У об'єктно-орієнтованих системах визначено два основні типи відношень між класами. Перше відношення "клієнти і постачальники", називається часто клієнтським відношенням або відношенням вкладеності (вбудовування). Друге відношення "батьки і спадкоємці" називається

відношенням спадкоємства [30].

60