Явная реализация интерфейсов
Кроме неявного применения интерфейсов, которое было рассмотрено в прошлой статье, сушествует также явная реализация интерфейса. При явной реализации указывается название метода или свойства вместе с названием интерфейса, при этом мы не можем использовать модификатор public, то есть методы являются закрытыми:
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();
interface IMovable
{
protected internal void Move();
protected internal string Name { get;}
delegate void MoveHandler();
protected internal event MoveHandler MoveEvent;
}
class Person : IMovable
{
string name;
// явная реализация события - дополнительно создается переменная
IMovable.MoveHandler? moveEvent;
event IMovable.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();
}
}
В данном случае опять же надо учитывать, что напрямую мы можем обратиться к подобным методам, свойствам и событиям через переменную интерфейса, но не переменную класса.