Рубріки: Теория

Зачем в C# нужны интерфейсы и как их использовать?

Ольга Змерзла

Interface в C# — это языковая конструкция, похожая с точки зрения синтаксиса на class, но фундаментально отличающаяся от него. Интерфейс можно сравнить с абстрактным классом, у которого только абстрактный метод, то есть нет никакой его реализации. У него есть функция множественного наследования (когда один класс наследуется сразу от нескольких).

Содержание статьи:

1. Вступление: знакомство с интерфейсами

2. Что определяет interface in C#?

3. Модификаторы доступа интерфейсов

4. Interface in C# и его применение

5. Interface implementation: как реализуются интерфейсы в C# по умолчанию

6. Множественная реализация интерфейсов

7. Интерфейсы в преобразованиях типов

8. В заключение

Вступление: знакомство с интерфейсами

Главная задача интерфейса — определить поведение, которое впоследствии будет реализовано в каком-то конкретном классе. У класса есть возможность поддерживать неограниченное количество интерфейсов, а значит и иметь множество поведений.

Поскольку в интерфейсах не заложена реализация, в них указывается только то, что необходимо сделать, но нет данных о том, как это сделать. Определяются свойства, функционал и методы без их реализации.

Давайте посмотрим на синтаксис:

public interface ITaxCalculator
{
int Calculate();
}

Так мы можем вызвать interface вместо class.

Обратите внимание на то, что для объектов интерфейсов в C# чаще всего используется вначале буква I, но это требование не обязательно.

Интерфейсы используются для создания приложений, компоненты которых не связаны между собой.

Что определяет interface in C#?

  • События.
  • Свойства (interface property в C#).
  • Методы.
  • Константы.
  • Индексаторы.

Важно: интерфейс не может содержать поля свойства, реализуемые автоматически.

Модификаторы доступа интерфейсов

Члены классов имеют свои модификаторы доступа. Они определяют контекст применения метода или использования переменной.

Уровень доступа интерфейсов по умолчанию internal (только в рамках проекта). Чтобы сделать его общедоступным, применяется модификатор public — доступен из любого места в коде и даже из других программ:

public interface IMovable
{
    void Move();
}

Полная противоположность public — модификатор private. Это закрытый класс, который может быть доступен из кода в одном и том же классе/контексте. Предоставляет доступ с минимальными правами:

class Employee
{
    private int i;
    double d;   // private access by default
}

Модификатор protected доступен из любого места в классе, а также в производных классах из других сборок:

class A
{
    protected int x = 123;
}

class B : A
{
    static void Main()
    {
        var a = new A();
        var b = new B();

        // Error CS1540, because x can only be accessed by
        // classes derived from A.
        // a.x = 10;

        // OK, because this class derives from A.
        b.x = 10;
    }
}

Сравнительная таблица модификаторов

Вызывающий объект Internal Public Private Protected
В Class да да да да
Производный класс — в одной сборке да да нет да
Непроизводный класс — в одной сборке да да нет нет
Производный класс — в другой сборке нет да нет да
Непроизводный класс — в другой сборке нет да нет нет

Существуют также такие модификаторы, как protected internal и private protected. Первый представляет собой сочетание сразу двух одноименных модификаторов. Доступен из одной сборки и производных классов. А второй — сочетает модификаторы private / protected и доступен из любого места в текущем и производных классах одной сборки.

Interface in C# и его применение

Объекты интерфейсов нельзя создать с помощью конструктора. Интерфейсы C# реализовываются в классах и структурах.

Например:

using System;
 
namespace HelloApp
{
    interface IMovable
    {
        void Move();
    }
    class Person : IMovable
    {
        public void Move()
        {
            Console.WriteLine("Буря мглою");
        }
    }
    struct Car : IMovable
    {
        public void Move()
        {
            Console.WriteLine("Небо кроет");
        }
    }
    class Program
    {
        static void Action(IMovable movable)
        {
            movable.Move();
        }
        static void Main(string[] args)
        {
            Person person = new Person();
            Car car = new Car();
            Action(person);
            Action(car);
            Console.Read();
        }
    }
}

В программе есть метод Action(), а принятый им объект интерфейса — IMovable. При написании кода ничего об объекте неизвестно, но мы знаем, что метод Move будет им обязательно реализован. Поэтому мы можем вызвать этот метод.

Внимание! Если в свойствах и методах интерфейса отсутствуют модификаторы доступа, по умолчанию они являются public.

Вывод программы в консоли:

Буря мглою
Небо кроет

What is interface in С#? По сути, интерфейс — это залог того, что определенный тип обязательно реализует какой-то конкретный функционал.

Interface implementation: как реализуются интерфейсы в C# по умолчанию

Что мы знали раньше об интерфейсах?

  • Они содержат только методы, свойства, события, индексаторы.
  • Они не содержат полей.
  • По умолчанию они public и abstract.
  • Не включают защищенные, внутренние и частные члены.

Но начиная с версии C# 8.0 методы и свойства могут реализовываться в интерфейсах по умолчанию.

Допустим, что интерфейс в приложении реализуется классами. Чтобы добавить новый член, вам нужно обновить все классы для его поддержки или создать расширенный интерфейс с новым методом, который будет унаследован от предыдущего:

interface ICar
{
    void GetSpeed();
    void GetMileage();
}
 
interface IInternetCar : ICar
{
    void SendCommand();
}

Но чем больше методов и функций в расширенном интерфейсе, тем все быстрее он становится неуправляемым. Эту проблему решают  интерфейсы, реализуемые по умолчанию.

Они полезны при добавлении новых членов, не нарушая существующую реализацию:

interface ICar
{
    void GetSpeed();
    void GetMileage();
    public void SendCommand() {
      //Send the Command.
    }
}

С interface implementation в C# нет никакой необходимости в применении новых интерфейсов и изменениях в существующей реализации.

Классы могут переопределить реализацию по умолчанию.

Например, если класс MorrisGarage реализует метод SendCommand, будет вызвана его реализация:

public class MorrisGarage : ICar
{
    public void GetMileage()
    {
        Console.WriteLine("10 KM Per Liter");
    }
 
    public void GetSpeed()
    {
        Console.WriteLine("200 KMPH");
    }
 
    public void SendCommand()
    {
        Console.WriteLine("Command Sent via Morris Garage Class");
    }
}

Интерфейсы в C# могут:

  • реализовывать методы по умолчанию и иметь private-члены:
public interface ICar
{
    private void Initialize()
    {
        //Initialize the Command Center
    }
    void GetSpeed();
    void GetMileage();
    public void SendCommand()
    {
        Initialize();
        Console.WriteLine("Command Sent via Interface");
    }  
}
  • реализовывать в С# interface static method — статические поля и члены:
public interface ICar
{
    private static string commandName = "LOCK_CAR";
    private void Initialize(string cName)
    {
        commandName = cName;
        //Initialize the Command Center
    }
    void GetSpeed();
    void GetMileage();
    public void SendCommand(string cName)
    {
        Initialize(cName);
        Console.WriteLine("Command Sent via Interface");
    }
}
  • иметь protected-члены, которые доступны в производном интерфейсе, но не в производном классе:
public interface ICar
{    
    public void SendCommand()
    {        
        Console.WriteLine("Command Sent via Interface");
    }
    protected void SendCriticalCommand()
    {
        Console.WriteLine("Critical Command Sent via Interface");
    }       
}
 
public interface IAnotherCar : ICar
{
    public void Send(bool bCritical)
    {
        if (bCritical)
            this.SendCriticalCommand();
        else
  • иметь virtual-члены (без возможности переопределения метода классом):
public interface ICar
{
    public virtual void SendCommand()
    {        
        Console.WriteLine("Command Sent via Interface");
    }
}
 
public interface IAnotherCar :ICar
{
    void ICar.SendCommand()
    {
        Console.WriteLine("Command Sent via another Interface");
    }
}
 
class MorrisGarage: ICar, IAnotherCar
{
     
}
 
class Program
{
   static void Main()
   {
      ICar mg= new MorrisGarage();
      mg.SendCommand(); //Calls the virtual implementation.
 
      IAnotherCar mgOverridden = new MorrisGarage();
      mgOverridden.SendCommand(); //Calls the overridden implementation.
  }
}

Реализация интерфейсов по умолчанию (interface implementation в С#) позволяет разработчикам не нарушать существующую реализацию, но при этом вносить новые изменения.

Множественная реализация интерфейсов

В C# нет поддержки множественного наследования — унаследовать класс можно только от одного класса. Благодаря тому, что в C# класс может реализовать сразу несколько интерфейсов (implement interface в C#), ограничение с невозможностью множественного наследования можно обойти.

Все интерфейсы в class обычно указываются через запятую.

Например:

using System;
 
namespace HelloApp
{
    interface IAccount
    {
        int CurrentSum { get; }  // Текущая сумма на счету
        void Put(int sum);      // Положить деньги на счет
        void Withdraw(int sum); // Взять со счета
    }
    interface IClient
    {
        string Name { get; set; }
    }
    class Client : IAccount, IClient
    {
        int _sum; // Переменная для хранения суммы
        public string Name { get; set; }
        public Client(string name, int sum)
        {
            Name = name;
            _sum = sum;
        }
 
        public int CurrentSum { get { return _sum; } }
 
        public void Put(int sum) {  _sum += sum; }
 
        public void Withdraw(int sum)
        {
            if (_sum >= sum)
            {
                _sum -= sum;
            }
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Client client = new Client("Tom", 200);
            client.Put(30);
            Console.WriteLine(client.CurrentSum); //230
            client.Withdraw(100);
            Console.WriteLine(client.CurrentSum); //130
            Console.Read();
        }
    }
}

Здесь реализованы интерфейсы IAccount (определяет свойство CurrentSum и методы Put — поступление суммы на счет, Withdraw — изъятие суммы) и IClient (определяет свойство Name).

Client class реализует оба интерфейса, после чего применяется в программе.

Интерфейсы в преобразованиях типов

Как видно из примера в разделе «Множественная реализация интерфейсов», IAccount-интерфейс реализуется классом Client. Ссылка на объект Client может храниться в переменной IAccount:

// Все объекты Client являются объектами IAccount
IAccount account = new Client("Том", 200);
account.Put(200);
Console.WriteLine(account.CurrentSum); // 400
// Не все объекты IAccount являются объектами Client, необходимо явное приведение
Client client = (Client)account;
// IAccount интерфейс не имеет свойства Name, необходимо явное приведение
string clientName = ((Client)account).Name;

Преобразование от класса к интерфейсу выполняется автоматически и аналогично преобразованию от производного типа к базовому классу, поскольку любой объект Client может реализовать IAccount-интерфейс.

Преобразование от интерфейса к классу идентично превращению базового типа в производный, поскольку не каждый объект IAccount является объектом Client. Здесь нужна операция преобразования типов.

В заключение

Интерфейс определяет методы, реализуемые в классе, который в C# наследует этот generic interface.

Интерфейсы используются для того, чтобы направить классы на реализацию методов: событий, индексаторов, свойств. В interface в C# описываются функциональные возможности, но без их реализации (он определяет только спецификацию).

Интерфейс — альтернатива множественному наследованию.

Interface в C# обладает такими особенностями:

  • не содержит поля;
  • не использует конструкторы;
  • в нем не осуществляется перегрузка операторов;
  • методы по умолчанию имеют статус public;
  • нет реализации элементов интерфейса;
  • нет возможности создать экземпляра интерфейса.

Интерфейс может быть реализован в любом количестве классов, а в одном классе может быть реализовано любое количество интерфейсов.

Для объявления интерфейса нужно применить ключевое слово interface.

Интерфейсы могут наследовать другие интерфейсы. Синтаксис такого наследования такой же, как синтаксис наследования классов. Например:

interface Name : name_interface1, name_interface2, ..., name_interfaceN
{
     // указываются события, свойства, методы, индексаторы интерфейса
     ...
}

где:

  • Name — имя интерфейса, который наследует другие интерфейсы;
  • name_interface1, name_interface2, ..., name_interfaceN — интерфейсы-предки (все они указываются через запятую).

Interface в C# лучше всего использовать в случаях, когда все понятия нужно описать с точки зрения их функционального назначения, они пригодятся, когда не нужны детали реализации свойств и методов.

Видео: интерфейсы в C#: зачем нужны и как используются

Останні статті

Обучение Power BI – какие онлайн курсы аналитики выбрать

Сегодня мы поговорим о том, как выбрать лучшие курсы Power BI в Украине, особенно для…

13.01.2024

Work.ua назвал самые конкурентные вакансии в IТ за 2023 год

В 2023 году во всех крупнейших регионах конкуренция за вакансию выросла на 5–12%. Не исключением…

08.12.2023

Украинская IT-рекрутерка создала бесплатный трекер поиска работы

Unicorn Hunter/Talent Manager Лина Калиш создала бесплатный трекер поиска работы в Notion, систематизирующий все этапы…

07.12.2023

Mate academy отправит работников в 10-дневный оплачиваемый отпуск

Edtech-стартап Mate academy принял решение отправить своих работников в десятидневный отпуск – с 25 декабря…

07.12.2023

Переписки, фото, история браузера: киевский программист зарабатывал на шпионаже

Служба безопасности Украины задержала в Киеве 46-летнего программиста, который за деньги устанавливал шпионские программы и…

07.12.2023

Как вырасти до сеньйора? Девелопер создал популярную подборку на Github

IT-специалист Джордан Катлер создал и выложил на Github подборку разнообразных ресурсов, которые помогут достичь уровня…

07.12.2023