Описание тега default-interface-member

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

Члены интерфейса по умолчанию были введены в C#-8. Они похожи на функцию Java по умолчанию.

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

Члены интерфейса по умолчанию помогают в следующих сценариях:

  • Управление версиями интерфейса
  • Взаимодействие с API для Android (Java) и iOS (Swift), которые поддерживают аналогичные функции.
  • Для реализации трейтов без необходимости множественного наследования, как у трейтов PHP и Scala. Java 8 и более поздние версии также поддерживают черты с помощью методов интерфейса по умолчанию.
  • Повторное использование кода в структурах (спасибо Эйрику Царпалису!)

Версии интерфейса

Этот пример адаптирован из статьи Мэдса Торгерсена о реализациях по умолчанию в интерфейсах:

Допустим, мы предлагаем следующий интерфейс:

interface ILogger
{
    void Log(LogLevel level, string message);
}

И класс, реализующий это:

class ConsoleLogger : ILogger
{
    public void Log(LogLevel level, string message) { ... }
}

С членами по умолчанию интерфейс может быть изменен без нарушения ConsoleLogger:

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

ConsoleLogger по-прежнему удовлетворяет контракту, предоставляемому интерфейсом: если он преобразован в интерфейс и вызывается новый метод Log, он будет работать нормально - просто вызывается реализация интерфейса по умолчанию:

public static void LogException(ConsoleLogger logger, Exception ex)
{
    ILogger ilogger = logger; // Converting to interface
    ilogger.Log(ex);          // Calling new Log overload
}

Реализующий класс, который знает о новом члене, может реализовать его по-своему. В этом случае реализация по умолчанию просто игнорируется.

Черты

В воображаемой игре предметы могут унаследовать от GameItem учебный класс:

public class GameItem    {    }

Предположим, у зелья нет места и оно не движется:

public class Potion:GameItem{}

У камня может быть местоположение:

public interface ILocatable
{
    public (double x,double y) Location{get;set;}
}

public class Rock:GameItem,ILocatable
{
    public (double x,double y) Location{get;set;}
}

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

Это можно решить с помощью IMovable черта, которая может быть применена к любому типу, имеющему Location свойство:

public interface IMovable
{
    public abstract (double x,double y) Location{get;set;}
    void Move(double angle,double speed)
    {              
          var x=Location.x + speed*Math.Sin(angle);
          var y=Location.y + speed*Math.Cos(angle);
          Location=(x,y);
    }
}    

Эта черта может быть применена к любому классу, если у него есть соответствие Location свойство:

public class Player:GameItem,ILocatable,IMovable
{
    public (double x,double y) Location{get;set;}
}

public class Monster:GameItem,ILocatable,IMovable
{
    public (double x,double y) Location{get;set;}
}

Пример трейта - чтение настроек в среде контейнера

В контейнерных или бессерверных приложениях одним из наиболее распространенных способов распространения настроек является использование переменных среды. Модули DIM можно использовать для создания признака, который извлекает конкретную переменную среды каждый раз, когда она вызывается, например:

interface IGithubSettings
{
    public string CurrentToken  => Environment.GetEnvironmentVariable("GitHubToken");
}

Ссылки: