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

Последнее обновление: 01.01.2022

Кроме неявного применения интерфейсов, которое было рассмотрено в прошлой статье, существует также явная реализация интерфейса. При явной реализации указывается название метода или свойства вместе с названием интерфейса. При этом при реализации мы не можем использовать какие-либо модификаторы:

interface IAction
{
	void Move();
}
class BaseAction : IAction
{
	void IAction.Move() => Console.WriteLine("Move in Base Class");
}

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

BaseAction baseAction1 = new BaseAction();

// baseAction1.Move();  // ! Ошибка - в BaseAction нет метода Move
// необходимо приведение к типу IAction
// небезопасное приведение
((IAction)baseAction1).Move();   
// безопасное приведение 
if (baseAction1 is IAction action) action.Move();
// или так
IAction baseAction2 = new BaseAction();
baseAction2.Move();

В какой ситуации может действительно понадобиться явная реализация интерфейса? Например, когда класс применяет несколько интерфейсов, но они имеют один и тот же метод с одним и тем же возвращаемым результатом и одним и тем же набором параметров:

class Person : ISchool, IUniversity
{
    public void Study() => Console.WriteLine("Учеба в школе или в университете");
}
interface ISchool
{
    void Study();
}
interface IUniversity
{
    void Study();
}

Класс Person определяет один метод Study(), создавая одну общую реализацию для обоих примененных интерфейсов. И вне зависимости от того, будем ли мы рассматривать объект Person как объект типа ISchool или IUniversity, результат метода будет один и тот же.

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

class Person : ISchool, IUniversity
{
    void ISchool.Study() => Console.WriteLine("Учеба в школе");
    void IUniversity.Study() => Console.WriteLine("Учеба в университете");
}

Использование:

Person person = new Person();

((ISchool)person).Study();
((IUniversity)person).Study();

Другая ситуация, когда в базовом классе уже реализован интерфейс, но необходимо в производном классе по-своему реализовать интерфейс:

interface IAction
{
    void Move();
}
class BaseAction : IAction
{
    public void Move() =>Console.WriteLine("Move in BaseAction");
}
class HeroAction : BaseAction, IAction
{
    void IAction.Move() => Console.WriteLine("Move in HeroAction");
}

Несмотря на то, что базовый класс BaseAction уже реализовал интерфейс IAction, но производный класс по-своему реализует его. Применение классов:

HeroAction action1 = new HeroAction();
action1.Move();            // Move in BaseAction
((IAction)action1).Move(); // Move in HeroAction

IAction action2 = new HeroAction();
action2.Move();             // Move in HeroAction

Модификаторы доступа

Члены интерфейса могут иметь разные модификаторы доступа. Если модификатор доступа не public, а какой-то другой, то при реализации метода, свойства или события интерфейса в классах и структурах мы можем применять два способа. Во-первых, можно также использовать явную реализацию интерфейса, реализовав все необходимые интерфейса без модификаторов доступа:

IMovable tom = new Person("Tom");
// подписываемся на событие
tom.MoveEvent += () => Console.WriteLine($"{tom.Name} is moving");
tom.Move();

delegate void MoveHandler();    // делегат перемещения
interface IMovable
{
    protected internal void Move();
    protected internal string Name { get;}
    protected internal event MoveHandler MoveEvent;
}
class Person : IMovable
{
    string name;
    // явная реализация события - дополнительно создается переменная
    MoveHandler? moveEvent;
    event MoveHandler IMovable.MoveEvent
    {
        add => moveEvent += value;
        remove => moveEvent -= value;
    }
    // явная реализация свойства - в виде автосвойства
    string IMovable.Name { get => name; }
    public Person(string name) => this.name = name;
    // явная реализация метода
    void IMovable.Move()
    {
        Console.WriteLine($"{name} is walking");
        moveEvent?.Invoke();
    }
}

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

Альтернативный вариант представляет неявная реализация с модификатором public

Person tom = new Person("Tom");
// подписываемся на событие
tom.MoveEvent += () => Console.WriteLine($"{tom.Name} is moving");
tom.Move();

delegate void MoveHandler();

interface IMovable
{
    protected internal void Move();
    protected internal string Name { get;}
    protected internal event MoveHandler MoveEvent;
}
class Person : IMovable
{
    string name;
    // явная реализация события - дополнительно создается переменная
    MoveHandler? moveEvent;
    // неявная реализация события с модификатором public
    public event MoveHandler MoveEvent
    {
        add => moveEvent += value;
        remove => moveEvent -= value;
    }
    // неявная реализация свойства - в виде автосвойства, но с модификатором public
    public string Name { get => name; }
    public Person(string name) => this.name = name;
    // неявная реализация метода, но с модификатором public 
    public void Move()
    {
        Console.WriteLine($"{name} is walking");
        moveEvent?.Invoke();
    }
}

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

Дополнительные материалы
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850