Скоро будет выпущен C # 8 вместе с .NET Core 3.0, в котором есть несколько новых функций и улучшений, которые расширяют возможности разработчиков и делают их повседневное программирование еще проще. На момент написания этой статьи C # 8 был доступен как предварительная версия 5.

В этой статье мы рассмотрим стандартные реализации членов интерфейса; однако сначала давайте взглянем на настройку, необходимую для работы на C # 8.

Предварительное условие для использования C # 8 (предварительная версия)

  • Visual Studio 2019
  • Измените настройку языковой версии

Щелкните проект правой кнопкой мыши, выберите «Свойства», «Сборка», «Дополнительно» и затем выберите «C # 8.0 (бета)» в языковой версии.

C#

  • Включите пакет SDK для .NET Core

В строке меню щелкните «Инструменты», выберите «Параметры», разверните «Среда», щелкните «Функции предварительного просмотра», а затем установите флажок «Использовать предварительные версии пакета SDK для .NET Core», как показано ниже.

C#2

Предпосылки и пример использования

Реализация интерфейса по умолчанию основана на технике программирования Traits, в которой поощряется повторное использование методов между несвязанными классами. Это также доступно на других языках программирования, например, это java в названии методов по умолчанию.

Теперь давайте попробуем разобраться в сценариях использования, которые пытается решить эта функция.

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

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

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

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

Исполнение

Давайте рассмотрим пример, чтобы понять, как с ним работать.


    interface ILogger  
        {  
            void Log(LogLevel level, string message);  
            void Log(Exception ex) => Log(LogLevel.Error, ex.ToString());  
        }  
      
    class CustomLogger : ILogger  
        {  
            public void Log(LogLevel level, string message)  
            {  
                Console.WriteLine($"{level.ToString()}: Hello readers of C# 8!! {message}");  
            }         
        }  
      
    class DefaultInterfaceImpl  
        {  
            public static void DoDivide()  
            {  
                try  
                {  
                    int a = 3;  
                    int b = 0;  
                    int c = a / b;  
                }  
                catch (DivideByZeroException ex)  
                {  
                    ILogger logger = new CustomLogger(); // Converting to interface                 
                    logger.Log(ex); // Calling new Log overload  
                           }  
                   }  
          }  

Как вы можете видеть в приведенном выше коде, этот член интерфейса по умолчанию вызывается из блока catch, несмотря на то, что конкретный класс CustomLogger не реализовал его. Перед вызовом метода Log экземпляр класса преобразуется в интерфейс ILogger.

Теперь попробуем вызвать метод Log только с классом CustomLogger следующим образом.


    CustomLogger logger2 = new CustomLogger();   
    logger2.Log(ex);  

Приведенный выше код приведет к ошибке времени компиляции: «Не указан аргумент, который соответствует требуемому формальному параметру ‘message’ CustomLogger.Log (LogLevel, string)», потому что класс CustomLogger не имеет информации о членах интерфейса по умолчанию.

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


    interface ILogger2  
        {  
            abstract void Log(LogLevel level, string message);  
            virtual void Log(Exception ex) => Log(LogLevel.Error, ex.ToString());  
        }  

Приведенный выше код будет давать ошибки времени компиляции в предыдущих версиях C # 8, как показано ниже.

C#3

И этот код будет идеально компилироваться в C # 8, и причина в том, что в C # 8 обычные элементы интерфейса классифицируются как абстрактные, а элементы интерфейса по умолчанию разработаны как виртуальные, и поэтому объявление их как абстрактных или виртуальных не вызывает никаких ошибок.

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

Чтобы увидеть то же самое в действии, давайте добавим новый класс, реализовав интерфейс ILogger2 следующим образом.


    class CustomLogger2 : ILogger2  
            {  
                public void Log(LogLevel level, string message)  
                {  
                    Console.WriteLine($"{level.ToString()}: Hello readers of C# 8!! {message}");  
                }  
                public void Log(Exception message)  
                {  
                    Console.WriteLine($"From: Overridden method: Hello readers of C# 8!! {message}");  
                }  
            }  

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


    public static void DoDivide2()  
                {  
                    try  
                    {  
                        int a = 3;  
                        int b = 0;  
                        int c = a / b;  
                    }  
                    catch (DivideByZeroException ex)  
                    {  
                        ILogger2 logger = new CustomLogger2(); // Converting to interface                 
                        logger.Log(ex); // Calling new Log overload  
                    }  
                }  

После выполнения приведенного выше кода будет вызвана переопределенная версия метода журнала (внутри CustomLogger2) и появится сообщение «От: Переопределенный метод: Здравствуйте, читатели Learning C # 8 !!» будут напечатаны.

Вы могли заметить, что ключевое слово override не было добавлено в метод Log в классе CustomLogger2, и это отличается от обычного виртуального и override.

Резюме

Реализация интерфейса по умолчанию — это наиболее ожидаемая функция, которая была добавлена как часть C # 8. Она помогает добавить реализацию по умолчанию, не нарушая существующие контракты.