Está en la página 1de 16

Interfaces

Las interfaces son un elemento bastante importante en .NET Framework, ya que de hecho se utiliza con bastante frecuencia, en esta leccin veremos que son las interfaces y como utilizarlas en nuestros proyectos, tambin veremos que papel juegan en .NET y cmo aplicar algunas de las definidas en la biblioteca base.

Qu es una interfaz?
Las interfaces son una forma especial de una clase, aunque la diferencia principal con las clases es que las interfaces no contienen cdigo ejecutable, solo definen los miembros y, en el caso de los mtodos, si reciben parmetros y de que tipo son. Las interfaces se utilizan para indicar el "comportamiento" que tendr una clase, o al menos qu miembros debe definir esa clase. Para definir una interfaz en C# tenemos que usar la instruccin interface seguida del nombre y encerrado entre un par de llaves los miembros que define:
public interface IAnimal { //... }

Tal como comentamos en el apartado de la accesibilidad, los miembros de una interfaz siempre son pblicos, pero no debemos indicarlo, ya que se sobreentiende que es as y el compilador producira un error indicndonos que el modificador de accesibilidad no se puede aplicar a ese tipo de elementos. Como los miembros de las interfaces no contienen cdigo ejecutable, solo debemos definir la "firma" del mtodo, propiedad o evento que queremos definir. En el caso de las propiedades, tendremos que indicar los bloques get y/o set que la clase o tipo que la implemente debe definir.
public interface IAnimal { void Desplazar(); void Alimentarse(); string Especie { get; set;} int Edad { get;} }

Nota: Segn las indicaciones de nomenclatura de .NET Framework, se recomienda que todas las interfaces empiecen con una I mayscula seguida del nombre al que hacer referencia la interfaz.

Qu puede contener una interfaz?


Tal como hemos visto anteriormente, una interfaz pude contener cualquiera de los miembros que una clase puede contener, salvo los campos; por tanto, tambin puede contener eventos, pero debido a cmo son tratados los eventos en C#, los cuales veremos con ms detalle en la leccin 4 de este primer mdulo, y la dependencia con los delegados, para poder definir un evento en una interfaz, antes debemos definir el delegado que se usar con ese evento. En el siguiente ejemplo tenemos una interfaz que define todos los tipos de miembros posibles, incluso dos indizadores, en uno de los cuales debemos implementar tanto el bloque get como el set y en el otro, que devuelve un valor del mismo tipo que la interfaz, solo define el bloque get:
// Declaracin del delegado para el evento de la interfaz public delegate void DatosCambiadosEventHandler(); public interface IPrueba { void Mostrar(); string Saludo(string nombre); string Nombre { get; set;} int Edad { get;} string this[int index] { get; set;} IPrueba this[string index] { get;} event DatosCambiadosEventHandler DatosCambiados; }

Una interfaz es un contrato


Siempre que leemos sobre las interfaces, lo primero con lo que nos solemos encontrar es que una interfaz es un contrato. Veamos que nos quieren decir con esa frase. Tal como acabamos de ver, las interfaces solo definen los miembros, pero no el cdigo a usar en cada uno de ellos, esto es as precisamente porque el papel que juegan las interfaces es el de solo indicar que es lo que una clase o estructura puede, o mejor dicho, debe implementar. Si en una clase indicamos que queremos "implementar" una interfaz, esa clase debe definir cada uno de los miembros que la interfaz expone. De esta forma nos aseguramos de que si una clase implementa una interfaz, tambin implementa todos los miembros definidos en dicha interfaz. Cuando una clase implementa una interfaz est firmando un contrato con el que se compromete a definir todos los miembros que la clase define, de hecho el propio compilador nos obliga a hacerlo.

Las interfaces y el polimorfismo


Como comentamos anteriormente, el polimorfismo es una caracterstica que nos permite acceder a los miembros de un objeto sin necesidad de tener un conocimiento exacto de ese objeto (o de la clase a partir del que se ha instanciado), lo nico que tenemos que saber es que ese objeto tiene ciertos mtodos (u otros miembros) a los que podemos acceder. Tambin hemos comentado que las interfaces representan un contrato entre las clases que las implementan, por tanto las interfaces pueden ser, (de hecho lo son), un medio para poner en prctica esta caracterstica de la programacin orientada a objetos. Si una clase implementa una interfaz, esa clase tiene todos los miembros de la interfaz, por tanto podemos acceder a esa clase, que en principio pude sernos desconocida, desde un objeto del mismo tipo que la interfaz.

Usar una interfaz en una clase


Para poder utilizar una interfaz en una clase, o dicho de otra forma: para "implementar" los miembros expuestos por una interfaz en una clase debemos hacerlo a continuacin del nombre de la clase, separndola con dos puntos y a continuacin el nombre de la interfaz:
public class Prueba : IPrueba { }

Si estamos usando el IDE de Visual C# 2005, al escribir el nombre de la interfaz, tal como podemos ver en la figura 2.9, nos indicar s queremos implementar los miembros de esa interfaz.

Figura 2.9. Ayuda del IDE de Visual C# 2005 para implementar una interfaz en una clase

Las opciones ofrecidas por el IDE de C# son dos, aunque la primera ser la que utilicemos con ms frecuencia. En la figura 2.10 podemos ver las dos opciones que nos ofrece:

Figura 2.10. Las dos opciones de implementacin ofrecidas por el IDE de Visual C# 2005

Al pulsar en la primera opcin tendremos el siguiente cdigo:


public class Prueba : IPrueba { #region IPrueba Members

public void Mostrar() { throw new Exception("The method or operation is not implemented."); }

public string Saludo(string nombre) { throw new Exception("The method or operation is not implemented."); }

public string Nombre { get { throw new Exception("The method or operation is not implemented."); } set { throw new Exception("The method or operation is not implemented."); } }

public int Edad { get { throw new Exception("The method or operation is not implemented."); } }

public string this[int index] { get { throw new Exception("The method or operation is not implemented."); } set

{ throw new Exception("The method or operation is not implemented."); } }

public IPrueba this[string index] { get { throw new Exception("The method or operation is not implemented."); } }

public event DatosCambiadosEventHandler DatosCambiados;

#endregion }

Como podemos apreciar, no solo ha aadido las definiciones de cada miembro de la interfaz, sino que tambin aade cdigo extra a cada uno de esos miembros con idea de que nos demos cuenta de que debemos escribir el nuestro propio. Tambin podemos apreciar que lo nico que ha hecho el IDE de C# es declarar cada uno de los miembros de la interfaz, pero no ha relacionado esos miembros con los definidos en la interfaz, para C# no es necesario hacer esa liga, ya que lo nico que le basta para crear esa relacin es que el miembro de la clase se llame igual que el de la interfaz y que tenga el mismo nmero de parmetros, tipos, etc., es decir, que tenga la misma "firma". Adems, comprobamos que todos los miembros los ha definido como pblicos, esto realmente no es obligatorio, y podemos cambiarlos a privado, interno o como queramos, pero debemos saber que en cualquier caso, aunque un mtodo lo hayamos declarado como privado en la clase, siempre ser accesible por medio de un objeto del tipo de la interfaz, pero de esto nos ocuparemos en la siguiente seccin.

Acceder a los miembros implementados


Una vez que tenemos implementada una interfaz en nuestra clase, podemos acceder a esos miembros de forma directa, es decir, usando un objeto creado a partir de la clase:

Prueba p = new Prueba();

p.Mostrar();

O bien de forma indirecta, por medio de una variable del mismo tipo que la interfaz:
Prueba prueba1 = new Prueba(); IPrueba interfaz1;

interfaz1 = prueba1;

interfaz1.Mostrar();

Qu ocurre aqu? Como ya comentamos anteriormente, cuando asignamos variables por referencia, realmente lo que asignamos son referencias a los objetos creados en la memoria, por tanto la variable interfaz1 est haciendo referencia al mismo objeto que prueba1, aunque esa variable solo tendr acceso a los miembros de la clase Prueba que conoce, es decir, los miembros definidos en IPrueba. Si la clase define otros miembros que no estn definidos en la interfaz, la variable interfaz1 no podr acceder a ellos.

Saber si un objeto implementa una interfaz (is y as)


Si las interfaces sirven para acceder de forma annima a los mtodos de un objeto, es normal que en C# tengamos algn mecanismo para descubrir si un objeto implementa una interfaz, as podemos tomar ciertas acciones dependiendo de que un objeto implemente o no una interfaz determinada. Para realizar esta comprobacin podemos usar en una expresin if la instruccin is, de forma que si la variable indicada contiene el tipo especificado despus de is, la condicin se cumple:
if (prueba1 is IPrueba) Console.WriteLine("prueba1 es del tipo IPrueba"); else Console.WriteLine("prueba1 no es del tipo IPrueba");

De esta forma nos aseguramos de que el cdigo se ejecutar solamente si la variable prueba1 contiene una definicin de la interfaz IPrueba. Si lo que queremos hacer es trabajar con objetos que implementen una interfaz en concreto, por ejemplo, podemos tener el siguiente cdigo en el que si el objeto comprobado implementa la interfaz IPrueba llamaremos al mtodo Mostrar:
static void comprobarIPrueba(object o) { if (o is IPrueba) ((IPrueba)o).Mostrar(); }

En este ejemplo, el parmetro es de tipo object, que es el que sirve como base de todas las clases de .NET, hacemos la comprobacin de que implementa la interfaz IPrueba, si es as, haciendo una conversin (cast) llamamos al mtodo Mostrar, hay que notar los parntesis que encierran el cast de la variable o para poder acceder al mtodo, ya que si no lo hacemos el compilador entender que queremos convertir o.Mostrar() al tipo IPrueba, aunque antes de llegar ah, nos dar un error de compilacin indicndonos que object no implementa Mostrar. Pero si en lugar de hacer una simple llamada a un mtodo del objeto necesitamos hacer ms cosas, el cdigo que usaramos sera algo as:
static void comprobarIPrueba(object o) { if (o is IPrueba) { IPrueba unaPrueba = (IPrueba)o; unaPrueba.Mostrar(); Console.WriteLine(unaPrueba.Saludo(unaPrueba.Nombre)); } }

Es decir, que no solo llamamos a un mtodo, sino que hacemos una conversin que asignamos a un objeto y despus usamos ese objeto para llamar a los mtodos y propiedades que queramos. En este ejemplo solo usamos dos de los miembros de ese objeto, pero podramos usar ms. En estos casos, es preferible usar otra instruccin de C#: as. Con as hacemos una conversin al tipo indicado y si no es de ese tipo devuelve un valor nulo, y si lo es, devuelve una referencia al objeto de ese tipo:
static void comprobarIPrueba2(object o) {

IPrueba unaPrueba = o as IPrueba; if (unaPrueba != null) { unaPrueba.Mostrar(); Console.WriteLine(unaPrueba.Saludo(unaPrueba.Nombre)); } }

En este caso, asignamos a la variable unaPrueba el contenido de la variable o, pero tomando la implementacin de IPrueba, (si es que la tiene), en caso de que el valor devuelto no sea nulo, usamos ese objeto. Aunque pueda parecer que es lo mismo usar is o as, al menos por el nmero de lneas de cdigo, debemos saber que el cdigo generado por el compilador al usar la instruccin as es ms compacto que el de la instruccin is.

Implementacin de mltiples interfaces


En .NET las clases solo pueden derivarse de una sola clase (herencia simple), pero lo que si pueden hacer las clases de .NET es implementar ms de una interfaz. Para indicar que implementamos ms de una interfaz lo haremos indicando las interfaces separadas por comas:
public class Prueba2 : IPrueba, IComparable

Ni que decir tiene que debemos implementar todos los miembros de todas las interfaces indicadas.

Mltiple implementacin de un mismo miembro


Como acabamos de comprobar, una misma clase puede implementar ms de una interfaz, y esto nos puede causar una duda: Qu ocurre si dos interfaces definen un mtodo que es idntico en ambas? En principio, no habra problemas, si la creacin de los mtodos implementados la hacemos ayudndonos por el IDE de Visual C# 2005, (ver figuras 2.9 y 2.10), ste creara solo un mtodo el cual servir para las dos interfaces, ya que, como comentamos anteriormente, el compilador de C# lo que espera es que exista un mtodo que se llame de la misma forma que el definido en la interfaz y que tenga la misma "firma".

Por ejemplo, si tenemos las siguientes interfaces y las implementamos en la clase Prueba3, el cdigo quedara de la siguiente forma:
public interface IPrueba2 { void Mostrar(); string Nombre { get; set;} }

public interface IMostrar { void Mostrar(); }

public class Prueba3 : IPrueba2, IMostrar { public void Mostrar() { Console.WriteLine("Mostrar en Prueba3"); } private string nombre; public string Nombre { get{ return nombre; } set{ nombre = value; } } }

Implementacin privada
Tal como hemos visto en la seccin anterior, podemos implementar ms de una interfaz en una misma clase, adems si existen mtodos con la misma "firma" en las interfaces, C# utiliza solo un mtodo para todos los mtodos que tengan esa

firma. Aunque es posible que queramos que no todos los miembros que tengan la misma firma utilicen el mismo mtodo pblico de la clase, o bien puede que queramos implementar una interfaz sin que se expongan los miembros como parte pblica de la clase, en esos casos, tambin podremos acceder a esos miembros privados, aunque siempre por medio de un objeto del mismo tipo que la interfaz. En estos casos, podemos usar lo que el IDE de C# llama "implementacin explcita de una interfaz", (ver figura 2.10). Si nos decidimos por esa opcin, el cdigo generado por el IDE para la clase Prueba3 del ejemplo de la seccin anterior, quedara de esta forma:
public class Prueba3 : IPrueba2, IMostrar { public void Mostrar() { Console.WriteLine("Mostrar en Prueba3"); } private string nombre; public string Nombre { get{ return nombre; } set{ nombre = value; } }

void IMostrar.Mostrar() { Console.WriteLine("Mostrar de IMostrar en Prueba3"); } }

IMostrar.Mostrar() no formara parte de la clase Prueba3, salvo que usemos un objeto del tipo IMostrar, tal como vemos en la figura 2.11:

Figura 2.11. Los miembros de la interfaz IMostrar

Para probar que esto funciona, podemos usar este cdigo:


Prueba3 p3 = new Prueba3(); p3.Nombre = "Juan"; p3.Mostrar(); IMostrar mostrar = p3; mostrar.Mostrar();

Cuya salida sera la siguiente: Mostrar en Prueba3 Mostrar de IMostrar en Prueba3

Resumiendo: podemos implementar varias interfaces y tambin podemos decidir si queremos que esas implementaciones sean visibles a travs de la propia clase o por medio de objetos del tipo de la interfaz implementada.

Dnde podemos implementar las interfaces?


Para ir acabando este tema nos queda por saber, entre otras cosas, dnde podemos implementar las interfaces, es decir, en que tipos de datos podemos usarlas. La implementacin de interfaces la podemos hacer en las clases (class), estructuras (struct) y en otras interfaces (interface). Debido a que una interfaz puede implementar otras interfaces, si en una clase implementamos una interfaz que a su vez implementa otras, esa clase tendr definidas cada una de las interfaces, lo mismo ocurre con una clase que "se

derive" de otra clase que implementa alguna interfaz, la nueva clase tambin incorporar esa interfaz.

Si en una clase implementamos una interfaz que a su vez implementa otras interfaces, esa clase tendr definiciones de todos los miembros de todas las interfaces, por ejemplo, si tenemos la siguiente definicin de la interfaz IPrueba3 que "implementa" la interfaz IMostrar:
public interface IPrueba3 : IMostrar { string Saludo(string nombre); string Nombre{get; set;} }

Y la clase Prueba4 implementa IPrueba3, la definicin de los miembros quedara de la siguiente forma:
public class Prueba4 : IPrueba3 { #region IPrueba3 Members public string Saludo(string nombre) { // El mtodo Saludo de IPrueba3 } // La propiedad Nombre de IPrueba3 public string Nombre { get{ ... } set{ ... } } #endregion

#region IMostrar Members public void Mostrar() {

// El mtodo Mostrar de IMostrar } #endregion }

En este cdigo, el mtodo Mostrar se indica mediante la interfaz IMostrar, pero tambin se puede hacer por medio de IPrueba3.Mostrar, ya que IPrueba3 tambin lo implementa (o hereda).

Un ejemplo prctico usando una interfaz de .NET


Tal como comentamos al principio, el propio .NET est "plagado" de interfaces, cada una de ellas tiene un fin concreto, por ejemplo, si queremos definir una clase que pueda ser clasificada por el propio .NET, esa clase debe implementar la interfaz IComparable, ya que el mtodo Sort, (de la clase que contiene los elementos del tipo definido por nosotros), que es el encargado de clasificar los elementos, har una llamada al mtodo IComparable.CompareTo de cada uno de los objetos que queremos clasificar, por tanto, si la clase no ha definido esa interfaz, no podremos clasificar los elementos que contenga. En el siguiente cdigo tenemos la definicin de una clase llamada Empleado que implementa la interfaz IComparable y en el mtodo CompareTo hace la comprobacin de que objeto es mayor o menor, si el de la propia clase o el indicado en el parmetro de esa funcin:
public class Empleado : IComparable { public string Nombre;

public Empleado() {} public Empleado(string nombre) { this.Nombre = nombre; }

public int CompareTo(object obj) {

if (obj is Empleado) { Empleado e1 = ((Empleado)obj);

return String.Compare(this.Nombre, e1.Nombre); } else return 0; } }

En el mtodo CompareTo hacemos una comprobacin de que el objeto con el que debemos realizar la comparacin es del tipo Empleado, en ese caso convertimos el objeto pasado en uno del tipo Empleado y comparamos los nombres. Si el objeto que recibe el mtodo no es del tipo Empleado, devolvemos un cero, para indicar que no haga ninguna clasificacin, ya que ese valor indica que los dos objetos son iguales. Esta clase la podemos usar de esta forma:
// Una coleccin de datos del tipo Empleado List<Empleado> empleados = new List<Empleado>();

// Aadimos varios empleados a la coleccin empleados.Add(new Empleado("Pepe")); empleados.Add(new Empleado("Bernardo")); empleados.Add(new Empleado("Juan")); empleados.Add(new Empleado("Ana"));

// La clasificamos empleados.Sort();

// Mostramos los nombres clasificados foreach (Empleado e1 in empleados)

Console.WriteLine(e1.Nombre);

Al llamar al mtodo Sort de la coleccin empleados, se utilizar la implementacin del mtodo CompareTo que es el encargado de indicar en que orden deben ir los datos. Si nuestra clase Empleado tuviese que hacer la clasificacin basndose en otras propiedades, sera en el mtodo CompareTo donde habra que hacer la decisin de que valor devolver, teniendo en cuenta de que un valor cero indica que los dos objetos son iguales, un valor menor que cero que nuestro objeto debe ir antes que el indicado en el parmetro y un valor mayor de cero para indicar que nuestro objeto debe ir despus del indicado. El resultado de este cdigo sera el siguiente: Ana Bernardo Juan Pepe

También podría gustarte