Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
19.1 General
Una interfaz define un contrato. Una clase o estructura que implementa una interfaz debe cumplir su contrato. Una interfaz puede heredar de varias interfaces base, y una clase o estructura puede implementar varias interfaces.
Las interfaces pueden contener varios tipos de miembros, como se describe en §19.4. La propia interfaz puede proporcionar una implementación para algunos o todos los miembros de función que declara. Los miembros para los que la interfaz no proporciona una implementación son abstractas. Sus implementaciones deben proporcionarse mediante clases o estructuras que implementan la interfaz o la interfaz derivada que proporcionan una definición de invalidación.
Nota: Históricamente, agregar un nuevo miembro de función a una interfaz afectaba a todos los consumidores existentes de ese tipo de interfaz; fue un cambio importante. La adición de implementaciones de miembros de función de interfaz permitió a los desarrolladores actualizar una interfaz, al tiempo que permitía a los implementadores invalidar esa implementación. Los usuarios de la interfaz pueden aceptar la implementación como un cambio no importante; sin embargo, si sus requisitos son diferentes, pueden invalidar las implementaciones proporcionadas. nota final
19.2 Declaraciones de interfaz
19.2.1 General
Una interface_declaration es una type_declaration (sección 14.7) que declara un nuevo tipo de interfaz.
interface_declaration
: attributes? interface_modifier* 'partial'? 'interface'
identifier variant_type_parameter_list? interface_base?
type_parameter_constraints_clause* interface_body ';'?
;
Un interface_declaration consta de un conjunto opcional de atributos (§23), seguido de un conjunto opcional de interface_modifiers (§19.2.2), seguido de un modificador parcial opcional (§15.2.7), seguido de la palabra clave interface y un identificador que denomina la interfaz, seguido de una especificación opcional de variant_type_parameter_list (§19.2.3), seguida de una especificación de interface_base opcional (§19.2.4)), seguido de una especificación de type_parameter_constraints_clauseopcional (§15.2.5), seguida de un interface_body (§19.3), seguido opcionalmente de un punto y coma.
Una declaración de interfaz no proporcionará type_parameter_constraints_clausea menos que también proporcione una variant_type_parameter_list.
Una declaración de interfaz que proporciona una variant_type_parameter_list es una declaración de interfaz genérica. Además, cualquier interfaz anidada dentro de una declaración de clase genérica o una declaración de estructura genérica es una declaración de interfaz genérica, ya que se proporcionarán argumentos de tipo para el tipo contenedor para crear un tipo construido (sección 8.4).
Modificadores de interfaz 19.2.2
Una interface_declaration puede incluir opcionalmente una secuencia de modificadores de interfaz:
interface_modifier
: 'new'
| 'public'
| 'protected'
| 'internal'
| 'private'
| unsafe_modifier // unsafe code support
;
unsafe_modifier (§24.2) solo está disponible en código no seguro (§24).
Es un error en tiempo de compilación porque el mismo modificador aparece varias veces en una declaración de interfaz.
El modificador new solo se permite en interfaces definidas dentro de una clase. Especifica que la interfaz oculta un miembro heredado con el mismo nombre, como se describe en la sección 15.3.5.
Los modificadores public, protected, internal y private controlan la accesibilidad de la interfaz. En función del contexto en el que se produzca la declaración de interfaz, solo se pueden permitir algunos de estos modificadores (sección 7.5.2). Cuando una declaración de tipo parcial (sección 15.2.7) incluye una especificación de accesibilidad (a través de los modificadores public, protected, internal y private), se aplican las reglas de la sección 15.2.2.
19.2.3 Listas de parámetros de tipo variant
19.2.3.1 General
Las listas de parámetros de tipo de variante solo pueden producirse en tipos de interfaz y delegado. La diferencia entre los type_parameter_listnormales es la variance_annotation opcional en 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'
;
Si la anotación de varianza es out, se dice que el parámetro de tipo es covariante. Si la anotación de varianza es in, se dice que el parámetro de tipo es contravariante. Si no hay ninguna anotación de varianza, se dice que el parámetro de tipo es invariable..
Ejemplo: En el siguiente:
interface C<out X, in Y, Z> { X M(Y y); Z P { get; set; } }
Xes covariante,Yes contravariante yZes invariable.ejemplo final
Si una interfaz genérica se declara en varias partes (§15.2.3), cada declaración parcial especificará la misma varianza para cada parámetro de tipo.
19.2.3.2 Seguridad de varianza
La aparición de anotaciones de varianza en la lista de parámetros de tipo de un tipo restringe los lugares donde pueden producirse los tipos dentro de la declaración de tipo.
Un tipo T es no seguro para la salida si se cumple una de las siguientes condiciones:
-
Tes un parámetro de tipo contravariante -
Tes un tipo de matriz con un tipo de elemento no seguro para la salida -
Tes un tipoSᵢ,... Aₑde interfaz o delegado construido a partir de un tipoS<Xᵢ, ... Xₑ>genérico en el que al menos unoAᵢde los siguientes contiene:-
Xᵢes covariante o invariable yAᵢno es seguro para la salida. -
Xᵢes contravariante o invariable yAᵢno es seguro para la entrada.
-
Un tipo T es no seguro para la entrada si se cumple una de las siguientes condiciones:
-
Tes un parámetro de tipo covariante. -
Tes un tipo de matriz con un tipo de elemento no seguro para la entrada -
Tes un tipoS<Aᵢ,... Aₑ>de interfaz o delegado construido a partir de un tipoS<Xᵢ, ... Xₑ>genérico en el que al menos unoAᵢde los siguientes contiene:-
Xᵢes covariante o invariable yAᵢno es seguro para la entrada. -
Xᵢes contravariante o invariable yAᵢno es seguro para la salida.
-
Intuitivamente, un tipo no seguro para la salida está prohibido en una posición de salida y un tipo no seguro para la entrada está prohibido en una posición de entrada.
Un tipo es seguro para la salida si no es inseguro para la salida y es seguro para la entrada si no es inseguro para la entrada.
19.2.3.3 Conversión de varianza
El propósito de las anotaciones de varianza es proporcionar conversiones más lenientes (pero todavía seguras para los tipos) en los tipos de interfaz y de delegado. Para ello, las definiciones de las conversiones implícitas (sección 10.2) y explícitas (sección 10.3) hacen uso de la noción de convertibilidad de varianza, que se define de la siguiente manera:
Un tipo T<Aᵢ, ..., Aᵥ> es de varianza convertible a un tipo T<Bᵢ, ..., Bᵥ> si T es un tipo de interfaz o delegado declarado con los parámetros de tipo variante T<Xᵢ, ..., Xᵥ> y para cada parámetro de tipo variante Xᵢ se cumple una de las siguientes condiciones:
-
Xᵢes covariante y existe una referencia implícita o conversión de identidad deAᵢaBᵢ -
Xᵢes contravariante y existe una referencia implícita o conversión de identidad deBᵢaAᵢ -
Xᵢes invariable y existe una conversión de identidad deAᵢaBᵢ
19.2.4 Interfaces base
Una interfaz puede heredar de cero o más tipos de interfaz, que se denominan interfaces base explícitasde la interfaz. Cuando una interfaz tiene una o varias interfaces base explícitas, en la declaración de esa interfaz, el identificador de interfaz va seguido de dos puntos y una lista separada por comas de tipos de interfaz base.
Una interfaz derivada puede declarar nuevos miembros que ocultan miembros heredados (§7.7.2.3) declarados en interfaces base o implementan explícitamente miembros heredados (§19.6.2) declarados en interfaces base.
interface_base
: ':' interface_type_list
;
Las interfaces base explícitas se pueden construir tipos de interfaz (§8.4, §19.2). Una interfaz base no puede ser un parámetro de tipo por si misma, aunque puede implicar los parámetros de tipo que están en el ámbito.
Para un tipo de interfaz construido, las interfaces base explícitas se forman tomando las declaraciones de interfaz base explícitas en la declaración de tipo genérico y sustituyendo, por cada type_parameter en la declaración de interfaz base, el type_argument correspondiente del tipo construido.
Las interfaces base explícitas de una interfaz deben ser al menos igual de accesibles que la interfaz (sección 7.5.5).
Nota: Por ejemplo, es un error en tiempo de compilación especificar una interfaz
privateointernalen la interface_base de una interfazpublic. nota final
Es un error de compilación que una interfaz herede directa o indirectamente de sí misma.
Las interfaces basede una interfaz son las interfaces base explícitas y sus interfaces base. En otras palabras, el conjunto de interfaces base es el cierre transitivo completo de las interfaces base explícitas, de sus propias interfaces base explícitas, y así sucesivamente. Una interfaz hereda todos los miembros de sus interfaces base.
Example: En el código de ejemplo siguiente
interface IControl { void Paint(); } interface ITextBox : IControl { void SetText(string text); } interface IListBox : IControl { void SetItems(string[] items); } interface IComboBox: ITextBox, IListBox {}las interfaces base de
IComboBoxsonIControl,ITextBoxyIListBox. En otras palabras, la interfazIComboBoxanterior hereda los miembrosSetTextySetItems, así comoPaint.ejemplo final
Los miembros heredados de un tipo genérico construido se heredan después de la sustitución de tipos. Es decir, en los tipos constituyentes del miembro se sustituyen los parámetros tipo de la declaración de clase base por los argumentos de tipo correspondientes usados en la especificación de class_base .
Example: En el código de ejemplo siguiente
interface IBase<T> { T[] Combine(T a, T b); } interface IDerived : IBase<string[,]> { // Inherited: string[][,] Combine(string[,] a, string[,] b); }la interfaz
IDerivedhereda el métodoCombinedespués de sustituirse el parámetro de tipoTporstring[,].ejemplo final
Una clase o estructura que implementa una interfaz también implementa implícitamente todas las interfaces base de la interfaz.
El control de interfaces en varias partes de una declaración de interfaz parcial (sección 15.2.7) se describe más adelante en la sección 15.2.4.3.
Cada interfaz base de una interfaz será segura para salidas (§19.2.3.2).
Cuerpo de la interfaz 19.3
El interface_body de una interfaz define los miembros de la interfaz.
interface_body
: '{' interface_member_declaration* '}'
;
19.4 Miembros de la interfaz
19.4.1 General
Los miembros de una interfaz son los miembros heredados de las interfaces base y los miembros declarados por la propia interfaz.
interface_member_declaration
: constant_declaration
| field_declaration
| method_declaration
| property_declaration
| event_declaration
| indexer_declaration
| static_constructor_declaration
| operator_declaration
| type_declaration
;
Esta cláusula aumenta la descripción de los miembros de las clases (§15.3) con restricciones para las interfaces. Los miembros de interfaz se declaran mediante member_declarations con las siguientes reglas adicionales:
- No se permite un finalizer_declaration .
- No se permiten constructores de instancia, constructor_declarations.
- Todos los miembros de la interfaz tienen implícitamente acceso público; sin embargo, se permite un modificador de acceso explícito (§7.5.2), excepto en constructores estáticos (§15.12).
- El
abstractmodificador está implícito para los miembros de función de interfaz sin cuerpos; ese modificador se puede dar explícitamente. - Un miembro de función de instancia de interfaz cuya declaración incluye un cuerpo es un miembro implícitamente
virtuala menos que se use elsealedmodificador oprivate. Elvirtualmodificador se puede dar explícitamente. - Un
privatemiembro de función osealedde una interfaz tendrá un cuerpo. - Un
privatemiembro de función no tendrá el modificadorsealed. - Una interfaz derivada puede invalidar un miembro abstracto o virtual declarado en una interfaz base.
- Un miembro de función implementado explícitamente no tendrá el modificador
sealed.
Algunas declaraciones, como constant_declaration (§15.4) no tienen restricciones en las interfaces.
Los miembros heredados de una interfaz no forman parte específicamente del espacio de declaración de la interfaz. Por lo tanto, se permite que una interfaz declare un miembro con el mismo nombre o firma que un miembro heredado. Cuando esto ocurre, se dice que el miembro de interfaz derivada oculta el miembro de interfaz base. Ocultar un miembro heredado no se considera un error, pero produce una advertencia (§7.7.2.3).
Si se incluye un new modificador en una declaración que no oculta un miembro heredado, se emite una advertencia en consecuencia.
Nota: Los miembros de la clase
objectno son, estrictamente hablando, miembros de ninguna interfaz (§19.4). Sin embargo, los miembros de la claseobjectestán disponibles a través de la búsqueda de miembros en cualquier tipo de interfaz (sección 12.5). nota final
El conjunto de miembros de una interfaz declarada en varias partes (sección 15.2.7) es la unión de los miembros declarados en cada parte. Los cuerpos de todas las partes de la declaración de interfaz comparten el mismo espacio de declaración (sección 7.3) y el ámbito de cada miembro (sección 7.7) se extiende a los cuerpos de todas las partes.
Ejemplo: considere una interfaz
IAcon una implementación para un miembroMy una propiedadP. Un tipoCde implementación no proporciona una implementación paraMoP. Se debe tener acceso a ellos a través de una referencia cuyo tipo en tiempo de compilación es una interfaz que se puede convertir implícitamente enIAoIB. Estos miembros no se encuentran a través de la búsqueda de miembros en una variable de 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 de las interfaces
IAyIB, el miembroMes accesible directamente por su nombre. Sin embargo, dentro del métodoMain, no podemos escribirc.M()oc.P, ya que esos nombres no son visibles. Para encontrarlos, se necesitan conversiones al tipo de interfaz adecuado. La declaración de enMusa la sintaxis de implementación deIBinterfaz explícita. Esto es necesario para que ese método invalide elIAde ; es posible que el modificadoroverrideno se aplique a un miembro de función. ejemplo final
19.4.2 Campos de interfaz
Esta cláusula aumenta la descripción de los campos de las clases §15.5 para los campos declarados en interfaces.
Los campos de interfaz se declaran mediante field_declarations (§15.5.1) con las siguientes reglas adicionales:
- Es un error en tiempo de compilación para field_declaration declarar un campo de instancia.
Ejemplo: El siguiente programa contiene miembros estáticos de varios 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}"); } }La salida generada es
static members initialized constant = 100, field = 0 static constructor has run constant = 100, field = 50ejemplo final
Consulte §19.4.8 para obtener información sobre la asignación e inicialización de campos estáticos.
Métodos de interfaz 19.4.3
Esta cláusula aumenta la descripción de los métodos de las clases §15.6 para los métodos declarados en interfaces.
Los métodos de interfaz se declaran mediante method_declarations (§15.6)). Los atributos, return_type, ref_return_type, identificador y parameter_list de una declaración de método de interfaz tienen el mismo significado que los de una declaración de método en una clase. Los métodos de interfaz tienen las siguientes reglas adicionales:
method_modifier no incluirá
override.Un método cuyo cuerpo es un punto y coma (
;) esabstract; elabstractmodificador no es necesario, pero se permite.Una declaración de método de interfaz que tiene un cuerpo de bloque o un cuerpo de expresión como un method_body es
virtual; elvirtualmodificador no es necesario, pero se permite.Un method_declaration no tendrá type_parameter_constraints_clausea menos que también tenga un type_parameter_list.
La lista de requisitos para combinaciones válidas de modificadores indicados para un método de clase se extiende de la siguiente manera:
- Una declaración estática que no sea extern tendrá un cuerpo de bloque o un cuerpo de expresión como method_body.
- Una declaración virtual que no sea extern tendrá un cuerpo de bloque o un cuerpo de expresión como method_body.
- Una declaración privada que no sea extern tendrá un cuerpo de bloque o un cuerpo de expresión como method_body.
- Una declaración sellada que no sea extern tendrá un cuerpo de bloque o cuerpo de expresión como method_body.
- Una declaración asincrónica tendrá un cuerpo de bloque o un cuerpo de expresión como method_body.
Todos los tipos de parámetros de un método de interfaz serán seguros para la entrada (§19.2.3.2) y el tipo de valor devuelto será o
voidseguro para la salida.Cualquier tipo de parámetro de salida o referencia también será seguro para la salida.
Nota: Los parámetros de salida deben ser seguros para la entrada debido a restricciones comunes de implementación. nota final
Cada restricción de tipo de clase, restricción de tipo de interfaz y restricción de parámetro de tipo en cualquier parámetro de tipo del método será segura para la entrada.
Estas reglas garantizan que cualquier uso covariante o contravariante de la interfaz siga siendo segura en cuanto a tipo.
Ejemplo:
interface I<out T> { void M<U>() where U : T; // Error }tiene un formato incorrecto porque el uso de
Tcomo restricción de parámetro de tipo enUno es seguro para la entrada.Si esta restricción no estuviera en efecto, sería posible infringir la seguridad de tipos de la siguiente manera:
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>();Esto es realmente una llamada a
C.M<E>. Pero esa llamada requiere queEderive deD, por lo que en este caso se infringiría la seguridad de tipo.ejemplo final
Nota: Vea §19.4.2 para ver un ejemplo que no solo muestra un método estático con una implementación, sino que se llama
Maina ese método y tiene el tipo de valor devuelto y la firma correctos, también es un punto de entrada. nota final
Un método virtual con la implementación declarada en una interfaz se puede invalidar para que sea abstracta en una interfaz derivada. Esto se conoce como reabstración.
Ejemplo:
interface IA { void M() { Console.WriteLine("IA.M"); } } interface IB: IA { abstract void IA.M(); // reabstraction of M }Esto resulta útil en interfaces derivadas en las que la implementación de un método es inapropiada y se debe proporcionar una implementación más adecuada mediante la implementación de clases. ejemplo final
19.4.4 Propiedades de la interfaz
Esta cláusula aumenta la descripción de las propiedades de las clases §15.7 para las propiedades declaradas en interfaces.
Las propiedades de interfaz se declaran mediante property_declarations (§15.7.1) con las siguientes reglas adicionales:
property_modifier no
overrideincluirá .Una implementación explícita del miembro de interfaz no contendrá una accessor_modifier (§15.7.3).
Una interfaz derivada puede implementar explícitamente una propiedad de interfaz abstracta declarada en una interfaz base.
Nota: Como una interfaz no puede contener campos de instancia, una propiedad de interfaz no puede ser una propiedad automática de instancia, ya que requeriría la declaración de campos de instancia ocultos implícitos. nota final
El tipo de una propiedad de interfaz será seguro para la salida si hay un descriptor de acceso get y será seguro para la entrada si hay un descriptor de acceso set.
Una declaración de método de interfaz que tiene un cuerpo de bloque o un cuerpo de expresión como un method_body es
virtual; elvirtualmodificador no es necesario, pero se permite.Una instancia property_declaration que no tiene ninguna implementación es
abstract; elabstractmodificador no es necesario, pero se permite. Nunca se considera una propiedad implementada automáticamente (§15.7.4).
19.4.5 Eventos de interfaz
Esta cláusula aumenta la descripción de los eventos en las clases §15.8 para los eventos declarados en interfaces.
Los eventos de interfaz se declaran mediante event_declarations (§15.8.1), con las siguientes reglas adicionales:
-
event_modifier no
overrideincluirá . - Una interfaz derivada puede implementar un evento de interfaz abstracta declarado en una interfaz base (§15.8.5).
- Se trata de un error en tiempo de compilación para variable_declarators en una instancia event_declaration contener cualquier variable_initializers.
- Un evento de instancia con los
virtualmodificadores osealeddebe declarar descriptores de acceso. Nunca se considera un evento similar a un campo implementado automáticamente (§15.8.2). - Un evento de instancia con el
abstractmodificador no debe declarar descriptores de acceso. - El tipo de un evento de interfaz será seguro para la entrada.
Indexadores de interfaz 19.4.6
Esta cláusula aumenta la descripción de los indexadores en las clases §15.9 para los indexadores declarados en interfaces.
Los indexadores de interfaz se declaran mediante indexer_declarations (§15.9), con las siguientes reglas adicionales:
indexer_modifier no
overrideincluirá .Un indexer_declaration que tiene un cuerpo de expresión o contiene un descriptor de acceso con un cuerpo de bloque o cuerpo de expresión es
virtual; elvirtualmodificador no es necesario, pero se permite.Un indexer_declaration cuyos cuerpos de descriptor de acceso son punto y coma (
;) esabstract; elabstractmodificador no es necesario, pero se permite.Todos los tipos de parámetro de un indexador de interfaz serán seguros para la entrada (§19.2.3.2).
Cualquier tipo de parámetro de salida o referencia también será seguro para la salida.
Nota: Los parámetros de salida deben ser seguros para la entrada debido a restricciones comunes de implementación. nota final
El tipo de un indexador de interfaz será seguro para la salida si hay un descriptor de acceso get y será seguro para la entrada si hay un descriptor de acceso establecido.
19.4.7 Operadores de interfaz
Esta cláusula aumenta la descripción de operator_declaration miembros en las clases §15.10 para los operadores declarados en interfaces.
Un operator_declaration en una interfaz es la implementación (§19.1).
Es un error en tiempo de compilación para que una interfaz declare una conversión, igualdad o operador de desigualdad.
19.4.8 Constructores estáticos de interfaz
Esta cláusula aumenta la descripción de los constructores estáticos en las clases §15.12 para los constructores estáticos declarados en interfaces.
El constructor estático de una interfaz cerrada (§8.4.3) se ejecuta como máximo una vez en un dominio de aplicación determinado. La ejecución de un constructor estático se desencadena mediante la primera de las siguientes acciones que se deben producir dentro de un dominio de aplicación:
- Se hace referencia a cualquiera de los miembros estáticos de la interfaz.
- Antes de llamar al
Mainmétodo para una interfaz que contiene elMainmétodo (§7.1) en el que comienza la ejecución. - Esa interfaz proporciona una implementación para un miembro y se accede a esa implementación como la implementación más específica (§19.4.10) para ese miembro.
Nota: En el caso de que no se produzca ninguna de las acciones anteriores, es posible que el constructor estático de una interfaz no se ejecute para un programa en el que se creen y usen instancias de tipos que implementan la interfaz. nota final
Para inicializar un nuevo tipo de interfaz cerrada, primero se crea un nuevo conjunto de campos estáticos para ese tipo cerrado determinado. Cada uno de los campos estáticos se inicializa en su valor predeterminado. A continuación, los inicializadores de campo estáticos se ejecutan para esos campos estáticos. Por último, se ejecuta el constructor estático.
Nota: Consulte §19.4.2 para obtener un ejemplo de uso de varios tipos de miembros estáticos (incluido un método Main) declarado dentro de una interfaz. nota final
19.4.9 Tipos anidados de interfaz
Esta cláusula aumenta la descripción de los tipos anidados en las clases §15.3.9 para los tipos anidados declarados en interfaces.
Es un error declarar un tipo de clase, un tipo de estructura o un tipo de enumeración dentro del ámbito de un parámetro de tipo que se declaró con un variance_annotation (§19.2.3.1).
Ejemplo: La declaración de
Cabajo es un error.interface IOuter<out T> { class C { } // error: class declaration within scope of variant type parameter 'T' }ejemplo final
Implementación más específica de la versión 19.4.10
Cada clase y estructura tendrán una implementación más específica para cada miembro virtual declarado en todas las interfaces implementadas por ese tipo entre las implementaciones que aparecen en el tipo o sus interfaces directas e indirectas. La implementación más específica es una implementación única que es más específica que todas las demás.
Nota: La regla de implementación más específica garantiza que el programador resuelva explícitamente la ambigüedad derivada de la herencia de la interfaz de diamante en el momento en que se produce el conflicto. nota final
Para un tipo T que es una estructura o una clase que implementa interfaces I2 y I3, donde I2 y I3 derivan directa o indirectamente de la interfaz I que declara un miembro M, la implementación más específica de M es:
- Si
Tdeclara una implementación deI.M, esa implementación es la implementación más específica. - De lo contrario, si
Tes una clase y una clase base directa o indirecta declara una implementación deI.M, la clase base más derivada deTes la implementación más específica. - De lo contrario, si
I2y son interfaces implementadas porI3yTderivan deI3directa o indirectamente,I2es una implementación más específica queI3.MI2.M. - De lo contrario, ni
I2.MsonI3.Mmás específicos ni se produce un error.
Ejemplo:
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(); }La regla de implementación más específica garantiza que el programador resuelva explícitamente un conflicto (es decir, una ambigüedad derivada de la herencia de diamantes) en el momento en que surge el conflicto. ejemplo final
Acceso a miembros de la interfaz 19.4.11
Se accede a los miembros de la interfaz a través de expresiones de acceso a miembros (§12.8.7) e indexador (§12.8.12.4) del formulario I.M y I[A], donde I es un tipo de interfaz, es una constante, M campo, método, propiedad o evento de ese tipo de interfaz y A es una lista de argumentos del indexador.
En una clase D, con clase Bbase directa o indirecta, donde B implementa directamente o indirectamente la interfaz I y I define un método M(), la expresión base.M() solo es válida si base.M() estáticamente (§12.3) se enlaza a una implementación de en un tipo de M() clase.
Para las interfaces que son estrictamente de herencia única (cada interfaz de la cadena de herencia tiene exactamente cero o una interfaz base directa), los efectos de la búsqueda de miembros (§12.5), la invocación de método (§12.8.10.2) y el acceso al indexador (§12.8.12.4) son exactamente los mismos que para las clases y estructuras: más miembros derivados ocultan menos miembros derivados con el mismo nombre o firma. Sin embargo, en el caso de las interfaces de herencia múltiple, pueden producirse ambigüedades cuando dos o más interfaces base no relacionadas declaran miembros con el mismo nombre o firma. Esta subcláusula muestra varios ejemplos, algunos de los cuales conducen a ambigüedades y otros no. En todos los casos, se pueden usar conversiones explícitas para resolver las ambigüedades.
Example: En el código de ejemplo siguiente
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 } }la primera declaración provoca un error en tiempo de compilación porque la búsqueda de miembros (§12.5) de
CountenIListCounteres ambigua. Como se muestra en el ejemplo, la ambigüedad se resuelve mediante la conversión dexal tipo de interfaz base adecuado. Estas conversiones no tienen costos en tiempo de ejecución y simplemente consisten en ver la instancia como un tipo menos derivado en tiempo de compilación.ejemplo final
Example: En el código de ejemplo siguiente
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 } }la invocación
n.Add(1)seleccionaIInteger.Addaplicando reglas de resolución de sobrecarga de la sección 12.6.4. Del mismo modo, la invocaciónn.Add(1.0)seleccionaIDouble.Add. Cuando se insertan conversiones explícitas, solo hay un método candidato y, por lo tanto, no hay ambigüedad.ejemplo final
Example: En el código de ejemplo siguiente
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 } }el
IBase.Fmiembro está oculto por elILeft.Fmiembro. Por lo tanto, la invocaciónd.F(1)seleccionaILeft.F, aunqueIBase.Fparece no estar oculto en la ruta de acceso que conduce a través deIRight.La regla intuitiva para ocultar en interfaces de herencia múltiple es simplemente esta: si un miembro está oculto en cualquier ruta de acceso, se oculta en todas las rutas de acceso. Dado que la ruta de acceso de
IDerivedaILeftaIBaseocultaIBase.F, el miembro también está oculto en la ruta de acceso deIDerivedaIRightaIBase.ejemplo final
19.5 Nombres de miembros de interfaz calificados
A veces, se refiere a un miembro de interfaz por su nombre de miembro de interfaz completo. El nombre completo de un miembro de interfaz consta del nombre de la interfaz en la que se declara el miembro, seguido de un punto, seguido del nombre del miembro. El nombre completo de un miembro hace referencia a la interfaz en la que se declara el miembro.
Ejemplo: Dadas las declaraciones
interface IControl { void Paint(); } interface ITextBox : IControl { void SetText(string text); }el nombre completo de
PaintesIControl.Painty el nombre completo de SetText esITextBox.SetText. En el ejemplo anterior, no es posible hacer referencia aPaintcomoITextBox.Paint.ejemplo final
Cuando una interfaz forma parte de un espacio de nombres, un nombre de miembro de interfaz completo puede incluir el nombre del espacio de nombres.
Ejemplo:
namespace GraphicsLib { interface IPolygon { void CalculateArea(); } }Dentro del
GraphicsLibespacio de nombres, ambosIPolygon.CalculateAreayGraphicsLib.IPolygon.CalculateAreason nombres calificados de miembro de interfaz para el métodoCalculateArea.ejemplo final
Implementaciones de interfaz 19.6
19.6.1 General
Las interfaces se pueden implementar mediante clases y estructuras. Para indicar que una clase o estructura implementa directamente una interfaz, la interfaz se incluye en la lista de clases base de la clase o estructura.
Una clase o estructura C que implementa una interfaz I debe proporcionar o heredar una implementación para cada miembro declarado en I que C pueda acceder. Los miembros públicos de I se pueden definir en miembros públicos de C. Los miembros no públicos declarados en I que son accesibles pueden C definirse en C mediante la implementación de interfaz explícita (§19.6.2).
Un miembro de un tipo derivado que satisface la asignación de interfaz (§19.6.5), pero no implementa el miembro de interfaz base coincidente introduce un nuevo miembro. Esto ocurre cuando se requiere la implementación explícita de la interfaz para definir el miembro de interfaz.
Ejemplo:
interface ICloneable { object Clone(); } interface IComparable { int CompareTo(object other); } class ListEntry : ICloneable, IComparable { public object Clone() {...} public int CompareTo(object other) {...} }ejemplo final
Una clase o estructura que implementa directamente una interfaz también implementa implícitamente todas las interfaces base de la interfaz. Esto es cierto incluso si la clase o estructura no enumera explícitamente todas las interfaces base de la lista de clases base.
Ejemplo:
interface IControl { void Paint(); } interface ITextBox : IControl { void SetText(string text); } class TextBox : ITextBox { public void Paint() {...} public void SetText(string text) {...} }Aquí, la clase
TextBoximplementaIControlyITextBox.ejemplo final
Cuando una clase C implementa directamente una interfaz, todas las clases derivadas de C también implementan la interfaz implícitamente.
Las interfaces base especificadas en una declaración de clase se pueden construir tipos de interfaz (§8.4, §19.2).
Ejemplo: El código siguiente muestra cómo una clase puede implementar tipos de interfaz construida:
class C<U, V> {} interface I1<V> {} class D : C<string, int>, I1<string> {} class E<T> : C<int, T>, I1<T> {}ejemplo final
Las interfaces base de una declaración de clase genérica cumplirán la regla de unicidad descrita en §19.6.3.
19.6.2 Implementaciones explícitas de miembros de interfaz
Para implementar interfaces, una clase, estructura o interfaz puede declarar implementaciones explícitas de miembros de interfaz. Una implementación explícita de miembro de interfaz es una declaración de método, propiedad, evento o indexador que hace referencia a un nombre de miembro de interfaz completo. Una clase o estructura que implementa un miembro no público en una interfaz base debe declarar una implementación de miembro de interfaz explícita. Una interfaz que implementa un miembro en una interfaz base debe declarar una implementación explícita de miembro de interfaz.
Un miembro de interfaz derivado que satisface la asignación de interfaz (§19.6.5) oculta el miembro de la interfaz base (§7.7.2). El compilador emitirá una advertencia a menos que el new modificador esté presente.
Ejemplo:
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) {...} }Aquí,
IDictionary<int,T>.thisyIDictionary<int,T>.Addson implementaciones explícitas de miembros de interfaz.ejemplo final
Ejemplo: En algunos casos, es posible que el nombre de un miembro de interfaz no sea adecuado para la clase de implementación, en cuyo caso, el miembro de interfaz se puede implementar mediante la implementación explícita del miembro de interfaz. Una clase que implementa una abstracción de archivos, por ejemplo, probablemente implementaría una función miembro
Closeque tenga el efecto de liberar el recurso de archivo e implementaría el métodoDisposede la interfazIDisposablemediante la implementación explícita del miembro de interfaz: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); } }ejemplo final
No es posible acceder a una implementación explícita de miembro de interfaz a través de su nombre de miembro de interfaz calificado en una invocación de método, acceso a propiedades, acceso a eventos o acceso al indexador. Solo se puede tener acceso a una implementación explícita de miembro de instancia de interfaz a través de una instancia de interfaz y, en ese caso, se hace referencia simplemente a ella por su nombre de miembro. Solo se puede tener acceso a una implementación de miembro estático de interfaz explícita a través del nombre de la interfaz.
Es un error en tiempo de compilación que una implementación de miembro de interfaz explícita incluya modificadores (sección 15.6) que no sean extern o async.
Una implementación explícita del método de interfaz hereda cualquier restricción de parámetro de tipo de la interfaz.
Un type_parameter_constraints_clause en una implementación explícita de un método de interfaz solo puede consistir en los classstructprimary_constraints aplicados a type_parameters que, según las restricciones heredadas, se conocen como tipos de referencia o de valor, respectivamente. Cualquier tipo del formulario T? en la firma de la implementación explícita del método de interfaz, donde T es un parámetro de tipo, se interpreta de la siguiente manera:
- Si se agrega una
classrestricción para el parámetro de tipoT, entoncesT?es un tipo de referencia anulable; de lo contrario, - Si no hay ninguna restricción agregada o se agrega una
structrestricción para el parámetroTde tipo, entoncesT?es un tipo de valor nulo.
Ejemplo: a continuación se muestra cómo funcionan las reglas cuando intervienen los parámetros de tipo:
#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 { } }Sin la restricción
where T : classde parámetro de tipo , no se puede invalidar el método base con el parámetro de tipo de referencia. ejemplo final
Nota: Las implementaciones explícitas de miembros de interfaz tienen características de accesibilidad diferentes a otros miembros. Dado que las implementaciones explícitas de miembros de interfaz nunca son accesibles a través de un nombre de miembro de interfaz completo en una invocación de método o un acceso a propiedades, son en cierto modo privadas. Sin embargo, dado que se puede acceder a ellas a través de la interfaz, también son públicas, como la interfaz en la que se declaran. Las implementaciones explícitas de miembros de interfaz sirven para dos propósitos principales:
- Dado que las implementaciones explícitas de miembros de interfaz no son accesibles a través de instancias de clase o estructura, permiten que las implementaciones de interfaz se excluyan de la interfaz pública de una clase o estructura. Esto resulta especialmente útil cuando una clase o estructura implementa una interfaz interna que no es de interés para un consumidor de esa clase o estructura.
- Las implementaciones explícitas de miembros de interfaz permiten la desambiguación de los miembros de interfaz con la misma firma. Sin implementaciones explícitas de miembros de interfaz, sería imposible que una clase, estructura o interfaz tuviera implementaciones diferentes de miembros de interfaz con la misma firma y tipo de valor devuelto, ya que sería imposible que una clase, estructura o interfaz tuviera cualquier implementación en todos los miembros de interfaz con la misma firma, pero con diferentes tipos de valor devuelto.
nota final
Para que una implementación explícita de miembro de interfaz sea válida, la clase, la estructura o la interfaz denominarán una interfaz en su lista de interfaz base o clase base que contenga un miembro cuyo nombre de miembro de interfaz calificado, tipo, número de parámetros de tipo y tipos de parámetro coincidan exactamente con los de la implementación explícita del miembro de interfaz. Si un miembro de función de interfaz tiene una matriz de parámetros, se permite que el parámetro correspondiente de una implementación de miembro de interfaz explícita asociada, pero no es necesario, que tenga el modificador params. Si el miembro de la función de interfaz no tiene una matriz de parámetros, una implementación de miembro de interfaz explícita asociada no tendrá una matriz de parámetros.
Ejemplo: Por lo tanto, en la siguiente clase
class Shape : ICloneable { object ICloneable.Clone() {...} int IComparable.CompareTo(object other) {...} // invalid }la declaración de
IComparable.CompareToda como resultado un error en tiempo de compilación porqueIComparableno aparece en la lista de clases base deShapey no es una interfaz base deICloneable. Del mismo modo, en las declaracionesclass Shape : ICloneable { object ICloneable.Clone() {...} } class Ellipse : Shape { object ICloneable.Clone() {...} // invalid }la declaración de
ICloneable.CloneenEllipseda como resultado un error en tiempo de compilación porqueICloneableno aparece explícitamente en la lista de clases base deEllipse.ejemplo final
El nombre del miembro de interfaz calificado de una implementación explícita del miembro de interfaz hará referencia a la interfaz en la que se declaró el miembro.
Ejemplo: Por lo tanto, en las declaraciones
interface IControl { void Paint(); } interface ITextBox : IControl { void SetText(string text); } class TextBox : ITextBox { void IControl.Paint() {...} void ITextBox.SetText(string text) {...} }La implementación explícita del miembro de interfaz de Paint debe escribirse como
IControl.Paint, no comoITextBox.Paint.ejemplo final
19.6.3 Unicidad de interfaces implementadas
Las interfaces implementadas por una declaración de tipo genérico seguirán siendo únicas para todos los tipos construidos posibles. Sin esta regla, sería imposible determinar el método correcto para llamar a determinados tipos construidos.
Ejemplo: Supongamos que se permitió escribir una declaración de clase genérica de la siguiente manera:
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() {...} }Si esto fuera permitido, sería imposible determinar qué código ejecutar en el siguiente caso:
I<int> x = new X<int, int>(); x.F();ejemplo final
Para determinar si la lista de interfaces de una declaración de tipo genérico es válida, se realizan los pasos siguientes:
- Supongamos que
Les la lista de interfaces especificadas directamente en una clase genérica, estructura o declaración de interfazC. - Agregue a
Lcualquier interfaz base de las interfaces que ya estén enL. - Quite los duplicados de
L. - Si en cualquier tipo construido posible creado a partir de
C, después de que los argumentos de tipo se sustituyan enL, dos interfaces enLson idénticas, la declaración deCno es válida. Las declaraciones de restricciones no se tienen en cuenta al determinar todos los tipos construidos posibles.
Nota: En la declaración de clase
Xanterior, la lista de interfazLconsta del<U>yI<V>. La declaración no es válida porque cualquier tipo construido conUyVdel mismo tipo haría que estas dos interfaces fueran de tipos idénticos. nota final
Es posible que las interfaces especificadas en diferentes niveles de herencia se unifiquen:
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() {...}
}
Este código es válido aunque Derived<U,V> implemente I<U> y I<V>. El código.
I<int> x = new Derived<int, int>();
x.F();
invoca el método en Derived, ya que Derived<int,int>' vuelve a implementar I<int> de forma eficaz (§19.6.7).
19.6.4 Implementación de métodos genéricos
Cuando un método genérico implementa implícitamente un método de interfaz, las restricciones dadas para cada parámetro de tipo de método serán equivalentes en ambas declaraciones (después de reemplazar cualquier parámetro de tipo de interfaz por los argumentos de tipo apropiados), donde los parámetros de tipo de método se identifican mediante posiciones ordinales, de izquierda a derecha.
Ejemplo: En el código siguiente:
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 }el método
C.F<T>implementa de manera explícitaI<object,C,string>.F<T>. En este caso,C.F<T>no es necesario (ni permitido) especificar la restricciónT: object, ya queobjectes una restricción implícita en todos los parámetros de tipo. El métodoC.G<T>implementa implícitamenteI<object,C,string>.G<T>porque las restricciones coinciden con las de la interfaz, después de reemplazar los parámetros de tipo de interfaz por los argumentos de tipo correspondientes. La restricción para el métodoC.H<T>es un error porque los tipos sellados (stringen este caso) no se pueden usar como restricciones. Omitir la restricción también sería un error, ya que las restricciones de implementaciones implícitas del método de interfaz deben coincidir. Por lo tanto, es imposible implementar implícitamenteI<object,C,string>.H<T>. Este método de interfaz solo se puede implementar mediante una implementación explícita de miembro de interfaz: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); } }En este caso, la implementación explícita del miembro de interfaz invoca un método público que tiene restricciones estrictamente más débiles. La asignación de t a s es válida, ya que
Thereda una restricción deT: string, aunque esta restricción no se pueda expresar en el código fuente. ejemplo final
Nota: Cuando un método genérico implementa explícitamente un método de interfaz no se permiten restricciones en el método de implementación (§15.7.1, §19.6.2). nota final
Asignación de interfaz 19.6.5
Una clase o estructura proporcionará implementaciones de todos los miembros abstractos de las interfaces que se enumeran en la lista de clases base de la clase o estructura. El proceso de buscar implementaciones de miembros de interfaz en una clase o estructura de implementación se conoce como asignación de interfaz.
La asignación de interfaz para una clase o estructura C busca una implementación para cada miembro de cada interfaz especificada en la lista de clases base de C. La implementación de un miembro I.Mde interfaz determinado , donde I es la interfaz en la que se declara el miembro M , se determina examinando cada clase, interfaz o estructura S, empezando por C y repitiendo para cada clase base sucesiva e interfaz implementada de C, hasta que se encuentre una coincidencia:
- Si
Scontiene una declaración de una implementación de miembro de interfaz explícita que coincide conIyM, este miembro es la implementación deI.M. - De lo contrario, si
Scontiene una declaración de un miembro público no estático que coincide conM, este miembro es la implementación deI.M. Si más de un miembro coincide, no se especifica qué miembro es la implementación deI.M. Esta situación solo puede producirse siSes un tipo construido en el que los dos miembros declarados en el tipo genérico tienen firmas diferentes, pero los argumentos de tipo hacen que sus firmas sean idénticas.
Se produce un error en tiempo de compilación si no se pueden encontrar implementaciones para todos los miembros de todas las interfaces especificadas en la lista de clases base de C. Los miembros de una interfaz incluyen los miembros que se heredan de las interfaces base.
Los miembros de un tipo de interfaz construido se considera que los parámetros de tipo han sido reemplazados por los argumentos de tipo correspondientes, tal como se especifica en §15.3.3.
Ejemplo: Por ejemplo, dada la declaración de interfaz genérica:
interface I<T> { T F(int x, T[,] y); T this[int y] { get; } }la interfaz construida
I<string[]>tiene los miembros:string[] F(int x, string[,][] y); string[] this[int y] { get; }ejemplo final
Con fines de asignación de interfaz, un miembro A de clase, interfaz o estructura coincide con un miembro B de interfaz cuando:
-
AyBson métodos y el nombre, tipo y listas de parámetros deAyBson idénticos. -
AyBson propiedades, el nombre y el tipo deAyBson idénticos, yAtiene los mismos descriptores de acceso queB(Ase permite que tenga descriptores de acceso adicionales si no es una implementación explícita de un miembro de la interfaz). -
AyBson eventos, y el nombre y tipo deAyBson idénticos. -
AyBson indexadores, el tipo y las listas de parámetros deAyBson idénticos yAtiene los mismos descriptores de acceso, ya queB(Ase permite que tenga descriptores de acceso adicionales si no es una implementación de miembro de interfaz explícita).
Las implicaciones importantes del algoritmo de asignación de interfaz son:
- Las implementaciones explícitas de miembros de interfaz tienen prioridad sobre otros miembros de la misma clase o estructura al determinar la clase o el miembro de estructura que implementa un miembro de interfaz.
- Ni los miembros no públicos ni los estáticos participan en la asignación de interfaz.
Example: En el código de ejemplo siguiente
interface ICloneable { object Clone(); } class C : ICloneable { object ICloneable.Clone() {...} public object Clone() {...} }el miembro
ICloneable.ClonedeCactúa como la implementación deCloneenICloneableporque las implementaciones explícitas de miembros de interfaz tienen prioridad sobre otros miembros.ejemplo final
Si una clase o estructura implementa dos o más interfaces que contienen un miembro con el mismo nombre, tipo y tipos de parámetros, es posible asignar cada uno de esos miembros de interfaz a un solo miembro de clase o estructura.
Ejemplo:
interface IControl { void Paint(); } interface IForm { void Paint(); } class Page : IControl, IForm { public void Paint() {...} }Aquí, los métodos
Painttanto deIControlcomo deIFormse asignan al métodoPaintenPage. Por supuesto, también es posible tener implementaciones de miembros de interfaz explícitas independientes para los dos métodos.ejemplo final
Si una clase o estructura implementa una interfaz que contiene miembros ocultos, es posible que algunos miembros deban implementarse a través de implementaciones explícitas de miembros de interfaz.
Ejemplo:
interface IBase { int P { get; } } interface IDerived : IBase { new int P(); }Una implementación de esta interfaz requeriría al menos una implementación explícita de miembro de interfaz y tomaría una de las siguientes 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() {...} }ejemplo final
Cuando una clase implementa varias interfaces que tienen la misma interfaz base, solo puede haber una implementación de la interfaz base.
Example: En el código de ejemplo siguiente
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) {...} }no es posible tener implementaciones independientes para el
IControldenominado en la lista de clases base, elIControlheredado porITextBoxy elIControlheredado porIListBox. De hecho, no hay ninguna noción de una identidad independiente para estas interfaces. En su lugar, las implementaciones deITextBoxyIListBoxcomparten la misma implementación deIControlyComboBoxsimplemente se considera que implementa tres interfaces,IControl,ITextBoxyIListBox.ejemplo final
Los miembros de una clase base participan en la asignación de interfaz.
Example: En el código de ejemplo siguiente
interface Interface1 { void F(); } class Class1 { public void F() {} public void G() {} } class Class2 : Class1, Interface1 { public new void G() {} }el método
FdeClass1se usa enClass2'simplementación deInterface1.ejemplo final
Herencia de implementación de la interfaz 19.6.6
Una clase hereda todas las implementaciones de interfaz proporcionadas por sus clases base.
Sin volver a implementar explícitamente una interfaz, una clase derivada no puede modificar de ninguna manera las asignaciones de interfaz que hereda de sus clases base.
Ejemplo: En las declaraciones
interface IControl { void Paint(); } class Control : IControl { public void Paint() {...} } class TextBox : Control { public new void Paint() {...} }el método
PaintenTextBoxoculta el métodoPaintenControl, pero no modifica la asignación deControl.PaintenIControl.Paint, y las llamadas aPainta través de instancias de clase e instancias de interfaz tendrán los siguientes efectosControl 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();ejemplo final
Sin embargo, cuando un método de interfaz se asigna a un método virtual en una clase, es posible que las clases derivadas invaliden el método virtual y modifiquen la implementación de la interfaz.
Ejemplo: Reescritura de las declaraciones anteriores a
interface IControl { void Paint(); } class Control : IControl { public virtual void Paint() {...} } class TextBox : Control { public override void Paint() {...} }ahora se observarán los siguientes efectos
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();ejemplo final
Dado que las implementaciones explícitas de miembros de interfaz no se pueden declarar virtuales, no es posible invalidar una implementación de miembro de interfaz explícita. Sin embargo, es perfectamente válido que una implementación de miembro de interfaz explícita llame a otro método y que otro método se pueda declarar virtual para permitir que las clases derivadas lo invaliden.
Ejemplo:
interface IControl { void Paint(); } class Control : IControl { void IControl.Paint() { PaintControl(); } protected virtual void PaintControl() {...} } class TextBox : Control { protected override void PaintControl() {...} }Aquí, las clases derivadas de
Controlpueden especializar la implementación deIControl.Paintinvalidando el métodoPaintControl.ejemplo final
19.6.7 Implementación de la interfaz
Una clase que hereda una implementación de interfaz puede volver a implementar la interfaz incluyiéndola en la lista de clases base.
Una nueva implementación de una interfaz sigue exactamente las mismas reglas de asignación de interfaz que una implementación inicial de una interfaz. Por lo tanto, la asignación de interfaz heredada no tiene ningún efecto en la asignación de interfaz establecida para la nueva implementación de la interfaz.
Ejemplo: En las declaraciones
interface IControl { void Paint(); } class Control : IControl { void IControl.Paint() {...} } class MyControl : Control, IControl { public void Paint() {} }el hecho de que
Controlse asigneIControl.PaintaControl.IControl.Paintno afecta a la nueva implementación enMyControl, que asignaIControl.PaintaMyControl.Paint.ejemplo final
Las declaraciones de miembros públicos heredados y las declaraciones de miembros de interfaz explícita heredada participan en el proceso de mapeo de interfaz para las interfaces reimplementadas.
Ejemplo:
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() {} }Aquí, la implementación de
IMethodsenDerivedasigna los métodos de interfaz aDerived.F,Base.IMethods.G,Derived.IMethods.HyBase.I.ejemplo final
Cuando una clase implementa una interfaz, también implementa implícitamente todas las interfaces base de esa interfaz. Del mismo modo, una nueva implementación de una interfaz también es implícitamente una nueva implementación de todas las interfaces base de la interfaz.
Ejemplo:
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() {...} }Aquí, la nueva implementación de
IDerivedtambién vuelve a implementarIBaseasignandoIBase.FaD.F.ejemplo final
19.6.8 Clases e interfaces abstractas
Al igual que una clase no abstracta, una clase abstracta proporcionará implementaciones de todos los miembros abstractos de las interfaces que se enumeran en la lista de clases base de la clase. Sin embargo, se permite que una clase abstracta asigne métodos de interfaz a métodos abstractos.
Ejemplo:
interface IMethods { void F(); void G(); } abstract class C : IMethods { public abstract void F(); public abstract void G(); }Aquí, la implementación de
IMethodsmapeaFyGen los métodos abstractos, que se sobrescribirán en clases no abstractas que deriven deC.ejemplo final
Las implementaciones explícitas de miembros de interfaz no pueden ser abstractas, pero las implementaciones explícitas de miembros de interfaz pueden llamar a métodos abstractos.
Ejemplo:
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(); }Aquí, las clases no abstractas que derivan de
Cserían necesarias para invalidarFFyGG, lo que proporciona la implementación real deIMethods.ejemplo final
ECMA C# draft specification