Интерфейсы — определение поведения для нескольких типов

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

С помощью интерфейсов можно, например, включить в класс поведение из нескольких источников. Эта возможность очень важна в C#, поскольку этот язык не поддерживает множественное наследование классов. Кроме того, необходимо использовать интерфейс, если требуется имитировать наследование для структур, поскольку они не могут фактически наследовать от другой структуры или класса.

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

interface IEquatable<T>
{
    bool Equals(T obj);
}

Имя интерфейса должно быть допустимым именем идентификатора C#. По соглашению имена интерфейсов начинаются с заглавной буквы I.

Любой объект (класс или структура), реализующий интерфейс IEquatable<T>, должен содержать определение для метода Equals, соответствующее сигнатуре, которую задает интерфейс. В результате можно рассчитывать на класс типа T , реализующего IEquatable<T> Equals метод, с помощью которого экземпляр этого класса может определить, равен ли он другому экземпляру того же класса.

Определение IEquatable<T> не предоставляет реализацию для метода Equals. Класс или структура может реализовывать несколько интерфейсов, но класс может наследовать только от одного класса.

Дополнительные сведения об абстрактных классах см. в статье Abstract and Sealed Classes and Class Members (Абстрактные и запечатанные классы и члены классов).

Интерфейсы могут содержать методы экземпляра, свойства, события, индексаторы, а также любое сочетание этих четырех типов членов. Интерфейсы могут содержать статические конструкторы, поля, константы или операторы. Начиная с C# 11, элементы интерфейса, которые не являются полями, могут быть static abstract. Интерфейс не может содержать поля экземпляров, конструкторы экземпляров или методы завершения. Члены интерфейса по умолчанию являются общедоступными, и вы можете явно указать модификаторы доступа, такие как public, protected, internal, private, protected internal или private protected. Элемент private должен иметь реализацию по умолчанию.

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

Примечание.

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

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

В следующем примере показана реализация интерфейса IEquatable<T>. Реализующий класс Car должен предоставлять реализацию метода Equals.

public class Car : IEquatable<Car>
{
    public string? Make { get; set; }
    public string? Model { get; set; }
    public string? Year { get; set; }

    // Implementation of IEquatable<T> interface
    public bool Equals(Car? car)
    {
        return (this.Make, this.Model, this.Year) ==
            (car?.Make, car?.Model, car?.Year);
    }
}

Свойства и индексаторы класса могут определять дополнительные методы доступа для свойства или индексатора, определенного в интерфейсе. Например, интерфейс может объявлять свойство, имеющее акцессор get. Класс, реализующий этот интерфейс, может объявлять это же свойство с обоими акцессорами (get и set). Однако если свойство или индексатор использует явную реализацию, методы доступа должны совпадать. Дополнительные сведения о явной реализации см. в статьях Явная реализация интерфейса и Свойства интерфейса.

Интерфейс может наследовать от одного или нескольких интерфейсов. Производный интерфейс наследует члены от своих базовых интерфейсов. Класс, реализующий производный интерфейс, должен реализовывать все члены в нем, включая все члены базовых интерфейсов производного интерфейса. Этот класс может быть неявно преобразован в производный интерфейс или любой из его базовых интерфейсов. Класс может включать интерфейс несколько раз через наследуемые базовые классы или через интерфейсы, которые наследуются другими интерфейсами. Однако класс может предоставить реализацию интерфейса только однократно и только если класс объявляет интерфейс как часть определения класса (class ClassName : InterfaceName). Если интерфейс наследуется, поскольку наследуется базовый класс, реализующий этот интерфейс, то базовый класс предоставляет реализацию членов этого интерфейса. Но производный класс может повторно реализовать любые члены виртуального интерфейса и не использовать наследованную реализацию. Когда интерфейсы объявляют реализацию метода по умолчанию, любой класс, реализующий этот интерфейс, наследует эту реализацию (необходимо привести экземпляр класса к типу интерфейса для доступа к реализации по умолчанию на члене интерфейса).

Базовый класс также может реализовывать члены интерфейса с помощью виртуальных членов. В таком случае производный класс может изменять поведение интерфейса путем переопределения виртуальных членов. Дополнительные сведения о виртуальных членах см. в статье Полиморфизм.

Сводка по интерфейсам

Интерфейс имеет следующие свойства.

  • В версии C# 8.0 и более ранних интерфейс подобен абстрактному базовому классу, содержащему только абстрактные элементы. Класс (или структура), реализующий интерфейс, должен реализовывать все его элементы.
  • Начиная с C# 8.0 интерфейс может определять реализации по умолчанию для некоторых или для всех его элементов. Класс или структура, реализующие интерфейс, не должны реализовывать элементы, имеющие реализации по умолчанию. Дополнительные сведения см. в статье о методах интерфейса по умолчанию.
  • Невозможно создать экземпляр интерфейса напрямую. Его члены реализуются любым классом (или структурой), реализующим интерфейс.
  • Класс или структура может реализовывать несколько интерфейсов. Класс может наследовать базовому классу и также реализовывать один или несколько интерфейсов.