Інтерфейси

Інтерфейси




1. Для чого використовуються інтерфейси?

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

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

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

 

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

В інтерфейсі жоден з методів не повинен мати тіла (реалізації). Це означає, що в інтерфейсі жоден з методів не повинен мати ніякої реалізації.

В абстрактному класі при оголошенні методу є присутнє тіло методу (реалізація методу).

 

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. В яких випадках краще використовувати інтерфейс, а в яких абстрактний клас?

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

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


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