Кроме неявного применения интерфейсов, которое было рассмотрено в прошлой статье, существует также явная реализация интерфейса. При явной реализации указывается название метода или свойства вместе с названием интерфейса. При этом при реализации мы не можем использовать какие-либо модификаторы:
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(); } }
В этом случае к методам, свойствам и событиям интейрфейса можно обращаться через переменную класса.