Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
19.1 Geral
Uma interface define um contrato. Uma classe ou struct que implementa uma interface deve aderir ao seu contrato. Uma interface pode herdar de várias interfaces base e uma classe ou struct pode implementar várias interfaces.
As interfaces podem conter vários tipos de membros, conforme descrito em §19.4. A interface em si pode fornecer uma implementação para alguns ou todos os membros da função que ele declara. Os membros para os quais a interface não fornece uma implementação são abstratos. Suas implementações devem ser fornecidas por classes ou structs que implementam a interface ou interface derivada que fornecem uma definição de substituição.
Observação: historicamente, a adição de um novo membro de função a uma interface impactou todos os consumidores existentes desse tipo de interface; foi uma alteração significativa. A adição de implementações de membro de função de interface permitiu que os desenvolvedores atualizassem uma interface, permitindo que os implementadores substituíssem essa implementação. Os usuários da interface podem aceitar a implementação como uma alteração sem interrupção; no entanto, se seus requisitos forem diferentes, eles poderão substituir as implementações fornecidas. nota final
19.2 Declarações de interface
19.2.1 Geral
Um interface_declaration é um type_declaration (§14.7) que declara um novo tipo de interface.
interface_declaration
: attributes? interface_modifier* 'partial'? 'interface'
identifier variant_type_parameter_list? interface_base?
type_parameter_constraints_clause* interface_body ';'?
;
Um interface_declaration consiste em um conjunto opcional de atributos (§23), seguido por um conjunto opcional de interface_modifiers (§19.2.2), seguido por um modificador parcial opcional (§15.2.7), seguido pela palavra-chave interface e um identificador que nomeia a interface, seguido por uma especificação de variant_type_parameter_list opcional (§19.2.3), seguida por uma especificação de interface_base opcional (§19.2.4), seguido por uma especificação de type_parameter_constraints_clauseopcional (§15.2.5), seguida por um interface_body (§19.3), opcionalmente seguido por um ponto e vírgula.
Uma declaração de interface não fornecerá type_parameter_constraints_clauses, a menos que ela também forneça uma variant_type_parameter_list.
Uma declaração de interface que fornece um variant_type_parameter_list é uma declaração de interface genérica. Além disso, qualquer interface aninhada dentro de uma declaração de classe genérica ou uma declaração de interface genérica é em si uma declaração de struct genérica, uma vez que os argumentos de tipo para o tipo que o contém devem ser fornecidos para criar um tipo construído (§8.4).
Modificadores de interface 19.2.2
Um interface_declaration pode incluir opcionalmente uma sequência de modificadores de interface:
interface_modifier
: 'new'
| 'public'
| 'protected'
| 'internal'
| 'private'
| unsafe_modifier // unsafe code support
;
unsafe_modifier (§24.2) só está disponível em código não seguro (§24).
É um erro de tempo de compilação para o mesmo modificador aparecer várias vezes em uma declaração de interface.
O new modificador só é permitido em interfaces definidas dentro de uma classe. Ele especifica que a interface oculta um membro herdado com o mesmo nome, conforme descrito em §15.3.5.
Os publicmodificadores , protected, internale private controlam a acessibilidade da interface. Dependendo do contexto em que a declaração de interface ocorre, apenas alguns desses modificadores podem ser permitidos (§7.5.2). Quando uma declaração de tipo parcial (§15.2.7) inclui uma especificação de acessibilidade (por meio dos publicmodificadores , protected, internal, e private ), as regras em §15.2.2 se aplicam.
Listas de parâmetros do tipo Variant 19.2.3
19.2.3.1 Geral
As listas de parâmetros de tipo de variante só podem ocorrer em tipos de interface e tipos de delegados. A diferença dos type_parameter_lists comuns é a variance_annotation opcional em cada parâmetro de tipo.
variant_type_parameter_list
: '<' variant_type_parameter (',' variant_type_parameter)* '>'
;
variant_type_parameter
: attributes? variance_annotation? type_parameter
;
variance_annotation
: 'in'
| 'out'
;
Se a anotação de variância for out, o parâmetro de tipo é considerado covariante. Se a anotação de variância for in, o parâmetro de tipo é considerado contravariante. Se não houver anotação de variância, o parâmetro de tipo é considerado invariável.
Exemplo: No seguinte:
interface C<out X, in Y, Z> { X M(Y y); Z P { get; set; } }
Xé covariante,Yé contravariante e é invarianteZ.fim de exemplo
Se uma interface genérica for declarada em várias partes (§15.2.3), cada declaração parcial deverá especificar a mesma variação para cada parâmetro de tipo.
19.2.3.2 Segurança de variação
A ocorrência de anotações de variação na lista de parâmetros de tipo de um tipo restringe os locais onde os tipos podem ocorrer dentro da declaração de tipo.
Um tipo T é inseguro de saída se um dos seguintes for válido:
-
Té um parâmetro do tipo contravariante -
Té um tipo de matriz com um tipo de elemento inseguro de saída -
Té um tipoSᵢ,... Aₑde interface ou delegado construído a partir de um tipoS<Xᵢ, ... Xₑ>genérico onde, para pelo menos umAᵢ, uma das seguintes condições se aplica:-
Xᵢé covariante ou invariante eAᵢnão é seguro para a saída. -
Xᵢé contravariante ou invariante eAᵢnão é seguro para a entrada.
-
Um tipo T é inseguro para entrada se uma das seguintes condições for atendida:
-
Té um parâmetro de tipo covariante -
Té um tipo de matriz com um tipo de elemento inseguro de entrada -
Té um tipoS<Aᵢ,... Aₑ>de interface ou delegado construído a partir de um tipoS<Xᵢ, ... Xₑ>genérico onde, para pelo menos umAᵢ, uma das seguintes condições se aplica:-
Xᵢé covariante ou invariante eAᵢnão é seguro para a entrada. -
Xᵢé contravariante ou invariante eAᵢnão é seguro para a saída.
-
Intuitivamente, um tipo inseguro de saída é proibido em uma posição de saída e um tipo inseguro de entrada é proibido em uma posição de entrada.
Um tipo é seguro para saída se não for inseguro para saída e seguro para entrada se não for inseguro para entrada.
19.2.3.3 Conversão de variação
A finalidade das anotações de variância é proporcionar conversões mais brandas (mas ainda seguras em termos de tipo) para tipos de interface e delegados. Para esse fim, as definições de conversões implícitas (§10.2) e explícitas (§10.3) fazem uso da noção de variância-conversibilidade, que é definida da seguinte forma:
Um tipo T<Aᵢ, ..., Aᵥ> é conversível por variância em um tipo T<Bᵢ, ..., Bᵥ> se T for uma interface ou um tipo delegado declarado com os parâmetros T<Xᵢ, ..., Xᵥ>de tipo variante e, para cada parâmetro Xᵢ de tipo variante, um dos seguintes é válido:
-
Xᵢé covariante e existe uma referência implícita ou conversão de identidade deAᵢparaBᵢ -
Xᵢé contravariante e existe uma referência implícita ou conversão de identidade deBᵢparaAᵢ -
Xᵢé invariante e existe uma conversão de identidade deAᵢparaBᵢ
19.2.4 Interfaces base
Uma interface pode herdar de zero ou mais tipos de interface, que são chamados de interfaces base explícitasda interface. Quando uma interface tem uma ou mais interfaces base explícitas, na declaração dessa interface, o identificador de interface é seguido por dois pontos e uma lista separada por vírgulas de tipos de interface base.
Uma interface derivada pode declarar novos membros que ocultam membros herdados (§7.7.2.3) declarados em interfaces base ou implementam explicitamente membros herdados (§19.6.2) declarados em interfaces base.
interface_base
: ':' interface_type_list
;
As interfaces base explícitas podem ser tipos de interface construídos (§8.4, §19.2). Uma interface base não pode ser um parâmetro de tipo por conta própria, embora possa envolver os parâmetros de tipo que estão no escopo.
Para um tipo de interface construída, as interfaces base explícitas são formadas tomando as declarações explícitas das interfaces base na declaração de tipo genérico, e substituindo cada parâmetro_de_tipo na declaração da interface base pelo correspondente argumento_de_tipo do tipo construído.
As interfaces de base explícitas de uma interface devem ser pelo menos tão acessíveis quanto a própria interface (§7.5.5).
Observação: por exemplo, é um erro em tempo de compilação especificar uma interface
privateouinternalno interface_base de umapublicinterface. nota final
Ocorre um erro de tempo de compilação quando uma interface herda direta ou indiretamente de si mesma.
As interfaces basede uma interface são as interfaces base explícitas e suas interfaces base. Em outras palavras, o conjunto de interfaces de base é o fechamento transitivo completo das interfaces de base explícitas, das interfaces de base explícitas delas e assim por diante. Uma interface herda todos os membros das suas interfaces de base.
Exemplo: no código a seguir
interface IControl { void Paint(); } interface ITextBox : IControl { void SetText(string text); } interface IListBox : IControl { void SetItems(string[] items); } interface IComboBox: ITextBox, IListBox {}As interfaces básicas de
IComboBoxsãoIControl,ITextBoxeIListBox. Em outras palavras, a interfaceIComboBoxacima herda os membrosSetTexteSetItems, bem comoPaint.fim de exemplo
Os membros herdados de um tipo genérico construído são herdados depois da substituição dos tipos. Ou seja, todos os tipos constituintes no membro têm os parâmetros de tipo da declaração de classe base substituídos pelos argumentos de tipo correspondentes usados na especificação class_base .
Exemplo: no código a seguir
interface IBase<T> { T[] Combine(T a, T b); } interface IDerived : IBase<string[,]> { // Inherited: string[][,] Combine(string[,] a, string[,] b); }A interface
IDerivedherda o métodoCombineapós o parâmetro de tipoTser substituído porstring[,].fim de exemplo
Uma classe ou struct que implementa uma interface também implementa implicitamente todas as interfaces base da interface.
O tratamento de interfaces em várias partes de uma declaração de interface parcial (§15.2.7) é discutido mais adiante em §15.2.4.3.
Cada interface base de uma interface deve ser segura de saída (§19.2.3.2).
19.3 Corpo da interface
O interface_body de uma interface define os membros da interface.
interface_body
: '{' interface_member_declaration* '}'
;
19.4 Membros da interface
19.4.1 Geral
Os membros de uma interface são os membros herdados das interfaces base e os membros declarados pela própria interface.
interface_member_declaration
: constant_declaration
| field_declaration
| method_declaration
| property_declaration
| event_declaration
| indexer_declaration
| static_constructor_declaration
| operator_declaration
| type_declaration
;
Essa cláusula aumenta a descrição de membros em classes (§15.3) com restrições para interfaces. Os membros da interface são declarados usando member_declarations com as seguintes regras adicionais:
- Uma finalizer_declaration não é permitida.
- Construtores de instância, constructor_declarations, não são permitidos.
- Todos os membros da interface têm acesso público implicitamente; no entanto, um modificador de acesso explícito (§7.5.2) é permitido, exceto em construtores estáticos (§15.12).
- O
abstractmodificador está implícito para membros da função de interface sem corpos; esse modificador pode ser fornecido explicitamente. - Um membro da função de instância de interface cuja declaração inclui um corpo é um membro implicitamente
virtual, a menos que osealedmodificador sejaprivateusado. Ovirtualmodificador pode ser fornecido explicitamente. - Um
privatemembro ousealedmembro de função de uma interface deve ter um corpo. - Um
privatemembro de função não deve ter o modificadorsealed. - Uma interface derivada pode substituir um membro abstrato ou virtual declarado em uma interface base.
- Um membro da função explicitamente implementado não deve ter o modificador
sealed.
Algumas declarações, como constant_declaration (§15.4) não têm restrições nas interfaces.
Os membros herdados de uma interface não fazem parte especificamente do espaço de declaração da interface. Assim, uma interface tem permissão para declarar um membro com o mesmo nome ou assinatura de um membro herdado. Quando isso ocorre, diz-se que o membro da interface derivada oculta o membro da interface base. Ocultar um membro herdado não é considerado um erro, mas resulta em um aviso (§7.7.2.3).
Se um modificador new for incluído em uma declaração que não oculte um membro herdado disponível, um aviso é emitido sobre isso.
Observação: os membros da classe
objectnão são, estritamente falando, membros de qualquer interface (§19.4). No entanto, os membros da classeobjectestão disponíveis por meio da pesquisa de membros em qualquer tipo de interface (§12.5). nota final
O conjunto de membros de uma interface declarada em várias partes (§15.2.7) é a união dos membros declarados em cada parte. Os corpos de todas as partes da declaração de interface compartilham o mesmo espaço de declaração (§7.3) e o escopo de cada membro (§7.7) se estende aos corpos de todas as partes.
Exemplo: considere uma interface
IAcom uma implementação para um membroMe uma propriedadeP. Um tipoCde implementação não fornece uma implementação para qualquer umMouP. Eles devem ser acessados por meio de uma referência cujo tipo de tempo de compilação é uma interface implicitamente conversível paraIAouIB. Esses membros não são encontrados por meio da pesquisa de membro em uma variável do tipoC.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 } }Dentro das interfaces
IAeIB, o membroMé acessível diretamente pelo nome. No entanto, dentro do métodoMain, não podemos escreverc.M()ouc.P, pois esses nomes não estão visíveis. Para encontrá-los, são necessárias conversões para o tipo de interface apropriado. A declaração emMIBusa a sintaxe de implementação de interface explícita. Isso é necessário para fazer com que esse método substitua aquele emIA; o modificadoroverridepode não ser aplicado a um membro da função. fim de exemplo
19.4.2 Campos de interface
Essa cláusula aumenta a descrição dos campos nas classes §15.5 para campos declarados em interfaces.
Os campos de interface são declarados usando field_declarations (§15.5.1) com as seguintes regras adicionais:
- É um erro de tempo de compilação para field_declaration declarar um campo de instância.
Exemplo: o programa a seguir contém membros estáticos de vários tipos:
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}"); } }A saída produzida é
static members initialized constant = 100, field = 0 static constructor has run constant = 100, field = 50fim de exemplo
Consulte o §19.4.8 para obter informações sobre a alocação e a inicialização de campos estáticos.
19.4.3 Métodos de interface
Essa cláusula aumenta a descrição dos métodos nas classes §15.6 para métodos declarados em interfaces.
Métodos de interface são declarados usando method_declarations (§15.6)). Os atributos, return_type, ref_return_type, identificador e parameter_list de uma declaração de método de interface têm o mesmo significado que os de uma declaração de método em uma classe. Os métodos de interface têm as seguintes regras adicionais:
method_modifier não deve incluir
override.Um método cujo corpo é um ponto e vírgula (
;) éabstract; oabstractmodificador não é necessário, mas é permitido.Uma declaração de método de interface que tem um corpo de bloco ou corpo de expressão como um method_body é
virtual; ovirtualmodificador não é necessário, mas é permitido.Um method_declaration não deve ter type_parameter_constraints_clausea menos que também tenha um type_parameter_list.
A lista de requisitos para combinações válidas de modificadores declaradas para um método de classe é estendida, da seguinte maneira:
- Uma declaração estática que não é extern deve ter um corpo de bloco ou corpo de expressão como um method_body.
- Uma declaração virtual que não é extern deve ter um corpo de bloco ou corpo de expressão como um method_body.
- Uma declaração privada que não é extern deve ter um corpo de bloco ou corpo de expressão como um method_body.
- Uma declaração lacrada que não é extern deve ter um corpo de bloco ou corpo de expressão como um method_body.
- Uma declaração assíncrona deve ter um corpo de bloco ou corpo de expressão como um method_body.
Todos os tipos de parâmetro de um método de interface devem ser seguros de entrada (§19.2.3.2) e o tipo de retorno deve ser seguro
voidou de saída.Qualquer tipo de parâmetro de saída ou referência também deve ser seguro de saída.
Observação: os parâmetros de saída devem ser seguros para entrada devido a restrições comuns de implementação. nota final
Cada restrição de tipo de classe, restrição de tipo de interface e restrição de parâmetro de tipo em qualquer parâmetro de tipo do método deve ser seguro de entrada.
Essas regras garantem que qualquer uso covariante ou contravariante da interface tenha um tipo seguro.
Exemplo:
interface I<out T> { void M<U>() where U : T; // Error }está mal formado porque o uso de
Tcomo uma restrição de parâmetro de tipo emUnão é seguro para entrada.Se essa restrição não estivesse em vigor, seria possível violar a segurança de tipo da seguinte maneira:
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>();Na verdade, isso é uma chamada para
C.M<E>. Mas essa chamada requer queEderive deD, então a segurança de tipo seria violada aqui.fim de exemplo
Observação: consulte §19.4.2 para obter um exemplo que não só mostra um método estático com uma implementação, mas como esse método é chamado
Maine tem o tipo de retorno e a assinatura corretos, ele também é um ponto de entrada. nota final
Um método virtual com implementação declarada em uma interface pode ser substituído para ser abstrato em uma interface derivada. Isso é conhecido como reabstração.
Exemplo:
interface IA { void M() { Console.WriteLine("IA.M"); } } interface IB: IA { abstract void IA.M(); // reabstraction of M }Isso é útil em interfaces derivadas em que a implementação de um método é inadequada e uma implementação mais apropriada deve ser fornecida implementando classes. fim de exemplo
19.4.4 Propriedades da interface
Essa cláusula aumenta a descrição das propriedades nas classes §15.7 para propriedades declaradas em interfaces.
As propriedades da interface são declaradas usando property_declarations (§15.7.1) com as seguintes regras adicionais:
property_modifier não deve incluir
override.Uma implementação de membro de interface explícita não deve conter um accessor_modifier (§15.7.3).
Uma interface derivada pode implementar explicitamente uma propriedade de interface abstrata declarada em uma interface base.
Observação: como uma interface não pode conter campos de instância, uma propriedade de interface não pode ser uma propriedade automática de instância, pois isso exigiria a declaração de campos de instância ocultos implícitos. nota final
O tipo de uma propriedade de interface deve ser seguro para saída se houver um acessador get e deve ser seguro para entrada se houver um acessador definido.
Uma declaração de método de interface que tem um corpo de bloco ou corpo de expressão como um method_body é
virtual; ovirtualmodificador não é necessário, mas é permitido.Uma instância property_declaration que não tem nenhuma implementação é
abstract; oabstractmodificador não é necessário, mas é permitido. Nunca é considerado uma propriedade implementada automaticamente (§15.7.4).
19.4.5 Eventos de interface
Essa cláusula aumenta a descrição dos eventos nas classes §15.8 para eventos declarados em interfaces.
Os eventos de interface são declarados usando event_declarations (§15.8.1), com as seguintes regras adicionais:
-
event_modifier não deve incluir
override. - Uma interface derivada pode implementar um evento de interface abstrato declarado em uma interface base (§15.8.5).
- É um erro de tempo de compilação para variable_declarators em uma instância event_declaration conter qualquer variable_initializers.
- Um evento de instância com os
virtualmodificadores ousealedmodificadores deve declarar acessadores. Nunca é considerado um evento semelhante a um campo implementado automaticamente (§15.8.2). - Um evento de instância com o
abstractmodificador não deve declarar acessadores. - O tipo de um evento de interface deve ser seguro para entrada.
19.4.6 Indexadores de interface
Essa cláusula aumenta a descrição dos indexadores nas classes §15.9 para indexadores declarados em interfaces.
Os indexadores de interface são declarados usando indexer_declarations (§15.9), com as seguintes regras adicionais:
indexer_modifier não deve incluir
override.Um indexer_declaration que tem um corpo de expressão ou contém um acessador com um corpo de bloco ou corpo de expressão é
virtual; ovirtualmodificador não é necessário, mas é permitido.Um indexer_declaration cujos corpos acessadores são ponto e vírgula (
;) éabstract; oabstractmodificador não é necessário, mas é permitido.Todos os tipos de parâmetro de um indexador de interface devem ser seguros de entrada (§19.2.3.2).
Qualquer tipo de parâmetro de saída ou referência também deve ser seguro de saída.
Observação: os parâmetros de saída devem ser seguros para entrada devido a restrições comuns de implementação. nota final
O tipo de um indexador de interface deve ser seguro para saída se houver um acessador get e deve ser seguro para entrada se houver um acessador definido.
19.4.7 Operadores de interface
Essa cláusula aumenta a descrição de membros operator_declaration nas classes §15.10 para operadores declarados em interfaces.
Um operator_declaration em uma interface é a implementação (§19.1).
É um erro de tempo de compilação para uma interface declarar um operador de conversão, igualdade ou desigualdade.
Construtores estáticos da interface 19.4.8
Essa cláusula aumenta a descrição de construtores estáticos nas classes §15.12 para construtores estáticos declarados em interfaces.
O construtor estático para uma interface fechada (§8.4.3) é executado no máximo uma vez em um determinado domínio de aplicativo. A execução de um construtor estático é disparada pela primeira das seguintes ações a ocorrer em um domínio de aplicativo:
- Qualquer um dos membros estáticos da interface é referenciado.
- Antes que o
Mainmétodo seja chamado para uma interface que contenha oMainmétodo (§7.1) no qual a execução começa. - Essa interface fornece uma implementação para um membro e essa implementação é acessada como a implementação mais específica (§19.4.10) para esse membro.
Observação: no caso em que nenhuma das ações anteriores ocorre, o construtor estático de uma interface pode não ser executado para um programa em que instâncias de tipos que implementam a interface são criadas e usadas. nota final
Para inicializar um novo tipo de interface fechada, primeiro um novo conjunto de campos estáticos para esse tipo fechado específico é criado. Cada um dos campos estáticos é inicializado para seu valor padrão. Em seguida, os inicializadores de campo estático são executados para esses campos estáticos. Por fim, o construtor estático é executado.
Observação: consulte §19.4.2 para obter um exemplo de como usar vários tipos de membros estáticos (incluindo um método Main) declarados em uma interface. nota final
19.4.9 Tipos aninhados de interface
Essa cláusula aumenta a descrição de tipos aninhados nas classes §15.3.9 para tipos aninhados declarados em interfaces.
É um erro declarar um tipo de classe, um tipo de struct ou um tipo de enumeração dentro do escopo de um parâmetro de tipo que foi declarado com um variance_annotation (§19.2.3.1).
Exemplo: a declaração abaixo
Cé um erro.interface IOuter<out T> { class C { } // error: class declaration within scope of variant type parameter 'T' }fim de exemplo
19.4.10 implementação mais específica
Cada classe e struct devem ter uma implementação mais específica para cada membro virtual declarado em todas as interfaces implementadas por esse tipo entre as implementações que aparecem no tipo ou suas interfaces diretas e indiretas. A implementação mais específica é uma implementação exclusiva que é mais específica do que todas as outras implementações.
Observação: a regra de implementação mais específica garante que uma ambiguidade decorrente da herança da interface de diamante seja resolvida explicitamente pelo programador no ponto em que o conflito ocorre. nota final
Para um tipo T que é um struct ou uma classe que implementa interfaces I2 e I3, onde I2 e I3 ambos derivam direta ou indiretamente da interface I que declara um membro M, a implementação M mais específica é:
- Se
Tdeclarar uma implementação deI.M, essa implementação será a implementação mais específica. - Caso contrário, se
Tfor uma classe e uma classe base direta ou indireta declarar uma implementação,I.Ma classeTbase mais derivada será a implementação mais específica. - Caso contrário, se
I2e forem interfaces implementadas eI3Tderivadas diretaI3ou indiretamente,I2será uma implementação mais específica do queI3.MI2.M. - Caso contrário, nem
I2.MsãoI3.Mmais específicos e ocorre um erro.
Exemplo:
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(); }A regra de implementação mais específica garante que um conflito (ou seja, uma ambiguidade decorrente da herança de diamante) seja resolvido explicitamente pelo programador no ponto em que o conflito surge. fim de exemplo
19.4.11 Acesso de membro da interface
Os membros da interface são acessados por meio do acesso de membro (§12.8.7) e do acesso ao indexador (§12.8.12.4) expressões do formulário I.M e I[A], onde I é um tipo de interface, M é uma constante, campo, método, propriedade ou evento desse tipo de interface e A é uma lista de argumentos do indexador.
Em uma classe D, com classe Bbase direta ou indireta, B em que implementa direta ou indiretamente a interface I e I define um método M(), a expressão base.M() é válida somente se base.M() estáticamente (§12.3) associa-se a uma implementação de um tipo de M() classe.
Para interfaces estritamente de herança única (cada interface na cadeia de herança tem exatamente zero ou uma interface base direta), os efeitos da pesquisa de membro (§12.5), invocação do método (§12.8.1 0.2) e as regras de acesso ao indexador (§12.8.12.4) são exatamente iguais às classes e structs: membros mais derivados ocultam membros menos derivados com o mesmo nome ou assinatura. No entanto, para interfaces de herança múltipla, podem ocorrer ambiguidades quando duas ou mais interfaces base não relacionadas declaram membros com o mesmo nome ou assinatura. Esta subcláusula mostra vários exemplos, alguns dos quais levam a ambiguidades e outros não. Em todos os casos, podem ser empregadas conversões explícitas para resolver as ambiguidades.
Exemplo: no código a seguir
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 } }a primeira instrução causa um erro de tempo de compilação porque a busca pelo membro (§12.5) de
CountemIListCounteré ambígua. Conforme ilustrado pelo exemplo, a ambiguidade é resolvida convertendoxpara o tipo de interface base apropriado. Essas conversões não têm custos de tempo de execução; elas consistem somente em considerar a instância como um tipo menos derivado em tempo de compilação.fim de exemplo
Exemplo: no código a seguir
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 } }A invocação
n.Add(1)selecionaIInteger.Addaplicando as regras de resolução de sobrecarga de §12.6.4. Da mesma forma, a invocaçãon.Add(1.0)selecionaIDouble.Add. Quando conversões explícitas são inseridas, há somente um método candidato e, consequentemente, nenhuma ambiguidade.fim de exemplo
Exemplo: no código a seguir
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 } }O membro
IBase.Fé ocultado pelo membroILeft.F. A invocaçãod.F(1)assim selecionaILeft.F, mesmo queIBase.Fpareça não estar oculto no caminho de acesso que leva através deIRight.A regra intuitiva para ocultar em interfaces de herança múltipla é simplesmente esta: se um membro estiver oculto em qualquer caminho de acesso, ele estará oculto em todos os caminhos de acesso. Como o caminho de acesso de
IDerivedparaILeftparaIBaseocultaIBase.F, o membro também está oculto no caminho de acesso deIDerivedparaIRightparaIBase.fim de exemplo
19.5 Nomes de membros de interface qualificada
Às vezes, um membro da interface é referido por seu nome de membro da interface qualificado. O nome qualificado de um membro da interface consiste no nome da interface na qual o membro é declarado, seguido por um ponto, seguido pelo nome do membro. O nome qualificado de um membro faz referência à interface na qual o membro é declarado.
Exemplo: dadas as declarações
interface IControl { void Paint(); } interface ITextBox : IControl { void SetText(string text); }o nome qualificado de
PaintéIControl.Painte o nome qualificado de SetText éITextBox.SetText. No exemplo acima, não é possível se referir aPaintcomoITextBox.Paint.fim de exemplo
Quando uma interface faz parte de um namespace, um nome de membro de interface qualificado pode incluir o nome do namespace.
Exemplo:
namespace GraphicsLib { interface IPolygon { void CalculateArea(); } }Dentro do
GraphicsLibnamespace, tantoIPolygon.CalculateAreaquantoGraphicsLib.IPolygon.CalculateAreasão nomes de membros de interface qualificados para o métodoCalculateArea.fim de exemplo
Implementações de interface 19.6
19.6.1 Geral
As interfaces podem ser implementadas por classes e structs. Para indicar que uma classe ou struct implementa diretamente uma interface, a interface é incluída na lista de classes base da classe ou struct.
Uma classe ou struct C que implementa uma interface I deve fornecer ou herdar uma implementação para cada membro declarado I que C possa acessar. Membros públicos de I podem ser definidos em membros públicos de C. Os membros não públicos declarados I que estão acessíveis C podem ser definidos usando C a implementação da interface explícita (§19.6.2).
Um membro em um tipo derivado que satisfaz o mapeamento de interface (§19.6.5), mas não implementa o membro da interface base correspondente, apresenta um novo membro. Isso ocorre quando a implementação da interface explícita é necessária para definir o membro da interface.
Exemplo:
interface ICloneable { object Clone(); } interface IComparable { int CompareTo(object other); } class ListEntry : ICloneable, IComparable { public object Clone() {...} public int CompareTo(object other) {...} }fim de exemplo
Uma classe ou struct que implementa diretamente uma interface também implementa implicitamente todas as interfaces base da interface. Isso é verdadeiro mesmo que a classe ou struct não liste explicitamente todas as interfaces base na lista de classes base.
Exemplo:
interface IControl { void Paint(); } interface ITextBox : IControl { void SetText(string text); } class TextBox : ITextBox { public void Paint() {...} public void SetText(string text) {...} }Aqui, a classe
TextBoximplementa ambosIControleITextBox.fim de exemplo
Quando uma classe C implementa diretamente uma interface, todas as classes derivadas de C também implementam a interface implicitamente.
As interfaces base especificadas em uma declaração de classe podem ser construídos tipos de interface (§8.4, §19.2).
Exemplo: o código a seguir ilustra como uma classe pode implementar tipos de interface construídos:
class C<U, V> {} interface I1<V> {} class D : C<string, int>, I1<string> {} class E<T> : C<int, T>, I1<T> {}fim de exemplo
As interfaces base de uma declaração de classe genérica devem atender à regra de exclusividade descrita em §19.6.3.
19.6.2 Implementações de membro da interface explícita
Para fins de implementação de interfaces, uma classe, um struct ou uma interface podem declarar implementações explícitas de membro de interface. Uma implementação explícita de membro de interface é um método, propriedade, evento ou declaração de indexador que faz referência a um nome de membro de interface qualificado. Uma classe ou struct que implementa um membro não público em uma interface base deve declarar uma implementação de membro de interface explícita. Uma interface que implementa um membro em uma interface base deve declarar uma implementação de membro de interface explícita.
Um membro de interface derivada que satisfaz o mapeamento de interface (§19.6.5) oculta o membro da interface base (§7.7.2). O compilador emitirá um aviso, a menos que o new modificador esteja presente.
Exemplo:
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) {...} }Aqui,
IDictionary<int,T>.thiseIDictionary<int,T>.Addsão implementações explícitas de membros da interface.fim de exemplo
Exemplo: em alguns casos, o nome de um membro da interface pode não ser apropriado para a classe de implementação, nesse caso, o membro da interface pode ser implementado usando a implementação explícita do membro da interface. Uma classe que implementa uma abstração de arquivo, por exemplo, provavelmente implementaria uma função de membro
Closeque tem o efeito de liberar o recurso de arquivo e implementaria o métodoDisposeda interfaceIDisposableusando a implementação explícita do membro da interface: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); } }fim de exemplo
Não é possível acessar uma implementação explícita de membro de interface por meio de seu nome de membro de interface qualificado ao invocar um método, acessar uma propriedade, acessar um evento ou acessar um indexador. Uma implementação de membro de instância de interface explícita só pode ser acessada por meio de uma instância de interface e, nesse caso, é referenciada simplesmente pelo nome do membro. Uma implementação de membro estático de interface explícita só pode ser acessada por meio do nome da interface.
É um erro em tempo de compilação se uma implementação explícita de um membro da interface incluir quaisquer modificadores (§15.6) diferentes de extern ou async.
Uma implementação de método de interface explícita herda quaisquer restrições de parâmetro de tipo da interface.
Uma type_parameter_constraints_clause em uma implementação de método de interface explícita pode consistir apenas nas classstructou aplicadas a type_parameterque são conhecidas de acordo com as restrições herdadas para serem tipos de referência ou valor, respectivamente. Qualquer tipo do formulário T? na assinatura da implementação do método de interface explícita, onde T é um parâmetro de tipo, é interpretado da seguinte maneira:
- Se uma
classrestrição for adicionada para o parâmetroTde tipo, seráT?um tipo de referência anulável; caso contrário, - Se não houver nenhuma restrição adicional, ou se uma restrição
structfor adicionada, então o parâmetro de tipoTserá um tipo de valor anulávelT?.
Exemplo: o seguinte demonstra como as regras funcionam quando os parâmetros de tipo estão envolvidos:
#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 { } }Sem a restrição de parâmetro de tipo
where T : class, o método base com o parâmetro de tipo referenciado não pode ser substituído. fim de exemplo
Observação: as implementações explícitas de membros da interface têm características de acessibilidade diferentes das de outros membros. Como as implementações explícitas de membros da interface nunca são acessíveis por meio de um nome de membro de interface qualificado em uma invocação de método ou acesso a uma propriedade, elas são, em certo sentido, privadas. No entanto, uma vez que podem ser acessados por meio da interface, eles são, em certo sentido, também tão públicos quanto a interface na qual são declarados. As implementações explícitas de membros da interface servem a duas finalidades principais:
- Como as implementações explícitas de membro de interface não são acessíveis por meio de instâncias de classe ou struct, elas permitem que as implementações de interface sejam excluídas da interface pública de uma classe ou struct. Isso é particularmente útil quando uma classe ou struct implementa uma interface interna que não interessa a um consumidor dessa classe ou struct.
- As implementações explícitas dos membros da interface permitem a desambiguação dos membros da interface com a mesma assinatura. Sem implementações explícitas de membro de interface, seria impossível para uma classe, struct ou interface ter diferentes implementações de membros de interface com o mesmo tipo de assinatura e retorno, como seria impossível para uma classe, struct ou interface ter qualquer implementação em todos os membros da interface com a mesma assinatura, mas com tipos de retorno diferentes.
nota final
Para que uma implementação de membro de interface explícita seja válida, a classe, o struct ou a interface devem nomear uma interface em sua classe base ou lista de interface base que contenha um membro cujo nome de membro de interface qualificado, tipo, número de parâmetros de tipo e tipos de parâmetro correspondem exatamente aos da implementação do membro da interface explícita. Se um membro da função de interface tiver uma matriz de parâmetros, é permitido, mas não é obrigatório, que o parâmetro correspondente de uma implementação de membro de interface explícita associada tenha o modificador params. Se o membro da função de interface não tiver uma matriz de parâmetros, uma implementação explícita de membro de interface associada não deverá ter uma matriz de parâmetros.
Exemplo: assim, na classe a seguir
class Shape : ICloneable { object ICloneable.Clone() {...} int IComparable.CompareTo(object other) {...} // invalid }A declaração de
IComparable.CompareToresulta em um erro de tempo de compilação porqueIComparablenão está listado na lista de classes básicas deShapee não é uma interface base deICloneable. Da mesma forma, nas declaraçõesclass Shape : ICloneable { object ICloneable.Clone() {...} } class Ellipse : Shape { object ICloneable.Clone() {...} // invalid }A declaração de
ICloneable.CloneemEllipseresulta em um erro de tempo de compilação porqueICloneablenão está explicitamente listada na lista de classes base deEllipse.fim de exemplo
O nome do membro de interface qualificado de uma implementação explícita de membro de interface deve fazer referência à interface na qual o membro foi declarado.
Exemplo: Assim, nas declarações
interface IControl { void Paint(); } interface ITextBox : IControl { void SetText(string text); } class TextBox : ITextBox { void IControl.Paint() {...} void ITextBox.SetText(string text) {...} }a implementação explícita do membro da interface do Paint deve ser escrita como
IControl.Paint, nãoITextBox.Paint.fim de exemplo
19.6.3 Exclusividade das interfaces implementadas
As interfaces implementadas por uma declaração genérica de tipo devem permanecer únicas para todos os tipos construídos possíveis. Sem essa regra, seria impossível determinar o método correto para chamar certos tipos construídos.
Exemplo: suponha que uma declaração de classe genérica possa ser escrita da seguinte maneira:
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() {...} }Se isso fosse permitido, seria impossível determinar qual código executar no seguinte caso:
I<int> x = new X<int, int>(); x.F();fim de exemplo
Para determinar se a lista de interfaces de uma declaração de tipo genérico é válida, as seguintes etapas são executadas:
- Seja
La lista de interfaces especificadas diretamente em uma declaração genérica de classe, struct ou interfaceC. - Adicione a
Lqualquer interface de base das interfaces já emL. - Remova todas as duplicatas do
L. - Se qualquer tipo construído possível criado a partir de
C, depois que os argumentos de tipo forem substituídos emL, fizer com que duas interfaces emLsejam idênticas, a declaração deCé inválida. As declarações de restrição não são consideradas ao determinar todos os tipos construídos possíveis.
Observação: Na declaração
Xde classe acima, a lista de interfacesLconsiste eml<U>eI<V>. A declaração é inválida porque qualquer tipo construído comUeVsendo o mesmo tipo faria com que essas duas interfaces fossem tipos idênticos. nota final
É possível que as interfaces especificadas em diferentes níveis de herança se unificem:
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() {...}
}
Esse código é válido mesmo que Derived<U,V> implemente tanto I<U> quanto I<V>. O código
I<int> x = new Derived<int, int>();
x.F();
invoca o método em Derived, uma vez Derived<int,int>' que efetivamente reimplencia I<int> (§19.6.7).
19.6.4 Implementação de métodos genéricos
Quando um método genérico implementa implicitamente um método de interface, as restrições fornecidas para cada parâmetro de tipo de método devem ser equivalentes em ambas as declarações (depois que quaisquer parâmetros de tipo de interface são substituídos pelos argumentos de tipo apropriados), onde os parâmetros de tipo de método são identificados por posições ordinais, da esquerda para a direita.
Exemplo: no código a seguir:
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 }O método
C.F<T>implementaI<object,C,string>.F<T>implicitamente . Nesse caso, não é necessário (nem permitido) especificar a restriçãoC.F<T>,T: objectpoisobjecté uma restrição implícita em todos os parâmetros de tipo. O métodoC.G<T>é implementadoI<object,C,string>.G<T>implicitamente porque as restrições correspondem às da interface, depois que os parâmetros de tipo de interface são substituídos pelos argumentos de tipo correspondentes. A restrição para methodC.H<T>é um erro porque os tipos sealed (stringneste caso) não podem ser usados como restrições. Omitir a restrição também seria um erro, pois as restrições de implementações implícitas do método de interface são necessárias para corresponder. Assim, é impossível implementarI<object,C,string>.H<T>implicitamente . Esse método de interface só pode ser implementado usando uma implementação explícita de membro de interface: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); } }Nesse caso, a implementação explícita do membro da interface invoca um método público com restrições estritamente mais fracas. A atribuição de t para s é válida, pois
Therda uma restrição deT: string, mesmo que essa restrição não seja expressável no código-fonte. fim de exemplo
Observação: quando um método genérico implementa explicitamente um método de interface, nenhuma restrição é permitida no método de implementação (§15.7.1, §19.6.2). nota final
Mapeamento de interface 19.6.5
Uma classe ou struct deve fornecer implementações de todos os membros abstratos das interfaces listadas na lista de classes base da classe ou struct. O processo de localização de implementações de membros de interface em uma classe ou struct de implementação é conhecido como mapeamento de interface.
O mapeamento de interface para uma classe ou struct C localiza uma implementação para cada membro de cada interface especificada na lista de classes base de C. A implementação de um membro I.Mde interface específico, onde I é a interface na qual o membro M é declarado, é determinada examinando cada classe, interface ou struct S, começando com C e repetindo para cada classe base sucessiva e interface implementada de C, até que uma correspondência esteja localizada:
- Se
Scontiver uma declaração de uma implementação explícita de membro de interface que corresponda aIeM, esse membro será a implementação deI.M. - Caso contrário, se
Scontiver uma declaração de um membro público não estático que corresponda aM, esse membro será a implementação deI.M. Se mais de um membro corresponder, não será especificado qual membro é a implementação deI.M. Essa situação só pode ocorrer seSfor um tipo construído em que os dois membros, conforme declarado no tipo genérico, têm assinaturas diferentes, mas os argumentos de tipo tornam suas assinaturas idênticas.
Ocorrerá um erro em tempo de compilação se as implementações não puderem ser localizadas para todos os membros de todas as interfaces especificadas na lista de classes base de C. Os membros de uma interface incluem os membros herdados de interfaces de base.
Os membros de um tipo de interface construída são considerados como tendo todos os parâmetros de tipo substituídos pelos argumentos de tipo correspondentes, conforme especificado em §15.3.3.
Exemplo: por exemplo, dada a declaração de interface genérica:
interface I<T> { T F(int x, T[,] y); T this[int y] { get; } }A interface
I<string[]>construída tem os membros:string[] F(int x, string[,][] y); string[] this[int y] { get; }fim de exemplo
Para fins de mapeamento de interface, um membro A de classe, interface ou struct corresponde a um membro B de interface quando:
-
AeBsão métodos, e o nome, tipo e listas de parâmetros deAeBsão idênticos. -
AeBsão propriedades, o nome e o tipo deAsão idênticos, eBtem os mesmos acessadores queA(Btem permissão para ter acessadores adicionais se não for uma implementação explícita de membro da interfaceA). -
AeBsão eventos, e o nome e o tipo deAeBsão idênticos. -
AeBsão indexadores, as listas de tipos e parâmetros deAeBsão idênticas eAtêm os mesmos acessadores queB(Atem permissão para ter acessadores adicionais se não for uma implementação explícita de membro de interface).
As implicações notáveis do algoritmo de mapeamento de interface são:
- As implementações explícitas de membro de interface têm precedência sobre outros membros na mesma classe ou struct ao determinar a classe ou o struct que implementa um membro de interface.
- Nem membros não públicos nem estáticos participam do mapeamento de interface.
Exemplo: no código a seguir
interface ICloneable { object Clone(); } class C : ICloneable { object ICloneable.Clone() {...} public object Clone() {...} }O membro
ICloneable.ClonedeCtorna-se a implementação deClonenoICloneableporque as implementações explícitas de membros da interface têm precedência sobre outros membros.fim de exemplo
Se uma classe ou struct implementar duas ou mais interfaces contendo um membro com o mesmo nome, tipo e tipos de parâmetro, será possível mapear cada um desses membros de interface em um único membro de classe ou struct.
Exemplo:
interface IControl { void Paint(); } interface IForm { void Paint(); } class Page : IControl, IForm { public void Paint() {...} }Aqui, os métodos
PaintdeIControleIFormsão mapeados para o métodoPaintemPage. É claro que também é possível ter implementações de membros de interface explícitas separadas para os dois métodos.fim de exemplo
Se uma classe ou struct implementar uma interface que contenha membros ocultos, alguns membros poderão precisar ser implementados por meio de implementações explícitas de membros de interface.
Exemplo:
interface IBase { int P { get; } } interface IDerived : IBase { new int P(); }Uma implementação dessa interface exigiria pelo menos uma implementação explícita de membro da interface e assumiria uma das seguintes formas
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() {...} }fim de exemplo
Quando uma classe implementa várias interfaces que têm a mesma interface base, pode haver apenas uma implementação da interface base.
Exemplo: no código a seguir
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) {...} }Não é possível ter implementações separadas para o
IControlnomeado na lista de classes de base, oIControlherdado porITextBoxe oIControlherdado porIListBox. De fato, não há noção de uma identidade separada para essas interfaces. Em vez disso, as implementações deITextBoxeIListBoxcompartilham a mesma implementação deIControl, eComboBoxé simplesmente considerado como implementando três interfaces,IControl,ITextBox, eIListBox.fim de exemplo
Os membros de uma classe base participam do mapeamento de interface.
Exemplo: no código a seguir
interface Interface1 { void F(); } class Class1 { public void F() {} public void G() {} } class Class2 : Class1, Interface1 { public new void G() {} }O método
FINClass1é usado naClass2'simplementação deInterface1.fim de exemplo
Herança de implementação da interface 19.6.6
Uma classe herda todas as implementações de interface fornecidas por suas classes base.
Sem reimplementar explicitamente uma interface, uma classe derivada não pode de forma alguma alterar os mapeamentos de interface que herda de suas classes base.
Exemplo: Nas declarações
interface IControl { void Paint(); } class Control : IControl { public void Paint() {...} } class TextBox : Control { public new void Paint() {...} }O método
PaintemTextBoxoculta o métodoPaintemControl, mas não altera o mapeamento deControl.PaintparaIControl.Paint, e as chamadas paraPaintatravés de instâncias de classe e instâncias de interface terão os seguintes efeitosControl 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();fim de exemplo
No entanto, quando um método de interface é mapeado em um método virtual em uma classe, é possível que as classes derivadas substituam o método virtual e alterem a implementação da interface.
Exemplo: Reescrever as declarações acima para
interface IControl { void Paint(); } class Control : IControl { public virtual void Paint() {...} } class TextBox : Control { public override void Paint() {...} }Os seguintes efeitos serão observados agora
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();fim de exemplo
Como as implementações explícitas de membros da interface não podem ser declaradas virtuais, não é possível substituir uma implementação explícita de membros da interface. No entanto, é perfeitamente válido que uma implementação explícita de membro de interface chame outro método, e esse outro método pode ser declarado virtual para permitir que classes derivadas o substituam.
Exemplo:
interface IControl { void Paint(); } class Control : IControl { void IControl.Paint() { PaintControl(); } protected virtual void PaintControl() {...} } class TextBox : Control { protected override void PaintControl() {...} }Aqui, as classes derivadas de
Controlpodem especializar a implementação deIControl.Paintsubstituindo o métodoPaintControl.fim de exemplo
Implementação da interface 19.6.7
Uma classe que herda uma implementação de interface tem permissão para reimplementar a interface incluindo-a na lista de classes base.
Uma reimplementação de uma interface segue exatamente as mesmas regras de mapeamento de interface que uma implementação inicial de uma interface. Assim, o mapeamento de interface herdado não tem nenhum efeito sobre o mapeamento de interface estabelecido para a reimplementação da interface.
Exemplo: Nas declarações
interface IControl { void Paint(); } class Control : IControl { void IControl.Paint() {...} } class MyControl : Control, IControl { public void Paint() {} }O fato de que
ControlmapeiaIControl.PaintparaControl.IControl.Paintnão afeta a reimplementação emMyControl, que mapeiaIControl.PaintparaMyControl.Paint.fim de exemplo
As declarações de membro público herdadas e declarações de membro de interface explícitas herdadas participam do processo de mapeamento de interface para as interfaces reimplementadas.
Exemplo:
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() {} }Aqui, a implementação de
IMethodsmapeia os métodos da interfaceDerivedemDerived.F,Base.IMethods.G,Derived.IMethods.HeBase.I.fim de exemplo
Quando uma classe implementa uma interface, ela implicitamente também implementa todas as interfaces base dessa interface. Da mesma forma, uma reimplementação de uma interface também é implicitamente uma reimplementação de todas as interfaces base da interface.
Exemplo:
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() {...} }Aqui, a reimplementação de
IDerivedtambém reimplementaIBase, mapeandoIBase.FemD.F.fim de exemplo
19.6.8 Classes abstratas e interfaces
Como uma classe não abstrata, uma classe abstrata deve fornecer implementações de todos os membros abstratos das interfaces listadas na lista de classes base da classe. No entanto, uma classe abstrata tem permissão para mapear métodos de interface em métodos abstratos.
Exemplo:
interface IMethods { void F(); void G(); } abstract class C : IMethods { public abstract void F(); public abstract void G(); }Aqui, a implementação de
IMethodsmapeiaFeGem métodos abstratos, que devem ser substituídos em classes não abstratas que derivam deC.fim de exemplo
As implementações explícitas de membros de interface não podem ser abstratas, mas as implementações explícitas de membros de interface têm permissão para chamar métodos abstratos.
Exemplo:
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(); }Aqui, as classes não abstratas que derivam de
Cseriam obrigadas a sobrescreverFFeGG, fornecendo assim a implementação real deIMethods.fim de exemplo
ECMA C# draft specification