Para definir una clase o estructura que implemente una o más interfaces basta incluir los nombres de las mismas como si de una clase base se tratase -separándolas con comas si son varias o si la clase definida hereda de otra clase- y asegurar que la clase cuente con definiciones para todos los miembros de las interfaces de las que hereda -lo que se puede conseguir definiéndolos en ella o heredándolos de su clase padre.
Las definiciones que se den de miembros de interfaces han de ser siempre públicas y no pueden incluir override, pues como sus miembros son implícitamente abstract se
sobreentiende. Sin embargo, sí pueden dársele los modificadores como virtual ó abstract
y usar override en redefiniciones que se les den en clases hijas de la clase que implemente la interfaz.
Cuando una clase deriva de más de una interfaz que incluye un mismo miembro, la implementación que se le dé servirá para todas las interfaces que cuenten con ese miembro. Sin embargo, también es posible dar una implementación diferente para cada una usando una implementación explícita, lo que consiste en implementar el miembro sin el modificador public y anteponiendo a su nombre el nombre de la interfaz a la que
pertenece seguido de un punto (carácter .)
Cuando un miembro se implementa explícitamente no puede dársele modificadores como en las implementaciones implícitas, ni siquiera virtual o abstract. Una forma de
simular los modificadores que se necesiten consiste en darles un cuerpo que lo que haga sea llamar a otra función que sí cuente con esos modificadores.
El siguiente ejemplo muestra cómo definir una clase CL que implemente la interfaz IC: class CL:IC
{
public int PropiedadA {
get {return 5;}
set {Console.WriteLine(“Asignado{0}a PropiedadA”, value);} }
void IA.Común(int x) {
Console.WriteLine(“Ejecutado Común() de IA”); }
public int this[int índice] {
get { return 1;}
set { Console.WriteLine(“Asignado {0} a indizador”, value); } }
void IB.Común(int x) {
Console.WriteLine(“Ejecutado Común() de IB”); }
public event D EventoC; }
Como se ve, para implementar la interfaz IC ha sido necesario implementar todos sus miembros, incluso los heredados de IA y IB, de la siguiente manera:
• Al EventoC se le ha dado la implementación por defecto, aunque si se quisiese se podría haber dado una implementación específica a sus bloques add y remove.
• Al método Común() se le ha dado una implementación para cada versión heredada de una de las clases padre de IC, usándose para ello la sintaxis de implementación explícita antes comentada. Nótese que no se ha incluido el modificador public en la implementación de estos miembros.
• A la PropiedadA se le ha dado una implementación con un bloque set que no aparecía en la definición de PropiedadA en la interfaz IA. Esto es válido hacerlo siempre y cuando la propiedad no se haya implementado explícitamente, y lo mismo ocurre con los indizadores y en los casos en que en vez de set sea get el bloque extra implementado.
Otra utilidad de las implementaciones explícitas es que son la única manera de conseguir poder dar implementación a métodos ocultados en las definiciones de interfaces. Por ejemplo, si tenemos:
interface IPadre { int P{get;} } interface IHija:Padre { new int P(); }
La única forma de poder definir una clase donde se dé una implementación tanto para el método P() como para la propiedad P, es usando implementación explícita así:
class C: IHija { void IPadre.P {} public int P() {…} } O así: class C: IHija
El lenguaje de programación C# Tema 15: Interfaces { public void P () {} int IHija.P() {} } O así: class C: IHija { void IPadre.P() {} int IHija.P() {} }
Pero como no se puede implementar es sin ninguna implementación explícita, pues se produciría un error al tener ambos miembros las misma signatura. Es decir, la siguiente definición no es correcta:
class C: IHija {
public int P() {} // ERROR: Ambos miembros tienen la misma signatura public void P() {}
}
Es posible reimplementar en una clase hija las definiciones que su clase padre diese para los métodos que heredó de una interfaz. Para hacer eso basta hacer que la clase hija también herede de esa interfaz y dar en ella las definiciones explícitas de miembros de la interfaz que se estimen convenientes, considerándose que las implementaciones para los demás serán las heredadas de su clase padre. Por ejemplo:
using System; interface IA { void F(); } class C1: IA { public void F() { Console.WriteLine("El F() de C1"); } } class C2: C1, IA {
void IA.F() // Sin implementación explícita no redefiniría, sino ocultaría {
Console.WriteLine("El F() de C2"); }
public static void Main() {
IA obj = new C1(); IA obj2 = new C2(); obj.F();
} }
Reimplementar un miembro de una interfaz de esta manera es parecido a redefinir los miembros reimplementados, sólo que ahora la redefinición sería sólamente accesible a través de variables del tipo de la interfaz Así, la salida del ejemplo anterior sería:
El F() de C1 El F() de C2
Hay que tener en cuenta que de esta manera sólo pueden hacerse reimplementaciones de miembros si la clase donde se reimplementa hereda directamente de la interfaz implementada explícitamente o de alguna interfaz derivada de ésta. Así, en el ejemplo anterior sería incorrecto haber hecho:
class C2:C1 //La lista de herencias e interfaces implementadas por C2 sólo incluye a C1 {
void IA.f(); // ERROR: Aunque C1 herede de IA, IA no se incluye directamente
// en la lista de interfaces implementadas por C2 }
Es importante señalar que el nombre de interfaz especificado en una implementación explícita ha de ser exactamente el nombre de la interfaz donde se definió el miembro implementado, no el de alguna subclase de la misma. Por ejemplo:
interface I1 { void F() } interface I2 {} class C1:I2 {
public void I2.F(); //ERROR: habría que usar I1.F() }
En el ejemplo anterior, la línea comentada contiene un error debido a que F() se definió dentro de la interfaz I1, y aunque también pertenezca a I2 porque ésta lo hereda de I1, a la hora de implementarlo explícitamente hay que prefijar su nombre de I1, no de I2.