Заметка
Доступ к этой странице требует авторизации. Вы можете попробовать войти в систему или изменить каталог.
Доступ к этой странице требует авторизации. Вы можете попробовать сменить директорию.
19.1 Общие
Интерфейс определяет контракт. Класс или структура, реализующая интерфейс, должна соответствовать своему контракту. Интерфейс может наследовать от нескольких базовых интерфейсов, а класс или структура может реализовать несколько интерфейсов.
Интерфейсы могут содержать различные виды элементов, как описано в §19.4. Сам интерфейс может предоставить реализацию для некоторых или всех членов функции, объявленных им. Члены, для которых интерфейс не предоставляет реализацию, являются абстрактными. Их реализации должны предоставляться классами или структурами, реализующими интерфейс или производный интерфейс, предоставляющий переопределяющее определение.
Примечание. Исторически добавление нового члена функции в интерфейс повлияло на всех существующих потребителей этого типа интерфейса; это было критическое изменение. Добавление реализаций элементов функции интерфейса позволило разработчикам обновить интерфейс, позволяя всем разработчикам переопределить реализацию. Пользователи интерфейса могут принимать реализацию как некрикционное изменение; однако, если их требования отличаются, они могут переопределить предоставленные реализации. конечная заметка
Объявления интерфейса 19.2
19.2.1 Общие
Объявление интерфейса interface_declaration — это объявление типа type_declaration (§14.7), которое объявляет новый тип интерфейса.
interface_declaration
: attributes? interface_modifier* 'partial'? 'interface'
identifier variant_type_parameter_list? interface_base?
type_parameter_constraints_clause* interface_body ';'?
;
Interface_declaration состоит из необязательного набора атрибутов (§23), за которым следует необязательный набор interface_modifiers (§19.2.2), а затем необязательный частичный модификатор (§15.2.7), а затем ключевое слово interface и идентификатор, который именует интерфейс, за которым следует необязательная спецификация variant_type_parameter_list (§19.2.3), за которой следует дополнительная спецификация interface_base (§19.2.4За которым следует необязательная спецификация type_parameter_constraints_clause(§15.2.5), за которой следует interface_body (§19.3), за которым следует точка с запятой.
Объявление интерфейса не должно содержать type_parameter_constraints_clauseы, если оно также не включает variant_type_parameter_list.
Объявление интерфейса, которое предоставляет variant_type_parameter_list , является универсальным объявлением интерфейса. Кроме того, любой интерфейс, вложенный в объявление универсального класса или универсальное объявление структуры, является универсальным объявлением интерфейса, так как аргументы типа для содержащего типа должны быть предоставлены для создания созданного типа (§8.4).
Модификаторы интерфейса 19.2.2
interface_declaration может необязательно включать в себя последовательность модификаторов интерфейса:
interface_modifier
: 'new'
| 'public'
| 'protected'
| 'internal'
| 'private'
| unsafe_modifier // unsafe code support
;
unsafe_modifier (§24.2) доступен только в небезопасном коде (§24).
Ошибка компиляции возникает, если один и тот же модификатор появляется несколько раз в объявлении интерфейса.
Модификатор new разрешен только в интерфейсах, определенных в классе. Он указывает, что интерфейс скрывает унаследованный элемент по тому же имени, как описано в разделе 15.3.5.
Модификаторы public, protected, internal и private контролируют доступность интерфейса. В зависимости от контекста, в котором происходит объявление интерфейса, может быть разрешено только некоторые из этих модификаторов (§7.5.2). Если объявление частичного типа (§15.2.7) включает спецификацию доступности, с помощью модификаторов , public, protected и internal, применяются правила в §15.2.2.
Списки параметров типа 19.2.3
19.2.3.1 Общие
Списки параметров типа Variant могут существовать только в типах интерфейсов и делегатов. Различие от обычных type_parameter_list заключается в наличии необязательной variance_annotation для каждого параметра типа.
variant_type_parameter_list
: '<' variant_type_parameter (',' variant_type_parameter)* '>'
;
variant_type_parameter
: attributes? variance_annotation? type_parameter
;
variance_annotation
: 'in'
| 'out'
;
Если заметка о дисперсии имеет значение out, параметр типа считается ковариантным. Если заметка о дисперсии имеет inзначение, параметр типа считается контравариантным. Если заметки о дисперсии отсутствуют, параметр типа считается инвариантным.
Пример. В следующем примере:
interface C<out X, in Y, Z> { X M(Y y); Z P { get; set; } }
Xявляется ковариантным,Yявляется контравариантным иZинвариантным.заключительный пример
Если универсальный интерфейс объявлен в нескольких частях (§15.2.3), каждое частичное объявление должно указывать одинаковые дисперсии для каждого параметра типа.
19.2.3.2 Вариативная безопасность
Вхождение аннотаций ковариантности в список параметров типа ограничивает, где типы могут использоваться в объявлении типа.
Тип T небезопасный, если выполняется одно из следующих условий:
-
Tявляется параметром контравариантного типа -
T— это тип массива с типом элемента, небезопасным для вывода -
T— это интерфейс или делегатный типSᵢ,... Aₑ, построенный из универсального типаS<Xᵢ, ... Xₑ>, где для по крайней мере одногоAᵢвыполняется одно из следующих условий:-
Xᵢявляется ковариантным или инвариантным иAᵢнебезопасным для выходных данных. -
Xᵢявляется контравариантным или инвариантным иAᵢнебезопасным для ввода.
-
Тип T является небезопасным для ввода, если выполняется одно из следующих условий:
-
T— параметр ковариантного типа -
T— это тип массива с типом элемента, небезопасным для ввода -
T— это интерфейс или делегатный типS<Aᵢ,... Aₑ>, построенный из универсального типаS<Xᵢ, ... Xₑ>, где для по крайней мере одногоAᵢвыполняется одно из следующих условий:-
Xᵢявляется ковариантным или инвариантным иAᵢявляется небезопасным для ввода. -
Xᵢявляется контравариантным или инвариантным иAᵢнебезопасным для выходных данных.
-
Интуитивно понятно, что небезопасный для выхода тип запрещён в выходной позиции, а небезопасный для входа тип запрещён в входной позиции.
Тип является выходобезопасным, если он не является выходно небезопасным, и входобезопасным, если он не является входно небезопасным.
Преобразование вариативности 19.2.3.3
Назначение аннотаций дисперсии заключается в том, чтобы обеспечить более свободные (но по-прежнему типобезопасные) преобразования в типы интерфейсов и делегатов. Для этого определения неявных (§10.2) и явных преобразований (§10.3) используют понятие вариативности преобразования, которое определяется следующим образом:
Тип T<Aᵢ, ..., Aᵥ> может быть преобразован в тип T<Bᵢ, ..., Bᵥ>, если T является интерфейсом или типом делегата, объявленным с вариантными параметрами типа T<Xᵢ, ..., Xᵥ>, и для каждого вариантного параметра типа Xᵢ выполняется одно из следующих условий:
-
Xᵢявляется ковариантным, а неявная ссылка или преобразование идентичности существует изAᵢвBᵢ -
Xᵢявляется контравариантным, и неявное преобразование или преобразование идентичности существует изBᵢвAᵢ. -
Xᵢявляется инвариантным, а тождественное преобразование существует изAᵢвBᵢ.
Базовые интерфейсы 19.2.4
Интерфейс может наследовать от нуля или нескольких типов интерфейсов, которые называются явными базовыми интерфейсамиинтерфейса. Если интерфейс имеет один или несколько явных базовых интерфейсов, то в объявлении этого интерфейса за идентификатором интерфейса следует двоеточие и список типов базовых интерфейсов с разделителем запятыми.
Производный интерфейс может объявлять новые элементы, которые скрывают унаследованные элементы (§7.7.2.3), объявленные в базовых интерфейсах или явно реализующие унаследованные элементы (§19.6.2), объявленные в базовых интерфейсах.
interface_base
: ':' interface_type_list
;
Явные базовые интерфейсы можно создать типы интерфейсов (§8.4, §19.2). Базовый интерфейс сам по себе не может быть параметром типа, хотя он может включать параметры типа, которые находятся в области видимости.
Для созданного типа интерфейса явные базовые интерфейсы формируются путем применения явных объявлений базового интерфейса в объявлении универсального типа и замены каждого type_parameter в объявлении базового интерфейса на соответствующий type_argument созданного типа.
Явные базовые интерфейсы интерфейса должны быть по крайней мере так же доступны, как и сам интерфейс (§7.5.5).
Примечание: Например, это ошибка компиляции при указании
privateилиinternalинтерфейса в interface_basepublicинтерфейса. конечная заметка
Это ошибка компиляции, если интерфейс напрямую или косвенно наследуется от самого себя.
Базовый интерфейсинтерфейса — это явные базовые интерфейсы и их базовые интерфейсы. Другими словами, набор базовых интерфейсов — это полное транзитивное закрытие явных базовых интерфейсов, их явных базовых интерфейсов и т. д. Интерфейс наследует все члены своих базовых интерфейсов.
Пример. В следующем коде
interface IControl { void Paint(); } interface ITextBox : IControl { void SetText(string text); } interface IListBox : IControl { void SetItems(string[] items); } interface IComboBox: ITextBox, IListBox {}базовые интерфейсы
IComboBox:IControl,ITextBoxиIListBox. Другими словами, приведенный выше интерфейсIComboBoxнаследует членыSetText, а также наследует членыSetItemsиPaint.заключительный пример
Элементы, унаследованные от созданного универсального типа, наследуются после подстановки типов. То есть все составляющие типы в члене имеют параметры типа объявления базового класса, замененные соответствующими аргументами типа, используемыми в спецификации class_base .
Пример. В следующем коде
interface IBase<T> { T[] Combine(T a, T b); } interface IDerived : IBase<string[,]> { // Inherited: string[][,] Combine(string[,] a, string[,] b); }интерфейс
IDerivedнаследует методCombineпосле того как параметр типаTзаменён наstring[,].заключительный пример
Класс или структура, реализующая интерфейс, также неявно реализует все базовые интерфейсы интерфейса.
Обработка интерфейсов в нескольких частях объявления частичного интерфейса (§15.2.7) рассматривается далее в §15.2.4.3.
Каждый базовый интерфейс интерфейса должен быть выходным (§19.2.3.2).
Текст интерфейса 19.3
Interface_body интерфейса определяет элементы интерфейса.
interface_body
: '{' interface_member_declaration* '}'
;
Элементы интерфейса 19.4
19.4.1 Общие
Члены интерфейса — это элементы, унаследованные от базовых интерфейсов, и члены, объявленные самим интерфейсом.
interface_member_declaration
: constant_declaration
| field_declaration
| method_declaration
| property_declaration
| event_declaration
| indexer_declaration
| static_constructor_declaration
| operator_declaration
| type_declaration
;
Это предложение расширяет описание элементов в классах (§15.3) с ограничениями для интерфейсов. Члены интерфейса объявляются с помощью member_declarationс помощью следующих дополнительных правил:
- Не допускается finalizer_declaration .
- Конструкторы экземпляров, constructor_declarations, не допускаются.
- Все члены интерфейса неявно имеют открытый доступ; однако явный модификатор доступа (§7.5.2) разрешен, кроме статических конструкторов (§15.12).
- Модификатор подразумевается для элементов функции интерфейса без тел. Этот
abstractмодификатор может быть явно задан. - Элемент функции экземпляра интерфейса, объявление которого включает текст неявно
virtual, еслиsealedне используется модификатор.privatevirtualМодификатор может быть явно задан. - Элемент
privateинтерфейса долженsealedиметь тело или функцию. - Элемент
privateфункции не должен иметь модификаторsealed. - Производный интерфейс может переопределить абстрактный или виртуальный член, объявленный в базовом интерфейсе.
- Явно реализованный член функции не должен иметь модификатор
sealed.
Некоторые объявления, такие как constant_declaration (§15.4), не имеют ограничений в интерфейсах.
Наследуемые элементы интерфейса однозначно не являются частью пространства объявления интерфейса. Таким образом, интерфейс может объявлять элемент с тем же именем или сигнатурой, как унаследованный элемент. Когда это происходит, говорят, что производный член интерфейса скрывает базовый член интерфейса. Скрытие унаследованного элемента не считается ошибкой, но это приводит к предупреждению (§7.7.2.3).
Если в объявление включён модификатор new, который не скрывает унаследованный элемент, по этой причине будет выдано предупреждение.
Примечание. Члены класса
objectне являются, строго говоря, членами любого интерфейса (§19.4). Однако члены классаobjectдоступны через поиск элементов для любого типа интерфейса (§12.5). конечная заметка
Набор элементов интерфейса, объявленного в нескольких частях (§15.2.7), является объединением членов, объявленных в каждой части. Тела всех частей объявления интерфейса имеют одно и то же пространство объявления (§7.3), а область каждого элемента (§7.7) распространяется на тела всех частей.
Пример. Рассмотрим интерфейс
IAс реализацией для членаMи свойстваP. ТипCреализации не предоставляет реализацию дляMодного илиP. К ней необходимо обращаться через ссылку, тип времени компиляции которой является неявно преобразованным вIAилиIB. Эти члены не найдены с помощью поиска элементов в переменной типаC.interface IA { public int P { get { return 10; } } public void M() { Console.WriteLine("IA.M"); } } interface IB : IA { public new int P { get { return 20; } } void IA.M() { Console.WriteLine("IB.M"); } } class C : IB { } class Test { public static void Main() { C c = new C(); ((IA)c).M(); // cast needed Console.WriteLine($"IA.P = {((IA)c).P}"); // cast needed Console.WriteLine($"IB.P = {((IB)c).P}"); // cast needed } }В интерфейсах
IAиIBчленMдоступен напрямую по имени. Однако в методеMainневозможно написатьc.M()илиc.P, так как эти имена не отображаются. Чтобы найти их, требуются приведения к соответствующему типу интерфейса. Объявление вMиспользовании явного синтаксисаIBреализации интерфейса. Это необходимо, чтобы этот метод переопределить один вIA; модификаторoverrideможет не применяться к члену функции. заключительный пример
Поля интерфейса 19.4.2
Это предложение расширяет описание полей в классах §15.5 для полей, объявленных в интерфейсах.
Поля интерфейса объявляются с помощью field_declarations (§15.5.1) со следующими дополнительными правилами:
- Это ошибка во время компиляции для field_declaration объявления поля экземпляра.
Пример: следующая программа содержит статические элементы различных типов:
public interface IX { public const int Constant = 100; protected static int field; static IX() { Console.WriteLine("static members initialized"); Console.WriteLine($"constant = {IX.Constant}, field = {IX.field}"); field = 50; Console.WriteLine("static constructor has run"); } } public class Test: IX { public static void Main() { Console.WriteLine($"constant = {IX.Constant}, field = {IX.field}"); } }Выходные данные создаются
static members initialized constant = 100, field = 0 static constructor has run constant = 100, field = 50заключительный пример
Сведения о выделении и инициализации статических полей см. в статье 19.4.8 .
Методы интерфейса 19.4.3
Это предложение расширяет описание методов в классах §15.6 для методов, объявленных в интерфейсах.
Методы интерфейса объявляются с помощью method_declarations (§15.6)). Атрибуты, return_type, ref_return_type, идентификатор и parameter_list объявления метода интерфейса имеют то же значение, что и объявление метода в классе. Методы интерфейса имеют следующие дополнительные правила:
method_modifier не должно включать
override.Метод, тело которого является точкой с запятой (
;) —abstractabstractмодификатор не является обязательным, но допускается.Объявление метода интерфейса с текстом блока или телом выражения в качестве method_body ;
virtualvirtualмодификатор не является обязательным, но допускается.Method_declaration не должны иметь type_parameter_constraints_clause, если он также не имеет type_parameter_list.
Список требований для допустимых сочетаний модификаторов, указанных для метода класса, расширен следующим образом:
- Статическое объявление, которое не является экстерном, должно иметь блоковый текст или тело выражения в качестве method_body.
- Виртуальное объявление, которое не является экстерном, должно иметь блоковый текст или тело выражения в качестве method_body.
- Частное объявление, которое не является экстерном, должно иметь блоковый текст или тело выражения в качестве method_body.
- Запечатаемое объявление, которое не является экстерном, должно иметь блоковый текст или тело выражения в качестве method_body.
- Асинхронное объявление должно иметь блоковый текст или текст выражения в виде method_body.
Все типы параметров метода интерфейса должны быть входобезопасны (§19.2.3.2), а возвращаемый тип должен быть
voidлибо выходным, либо выходным.Все типы выходных или ссылочных параметров также должны быть выходными.
Примечание. Выходные параметры должны быть входобезопасны из-за распространенных ограничений реализации. конечная заметка
Каждое ограничение типа класса, ограничение типа интерфейса и ограничение параметра типа для любых параметров типа метода должно быть безопасным для ввода.
Эти правила гарантируют, что любое ковариантное или контравариантное использование интерфейса остается типобезопасным.
Пример:
interface I<out T> { void M<U>() where U : T; // Error }недоформен, так как использование
Tв качестве ограниченияUпараметра типа не является безопасным для ввода.Если бы это ограничение не существовало, можно было бы нарушить безопасность типов следующим таким образом:
interface I<out T> { void M<U>() where U : T; } class B {} class D : B {} class E : B {} class C : I<D> { public void M<D>() {...} } ... I<B> b = new C(); b.M<E>();Это фактически вызов
C.M<E>. Но этот вызов требует, чтобыEнаследуется отD, поэтому здесь будет нарушена безопасность типов.заключительный пример
Примечание. См. пример ,19.4.2 , который не только показывает статический метод с реализацией, но и вызывается
Mainи имеет правильный тип возврата и подпись, это также точка входа. конечная заметка
Виртуальный метод с реализацией, объявленной в интерфейсе, может быть переопределен, чтобы быть абстрактным в производном интерфейсе. Это называется reabstraction.
Пример:
interface IA { void M() { Console.WriteLine("IA.M"); } } interface IB: IA { abstract void IA.M(); // reabstraction of M }Это полезно в производных интерфейсах, где реализация метода неуместна, и более соответствующая реализация должна быть предоставлена путем реализации классов. заключительный пример
Свойства интерфейса 19.4.4
Это предложение расширяет описание свойств в классах §15.7 для свойств, объявленных в интерфейсах.
Свойства интерфейса объявляются с помощью property_declarations (§15.7.1) со следующими дополнительными правилами:
property_modifier не включается
override.Явная реализация члена интерфейса не должна содержать accessor_modifier (§15.7.3).
Производный интерфейс может явно реализовать абстрактное свойство интерфейса, объявленное в базовом интерфейсе.
Примечание. Поскольку интерфейс не может содержать поля экземпляра, свойство интерфейса не может быть автоматическим свойством экземпляра, так как для этого требуется объявление неявных полей скрытого экземпляра. конечная заметка
Тип свойства интерфейса должен быть безопасным для вывода, если есть аксессор get, и должен быть безопасным для ввода, если есть аксессор set.
Объявление метода интерфейса с текстом блока или телом выражения в качестве method_body ;
virtualvirtualмодификатор не является обязательным, но допускается.Экземпляр property_declaration , не имеющий реализации
abstract;abstractмодификатор не является обязательным, но допускается. Он никогда не считается автоматически реализованным свойством (§15.7.4).
События интерфейса 19.4.5
Это предложение расширяет описание событий в классах §15.8 для событий, объявленных в интерфейсах.
События интерфейса объявляются с помощью event_declarations (§15.8.1) со следующими дополнительными правилами:
-
event_modifier не должны включать
override. - Производный интерфейс может реализовать абстрактное событие интерфейса, объявленное в базовом интерфейсе (§15.8.5).
- Это ошибка во время компиляции для variable_declarators в экземпляре event_declaration для хранения любых variable_initializer.
- Событие экземпляра
virtualsealedс модификаторами или модификаторами должно объявлять методы доступа. Оно никогда не считается автоматически реализованным событием типа полей (§15.8.2). - Событие экземпляра с модификатором
abstractне должно объявлять методы доступа. - Тип события интерфейса должен быть безопасным для ввода.
Индексаторы интерфейсов 19.4.6
Это предложение расширяет описание индексаторов в классах §15.9 для индексаторов, объявленных в интерфейсах.
Индексаторы интерфейса объявляются с помощью indexer_declarations (§15.9) со следующими дополнительными правилами:
indexer_modifier не должны включать
override.Indexer_declaration с текстом выражения или содержит метод доступа с блоком тела или текста
virtualвыражения;virtualмодификатор не является обязательным, но допускается.Indexer_declaration, тела которого являются точками с запятой (
;)abstract—abstractмодификатор не является обязательным, но допускается.Все типы параметров индексатора интерфейса должны быть входобезопасны (§19.2.3.2).
Все типы выходных или ссылочных параметров также должны быть выходными.
Примечание. Выходные параметры должны быть входобезопасны из-за распространенных ограничений реализации. конечная заметка
Тип индексатора интерфейса должен быть безопасным для вывода, если имеется метод доступа get, и безопасным для ввода, если есть метод доступа set.
Операторы интерфейса 19.4.7
Это предложение расширяет описание элементов operator_declaration в классах §15.10 для операторов, объявленных в интерфейсах.
Operator_declaration в интерфейсе — это реализация (§19.1).
Это ошибка во время компиляции для интерфейса для объявления оператора преобразования, равенства или неравенства.
Статические конструкторы интерфейса 19.4.8
Это предложение расширяет описание статических конструкторов в классах §15.12 для статических конструкторов, объявленных в интерфейсах.
Статический конструктор для закрытого интерфейса (§8.4.3) выполняется не более одного раза в определенном домене приложения. Выполнение статического конструктора активируется первым из следующих действий, выполняемых в домене приложения:
- Ссылки на любые статические члены интерфейса.
-
MainПеред вызовом метода для интерфейса,Mainсодержащего метод (§7.1), в котором начинается выполнение. - Этот интерфейс предоставляет реализацию для члена, и эта реализация осуществляется как наиболее конкретная реализация (§19.4.10) для этого элемента.
Примечание. В случае, если ни один из предыдущих действий не выполняется, статический конструктор интерфейса может не выполняться для программы, в которой экземпляры типов, реализующих интерфейс, создаются и используются. конечная заметка
Чтобы инициализировать новый тип закрытого интерфейса, сначала создается новый набор статических полей для конкретного закрытого типа. Каждое из статических полей инициализировано в значение по умолчанию. Затем для этих статических полей выполняются инициализаторы статических полей. Наконец, выполняется статический конструктор.
Примечание. Пример использования различных типов статических элементов (включая метод Main), объявленных в интерфейсе, см. в разделе §19.4.2 . конечная заметка
Вложенные типы интерфейса 19.4.9
Это предложение расширяет описание вложенных типов в классах §15.3.9 для вложенных типов, объявленных в интерфейсах.
Это ошибка объявления типа класса, типа структуры или типа перечисления в области параметра типа, объявленного с variance_annotation (§19.2.3.1).
Пример. Объявление
Cниже является ошибкой.interface IOuter<out T> { class C { } // error: class declaration within scope of variant type parameter 'T' }заключительный пример
19.4.10 наиболее конкретная реализация
Каждый класс и структура должны иметь наиболее конкретную реализацию для каждого виртуального члена, объявленного во всех интерфейсах, реализованных этим типом среди реализаций, отображаемых в типе или его прямых и косвенных интерфейсах. Наиболее конкретная реализация — это уникальная реализация , которая является более конкретной, чем любая другая реализация.
Примечание. Наиболее конкретное правило реализации гарантирует, что неоднозначность, связанная с наследованием алмазного интерфейса, разрешается явным образом программистом в точке возникновения конфликта. конечная заметка
Для типа T , являющегося структурой или классом, реализующим интерфейсы I2 и I3где I2 и I3 как производный напрямую, так и косвенно от интерфейса I , объявляющего член M, является наиболее конкретной реализацией M :
- Если
Tобъявляется реализацияI.M, эта реализация является самой конкретной реализацией. - В противном случае, если
Tэто класс и прямой или косвенный базовый класс объявляет реализациюI.M, наиболее производный базовый классTявляется самой конкретной реализацией. - В противном случае, если
I2иI3являются интерфейсами, реализованными иTпроизводнымиI3отI2прямого или косвенного,I3.Mявляется более конкретной реализацией, чемI2.M. - В противном случае ни более конкретные, ни
I2.MI3.Mболее конкретные ошибки не возникают.
Пример:
interface IA { void M() { Console.WriteLine("IA.M"); } } interface IB : IA { void IA.M() { Console.WriteLine("IB.M"); } } interface IC: IA { void IA.M() { Console.WriteLine("IC.M"); } } abstract class C: IB, IC { } // error: no most specific implementation for 'IA.M' abstract class D: IA, IB, IC // OK { public abstract void M(); }Наиболее конкретное правило реализации гарантирует, что конфликт (т. е. неоднозначность, возникающая из наследования алмазов) разрешается явным образом программистом в момент возникновения конфликта. заключительный пример
Доступ к члену интерфейса 19.4.11
Члены интерфейса получают доступ к выражениям формы (§12.8.7) и индексатору (§12.8.12.4) формы I.M и I[A], где I является типом интерфейса, M является константой, полем, методом, свойством или событием этого типа интерфейса и A является списком аргументов индексатора.
В классе Dс прямым или косвенным базовым классом B, где B напрямую или косвенно реализует интерфейс I и I определяет метод M(), выражение base.M() допустимо, только если base.M() статически (§12.3) привязывается к реализации M() в типе класса.
Для интерфейсов, которые строго однонаследование (каждый интерфейс в цепочке наследования имеет ровно ноль или один прямой базовый интерфейс), эффекты подстановки элементов (§12.5), вызов метода (§12.8. 10.2) и правила доступа индексатора (§12.8.12.4) точно совпадают с правилами, что и для классов и структур: более производные элементы скрывают менее производные элементы с тем же именем или сигнатурой. Однако для интерфейса с множественным наследованием может возникать неоднозначность, когда два или более несвязанных базовых интерфейса объявляют члены с одинаковым именем или сигнатурой. В этом пункте показаны несколько примеров: одни из них приводят к неоднозначностям, а другие — нет. Во всех случаях явные приведения можно использовать для устранения неоднозначности.
Пример. В следующем коде
interface IList { int Count { get; set; } } interface ICounter { int Count { get; set; } } interface IListCounter : IList, ICounter {} class C { void Test(IListCounter x) { x.Count = 1; // Error ((IList)x).Count = 1; // Ok, invokes IList.Count.set ((ICounter)x).Count = 1; // Ok, invokes ICounter.Count } }Первая инструкция вызывает ошибку компиляции, так как поиск члена (§12.5)
CountвIListCounterявляется неоднозначным. Как показано в примере, неоднозначность разрешается путем приведенияxк соответствующему типу базового интерфейса. Такие приведения не имеют издержек в процессе выполнения — они просто представляют собой представление экземпляра как менее производный тип во время компиляции.заключительный пример
Пример. В следующем коде
interface IInteger { void Add(int i); } interface IDouble { void Add(double d); } interface INumber : IInteger, IDouble {} class C { void Test(INumber n) { n.Add(1); // Invokes IInteger.Add n.Add(1.0); // Only IDouble.Add is applicable ((IInteger)n).Add(1); // Only IInteger.Add is a candidate ((IDouble)n).Add(1); // Only IDouble.Add is a candidate } }вызов
n.Add(1)выбирается путем применения правил разрешения перегрузкиIInteger.Addсогласно §12.6.4. Аналогичным образом, вызовn.Add(1.0)выбираетIDouble.Add. При вставке явных приведений имеется только один метод-кандидат и, следовательно, нет неоднозначности.заключительный пример
Пример. В следующем коде
interface IBase { void F(int i); } interface ILeft : IBase { new void F(int i); } interface IRight : IBase { void G(); } interface IDerived : ILeft, IRight {} class A { void Test(IDerived d) { d.F(1); // Invokes ILeft.F ((IBase)d).F(1); // Invokes IBase.F ((ILeft)d).F(1); // Invokes ILeft.F ((IRight)d).F(1); // Invokes IBase.F } }
IBase.Fэлемент скрыт элементомILeft.F. Вызовd.F(1), таким образом, выбираетILeft.F, хотяIBase.F, по-видимому, не скрыт в пути доступа, который ведет черезIRight.Интуитивно понятное правило скрытия в интерфейсах с множественным наследованием следующее: если член скрыт в любом из путей доступа, он скрыт во всех путях доступа. Поскольку путь доступа от
IDerivedдоILeftдоIBaseскрываетIBase.F, этот член также скрыт на пути доступа отIDerivedдоIRightдоIBase.заключительный пример
19.5 Квалифицированные имена элементов интерфейса
Иногда член интерфейса называют по его квалифицированному имени элемента интерфейса. Полное имя члена интерфейса состоит из имени интерфейса, в котором объявляется член, затем следует точка, за которой следует имя члена. Полное имя члена ссылается на интерфейс, в котором объявлен член.
Пример: Учитывая объявления
interface IControl { void Paint(); } interface ITextBox : IControl { void SetText(string text); }полное имя
Paint—IControl.Paint, а полное имя SetText —ITextBox.SetText. В приведенном выше примере невозможно ссылаться наPaintкак наITextBox.Paint.заключительный пример
Если интерфейс является частью пространства имен, полное имя члена интерфейса может включать имя пространства имен.
Пример:
namespace GraphicsLib { interface IPolygon { void CalculateArea(); } }В пространстве имен
GraphicsLib, обаIPolygon.CalculateAreaиGraphicsLib.IPolygon.CalculateAreaявляются квалифицированными именами элементов интерфейса для методаCalculateArea.заключительный пример
Реализации интерфейса 19.6
19.6.1 Общие
Интерфейсы могут быть реализованы классами и структурами. Чтобы указать, что класс или структура напрямую реализует интерфейс, интерфейс включается в список базовых классов класса или структуры.
Класс или структура C , реализующая интерфейс I , должна предоставлять или наследовать реализацию для каждого члена, объявленного в I этом C доступе. Общедоступные I члены могут быть определены в общедоступных членах C. Недоступные члены, объявленные I в C них, могут быть определены с C помощью явной реализации интерфейса (§19.6.2).
Элемент в производном типе, удовлетворяющий сопоставлению интерфейсов (§19.6.5), но не реализует соответствующий базовый элемент интерфейса. Это происходит при необходимости явной реализации интерфейса для определения члена интерфейса.
Пример:
interface ICloneable { object Clone(); } interface IComparable { int CompareTo(object other); } class ListEntry : ICloneable, IComparable { public object Clone() {...} public int CompareTo(object other) {...} }заключительный пример
Класс или структура, которая непосредственно реализует интерфейс, также неявно реализует все базовые интерфейсы интерфейса. Это верно, даже если класс или структура явно не перечисляют все базовые интерфейсы в списке базовых классов.
Пример:
interface IControl { void Paint(); } interface ITextBox : IControl { void SetText(string text); } class TextBox : ITextBox { public void Paint() {...} public void SetText(string text) {...} }Здесь класс
TextBoxреализует какIControl, так иITextBox.заключительный пример
Когда класс C напрямую реализует интерфейс, все классы, производные от C этого, также реализуют интерфейс неявно.
Базовые интерфейсы, указанные в объявлении класса, можно создать типы интерфейсов (§8.4, §19.2).
Пример. В следующем коде показано, как класс может реализовать созданные типы интерфейсов:
class C<U, V> {} interface I1<V> {} class D : C<string, int>, I1<string> {} class E<T> : C<int, T>, I1<T> {}заключительный пример
Базовые интерфейсы объявления универсального класса должны соответствовать правилу уникальности, описанному в §19.6.3.
Реализация элементов интерфейса 19.6.2
Для реализации интерфейсов, класса, структуры или интерфейса могут объявлять явные реализации члена интерфейса. Явная реализация элемента интерфейса — это объявление метода, свойства, события или индексатора, ссылающееся на полное имя члена интерфейса. Класс или структура, реализующая недоступный член в базовом интерфейсе, должен объявить явную реализацию члена интерфейса. Интерфейс, реализующий член в базовом интерфейсе, должен объявить явную реализацию элемента интерфейса.
Производный элемент интерфейса, удовлетворяющий сопоставлению интерфейсов (§19.6.5), скрывает базовый элемент интерфейса (§7.7.2). Компилятор должен выдавать предупреждение, если new модификатор не присутствует.
Пример:
interface IList<T> { T[] GetElements(); } interface IDictionary<K, V> { V this[K key] { get; } void Add(K key, V value); } class List<T> : IList<T>, IDictionary<int, T> { public T[] GetElements() {...} T IDictionary<int, T>.this[int index] {...} void IDictionary<int, T>.Add(int index, T value) {...} }Здесь
IDictionary<int,T>.thisиIDictionary<int,T>.Addесть явные реализации элементов интерфейса.заключительный пример
Пример. В некоторых случаях имя члена интерфейса может не соответствовать реализации класса, в этом случае член интерфейса может быть реализован с помощью явной реализации элемента интерфейса. Класс, реализующий абстракцию файлов, например, может реализовать
Closeфункцию-член для освобождения файлового ресурса и реализовать методDisposeинтерфейса с помощью явной реализации члена интерфейсаIDisposable.interface IDisposable { void Dispose(); } class MyFile : IDisposable { void IDisposable.Dispose() => Close(); public void Close() { // Do what's necessary to close the file System.GC.SuppressFinalize(this); } }заключительный пример
Невозможно получить доступ к явной реализации элемента интерфейса с помощью его квалифицированного имени члена интерфейса в вызове метода, доступе к свойствам, доступе к событиям или доступе индексатора. Явная реализация элемента экземпляра интерфейса может быть доступна только через экземпляр интерфейса и в этом случае ссылается просто по имени члена. К явной реализации статического члена интерфейса можно получить доступ только с помощью имени интерфейса.
Ошибка компиляции возникает, если явная реализация члена интерфейса включает любые модификаторы (§15.6), кроме extern или async.
Явная реализация метода интерфейса наследует все ограничения параметров типа из интерфейса.
Type_parameter_constraints_clause в реализации метода явного интерфейса должно состоять только из classstructprimary_constraint, примененных к type_parameter, которые, согласно унаследованным ограничениям, известны как либо ссылочные типы, либо типы значений соответственно. Любой тип формы T? в сигнатуре явной реализации метода интерфейса, где T является параметром типа, интерпретируется следующим образом:
-
classЕсли ограничение добавляется для параметраTтипа, тоT?является ссылочным типом, допускающим значение NULL; в противном случае - Если ограничение не добавлено или добавлено ограничение
structдля параметра типаT, тоT?является значением Nullable.
Пример. Ниже показано, как работают правила при использовании параметров типа:
#nullable enable interface I { void Foo<T>(T? value) where T : class; void Foo<T>(T? value) where T : struct; } class C : I { void I.Foo<T>(T? value) where T : class { } void I.Foo<T>(T? value) where T : struct { } }Без ограничения параметра типа
where T : class, базовый метод с параметром ссылочного типа нельзя переопределить. заключительный пример
Примечание. Явные реализации членов интерфейса имеют другие характеристики доступности по сравнению с другими членами. Так как явные реализации элементов интерфейса никогда не доступны через полное имя члена интерфейса в вызове метода или доступе к свойству, они по сути являются приватными. Тем не менее, поскольку они могут быть доступны через интерфейс, они по сути так же публичны, как и сам интерфейс, в котором они объявлены. Явные реализации элементов интерфейса служат двумя основными целями:
- Так как явные реализации элементов интерфейса недоступны через экземпляры классов или структур, они позволяют исключить реализации интерфейса из общедоступного интерфейса класса или структуры. Это особенно полезно, если класс или структура реализует внутренний интерфейс, который не является интересом для потребителя этого класса или структуры.
- Явные реализации элементов интерфейса позволяют дискредитировать членов интерфейса с той же сигнатурой. Без явных реализаций элементов интерфейса было бы невозможно для класса, структуры или интерфейса иметь различные реализации элементов интерфейса с одинаковым сигнатурой и типом возврата, так как было бы невозможно для класса, структуры или интерфейса иметь любую реализацию во всех элементах интерфейса с одной и той же сигнатурой, но с различными типами возвращаемых данных.
конечная заметка
Чтобы явная реализация элемента интерфейса была допустимой, класс, структура или интерфейс должен называть интерфейс в его базовом классе или списке базовых интерфейсов, который содержит член, имя члена которого, тип, число параметров типа и типы параметров точно соответствуют типу реализации явного элемента интерфейса. Если член интерфейса имеет массив параметров, то соответствующий параметр реализации связанного явного интерфейса может, но не обязан, иметь модификатор params. Если элемент функции интерфейса не имеет массив параметров, то связанная явная реализация элемента интерфейса не должна иметь массив параметров.
Пример: Таким образом, в следующем классе
class Shape : ICloneable { object ICloneable.Clone() {...} int IComparable.CompareTo(object other) {...} // invalid }Объявление
IComparable.CompareToприводит к ошибке компиляции, потому чтоIComparableне указано в списке базовых классовShapeи не является базовым интерфейсомICloneable. Аналогичным образом, в объявленияхclass Shape : ICloneable { object ICloneable.Clone() {...} } class Ellipse : Shape { object ICloneable.Clone() {...} // invalid }Объявление
ICloneable.CloneвEllipseвызывает ошибку времени компиляции, так какICloneableне указано явно в списке базовых классовEllipse.заключительный пример
Квалифицированное имя члена интерфейса для реализации члена интерфейса с указанной явной принадлежностью должно указывать на интерфейс, в котором был объявлен этот член.
Пример: Таким образом, в объявлениях
interface IControl { void Paint(); } interface ITextBox : IControl { void SetText(string text); } class TextBox : ITextBox { void IControl.Paint() {...} void ITextBox.SetText(string text) {...} }Явная реализация элемента интерфейса Paint должна быть записана как
IControl.Paint, а неITextBox.Paint.заключительный пример
19.6.3 Уникальность реализованных интерфейсов
Интерфейсы, реализованные объявлением универсального типа, должны оставаться уникальными для всех возможных созданных типов. Без этого правила невозможно определить правильный метод для вызова определенных созданных типов.
Пример. Предположим, что объявление универсального класса было разрешено писать следующим образом:
interface I<T> { void F(); } class X<U ,V> : I<U>, I<V> // Error: I<U> and I<V> conflict { void I<U>.F() {...} void I<V>.F() {...} }Если это разрешено, невозможно определить, какой код будет выполняться в следующем случае:
I<int> x = new X<int, int>(); x.F();заключительный пример
Чтобы определить, является ли список интерфейсов объявления универсального типа допустимым, выполняются следующие действия.
- Пусть
Lбудет списком интерфейсов, непосредственно указанных в универсальном классе, структуре или интерфейсеC. - Добавьте в
Lлюбые базовые интерфейсы из уже имеющихся вL. - Удалите все дубликаты из
L. - Если любой возможный тип, созданный из
C, после подстановки аргументов типа вL, делает два интерфейса вLидентичными, то объявлениеCнедействительно. Объявления ограничений не учитываются при определении всех возможных созданных типов.
Примечание. В приведенном выше объявлении
Xкласса списокLинтерфейсов состоит изl<U>иI<V>. Объявление недопустимо, так как любой созданный тип сUVодинаковым типом приведет к тому, что эти два интерфейса будут идентичными типами. конечная заметка
Интерфейсы, указанные на разных уровнях наследования, можно объединить:
interface I<T>
{
void F();
}
class Base<U> : I<U>
{
void I<U>.F() {...}
}
class Derived<U, V> : Base<U>, I<V> // Ok
{
void I<V>.F() {...}
}
Этот код действителен, даже если Derived<U,V> реализует оба I<U> и I<V>. Код
I<int> x = new Derived<int, int>();
x.F();
вызывает метод в Derived, так как Derived<int,int>' эффективно повторно реализует I<int> (§19.6.7).
19.6.4 Реализация универсальных методов
Если универсальный метод неявно реализует метод интерфейса, ограничения, заданные для каждого параметра типа метода, должны быть эквивалентны в обоих объявлениях (после замены любых параметров типа интерфейса соответствующими аргументами типа), где параметры типа метода определяются порядковых позиций слева направо.
Пример: в следующем коде:
interface I<X, Y, Z> { void F<T>(T t) where T : X; void G<T>(T t) where T : Y; void H<T>(T t) where T : Z; } class C : I<object, C, string> { public void F<T>(T t) {...} // Ok public void G<T>(T t) where T : C {...} // Ok public void H<T>(T t) where T : string {...} // Error }метод
C.F<T>неявноI<object,C,string>.F<T>реализует. В этом случае не требуется (и не разрешено) указывать ограничениеC.F<T>, посколькуT: objectэто неявное ограничение для всех параметров типа. МетодC.G<T>неявно реализуетсяI<object,C,string>.G<T>, так как ограничения соответствуют ограничениям в интерфейсе после замены параметров типа интерфейса соответствующими аргументами типа. Ограничение методаC.H<T>является ошибкой, так как в этом случае не могут использоваться в качестве ограничений запечатанные типы (stringв данном случае). Опущение ограничения также будет ошибкой, так как ограничения реализации неявного метода интерфейса необходимы для сопоставления. Таким образом, неявно реализоватьI<object,C,string>.H<T>невозможно. Этот метод интерфейса можно реализовать только с помощью явной реализации члена интерфейса:class C : I<object, C, string> { ... public void H<U>(U u) where U : class {...} void I<object, C, string>.H<T>(T t) { string s = t; // Ok H<T>(t); } }В этом случае явная реализация элемента интерфейса вызывает открытый метод с строго слабыми ограничениями. Назначение от t к s допустимо, так как
Tнаследует ограничениеT: string, даже если это ограничение не является явным в исходном коде. заключительный пример
Примечание. Если универсальный метод явно реализует метод интерфейса, ограничения не допускаются для метода реализации (§15.7.1, §19.6.2). конечная заметка
Сопоставление интерфейсов 19.6.5
Класс или структура должны предоставлять реализации всех абстрактных элементов интерфейсов, перечисленных в списке базовых классов класса или структуры. Процесс поиска реализаций элементов интерфейса в реализации класса или структуры называется сопоставлением интерфейсов.
Сопоставление интерфейсов для класса или структуры C находит реализацию для каждого члена каждого интерфейса, указанного в списке базовых Cклассов. Реализация определенного элемента интерфейса, в котором I.M находится интерфейс, в котором объявлен членIM, определяется путем изучения каждого класса, интерфейса или структурыS, начиная с C каждого последовательного базового класса и реализованного интерфейса Cдо тех пор, пока совпадение не будет установлено:
- Если
Sсодержит объявление явной реализации элемента интерфейса, соответствующегоI, иMэтот элемент является реализациейI.M. - В противном случае, если
Sсодержит объявление нестатитического общедоступного члена, соответствующегоM, этот элемент является реализациейI.M. Если несколько элементов совпадают, не указано, какой элемент является реализациейI.M. Эта ситуация может возникать только в том случае, еслиSэто созданный тип, в котором два члена, объявленные в универсальном типе, имеют разные подписи, но аргументы типа делают их подписи идентичными.
Ошибка во время компиляции возникает, если реализации не могут находиться для всех элементов всех интерфейсов, указанных в списке Cбазовых классов. Члены интерфейса включают те элементы, которые наследуются от базовых интерфейсов.
Члены созданного типа интерфейса рассматриваются так, будто параметры типа заменены соответствующими аргументами типа, как указано в §15.3.3.
Пример: например, учитывая объявление универсального интерфейса:
interface I<T> { T F(int x, T[,] y); T this[int y] { get; } }Созданный интерфейс
I<string[]>содержит элементы:string[] F(int x, string[,][] y); string[] this[int y] { get; }заключительный пример
Для сопоставления интерфейсов класс, интерфейс или член A структуры соответствует члену B интерфейса при следующем:
-
AиBявляются методами, а также именами, типами и спискамиAпараметров иBидентичными. -
AиBявляются свойствами, именем и типомAиBидентичными и имеют те же методы доступа, чтоAиB(Aразрешено иметь дополнительные методы доступа, если он не является явной реализацией элемента интерфейса). -
AиBявляются событиями, а имя и типAиBидентичны. -
AиBявляются индексаторами, спискиABтипов и параметров идентичны и имеют те же методы доступа, чтоAиB(Aразрешено иметь дополнительные методы доступа, если он не является явной реализацией элемента интерфейса).
Важные последствия алгоритма сопоставления интерфейсов:
- Явные реализации элементов интерфейса имеют приоритет над другими членами того же класса или структуры при определении класса или элемента структуры, реализующего член интерфейса.
- Ни не-открытые, ни статические члены не участвуют в сопоставлении интерфейсов.
Пример. В следующем коде
interface ICloneable { object Clone(); } class C : ICloneable { object ICloneable.Clone() {...} public object Clone() {...} }
ICloneable.CloneчленCстановится реализациейCloneвICloneable, так как явные реализации элементов интерфейса имеют приоритет над другими членами.заключительный пример
Если класс или структура реализует два или более интерфейсов, содержащих член с одинаковым именем, типом и типами параметров, можно сопоставить каждый из этих элементов интерфейса с одним классом или элементом структуры.
Пример:
interface IControl { void Paint(); } interface IForm { void Paint(); } class Page : IControl, IForm { public void Paint() {...} }
PaintЗдесь методы обоихIControlиIFormсопоставляются с методомPaintPage. Конечно, можно также иметь отдельные явные реализации элементов интерфейса для двух методов.заключительный пример
Если класс или структура реализует интерфейс, содержащий скрытые элементы, некоторые элементы могут быть реализованы с помощью явных реализаций элементов интерфейса.
Пример:
interface IBase { int P { get; } } interface IDerived : IBase { new int P(); }Реализация этого интерфейса потребует по крайней мере одной явной реализации члена интерфейса и будет принимать одну из следующих форм.
class C1 : IDerived { int IBase.P { get; } int IDerived.P() {...} } class C2 : IDerived { public int P { get; } int IDerived.P() {...} } class C3 : IDerived { int IBase.P { get; } public int P() {...} }заключительный пример
Если класс реализует несколько интерфейсов, имеющих один базовый интерфейс, может быть только одна реализация базового интерфейса.
Пример. В следующем коде
interface IControl { void Paint(); } interface ITextBox : IControl { void SetText(string text); } interface IListBox : IControl { void SetItems(string[] items); } class ComboBox : IControl, ITextBox, IListBox { void IControl.Paint() {...} void ITextBox.SetText(string text) {...} void IListBox.SetItems(string[] items) {...} }Невозможно иметь отдельные реализации для
IControl, указанного в списке базовых классов,IControl, унаследованногоITextBox, иIControl, унаследованногоIListBox. Действительно, для этих интерфейсов нет понятия отдельной идентичности. Скорее, реализацииITextBoxиIListBoxиспользуют одну и ту же реализациюIControl, аComboBoxпросто считается реализацией трех интерфейсов:IControl,ITextBoxиIListBox.заключительный пример
Члены базового класса участвуют в сопоставлении интерфейсов.
Пример. В следующем коде
interface Interface1 { void F(); } class Class1 { public void F() {} public void G() {} } class Class2 : Class1, Interface1 { public new void G() {} }Метод
Fиспользуется вClass1для реализацииClass2'sInterface1.заключительный пример
Наследование реализации интерфейса 19.6.6
Класс наследует все реализации интерфейса, предоставляемые его базовыми классами.
Без явной повторной реализации интерфейса производный класс не может изменять сопоставления интерфейсов, наследуемых от его базовых классов.
Пример. В объявлениях
interface IControl { void Paint(); } class Control : IControl { public void Paint() {...} } class TextBox : Control { public new void Paint() {...} }
Paintметод вTextBoxскрываетPaintметод вControl, но он не изменяет сопоставлениеControl.PaintнаIControl.Paint, а вызовыPaintчерез экземпляры классов и экземпляры интерфейсов будут иметь следующие эффекты.Control c = new Control(); TextBox t = new TextBox(); IControl ic = c; IControl it = t; c.Paint(); // invokes Control.Paint(); t.Paint(); // invokes TextBox.Paint(); ic.Paint(); // invokes Control.Paint(); it.Paint(); // invokes Control.Paint();заключительный пример
Однако, когда метод интерфейса сопоставляется с виртуальным методом в классе, для производных классов становится возможным переопределить виртуальный метод и изменить реализацию интерфейса.
Пример: Переписывание объявлений выше в
interface IControl { void Paint(); } class Control : IControl { public virtual void Paint() {...} } class TextBox : Control { public override void Paint() {...} }Теперь будут наблюдаться следующие эффекты.
Control c = new Control(); TextBox t = new TextBox(); IControl ic = c; IControl it = t; c.Paint(); // invokes Control.Paint(); t.Paint(); // invokes TextBox.Paint(); ic.Paint(); // invokes Control.Paint(); it.Paint(); // invokes TextBox.Paint();заключительный пример
Так как явные реализации элементов интерфейса не могут быть объявлены виртуальными, невозможно переопределить явную реализацию члена интерфейса. Тем не менее, он является совершенно допустимым для явной реализации члена интерфейса для вызова другого метода, и что другой метод может быть объявлен виртуальным, чтобы разрешить производным классам переопределить его.
Пример:
interface IControl { void Paint(); } class Control : IControl { void IControl.Paint() { PaintControl(); } protected virtual void PaintControl() {...} } class TextBox : Control { protected override void PaintControl() {...} }Здесь классы, производные от
Controlэтого, могут специализировать реализациюIControl.Paintпутем переопределенияPaintControlметода.заключительный пример
Повторное внедрение интерфейса 19.6.7
Класс, наследующий реализацию интерфейса, может повторно реализовать интерфейс, включив его в список базовых классов.
Повторная реализация интерфейса соответствует точно тем же правилам сопоставления интерфейсов, что и начальная реализация интерфейса. Таким образом, сопоставление унаследованных интерфейсов не влияет ни на какое из сопоставлений интерфейсов, установленных для повторной реализации интерфейса.
Пример. В объявлениях
interface IControl { void Paint(); } class Control : IControl { void IControl.Paint() {...} } class MyControl : Control, IControl { public void Paint() {} }Тот факт, что
ControlотображаетIControl.PaintнаControl.IControl.Paint, не влияет на повторную реализацию вMyControl, которая отображаетIControl.PaintнаMyControl.Paint.заключительный пример
Унаследованные объявления открытых членов и унаследованные явные объявления членов интерфейса участвуют в процессе сопоставления интерфейсов для повторно реализуемых интерфейсов.
Пример:
interface IMethods { void F(); void G(); void H(); void I(); } class Base : IMethods { void IMethods.F() {} void IMethods.G() {} public void H() {} public void I() {} } class Derived : Base, IMethods { public void F() {} void IMethods.H() {} }Здесь реализация
IMethodsвDerivedотображает методы интерфейса наDerived.F,Base.IMethods.G,Derived.IMethods.HиBase.I.заключительный пример
Когда класс реализует интерфейс, он неявно реализует все базовые интерфейсы интерфейса. Аналогичным образом, повторная реализация интерфейса также неявно является повторной реализацией всех базовых интерфейсов интерфейса.
Пример:
interface IBase { void F(); } interface IDerived : IBase { void G(); } class C : IDerived { void IBase.F() {...} void IDerived.G() {...} } class D : C, IDerived { public void F() {...} public void G() {...} }Здесь повторная реализация
IDerivedтакже повторно реализуетIBase, тем самым сопоставляяIBase.FсD.F.заключительный пример
19.6.8 Абстрактные классы и интерфейсы
Как и не абстрактный класс, абстрактный класс должен предоставлять реализации всех абстрактных элементов интерфейсов, перечисленных в списке базовых классов класса. Однако абстрактный класс позволяет сопоставить методы интерфейса с абстрактными методами.
Пример:
interface IMethods { void F(); void G(); } abstract class C : IMethods { public abstract void F(); public abstract void G(); }Здесь реализация
IMethodsотображаетFиGна абстрактные методы, которые должны быть переопределены в неабстрактных классах, производных отC.заключительный пример
Явные реализации элементов интерфейса не могут быть абстрактными, но явные реализации элементов интерфейса, конечно, разрешены вызывать абстрактные методы.
Пример:
interface IMethods { void F(); void G(); } abstract class C: IMethods { void IMethods.F() { FF(); } void IMethods.G() { GG(); } protected abstract void FF(); protected abstract void GG(); }Здесь неабстрактные классы, производные от
C, должны переопределитьFFиGG, таким образом предоставляя фактическую реализациюIMethods.заключительный пример
ECMA C# draft specification