Попередня сторінка          Зміст           Наступна сторінка          Електронні посібники ВНТУ

 

ЛАБОРАТОРНА РОБОТА № 6

КЛАСИ

 

 

Мета: навчитися створювати та працювати з класами.

 

1.1 Теоретичні відомості

 

6.1.1 Створення класів та обєктів

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

У C# ви можете визначити власний тип створивши клас. Як програмна конструкція, класи в C# є основною частиною об’єктно-орієнтованого програмування. Створюючи клас, ви визначаєте структурудля типу. Клас визначає характеристики та поведінку кожної сутності в логічний та розширювальний спосіб. Ви реалізуєте поведінку визначаючи методи, поля, властивості та події усвоєму класі.

Припустимо ви створили клас DrinksMachine.

Ви використали ключове слово class для оголошення класу, як показано в прикладі:

public class DrinksMachine

{

    //методи, поля, властивості та події

}

Ключове слово class йде після будь-якого модифікатору доступу. В даному прикладі використовується модифікатору доступу public.

Додавання полей до класів

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

У свому класі ви можете додавати методи, поля, властивості та події що визначають поведінку та характеристики вашого типу так як показано в прикладі:

public class DrinksMachine

{

  // Приватне поле

  private string _location;

 

  // Конструктор

  public DrinksMachine(string model, string make)

  {

    Model = model;

    Make = make;

  }

 

  // Властивість

  public string Location

  {

    get { return _location; }

    set

    {

      if (value != null)

        _location = value;

    }

  }

 

  // Властивості

  public string Make { get;  }

  public string Model { get;  }

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

  public void MakeCappuccino()

  {

    // Код методу

  }

 

  public void MakeEspresso()

  {

    // Код методу

  }

 

  // Оголошення події

  public event OutOfBeansHandler OutOfBeans;

}

 

6.1.2 Часткові класи

C# надає можливість реалізації часткових класів. Часткові класи надають можливість розділяти реалізацію класу в декількох файлах, які при компіляції "поєднаються" в один загальний файл.

Часткові класи корисні в таких випадках:

  • Робота над великим проектом. Розділення класу між декількома файлами надає можливість декільком програмістам працювати над одним класомодночасно.
  • Під час роботи з автоматично згенерованим кодом. Visual Studio використовує даний підхід під час роботи з Windows Forms, Web сервісами та іншими. Microsoft рекомендує незмінювати автозгенерований код для даних компонентів, бовони можуть змінитися під часкомпіляціх чи зміни проекту. Як рішення, ви можете створити інший клас як частковий з таким же іменем та реалізувати свої зміни там.

Приклад частково класу:

public partial class DrinksMachine

{

  public void MakeCappuccino()

  {

    // Логіка методу тут

  }

}

 

public partial class DrinksMachine

{

  public void MakeEspresso()

  {

    // Логіка методу тут

  }

}

Додаток: ви також можете розділяти структури та інтерфейси між декількома файлами.

 

6.1.3 Ініціалізація класу

Клас – це тільки опис структури для типу. Для використання поведінки та характеристик, які ви визначили в класі, потрібно створити об’єкт даного класу.

Для створення об’єкту класу, використовуйте ключове слово newтак як в прикладі:

// Створення об'єкта

DrinksMachine dm = new DrinksMachine();

При ініціалізації класу в такий спосіб відбуваються наступні речі:

  • Ви створюєте новий об’єкт типу DrinksMachine в пам’яті.
  • Ви створюєте вказівник (змінну) з іменем dm який посилається на новий об’єкт DrinsMachine.

При створенні змінної замість явного визначення типу DrinksMachine ви можете надати можливість компілятору визначити тип під час компіляції. Даний спосіб називається автоматичним визначенням типу. Для використання способувизначення типу, ви створюєте об’єкт за допомогою з ключовим словом var так як в прикладі:

// Створення об'єкт

var dm = new DrinksMachine();

В даному випадку компілятор не знає точного типу змінної dm. Коли змінна ініціалізується як посилання на об’єкт DrinksMachine компілятор присвоює їй тип DrinksMachine. Використання визначення типу в такий спосіб не викликає змінв роботі програми, це використовується для уникнення повторень імен класів.

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

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

var dm = new DrinksMachine("Beancrusher 3000", "Fourth Coffee");

 

Console.WriteLine(dm.Model);

 

dm.Location = "Somewhere near Kyiv";

 

dm.MakeEspresso();

 

6.1.4 Інкапсуляція в C#

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

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

PrivatePublicProtected та Internal

Подана таблиця Е.1 (додаток Е) відображає модифікатори доступу які можуть бути застосовані до членів класу та як вони контролюють доступ іншого коду програми.

Властивості

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

Нижче подано приклад реалізації властивостей для класу DrinksMachine:

public class DrinksMachine

{

private int age;

private string make;


public int Age

{

get { return age; }

set { age = value; }

}


public string Make

{

get { return make; }

set { make = value; }

}


public string Model { get; set; }


public DrinksMachine(int age)

{

this.Age = age;

}


public DrinksMachine(string make, string model)

{

this.Make = make;

this.Model = model;

}

 

public DrinksMachine(int age, string make, string model)

{

this.Age = age;

this.Make = make;

this.Model = model;

}

}

Так само як і в структурах можливо написати властивості лише для зчитування, лише для запису та автоматично згенеровані властивості.

 

6.1.5 Використання конструкторів

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

DrinksMachine dm = new DrinksMachine();

Синтаксис схожий на виклик методу. Це тому, що коли ви ініціалізуєте клас, ви викликаєте спеціальний метод, який називається конструктором. Конструктор – метод класу, який має те саме ім’я, що і самклас. Конструктор не повертає значення(навіть не void).

Конструктор часто використовується для присвоєння спеціальних чи стандартних значень членам нового об’єкту, приклад:

public class DrinksMachine

{

public int Age { get; set; }


public DrinksMachine()

{

Age = 0;

}

}

Конструктор без параметрів називається конструктором за замовчуванням. Цей конструктор викликається коли відбувається ініціалізація класу без аргументів. Якщо ви не реалізуєте конструктор увашому класі, C# компілятор автоматично створить порожній public конструктор для вашого скомпільованого класу.

В багатьох випадках буває корисно для користувача вашого класу мати можливість встановлювати спеціальні значення членів класу, коли він ініціалізується. Для прикладу, хтось хоче створити новийоб’єкт класу DrinksMachine задавши одночасно виробника та модель автомата. Ваш клас може містити декілька конструкторів з різними комбінаціями параметрів для ініціалізації. Даний спосіб називається перевантаженням.

Приклад декількох конструкторів класу:

public class DrinksMachine

{

public int Age { get; set; }

public string Make { get; set; }

public string Model { get; set; }

 

public DrinksMachine(int age)

{

this.Age = age;

}

 

public DrinksMachine(string make, string model)

{

this.Make = make;

this.Model = model;

}

 

public DrinksMachine(int age, string make, string model)

{

this.Age = age;

this.Make = make;

this.Model = model;

}

}

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

var dm1 = new DrinksMachine(2);

var dm2 = new DrinksMachine("Fourth Coffee", "BeanCrusher 3000");

var dm3 = new DrinksMachine(3, "Fourth Coffee", "BeanToaster Turbo");

 

6.1.6 Створення статичних класів та властивостей

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

В таких випадках ви можете створити статичний клас. Статичний клас – це клас який не може бути ініціалізований. Для створення статичного класу треба використати ключове слово static. Будь-які члени класу також повинні використовувати ключове слово static. Приклад:

public static class Conversions

{

public static double PoundsToKilos(double pounds)

{

// фунти в кілограми

double kilos = pounds * 0.4536;

return kilos;

}

 

public static double KilosToPounds(double kilos)

{

// кілограми в фунти

double pounds = kilos * 2.205;

return pounds;

}

}

Для виклику методу статичного класу, ви викликаєте метод від імені класу замість імені об’єкту класу. Приклад:

double weightInKilos = 80;

double weightInPounds = Conversions.KilosToPounds(weightInKilos);

Статичні члени класу

Не статичні класи можуть містити статичні члени. Це корисно коли певна поведінка чи властивість залежить від об’єкту, а певна від самого класу. Методи, поля, властивості та події можуть бутиоголошені статичними. Статичні властивості часто використовуються для повернення значень властивих всім об’єктам класу чи відслідковування скільки об’єктів класу було створено.Статичні методи часто використовуються в методах, які пов’язані з класом, наприклад, функція порівняння.

Для оголошення статичного члена класу використовується ключове слово static:

public class DrinksMachine

{

public int Age { get; set; }

public string Make { get; set; }

public string Model { get; set; }

 

public static int CountDrinksMachines()

{

// логіка методу тут

}

}

Незалежно від кількості об’єктів вашого класу, існує тільки один екземпляр статичного члена. Непотрібно ініціалізовувати об'єкт класу для використання статичного методу. Можна отримати доступ використовуючи ім’я класу замість імені змінної. Приклад:

int drinksMachineCount = DrinksMachine.CountDrinksMachines();

 

6.1.7 Анонімні класи

Як вже, мабуть, ви здогадались анонімні класи – це класи, що не мають явно вказаної назви. Анонімні класи дають змогу створити об’єкт без потреби спочатку визначити тип. Назву цього класу буде автоматично згенеровано компілятором.

Ось як можна створити об’єкт анонімного класу:

var anAnonymousObject = new { Name = "Tom", Age = 65 };

Цей клас буде мати два публічних поля Name (зі значенням “Tom”) та Age (зі значенням 65).

Після створення ви можете працювати з цим об’єктом так само, як з об’єктом не анонімного класу:

Console.WriteLine("Name: {0} Age: {1}", anAnonymousObject.Name, anAnonymousObject.Age;

Після створення анонімного класу ви можете використовувати його, щоб створювати інші об’єкти цього класу:

var secondAnonymousObject = new { Name = "Hal", Age = 46 };

(Компілятор С# дивиться на назви, тип, кількість та порядок полів в оголошенні анонімного класу, щоб зрозуміти чи ці класи є однаковими.)

Зауваження: Існує декілька обмежень при роботі з анонімними класами:

  • Анонімні класи можуть мати лише публічні поля.
  • Всі поля повинні бути ініціалізованими(присвоєне значення).
  • Поля не можуть бути статичними.
  • Ви не можете використовувати оголошення методів всередині анонімних класів.

 

6.1.8 Успадкування(наслідування). Класи, що не наслідуються (Sealed класи)

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

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

Наслідування дозволяє створити базовий клас, що містить основні параметри і кожен клас, що буде наслідувати базовий, буде мати(успадкує) ці параметри і, можливо, якісь інші. Клас, що наслідує базовий, зазвичай називають дочірнім(підкласом), а сам базовий – батьківським(надкласом

Наш приклад можна зобразити за допомогою наступної діаграми на рис.Е.1 (Додаток Е).

Застосування наслідування (Inheritance)

У C# не підтримується напряму множинне наслідування (успадкування). Множинне наслідування – це концепт, в результаті якого кілька базових класів можуть бути успадковані одним підкласом.

Щоб зробити успадкування від базового класу в C# ви додаєте після ім'я похідного класу двокрапку та ім'я базового класу. Наступний приклад демонструє клас Manager, який наслідує клас Employee.

class Manager : Employee

{

private char payRateIndicator;

private Employee[] emps;

}

Для простого визначення класу в C# використовується ключове слово class, після якого ми пишемо ім'я класу Manager, двокрапку і після цього ім'я базового класу Employee. Дивлячись на код вище ми не можемо сказати, що клас Managerуспадкував від Employee , тому нам потрібно буде подивитись в цей клас(Employee) теж, щоб зрозуміти, які властивості для нас є доступні. Клас Employee:

class Employee

{

private string empNumber;

private string firstName;

private string lastName;

private string address;

 

public string EmpNumber

{

get

{

return empNumber;

}

set

{

empNumber = value;

}

}

 

public string FirstName

{

get

{

return firstName;

}

 

set

{

firstName = value;

}

}

 

public string LastName

{

get

{

return lastName;

}

 

set

{

lastName = value;

}

}

 

public string Address

{

get

{

return address;

}

 

set

{

address = value;

}

}

}

Intellisense може дати вам можливість візуального представлення успадкованих полів. Наприклад, коли ми задаємо екземпляр типу Manager в нашому коді і після цього ставимо крапку, ми побачимо список властивостей класу Manager, а також властивості з базового класу Employee., що демонструється на рис. Е.2 (Додаток Е).

Ми вже обговорили успадкування для класів і показали як базовий клас може бути успадкований підкласом і ми обговорили абстрактні класи. Обидва концепти фокусуються на можливості класу бути успадкованим і надавати атрибути та поведінку іншим класам для того, щоб ми могли не писати новий, а використовувати уже написаний код. Але що трапиться, якщо ви вирішите, що ви не хочете, щоб ваш клас не був успадкований? Ми це можемо зробити дуже просто за допомогою створення sealed класу. Ви можете використати ключове слово sealed у вашому класі для того, щоб заборонити можливість успадковування для вашого класу. Якщо клас спробує зробити наслідування від sealed класу, то компілятор покаже помилку.

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

 

6.1.9 Абстрактні класи

Пригадаємо попередню тему – наслідування. Ми помітили, що клас Employee є базовим класом для Manager та Programmer. Ми можемо продовжувати розширювати клас Employee, створюючи стільки підкласів скільки вимагається для різних типів працівників у нашому додатку. Водночас зрозуміло, що немає сенсу мати можливість створювати об'єкт класу Employee напряму, адже абсурдно мати працівника без обов'язків.

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

Employee newEmployee = new Employee();

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

abstract class Employee

{

private string empNumber;

private string firstName;

private string lastName;

private string address;

 

.....

 

public virtual void Login()

{

}

 

public virtual void LogOff()

{

}

 

public abstract void EatLunch();

}

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

Також помітьте, що ми додали ключове слово abstract до нашого класу: abstract class Employee. Саме ця дія робить наш клас абстрактним і встановлює певні вимоги. Як тільки ви створили абстрактний клас, ви можете вирішити і задати методи, які необхідно буде задавати у підкласах і які методи можна буде задавати чи переписати в підкласах.

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

Помітьте, що у попередньому прикладі коду Login() та LogOff() обидва зазначені зі словом virtual. Це означає, що ви можете задати імплементацію в абстрактному класі, але у під класі ви можете переписати реалізацію цього методу або, якщо вона вас влаштовує в абстрактному класі, залишити поточну реалізацію.

Метод EatLunch() заданий з ключовим словом abstract, як клас. Є певні обмеження щодо таких методів:

  • Абстрактний метод не може існувати не в абстрактному класі.
  • Абстрактний метод не може мати жодної імплементації, включаючи пусті фігурні дужки.
  • Сигнатура абстрактного методу повинна закінчуватись крапкою з комою.
  • Абстрактний метод повинен бути реалізований в будь-якому підкласі, в інакшому разі буде згенеровано попередження компілятора в C#.

 

6.2 Завдання до лабораторної роботи (Додаток Е)

 

6.3 Контрольні запитання:

 

  1. Що таке клас та об’єкт класу?
  2. Яка відмінність між описом класу та об’єктом класу?
  3. Що оголошується в класі?
  4. Що називається полями класу?
  5. Які елементи мови C# можуть відноситись до функцій-членів класу?
  6. Яка загальна форма опису класу?
  7. Які є типи (модифікатори) доступу до членів класу?
  8. Які можливості доступу дає використання ідентифікатору protected internal?
  9. Чи може бути відсутній тип (модифікатор) доступу при описі членів класу?
  10. Приклади опису найпростіших класів, що містять тільки дані.
  11. Як створити об’єкт (екземпляр класу)?
  12. До яких типів даних належать класи: до типів значення чи до посилальних типів?
  13. Що таке конструктори? Які відмінності між методами класу і конструкторами?
  14. Яка загальна форма конструктора?
  15. Скільки конструкторів може мати клас?
  16. Що таке конструктор за замовчуванням?
  17. Приклад ініціалізації класу з допомогою конструкторів.
  18. Що означає “збір сміття” в C#?
  19. Для чого призначені деструктори?
  20. Які особливості використання ключового слова this в класі?