You are on page 1of 38

Programacin Orientada a Objetos en .

NET
Introduccin:

La programacin orientada a objetos (POO) nos permite escribir cdigo menos propenso a fallos adems de permitirnos la reutilizacin de cdigo de forma ms conveniente. En este artculo veremos las caractersticas de la POO desde el punto de vista de los lenguajes de .NET Framework y cmo utilizar los distintos elementos que nos permitirn crear cdigo que sea ms fcil de escribir y mantener.

LA PROGRAMACIN ORIENTADA A OBJETOS

En Todo Programacin existe una seccin denominada Cuadernos de Principiantes donde se estudia algoritmia y estructuras de datos a nivel iniciacin. Est planificado que se estudie a nivel terico la programacin orientada a objetos, por tanto para aquellos que no tengis nocin alguna sobre POO mejor guarda a buen recaudo este nmero de TP y espera a aprender los conceptos tericos necesarios para luego aplicarlos en el marco, nunca mejor dicho, de .NET.

LOS PILARES DE LA POO

Recordemos que tres son las principales caractersticas de un lenguaje orientado a objetos, es decir, se considera que un lenguaje est totalmente orientado a objetos si es capaz de proveer estas tres caractersticas:
y y y

Encapsulacin Herencia Polimorfismo

Veamos una pequea descripcin de cada una de ellas y despus las ampliaremos para comprender mejor su significado y cmo puede ayudarnos a crear aplicaciones que aprovechen todas las posibilidades que nos da la POO. La ENCAPSULACIN es la cualidad de unificar los datos y la forma de manipularlos, de esta forma podemos ocultar el funcionamiento de una clase y exponer solo los datos que manipula (mediante propiedades), as como proveer de medios para poder manipular dichos datos (mediante mtodos). De esta forma solo exponemos al mundo exterior la informacin y la forma de manipularla, ocultando los detalles usados para manejar esos datos y, lo que

es ms importante, evitando que nadie manipule de una forma no controlada dicha informacin. La HERENCIA es la cualidad de poder crear nuevas clases (o tipos) basadas en otras clases, de forma que la nueva clase obtenga todas las caractersticas de la clase que ha heredado, tanto los datos que contiene como la forma de manipularlos, pudiendo aadir nuevas caractersticas e incluso cambiar el comportamiento de algunas de las incluidas en la clase base, (siempre que as se haya previsto). Mediante la herencia podemos crear de forma fcil una jerarqua de clases que comparten un mismo comportamiento bsico pero que cada nueva generacin puede tener (y de hecho tiene) un nuevo comportamiento. El POLIMORFISMO es la cualidad de implementar de forma particular algunas de las caractersticas que tienen las clases, de forma que cuando necesitemos usarlas no nos preocupe la implementacin interna que cada una tenga, lo que realmente nos interesa o nos debe importar es que podemos usar esas caractersticas e incluso podamos acceder a ellas de forma annima... o casi.

OTROS CONCEPTOS DE LA POO

Tal como tendrs oportunidad de ver en los Cuadernos de Principiantes y lo indicado en el cuadro Los pilares de la POO, la POO se basa en tres caractersticas que son comunes a todos los lenguajes orientados a objetos, pero si tenemos esas caractersticas y no sabemos cmo aplicarlas, la verdad es que no nos ser de mucha utilidad. Pero antes de ver algo de cdigo concreto, creo que es importante que aprendamos otros conceptos relacionados tambin con la POO, pero esta vez desde un punto de vista del programador, es decir, vamos a dejar en parte la teora y vamos a ser algo ms prcticos, ya que los siguientes conceptos sern con los que tendremos que "bregar" a diario. Adems nos interesa conocerlos para aprovechar lo que un lenguaje de programacin orientado a objetos nos ofrece, si bien, es posible que, al menos de forma genrica, no todos los lenguajes dispongan de ellos. Por eso, aunque lo que se ha dicho y se diga a continuacin ser vlido para cualquier lenguaje orientado a objetos, lo vamos a enfocar desde el punto de vista de .NET Framework, ms concretamente desde el punto de vista del programador de Visual Basic .NET y C#.

LAS CLASES Y ESTRUCTURAS

Como hemos estado mencionando, en los lenguajes orientados a objetos, existe el concepto clase. Cuando hablamos de clases, tambin podemos extenderlo a estructuras, de hecho,

para los programadores de C++ una clase no es ms que una estructura que se comporta de forma diferente. Una clase es una pieza de cdigo en la que podemos definir una serie de datos y al mismo tiempo unos mtodos (funciones o procedimientos) que nos permitirn acceder a esos datos. Cuando definimos una clase, lo que estamos haciendo es definir una plantilla, a partir de la cual podemos crear objetos en la memoria. Por tanto, la clase es el molde con el cual podemos crear nuevos objetos. Para poder crear algo "tangible" a partir de una clase, tenemos que crear en la memoria un nuevo objeto del tipo de la clase, en estos casos lo que decimos es que instanciamos un nuevo objeto de la clase. A partir de ese momento tendremos algo real con lo que podemos trabajar: una instancia de la clase, es decir, la definicin realizada en la clase se ha convertido en un objeto al que podemos acceder y que podemos empezar a utilizar, dndole nuevos valores a los datos que manipula y usando las funciones que nos permiten manipular dichos datos. La diferencia principal entre una clase y una estructura es la forma en que se crean los objetos que representan a esas "ideas". Los objetos creados a partir de las clases son objetos por referencia, es decir, si declaramos una variable para manipular ese objeto, lo que tendremos ser una referencia (o puntero) a una direccin de memoria en la que realmente est el objeto. Mientras que los objetos creados a partir de una estructura se almacenan de forma diferente, en lugar de "apuntar" a una direccin de memoria en la que se encuentra el objeto, es como si las variables declaradas como estructuras fuesen realmente el objeto permitindonos hacer ciertas operaciones y manipulaciones que los objetos obtenidos a partir de una clase no pueden realizar de la misma forma. Esto lo veremos despus con ms detalle.

NOTA: Clases En .NET siempre usamos una clase para escribir cualquier tipo de cdigo. Por tanto, hagamos lo que hagamos en .NET Framework, debemos hacerlo dentro de una clase. Esto no quiere decir que siempre tengamos que usar las caractersticas de la POO, ya que si simplemente queremos hacer una aplicacin que muestre un mensaje en la consola, el cdigo no tiene porqu usar la herencia, el polimorfismo o la encapsulacin, simplemente escribimos el cdigo que muestre el mensaje y asunto arreglado, pero lo que si podremos hacer es usar algunas de las "otras" ventajas que nos aporta la programacin orienta a objetos.

INTERFACES

Cuando hablamos de polimorfismo, ineludiblemente tenemos que hablar de las interfaces, ya que, principalmente, nos posibilita utilizar esta caracterstica de la POO. La pregunta es: qu es una interfaz? Aqu no hablamos de "interfaces de usuario", es decir, lo que se mostrar al usuario de nuestra aplicacin, sino a una clase especial en la que solamente se definen los mtodos y propiedades que una clase que la implemente debe codificar. Las interfaces representan un contrato, de forma que cualquier clase que la implemente debe utilizar los miembros de la interfaz usando la misma forma en que sta la ha descrito: mismo nmero de argumentos, mismo tipo de datos devuelto, etc. Gracias a la implementacin de interfaces podemos crear relaciones entre clases que no estn derivadas de la misma clase base, pero que tengan mtodos comunes, al menos en la forma, aunque no necesariamente en el fondo. Anteriormente usamos el ejemplo del mtodo Guardar, este mtodo se puede definir en una interfaz, las clases que quieran implementar un mtodo Guardar "estandarizado" firmarn un contrato con la interfaz que lo especifica, aunque la forma interna de funcionamiento solo atae al programador de la clase, lo importante es saber que cualquier clase que haya firmado ese contrato tendr que seguir las condiciones impuestas por la interfaz, de esta forma todas las clases tendrn un mtodo Guardar "compatible", aunque, tal como mostramos antes, cmo se realice esa accin de guardar no debe preocuparnos, simplemente nos fiaremos de que se ha implementado adecuadamente para almacenar los datos que la clase manipula.

NOTA: HERENCIA MLTIPLE Y HERENCIA SIMPLE En C++ y algunos otros lenguajes orientados a objetos se permiten la herencia mltiple, es decir, una clase se puede derivar de varias clases a la vez. Los lenguajes de .NET Framework, usan lo que se denomina herencia simple, es decir, una clase solo se puede derivarse directamente de otra clase, si bien se permite implementar mltiples interfaces. Pero debido a cmo funciona la herencia, cualquier clase derivada a partir de otra, heredar indirectamente todas las clases e interfaces que la clase base haya heredado o declarado. Adems, en .NET, todas las clases siempre se derivan de la clase base Object que es la clase que est en la parte superior de la jerarqua de clases.

CONSTRUCTORES Y DESTRUCTORES, EL PUNTO DE INICIO Y FINAL DE LAS CLASES

Cuando creamos un objeto a partir de una clase, se sigue un proceso, el cual empieza en el momento en que decidimos crear una nueva instancia de dicha clase. En estos casos, el compilador utiliza lo que se llama el constructor de la clase. Siempre que se crea un nuevo objeto en la memoria est involucrado el constructor de la clase. Los constructores son procedimientos especiales (funciones que no devuelven un valor) en

los que podemos escribir toda la lgica que debe usarse para la correcta creacin del objeto. Por ejemplo, podemos inicializar las variables usadas, podemos asignarle algunos valores predeterminados, etc. De igual forma, cuando un objeto ya no se necesita ms, se destruye mediante una llamada al destructor de la clase. En .NET la destruccin de los objetos suele hacerse de forma automatizada, es decir, a diferencia de lo que ocurre en otros entornos de programacin, no es necesario destruir explcitamente un objeto para eliminarlo de la memoria, esa gestin de limpieza de objetos la realiza el recolector de basura (Garbage Collector, GC) de .NET, el cual decide cuando un objeto no se necesita ms y en ese caso lo elimina dejando libre la memoria utilizada para otros menesteres.

SOBRECARGA (OVERLOAD)

Una de las caractersticas que tambin nos ofrece los lenguajes orientados a objetos es la posibilidad de definir varias funciones de las clases con un mismo nombre, de esta forma, podremos crear versiones diferentes, por ejemplo para que reciban argumentos de distintos tipos sin necesidad de cambiarle el nombre. Supongamos que queremos hacer una funcin que realice cualquier tipo de operacin sobre dos valores numricos, sera lgico pensar que si esos valores son de tipo entero, el resultado que devuelva la funcin tambin debera ser de tipo entero, en caso de que los valores a usar en la operacin son de tipo flotante, el resultado podra devolverlo de ese mismo tipo. En los lenguajes no orientado a objetos, tendramos que crear dos funciones con nombres diferentes, por ejemplo: sumaInt y sumaFloat. Pero la sobrecarga nos permite crear dos funciones que se llamen suma y el compilador utilizar la adecuada segn el tipo de datos que pasemos como argumentos. El nico requisito para poder crear sobrecargas de mtodos es que las diferentes versiones se diferencien en los argumentos, ya sea porque sean de diferentes tipos de datos o porque el nmero de argumentos usados sea diferente, de esa forma el compilador no tendr ningn problema en saber cual debe usar en cada ocasin. La sobrecarga la podemos aplicar tanto a los constructores como a cualquier otro mtodo de la clase.

NOTA: Sobrecarga No existir la posibilidad de crear mtodos sobrecargados si solamente se diferencian en el tipo de datos devuelto, ya que en esos casos el compilador no podr decidir correctamente qu mtodo debe utilizar.

LOS MIEMBROS DE LAS CLASES: CAMPOS, PROPIEDADES Y MTODOS

Como hemos comentado, las clases manejan datos y proveen de funciones para acceder a esos datos. Para ser precisos, los datos se mantienen o almacenan internamente en los campos declarados en las clases. Los campos no son otra cosa que variables declaradas en la clase, habitualmente declaradas de forma privada. Por qu declaradas de forma privada? Precisamente para seguir o cumplir la caracterstica de encapsulacin de la POO, es decir, los datos no deben exponerse de forma directa. Si queremos exponer los datos, podemos usar las propiedades. Las propiedades son funciones especiales que nos permiten acceder a esos datos, aunque para ser ms precisos, las propiedades realmente representan a los datos que una clase contiene, al menos de forma pblica. De esa forma podemos "controlar" la forma en que se leen o asignan esos datos, ya que las propiedades realmente son funciones en las que podemos escribir cdigo para controlar los valores asignados o ledos. Los mtodos nos permitirn realizar acciones sobre los datos, por ejemplo devolver un rango de valores o simplemente una representacin amigable de la informacin contenida. Debido a que algunas veces los mtodos devolvern algo y otras no, podemos usar tanto funciones que devuelvan o no un valor.

NOTA: Mtodos En C# los mtodos siempre son funciones, que devolvern un tipo concreto o el valor especial void, que se usa para indicar que una funcin no devolver ningn valor. En Visual Basic .NET existen dos tipos de mtodos distintos, las funciones (Function) que siempre devuelven un valor y los procedimientos (Sub) que no devuelven ningn valor.

Adems de los campos, mtodos y propiedades, las clases tienen otros miembros como los eventos y las enumeraciones. stos nos permitirn recibir notificaciones de cuando algo ocurra (eventos) o declarar ciertos valores constantes que podemos usar para restringir algunos valores asignados a las propiedades o que nos permitan seleccionar de forma coherente la informacin que queremos obtener (enumeraciones).

EL MBITO DE LOS MIEMBROS DE LAS CLASES

Las buenas formas de trabajar con las clases nos indican que los campos deberan ser privados, con idea de que no estn accesibles de forma externa. Por supuesto tambin

podemos definir otros miembros de las clases de forma privada, esto es til cuando la funcionalidad es para uso exclusivo de otros miembros de la clase. Pero cuando queremos exponer la funcionalidad fuera de la clase podemos hacerla de varias formas, aqu es donde entran en juego el mbito de los miembros de las clases. El mbito lo aplicamos para permitir el acceso desde cualquier cdigo fuera de la clase o para restringir ese acceso. Dependiendo de cmo queramos que se acceda a los miembros de la clase podemos usar distintos modificadores de mbito. Veamos los que podemos usar y cuando y porqu usarlos. La instruccin entre parntesis ser la que tendremos que usar en C#.
y

y y

y y

Private (private). Para uso privado. Cuando declaramos un miembro como privado slo lo podremos acceder desde la propia clase. Este es el ms restrictivo y el que se recomienda para los campos y las funciones de uso interno. Protected (protected). Uso protegido. Los elementos declarados como protegidos slo estarn accesibles, adems de en la propia clase, por cualquier clase derivada. Friend (internal). Para uso dentro de la propia aplicacin. Cuando declaramos un miembro con este modificador, solo podremos acceder a l desde la propia clase o desde cualquier cdigo que se encuentre en el mismo ensamblado (proyecto). Protected Friend (protected internal). Una mezcla de Protected y Friend, es decir solo accesible desde las clases derivadas o desde el mismo proyecto. Public (public). Este modificador de mbito nos permite exponer pblicamente cualquier miembro de la clase, de forma que no haya restricciones para acceder a l.

NOTA: mbito Los miembros de una clase los podemos declarar sin especificar el mbito, dependiendo del lenguaje de programacin que usemos se aplicar un modificador de mbito u otro. En C#, si no indicamos el mbito, las declaraciones se consideran privadas, mientras que en Visual Basic .NET el mbito predeterminado es Friend.

MIEMBROS VIRTUALES, NO REEMPLAZABLES Y ABSTRACTOS

Para ir terminando la parte "terica" sobre la programacin orientada a objetos, veamos cmo podemos darle un significado distinto a los miembros de una clase, dependiendo de cmo queramos que se comporten y por extensin cmo podemos utilizarlos tanto en la propia clase como en las clases derivadas. Como hemos comentado, cuando una clase hereda a otra podemos modificar el comportamiento de los miembros heredados, pero estos solamente se podrn modificar si la clase base as lo contempla o lo permite. De forma predeterminada, al menos en .NET,

cuando declaramos un mtodo o una propiedad en una clase, solo podremos acceder a l desde una instancia creada (un objeto) en memoria, desde donde podemos usarlos depender del mbito que le hayamos aplicado. De igual forma, el que una clase que se base en otra, pueda crear su propia versin de ese mtodo o propiedad depender de que la clase base lo haya declarado como virtual (Overridable en VB .NET). Los mtodos virtuales sern los que podamos sobrescribir en las clases derivadas, de forma que podamos crear nuestras propias versiones. En .NET los miembros de una clase no son virtuales de forma predeterminada. Por tanto, si queremos que la clase derivada pueda crear su propia versin de un mtodo, debemos declararlo como virtual o "redefinible". Si en una clase base hemos definido un mtodo virtual, pero posteriormente queremos que no se pueda seguir redefiniendo en otras clases derivadas, debemos indicarlo usando el modificador NotOverridable, el cual se usar junto con Overrides, ya que sobrescribe un miembro de la clase base y como adems lo queremos marcar como no virtual, debemos usar las dos instrucciones: Overrides NotOverridable, (en C# se indicar con override sealed). Pero tambin se nos puede presentar el caso contrario, en el que queremos que un mtodo forzosamente haya que redefinirlo en las clases derivadas, en esos casos la clase base que lo define no incluye ninguna implementacin, es decir, el mtodo no contiene cdigo ejecutable, solo la definicin, (como ocurre con las interfaces). Se dice que estos mtodos son abstractos porque solo se ha definido en la forma y no se ha implementado ningn cdigo ejecutable. En Visual Basic se definen usando el modificador MustOverride (asbtract en C#). Estos mtodos abstractos solo se pueden declarar en clases abstractas (MustInherit en Visual Basic, abstract en C#) y por la necesidad de tener que redefinirlos, son implcitamente virtuales. Las instrucciones o modificadores que nos permiten crear estos tipos de miembros son:
y

Overridable (virtual). Los miembros virtuales son los que las clases derivadas puedes sobrescribir para crear su propia versin. Para indicar en una clase derivada que estamos sobrescribiendo dicho elemento, usaremos Overrides (override en C#). NotOverridable (sealed). Los miembros virtuales heredados se pueden marcar como no virtuales, (las siguientes clases derivadas no podrn sobrescribirlos), usando esta instruccin a continuacin de Overrides (override). MustOverride (abstract). Un mtodo que se debe reemplazar en la clase derivada y que solo se define en la clase base sin ningn cdigo ejecutable. Los mtodos abstractos son virtuales por defecto y solo se pueden declarar en clases abstractas.

MIEMBROS DE INSTANCIAS Y COMPARTIDOS

En todos estos casos, los miembros de la clase siempre son miembros de instancia, es decir, solo existen en la memoria cuando se crea un nuevo objeto (se crea una nueva instancia). Pero es posible que nos interese crear miembros compartidos, es decir, miembros que

pertenecen a la propia clase, no a ninguna instancia en particular. Dndonos la oportunidad de poder acceder siempre a ellos, independientemente de que hayamos creado o no un nuevo objeto en la memoria. En estos casos decimos que creamos miembros compartidos (estticos en el argot de C#/C++), esta diferencia de "nomenclatura" dependiendo del lenguaje de programacin, es porque para definir un miembro perteneciente a la clase y no a una instancia en particular, usaremos en Visual Basic la instruccin Shared (compartido), mientras que en C# se usar la instruccin static (esttico). Resumiendo, Shared (static), declara un miembro compartido, los miembros compartidos no pertenecen a ninguna instancia en particular y solamente pueden acceder a campos u otros miembros tambin compartidos. Desde los miembros de instancia podemos acceder tanto a miembros compartidos como de instancia.

NOTA: STATIC En Visual Basic existe tambin la instruccin Static, (que no tiene equivalencia en C#), en este caso se utiliza con variables declaradas en un procedimiento y sirven para indicar que esa variable debe mantener el valor entre distintas llamadas a dicho procedimiento, a diferencia del resto de variables que solo existen mientras se ejecuta el cdigo del procedimiento y cuyos valores se pierden al finaliza la ejecucin del mismo.

CLASES ABSTRACTAS Y SELLADAS

De igual forma que podemos modificar el comportamiento de los miembros de una clase, tambin podemos cambiar el comportamiento predeterminado de las clases. Como hemos comentado, las clases de .NET pueden usarse para crear nuevas clases derivadas de ellas, esta es la funcionalidad predeterminada, pero no obligatoria, es decir, si queremos podemos usar una clase por si misma o como base de otras. Pero tambin podemos hacer que una clase solamente se use como clase base de otras, pero no se puedan usar para crear nuevas instancias en memoria, este es el caso de las clases abstractas. Una clase abstracta puede contener miembros abstractos, miembros normales o virtuales. Para indicar que una clase es abstracta, se usa el modificador MustInherit en Visual Basic o abstract en C#. La contrapartida de las clases abstractas son las clases selladas o clases que no se pueden usar como clases base, en estos casos las clases las definiremos como NotInheritable en Visual Basic o sealed en C#. Como es lgico, las clases no heredables se pueden usar en ocasiones en las que no nos interese que nadie cambie el comportamiento que tiene, por tanto no se podrn declarar miembros virtuales ni abstractos, ya que no tendra ningn sentido.

Las estructuras siempre son "clases selladas", (aunque no se use un modificador para indicarlo), por tanto, no podemos usarlas como base de otras.

PARTE 2

VISUAL BASIC .NET Y LA POO

Tal como comentamos en el artculo anterior, uno de los pilares de la POO es la herencia. Mediante la herencia podemos definir clases totalmente operativas y crear nuevas clases basadas en ellas de forma que hereden toda la funcionalidad de la clase base y nos permita ampliarla. Por tanto, vamos a empezar viendo cmo definir una clase y cmo aplicar en Visual Basic .NET el resto de conceptos relacionados con la programacin orientada a objetos.

Nota: Este artculo se public en Octubre de 2004, y por tanto todo lo aqu explicado est relacionado con la versin 1.1 de .NET Framework. La versin de .NET Framework que hay actualmente (a la hora de escribir esta nota) es la 3.0, (que en el fondo es la misma que la 2.0), y se han introducido ciertos cambios o mejoras, pero bsicamente lo aqu explicado sigue siendo tan vlido ahora como hace dos aos.

DEFINIR CLASES EN VISUAL BASIC .NET

Antes de poder usar las caractersticas de la POO tendremos primero que aprender a declarar una clase. La declaracin de una clase se define usando la instruccin Class seguida del nombre de la clase y termina usando las instrucciones End Class. Dentro de ese bloque definiremos los campos, propiedades y mtodos que queramos que tenga la clase. NOTA: En Visual Basic .NET la definicin de una clase se puede hacer en cualquier fichero de cdigo (con extensin .vb), aunque no es obligatorio hacerlo en un fichero independiente como ocurra con las versiones anteriores, es recomendable hacerlo, para que nos resulte ms fcil de mantener.

Public Class A Private _prop2 As Integer Private _prop1 As String ' Public Property Prop1() As String Get Return _prop1 End Get

Set(ByVal value As String) If value <> "" Then _prop1 = value End If End Set End Property Public Property Prop2() As Integer Get Return _prop2 End Get Set(ByVal value As Integer) _prop2 = value End Set End Property ' Public Sub Mostrar() Console.WriteLine("{0}, {1}", _prop1, _prop2) End Sub End Class

Listado 1

Tal como podemos ver en el listado 1, tenemos una clase llamada A que define dos campos, dos propiedades y un mtodo. Los dos campos, declarados como privados, se usan para mantener "internamente" la informacin que se expone mediante las dos propiedades pblicas, de esta forma protegemos los datos y esta sera una forma de encapsular la informacin. De las dos propiedades definidas para acceder a esos datos, solo la propiedad Prop1 hace una comprobacin de que no se asigne una cadena vaca al campo que mantiene internamente la informacin, aunque en este ejemplo por su simplicidad no hacemos ms comprobaciones, en una clase algo ms compleja, se podran realizar otras comprobaciones, por ejemplo si el valor a almacenar es una cuenta de email, podramos comprobar que es una cadena correctamente formada. Las propiedades suelen definir dos bloques de cdigo, uno, el bloque Get se utiliza cuando queremos acceder al valor devuelto por la propiedad, el otro es el bloque Set, el cual se utilizar cuando asignemos un valor a la propiedad. El mtodo Mostrar se usar para mostrar el contenido de las dos propiedades por la consola y est definido como Sub (void en C#) porque no devuelve ningn valor.

SOBRESCRIBIR MIEMBROS HEREDADOS

Tal como comentamos en el artculo anterior, todas las clases de .NET se derivan directa o indirectamente de la clase Object. La clase definida en el listado 1 tambin se deriva de

Object aunque no se indique expresamente. La clase base de todas las clases de .NET tiene un mtodo que se utiliza para recuperar informacin, (en formato cadena), del contenido de la clase: el mtodo ToString. Cada clase puede crear su propia versin del mtodo ToString para que devuelva una representacin adecuada, por ejemplo los tipos numricos devuelven una cadena que representa al nmero que contiene. En nuestra clase podemos redefinirlo para que nos devuelva el contenido de los dos datos que la clase mantiene:
Public Overrides Function ToString() As String Return String.Format("{0}, {1}", _prop1, _prop2) End Function

Para indicarle al compilador que estamos redefiniendo un mtodo ya existente, lo indicamos con la instruccin Overrides (override en C#). Debido a que ahora nuestra clase tiene una nueva versin de este mtodo, cualquier clase que se derive de ella tambin heredar la nueva implementacin del mtodo ToString.

USAR LA HERENCIA EN VISUAL BASIC .NET

La clase que hemos definido en el listado 1 no indicaba de ninguna forma que se deriva de la clase Object, este es un caso excepcional, ya que todas las clases de .NET se derivan de forma "automtica" de la clase Object. Para indicar en Visual Basic .NET que una clase se deriva de otra, debemos usar la instruccin (o palabra clave) Inherits seguida de la clase que queremos usar como base. Esa instruccin tiene que indicarse al principio de la declaracin de la clase, antes que cualquier otra instruccin, con excepcin de los comentarios. Para crear una clase que se derive de la clase A definida en el listado 1, tendramos que hacer lo siguiente:
Public Class B Inherits A End Class

Tal como podemos comprobar, la clase B no define ningn mtodo ni propiedad, pero realmente si que tiene mtodos y propiedades: todos los que tenga la clase A (adems de los de la clase Object). Para comprobarlo podemos definir una variable del tipo de la clase B y comprobaremos que esta clase tiene los mismos miembros que la clase A, tal como se muestra en el listado 2.

Sub Main()

Dim objB As New B objB.Prop1 = "guille" objB.Prop2 = 47 objB.Mostrar() Console.WriteLine("{0}", objB.ToString) End Sub

Listado 2

OCULTAR MIEMBROS HEREDADOS

Todo funciona como esperbamos, aunque hay un pequeo problema, si quisiramos modificar el comportamiento de los miembros heredados por la clase B, no podramos hacerlo. La razn es bien simple: la clase A no ha definido los miembros como virtuales (o reemplazables). Por tanto no se pueden crear nuevas versiones de los mismos en la clase B, o casi... Realmente la clase B si que puede definir nuevas versiones de los miembros que tiene la clase A, al menos puede crear mtodos y propiedades que tengan el mismo nombre, por ejemplo:
Public Sub Mostrar() Console.WriteLine("Mostrar en la clase B: {0}, {1}", Prop1, Prop2) End Sub

Esto es totalmente correcto, al menos en el sentido de que no produce ningn error; lo ms que producir esa declaracin es una advertencia del compilador indicndonos que ese mtodo entra en conflicto con el definido en la clase base A, tal como podemos comprobar en la figura 1.

Figura 1. Advertencia de ocultacin

Esa advertencia nos informa que deberamos indicar que la declaracin "oculta" a la definida en la clase A y por tanto deberamos usar la instruccin Shadows (new en C#). Aunque usemos Shadows, el problema real sigue existiendo: el mtodo declarado en la clase B oculta al declarado (y heredado) en la clase A. Si despus de definir este mtodo de la clase B volvemos a ejecutar el cdigo del listado 2, comprobaremos que se utiliza el nuevo mtodo. Posiblemente el lector pensar que eso es lo que queramos conseguir: tener nuestra propia versin del mtodo Mostrar. Es ms, si definimos una nueva clase que se derive de B podemos comprobar que realmente es ese mtodo el que se hereda por la nueva clase. En el listado 3 podemos ver la definicin de la clase C y el cdigo para comprobar lo que mostrara:

Public Class C Inherits B End Class Sub Main() Dim objC As New C objC.Prop1 = "guille" objC.Prop2 = 47 objC.Mostrar() Console.WriteLine("{0}", objC.ToString) End Sub

Listado 3

Y lo que imprime es exactamente lo mismo que usando la clase B. Por tanto, el objetivo est conseguido, es decir, la clase B ha "redefinido" un mtodo de la clase A y esa nueva versin es la que se usar a partir de ese momento por las clases que se basen en la clase B. Aparentemente as es. Al menos si lo tomamos al pie de la letra. El nico problema es que acabamos de romper una de las cualidades de la programacin orientada a objetos: el polimorfismo. Esta nueva definicin del mtodo Mostrar ya no tiene nada que ver con la definida por la clase A y por tanto no existe ninguna relacin "polimrfica" entre ambos mtodos. Para ser ms precisos, tanto la clase B como la clase C tienen dos definiciones del mtodo Mostrar: el inicialmente heredado de la clase A y el nuevo definido por la clase B, aunque siempre prevalecer el definido expresamente en la clase derivada frente al heredado de la clase base.

Si pudisemos ver el objeto creado en la memoria a partir de la clase B (e incluso de la clase C), nos daramos cuenta de que realmente est dividido en tres partes, tal como se muestra en la figura 2: 1. La parte heredada de la clase Object 2. La parte heredada de la clase A 3. Las definiciones propias de la clase B

Figura 2. Objeto B en memoria

El mtodo ToString definido en Object ha sido reemplazado o, para que lo comprendamos mejor, sustituido por el redefinido en la clase A, pero el mtodo Mostrar de la clase A an existe, lo que ocurre es que ha sido ocultado por el que se ha definido en la clase B. Para demostrar que es as, que existen dos mtodos Mostrar en la memoria, podemos utilizar el polimorfismo para "extraer" el mtodo Mostrar que est oculto. Para ello tendremos que declarar una variable del tipo de la clase A y decirle que extraiga del objeto B la parte que le corresponde: solo la parte definida en la clase A. Esto se consigue de una forma muy simple: asignando a una variable del tipo A el contenido de la variable que apunta al objeto B:
Dim objA As A objA = objB

A partir de este momento el objeto B est siendo referenciado por el objeto A, pero, y esto es importante, solo la parte que A conoce, es decir, la variable objA solamente podr acceder al "trozo" del objeto B que se deriv de la clase A. Por tanto, si llamamos al mtodo Mostrar de la variable objA, accederemos al mtodo Mostrar que el objeto B contiene porque la consigui al derivarse de A. S, esto es algo complicado y que no es fcil de comprender, pero es importante intentar asimilarlo, ya que es muy probable que lo necesitemos en nuestros proyectos, sobre todo si sabemos que se puede hacer. Adems de que esta sera la nica forma que tenemos de acceder a ese mtodo "oculto", ya que cualquier intento de acceder al mtodo Mostrar mediante un objeto del tipo B, siempre acceder al definido en la propia clase B y que oculta al heredado de la clase A.

NOTA: Cuando declaramos una variable y la instanciamos, (creando un nuevo objeto a partir de una clase), dicha variable simplemente tiene una referencia al objeto creado en la memoria, (la variable simplemente tiene un puntero al objeto real), por tanto, ese objeto existe y puede ser referenciado por otras variables, aunque esas otras variables deben ser de tipos "incluidos" en la clase usada para crear dicho objeto. Al instanciar un nuevo objeto del tipo B, (tal como se muestra en la figura 2), podemos acceder a l mediante variables de tipo Object, de tipo A y, por supuesto, de tipo B.

INDICAR LOS MIEMBROS VIRTUALES

Como hemos comprobado, si queremos que los miembros de nuestra clase se puedan redefinir en clases derivadas, debemos indicarlo de forma expresa. Para que esto sea as, utilizaremos la instruccin Overridable (virtual en C#). De esta forma le indicaremos al compilador que el mtodo se puede redefinir, es decir, que en la clase derivada se puede crear una nueva versin "personalizada" de dicho miembro. Con esto logramos que en sucesivas derivaciones de la clase solamente exista un mismo miembro polimrfico. La ventaja principal es que si en otra clase decidimos crear una nueva versin de, por ejemplo, un mtodo, cuando se cree un objeto en la memoria, solo existir ese mtodo, no varios mtodos con el mismo nombre, pero sin ninguna relacin entre ellos, exceptuando el hecho de que se llamen de la misma forma. Tal como vimos en la seccin SOBRESCRIBIR MIEMBROS HEREDADOS, tendremos que usar la instruccin Overrides (override en C#), para indicar que nuestra intencin es crear una versin propia de uno de los miembros heredados. Pero no solo basta con usar esa instruccin, ya que si queremos redefinir un miembro

existente en alguna de las clases que hemos heredado, la "firma" de nuestra versin debe ser la misma que el original. Es decir, si es un mtodo que devuelve una cadena y no recibe parmetros, debemos "respetar" esas mismas caractersticas, porque de lo que se trata es de cumplir con un contrato, dicho contrato estipula que tipo de miembro es, (un mtodo o una propiedad), que tipo de datos devuelve, si recibe o no parmetros y en caso de que los reciba de que tipo deben ser. Todo esto es para garantizar que esos miembros se puedan usar de forma independiente de la clase en el que se ha declarado, con idea de que podamos acceder a ellos mediante objetos de las clases que se han usado para crear la nueva, (las clases de las que se deriva la clase que sobrescribe el mtodo). Por ejemplo, si el mtodo Mostrar definido en la clase A se hubiese declarado como virtual, (Overridable), y en la clase B lo hubisemos redefinido usando la instruccin Overrides, podramos acceder a dicho mtodo (de un objeto creado a partir de la clase B) usando tanto una variable de la clase A como una de la clase B; ya que en la memoria solamente existir un mtodo llamado Mostrar. A diferencia de lo que ocurra antes (al no declararlo como virtual), que realmente existan dos mtodos Mostrar.

DEFINIENDO INTERFACES

Una interfaz realmente es la definicin de los miembros pblicos de una clase. Pero en los lenguajes de programacin de .NET tambin podemos definir clases especiales que simplemente definan cmo deben ser los miembros que una clase implemente. Es decir que caractersticas deben tener. De esta forma podemos garantizar que si varias clases implementan los miembros definidos en una interfaz, podemos usarlos de manera annima, es decir, sin necesidad de saber si estamos usando un objeto de una clase o de otra, ya que si ambas clases implementan la interfaz, tendremos la certeza de que dichas clases tienen los miembros definidos en dicha interfaz. Una interfaz representa un contrato, si una clase implementa una interfaz, est suscribiendo dicho contrato, es ms, est obligada a cumplirlo, por tanto, la clase tendr que definir todos los miembros que la interfaz contenga. Antes de ver cmo usar las interfaces en nuestras clases, veamos cmo definir una interfaz.
Public Interface IPrueba2 Property Prop1() As String Sub Mostrar() End Interface

En este caso hemos definido una interfaz llamada IPrueba2 (por convencin los nombres de las interfaces siempre empiezan con la letra I mayscula), en la que se define una propiedad y un mtodo. Los miembros de las interfaces siempre son pblicos y no deben implementar cdigo, solamente la definicin propiamente dicha.

UTILIZAR INTERFACES EN LAS CLASES

Cualquier clase que quiera disponer de los miembros definidos en la interfaz debe indicarlo de forma expresa, ya que no solo es suficiente con definir los miembros con nombres y caractersticas similares. Para "implementar" en una clase los miembros definidos en una interfaz tendremos que usar la instruccin Implements seguida del nombre de la interfaz. Adems tendremos que definir los mtodos y propiedades que dicha interfaz contiene, aunque en Visual Basic adems hay que indicarlo expresamente, de forma que se sepa con seguridad de que cada uno de esos miembros equivale a los definidos en la interfaz. En el listado 4 vemos cmo definir una clase que utilice la interfaz que acabamos de ver en la seccin anterior.

Public Class Prueba2 Implements IPrueba2 Public Sub Mostrar() _ Implements IPrueba2.Mostrar ' nuestra version del mtodo Mostrar End Sub Public Property Prop1() As String _ Implements IPrueba2.Prop1 Get ' el cdigo que devuelve ' el valor de la propiedad End Get Set(ByVal value As String) ' el cdigo que asigna ' el valor de la propiedad End Set End Property End Class

Listado 4

El mtodo y las dos propiedades deben tener el mismo nombre y parmetros (si los hubiera) que los definidos en la interfaz. Cuando trabajamos con Visual Basic adems debemos indicar expresamente que dicho mtodo o propiedad est "ligado" con el definido en la interfaz, cuando trabajamos con C# no es necesario indicarlo. La ventaja de esta "redundancia" de VB es que podemos dar un nombre diferente al

miembro implementado, pero "internamente" el compilador sabr que nos estamos refiriendo al que implementa la interfaz. Tal como podemos ver en el listado 5, a pesar de que en la clase Prueba2B al mtodo le hemos dado otro nombre, realmente est haciendo referencia al que se ha declarado en la interfaz.

Public Class Prueba2B Implements IPrueba2 Public Sub OtroNombre() _ Implements IPrueba2.Mostrar Console.WriteLine("Este es el mtodo Mostrar de la clase Prueba2B") End Sub Public Property Prop1() As String _ Implements IPrueba2.Prop1 Get Return "Prop1 de la clase prueba2B" End Get Set(ByVal value As String) ' End Set End Property End Class Dim p2 As New Prueba2 Dim p2b As New Prueba2B Dim i As IPrueba2 ' i = p2 i.Mostrar() ' i = p2b i.Mostrar()

Listado 5

POLIMORFISMO USANDO CLASES E INTERFACES

En los ejemplos mostrados ya hemos visto cmo usar el polimorfismo tanto a travs de variables incluidas en las clases como con interfaces que dichas clases implementan; vamos a detallar un poco ms, ya que esta es una de las caractersticas ms usadas en .NET, por la sencilla razn de que muchas clases de .NET Framework implementan interfaces de forma que podamos acceder a los miembros implementados por medio de variables del tipo de dichas interfaces.

Es ms, muchas de las clases de .NET adems permiten que demos nueva funcionalidad a nuestras propias clases si implementamos ciertas interfaces. Por ejemplo, si queremos que nuestra clase sea "clasificable", es decir, que se pueda usar en una coleccin que clasifique los elementos que contiene, nuestra clase debe implementar la interfaz IComparable. Si definimos una clase en la que queremos que el mtodo ToString acte de forma que podamos especificar ciertos formatos a la hora de mostrar el contenido, nuestra clase debe implementar IFormattable. Que queremos que nuestra clase acte como una coleccin, en la que se pueda enumerar o recorrer el contenido de la misma, debemos implementar la interfaz IEnumerable. Pero esas interfaces propias del .NET Framework lo que harn ser darle una nueva funcionalidad a nuestras clases, por tanto, lo importante es saber de que forma actan los objetos creados en la memoria. Tal como hemos comentado antes, solo existe un objeto en la memoria y cuando accedemos a l lo podemos hacer bien usando alguna de las clases de las que se deriva o bien mediante alguna de las interfaces que implementa. Cuando accedemos a dicho objeto mediante algunas de estas clases o interfaces simplemente estamos accediendo a la parte "conocida" por dicho tipo. En el cdigo del listado 4 y 5 la interfaz IPrueba2 "sabe" cmo acceder al mtodo Mostrar y a la propiedad Prop1, independientemente del objeto que la haya implementado, por tanto si una clase implementa dicha interfaz podemos acceder a esos miembros mediante una variable del tipo IPrueba2, tal como se demuestra en el listado 5 en el que accedemos al mtodo Mostrar definido en la interfaz e implementado por las dos clases.

USAR EL POLIMORFISMO PARA ACCEDER A ELEMENTOS DIFERENTES DE UN ARRAY O COLECCIN

Una de las utilidades del polimorfismo (de clases o interfaces) es que podemos crear arrays de variables de un tipo "bsico" y en ese array incluir objetos que si bien son distintos, en el fondo tienen como parte componente la clase de la que se ha declarado el array. El ejemplo ms bsico y vlido tanto para las clases declaradas en el propio .NET Framework como las declaradas por nosotros mismos, es crear un array de tipo Object, dicho array podr contener objetos de cualquier tipo (incluso tipos como nmeros enteros, cadenas, etc.), y posteriormente poder acceder a cualquiera de los elementos mediante un objeto del tipo Object, en cuyo caso solo podremos acceder a los mtodos que Object implementa o bien mediante objetos de un tipo en particular, en cuyo caso nos veremos obligados a hacer una conversin desde el tipo contenido en el array (Object) al tipo particular que nos interese. En el listado 6 podemos ver un ejemplo en el que se crea un array de tipo Object pero que se almacenan tanto objetos del tipo clase A, clase B, IPrueba2, Integer y String.

Dim a(6) As Object ' Dim a1 As New A a1.Prop1 = "Objeto A" Dim b1 As New B b1.Prop1 = "Objeto B" Dim c1 As New C c1.Prop1 = "Objeto C" ' a(0) = a1 a(1) = b1 a(2) = c1 a(3) = New Prueba2 a(4) = New Prueba2B a(5) = 15 a(6) = "Hola" ' Dim i As Integer Console.Write("Usando el mtodo ToString") Console.WriteLine(" de los objetos contenidos") For i = 0 To a.Length - 1 Console.WriteLine("a({0}) = {1}", i, a(i).ToString()) Next ' Console.WriteLine() ' Console.WriteLine("Usando un Object") Dim o As Object For Each o In a Console.WriteLine("o.ToString = {0}", o.ToString()) Next ' Console.WriteLine() ' Console.WriteLine("Usando tipos especficos") For Each o In a Console.WriteLine("El tipo es: {0}", o.GetType().Name) If TypeOf o Is A Then Dim tA As A = CType(o, A) tA.Mostrar() ElseIf TypeOf o Is IPrueba2 Then Dim tIPrueba2 As IPrueba2 = CType(o, IPrueba2) tIPrueba2.Mostrar() ElseIf TypeOf o Is Integer Then Dim tInt As Integer = CType(o, Integer) Console.WriteLine(tInt.ToString("00000")) ElseIf TypeOf o Is String Then Dim tStr As String = o.ToString Console.WriteLine(tStr) Else Console.WriteLine("o.ToString = {0}", o.ToString()) End If Next

Listado 6

Tal como podemos comprobar en el ltimo bucle de dicho listado, se utiliza la instruccin compuesta TypeOf ... Is para saber si un objeto es de un tipo concreto (en C# usaramos is), tambin podemos ver que usando el mtodo GetType podemos obtener el tipo subyacente as como el nombre de dicho tipo. Si nos fijamos, al hacer la comprobacin TypeOf o Is A aqu se procesarn tanto los objetos del tipo A como los derivados de dicho tipo, lo mismo ocurre con la interfaz IPrueba2. Pero este ejemplo al ser genrico y usando la clase Object seguramente no acabar de "cuajar", por tanto vamos a crear un ejemplo en el que crearemos variables de un tipo concreto: Cliente y derivaremos un par de clases en las que agregaremos nueva funcionalidad a la clase base, posteriormente crearemos un array del tipo Cliente en el que podremos almacenar variables de cualquiera de esos tipos derivados de ella. Nota: Por la extensin del listado, el mismo se incluye en el ZIP con el cdigo de los ejemplos (tanto para Visual Basic como para C#), en el listado 7 puedes ver cmo usar esas clases. En dicho cdigo tendremos ocasin de ver cmo podemos implementar la interfaz IComparable para que estas clases se puedan agregar a una coleccin y posteriormente clasificarlas. Adems implementaremos la interfaz IFormattable para que, si los mostramos por la consola o usamos el mtodo Format de la clase String, podamos usar los siguientes formatos personalizados:
y y y y y

ANS mostrar los apellidos, el nombre y el saldo NAS mostrar el nombre, los apellidos y el saldo AN mostrar los apellidos y el nombre (predeterminado) NA mostrar el nombre y los apellidos S mostrar el saldo

Sub Main() Dim acli(6) As Cliente ' acli(0) = New Cliente("Jose", "Sanchez", 125.5D) acli(1) = New ClienteOro("Luis", "Rebelde", 2500.75D) acli(2) = New ClienteMoroso("Antonio", "Perez", -500.25D) acli(3) = New Cliente("Miguel", "Rodriguez", 200) acli(4) = New ClienteMoroso("Juan", "Ruiz", -310) acli(5) = New ClienteOro("Mariano", "Alvarez", 500.33D)

acli(6) = New Cliente("Carlos", "Bueno", 975) ' Console.WriteLine("Antes de clasificar:") For Each c As Cliente In acli Console.WriteLine("{0}, saldo= {1}", c, c.MostrarSaldo()) Next Array.Sort(acli) ' Console.WriteLine() Console.WriteLine("Despus de clasificar:") For Each c As Cliente In acli Console.Write("{0}, saldo= {1}", c, c.MostrarSaldo()) If TypeOf c Is ClienteOro Then Console.WriteLine(" -> $$$ es cliente ORO $$$") ElseIf TypeOf c Is ClienteMoroso Then Console.WriteLine(" -> OJO que es un cliente moroso") Else Console.WriteLine() End If Next ' Console.WriteLine() Console.WriteLine("Mostrar usando formatos:") For Each c As Cliente In acli Console.WriteLine("Usando NAS= {0:NAS}", c) Console.WriteLine("Usando AN= {0:AN}", c) Console.WriteLine("Usando S= {0:S}", c) Next ' Console.ReadLine() End Sub

Listado 7

CONSTRUCTORES Y SOBRECARGA DE CONSTRUCTORES

El punto de inicio de cualquier clase, cuando se crea una instancia en la memoria, es un mtodo especial al que se le conoce como constructor. Un constructor no devuelve ningn valor, por tanto en Visual Basic sera un mtodo de tipo Sub llamado New (en C# no se declara como void, simplemente tendr el mismo nombre de la clase). Los constructores tambin se pueden sobrecargar, es decir, pueden existir varias versiones en las que cada una de ellas reciba distintos parmetros, en nmero y/o en tipo. Debido a que todas las clases (y estructuras) deben tener un constructor, si nosotros no lo definimos de forma expresa, ser el propio compilador el que se encargue de aadirlo por nosotros, en ese caso ser un constructor en el que no se reciba ningn argumento. Aunque hay que tener presente que en el momento en que hemos definido un constructor el

compilador ya no agregar ninguno de forma automtica. Esto tiene sus ventajas, ya que en ocasiones es posible que nos interese que nuestras clases solamente se puedan instanciar si se le pasa algunos parmetros al constructor de la misma. Pero si adems de un constructor con parmetros queremos seguir manteniendo el constructor "predeterminado", entonces tendremos que declararlo aunque no escribamos nada de cdigo en el interior. En el listado 8 podemos ver la clase Cliente con tres constructores.

Public Class Cliente Implements IComparable, IFormattable ' Private _nombre As String Private _apellidos As String Private _saldo As Decimal ' Public Sub New() End Sub Public Sub New( _ ByVal elNombre As String, _ ByVal losApellidos As String) _nombre = elNombre _apellidos = losApellidos End Sub Public Sub New( _ ByVal elNombre As String, _ ByVal losApellidos As String, _ ByVal elSaldo As Decimal) _nombre = elNombre _apellidos = losApellidos _saldo = elSaldo End Sub

Listado 8

LOS CONSTRUCTORES NO SE HEREDAN El constructor es el nico miembro de una clase que no se hereda, por tanto si necesitamos crear constructores en clases derivadas debemos declararlos de forma explcita.

LOS CONSTRUCTORES DE LAS ESTRUCTURAS

Los constructores de las estructuras son un caso especial, ya que siempre existir un constructor sin parmetros, el cual adems no podemos definirlo por medio de cdigo, porque es el propio compilador el que se encargar de su creacin. Por tanto en las estructuras solamente podemos definir constructores parametrizados (que reciban parmetros) y en el caso de definirlo, en el cdigo del mismo, tendremos que asignarle un valor a cualquiera de las propiedades (o campos) pblicos que tenga esa estructura, siempre y cuando no sean estticos (compartidos).

CONSTRUCTORES QUE USAN OTROS CONSTRUCTORES

Debido a que los constructores pueden recibir parmetros, en algunas ocasiones nos puede ser til poder llamar a otros constructores, por ejemplo de la clase base o de la misma clase. En estos casos, en Visual Basic es fcil hacerlo, como sabemos que los constructores realmente son mtodos llamados New, los podemos usar de la misma forma que haramos con cualquier otro mtodo. Por ejemplo, si modificamos el cdigo mostrado en el listado 8, podramos hacer esto:
Public Sub New( _ ByVal elNombre As String, _ ByVal losApellidos As String, _ ByVal elSaldo As Decimal) Me.New(elNombre, losApellidos) _saldo = elSaldo End Sub

De forma que desde el constructor que recibe tres parmetros llamemos al que recibe dos. En este caso, la instruccin o palabra clave Me representa a la instancia actual (el objeto que se ha creado en memoria). Si en lugar de llamar a otro constructor de la propia clase, queremos llamar a un constructor de la clase base, en lugar de Me, usaremos MyBase. En C# este mismo cdigo se hara de la siguiente forma:
public Cliente(string elNombre, string losApellidos, decimal elSaldo) : this(elNombre, losApellidos) { _saldo = elSaldo; }

Es decir, se llamara al otro constructor indicndolo despus del cierre de parntesis y separndolo con dos puntos. En C# la instruccin o palabra clave que hace referencia a la instancia actual es this y la que hace referencia a la clase base es: base.

CLASES ABSTRACTAS

Tal como comentamos el mes anterior, en ocasiones tendremos la necesidad de crear clases que no se puedan usar para crear objetos, pero si para usarla como base de otras. La razn principal para que hacer que una clase no sea instanciable es que dicha clase por s sola no tenga ningn sentido, al menos para poder crear nuevos objetos de ese tipo, pero si tendr sentido si la usamos como clase base de otras clases. Por ejemplo, podramos tener una clase Animal, la cual no tendra sentido si a partir de ella se pudiesen crear nuevos objetos, ya que el concepto de animal es demasiado abstracto para poder crear un objeto a partir de l. Pero si la podramos utilizar para derivar de ella otras clases que bien podran ser a la vez abstractas o bien clases "normales". .NET Framework nos permite crear clases abstractas, las cuales son como las interfaces, pero las que pueden tener mtodos y otros miembros que tengan no solo la definicin de esos miembros sino tambin cdigo funcional. Adems, debido a que las clases abstractas estn pensadas para usarse como clases base de otras, todos los miembros son virtuales de forma predeterminada, por tanto no es necesario indicarlos usando el modificador Overridable (virtual en C#).

DECLARAR CLASES ABSTRACTAS

La definicin de una clase abstracta se hace como las clases normales, salvo de que hay que usar el modificador MustInherit (abstract en C#), de esta forma le indicamos al compilador de que nuestra intencin es la de que nuestra clase sea "heredable". Como ya hemos comentado anteriormente, el concepto de clases abstractas o clases que solo se pueden usar como clases base es importante cuando queremos ofrecer cierta funcionalidad a nuestras clases, sobre todo las que formen parte de ciertas jerarquas de clases, ya que las clases abstractas nos servirn para definir el comportamiento del resto de las clases, adems de que, como las interfaces, nos permitirn disponer de cierta funcionalidad o, mejor dicho, nos permitirn dar unas pautas que los que decidan usarlas, tendrn que seguir. Pero a diferencia de las interfaces, las clases abstractas no solo representarn un contrato, sino que tambin pueden ofrecer "de fbrica" un funcionamiento que, aunque las clases

derivadas no implementen, stas ya tendrn, en este aspecto funcionarn como las clases normales, ya que las clases derivadas podrn usar el cdigo implementado en ellas.

MIEMBROS ABSTRACTOS

Cuando comentamos que las clases abstractas son como las interfaces no solo nos referamos a que se podran usar para proporcionar polimorfismo, sino porque en las clases abstractas tambin podemos definir miembros que a su vez sean abstractos, es decir, que en las clases abstractas solamente se defina el mtodo o propiedad, pero que no tenga ninguna funcionalidad, es ms, si declaramos un miembro como abstracto la clase que se derive de la clase abstracta estar obligada a definirlo. Esto nos ofrece la ventaja de poder definir miembros funcionales y miembros que solo tengan sentido en las clases derivadas y por tanto sern las clases derivadas las que deban definir el cdigo que los haga funcionales y le den la utilidad adecuada. Para indicar que un miembro es abstracto, debemos indicarlo usando la instruccin MustOverride (abstract en C#), de esta forma nos permitir el compilador escribir solo la definicin del mismo y no tener que escribir ningn cdigo. En las clases derivadas tendremos que usar la instruccin Overrides (override en C#) de la misma forma que lo usamos con el resto de las clases para indicar que dicho mtodo est reemplazando a un miembro definido en la clase base. Una puntualizacin: Los miembros abstractos solo se pueden definir en clases abstractas. En el cdigo incluido en el ZIP que acompaa a este artculo se incluye un ejemplo que define y usa las clases abstractas, como es habitual, se incluye cdigo para Visual Basic y C#.

CLASES SELLADAS (NO HEREDABLES)

Otro concepto, que posiblemente pueda parecer que no est "ligado" con la herencia es el de las clases selladas o no heredables. Este tipo de clases no permitirn que se usen como clases base de otras nuevas clases. La existencia de las clases normales y las abstractas nos permiten derivar nuevas clases a partir de ellas, eso tiene sentido, pero, qu sentido puede tener una clase de la que no se puedan derivar nuevas clases? La razn principal para definir una clase como sellada o no heredable es precisamente porque no queremos que nadie pueda modificar el comportamiento de dicha clase, ya que al

no poder usarla como base de nuevas clases, nadie podr ofrecer nueva funcionalidad, de esta forma nos aseguramos que esa clase siempre funcionar como la hemos definido. Esto es as, porque al estar "sellada" tampoco podremos definir miembros virtuales, por la sencilla razn de que nadie podr derivar nuevas clases y por tanto tampoco podr reemplazar el comportamiento de los mismos. Debido a que el comportamiento normal de una clase es que sea heredable, para poder crear clases que estn selladas, y por tanto hacerlas no heredables, debemos usar la instruccin o modificador NotInheritable (sealed en C#).

METODOS PARCIALES

Clases y mtodos parciales (Gua de programacin de C#)


Visual Studio 2010

Es posible dividir la definicin de una clase, struct, interfaz o mtodo en dos o ms archivos de cdigo fuente.Cada archivo de cdigo fuente contiene una seccin de la definicin de tipos o mtodos, y todas las partes se combinan cuando se compila la aplicacin.
Clases parciales

Existen diversas situaciones en las que es conveniente dividir una definicin de clase:
y

Al trabajar en proyectos grandes, el hecho de dividir una clase en archivos independientes permite que varios programadores trabajen al mismo tiempo con ella. Al trabajar con un cdigo fuente generado automticamente, se puede agregar el cdigo a la clase sin tener que volver a crear el archivo de cdigo fuente.Visual Studio utiliza este enfoque al crear formularios Windows Forms, cdigo contenedor de un servicio Web, etc.Se puede crear cdigo que utilice estas clases sin tener que modificar el archivo creado por Visual Studio. Para dividir una definicin de clase, utilice el modificador partial, como se muestra a continuacin:

public partial class Employee { public void DoWork() { } } public partial class Employee { public void GoToLunch() { } }

La palabra clave partial indica que otras partes de la clase, struct o interfaz se pueden definir en el espacio de nombres.Todas las partes deben utilizar la palabra clave partial.Todas las partes deben estar disponibles en tiempo de compilacin para formar el tipo final.Todas las partes deben tener la misma accesibilidad, ya sea public, private, etc.

Si alguna parte se declara abstracta, todo el tipo se considera abstracto.Si alguna parte se declara sealed, todo el tipo se considera sealed.Si alguna parte declara un tipo base, todo el tipo hereda esa clase. Todas las partes que especifican una clase base deben concordar, pero las partes que omiten una clase base heredan igualmente el tipo base.Las partes pueden especificar diferentes interfaces base, pero el tipo final implementa todas las interfaces mostradas por todas las declaraciones parciales.Cualquier miembro de clase, struct o interfaz declarado en una definicin parcial est disponible para todas las dems partes.El tipo final es la combinacin de todas las partes en tiempo de compilacin.
Nota

El modificador partial no est disponible en declaraciones de delegado o enumeracin. El siguiente ejemplo muestra que los tipos anidados pueden ser parciales, aunque el tipo en el que estn anidados no sea propiamente parcial.
class Container { partial class Nested { void Test() { } } partial class Nested { void Test2() { } } }

En tiempo de compilacin, se combinan los atributos de definiciones de tipo parcial.Por ejemplo, consideremos las siguientes declaraciones:
[SerializableAttribute] partial class Moon { } [ObsoleteAttribute] partial class Moon { }

Son equivalentes a las declaraciones siguientes:


[SerializableAttribute] [ObsoleteAttribute] class Moon { }

Los siguientes elementos se combinan a partir de todas las definiciones de tipo parcial:
y y y y y

comentarios XML interfaces atributos de parmetro de tipo genrico atributos de clase miembros

Por ejemplo, consideremos las siguientes declaraciones:


partial class Earth : Planet, IRotate { } partial class Earth : IRevolve { }

Son equivalentes a las declaraciones siguientes:


class Earth : Planet, IRotate, IRevolve { }

Restricciones

Existen varias reglas que se deben seguir al trabajar con definiciones de clase parciales:
y

Todas las definiciones de tipo parcial creadas para ser parte del mismo tipo deben modificarse con partial.Por ejemplo, las siguientes declaraciones de clase generan un error:
public partial class A { } //public class tcA { } // Error, must also be marked partial

y y

El modificador partial slo puede aparecer inmediatamente antes de las palabras clave class, struct o interface. Se permiten tipos parciales anidados en definiciones de tipo parcial, como se muestra en el siguiente ejemplo:

partial class ClassWithNestedClass { partial class NestedClass { }

} partial class ClassWithNestedClass { partial class NestedClass { } }

Todas las definiciones de tipo parcial que deben ser parte del mismo tipo deben definirse en el mismo ensamblado y el mismo mdulo (archivo .exe o .dll).Las definiciones parciales no pueden abarcar varios mdulos. Los parmetros de nombre de clase y tipo genrico deben coincidir en todas las definiciones de tipo parcial.Los tipos genricos pueden ser parciales.Todas las declaraciones parciales deben utilizar los mismos nombres de parmetro en el mismo orden. Las siguientes palabras clave en una definicin de tipo parcial son opcionales, pero si hay alguna en una definicin de tipo parcial, no puede entrar en conflicto con las palabras clave especificadas en otra definicin parcial para el mismo tipo: o public o private o protected o internal o abstract o sealed o clase base o modificador new (partes anidadas) o restricciones genricas Para obtener ms informacin, vea Restricciones de tipos de parmetros (Gua de programacin de C#).

Ejemplo 1

Description

En el siguiente ejemplo, los campos y el constructor de la clase, CoOrds, se declaran en una definicin de clase parcial, mientras que el miembro PrintCoOrds se declara en otra definicin de clase parcial.
public partial class CoOrds { private int x; private int y; public CoOrds(int x, int y) {

this.x = x; this.y = y; } } public partial class CoOrds { public void PrintCoOrds() { Console.WriteLine("CoOrds: {0},{1}", x, y); } } class TestCoOrds { static void Main() { CoOrds myCoOrds = new CoOrds(10, 15); myCoOrds.PrintCoOrds(); // Keep the console window open in debug mode. Console.WriteLine("Press any key to exit."); Console.ReadKey(); } } // Output: CoOrds: 10,15

Ejemplo 2

Descripcin

El siguiente ejemplo muestra que tambin se pueden desarrollar structs e interfaces parciales.
partial interface ITest { void Interface_Test(); } partial interface ITest { void Interface_Test2(); } partial struct S1 { void Struct_Test() { } } partial struct S1

{ void Struct_Test2() { } }

Mtodos parciales

Una clase o struct parcial puede contener un mtodo parcial.Una parte de la clase contiene la firma del mtodo.Una implementacin opcional se puede definir en la misma parte u otra parte.Si no se proporciona la implementacin, el mtodo y todas las llamadas a l se quitan en tiempo de compilacin. Los mtodos parciales permiten al implementador de una parte de una clase definir un mtodo, de forma similar a un evento.El implementador de la otra parte de la clase puede decidir si implementar el mtodo o no.Si no se implementa el mtodo, el compilador quita la firma del mtodo y todas las llamadas al mtodo.Por consiguiente, cualquier cdigo en la clase parcial puede utilizar libremente un mtodo parcial, aun cuando no se proporcione la implementacin.No se producir ningn error en tiempo de compilacin o en tiempo de ejecucin si se realizan llamadas al mtodo y ste no est implementado. Los mtodos parciales son especialmente tiles como una manera de personalizar el cdigo generado.Permiten reservar un nombre y una firma de mtodo, de modo que el cdigo generado pueda llamar al mtodo y el programador pueda decidir si lo implementa o no.De forma muy similar a las clases parciales, los mtodos parciales permiten que el cdigo creado por un generador de cdigo y el cdigo creado por un programador humano puedan funcionar juntos sin costes en tiempo de ejecucin. Una declaracin de mtodo parcial consta de dos partes: la definicin y la implementacin.stas pueden estar en partes independientes de una clase parcial o en la misma parte.Si no existe ninguna declaracin de implementacin, el compilador quita la declaracin de definicin y todas las llamadas al mtodo.
Copiar
// Definition in file1.cs partial void onNameChanged(); // Implementation in file2.cs partial void onNameChanged() { // method body } y y

Las declaraciones de mtodo parciales deben comenzar con la palabra clave contextual partial, y el mtodo debe devolver void. Los mtodos parciales pueden tener parmetros ref, pero no out.

y y y y

Los mtodos parciales son implcitamente private y, por consiguiente, no pueden ser virtual. Los mtodos parciales no pueden ser extern, porque la presencia del cuerpo determina si son de definicin o de implementacin. Los mtodos parciales pueden tener modificadores static y unsafe. Los mtodos parciales pueden ser genricos.Las restricciones se colocan en la declaracin de mtodo parcial que realiza la definicin, y se pueden repetir opcionalmente en la parte de implementacin.Los nombres de parmetros y parmetros de tipo no tienen que ser iguales en la declaracin que realiza la implementacin y en la que realiza la definicin. Se puede crear un delegado de un mtodo parcial que se ha definido e implementado, pero no de un mtodo parcial que solo se ha definido.

You might also like