Ескертпе
Бұл бетке кіру үшін қатынас шегін айқындау қажет. Жүйеге кіруді немесе каталогтарды өзгертуді байқап көруге болады.
Бұл бетке кіру үшін қатынас шегін айқындау қажет. Каталогтарды өзгертуді байқап көруге болады.
Подсказка
Вы новичок в разработке программного обеспечения? Сначала начните с учебников для начинающих. Вы столкнетесь с интерфейсами, как только вам понадобится определить общее поведение для несвязанных типов.
Есть опыт на другом языке? Интерфейсы C# похожи на интерфейсы в Java или протоколах Swift. Просмотрите явную реализацию в поисках специфических паттернов C#.
Интерфейс определяет контракт: группу связанных методов, свойств, событий и индексаторов, которые classstruct необходимо реализовать. Интерфейсы позволяют одному типу реализовать несколько контрактов, что важно, так как C# не поддерживает множественное наследование классов. Структуры не могут наследовать от других структур или классов, поэтому интерфейсы являются единственным способом добавления общего поведения между типами структур.
В следующем примере объявляется интерфейс и класс, реализующий его:
interface IEquatable<T>
{
bool Equals(T obj);
}
public class Car : IEquatable<Car>
{
public string? Make { get; set; }
public string? Model { get; set; }
public string? Year { get; set; }
public bool Equals(Car? car) =>
car is not null &&
(Make, Model, Year) == (car.Make, car.Model, car.Year);
}
Любой класс или структура, реализующая IEquatable<T> , должна предоставлять метод, соответствующий Equals сигнатуре интерфейса. Вы можете рассчитывать на то, что любая реализация IEquatable<T> будет поддерживать сравнение на равенство, независимо от ее конкретного типа. Это прогнозируемость является основным значением интерфейсов.
Объявление интерфейса
Определите интерфейс с ключевым словом interface . По соглашению имена интерфейсов начинаются с буквы I:
interface ILogger
{
void Log(string message);
string Name { get; }
}
Интерфейсы могут содержать методы, свойства, события и индексаторы. Интерфейс не может содержать поля экземпляра, конструкторы экземпляра или финализаторы. Члены по умолчанию public. При необходимости можно указать другие модификаторы специальных возможностей. Например, используйте internal для элементов, которые не должны быть видимы за пределами сборки.
Реализация интерфейса
Класс или структура перечисляет интерфейсы, которые он реализует после двоеточия в объявлении. Класс должен предоставить реализацию для каждого члена, объявленного в интерфейсе:
public class ConsoleLogger : ILogger
{
public string Name => "Console";
public void Log(string message) =>
Console.WriteLine($"[{Name}] {message}");
}
public class FileLogger : ILogger
{
public string Name => "File";
public void Log(string message)
{
// In a real app, write to a file
Console.WriteLine($"[{Name}] Writing to file: {message}");
}
}
Класс может реализовать несколько интерфейсов, разделенных запятыми. Он должен предоставлять реализации для всех членов из каждого интерфейса, который он перечисляет.
Явная реализация
Иногда необходимо реализовать член интерфейса, не делая его частью общедоступного API класса. Явная реализация квалифицирует член с именем интерфейса. Член доступен только с помощью переменной типа интерфейса:
interface IMetric
{
double GetDistance(); // Returns meters
}
interface IImperial
{
double GetDistance(); // Returns feet
}
public class Runway(double meters) : IMetric, IImperial
{
// Explicit implementation for IMetric
double IMetric.GetDistance() => meters;
// Explicit implementation for IImperial
double IImperial.GetDistance() => meters * 3.28084;
}
Явная реализация полезна, если два интерфейса объявляют члены с одинаковым именем, или если вы хотите сохранить публичный интерфейс класса чистым. Дополнительные сведения см. в разделе "Явная реализация интерфейса".
Наследование интерфейса
Интерфейсы могут наследоваться от одного или нескольких других интерфейсов. Класс, реализующий производный интерфейс, должен реализовать все члены из производного интерфейса и всех его базовых интерфейсов:
interface IDrawable
{
void Draw();
}
interface IShape : IDrawable
{
double Area { get; }
}
public class Circle(double radius) : IShape
{
public double Area => Math.PI * radius * radius;
public void Draw() =>
Console.WriteLine($"Drawing circle with area {Area:F2}");
}
Класс, реализующий IShape, может быть неявно преобразован в IDrawable, так как IShape наследует от него.
Интерфейсы и абстрактные классы
Интерфейсы и абстрактные классы определяют контракты, которые должны выполнять производные типы.
- Используйте абстрактный класс , если связанные типы совместно используют состояние (поля), конструкторы или не открытые члены. Абстрактные классы позволяют развивать иерархию, добавляя новые члены с поведением по умолчанию, не нарушая существующие производные типы.
- Используйте интерфейс, когда типу требуется выполнить контракт, который пересекает несвязанные иерархии, или когда необходимо реализовать несколько контрактов. Интерфейсы не могут объявлять поля экземпляров или конструкторы, поэтому они лучше всего подходят для добавления функциональности в типы, которые уже имеют базовый класс. В расширенных сценариях интерфейсы также поддерживают реализации элементов по умолчанию.
Класс может наследоваться только от одного базового класса, но может реализовать несколько интерфейсов. Это различие часто делает интерфейсы лучшим выбором для определения возможностей, которые охватывают иерархии типов.
Работа с внутренними интерфейсами
Как правило, можно реализовать внутренний интерфейс с общедоступными элементами, если все типы в сигнатуре интерфейса общедоступны. Если интерфейс использует внутренние типы в подписях членов, необходимо использовать явную реализацию, так как реализующий член не может быть общедоступным во время предоставления внутренних типов:
internal class InternalConfiguration
{
public string Setting { get; set; } = "";
}
internal interface ILoggable
{
void Log(string message);
}
internal interface IConfigurable
{
void Configure(InternalConfiguration config);
}
public class ServiceImplementation : ILoggable, IConfigurable
{
// Implicit implementation: ILoggable uses only public types in its signature
public void Log(string message) =>
Console.WriteLine($"Log: {message}");
// Explicit implementation: IConfigurable uses internal types
void IConfigurable.Configure(InternalConfiguration config) =>
Console.WriteLine($"Configured with: {config.Setting}");
}
В предыдущем примере IConfigurable используется внутренний тип InternalConfiguration в сигнатуре метода.
ServiceImplementation использует явную реализацию для этого элемента. В отличие от этого, ILoggable использует только открытые типы (string) в его сигнатуре и может быть реализована неявно.
Элементы интерфейса по умолчанию и статические абстрактные элементы
Интерфейсы поддерживают две расширенные функции, которые выходят за рамки основных контрактов:
- Элементы интерфейса по умолчанию позволяют интерфейсу предоставлять текст метода. Типы наследуют реализацию по умолчанию и могут при необходимости ее переопределить. Дополнительные сведения см. в статье о методах интерфейса по умолчанию.
- Статические абстрактные члены требуют реализации типов для предоставления статического элемента, который полезен для определения контрактов операторов или шаблонов фабрики. Дополнительные сведения см. в разделе статических абстрактных элементов в интерфейсах.
Оба компонента рассматриваются в статье по интерфейсам в справочнике по языку. Большинство повседневного использования интерфейса включает объявление и реализацию шаблонов, описанных ранее в этой статье.
Сводка по интерфейсам
- Интерфейс определяет контракт методов, свойств, событий и индексаторов.
- Класс или структура, реализующая интерфейс, должна предоставлять реализации для всех объявленных членов (если интерфейс не предоставляет реализацию по умолчанию).
- Невозможно создать экземпляр интерфейса напрямую.
- Класс или структура может реализовывать несколько интерфейсов. Класс может наследовать базовому классу и также реализовывать один или несколько интерфейсов.
- Имена интерфейсов обычно начинаются с
I.