Sie sind auf Seite 1von 29

Programacin Orientada a Objetos Trabajo Voluntario POO-TV-C#_Clases mayo, 2002

C#: Clases, Polimorfismo y Excepciones

Daniel Palacios Mellado Daniel Javier Martn Torres

Departamento de Informtica y Automtica Universidad de Salamanca

Informacin de los autores: Daniel Palacios Mellado


3 de Ingeniera Tcnica en Informtica de Sistemas Facultado de Ciencias Universidad de Salamanca danielpm20@hotmail.com

Daniel Javier Martn Torres


3 de Ingeniera Tcnica en Informtica de Sistemas Facultado de Ciencias Universidad de Salamanca dani@yecladeyeltes.net

Este documento puede ser libremente distribuido. 2002 Departamento de Informtica y Automtica - Universidad de Salamanca.

Resumen
Muchas son las cosas que se podran escribir acerca de este nuevo lenguaje orientado a objetos creado por Microsoft, pero eso es un trabajo que ocupara cientos de folios y ya hay libros que tratan de cubrir esa necesidad. Nosotros nos hemos limitado a contar algunas de las caractersticas ms importantes de este lenguaje en propiedades inherentes a los lenguajes orientados a objetos como son las clases, la herencia o el polimorfismo, tratando desde conceptos tan simples como la definicin de una clase hasta algunos ms complejos como es la utilizacin de mtodos virtuales, las clases abstractas o la redefinicin de operadores. Tambin nos hemos encargado de tratar el tema de las excepciones, usadas en los lenguajes de orientacin a objetos modernos como importante ayuda en la difcil tarea de tratamiento de errores producidos en tiempo de ejecucin. En definitiva, un pequeo informe para conocer algo ms sobre un lenguaje con mucho futuro dentro del mundo de la programacin.

Abstract
Many are the things that could be written about this new object oriented language created by Microsoft, but that is a work that would occupy hundred of sheets and there are already books that try to cover that necessity. We have limited ourselves to count some of the most important characteristics in this language in inherent properties to the object oriented languages like they are the classes, the inheritance or the polymorphism, trying from concepts so simple as the definition of a class until some more complex ones as it is the use of virtual methods, the abstract classes or the redefinition of operators. We have also taken charge of treating the topic of the exceptions, used in the modern object oriented languages as important help in the difficult task of treatment of errors taken place in time of execution. In definitive, a small report to know something more envelope a language with much future inside the world of the programming.

POO-TV-C#_Clases

Tabla de Contenidos
C#: CLASES, POLIMORFISMO Y EXCEPCIONES .................................................................. 1 1. 2. INTRODUCCIN.................................................................................................................... 1 CLASES Y HERENCIA .......................................................................................................... 1 2.1. CONCEPTOS DE CLASE Y OBJETO ........................................................................................ 1 2.2. SINTAXIS DE DEFINICIN DE CLASES .................................................................................. 1 2.2.1 Campos.......................................................................................................................... 1 2.2.2 Mtodos......................................................................................................................... 2 2.3. CONCEPTO DE HERENCIA .................................................................................................... 2 2.4. CONCEPTO DE MTODO ...................................................................................................... 3 2.5. TIPOS DE PARMETROS. SINTAXIS DE DEFINICIN .............................................................. 3 2.5.1 Parmetros de entrada.................................................................................................. 3 2.5.2 Parmetros de salida .................................................................................................... 4 2.5.3 Parmetros por referencia ............................................................................................ 4 2.5.4 Parmetros de nmero indefinido................................................................................. 4 2.5.5 Sobrecarga de tipos de parmetros............................................................................... 5 2.6. MTODOS VIRTUALES ........................................................................................................ 5 2.7. CONCEPTO DE PROPIEDAD .................................................................................................. 6 2.8. DEFINICIN DE PROPIEDADES ............................................................................................. 6 2.9. ACCESO A PROPIEDADES .................................................................................................... 8 2.10. IMPLEMENTACIN INTERNA DE PROPIEDADES .................................................................... 8 2.11. CLASES ABSTRACTAS ......................................................................................................... 9 2.12. LA CLASE PRIMIGENIA: SYSTEM.OBJECT............................................................................ 9 3. POLIMORFISMO ................................................................................................................. 11 3.1. 3.2. 3.3. 3.4. 3.5. 3.6. 4. CONCEPTO DE POLIMORFISMO .......................................................................................... 11 MTODOS GENRICOS ...................................................................................................... 12 DETERMINACIN DE TIPO. OPERADOR IS .......................................................................... 12 ACCESO A LA CLASE BASE ................................................................................................ 12 DOWNCASTING ................................................................................................................. 13 CLASES Y MTODOS SELLADOS ........................................................................................ 14

EXCEPCIONES ..................................................................................................................... 15 4.1. 4.2. 4.3. 4.4. 4.5. CONCEPTO DE EXCEPCIN. ............................................................................................... 15 LA CLASE SYSTEM.EXCEPTION......................................................................................... 16 EXCEPCIONES PREDEFINIDAS COMUNES ........................................................................... 17 LANZAMIENTO DE EXCEPCIONES. INSTRUCCIN THROW .................................................. 18 CAPTURA DE EXCEPCIONES. INSTRUCCIN TRY................................................................ 18

5.

REDEFINICIN DE OPERADORES................................................................................. 22 5.1. DEFINICIN DE REDEFINICIONES DE OPERADORES ............................................................ 23 5.1.1 Sintaxis general de redefinicin de operador ............................................................. 23

6. 7.

CONCLUSIONES .................................................................................................................. 24 BIBLIOGRAFA .................................................................................................................... 25

ii

POO-TV-C#_Clases

1. Introduccin
C# es un lenguaje de nueva creacin que a parece en .NET, la nueva generacin de productos orientados a Internet y dispositivos mviles que recientemente ha lanzado Microsoft. Combina algunas de las caractersticas mas avanzadas de Java con algunas de las mas potentes de C y C++. Como en Java, su sintaxis es sencilla y fiable pero tambin permite programar a ms bajo nivel a travs del uso de punteros, como en C y C++. En este documento se explica lo referente a las clases, el polimorfismo y las excepciones que se dan en C#. Para terminar se explica la posibilidad de redefinicin de operadores que aporta C#.

2. Clases y herencia
2.1. Conceptos de clase y objeto
C# es un lenguaje orientado a objetos puro, lo que significa que todo con lo que vamos a trabajar en este lenguaje son objetos. Un objeto es un agregado de datos y de mtodos que permiten manipular dichos datos, y un programa en C# no es ms que un conjunto de objetos que interaccionan unos con otros a travs de sus mtodos. Una clase es la definicin de las caractersticas concretas de un determinado tipo de objetos. Es decir, de cules son los datos y los mtodos de los que van a disponer todos los objetos de ese tipo. Por esta razn, se suele decir que el tipo de dato de un objeto es la clase que define las caractersticas del mismo.

2.2.

Sintaxis de definicin de clases

La sintaxis bsica para definir una clase es la que a continuacin se muestra:


class <nombreClase> { <miembros> }

De este modo se definira una clase de nombre <nombreClase> cuyos miembros son los definidos en <miembros> Los miembros de una clase son los datos y mtodos de los que van a disponer todos los objetos de la misma. Aunque en C# hay muchos tipos de miembros distintos, por ahora vamos a considerar que estos nicamente pueden ser campos o mtodos y vamos a hablar un poco acerca de ellos y de cmo se definen.

2.2.1

Campos

Un campo es un dato comn a todos los objetos de una determinada clase. Para definir cules son los campos de los que una clase dispone se usa la siguiente sintaxis dentro de la zona sealada como <miembros> en la definicin de la misma:

<tipoCampo> <nombreCampo>;

-1-

C#: Clases, Polimorfismo y Excepciones

Los campos de un objeto son a su vez objetos, y en <tipoCampo> hemos de indicar cul es el tipo de dato del objeto que vamos a crear. Para acceder a un campo de un determinado objeto se usa la sintaxis:

<objeto>.<campo>

2.2.2

Mtodos

Un mtodo es un conjunto de instrucciones a las que se les asocia un nombre de modo que si se desea ejecutarlas basta referenciarlas a travs de dicho nombre en vez de tener que escribirlas. Dentro de estas instrucciones es posible acceder con total libertad a la informacin almacenada en los campos pertenecientes a la clase dentro de la que el mtodo se ha definido, por lo que los mtodos permiten manipular los datos almacenados en los objetos. La sintaxis que se usa en C# para definir los mtodos es la siguiente:

<tipoDevuelto> <nombreMtodo> (<parametros>) { <instrucciones> }


Todo mtodo puede devolver un objeto como resultado de la ejecucin de las instrucciones que lo forman, y el tipo de dato al que pertenece este objeto es lo que se indica en <tipoDevuelto>. Si no devuelve nada se indica void, y si devuelve algo es obligatorio finalizar la ejecucin de sus instrucciones con alguna instruccin que indique qu objeto ha de devolverse

return <objeto>;
Opcionalmente todo mtodo puede recibir en cada llamada una lista de objetos a los que podr acceder durante la ejecucin de sus instrucciones. En <parametros> se indica es cules son los tipos de dato de estos objetos y cul es el nombre con el que harn referencia las instrucciones del mtodo a cada uno de ellos. Aunque los objetos que puede recibir el mtodo pueden ser diferentes cada vez que se solicite su ejecucin, siempre han de ser de los mismos tipos y han de seguir el orden establecido en <parametros>. La sintaxis usada para llamar a los mtodos de un objeto es la misma que la usada para llamar a sus campos, slo que ahora tras el nombre del mtodo al que se desea llamar hay que indicar entre parntesis cules son los valores que se desea dar a los parmetros del mtodo al hacer la llamada. O sea, se escribe:

<objeto>.<mtodo>(<parmetros>)

2.3.

Concepto de herencia

El mecanismo de herencia es uno de los pilares fundamentales en los que se basa la programacin orientada a objetos. Es un mecanismo que permite definir nuevas clases a partir de otras ya definidas de modo que si en la definicin de una clase indicamos que sta deriva de otra, entonces la primera -a la que se le suele llamar clase hija- ser tratada por el compilador automticamente como si su definicin incluyese la definicin de la segunda a la que se le

POO-TV-C#_Clases

Palacios y Martn

suele llamar clase padre o clase base. Las clases que derivan de otras se definen usando la siguiente sintaxis:
class <nombreHija>:<nombrePadre> { <miembrosHija> }

A los miembros definidos en <miembrosHijas> se le aadirn los que hubisemos definido en la clase padre.

2.4.

Concepto de mtodo

Un mtodo es un conjunto de instrucciones a las que se les da un determinado nombre de tal manera que sea posible ejecutarlas en cualquier momento sin tenerlas que reescribir sino usando slo su nombre. A estas instrucciones se les denomina cuerpo del mtodo, y a su ejecucin a travs de su nombre se le denomina llamada al mtodo. La ejecucin de las instrucciones de un mtodo puede producir como resultado un objeto de cualquier tipo. A este objeto se le llama valor de retorno del mtodo y es completamente opcional, pudindose escribir mtodos que no devuelvan ninguno. La ejecucin de las instrucciones de un mtodo puede depender del valor de unas variables especiales denominadas parmetros del mtodo, de manera que en funcin del valor que se d a estas variables en cada llamada la ejecucin del mtodo se pueda realizar de una u otra forma y podr producir uno u otro valor de retorno. Al conjunto formado por el nombre de un mtodo y el nmero y tipo de sus parmetros se le conoce como signatura del mtodo. La signatura de un mtodo es lo que verdaderamente lo identifica, de modo que es posible definir en un mismo tipo varios mtodos con idntico nombre siempre y cuando tengan distintos parmetros. Cuando esto ocurre se dice que el mtodo que tiene ese nombre est sobrecargado.

2.5.

Tipos de parmetros. Sintaxis de definicin

La forma en que se define cada parmetro de un mtodo depende del tipo de parmetro del que se trate. En C# se admiten cuatro tipos de parmetros: parmetros de entrada, parmetros de salida, parmetros por referencia y parmetros de nmero indefinido.

2.5.1

Parmetros de entrada

Un parmetro de entrada recibe una copia del valor que almacenara una variable del tipo del objeto que se le pase. Por tanto, si el objeto es de un tipo valor se le pasar una copia del objeto y cualquier modificacin que se haga al parmetro dentro del cuerpo del mtodo no afectar al objeto original sino a su copia; mientras que si el objeto es de un tipo referencia entonces se le pasar una copia de la referencia al mismo y cualquier modificacin que se haga al parmetro dentro del mtodo tambin afectar al objeto original ya que en realidad el parmetro referencia a ese mismo objeto original. Para definir un parmetro de entrada basta indicar cul el nombre que se le desea dar y cul es el tipo de dato que podr almacenar. En las llamadas a mtodos se expresan los valores que se deseen dar a este tipo de parmetros indicando simplemente el valor deseado.

POO-TV-C#_Clases

C#: Clases, Polimorfismo y Excepciones

2.5.2

Parmetros de salida

Un parmetro de salida se diferencia de uno de entrada en que todo cambio que se le realice en el cdigo del mtodo al que pertenece afectar al objeto que se le pase al llamar dicho mtodo tanto si ste es de un tipo valor como si es de un tipo referencia. Esto se debe a que lo que a estos parmetros se les pasa es siempre una referencia al valor que almacenara una variable del tipo del objeto que se les pase. Cualquier parmetro de salida de un mtodo siempre ha de modificarse dentro del cuerpo del mtodo y adems dicha modificacin ha de hacerse antes que cualquier lectura de su valor. Si esto no se hiciese as el compilador lo detectara e informara de ello con un error. Por esta razn es posible pasar parmetros de salida que sean variables no iniciadas, pues se garantiza que en el mtodo se iniciarn antes de leerlas. Adems, tras la llamada a un mtodo se considera que las variables que se le pasaron como parmetros de salida ya estarn inicializadas, pues dentro del mtodo seguro que se las inicializacin. Ntese que este tipo de parmetros permiten disear mtodos que devuelvan mltiples objetos: un objeto se devolvera como valor de retorno y los dems se devolveran escribiendo en los parmetros de salida. Los parmetros de salida se definen de forma parecida a los parmetros de entrada pero se les ha de aadir la palabra reservada out. O sea, se definen as:
out <tipoParmetro> <nombreParmetro>

Al llamar a un mtodo que tome parmetros de este tipo tambin se ha preceder el valor especificado para estos parmetros del modificador out. Una utilidad de esto es facilitar la legibilidad de las llamadas a mtodos.

2.5.3

Parmetros por referencia

Un parmetro por referencia es similar a un parmetro de salida slo que no es obligatorio modificarlo dentro del mtodo al que pertenece, por lo que ser obligatorio pasarle una variable inicializada ya que no se garantiza su inicializacin en el mtodo. Los parmetros por referencia se definen igual que los parmetros de salida pero sustituyendo el modificador out por el modificador ref. Del mismo modo, al pasar valores a parmetros por referencia tambin hay que precederlos del ref.

2.5.4

Parmetros de nmero indefinido

C# permite disear mtodos que puedan tomar cualquier nmero de parmetros. Para ello hay que indicar como ltimo parmetro del mtodo un parmetro de algn tipo de tabla unidimensional o dentada precedido de la palabra reservada params. Por ejemplo:
static void F(int x, params object[] extras) {}

Todos los parmetros de nmero indefinido que se pasan al mtodo al llamarlo han de ser del mismo tipo que la tabla. Ntese que en el ejemplo ese tipo es la clase primigenia object, con lo que se consigue que gracias al polimorfismo el mtodo pueda tomar cualquier nmero de parmetros de cualquier tipo. Es importante sealar que la prioridad de un mtodo que incluya el params es inferior a la de cualquier otra sobrecarga del mismo

POO-TV-C#_Clases

Palacios y Martn

2.5.5

Sobrecarga de tipos de parmetros

En realidad los modificadores ref y out de los parmetros de un mtodo tambin forman parte de lo que se conoce como signatura del mtodo, por lo que esta clase es vlida:
class Sobrecarga { public void f(int x) {} public void f(out int x) {} }

Ntese que esta clase es correcta porque cada uno de sus mtodos tiene una signatura distinta: el parmetro es de entrada en el primero y de salida en el segundo. Sin embargo, hay una restriccin: no puede ocurrir que la nica diferencia entre la signatura de dos mtodos sea que en uno un determinado parmetro lleve el modificador ref y en el otro lleve el modificador out. Por ejemplo, no es vlido:
class SobrecargaInvlida { public void f(ref int x) {} public void f(out int x) {} }

2.6.

Mtodos virtuales

Veamos cmo es posible cambiar la definicin de un mtodo en la clase hija, para lo que habra que haber precedido con la palabra reservada virtual la definicin de dicho mtodo en la clase padre. A este tipo de mtodos se les llama mtodos virtuales, y la sintaxis que se usa para definirlos es la siguiente:
virtual <tipoDevuelto> <nombreMtodo>(<parmetros>) { <cdigo> }

Si en alguna clase hija quisiramos dar una nueva definicin del <cdigo> del mtodo, simplemente lo volveramos a definir en la misma pero sustituyendo en su definicin la palabra reservada virtual por override. Es decir, usaramos esta sintaxis:
override <tipoDevuelto> <nombreMtodo>(<parmetros>) { <nuevoCdigo> }

Ntese que esta posibilidad de cambiar el cdigo de un mtodo en su clase hija slo se da si en la clase padre el mtodo fue definido como virtual. En caso contrario, el compilador considerar un error intentar redefinirlo. El lenguaje C# impone la restriccin de que toda redefinicin de mtodo que queramos realizar incorpore la partcula override para forzar a que el programador est seguro de que verdaderamente lo que quiere hacer es cambiar el significado de un mtodo heredado. As se evita que por accidente defina un mtodo del que ya exista una definicin en una clase padre.

POO-TV-C#_Clases

C#: Clases, Polimorfismo y Excepciones

Adems, C# no permite definir un mtodo como override y virtual a la vez, ya que ello tendra un significado absurdo: estaramos dando una redefinicin de un mtodo que vamos a definir. Por otro lado, cuando definamos un mtodo como override ha de cumplirse que en alguna clase antecesora (su clase padre, su clase abuela, etc.) de la clase en la que se ha realizado la definicin del mismo exista un mtodo virtual con el mismo nombre que el redefinido. Si no, el compilador informar de error por intento de redefinicin de mtodo no existente o no virtual. As se evita que por accidente un programador crea que est redefiniendo un mtodo del que no exista definicin previa o que redefina un mtodo que el creador de la clase base no desee que se pueda redefinir. Tambin es importante sealar que para que la redefinicin sea vlida es necesario aadir la partcula public a la definicin del mtodo original, pues si no se incluyese se considerara que el mtodo slo es accesible desde dentro de la clase donde se ha definido, lo que no tiene sentido en mtodos virtuales ya que entonces nunca podra ser redefinido. De hecho, si se excluyese el modificador public el compilador informara de un error ante este absurdo. Adems, este modificador tambin se ha de mantener porque toda redefinicin de un mtodo virtual ha de mantener los mismos modificadores de acceso que el mtodo original para ser vlida.

2.7.

Concepto de propiedad

Una propiedad es una mezcla entre el concepto de campo y el concepto de mtodo. Externamente es accedida como si de un campo normal se tratase, pero internamente es posible asociar cdigo a ejecutar en cada asignacin o lectura de su valor. ste cdigo puede usarse para comprobar que no se asignen valores invlidos, para calcular su valor slo al solicitar su lectura, etc. Una propiedad no almacena datos, sino slo se utiliza como si los almacenase. En la prctica lo que se suele hacer es escribir como cdigo a ejecutar cuando se le asigne un valor, cdigo que controle que ese valor sea correcto y que lo almacene en un campo privado si lo es; y como cdigo a ejecutar cuando se lea su valor, cdigo que devuelva el valor almacenado en ese campo pblico. As se simula que se tiene un campo pblico sin los inconvenientes que estos presentan por no poderse controlar el acceso a ellos.

2.8.

Definicin de propiedades
< tipoPropiedad> <nombrePropiedad> { set { <cdigoEscritura> } get { <cdigoLectura> } }

Para definir una propiedad se usa la siguiente sintaxis:

Una propiedad as definida sera accedida como si de un campo de tipo <tipoPropiedad> se tratase, pero en cada lectura de su valor se ejecutara el <cdigoLectura> y en cada escritura de un valor en ella se ejecutara <cdigoEscritura>

POO-TV-C#_Clases

Palacios y Martn

Al escribir los bloques de cdigo get y set hay que tener en cuenta que dentro del cdigo set se puede hacer referencia al valor que se solicita asignar a travs de un parmetro especial del mismo tipo de dato que la propiedad llamado value (luego nosotros no podemos definir uno con ese nombre en <cdigoEscritura>); y que dentro del cdigo get se ha de devolver siempre un objeto del tipo de dato de la propiedad. En realidad el orden en que aparezcan los bloques de cdigo set y get es irrelevante. Adems, es posible definir propiedades que slo tengan el bloque get (propiedades de slo lectura) o que slo tengan el bloque set (propiedades de slo escritura) Lo que no es vlido es definir propiedades que no incluyan ninguno de los dos bloques. Las propiedades participan del mecanismo de polimorfismo igual que los mtodos, siendo incluso posible definir propiedades cuyos bloques de cdigo get o set sean abstractos. Esto se hara prefijando el bloque apropiado con un modificador abstract y sustituyendo la definicin de su cdigo por un punto y coma. Por ejemplo:
using System; abstract class A { public abstract int PropiedadEjemplo { set; get; } } class B:A { private int valor; public override int PropiedadEjemplo { get { Console.WriteLine(Ledo {0} de PropiedadEjemplo, valor); return valor; } set { valor = value; Console.WriteLine(Escrito {0} en PropiedadEjemplo, valor); } } }

En este ejemplo se ve cmo se definen y redefinen propiedades abstractas. Al igual que


abstract y override, tambin es posible usar cualquiera de los modificadores relativos a herencia y polimorfismo ya vistos: virtual, new y sealed.

Ntese que aunque en el ejemplo se ha optado por asociar un campo privado valor a la propiedad PropiedadEjemplo, en realidad nada obliga a que ello se haga y es posible definir propiedades que no tenga campos asociados. Es decir, una propiedad no se tiene porqu corresponder con un almacn de datos.

POO-TV-C#_Clases

C#: Clases, Polimorfismo y Excepciones

2.9.

Acceso a propiedades

La forma de acceder a una propiedad, ya sea para lectura o escritura, es exactamente la misma que la que se usara para acceder a un campo de su mismo tipo. Por ejemplo, se podra acceder a la propiedad de un objeto de la clase B del ejemplo anterior con:
B obj = new B(); obj.PropiedadEjemplo++;

El resultado que por pantalla se mostrara al hacer una asignacin como la anterior sera:
Ledo 0 de PropiedadEjemplo; Escrito 1 en PropiedadEjemplo;

Ntese que en el primer mensaje se muestra que el valor ledo es 0 porque lo que devuelve el bloque get de la propiedad es el valor por defecto del campo privado valor, que como es de tipo int tiene como valor por defecto 0.

2.10. Implementacin interna de propiedades


En realidad la definicin de una propiedad con la sintaxis antes vista es convertida por el compilador en la definicin de un par de mtodos de la siguiente forma:
<tipoPropiedad> get_<nombrePropiedad>() { // Mtodo en que se convierte el bloque get <cdigoLectura> } void set_<nombrePropiedad> (<tipoPropiedad> value) { // Mtodo en que se convierte el bloque set <cdigoEscritura> }

Esto se hace para que desde lenguajes que no soporten las propiedades se pueda acceder tambin a ellas. Si una propiedad es de slo lectura slo se generar el mtodo get_X(), y si es de slo escritura slo se generar el set_X() Ahora bien, en cualquier caso hay que tener cuidado con no definir en un mismo tipo de dato mtodos con signaturas como estas si se van a generar internamente debido a la definicin de una propiedad, ya que ello provocara un error de definicin mltiple de mtodo. Teniendo en cuenta la implementacin interna de las propiedades, es fcil ver que el ltimo ejemplo de acceso a propiedad es equivalente a:
B b = new B(); obj.set_PropiedadEjemplo(obj.get_Propiedad_Ejemplo()++);

Como se ve, gracias a las propiedades se tiene una sintaxis mucho ms compacta y clara para acceder a campos de manera controlada. Se podra pensar que la contrapartida de esto es que el tiempo de acceso al campo aumenta considerablemente por perderse tiempo en hacer las llamada a mtodos set/get. Pues bien, esto no tiene porqu ser as ya que el compilador de C# elimina llamadas haciendo inlining (sustitucin de la llamada por su cuerpo) en los accesos a bloques get/set no virtuales y de cdigos pequeos, que son los ms habituales. Ntese que de la forma en que se definen los mtodos generados por el compilador se puede deducir el porqu del hecho de que en el bloque set se pueda acceder a travs de value al

POO-TV-C#_Clases

Palacios y Martn

valor asignado y de que el objeto devuelto por el cdigo de un bloque get tenga que ser del mismo tipo de dato que la propiedad a la que pertenece.

2.11. Clases abstractas


Una clase abstracta es aquella que forzosamente se ha de derivar si se desea que se puedan crear objetos de la misma o acceder a sus miembros estticos. Para definir una clase abstracta se antepone abstract a su definicin, como se muestra en el siguiente ejemplo:
public abstract class A { public abstract void F(); } abstract public class B: A { public void G() {} } class C: B { public override void F() {} }

Las clases A y B del ejemplo son abstractas, y como puede verse es posible combinar en cualquier orden el modificador abstract con modificadores de acceso. La utilidad de las clases abstractas es que pueden contener mtodos para los que no se d directamente una implementacin sino que se deje en manos de sus clases hijas darla. No es obligatorio que las clases abstractas contengan mtodos de este tipo, pero s lo es marcar como abstracta a toda la que tenga alguno. Estos mtodos se definen precediendo su definicin del modificador abstract y sustituyendo su cdigo por un punto y coma (;), como se muestra en el mtodo F() de la clase A del ejemplo (ntese que B tambin ha de definirse como abstracta porque tampoco implementa el mtodo F() que hereda de A) Obviamente, como un mtodo abstracto no tiene cdigo no es posible llamarlo. Hay que tener especial cuidado con esto a la hora de utilizar this para llamar a otros mtodos de un mismo objeto, ya que llamar a los abstractos provoca un error al compilar. Vase que todo mtodo definido como abstracto es implcitamente virtual, pues si no sera imposible redefinirlo para darle una implementacin en las clases hijas de la clase abstracta donde est definido. Por ello es necesario incluir el modificador override a la hora de darle implementacin y es redundante marcar un mtodo como abstract y virtual a la vez (de hecho, hacerlo provoca un error al compilar) Es posible marcar un mtodo como abstract y override a la vez, lo que convertira al mtodo en abstracto para sus clases hijas y forzara a que stas lo tuviesen que reimplementar si no se quisiese que fuesen clases abstractas.

2.12. La clase primigenia: System.Object


Ahora es el momento apropiado para explicar que en .NET todos los tipos que se definan heredan implcitamente de la clase System.Object, por lo que dispondrn de todos los miembros de sta. Por esta razn se dice que System.Object es la raz de la jerarqua de objetos de .NET.

POO-TV-C#_Clases

C#: Clases, Polimorfismo y Excepciones

A continuacin vamos a explicar cules son estos mtodos comunes a todos los objetos:
public virtual bool Equals(object o): Se usa para comparar el objeto sobre el que se aplica con cualquier otro que se le pase como parmetro. Devuelve true si ambos objetos son iguales y false en caso contrario.

La implementacin que por defecto se ha dado a este mtodo consiste en usar igualdad por referencia para los tipos por referencia e igualdad por valor para los tipos por valor. Es decir, si los objetos a comparar son de tipos por referencia slo se devuelve true si ambos objetos apuntan a la misma referencia en memoria dinmica, y si los tipos a comparar son tipos por valor slo se devuelve true si todos los bits de ambos objetos son iguales, aunque se almacenen en posiciones diferentes de memoria.

Como se ve, el mtodo ha sido definido como virtual, lo que permite que los programadores puedan redefinirlo para indicar cundo ha de considerarse que son iguales dos objetos de tipos definidos por ellos.

public virtual int GetHashCode(): Devuelve un cdigo de dispersin (hash) que

representa de forma numrica al objeto sobre el que el mtodo es aplicado.


GetHashCode() suele usarse para trabajar con tablas de dispersin, y se cumple que si

dos objetos son iguales sus cdigos de dispersin sern iguales, mientras que si son distintos la probabilidad de que sean iguales es nfima. En tanto que la bsqueda de objetos en tablas de dispersin no se realiza nicamente usando la igualdad de objetos (mtodo Equals()) sino usando tambin la igualdad de cdigos de dispersin, suele ser conveniente redefinir GetHashCode() siempre que se redefina Equals() De hecho, si no se hace el compilador informa de la situacin con un mensaje de aviso.

public virtual string ToString(): Devuelve una representacin en forma de cadena del

objeto sobre el que se el mtodo es aplicado, lo que es muy til para depurar aplicaciones ya que permite mostrar con facilidad el estado de los objetos. La implementacin por defecto de este mtodo simplemente devuelve una cadena de texto con el nombre de la clase a la que pertenece el objeto sobre el que es aplicado.

protected object MemberWiseClone(): Devuelve una copia shallow copy del

objeto sobre el que se aplica. Esta copia es una copia bit a bit del mismo, por lo que el objeto resultante de la copia mantendr las mismas referencias a otros que tuviese el objeto copiado y toda modificacin que se haga a estos objetos a travs de la copia afectar al objeto copiado y viceversa. Si lo que interesa es disponer de una copia ms normal, en la que por cada objeto referenciado se crease una copia del mismo a la que referenciase el objeto clonado, entonces el programador ha de escribir su propio mtodo clonador pero puede servirse

POO-TV-C#_Clases

10

Palacios y Martn

de MemberwiseClone() como base con la que copiar los campos que no sean de tipos referencia.
public System.Type GetType(): Devuelve un objeto de clase System.Type que representa al tipo de dato del objeto sobre el que el mtodo es aplicado. A travs de los mtodos ofrecidos por este objeto se puede acceder a metadatos sobre el mismo como su nombre, su clase padre, sus miembros, etc. protected virtual void Finalize(): Contiene el cdigo que se ejecutar siempre que vaya ha ser destruido algn objeto del tipo del que sea miembro. La implementacin dada por defecto a Finalize() consiste en no hacer nada.

Aunque es un mtodo virtual, en C# no se permite que el programador lo redefina explcitamente. Aparte de los mtodos ya comentados que todos los objetos heredan, la clase
System.Object tambin incluye en su definicin los siguientes mtodos de tipo:

public static bool Equals(object objeto1, object objeto2) Versin esttica del mtodo Equals() ya visto. Indica si los objetos que se le pasan como parmetros son

iguales, y para compararlos lo que hace es devolver el resultado de calcular


objeto1.Equals(objeto2) comprobando antes si alguno de los objetos vale null (slo se devolvera true slo si el otro tambin lo es)

Obviamente si se da una redefinicin al Equals() no esttico, esta tambin se aplicar al esttico.

public static bool ReferenceEquals(object objeto1, object objeto2)

Indica si los dos objetos que se le pasan como parmetro se almacenan en la misma posicin de memoria dinmica. A travs de este mtodo, aunque se hayan redefinido Equals() y el operador de igualdad (==) para un cierto tipo por referencia, se podrn seguir realizando comparaciones por referencia.entre objetos de ese tipo en tanto que redefinir de Equals() no afecta a este mtodo.

3. Polimorfismo
3.1. Concepto de polimorfismo
El polimorfismo es otro de los pilares fundamentales de la programacin orientada a objetos. Es la capacidad de almacenar objetos de un determinado tipo en variables de tipos antecesores del primero a costa, claro est, de slo poderse acceder a travs de dicha variable a los miembros comunes a ambos tipos. Sin embargo, las versiones de los mtodos virtuales a las que se llamara a travs de esas variables no seran las definidas como miembros del tipo de dichas variables, sino las definidas en el verdadero tipo de los objetos que almacenan.

11

POO-TV-C#_Clases

C#: Clases, Polimorfismo y Excepciones

3.2.

Mtodos genricos

El polimorfismo es muy til ya que permite escribir mtodos genricos que puedan recibir parmetros que sean de un determinado tipo o de cualquiera de sus tipos hijos. Es ms, en C# todos los tipos derivan implcitamente del tipo System.Object, podemos escribir mtodos que admitan parmetros de cualquier tipo sin ms que definirlos como mtodos que tomen parmetros de tipo System.Object.

3.3.

Determinacin de tipo. Operador is

Dentro de una rutina polimrfica que admita parmetros que puedan ser de cualquier tipo, muchas veces es conveniente poder consultar en el cdigo de la misma cul es el tipo en concreto del parmetro que se haya pasado al mtodo en cada llamada al mismo. Para ello C# ofrece el operador is, cuya forma sintaxis de uso es:
<expresin> is <nombreTipo>

Este operador devuelve true en caso de que el resultado de evaluar <expresin> sea del tipo cuyo nombre es <nombreTipo> y false en caso contrario. Gracias a ellas podemos escribir mtodos genricos que puedan determinar cul es el tipo que tienen los parmetros que en cada llamada en concreto se les pasen. O sea, mtodos como:
public void MtodoGenrico(object o) { if (o is int) // Si o es de tipo int (entero)... // ...Cdigo a ejecutar si el objeto o es de tipo int else if (o is string) // Si no, si o es de tipo string (cadena)... // ...Cdigo a ejecutar si o es de tipo string //... Idem para otros tipos }

3.4.

Acceso a la clase base

Hay determinadas circunstancias en las que cuando redefinamos un determinado mtodo nos interese poder acceder al cdigo de la versin original. Por ejemplo, porque el cdigo redefinido que vayamos a escribir haga lo mismo que el original y adems algunas cosas extras. En estos casos se podra pensar que una forma de conseguir esto sera convirtiendo el objeto actual al tipo del mtodo a redefinir y entonces llamar as a ese mtodo, como por ejemplo en el siguiente cdigo:
using System; class A { public virtual void F() { Console.WriteLine(A); } } class B:A { public override void F() {

POO-TV-C#_Clases

12

Palacios y Martn

Console.WriteLine(Antes); ((A) this).F(); // (2) Console.WriteLine(Despus); } public static void Main() { B b = new B(); b.F(); }

} Pues bien, si ejecutamos el cdigo anterior veremos que la aplicacin nunca termina de ejecutarse y est constantemente mostrando el mensaje Antes por pantalla. Esto se debe a que debido al polimorfismo se ha entrado en un bucle infinito: aunque usemos el operador de conversin para tratar el objeto como si fuese de tipo A, su verdadero tipo sigue siendo B, por lo que la versin de F() a la que se llamar en (2) es a la de B de nuevo, que volver a llamarse as misma una y otra vez de manera indefinida. Para solucionar esto, los diseadores de C# han incluido una palabra reservada llamada
base que devuelve una referencia al objeto actual semejante a this pero con la peculiaridad de

que los accesos a ella son tratados como si el verdadero tipo fuese el de su clase base. Usando base, podramos reemplazar el cdigo de la redefinicin de F() de ejemplo anterior por:
public override void F() { Console.WriteLine(Antes); base.F(); Console.WriteLine(Despus); }

Si ejecutamos el programa veremos que ahora s que la versin de F() en B llama a la versin de F() en A, resultando la siguiente salida por pantalla:
Antes A Despus

A la hora de redefinir mtodos abstractos hay que tener cuidado con una cosa: desde el mtodo redefinidor no es posible usar base para hacer referencia a mtodos abstractos de la clase padre, aunque s para hacer referencia a los no abstractos.

3.5.

Downcasting

Dado que una variable de un determinado tipo puede estar en realidad almacenando un objeto que sea de algn tipo hijo del tipo de la variable y en ese caso a travs de la variable slo puede accederse a aquellos miembros del verdadero tipo del objeto que sean comunes con miembros del tipo de la variable que referencia al objeto, muchas veces nos va a interesar que una vez que dentro de un mtodo genrico hayamos determinado cul es el verdadero tipo de un objeto (por ejemplo, con el operador is) podamos tratarlo como tal. En estos casos lo que hay es que hacer una conversin del tipo padre al verdadero tipo del objeto, y a esto se le llama downcasting Para realizar un downcasting una primera posibilidad es indicar preceder la expresin a convertir del tipo en el que se la desea convertir indicado entre parntesis. Es decir, siguiendo la siguiente sintaxis:

13

POO-TV-C#_Clases

C#: Clases, Polimorfismo y Excepciones

(<tipoDestino>) <expresinAConvertir>

El resultado de este tipo de expresin es el objeto resultante de convertir el resultado de


<expresinAConvertir> a <tipoDestino>. En caso de que la conversin no se pudiese realizar se lanzara una excepcin del tipo predefinido System.InvalidCastException

Otra forma de realizar el downcasting es usando el operador as, que se usa as:
<expresinAConvertir> as <tipoDestino>

La principal diferencia de este operador con el anterior es que si ahora la conversin no se pudiese realizar se devolvera null en lugar de lanzarse una excepcin. La otra diferencia es que as slo es aplicable a tipos referencia y slo a conversiones entre tipos de una misma jerarqua (de padres a hijos o viceversa) Los errores al realizar conversiones de este tipo en mtodos genricos se producen cuando el valor pasado a la variable genrica no es ni del tipo indicado en <tipoDestino> ni existe ninguna definicin de cmo realizar la conversin a ese tipo.

3.6.

Clases y mtodos sellados

Una clase sellada es una clase que no puede tener clases hijas, y para definirla basta anteponer el modificador sealed a la definicin de una clase normal. Por ejemplo:
sealed class ClaseSellada { }

Una utilidad de definir una clase como sellada es que permite que las llamadas a sus mtodos virtuales heredados se realicen tan eficientemente como si fuesen no virtuales, pues al no poder existir clases hijas que los redefinan no puede haber polimorfismo y no hay que determinar cul es la versin correcta del mtodo a la que se ha de llamar. Ntese que se ha dicho mtodos virtuales heredados, pues lo que no se permite es definir miembros virtuales dentro de este tipo de clases, ya que al no poderse heredarse de ellas es algo sin sentido en tanto que nunca podran redefinirse. Ahora bien, hay que tener en cuenta que sellar reduce enormemente su capacidad de reutilizacin, y eso es algo que el aumento de eficiencia obtenido en las llamadas a sus mtodos virtuales no suele compensar. En realidad la principal causa de la inclusin de estas clases en C# es que permiten asegurar que ciertas clases crticas nunca podrn tener clases hijas. Tngase en cuenta que es absurdo definir simultneamente una clase como abstract y sealed, pues nunca podra accederse a la misma al no poderse crear clases hijas suyas que definan sus mtodos abstractos. Por esta razn, el compilador considera errneo definir una clase con ambos modificadores a la vez. Aparte de para sellar clases, tambin se puede usar sealed como modificador en la redefinicin de un mtodo para conseguir que la nueva versin del mismo que se defina deje de ser virtual y se le puedan aplicar las optimizaciones arriba comentadas.

POO-TV-C#_Clases

14

Palacios y Martn

4. Excepciones
4.1. Concepto de excepcin.
Las excepciones son el mecanismo recomendado en la plataforma .NET para la propagacin de errores que se produzcan durante la ejecucin de las aplicaciones (divisiones por cero, intentos de lectura de archivos daados, etc.) Bsicamente una excepcin es un objeto derivado de System.Exception que se genera cuando en tiempo de ejecucin se produce algn error y que contiene informacin sobre el mismo. Tradicionalmente, el sistema que en otros lenguajes y plataformas se ha venido usando para informar estos errores consista simplemente en hacer que los mtodos en cuya ejecucin pudiesen producirse devolvieran cdigos que informasen sobre si se han ejecutado correctamente o, en caso contrario, sobre cul fue el error producido. Sin embargo, las excepciones proporcionan las siguientes ventajas frente a dicho sistema: Claridad: El uso de cdigos especiales para informar de error suele dificultar la legibilidad del fuente en tanto que se mezclan las instrucciones propias de la lgica del mismo con las instrucciones propias del tratamiento de los errores que pudiesen producirse durante su ejecucin. Por ejemplo:
int resultado = obj.Mtodo(); if (resultado == 0) // Sin errores al ejecutar obj.Mtodo(); {...} else if (resultado == 1) // Tratamiento de error de cdigo 1 {...} else if (resultado == 2) // Tratamiento de error de cdigo 2 ...

Como se ver, utilizando excepciones es posible escribir el cdigo como si nunca se fuesen a producir errores y dejar en una zona aparte todo el cdigo de tratamiento de errores, lo que contribuye a facilitar la legibilidad de los fuentes.

Ms informacin: A partir del valor de un cdigo de error puede ser difcil deducir las causas del mismo y conseguirlo muchas veces implica tenerse que consultar la documentacin que proporcionada sobre el mtodo que lo provoc, que puede incluso que no especifique claramente su causa. Por el contrario, una excepcin es un objeto que cuenta con campos que describen las causas del error y a cuyo tipo suele drsele un nombre que resuma claramente su causa. Por ejemplo, para informar errores de divisin por cero se suele utilizar una excepcin predefinida de tipo DivideByZeroException en cuyo campo Message se detallan las causas del error producido

Tratamiento asegurado: Cuando se utilizan cdigos de error nada obliga a tratarlos en cada llamada al mtodo que los pueda producir, e ignorarlos puede provocar ms adelante en el cdigo comportamientos inesperados de causas difciles de descubrir. Cuando se usan excepciones siempre se asegura que el programador trate toda excepcin que pueda producirse o que, si no lo hace, se aborte la ejecucin de la aplicacin mostrndose un mensaje indicando dnde se ha producido el error.

15

POO-TV-C#_Clases

C#: Clases, Polimorfismo y Excepciones

Ahora bien, tradicionalmente en lenguajes como C++ el uso de excepciones siempre ha tenido las desventajas respecto al uso de cdigos de error de complicar el compilador y dar lugar a cdigos ms lentos y difciles de optimizar en los que tras cada instruccin que pudiese producir excepciones el compilador debe introducir las comprobaciones necesarias para detectarlas y tratarlas as como para comprobar que los objetos creados sean correctamente destruidos si se producen. Sin embargo, en la plataforma .NET desaparacen los problemas de complicar el compilador y dificultar las optimizaciones ya que es el CLR(Common Language Runtime) quien se encarga de detectar y tratar las excepciones y es su recolector de basura quien se encarga asegurar la correcta destruccin de los objetos. Obviamente el cdigo seguir siendo algo ms lento, pero es un pequeo sacrificio que merece la pena hacer en tanto que ello asegura que nunca se producirn problemas difciles de detectar derivados de errores ignorados.

4.2.

La clase System.Exception

Como ya se ha dicho, todas las excepciones derivan de un tipo predefinido en la BCL(Biblioteca de la Clase Base) llamado System.Exception. Los principales miembros que heredan de ste son:
string Message {virtual get;}: Contiene un mensaje descriptivo de las causas de la excepcin. Por defecto este mensaje es una cadena vaca () Exception InnerException {virtual get;}: Si una excepcin fue causada como consecuencia de otra, esta propiedad contiene el objeto System.Exception que representa a la excepcin que la caus. As se pueden formar cadenas de excepciones de cualquier longitud. Si se desea obtener la ltima excepcin de la cadena es mejor usar el mtodo virtual Exception GetBaseException() string StackTrace {virtual get;}: Contiene la pila de llamadas a mtodos que se tena

en el momento en que se produjo la excepcin. Esta pila es una cadena con informacin sobre cul es el mtodo en que se produjo la excepcin, cul es el mtodo que llam a este, cul es el que llam a ese otro, etc.
string Source {virtual get; virtual set;}: Almacena informacin sobre cul fue la

aplicacin u objeto que caus la excepcin.


MethodBase TargetSite {virtual get;}: Almacena cul fue el mtodo donde se produjo la excepcin en forma de objeto System.Reflection.MethodBase. Puede consultar la

documentacin del SDK si desea cmo obtener informacin sobre las caractersticas del mtodo a travs del objeto MethodBase.
string HelpLink {virtual get;}: Contiene una cadena con informacin sobre cul es la URI donde se puede encontrar informacin sobre la excepcin. El valor de esta cadena puede establecerse con virtual Exception SetHelpLink (string URI), que devuelve la excepcin sobre la que se aplica pero con la URI ya actualizada.

Para crear objetos de clase System.Exception se puede usar los constructores:

POO-TV-C#_Clases

16

Palacios y Martn

Exception() Exception(string msg) Exception(string msg, Exception causante)

El primer constructor crea una excepcin cuyo valor para Message ser y no causada por ninguna otra excepcin (InnerException valdr null) El segundo la crea con el valor indicado para Message, y el ltimo la crea con adems la excepcin causante indicada. En la prctica, cuando se crean nuevos tipos derivados de System.Exception no se suele redefinir sus miembros ni aadirles nuevos, sino que slo se hace la derivacin para distinguir una excepciones de otra por el nombre del tipo al que pertenecen. Ahora bien, es conveniente respetar el convenio de darles un nombre acabado en Exception y redefinir los tres constructores antes comentados.

4.3.

Excepciones predefinidas comunes

En el espacio de nombres System de la BCL hay predefinidas mltiples excepciones derivadas de System.Exception que se corresponden con los errores ms comunes que pueden surgir durante la ejecucin de una aplicacin. En la siguiente tabla se recogen algunas:
Causa de que se produzca la excepcin Tipo de la excepcin ArgumentException ArgumentNullException ArgumentOutOfRangeException ArrayTypeMistmatchException COMException DivideByZeroException IndexOutOfRangeException InvalidCastException InvalidOperationException InteropException NullReferenceException OverflowException Pasado argumento no vlido (base de excepciones de argumentos) Pasado argumento nulo Pasado argumento fuera de rango Asignacin a tabla de elemento que no es de su tipo Excepcin de objeto COM Divisin por cero ndice de acceso a elemento de tabla fuera del rango vlido (menor que cero o mayor que el tamao de la tabla) Conversin explcita entre tipos no vlida Operacin invlida en estado actual del objeto Base de excepciones producidas en comunicacin con cdigo inseguro Acceso a miembro de objeto que vale null Desbordamiento dentro de contexto donde se ha de comprobar los desbordamientos (expresin constante, instruccin checked, operancin checked u opcin del compilador /checked) Falta de memoria para crear un objeto con new Excepcin SHE del API Win32 Desbordamiento de la pila, generalmente debido a un excesivo nmero de llamadas recurrentes. Ha ocurrido alguna excepcin al inicializar los campos estticos o el constructor esttico de un tipo. En InnerException se indica cul es.

OutOfMemoryException SEHException StackOverflowException TypeInizializationException

17

POO-TV-C#_Clases

C#: Clases, Polimorfismo y Excepciones

4.4.

Lanzamiento de excepciones. Instruccin throw

Para informar de un error no basta con crear un objeto del tipo de excepcin apropiado, sino que tambin hay pasrselo al mecanismo de propagacin de excepciones del CLR. A esto se le llama lanzar la excepcin, y para hacerlo se usa la siguiente instruccin:
throw <objetoExcepcinALanzar>;

Por ejemplo, para lanzar una excepcin de tipo DivideByZeroException se podra hacer:
throw new DivideByZeroException();

Si el objeto a lanzar vale null, entonces se producir una NullReferenceException que ser lanzada en vez de la excepcin indicada en la instruccin throw.

4.5.

Captura de excepciones. Instruccin try

Una vez lanzada una excepcin es posible escribir cdigo que se encargue de tratarla. Por defecto, si este cdigo no se escribe la excepcin provoca que la aplicacin aborte mostrando un mensaje de error en el que se describe la excepcin producida (informacin de su propiedad Message) y dnde se ha producido (informacin de su propiedad StackTrace) As, dado el siguiente cdigo fuente de ejemplo:
using System; class PruebaExcepciones { static void Main() { A obj1 = new A(); obj1.F(); } } class A { public void F() { G(); } static public void G() { int c = 0; int d = 2/c; } }

Al compilarlo no se detectar ningn error ya que al compilador no le merece la pena calcular el valor de c en tanto que es una variable, por lo que no detectar que dividir 2/c no es vlido. Sin embargo, al ejecutarlo se intentar dividir por cero en esa instruccin y ello provocar que aborte la aplicacin mostrando el siguiente mensaje:
Unhandled Exception: System.DivideByZeroException: Attempted to divide by zero. at PruebaExcepciones.Main()

POO-TV-C#_Clases

18

Palacios y Martn

Como se ve, en este mensaje se indica que no se ha tratado una excepcin de divisin por cero (tipo DivideByZeroException) dentro del cdigo del mtodo Main() del tipo PruebaExcepciones. Si al compilar el fuente hubisemos utilizado la opcin /debug, el compilador habra creado un fichero .pdb con informacin extra sobre las instrucciones del ejecutable generado que permitira que al ejecutarlo se mostrase un mensaje mucho ms detallado con informacin sobre la instruccin exacta que provoc la excepcin, la cadena de llamadas a mtodos que llevaron a su ejecucin y el nmero de lnea que cada una ocupa en el fuente:
Unhandled Exception: System.DivideByZeroException: Attempted to divide by zero. at A.G() in E:\c#\Ej\ej.cs:line 22 at A.F() in E:\c#\Ej\ej.cs:line 16 at PruebaExcepciones.Main() in E:\c#\Ej\ej.cs:line 8

Si se desea tratar la excepcin hay que encerrar la divisin dentro de una instruccin try con la siguiente sintaxis:
try <instrucciones> catch (<excepcin1>) <tratamiento1> catch (<excepcin2>) <tratamiento2> ... finally <instruccionesFinally>

El significado de try es el siguiente: si durante la ejecucin de las <instrucciones> se lanza una excepcin de tipo <excepcin1> (o alguna subclase suya) se ejecutan las instrucciones <tratamiento1>, si fuese de tipo <excepcin2> se ejecutara <tratamiento2>, y as hasta que se encuentre una clusula catch que pueda tratar la excepcin producida. Si no se encontrase ninguna y la instruccin try estuviese anidada dentro de otra, se mirara en los catch de su try padre y se repetira el proceso. Si al final se recorren todos los trys padres y no se encuentra ningn catch compatible, entonces se buscara en el cdigo desde el que se llam al mtodo que produjo la excepcin. Si as se termina llegando al mtodo que inici el hilo donde se produjo la excepcin y tampoco all se encuentra un tratamiento apropiado se aborta dicho hilo; y si ese hilo es el principal (el que contiene el punto de entrada) se aborta el programa y se muestra el mensaje de error con informacin sobre la excepcin lanzada ya visto. As, para tratar la excepcin del ejemplo anterior de modo que una divisin por cero provoque que a d se le asigne el valor 0, se podra reescribir G() de esta otra forma:
static public void G() { try { int c = 0; int d = 2/c; } catch (DivideByZeroException) { d=0; } }

19

POO-TV-C#_Clases

C#: Clases, Polimorfismo y Excepciones

Para simplificar tanto el compilador como el cdigo generado y favorecer la legibilidad del fuente, en los catchs se busca siempre orden de aparacin textual, por lo que para evitar catchs absurdos no se permite definir catchs que puedan capturar excepciones capturables por catchs posteriores a ellos en su misma instruccin try. Tambin hay que sealar que cuando en <instrucciones> se lance una excepcin que sea tratada por un catch de algn try -ya sea de la que contiene las <instrucciones>, de algn try padre suyo o de alguno de los mtodos que provocaron la llamada al que produjo la excepcinse seguir ejecutando a partir de las instrucciones siguientes a ese try. El bloque finally es opcional, y si se incluye ha de hacerlo tras todas los bloques catch. Las <instruccionesFinally> de este bloque se ejecutarn tanto si se producen excepciones en <instrucciones> como si no. En el segundo caso sus instrucciones se ejecutarn tras las <instrucciones>, mientras que en el primero lo harn despus de tratar la excepcin pero antes de seguirse ejecutando por la instruccin siguiente al try que la trat. Si en un try no se encuentra un catch compatible, antes de pasar a buscar en su try padre o en su mtodo llamante padre se ejecutarn las <instruccionesFinally>. Slo si dentro de un bloque finally se lanzase una excepcin se aborta la ejecucin del mismo. Dicha excepcin sera propagada al try padre o al mtodo llamante padre del try que contuviese el finally. Aunque los bloques catch y finally son opcionales, toda instruccin try ha de incluir al menos un bloque catch o un bloque finally. El siguiente ejemplo resume cmo funciona la propagacin de excepciones:
using System; class MiException:Exception {} class Excepciones { public static void Main() { try { Console.WriteLine(En el try de Main()); Mtodo(); Console.WriteLine(Al final del try de Main()); } catch (MiException) { Console.WriteLine(En el catch de Main()); } finally { Console.WriteLine(finally de Main()); } } public static void Mtodo() { try { Console.WriteLine(En el try de Mtodo()); Mtodo2(); Console.WriteLine(Al final del try de Mtodo()); }

POO-TV-C#_Clases

20

Palacios y Martn

catch (OverflowException) { Console.WriteLine(En el catch de Mtodo()); } finally { Console.WriteLine(finally de Mtodo()); } } public static void Mtodo2() { try { Console.WriteLine(En el try de Mtodo2()); throw new MiException(); Console.WriteLine(Al final del try de Mtodo2()); } catch (DivideByZeroException) { Console.WriteLine(En el catch de Mtodo2()); } finally { Console.WriteLine(finally de Mtodo2()); } } }

Ntese que en este cdigo lo nico que se hace es definir un tipo nuevo de excepcin llamado MiException y llamarse en el Main() a un mtodo llamado Mtodo() que llama a otro de nombre Mtodo2() que lanza una excepcin de ese tipo. Viendo la salida de este cdigo es fcil ver el recorrido seguido durante la propagacin de la excepcin:
En try de Main() En try de Mtodo() En try de Mtodo2() finally de Mtodo2 finally de Mtodo En catch de Main() finally de Main()

Como se puede observar, hay muchos WriteLine() que nunca se ejecutan ya que en cuento se lanza una excepcin se sigue ejecutando tras la instruccin siguiente al try que la trat (aunque ejecutando antes los finally pendientes, como se deduce de la salida del ejemplo) De hecho, el compilador se dar cuenta que la instruccin siguiente al throw nunca se ejecutar e informar de ello con un mensaje de aviso. La idea de todo este mecanismo de excepciones es evitar mezclar el cdigo normal con el cdigo de tratamiento de errores. As, en <instrucciones> se escribira el cdigo como si no se pudiesen producir errores, en las clusulas catch se trataran los posibles errores, y en la clusula finally se incluira cdigo a ejecutar tanto si produjesen errores como si no (suele usarse para liberar recursos ocupados, como fichero o conexiones de red abiertas) En realidad, tambin es posible escribir cada clusula catch definiendo una variable que se podr usar dentro del cdigo de tratamiento de la misma para hacer referencia a la excepcin capturada. Esto se hace con la sintaxis:
catch (<tipoExcepcin> <nombreVariable>) { <tratamiento>

21

POO-TV-C#_Clases

C#: Clases, Polimorfismo y Excepciones

Ntese que en tanto que todas las excepciones derivan de System.Exception, para definir una clusula catch que pueda capturar cualquier tipo de excepcin basta usar:
catch(System.Exception <nombreObjecto>) { <tratamiento> }

En realidad la sintaxis anterior slo permite capturar las excepciones propias de la plataforma .NET, que derivan de System.Exception. Sin embargo, hay lenguajes como C++ que permiten lanzar excepciones no derivadas de dicha clase, y para esos casos se ha incluido en C# una variante de catch s que realmente puede capturar excepciones de cualquier tipo, tanto si derivan de System.Exception como si no. Su sintaxis es:
catch { <tratamiento> }

Como puede deducirse de su sintaxis, el problema que presenta esta ltima variante de
catch es que no proporciona informacin sobre cul es la excepcin capturada, por lo que a

veces puede resultar poco til y si slo se desea capturar cualquier excepcin derivada de System.Exception es mejor usar la sintaxis explicada previamente a ella. En cualquier caso, ambos tipos de clusulas catch slo pueden ser escritas como la ltima clusula catch del try, ya que si no las clusulas catch que le siguiesen nunca llegasen a ejecutarse debido a que las primeras capturaran antes cualquier excepcin derivada de System.Exception. Respecto al uso de throw, hay que sealar que hay una forma extra de usarlo que slo es vlida dentro de cdigos de tratamiento de excepciones (cdigos <tratamientoi> de las clusulas catch) Esta forma de uso consiste en seguir simplemente esta sintaxis:
throw;

En este caso lo que se hace es relanzar la misma excepcin que se captur en el bloque
catch dentro de cuyo de cdigo de tratamiento se usa el throw; Hay que precisar que la excepcin relanzada es precisamente la capturada, y aunque en el bloque catch se la modifique

a travs de la variable que la repreesnta, la versin relanzada ser la versin original de la misma y no la modificada. Adems, cuando se relance una excepcin en un try con clusula finally, antes de pasar a reprocesar la excepcin en el try padre del que la relanz se ejecutar dicha clusula.

5. Redefinicin de operadores
Un operador en C# no es ms que un smbolo formado por uno o ms caracteres que permite realizar una determinada operacin entre uno o ms datos y produce un resultado. C# cuenta con un buen nmero de operadores que permiten realizar con una sintaxis clara e intuitiva las operaciones comunes a la mayora de lenguajes (aritmtica, lgica, etc) as como otras operaciones ms particulares de C# (operador is, operador stackalloc, etc.)

POO-TV-C#_Clases

22

Palacios y Martn

En C# viene predefinido el comportamiento de sus operadores cuando se aplican a ciertos tipos de datos. Por ejemplo, si se aplica el operador + entre dos objetos int devuelve su suma, y si se aplica entre dos objetos string devuelve su concatenacin. Sin embargo, tambin se permite que el programador pueda definir el significado la mayora de estos operadores cuando se apliquen a objetos de tipos que l haya definido, y esto es a lo que se le conoce como redefinicin de operador. Ntese que en realidad la posibilidad de redefinir un operador no aporta ninguna nueva funcionalidad al lenguaje y slo se ha incluido en C# para facilitar la legibilidad del cdigo. Por ejemplo, si tenemos una clase Complejo que representa nmeros complejos podramos definir una funcin Sumar() para sus objetos de modo que a travs de ella se pudiese conseguir la suma de dos objetos de esta clase como muestra este ejemplo:
Complejo c1 = new Complejo(3,2); Complejo c2 = new Complejo(5,2); Complejo c3 = c1.Sumar(c2); // c1 = 3 + 2i // c2 = 5 + 2i // c3 = 8 + 4i

Sin embargo, el cdigo sera mucho ms legible e intuitivo si en vez de tenerse que usar el mtodo Sumar() se redefiniese el significado del operador + para que al aplicarlo entre objetos Complejo devolviese su suma. Con ello, el cdigo anterior quedara as:
Complejo c1 = new Complejo(3,2); Complejo c2 = new Complejo(5,2); Complejo c3 = c1 + c2; // c1 = 3 + 2i // c2 = 5 + 2i // c3 = 8 + 4i

sta es precisamente la utilidad de la redefinicin de operadores: hacer ms claro y legible el cdigo, no hacerlo ms corto. Por tanto, cuando se redefina un operador es importante que se le d un significado intuitivo ya que si no se ira contra de la filosofa de la redefinicin de operadores. Por ejemplo, aunque sera posible redefinir el operador * para que cuando se aplicase entre objetos de tipo Complejo devuelva su suma o imprimiese los valores de sus operandos en la ventana de consola, sera absurdo hacerlo ya que ms que clarificar el cdigo lo que hara sera dificultar su comprensin. De todas formas, suele ser buena idea que cada vez que se redefina un operador en un tipo de dato tambin se d una definicin de un mtodo que funcione de forma equivalente al operador. As desde lenguajes que no soporten la redefinicin de operadores tambin podr realizarse la operacin y el tipo ser ms reutilizable.

5.1.

Definicin de redefiniciones de operadores 5.1.1 Sintaxis general de redefinicin de operador

La forma en que se redefine un operador depende del tipo de operador del que se trate, ya que no es lo mismo definir un operador unario que uno binario. Sin embargo, como regla general podemos considerar que se definiendo un mtodo pblico y esttico cuyo nombre sea el smbolo del operador a redefinir y venga precedido de la palabra reservada operator. Es decir, se sigue una sintaxis de la forma:
public static <tipoDevuelto> operator <smbolo>(<operandos>) { <cuerpo> }

Los modificadores public y static pueden permutarse si se desea, lo que es importante es que siempre aparezcan en toda redefinicin de operador. Se puede redefinir tanto operadores

23

POO-TV-C#_Clases

C#: Clases, Polimorfismo y Excepciones

unarios como binarios, y en <operandos> se ha de incluir tantos parmetros como operandos pueda tomar el operador a redefinir, ya que cada uno representar a uno de sus operandos. Por ltimo, en <cuerpo> se ha de escribir las instrucciones a ejecutar cada vez que se aplique la operacin cuyo operador es <smbolo> a operandos de los tipos indicados en <operandos>
<tipoDevuelto> no puede ser void, pues por definicin toda operacin tiene un resultado,

por lo que todo operador ha de devolver algo. Adems, permitirlo complicara innecesariamente el compilador y ste tendra que admitir instrucciones poco intuitivas (como a+b; si el + estuviese redefinido con valor de retorno void para los tipos de a y b) Adems, los operadores no pueden redefinirse con total libertad ya que ello dificultara inncesariamente la legibilidad del cdigo, por lo que se han introducido las siguientes restricciones al redefinirlos: Al menos uno de los operandos ha de ser del mismo tipo de dato del que sea miembro la redefinicin del operador. Como puede deducirse, ello implica que aunque puedan sobrecargarse los operadores binarios nunca podr hacerse lo mismo con los binarios ya que su nico parmetro slo puede ser de un nico tipo (el tipo dentro del que se defina) No puede alterarse sus reglas de precedencia, asociatividad, ubicacin y nmero de operandos, pues si ya de por s es difcil para muchos recordarlas cuando son fijas, mucho ms lo sera si pudiesen modificarse segn los tipos de sus operandos. No puede definirse nuevos operadores ni combinaciones de los ya existentes con nuevos significados (por ejemplo ** para representar exponenciacin), pues ello complicara innecesariamente el compilador, el lenguaje y la legibilidad del cdigo cuando en realidad es algo que puede simularse definiendo mtodos.

6. Conclusiones
A la vista de la informacin que hemos conseguido encontrar nuestra opinin es que estamos ante un lenguaje que podramos llamar C++ mejorado ya que mucha de la sintaxis de C\C++ permanece inalterada en C#. Esto pone a su favor el que la mayora de los programadores de C++ puedan adaptarse sin mucha complicacin a este nuevo lenguaje. Adems, a simple vista y sin haber estudiado el lenguaje anteriormente, un cdigo en C# es fcilmente comprensible ya que muchas de las novedades introducidas tienen como principal objetivo hacer ms legible el cdigo. Por otra parte no hay que olvidar que estamos ante un lenguaje orientado a objetos puro, como JAVA, que incorpora muchas de las facilidades de este, como puede ser el recolector de basura, y elimina algunas de sus limitaciones. De esta manera, y a diferencia de C++, un programa en C# se basa en una coleccin de objetos interactuando entre ellos, donde aparecer un mtodo main, que estar incluido entre los mtodos de un objeto con lo que la funcin main de C++ desaparece. Para acabar, tenemos que decir que no hemos tenido la posibilidad de programar en C# ya que la mayora de sus compiladores son comerciales as como los entornos de programacin en los que aparece. A simple vista no parece difcil adaptarse a este lenguaje habiendo estudiado C\C++ as que posiblemente el futuro de este lenguaje pase por aprovechar este filn siempre y cuando la poltica de Microsoft no se cierre en cuanto a portarlo a otras plataformas.

POO-TV-C#_Clases

24

Palacios y Martn

7. Bibliografa
Gonzlez Saeco, Jos Antonio El Lenguaje de programacin C# Revista PCPlus Articulo Aprenda a programar en C# Thomas, Jorge Articulo Ha nacido el lenguaje C# Revista Programacin Actual N 44 Charte, Francisco Articulo Microsoft Visual Studio .NET Revista Programacin Actual N 52 Rincn en Espaol de C# (http://tdg.lsi.us.es/csharp) C# Corner (http://www.c-sharpcorner.com) C# Help(http://www.csharphelp.com) C# Station (http://www.csharp-station.com)

25

POO-TV-C#_Clases

Das könnte Ihnen auch gefallen