Interfejsy — definiowanie zachowania dla wielu typów

Interfejs zawiera definicje dla grupy powiązanych funkcji, które nieskonstrakcji class lub struct muszą zostać zaimplementowane. Interfejs może definiować static metody, które muszą mieć implementację. Interfejs może definiować domyślną implementację dla elementów członkowskich. Interfejs może nie zadeklarować danych wystąpienia, takich jak pola, właściwości zaimplementowane automatycznie lub zdarzenia podobne do właściwości.

Za pomocą interfejsów można na przykład uwzględnić zachowanie z wielu źródeł w klasie. Ta funkcja jest ważna w języku C#, ponieważ język nie obsługuje wielu dziedziczenia klas. Ponadto należy użyć interfejsu, jeśli chcesz symulować dziedziczenie dla struktur, ponieważ nie mogą one rzeczywiście dziedziczyć z innej struktury lub klasy.

Interfejs definiuje się przy użyciu słowa kluczowego interface , jak pokazano w poniższym przykładzie.

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

Nazwa interfejsu musi być prawidłową nazwą identyfikatora języka C#. Zgodnie z konwencją nazwy interfejsów zaczynają się od litery I.

Każda klasa lub struktura, która implementuje IEquatable<T> interfejs, musi zawierać definicję Equals metody zgodnej z sygnaturą określaną przez interfejs. W związku z tym można liczyć na klasę, która implementuje IEquatable<T> element , aby zawierał metodę Equals , za pomocą której wystąpienie klasy może określić, czy jest równe innemu wystąpieniu tej samej klasy.

Definicja IEquatable<T> elementu nie zapewnia implementacji dla elementu Equals. Klasa lub struktura może implementować wiele interfejsów, ale klasa może dziedziczyć tylko z jednej klasy.

Aby uzyskać więcej informacji na temat klas abstrakcyjnych, zobacz Klasy abstrakcyjne i zapieczętowane oraz składowe klas.

Interfejsy mogą zawierać metody wystąpienia, właściwości, zdarzenia, indeksatory lub dowolną kombinację tych czterech typów składowych. Interfejsy mogą zawierać konstruktory statyczne, pola, stałe lub operatory. Począwszy od języka C# 11, elementy członkowskie interfejsu, które nie są polami, mogą mieć wartość static abstract. Interfejs nie może zawierać pól wystąpień, konstruktorów wystąpień ani finalizatorów. Domyślnie elementy członkowskie interfejsu są publiczne i można jawnie określić modyfikatory ułatwień dostępu, takie jak public, protected, internal, private, , protected internallub private protected. Element private członkowski musi mieć domyślną implementację.

Aby zaimplementować składową interfejsu, odpowiedni element członkowski klasy implementowania musi być publiczny, niestatyczny i mieć taką samą nazwę i podpis jak składowa interfejsu.

Uwaga

Gdy interfejs deklaruje statyczne elementy członkowskie, typ implementujący ten interfejs może również zadeklarować statyczne elementy członkowskie z tym samym podpisem. Są one odrębne i unikatowo identyfikowane przez typ deklarujący element członkowski. Statyczny element członkowski zadeklarowany w typie nie zastępuje statycznego elementu członkowskiego zadeklarowanego w interfejsie.

Klasa lub struktura, która implementuje interfejs, musi zapewnić implementację dla wszystkich zadeklarowanych elementów członkowskich bez domyślnej implementacji dostarczonej przez interfejs. Jeśli jednak klasa bazowa implementuje interfejs, każda klasa pochodząca z klasy bazowej dziedziczy tę implementację.

Poniższy przykład przedstawia implementację interfejsu IEquatable<T> . Klasa implementowania , Carmusi zapewnić implementację Equals metody .

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);
    }
}

Właściwości i indeksatory klasy mogą definiować dodatkowe metody dostępu dla właściwości lub indeksatora zdefiniowanego w interfejsie. Na przykład interfejs może zadeklarować właściwość, która ma metodę uzyskiwania dostępu. Klasa, która implementuje interfejs, może zadeklarować tę samą właściwość przy użyciu metody get dostępu i zestawu . Jeśli jednak właściwość lub indeksator używa jawnej implementacji, metody dostępu muszą być zgodne. Aby uzyskać więcej informacji na temat jawnej implementacji, zobacz Jawna implementacja interfejsu i właściwości interfejsu.

Interfejsy mogą dziedziczyć z co najmniej jednego interfejsu. Interfejs pochodny dziedziczy elementy członkowskie z interfejsów podstawowych. Klasa, która implementuje interfejs pochodny, musi implementować wszystkie elementy członkowskie w interfejsie pochodnym, w tym wszystkie elementy członkowskie interfejsu podstawowego pochodnego. Ta klasa może zostać niejawnie przekonwertowana na interfejs pochodny lub dowolny z jego interfejsów podstawowych. Klasa może zawierać interfejs wiele razy za pośrednictwem klas bazowych, które dziedziczą lub za pośrednictwem interfejsów dziedziczynych przez inne interfejsy. Jednak klasa może zapewnić implementację interfejsu tylko raz i tylko wtedy, gdy klasa deklaruje interfejs jako część definicji klasy (class ClassName : InterfaceName). Jeśli interfejs jest dziedziczony, ponieważ dziedziczysz klasę bazową, która implementuje interfejs, klasa bazowa zapewnia implementację składowych interfejsu. Jednak klasa pochodna może ponownie zaimplementować wszystkie elementy członkowskie interfejsu wirtualnego zamiast używać dziedziczonej implementacji. Gdy interfejsy deklarują domyślną implementację metody, każda klasa implementujący ten interfejs dziedziczy tę implementację (aby uzyskać dostęp do domyślnej implementacji elementu członkowskiego interfejsu, należy rzutować wystąpienie klasy na typ interfejsu).

Klasa podstawowa może również implementować elementy członkowskie interfejsu przy użyciu wirtualnych elementów członkowskich. W takim przypadku klasa pochodna może zmienić zachowanie interfejsu, przesłaniając wirtualne elementy członkowskie. Aby uzyskać więcej informacji na temat wirtualnych elementów członkowskich, zobacz Polymorphism (Polimorfizm).

Podsumowanie interfejsów

Interfejs ma następujące właściwości:

  • W wersjach języka C# wcześniejszych niż 8.0 interfejs przypomina abstrakcyjną klasę bazową z tylko abstrakcyjnymi elementami członkowskimi. Klasa lub struktura, która implementuje interfejs, musi implementować wszystkie jego elementy członkowskie.
  • Począwszy od języka C# 8.0, interfejs może definiować domyślne implementacje dla niektórych lub wszystkich jego elementów członkowskich. Klasa lub struktura, która implementuje interfejs, nie musi implementować składowych, które mają domyślne implementacje. Aby uzyskać więcej informacji, zobacz domyślne metody interfejsu.
  • Nie można utworzyć wystąpienia interfejsu bezpośrednio. Jego składowe są implementowane przez dowolną klasę lub strukturę, która implementuje interfejs.
  • Klasa lub struktura może implementować wiele interfejsów. Klasa może dziedziczyć klasę bazową, a także implementować jeden lub więcej interfejsów.