C# ТА ОБ’ЄКТНО-ОРІЄНТОВАНЕ ПРОГРАМУВАННЯ (OOP)

текст перекладу
Привіт! Цю статтю я написав, щоб поділитись своїми нотатками, які я вів під час вивчення курсу Об'єктно-орієнтоване програмування 2 і успішно його завершив. Можливо, я припустився деяких помилок або недоліків у написанні, тому хочу зазначити, що відкритий до критики. Моя мета — поділитись отриманими знаннями і створити корисний посібник з цієї теми.

У цій статті я розгляну тему віртуальних структур (virtual structures), пояснюючи, що це таке, як їх використовувати, а також як працюють ключові слова virtual та override. Крім того, я торкнуся поняття пізнього зв'язування (LateBinding). Також буде розглянуто типи відносин між об'єктами, такі як is-a (спадкування), has-a (володіння) і can-do (поведінка/здатність), і як ці відносини використовуються на практиці. Щодо sealed структур, я поясню концепцію блокування наслідування та вплив ключового слова sealed на методи та поведінку класів.

Я також розгляну тему абстракції (abstraction), поділюся деталями використання abstract class і interface, а також поясню, як ці структури пов'язані з конкретними класами і як створюються об'єкти з абстрактних класів. Коли дійде справа до інтерфейсів, я розгляну створення та імплементацію інтерфейсів, а також поясню такі поняття, як name hiding і explicit implementation на прикладах.

Далі ми перейдемо до делегатів (delegates), де я розповім, що це таке, як працює механізм зворотного виклику (callback), та яку роль вони відіграють у подієвому програмуванні. У розділі про події (events) я визначу структуру події, поясню, як її викликати, і розкажу, як працюють блоки add і remove для прив'язки методів до подій.

В кінці, я поділюсь знаннями про використання generic структур, таких як generic class, method, interface та struct, розгляну обмеження (constraints) для generic і як працює успадкування з generic структурами. Ці нотатки є коротким підсумком моїх занять, що допомогли мені зрозуміти курс і успішно його пройти. Сподіваюся, вони будуть корисні як тим, хто тільки починає вивчати, так і тим, хто хоче закріпити свої знання.

Віртуальні Структури

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

Віртуальні члени не обов'язково повинні бути перевизначені у всіх підкласах.

Віртуальні структури створюються за допомогою ключового слова virtual, а перевизначення — за допомогою override.

class MyClass  
{ public virtual void MyMethod()   
 {}  
}  
class MyClass2: MyClass  
{ public override void MyMethod()  
 {}  
} 

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

Типи Відносин між Об'єктами (Association-Aggregation-Composition)

Типи відносин між об'єктами: -> is-a / has-a / can-do

is-a відношення: Це повністю пов'язано з спадкуванням.

текст перекладу
У мові програмування C#, відносини "is-a" виникають у результаті успадкування між двома класами за допомогою оператора ":".

class Car  
{}  
class Renault: Car // відношення is-a  
{}

Відношення has-a: Це відношення між класом і об'єктом іншого класу, що визначає наявність володіння. Це також означає відношення композиції.

class Car  
{}  
class Renault: Car   
{ Engine engine; // відношення has-a, тобто Renault має Engine  
}  
class Engine  
{}

Відношення can-do: Оскільки інтерфейси містять підписи методів, які реалізуються класами, ці інтерфейси вказують на можливості об'єктів. Тобто відношення can-do описує поведінку/здатності об'єкта. Ці поведінки можуть бути визначені в інтерфейсах.

public interface IFlyable  
{  
 void Fly();  
}  

public class Bird : IFlyable  
{  
 public void Fly()  
 {  
 Console.WriteLine("Птах летить!");  
 }  
}

Association: Це слабке з'єднання між класами. Це досить вільне з'єднання, коли класи пов'язані, але залишаються незалежними один від одного. Тут немає відношення частини та цілого.

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

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

Sealed: Це ключове слово, яке забороняє наслідування класу. Може використовуватись тільки для класів та методів, які були переозначені за допомогою "override". У випадку успадкування, можна заблокувати подальше переозначення методів, використовуючи "sealed".

Абстракція (Abstraction)

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

Абстрактний клас (Abstract Class)

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

abstract class MyClass  
{ public void x()   
 {}  
 abstract public void y() // цей метод повинен бути переозначений у класах-наступниках  
 {}

→ Абстрактний клас — це тип класу. Тому його можна використовувати як посилання і працювати з ним на стеку. Абстрактний клас також може містити конструктори.

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

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

текст перекладу
Класи, що реалізують/імплементують абстрактні класи, називаються конкретними класами. Абстрактні класи можуть надавати наслідування між собою, УВАГА, це наслідування, а не імплементація.

Інтерфейси

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

→ Інтерфейси — це лише абстракція класів, зосереджена на підписах методів.

→ Інтерфейси — це змінні типу посилання. Як і для класів, інтерфейси можуть бути визначені в тому ж місці, де і класи.

→ У інтерфейсі можна визначити підписи методів та властивостей. Однак не можна визначати тіла методів або властивостей, а також не можна оголошувати поля.

interface IExample  
{ void amethod();  
 int bmethod(); // Оскільки передача тут є обов'язковою, доступ не визначається  
}  
class MyClass: IExample // процес імплементації  
{ public void amethod()  
 {}  
 public int bmethod()  
 {}  
}

→ Один клас може імплементувати кілька інтерфейсів одночасно. Інтерфейси можуть успадковувати один від одного.

→ Клас може наслідувати інший клас і одночасно імплементувати інтерфейс.

class MyClass: MyClass2, IA, IB

Ховання імен (Name Hiding)

Коли кілька інтерфейсів мають однакові підписи методів і ці методи реалізуються в одному класі, це називається хованням імен (Name Hiding). Якщо клас імплементує два інтерфейси з однаковими підписами, обидва інтерфейси використовуватимуть відповідний метод як свій власний.

interface IA  
{ double calculate();}  
interface IB  
{ double calculate();}  
class Calculate: IA, IB  
{ public double calculate()   
 {}  
}

Явна імплементація (Explicit Implementation)

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

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

class Calculator: IA, IB  
{ double IA.calculate()  
 {}  
 double IB.calculate()  
 {}  
}

Делегати (Delegates)

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

→Завдяки делегатам, замість зберігання значень чи екземплярів, можна зберігати змінні, параметри або властивості, які містять методи, і викликати методи через делегат для виконання операцій.

→ Делегати використовуються для налаштування поведінки класу.

Цілі делегатів

Callback → Для виклику додаткових операцій після завершення виконання методу через callback.

Event Based → Для подійного програмування за допомогою делегатів.

Функціональні параметри → Для передачі методів як параметрів іншим методам та їх виклику.

Оголошення делегата

При оголошенні делегата слід звертати увагу на підписи методів, які він буде представляти.

public delegate void YHandler(int a, string b); // делегат для методів, які не повертають значення та приймають 2 параметри
public delegate int XHandler(int c); // делегат для методів з 1 параметром, що повертають int

Представлення методу через делегат

XHandler xdelegate = new XHandler(XMethod);  
void XMethod(int z)  
{}
XHandler xdelegate = XMethod;  
void XMethod(int z)  
{}
XHandler xdelegate = () => {};  

Використання методу, що представляється через делегат

xdelegate(123); // або  
xdelegate.invoke(123);

Події

Події використовуються для відстеження, коли відбувається подія, і для реагування на неї.

→ Відстеження подій та реагування на них: натискання кнопки, збереження файлів, встановлення з'єднання з базою даних тощо.

→ Запис подій: зберігання подій, які відбулися.

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

Оголошення та використання події

class MyEventPublisher  
{ 
    public delegate void XHandler();  
    public event XHandler MyEvent;  
    public void TriggerEvent()   
    { 
        MyEvent(); // тут подія буде викликана всередині методу  
    }  
    public void X()  
    {}  
}  

MAIN  
MyEventPublisher m = new MyEventPublisher();  
m.MyEvent += X; // додано метод X до події  
m.MyEvent -= X; // видалено метод X з події

ЗАУВАЖЕННЯ

class A  
{ 
    public delegate void XHandler(); 
}  
A a = new A();  
a //--> до делегата не можна звернутися через об'єкт класу  
 // Як видно, делегати не можуть бути доступні через екземпляр класу  
A.XHandler // доступ до делегата через клас

Це закривається за допомогою відкритої структури події. Делегат дозволяє отримати доступ до події з різних місць.

Блоки Add та Remove у події

Коли метод додається до події, виконується блок add, коли метод видаляється — блок remove.

public event XHandler myEvent  
{ 
    add { xHandler += value; }  
    remove { xHandler -= value; }  
}

Generic структури

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

Які структури можуть бути generic

→class →method →interface →struct →record →delegate

Generic клас

У мові програмування C# класи, які можуть працювати з різними типами даних.

class MyClass  
{ 
    T x; 
}  

→ Параметр T є змінною. Завдяки параметру T, MyClass може представляти різні типи даних і демонструвати узагальнене поводження.

→ Поле змінюється залежно від значення, яке передається в параметр T. Якщо передано int, поле буде числовим.

class MyClass  
{ 
    public T property { get; set; }  
    public T Method() {};  
}  
MyClass m = new MyClass(); // при створенні об'єкта generic класу передається значення для параметра

Роль Generic класів у наслідуванні

Generic клас може успадковувати від будь-якого іншого класу.
текст перекладу
Наслідування класу без generic від generic класу

class GenericClass  
{}  
class NonGeneric: GenericClass  
{}
  1. Generic клас успадковує від класу без generic.
class NonGeneric  
{}  
class Generic: NonGeneric  
{}
  1. Generic клас успадковує від іншого Generic класу
class GenericClass1  
{}  
class GenericClass2: GenericClass1  
{}

Generic методи

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

→ Властивості або поля не можуть бути визначені як generic незалежно від класу.

class MyClass  
{  
    public int Method(T3, T4)  
    {}  
}  
MyClass m = new();  
m.Method(123, "Омер");  
// Призначення значення для generic методу

Обмеження в Generic структурах (Constraints)

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

New Constraints: Обмеження, які вимагають, щоб параметр типу був типом, що можна використовувати для створення об'єктів.

class MyClass where T: new();

Base Class Constraints: Обмеження на параметр типу, який має бути певного типу або підтипом цього типу.

class MyClass where T: ; 

Interface Constraints: Обмеження, що вимагають, щоб тип реалізував певний інтерфейс.

class MyClass where T: <назва інтерфейсу>;

Enum Constraints: Обмеження, що вимагають, щоб тип був типом Enum.

class MyClass where T: Enum;

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

Перекладено з: C# İLE NESNE YÖNELİMLİ PROGRAMLAMA(OOP)

Leave a Reply

Your email address will not be published. Required fields are marked *