[Из песочницы] Новые возможности интерфейсов в C# 8
Для начала посмотрим на определение интерфейса у Эндрю Троелсена и Филиппа Джепикса: «Интерфейс представляет собой всего лишь именованный набор абстрактных членов. Абстрактные методы являются чистым протоколом, поскольку они не предоставляют свои стандартные реализации. Специфичные члены, определяемые интерфейсом, зависят от того, какое точно поведение он моделирует. Другими словами, интерфейс выражает поведение, которое заданный класс или структура может избрать для поддержки». И дальше по тексту: «Запомните, что при определении членов интерфейса область реализации для них не определяется. Интерфейсы — это чистый протокол и потому реализация для них никогда не предоставляется».
Отлично, вроде бы все понятно, приблизительно такая же трактовка интерфейса будет описана и в других книгах по языку, но в C# 8 интерфейс несколько пересмотрен и добавлены на первый взгляд противоречащие возможности типа. Рассмотрим же интерфейсы в C# 8 более подробно.
Default implementations или реализация членов по умолчанию
По определению, если мы наследуемся от интерфейса, то должны реализовать поведение каждого члена интерфейса. В C# 8 появилась возможность реализации члена интерфейса по умолчанию:
interface ICommand
{
void exec();
// default implementations
public void sendNotification(string mes)
{
Console.WriteLine(mes);
}
}
Метод «exec» является обычным абстрактным методом в интерфейсе к которым мы все очень привыкли, а вот метод «sendNotification» для интерфейса выглядит странно и предлагает свою реализацию по умолчанию.
Посмотрим как это работает:
public class AppFirstCommand : ICommand
{
public void exec()
{
// some action
Console.WriteLine("Action complete!");
}
}
При реализации интерфейса мы обязаны реализовать абстрактный метод «exec» и можем опустить реализацию метода «sendNotification». Удобно?
class Program
{
static void Main(string[] args)
{
ICommand appFirstCommand = new AppFirstCommand();
appFirstCommand.exec();
appFirstCommand.sendNotification("Default implementations");
}
}
Результат выполнения:
Action complete!
Default implementations
Что бы обратиться к методу интерфейса с реализацией по умолчанию, нам нужно объявить переменную ссылочного интерфейсного типа, т.е. ссылку на интерфейс:
ICommand appFirstCommand = new AppFirstCommand();
Иначе мы не сможем обратиться к методу «sendNotification» интерфейса, так как в классе «AppFirstCommand» нет реализации этого метода. Так работать не будет:
AppFirstCommand appFirstCommand = new AppFirstCommand();
И да, переопределить поведение метода «sendNotification» мы так же можем:
public class AppSecondCommand : ICommand
{
public void exec()
{
// some action
Console.WriteLine("Action complete!");
}
// override sendNotification
public void sendNotification()
{
Console.WriteLine("New implementation");
}
}
class Program
{
static void Main(string[] args)
{
AppSecondCommand appSecondCommand = new AppSecondCommand();
appSecondCommand.exec();
appSecondCommand.sendNotification();
}
}
Результат выполнения:
Action complete!
override method from AppSecondCommand
В этом случае мы обращаемся напрямую к экземпляру класса «AppSecondCommand», так как в этом классе мы явно реализовали метод «sendNotification».
Чем подход с «Default implementations» удобен?
Члены интерфейса по умолчанию позволяют добавлять новые члены к интерфейсу в следующих версиях, не нарушая совместимость исходного кода с существующими реализациями этого интерфейса. Удобно.
Отмечу, что доступны все модификаторы доступа C# для членов интерфейса реализованных по умолчанию.
Интерфейсы могут содержать статические члены
interface IStaticMembers
{
private static string commandName = "Default command name";
private void build(string name)
{
commandName = name;
}
public void setCommandName(string name)
{
build(name);
}
}
Интерфейсы могут содержать виртуальные члены
Но класс, который наследует интерфейс переопределить виртуальный член не может.
Переопределять виртуальные члены интерфейсов могут только интерфейсы.
interface IVirtualMembers
{
public virtual void sendNotification()
{
Console.WriteLine("IVirtualMembers's sendNotification");
}
}
interface IOverrideVirtualMembers : IVirtualMembers
{
void IVirtualMembers.sendNotification()
{
Console.WriteLine("IOverrideVirtualMembers's sendNotification");
}
}
Стоит также отметить, что так же можно определять абстрактные члены интерфейсов, но в этом нет никакого смысла.
Итак, краткий список новых возможностей интерфейсов в C# 8:
- Default implementations или реализация членов по умолчанию;
- Интерфейсы могут содержать статические члены;
- Интерфейсы могут содержать виртуальные члены.
Весь код из статьи выложен на github.