Інтерфейси

Інтерфейси



1. Призначення інтерфейсів. Особливості застосування інтерфейсів у програмах на C#

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

Використання інтерфейсів є ефективним у випадках, коли потрібно створити альтернативу множинного успадкування. Будь-який клас може успадковувати декілька інтерфейсів. При цьому всі методи успадкованих інтерфейсів повинні бути реалізовані в класі.

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

Особливості інтерфейсів

  • в інтерфейсі не можна вписувати реалізацію його елементів (членів);
  • неможливо створити екземпляр (об’єкт) інтерфейсу;
  • можна оголосити посилання на інтерфейс;
  • в інтерфейсі не може бути конструкторів;
  • інтерфейс не може містити полів;
  • в інтерфейсі не може бути реалізовано перевантаження операторів
  • усі методи інтерфейсу за замовчуванням оголошені як public.

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

  • забороняється змінювати модифікатор доступу для методу при його реалізації;
  • неможливо оголосити методи інтерфейсу з ключовим словом virtual;
  • заборонено оголошувати методи інтерфейсу з ключовим словом static (як статичні).

 

2. Яка відмінність між інтерфейсами та абстрактними класами?

У мові програмування C# між інтерфейсами та абстрактними класами існують наступні відмінності:

  1. В інтерфейсі заборонено прописувати реалізацію його членів. В абстрактному класі частина членів може мати реалізацію. Іншими словами, інтерфейс це той самий абстрактний клас, у якого всі методи абстрактні.
  2. В інтерфейсі заборонено описувати поля (змінні, об’єкти), в абстрактному класі можна.
  3. Інтерфейс не може мати конструктор. В абстрактному класі може бути оголошений конструктор.
  4. Будь-який клас може бути успадкований від декількох інтерфейсів. При цьому клас може бути успадкований тільки від одного абстрактного класу (і не більше).

 

3. Скільки класів можуть мати реалізацію методів інтерфейсу?

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

 

4. Скільки інтерфейсів може бути реалізовано в одному класі?

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

 

5. Який загальний вигляд опису інтерфейсу?

Інтерфейси оголошуються з допомогою ключового слова interface. Загальна форма опису інтерфейсу:

interface ім’я
{
    тип_повернення1 імя_методу1(параметри1);
    тип_повернення2 імя_методу2(параметри2);
    // ...
    тип_поверненняN імя_методуN(параметриN);
}

де

  • ім’я – конкретне ім’я інтерфейсу;
  • імя_методу1, імя_методу2, …, імя_методуN – імена методів інтерфейсів;
  • тип_повернення1, тип_повернення2, …, тип_поверненняN – типи, що повертаються методами інтерфейсу;
  • параметри1, параметри2, …, параметриN – списки параметрів методів інтерфейсу.

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

 

6. Які елементи мови програмування можна вказувати в інтерфейсах?

В інтерфейсах можна вказувати:

  • методи;
  • властивості;
  • індексатори;
  • події.

 



7. Як виглядає загальна форма реалізації інтерфейсу в класі?

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

class ім’я_класу : ім’я_інтерфейсу
{
    // тіло класу
    ...
}

де ім’я_інтерфейсу – ім’я інтерфейсу, що реалізується в класі. Клас обов’язково повинен реалізувати усі методи інтерфейсу.

 

8. Яка загальна форма класу, що реалізує декілька інтерфейсів?

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

Загальна форма класу, що реалізує декілька інтерфейсів:

class ім’я_класу : ім’я_інтерфейсу1, ім’я_інтерфейсу2, ..., ім’я_інтерфейсуN
{
    // тіло класу
    ...
}

де ім’я_інтерфейсу1, ім’я_інтерфейсу2, …, ім’я_інтерфейсуN – імена інтерфейсів, які повинен реалізувати клас. Клас повинен реалізувати усі методи усіх інтерфейсів.

 

9. Приклад оголошення інтерфейсу та класу, що визначає цей інтерфейс.

Цьому інтерфейсу присвоюється ім’я IMyInterface. Рекомендовано до імені інтерфейсу додавати префікс ‘I’ згідно з загальнорозповсюдженою практикою.

Інтерфейс оголошено як public.

public interface IMyInterface
{
    int MyGetInt(); // метод, що повертає число типу int
    double MyGetPi(); // метод, що повертає число Pi
    int MySquare(int x); // метод, що повертає x в квадраті
    double MySqrt(double x); // метод, що повертає корінь квадратний з x

}

У даному прикладі, в інтерфейсі оголошено опис чотирьох методів, які повинні бути реалізовані у всіх класах, що визначають ці інтерфейси. Це методи: MyGetInt(), MyGetPi(), MySquare(), MySqrt().

Приклад опису класу, що використовує цей інтерфейс.

public class MyClass : IMyInterface
{
    // модифікатор доступу public
    public int MyGetInt()
    {
        return 25;
    }

    public double MyGetPi()
    {
        return Math.PI;
    }

    public int MySquare(int x)
    {
        return (int)(x * x);
    }

    public double MySqrt(double x)
    {
        return (double)Math.Sqrt(x);
    }
}

Усі методи, що реалізуються в класі, повинні мати тип доступу public. Якщо встановити інший тип доступу (private або protected), то Visual Studio видасть наступне повідомлення:

"MyClass does not implement interface member MyFun() because it is not public."

де MyFun() – назва функції, що реалізована в класі з модифікатором доступу private або protected.

Це зв’язано з тим, що в самому інтерфейсі ці методи неявно вважаються відкритими (public). Тому їх реалізація повинна бути відкритою.

 

10. Приклад оголошення двох інтерфейсів та класу, що реалізує методи цих інтерфейсів.

У нижченаведеному прикладі оголошено два інтерфейси з іменами MyInterface та MyInterface2. Перший інтерфейс містить 4 методи. Другий інтерфейс містить 1 метод.

Також оголошено клас MyClass, що використовує ці два інтерфейси. Клас обов’язково повинен реалізувати усі методи обох інтерфейсів, тобто 5 методів.

public interface IMyInterface
{
    int MyGetInt(); // метод, що повертає число типу int
    double MyGetPi(); // метод, що повертає число Pi
    int MySquare(int x); // метод, що повертає x в квадраті
    double MySqrt(double x); // метод, що повертає корінь квадратний з x
}

public interface IMyInterface2
{
    double MySqrt2(double x); // корінь квадратний з x
}

public class MyClass : IMyInterface, IMyInterface2
{
    // методи з інтерфейсу MyInterface
    public int MyGetInt()
    {
        return 25;
    }

    public double MyGetPi()
    {
        return Math.PI;
    }

    public int MySquare(int x)
    {
        return (int)(x * x);
    }

    public double MySqrt(double x)
    {
        return (double)Math.Sqrt(x);
    }

    // метод з інтерфейсу MyInterface2
    public double MySqrt2(double x)
    {
        return (double)Math.Sqrt(x);
    }
}

 

11. Приклад використання посилання на інтерфейс для доступу до методів класу.

В C# допускається описувати посилання на інтерфейс. Якщо описати змінну-посилання на інтерфейс, то з її допомогою можна викликати методи класу, який використовує цей інтерфейс.

Приклад.

public interface IMyInterface
{
    double MyGetPi(); // метод, що повертає число Pi
}

class MyClass : IMyInterface
{
    // методи з інтерфейсу MyInterface
    public double MyGetPi()
    {
        return Math.PI;
    }
}

// виклик з деякого програмного коду
private void button1_Click(object sender, EventArgs e)
{
    MyClass mc = new MyClass(); // створення об'єкту класу mc
    IMyInterface mi; // посилання на інтерфейс
    double d;
    mi = mc; // mi посилається на об'єкт класу mc
    d = mi.MyGetPi(); // d = 3.14159265358979

    label1.Text = d.ToString();
}

У даному прикладі створюється об’єкт (екземпляр) класу MyClass з іменем mc. Потім описується посилання на інтерфейс IMyInterface з іменем mi.

Рядок

mi=mc;

приводить до того, що посилання mi вказує на об’єкт класу mc. Таким чином, через посилання mi можна мати доступ до методів класу MyClass, тому що клас MyClass реалізує методи інтерфейсу IMyInterface.

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

 

12. Яким чином в інтерфейсі описується властивість?

Властивість описується в інтерфейсі без тіла. Загальна форма оголошення інтерфейсної властивості наступна:

тип ім’я
{
    get;
    set;
}

Якщо властивість призначена тільки для читання, то використовується один тільки аксесор get.

Якщо властивість призначена для запису, то використовується тільки один аксесор set.

Приклад. Описується інтерфейс і клас. Клас повертає властивість MyPi.

public interface IMyInterface
{
    double MyGetPi(); // метод, що повертає число Pi
    // властивість, що повертає число Pi
    double MyPi
    {
        get;
    }
}

class MyClass : IMyInterface
{
    // метод
    public double MyGetPi()
    {
        return Math.PI;
    }

    // реалізація властивості в класі
    public double MyPi
    {
        get
        {
            return Math.PI;
        }
    }
}

// використання інтерфейсної властивості в обробнику події кліку на кнопці
private void button1_Click(object sender, EventArgs e)
{
    MyClass mc = new MyClass(); // створення об'єкту класу mc
    label1.Text = mc.MyPi.ToString(); // читання властивості
}

 

13. Приклад інтерфейсу в якому описується індексатор.

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

тип this[int індекс]
{
    get;
    set;
}

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

public interface IMyInterface
{
    // інтерфейсний індексатор
    double this[int index]
    {
        get;
    }
}

class MyClass : IMyInterface
{
    double[] mas = { 3, 2.9, 0.5, 7, 8.3 };

    public double this[int index]
    {
        get
        {
            return mas[index];
        }
    }
}

private void button1_Click(object sender, EventArgs e)
{
    MyClass mc = new MyClass(); // створення об'єкту класу mc
    double d;

    d = mc[2]; // d = 0.5

    label1.Text = d.ToString();
}

 

14. Які елементи програмування мови C# не можна описувати в інтерфейсах?

Інтерфейси не можуть містити:

  • члени даних;
  • конструктори;
  • деструктори;
  • операторні методи.

 

15. Як працює механізм успадкування інтерфейсів?

Інтерфейс може успадковувати інший інтерфейс. Синтаксис успадкування інтерфейсів такий самий як і в класів.

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

interface ім’я_інтерфейсу : ім’я_інтерфейсу1, ім’я_інтерфейсу2, ..., ім’я_інтерфейсуN
{
    // методи, властивості, індексатори та події інтерфейсу
    ...
}

де ім’я_інтерфейсу – ім’я інтерфейсу, який успадковує інші інтерфейси;

ім’я_інтерфейсу1, ім’я_інтерфейсу2, …, ім’я_інтерфейсуN – імена інтерфейсів-предків.

Приклад. У даному прикладі клас MyClass використовує інтерфейс, який успадковує інший інтерфейс. У класі потрібно реалізувати усі методи (властивості, індексатори, події) інтерфейсу MyInterface1 та інтерфейсу MyInterface2.

// базовий інтерфейс
interface MyInterface1
{
    void Int1_Meth();
}

// інтерфейс, що успадковує інший інтерфейс
interface MyInterface2 : MyInterface1
{
    void Int2_Meth();
}

// клас, що викликає інтерфейс MyInterface2
class MyClass : MyInterface2
{
    // реалізація методу інтерфейсу MyInterface1
    public void Int1_Meth()
    {
        // тіло методу
        // ...
        return;
    }

    // реалізація методу інтерфейсу MyInterface2
    public void Int2_Meth()
    {
        // тіло методу
        // ...
        return;
    }
}

 

16. Що таке явна реалізація члену інтерфейсу?

Якщо перед іменем методу (властивості, індексатора, події) стоїть ім’я інтерфейсу через розділювач ‘.‘, то це називається явною реалізацією члену інтерфейсу.

Приклад явної реалізації.

// базовий інтерфейс
interface MyInterface1
{
    void Method();
}

// клас, що реалізує інтерфейс MyInterface1
class MyClass : MyInterface1
{
    // явна реалізація методу інтерфейсу MyInterface1
    void MyInterface1.Method() // вказується ім'я інтерфейсу
    {
        // тіло інтерфейсного методу
        // ...
        return;
    }
}

 

17. Коли доцільно застосовувати явну реалізацію члену інтерфейсу? Приклади.

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

  • коли потрібно, щоб інтерфейсний метод був доступний за інтерфейсним посиланням, а не за об’єктом класу що реалізує даний інтерфейс. У цьому випадку інтерфейсний метод не є відкритим (public) членом класу (див. приклад 1);
  • коли в одному класі реалізовані два інтерфейси в яких методи мають однакові імена та сигнатуру (див. приклад 2).

Приклад 1. Явна реалізація інтерфейсного методу. За інтерфейсним посиланням метод є доступний, а за об’єктом класу недоступний.

// Інтерфейс
interface MyInterface1
{
    void Method();
}

// клас, що викликає інтерфейс
class MyClass : MyInterface1
{
    // явна реалізація методу інтерфейсу MyInterface1
    // модифікатор доступу має бути відсутній
    void MyInterface1.Method() // вказується ім'я інтерфейсу
    {
        // тіло методу
        // ...
        return;
    }

    void InternalMethod()
    {
        MyInterface1 mi = this; // mi - інтерфейсне посилання
        mi.Method(); // працює!

        MyClass mc = this; // mc - об'єкт класу MyClass

        // mc.Method() - неможливо викликати, метод не відкритий для об’єкту
    }
}

Приклад 2. Є два інтерфейси MyInterface1 та MyInterface2. Кожен з них має методи з однаковими іменами та сигнатурами. У даному випадку це метод Method(), який не повертає параметрів (void). З допомогою явної реалізації клас розпізнає ці методи.

// інтерфейс 1
interface MyInterface1
{
    void Method();
}

// інтерфейс 2
interface MyInterface2
{
    void Method();
}

// клас, що використовує два інтерфейси
class MyClass : MyInterface1, MyInterface2
{
    // явна реалізація - модифікатор доступу (public) має бути відсутній
    // метод з інтерфейсу MyInterface1
    void MyInterface1.Method()
    {
        // тіло методу
        // ...
        return;
    }

    // метод з інтерфейсу MyInterface2
    void MyInterface2.Method()
    {
        // тіло методу
        // ...
        return;
    }
}

 

18. В яких випадках краще використовувати інтерфейс, а в яких абстрактний клас?

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

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


Зв’язані теми