Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Interfejs zawiera definicje dla grupy powiązanych funkcji, które nieskonstrakcji class lub struct muszą zostać zaimplementowane. Interfejs może definiować static metody. Interfejs może definiować domyślną implementację dla elementów. Interfejs nie może zadeklarować danych wystąpienia, takich jak pola, automatycznie zaimplementowane właściwości 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ę typu T , która implementuje IEquatable<T> , aby zawierać metodę Equals , z którą wystąpienie tej 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. 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 przy użyciu niejawnej implementacji, odpowiedni element członkowski klasy implementowania musi być publiczny, niestatyczny i mieć taką samą nazwę i podpis jak składowa interfejsu. Aby zaimplementować elementy członkowskie interfejsu, które nie mają być publiczne, należy użyć jawnej implementacji 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. Ci członkowie są odrębni i unikatowo identyfikowani przez typ je deklarujący. 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 zadeklarowanego 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).
Praca z interfejsami wewnętrznymi
Interfejs wewnętrzny może być zwykle implementowany przy użyciu niejawnej implementacji z publicznymi elementami członkowskimi, o ile wszystkie typy w podpisie interfejsu są publicznie dostępne. Jednak gdy interfejs używa typów wewnętrznych w podpisach składowych, niejawna implementacja staje się niemożliwa, ponieważ implementująca składowa klasy musi być publiczna podczas uwidaczniania typów wewnętrznych. W takich przypadkach należy użyć jawnej implementacji interfejsu.
W poniższym przykładzie przedstawiono oba scenariusze:
// Internal type that cannot be exposed publicly
internal class InternalConfiguration
{
public string Setting { get; set; } = "";
}
// Internal interface that CAN be implemented with public members
// because it only uses public types in its signature
internal interface ILoggable
{
void Log(string message); // string is public, so this works with implicit implementation
}
// Interface with internal accessibility using internal types
internal interface IConfigurable
{
void Configure(InternalConfiguration config); // Internal type prevents implicit implementation
}
// This class shows both implicit and explicit interface implementation
public class ServiceImplementation : ILoggable, IConfigurable
{
// Implicit implementation works for ILoggable because string is public
public void Log(string message)
{
Console.WriteLine($"Log: {message}");
}
// Explicit implementation required for IConfigurable because it uses internal types
void IConfigurable.Configure(InternalConfiguration config)
{
// Implementation here
Console.WriteLine($"Configured with: {config.Setting}");
}
// If we tried implicit implementation for IConfigurable, this wouldn't compile:
// public void Configure(InternalConfiguration config) // Error: cannot expose internal type
}
W poprzednim przykładzie IConfigurable interfejs używa typu InternalConfiguration wewnętrznego w podpisie metody. Klasa ServiceImplementation nie może używać implementacji niejawnej, ponieważ wymaga to upublicznienie Configure metody, która nie jest dozwolona, gdy podpis metody zawiera typy wewnętrzne. Zamiast tego jest używana jawna implementacja interfejsu, która nie ma modyfikatora dostępu i jest dostępna tylko za pośrednictwem typu interfejsu.
Z kolei interfejs można zaimplementować niejawnie z publicznymi elementami członkowskimi, ILoggable ponieważ wszystkie typy w podpisie (string) są publicznie dostępne, mimo że sam interfejs jest wewnętrzny.
Aby uzyskać więcej informacji na temat jawnej implementacji interfejsu, zobacz Jawna implementacja interfejsu.
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.
- 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.