Está en la página 1de 47

Programaci on Orientada a Objetos C#

F elix G omez M armol


3o Ingenier a Inform atica Julio de 2004

Indice general
Indice General 2. Clases y Objetos 2.1. Clases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.1.1. Estructura . . . . . . . . . . . . . . . . . . . . . . . 2.1.2. Ocultaci on de la informaci on . . . . . . . . . . . . . 2.1.3. Relaciones entre clases: Cliente-Servidor y Herencia 2.1.4. Visibilidad . . . . . . . . . . . . . . . . . . . . . . . 2.2. Objetos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.3. Mensajes . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.3.1. Sintaxis. Notaci on punto . . . . . . . . . . . . . . . . 2.3.2. Sem antica . . . . . . . . . . . . . . . . . . . . . . . . 2.4. Sem antica referencia versus sem antica almacenamiento . . . 2.5. Creaci on de objetos . . . . . . . . . . . . . . . . . . . . . . 2.5.1. Destructores . . . . . . . . . . . . . . . . . . . . . . 2.6. Sem antica de la asignaci on e igualdad entre objetos . . . . . 2.7. Genericidad . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.8. Denici on de una clase Lista Doblemente Enlazada . . . 3. Dise no por Contrato: Asertos y Excepciones 3.1. Contrato software . . . . . . . . . . . . . . . . 3.2. Clases y Correcci on del software: Asertos . . 3.3. Excepciones en C# . . . . . . . . . . . . . . . 3.3.1. Lanzamiento de excepciones . . . . . . 3.3.2. Bloques try . . . . . . . . . . . . . . . 3.3.3. Manejadores . . . . . . . . . . . . . . 3.4. Conversi on de asertos en excepciones . . . . . 4. Herencia 4.1. Introducci on . . . . . . . . . . . . . . . . . 4.2. Doble aspecto de la herencia . . . . . . . . 4.3. Polimorsmo . . . . . . . . . . . . . . . . 4.3.1. Sobrecarga . . . . . . . . . . . . . 4.3.2. Regla de aplicaci on de propiedades 4.3.3. Estructuras de datos polim orcas . 4.3.4. Operadores is y as . . . . . . . . 4.4. Ligadura Din amica . . . . . . . . . . . . . 4.5. Clases Diferidas . . . . . . . . . . . . . . . 4.5.1. Interfaces . . . . . . . . . . . . . . 4.6. Herencia, reutilizaci on y extensibilidad del 4.7. Herencia m ultiple . . . . . . . . . . . . . . 3 4 5 5 5 7 8 9 10 10 10 10 11 11 12 12 14 14 17 17 17 18 20 21 22 22 25 25 26 26 27 29 29 29 30 31 32 34 37

. . . . . . . . . . . . . . .

. . . . . . . . . . . . . . .

. . . . . . . . . . . . . . .

. . . . . . . . . . . . . . .

. . . . . . . . . . . . . . .

. . . . . . . . . . . . . . .

. . . . . . . . . . . . . . .

. . . . . . . . . . . . . . .

. . . . . . . . . . . . . . .

. . . . . . . . . . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . software . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

INDICE GENERAL

4.7.1. Problemas: Colisi on de nombres y herencia repetida . . . . . . . . . . 4.7.2. Ejemplos de utilidad . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.8. C#, Java y C++: Estudio comparativo . . . . . . . . . . . . . . . . . . . . . Indice de Figuras Indice de Tablas Indice de C odigos Bibliograf a

37 38 39 41 43 45 47

Tema 2

Clases y Objetos
2.1. Clases

Denici on 2.1 Una clase es una implementaci on total o parcial de un tipo abstracto de dato (TAD). Sus caracter sticas m as destacables son que se trata de entidades sint acticas y que describen objetos que van a tener la misma estructura y el mismo comportamiento.

2.1.1.

Estructura

Los componentes principales de una clase, que a partir de ahora llamaremos miembros, son: Atributos, que determinan una estructura de almacenamiento para cada objeto de la clase, y M etodos, que no son m as que operaciones aplicables sobre los objetos. Ejemplo 2.1 La clase mostrada en el c odigo 2.1, llamada Luna, convierte a kil ometros la distancia de la Tierra a la Luna en millas.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

// Distancia hasta la Luna convertida a kil o metros using System ; public class Luna { public static void Main () { int luna = 238857; int lunaKilo ; Console . WriteLine (" De la Tierra a la Luna = " + luna + " millas ") ; lunaKilo = ( int ) ( luna * 1.609) ; Console . WriteLine (" Kil o metros = " + lunaKilo + " km .") ; } } C odigo 2.1: Clase Luna

Tema 2. Clases y Objetos

Tipo short ushort int uint long ulong

Bytes 2 2 4 4 8 8

Rango de Valores (-32768, 32767) (0, 65535) (-2147483648, 2147483647) (0, 4294967295) (-9223372036854775808, 9223372036854775807) (0, 18446744073709551615)

Tabla 2.1: Tipos enteros primitivos Tipo float double decimal Bytes 4 8 16 Rango de Valores (3,4 1038 ) 7 d gitos signicativos (1,7 1038 ) de 15 a 16 d gitos signicativos 28 +28 (10 , 7,9 10 ) de 28 a 29 d gitos signicativos Tabla 2.2: Tipos otantes primitivos Tipo byte ubyte bool char Bytes 1 1 1 2 Rango de Valores (-128,127) (0,255) {true, false} Tabla ASCII

Tabla 2.3: Otros tipos primitivos Tipos de datos primitivos Las tablas 2.1, 2.2 y 2.3 muestran los tipos primitivos soportados por C#. Para declarar un tipo consistente en un conjunto etiquetado de constantes enteras se emplea la palabra clave enum (por ejemplo, enum Edades {F elix = 21, Alex, Alberto = 15}). Palabras reservadas La tabla 2.4 muestra las palabras reservadas de C#. abstract as base bool break byte case catch char checked class const continue decimal default delegate do double else enum event explicit extern false nally xed oat for foreach get goto if implicit in int interface internal is lock long namespace new null object operator out override params private protected public readonly ref return sbyte sealed set short sizeof stackalloc static string struct switch this throw true try typeof unit ulong unchecked unsafe ushort using value virtual void volatile while

Tabla 2.4: Palabras reservadas

2.1 Clases

2.1.2.

Ocultaci on de la informaci on

En ocasiones conviene ocultar ciertas caracter sticas (atributos y/o m etodos) de una clase al exterior. Por ejemplo, en una relaci on entre clases de Cliente-Servidor, el servidor deber a ocultar los aspectos de implementaci on al cliente. Para llevar a cabo esto, C# proporciona tres tipos de acceso: p ublico, privado y protegido. Un miembro con acceso privado (opci on por defecto) s olo es accesible desde otros miembros de esa misma clase, mientras que uno con acceso p ublico es accesible desde cualquier clase. Por u ltimo, un miembro protegido s olo es accesible por miembros de la misma clase o bien por miembros de alguna de las subclases. Ejemplo 2.2 En el c odigo 2.2 se muestra un ejemplo de ocultaci on de informaci on, mediante la clase Punto.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

public class Punto { private double x , y ; // O simplemente double x , y ; public void SetPunto ( double u , double v ) { x = u; y = v; } } . . . class Test { public void Prueba ( Punto w ) { . . . w . SetPunto (4.3 ,6.9) ; // Correcto w . x = 5.5; // Error sint a ctico . . . } } C odigo 2.2: Ejemplo de Ocultaci on de Informaci on A diferencia de C++, en C# es posible declarar un miembro como de s olo lectura mediante la palabra clave readonly. La diferencia entre readonly y const es que con la primera opci on la inicializaci on tiene lugar en tiempo de ejecuci on, mientras que con la segunda opci on la inicializaci on se da en tiempo de compilaci on. Una forma de simular el efecto de const mediante readonly es usando adem as la palabra clave static. Ejemplo 2.3 En el siguiente c odigo, n y m podr amos decir que son estructuralmente equivalentes: public const n; public static readonly m; Otra pr actica interesante consiste en declarar un atributo como privado y, mediante otro atributo p ublico tener acceso de lectura y/o escritura sobre el atributo privado. Para ello nos valemos de los modicadores de acceso get y set. Ejemplo 2.4 En el c odigo 2.3 se muestra un ejemplo de ocultaci on de informaci on mediante el uso de get y set. Obs ervese que el identicador value es siempre un objeto del mismo tipo que el atributo que lo contiene.

Tema 2. Clases y Objetos

1 2 3 4 5 6 7 8 9 10 11 12 13

public class CuentaPalabras { private string m_file_output ; // Atributo privado public string OutFile // Atributo p u blico asociado { get { return m_file_output ; } // Acceso de lectura set { // Acceso de escritura if ( value . Length != 0 ) m_file_output = value ; } } } C odigo 2.3: Otro ejemplo de Ocultaci on de Informaci on

2.1.3.

Relaciones entre clases: Cliente-Servidor y Herencia

Un cliente de una clase debe ver a esta como un TAD, es decir, conocer su especicaci on, pero sin importarle su implementaci on (siempre que esta sea eciente y potente). El creador de la clase, en cambio, debe preocuparse de que dicha clase sea lo m as reutilizable, adaptable, y eciente, as como que pueda ser usada por el mayor n umero de clientes posible. Denici on 2.2 Una clase A se dice que es cliente de una clase B, si A contiene una declaraci on en la que se establezca que cierta entidad (atributo, par ametro, variable local) e es de tipo B. Denici on 2.3 Una clase A se dice que hereda de otra clase B, si A es una versi on especializada de B (herencia de especializaci on), o bien si A es una implementaci on de B (herencia de implementaci on). La gura 2.1.a muestra la relaci on de clientela entre A y B, mientras que en la 2.1.b se observa la relaci on de herencia entre dichas clases.

AB
(a)

B A
(b)

Figura 2.1: Clientela y Herencia entre A y B

Ejemplo 2.5 El siguiente c odigo muestra la relaci on de clientela vista en la gura 2.1.a: class A { . . . B conta; . . . } Por su parte, el siguiente c odigo muestra la relaci on de herencia mostrada en la gura 2.1.b: class A: B { . . . }

2.1 Clases

Por u ltimo cabe decir que la herencia es una decisi on de dise no m as comprometedora que la clientela, pues en esta u ltima se puede cambiar la implementaci on de la clase que se emplea en el cliente, sin afectar a este (en nuestro ejemplo, se puede modicar la implementaci on de B sin que esto afecte a A).

2.1.4.

Visibilidad

Adem as de lo comentado en el apartado 2.1.2 acerca de la ocultaci on de informaci on, en esta secci on trataremos la cuesti on de las clases anidadas. Denici on 2.4 Una clase anidada no es m as que una clase que se declara dentro de otra y que tiene visibilidad sobre todas las propiedades de los objetos de la clase que la incluye. Ejemplo 2.6 El c odigo 2.4 muestra un ejemplo de clases anidadas. El resultado mostrado por pantalla ser a: IntAnidado(6) = 17
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28

public class Uno { public int c ; } public class Dos { public int c ; public class Tres { public int IntAnidado ( int e ) { Dos d = new Dos () ; Uno u = new Uno () ; u.c = 5 + e; d.c = c = e; return u . c + c ; } private int c ; } public Tres y ; } public class PruebaAnidada { public static void Main () { Dos x = new Dos () ; x . y = new Tres () ; Console . WriteLine (" IntAnidado (6) = " + IntAnidado (6) ) ; } } C odigo 2.4: Ejemplo de Clases Anidadas

10

Tema 2. Clases y Objetos

2.2.

Objetos

Denici on 2.5 Un objeto es una instancia de una clase, creada en tiempo de ejecuci on y formada por tantos campos como atributos tenga la clase. Dichos campos son simples si corresponden a atributos de tipos primitivos (v ease tablas 2.1, 2.2 y 2.3), mientras que se dice que son compuestos si sus valores son subobjetos o referencias. Mientras exista, cada objeto se identica un vocamente mediante su identicador de objeto (oid). Una posible clasicaci on de los objetos podr a ser: Objetos externos: Son aquellos que existen en el dominio de la aplicaci on. Por ejemplo, Producto, Socio, Comercial, Descuento, ... Objetos software: Procedentes del an alisis: objetos del dominio. Procedentes del dise no/implementaci on: TDAs, patrones de dise no y GUI. El estado de un objeto viene dado por la lista de pares atributo/valor de cada campo. Su modicaci on y consulta se realiza mediante mensajes. En cada instante de la ejecuci on de una aplicaci on Orientada a Objetos (en adelante, OO) existe un objeto destacado sobre el que se realiza alg un tipo de operaci on. Este objeto recibe el nombre de instancia actual. Para hacer referencia a la instancia actual en C# se emplea la palabra reservada this.

2.3.

Mensajes

Los mensajes son el mecanismo b asico de la computaci on OO y consisten en la invocaci on de la aplicaci on de un m etodo sobre un objeto. Constan de tres partes: objeto receptor, identicador del m etodo y los argumentos de este u ltimo.

2.3.1.

Sintaxis. Notaci on punto

Como se ha podido observar en algunos de los ejemplos vistos hasta ahora, la forma en que se invoca a un m etodo es mediante el operador punto, siguiendo una sintaxis como la que a continuaci on se muestra: receptor.m etodo(argumentos)

2.3.2.

Sem antica

La diferencia entre un mensaje y la invocaci on a un procedimiento es que en este u ltimo todos los argumentos reciben el mismo trato, mientras que en los mensajes uno de esos argumentos, a saber, el objeto receptor, recibe un trato especial. Ejemplo 2.7 En el mensaje w.SetPunto(4.3,6.9) lo que se est a queriendo decir es que se aplique el m etodo SetPunto sobre el objeto receptor w, efectuando el correspondiente paso de par ametros. Cuando un mensaje no especica al objeto receptor la operaci on se aplica sobre la instancia actual. Si no se incluye nada, el paso de par ametros es por valor. Para especicar un paso de par ametros por referencia se emplea la palabra clave ref delante del par ametro en cuesti on.

2.4 Sem antica referencia versus sem antica almacenamiento

11

2.4.

Sem antica referencia versus sem antica almacenamiento

En C# se tiene sem antica referencia (gura 2.41 ) para cualquier entidad asociada a una clase, aunque tambi en existen los tipos primitivos (gura 2.22 ), como ya hemos visto. Toda referencia puede estar, o bien no ligada (con valor null), o bien ligada a un objeto (mediante el operador new, o si se le asigna una referencia ya ligada). Pero ojo, la asignaci on no implica copia de valores sino de referencias ( Aliasing).

Figura 2.2: Variables en los lenguajes tradicionales

Figura 2.3: Variables tipo puntero en los lenguajes tradicionales

Figura 2.4: Variables en los lenguajes OO Algunas ventajas de los tipos referencia son: Son m as ecientes para manejar objetos. Constituyen un soporte para denir estructuras de datos recursivas. Dan soporte al polimorsmo. Los objetos son creados cuando son necesarios. Se permite la compartici on de un objeto.

2.5.

Creaci on de objetos

En C# existe un mecanismo expl cito de creaci on de objetos mediante la instrucci on de creaci on new y los llamados m etodos constructores (que deben tener el mismo nombre que la clase en la que se denen). Estos constructores se encargan de inicializar los atributos con valores consistentes. Sin embargo tambi en se pueden inicializar con unos valores por defecto, mediante los constructores por defecto (aquellos que no tienen argumentos).
1 2

TETiempo de Ejecuci on TCTiempo de Compilaci on

12

Tema 2. Clases y Objetos

Ejemplo 2.8 El c odigo 2.5 muestra un ejemplo de una clase con un constructor y un constructor por defecto.
1 2 3 4 5 6 7 8 9 10 11 12

public class Contador { private int conta ; public public public { get set } public } C odigo 2.5: Ejemplo de Constructores Contador () { conta = 0; } // Constructor por defecto Contador ( int i ) { conta = i % 100; } // Constructor int Conta { return conta ; } { conta = value % 100; } void Click () { conta = ( conta + 1) % 100; }

2.5.1.

Destructores

Un destructor es un m etodo cuyo nombre es el nombre de la clase precedido de una tilde . Mientras que los constructores s pueden tener modicadores de acceso, los destructores carecen de ellos. Cuando un objeto es recogido por el recolector de basura3 se llama impl citamente a su destructor. No obstante, en la mayor a de los casos las clases no necesitan destructores, pues con el recolector de basura es suciente para la nalizaci on.

2.6.

Sem antica de la asignaci on e igualdad entre objetos

Denici on 2.6 Una copia se dice que es supercial si se copian los valores de cada campo del objeto origen en el objeto destino y ambos comparten referencias (gura 2.5).

Figura 2.5: Ejemplo de copia supercial Denici on 2.7 Una copia se dice que es profunda si se crea un objeto con una estructura id entica al objeto origen y sin compartici on de referencias (gura 2.6).

Figura 2.6: Ejemplo de copia profunda


3

garbage collector

2.6 Sem antica de la asignaci on e igualdad entre objetos

13

Como ya se dijo anteriormente, la asignaci on implica compartici on de referencias si se tiene sem antica referencia, mientras que si se tiene sem antica almacenamiento entonces se copian los valores. A este tipo de copia se le llama copia supercial y est a soportada en C#. Si se tiene sem antica referencia y se realiza una copia supercial, los cambios hechos en una variable tienen repercusi on en su(s) correspondiente(s) copia(s). Para evitar este efecto debemos realizar una copia profunda, lo cual puede llevarse a cabo, por ejemplo, implementando el m etodo Clone() de la interfaz ICloneable. Ejemplo 2.9 El c odigo 2.6 muestra un ejemplo de una clase que implementa el m etodo Clone() de la interfaz ICloneable para conseguir una copia profunda.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

public class matrix : ICloneable { . . . public matrix ( int row , int col ) { m_row = ( row <= 0 ) ? 1 : row ; m_col = ( col <= 0 ) ? 1 : col ; m_mat = new double [ m_row , m_col ]; } public object Clone () { matrix mat = new matrix ( m_row , m_col ) ; for ( int i = 0; i < m_row ; ++ i ) for ( int j = 0; j < m_col ; ++ j ) mat . m_mat [i , j ] = m_mat [i , j ]; return mat ; } } C odigo 2.6: Ejemplo de implementaci on del m etodo Clone() Nota.- La l nea 6 (equivalentemente la l nea 7) del c odigo 2.6 es equivalente al siguiente c odigo: if (row <= 0) m_row = 1; else m_row = row; Denici on 2.8 Dos variables se dice que son id enticas si ambas referencian al mismo objeto (gura 2.7). En C# esto se expresa de la siguiente forma: oa == ob

Figura 2.7: Ejemplo de variables id enticas Denici on 2.9 Dos variables se dice que son iguales si ambas referencian a objetos con valores iguales en sus campos (guras 2.5 y 2.7). En C# esto se expresa de la siguiente forma: oa.Equals(ob)

14

Tema 2. Clases y Objetos

De esta u ltima denici on se deduce que siempre que haya identidad, tambi en habr a igualdad supercial (gura 2.7), pero no necesariamente a la inversa (gura 2.5).

2.7.

Genericidad

C++ hace uso de plantillas, que son altamente ecientes y muy potentes, para implementar clases contenedoras gen ericas. Dichas plantillas est an propuestas para Java y C#, pero a un est an en una fase experimental. C# consigue genericidad al estilo de Java, a saber, mediante el empleo de la clase ra z Object, con los consabidos inconvenientes: Necesidad de conversiones expl citas de tipo. Imposibilidad de asegurar homogeneidad.

2.8.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34

Denici on de una clase Lista Doblemente Enlazada

using System ; public class DListElement { public DListElement ( object val ) { data = val ; } public DListElement ( object val , DListElement d ) { data = val ; next = d ; d . previous = this ; } public DListElement Next { get { return next ; } set { next = value ; } } public DListElement Previous { get { return previous ; } set { previous = value ; } } public DListElement Data { get { return data ; } set { data = value ; } } private DListElement next , previous ; private object data ; } public class DList { public DList () { head = null ; } public DList ( object val ) { head = new DListElement ( val ) ; head . Next = head . Previous = null ; }

2.8 Denici on de una clase Lista Doblemente Enlazada

15

35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85

public bool IsEmpty () { return head == null ; } public void Add ( object val ) { if ( IsEmpty () ) { head = new DListElement ( val ) ; head . Next = head . Previous = null ; } else { DListElement h = new DListElement ( val , head ) ; head . Previous = h ; head = h ; } } public object Front () { if ( IsEmpty () ) throw new System . Exception (" Empty DList ") ; return head . Data ; } public DListElement Find ( object val ) { DListElement h = head ; while ( h != null ) { if ( val . Equals ( h . Data ) ) break ; h = h . Next ; } return h ; } private DListElement Delete ( DListElement h ) { if ( h == null ) return null ; DListElement np = h . Next , pp = h . Previous ; if ( np != null ) np . Previous = pp ; else head = null ; if ( pp != null ) pp . Next = np ; return h . Next ; }

16

Tema 2. Clases y Objetos

86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102

public DListElement Delete ( object v ) { return Delete ( Find ( v ) ) ; } public override string ToString () { return ToStr ingRec ursive ( head ) ; } static string To String Recursive ( DListElement first ) { if ( first == null ) return ""; else return ( first . Data + "\ n ") + ToStr ingRec ursive ( first . Next ) ; } private DListElement head ; } C odigo 2.7: Lista gen erica doblemente enlazada A continuaci on destacaremos algunos aspectos de este c odigo: Obs ervese el uso de get y set en las l neas 13 a 20 (en m etodos declarados como p ublicos) y las declaraciones privadas de las l neas 22 y 23. Es este un ejemplo de ocultaci on de informaci on. Otro ejemplo de ocultaci on de informaci on es la declaraci on como privados de los m etodos Delete y ToStringRecursive de las l neas 72 y 93, respectivamente. Podemos ver la implementaci on de un constructor por defecto y un constructor en las l neas 28 y 29, respectivamente, as como la sobrecarga de constructores en las l neas 4 y 6. Como se observa en la l nea 23 de este c odigo los datos almacenados en la lista son de tipo object. Como ya se dijo anteriormente, as es como se consigue genericidad en C#. En la l nea 65 vemos un ejemplo de uso del m etodo Equals. El empleo del operador == en esta l nea provocar a una ejecuci on incorrecta, distinta a la esperada. En la l nea 55 se ve un ejemplo del uso de excepciones, las cuales trataremos en el siguiente tema; y en la l nea 90 vemos c omo se sobrescribe el m etodo heredado ToString()4 .

Todo lo relacionado con la herencia se ver a en profundidad en el tema 4

Tema 3

Dise no por Contrato: Asertos y Excepciones


En este tema se describe el manejo de excepciones en C#. Las excepciones son habitualmente condiciones inesperadas de error que provocan la nalizaci on del programa en el que tienen lugar, con un mensaje de error. C#, sin embargo permite al programador intentar recuperarse frente a estas situaciones y as continuar con la ejecuci on del programa.

3.1.

Contrato software

Desde un punto de vista una excepci on se puede decir que se basa en el incumplimiento del contrato software entre el cliente y el servidor de una clase. Seg un este modelo, el usuario (cliente) debe garantizar que se cumplen las condiciones necesarias para una correcta aplicaci on del software. A estas condiciones se le conocen como precondiciones. El proveedor (servidor), por su parte, debe garantizar que, si el cliente cumple con las precondiciones, el software realizar a la tarea deseada satisfactoriamente. Si esto es as , se dice que se cumplen las postcondiciones.

3.2.

Clases y Correcci on del software: Asertos

La correcci on de los programas se puede ver en parte como una prueba de que la ejecuci on terminar a proporcionando una salida correcta, siempre que la entrada sea correcta. Establecer una prueba formal completa de correcci on del software es algo muy deseable, pero por desgracia, no se lleva a cabo en la mayor a de las ocasiones. De hecho, la disciplina consistente en plantear asertos apropiados frecuentemente consigue que el programador advierta y evite bastantes errores de programaci on que antes podr an pasar desapercibidos. En C# la clase Debug contiene el m etodo Assert(), invocado de la siguiente manera: Assert(expresi on booleana ,mensaje ); Si la expresi on expresi on booleana se eval ua como falsa, la ejecuci on proporciona una salida de diagn ostico, con el mensaje mensaje. Los asertos se habilitan deniendo la variable DEBUG. Para acceder a dicha salida de diagn ostico se emplean los listener. Ejemplo 3.1 En el c odigo 3.1 se muestra un ejemplo del uso de asertos, as como del empleo de listeners para visualizar los mensajes de diagn ostico por pantalla.

17

18

Tema 3. Dise no por Contrato: Asertos y Excepciones

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22

# define DEBUG using System ; using System . Diagnostics ; public class AssertSqrRoot { public static void Main () { Debug . Listeners . Clear () ; Debug . Listeners . Add ( new T e x t W r i t e rT r ac eL i st e ne r ( Console . Out ) ) ; double x ; string datos ; Console . WriteLine (" Introduzca un n u mero real positivo :") ; datos = Console . readLine () ; x = double . Parse ( datos ) ; Console . WriteLine ( x ) ; Debug . Assert ( x > 0 , " Valor no positivo ") ; Console . WriteLine (" La ra z cuadrada es " + Math . Sqrt ( x ) ) ; } } C odigo 3.1: Ejemplo del uso de asertos El uso de asertos reemplaza el uso ad hoc de comprobaciones condicionales con una metodolog a m as uniforme. El inconveniente de los asertos es que no permiten una estrategia de reparaci on o reintento para continuar con la ejecuci on normal del programa.

3.3.

Excepciones en C#

C# contiene la clase est andar System.Exception, que es el objeto o la clase base de un objeto lanzado por el sistema o por el usuario cuando ocurre un error en tiempo de ejecuci on. El mecanismo de manejo de excepciones en C# es sensible al contexto. Y dicho contexto no es m as que un bloque try, en el cual se declaran los manejadores (handlers ) al nal del mismo mediante la palabra clave catch. Un c odigo de C# puede alcanzar una excepci on en un bloque try mediante la expresi on throw. La excepci on es entonces tratada por alguno de los manejadores que se encuentran al nal del bloque try. Ejemplo 3.2 El c odigo 3.2 muestra un ejemplo del uso de excepciones, con un bloque try, la expresi on throw y los manejadores denidos mediante la palabra clave catch.
1 2 3 4 5 6 7 8

using System ; public class LanzaExcepcion { public static void Main () { try { double x ; string datos ;

3.3 Excepciones en C#

19

9 10 11 12 13 14 15 16 17 18 19 20 21

Console . WriteLine (" Introduzca un double :") ; datos = Console . readLine () ; x = double . Parse ( datos ) ; Console . WriteLine ( x ) ; if ( x > 0) throw ( new System . Exception () ) ; else Console . WriteLine (" La ra z cuadrada es " + Math . Sqrt ( x ) ) ; } catch ( Exception e ) { Console . WriteLine (" Lanzada excepci on " + e); } } } C odigo 3.2: Ejemplo del uso de excepciones La tabla 3.1 muestra algunas excepciones est andar en C#, mientras que en la tabla 3.2 podemos observar las propiedades est andar que toda excepci on debe tener. SystemException ApplicationException ArgumentException ArgumentNullException ArgumentOutOfRangeException ArithmeticException DivideByZeroException IndexOutOfRangeException InvalidCastException IOException NullReferenceException OutOfMemoryException Clase base para las excepciones del sistema Clase base para que los usuarios proporcionen errores de aplicaci on Uno o m as argumentos son inv alidos Pasado null no permitido Fuera de los valores permitidos Valor innito o no representable Auto-explicativa Indice fuera de los l mites del array Cast no permitido Clase base en el espacio de nombres System.IO para las excepciones de E/S Intento de referenciar a null Ejecuci on fuera de la memoria heap (mont on)

Tabla 3.1: Algunas Excepciones est andar HelpLink InnerException Message StackTrace Source TargetSize Obtiene o establece un enlace a un chero de ayuda Obtiene la instancia de Exception que caus o la excepci on Texto que describe el signicado de la excepci on Traza de la pila cuando se llam o a la excepci on Aplicaci on u objeto que gener o la excepci on M etodo que lanz o la excepci on

Tabla 3.2: Propiedades de las excepciones

20

Tema 3. Dise no por Contrato: Asertos y Excepciones

3.3.1.

Lanzamiento de excepciones

Mediante la expresi on throw se lanzan excepciones, las cuales deben ser objetos de la clase Exception. El bloque try m as interno en el que se lanza una excepci on es empleado para seleccionar la sentencia catch que procesar a dicha excepci on. Si lo que queremos es relanzar la excepci on actual podemos emplear un throw sin argumentos en el cuerpo de un catch. Esta situaci on puede ser u til si deseamos que un segundo manejador llamado desde el primero realice un procesamiento m as exhaustivo de la excepci on en cuesti on. Ejemplo 3.3 El c odigo 3.3 muestra un ejemplo del relanzamiento de excepciones.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29

using System ; public class ReLanzaExcepcion { public static void LanzaMsg () { try { . . . throw new Exception (" Lanzada en LanzaMsg ") ; . . . } catch ( Exception e ) { . . . Console . WriteLine (" Primera captura " + e ) ; throw ; // Relanzamiento } } public static void Main () { try { . . . LanzaMsg () ; . . . } catch ( Exception e ) { Console . WriteLine (" Excepci o n recapturada " + e ) ; } } } C odigo 3.3: Ejemplo del relanzamiento de excepciones Conceptualmente, el lanzamiento de una excepci on pasa cierta informaci on a los manejadores, pero con frecuencia estos no precisa de dicha informaci on. Por ejemplo, un manejador que simplemente imprime un mensaje y aborta la ejecuci on no necesita m as informaci on de su entorno. Sin embargo, el usuario probablemente querr a informaci on adicional por pantalla para seleccionar o ayudarle a decidir la acci on a realizar por el manejador. En este caso ser a apropiado empaquetar la informaci on en un objeto derivado de una clase Exception ya existente.

3.3 Excepciones en C#

21

Ejemplo 3.4 El c odigo 3.4 muestra un ejemplo del uso de objetos para empaquetar informaci on que se pasa a los manejadores de excepciones. El resultado mostrado por pantalla ser a algo como: Out of bounds with last char z
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31

using System ; public class Stack { public char [] s = new char [100]; } public class StackError : Exception { public StackError ( Stack s , string message ) { st = s ; msg = message ; } public char TopEntry () { return st . s [99]; } public string Msg { set { msg = value ; } get { return msg ; }} private Stack st ; private string msg ; } public class StackErrorTest { public static void Main () { Stack stk = new Stack ; stk . s [99] = z ; try { throw new StackError ( stk ," Out of bounds ") ; } catch ( StackError se ) { Console . WriteLine ( se . Msg + " with last char " + se . TopEntry () ) ; } } } C odigo 3.4: Uso de excepciones con m as informaci on

3.3.2.

Bloques try

Un bloque try es el contexto para decidir qu e manejador invocar para cada excepci on que se dispara. El orden en el que se denen los manejadores determina el orden en el que se intenta invocar cada manejador que puede tratar la excepci on que haya saltado. Una excepci on puede ser tratada por una manejador en concreto si se cumplen alguna de estas condiciones: 1. Si hay una coincidencia exacta. 2. Si la excepci on lanzada ha sido derivada de la clase base del manejador. Es un error listar los manejadores en un orden en el que se impida la ejecuci on de alguno de ellos, como muestra el siguiente ejemplo.

22

Tema 3. Dise no por Contrato: Asertos y Excepciones

Ejemplo 3.5 Seg un el siguiente c odigo: catch(ErrorClaseBase e) catch(ErrorClaseDerivada e) nunca se ejecutar a el cuerpo del segundo catch, pues antes se encontrar a una coincidencia con el primer catch, seg un lo dicho en las condiciones anteriores.

3.3.3.

Manejadores

La sentencia catch parece la declaraci on de un m etodo con un solo argumento y sin valor de retorno. Incluso existe un catch sin ning un argumento, el cual maneja las excepciones no tratadas por los manejadores que s tienen argumento. Cuando se invoca a un manejador a trav es de una expresi on throw, nos salimos del bloque try y autom aticamente se llama a los m etodos (incluidos los destructores) que liberan la memoria ocupada por todos los objetos locales al bloque try. La palabra clave finally despu es de un bloque try introduce otro bloque que se ejecuta despu es del bloque try independientemente de si se ha lanzado una excepci on o no.

3.4.

Conversi on de asertos en excepciones

La l ogica de las excepciones es m as din amica ya que los manejadores pueden recibir m as informaci on que los asertos, como ya hemos visto anteriormente. Los asertos simplemente imprimen un mensaje por pantalla, mientras que con las excepciones, adem as de imprimir m as informaci on, se puede decidir (en algunos casos) entre continuar la ejecuci on del programa o abortarla de inmediato. Ejemplo 3.6 El c odigo 3.5 es una reescritura del c odigo 3.1 que se muestra en el ejemplo 3.1 del uso de asertos, pero ahora mediante excepciones. En este ejemplo se ve c omo el programa hace uso del mecanismo de excepciones para llamarse a s mismo recursivamente hasta que el usuario introduzca un n umero v alido para la entrada.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

using System ; public class ExAssert { public static void MyAssert ( bool cond , string message , Exception e ) { if (! cond ) { Console . Error . WriteLine ( message ) ; throw e ; } } } class ExceptionSqrRoot { public static void ConsoleSqrt () { double x ; string datos ; Console . WriteLine (" Introduzca un double positivo :") ;

3.4 Conversi on de asertos en excepciones

23

20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40

datos = Console . readLine () ; x = double . Parse ( datos ) ; Console . WriteLine ( x ) ; try { ExAssert . MyAssert ( x > 0 , " Valor no positivo : x = " + x . ToString () , new Exception () ) ; Console . WriteLine (" La ra z cuadrada es " + Math . Sqrt ( x )); } catch ( Exception e ) { Console . WriteLine ( e ) ; ExceptionSqrRoot . ConsoleSqrt () ; // Se vuelve a intentar } } public static void Main () { Console . WriteLine (" Probando Ra ces Cuadradas ") ; ConsoleSqrt () ; } } C odigo 3.5: Ejemplo del uso de excepciones en vez de asertos

24

Tema 3. Dise no por Contrato: Asertos y Excepciones

Tema 4

Herencia
4.1. Introducci on

La herencia es un potente mecanismo para conseguir reutilizaci on en el c odigo, consistente en derivar una nueva clase a partir de una ya existente. A trav es de la herencia es posible crear una jerarqu a de clases relacionadas que compartan c odigo y/o interfaces. En muchas ocasiones distintas clases resultan ser variantes unas de otras y es bastante tedioso tener que escribir el mismo c odigo para cada una. Una clase derivada hereda la descripci on de la clase base, la cual se puede alterar a nadiendo nuevos miembros y/o modicando los m etodos existentes, as como los privilegios de acceso. Ejemplo 4.1 En la gura 4.1 se muestra un ejemplo de una jerarqu a de clases.

Figura 4.1: Jerarqu a de clases Denici on 4.1 Si B hereda de A entonces B incorpora la estructura (atributos) y comportamiento (m etodos) de A, pero puede incluir adaptaciones: B puede a nadir nuevos atributos. B puede a nadir nuevos m etodos. B puede redenir m etodos de A (bien para extender el m etodo original, bien para mejorar la implementaci on). B puede implementar un m etodo diferido en A. 25

26

Tema 4. Herencia

La tabla 4.1 muestra los m etodos p ublicos y protegidos de la clase object, ra z en toda jerarqu a de clases de C#. bool Equals(object o) void Finalize() int GetHashCode() Type GetType() object MemberwiseClone() bool ReferenceEquals (object a, object b) string ToString() Devuelve true si dos objetos son equivalentes, si no, devuelve false Equivalente a escribir un destructor Proporciona un entero u nico para cada objeto Permite averiguar din amicamente el tipo de objeto Permite clonar un objeto Devuelve true si los objetos son la misma instancia Devuelve un string que representa al objeto actual

Tabla 4.1: Propiedades de las excepciones

4.2.

Doble aspecto de la herencia

Como ya se dijo en la denici on 2.3, la herencia es, a la vez, un mecanismo de especializaci on y un mecanismo de implementaci on. Esto es, una clase B puede heredar de otra clase A, por ser B una especializaci on de A. Sin embargo, puede ocurrir que el motivo por el cual B hereda de A sea la necesidad de construir una implementaci on distinta (si se quiere, m as renada) de A. Ejemplo 4.2 La herencia entre clases que se observa en la gura 4.1, as como en las guras 4.2.a y 4.2.b sirve como mecanismo de especializaci on. Por otra parte, los ejemplos de las guras 4.2.c y 4.2.d muestran la herencia como mecanismo de implementaci on.

Figura 4.2: Herencia de especializaci on y de implementaci on

4.3.

Polimorsmo

Denici on 4.2 Podr amos denir el polimorsmo como la capacidad que tiene una entidad para referenciar en tiempo de ejecuci on a instancias de diferentes clases. C# soporta el polimorsmo, lo cual quiere decir que sus entidades (las cuales poseen un u nico tipo est atico y un conjunto de tipos din amicos) pueden estar conectadas a una instancia de la clase asociada en su declaraci on o de cualquiera de sus subclases. Podr amos categorizar el polimorsmo en dos tipos: Param etrico Real y Aparente Sobrecarga Inclusi on (basado en la herencia)

4.3 Polimorsmo

27

Ejemplo 4.3 Dada la siguiente declaraci on, siguiendo la jerarqu a de clases de la gura 4.1, Poligono p; Triangulo t; Rectangulo r; Cuadrado c;

podemos decir que: te(p) = Poligono ctd(p) = {Poligono, Triangulo, Rectangulo, Cuadrado} te(t) = Triangulo ctd(t) = {Triangulo} te(r) = Rectangulo ctd(r) = {Rectangulo, Cuadrado} te(c) = Cuadrado ctd(c) = {Cuadrado} donde te signica tipo est atico y ctd signica conjunto de tipos din amicos. Y por lo tanto las siguientes asignaciones ser an v alidas: p = t; p = r; r = c; t = r; r = p; p = c; c = p; c = t;

Pero no lo ser an, por ejemplo:

4.3.1.

Sobrecarga

Denici on 4.3 La sobrecarga consiste en dar distintos signicados a un mismo m etodo u operador. C# soporta la sobrecarga de m etodos y de operadores. Uno de los ejemplos m as claros es el del operador +. Ejemplo 4.4 La expresi on a + b puede signicar desde concatenaci on, si a y b son de tipo string, hasta una suma entera, en punto otante o de n umeros complejos, todo ello dependiendo del tipo de datos de a y b. Sin embargo, lo realmente interesante es poder redenir operadores cuyos operandos sean de tipos denidos por el usuario, y C# permite hacer esto. Pero conviene tener en cuenta que este tipo de actuaci on s olo es provechosa en aquellos casos en los que existe una notaci on ampliamente usada que conforme con nuestra sobrecarga (m as a un, si cabe, en el caso de los operadores relacionales <, >, <= y >=). La tabla 4.2 muestra los operadores unarios que en C# se pueden sobrecargar, mientras que la tabla 4.3 hace lo propio con los operadores binarios. + ! ++ true f alse Tabla 4.2: Operadores unarios sobrecargables + / % & | << >> == ! = < > >= <= Tabla 4.3: Operadores binarios sobrecargables Cuando se sobrecarga un operador tal como +, su operador de asignaci on asociado, +=, tambi en es sobrecargado impl citamente. N otese que el operador de asignaci on no puede sobrecargarse y que los operadores sobrecargados mantienen la precedencia y asociatividad de los operandos. Tanto los operadores binarios como los unarios s olo pueden ser sobrecargados mediante m etodos est aticos.

28

Tema 4. Herencia

Ejemplo 4.5 El c odigo 4.1 muestra un ejemplo de sobrecarga de operadores, tanto unarios, como binarios. Obs ervese como en la l nea 32 se vuelve a denir el operador *, previamente sobrecargado por el m etodo de la l nea 29, pero con los par ametros en orden inverso. Esta pr actica com un evita que el operador sobrecargado tenga un orden prejado en sus operandos. El resultado mostrado por pantalla tras ejecutarse el m etodo Main() ser a: Initial times day 00 time:00:00:59 day 01 time:23:59:59 One second later day 00 time:00:01:00 day 02 time 00:00:00
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36

using System ; class MyClock { public MyClock () { } public MyClock ( unit i ) { Reset ( i ) ; } public void Reset ( unit i ) { totSecs = i ; secs = i % 60; mins = ( i / 60) % 60; hours = ( i / 3600) % 24; days = i / 86400; } public override string ToString () { Reset ( totSecs ) ; return String . Format (" day {0: D2 } time :{1: D2 }:{2: D2 }:{3: D2 }" , days , hours , mins , secs ) ; } public void Tick () { Reset (++ totSecs ) ; } public static MyClock operator ++( MyClock c ) { c . Tick () ; return c ; } public static MyClock operator +( MyClock c1 , MyClock c2 ) { return new MyClock ( c1 . totSecs + c2 . totSecs ) ; } public static MyClock operator *( uint m , MyClock c ) { return new MyClock ( m * c . totSecs ) ; } public static MyClock operator *( MyClock c , uint m ) { return ( m * c ) ; } private uint totSecs = 0 , secs = 0 , mins = 0 , hours = 0 , days = 0; }

4.3 Polimorsmo

29

37 38 39 40 41 42 43 44 45

class MyClockTest { public static void Main () { MyClock a = new MyClock (59) , b = new MyClock (172799) ; Console . WriteLine (" Initial times \ n " + a + \n + b ) ; ++ a ; ++ b ; Console . WriteLine (" One second later \ n " + a + \n + b ) ; } } C odigo 4.1: Ejemplo de Sobrecarga de Operadores

4.3.2.

Regla de aplicaci on de propiedades

Denici on 4.4 Un tipo T1 es compatible con otro tipo T2, si la clase de T1 es la misma que la de T2 o una subclase de T2. Denici on 4.5 Regla de la Asignaci on 1 Una asignaci on x = y o una invocaci on r(..,y,..) a una rutina r(..,T x,..), ser a legal si el tipo de y es compatible con el tipo de x. Denici on 4.6 Regla de Validez de un Mensaje 2 Un mensaje ox.r(y), supuesta la declaraci on X x, ser a legal si: 1. X incluye una propiedad con nombre nal r. 2. Los argumentos son compatibles con los par ametros y coinciden en n umero. 3. Y r est a disponible para la clase que incluye el mensaje.

4.3.3.

Estructuras de datos polim orcas

La lista doblemente enlazada implementada en el c odigo 2.7 es un claro ejemplo de estructura de datos polimorfa. Si en la l nea 23 de dicho c odigo en vez de poner private object data escribi eramos private Figura data, siguiendo el ejemplo de jerarqu a de clases de la gura 4.1, en un instante dado podr amos tener una lista como la que se observa en la gura 4.3.

Figura 4.3: Ejemplo de estructura de datos polimorfa

4.3.4.

Operadores is y as

El operador is, cuya sintaxis es expresi on is tipo, devuelve true si se puede hacer un cast de la expresi on expresi on al tipo tipo. El operador as, con id entica sintaxis devuelve la expresi on expresi on convertida al tipo tipo, o bien, si la conversi on no est a permitida, devuelve null.
1 2

En adelante, RA En adelante, RVM

30

Tema 4. Herencia

4.4.

Ligadura Din amica

Como ya dijimos en la denici on 4.1, si una clase B hereda de otra clase A, B puede redenir m etodos de A e implementar m etodos que en A sean diferidos. Entonces cuando tengamos un objeto de la clase A y una invocaci on a un m etodo redenido en la clase B, la versi on del m etodo que se ejecute depender a del tipo din amico del objeto de la clase A. En esto consiste la ligadura din amica. Ejemplo 4.6 En la gura 4.4 se muestra una jerarqu a de clases en las que algunas de ellas redenen el m etodo f de sus ancestras. Las letras griegas representan la versi on de cada m etodo.

Figura 4.4: Herencia y redenici on de m etodos Dadas las siguientes declaraciones: A oa; B ob = new B(); C oc = new C(); D od = new D();

A continuaci on se muestra qu e versi on del m etodo f se ejecutar a, dependiendo del tipo din amico del objeto oa: oa = ob; oa.f oa = oc; oa.f oa = od; oa.f En C#, como en C++, la ligadura es est atica por defecto, siendo necesario incluir la palabra clave virtual en aquellos m etodos que vayan a ser redenidos y a los cuales s se aplicar a ligadura din amica. Aquellas subclases que quieran redenir un m etodo declarado como virtual en la clase base deber an emplear para ello la palabra clave override, como ya hemos visto en varios ejemplos con el m etodo ToString(). N otese la diferencia entre un m etodo redenido y un m etodo sobrecargado. Los m etodos sobrecargados son seleccionados en tiempo de compilaci on bas andonos en sus prototipos (argumentos y valor de retorno) y pueden tener distintos valores de retorno. Un m etodo redenido, por contra, es seleccionado en tiempo de ejecuci on bas andonos en el tipo din amico del objeto receptor y no puede tener distinto valor de retorno del que tenga el m etodo al que redene. Ejemplo 4.7 El c odigo 4.2 muestra un ejemplo de redenici on de un m etodo heredado de la clase base. Obs ervese el uso de la palabra clave virtual en la l nea 4, as como de la palabra clave override en la l nea 9. Obs ervese tambi en como el valor devuelto por el m etodo redenido de la l nea 9 es el mismo que el del m etodo original de la l nea 4, a saber, void. El resultado mostrado por pantalla tras ejecutarse el m etodo Main() ser a: Dentro de la clase base Dentro de la clase derivada Dentro de la clase derivada

4.5 Clases Diferidas

31

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

using System ; class ClaseBase { public virtual void Status () { Console . WriteLine (" Dentro de la clase base ") ; } } class ClaseDerivada : ClaseBase { public override void Status () { Console . WriteLine (" Dentro de la clase derivada ") ; } } class L i g a d u r a D i n a m i c a T e s t { public static void Main () { ClaseBase b = new ClaseBase () ; ClaseDerivada d = new ClaseDerivada () ; b . Status () ; d . Status () ; b = d; b . Status () ; } } C odigo 4.2: Redinici on de un m etodo heredado

4.5.

Clases Diferidas

Denici on 4.7 Una clase diferida o abstracta es aquella que contiene m etodos abstractos, esto es, m etodos que deben ser implementados en las subclases. No se pueden crear instancias de una clase abstracta. Para declarar en C# un m etodo como abstracto se emplea la palabra clave abstract, y para implementarlo en alguna subclase utilizamos la palabra clave override. Si una clase hereda de otra clase abstracta y no implementa todos sus m etodos abstractos, entonces sigue siendo abstracta. Ejemplo 4.8 El c odigo 4.3 muestra un ejemplo de una clase abstracta Figura, cuyo m etodo abstracto Area() es implementado en las subclases Rectangulo y Circulo, pero no en la subclase Triangulo. Es por ello que la subclase Triangulo sigue siendo abstracta. Para ser m as preciso, es parcialmente abstracta (ver denici on 4.8). Sin embargo el m etodo abstracto Perimetro() es implementado en las tres subclases.
1 2 3 4 5 6

using System ; abstract class Figura { abstract public double Area () ; abstract public double Perimetro () ; }

32

Tema 4. Herencia

7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33

class Rectangulo : Figura { public Rectangulo ( double a , double b ) { altura = a ; base = b ; } public override double Area () { return ( base * altura ) ; } public override double Perimetro () { return (( base * 2) + ( altura * 2) ) ; } private double altura , base ; } class Circulo : Figura { public Circulo ( double r ) { radio = r ; } public override double Area () { return ( Math . PI * radio * radio ) ; } public override double Perimetro () { return ( Math . PI * radio * 2) ; } private double radio ; } abstract class Triangulo : Figura { public Triangulo ( double l1 , double l2 , double l3 ) { lado1 = l1 ; lado2 = l2 ; lado3 = l3 ; } public override double Perimetro () { return ( lado1 + lado2 + lado3 ) ; } private double lado1 , lado2 , lado3 ; } C odigo 4.3: Clase abstracta Figura Denici on 4.8 Una clase parcialmente abstracta es aquella que contiene m etodos abstractos y efectivos. Para referenciar al constructor de la clase base a nadimos al nal del constructor de la clase derivada lo siguiente: : base(argumentos ). El constructor de la clase base es invocado antes de ejecutar el constructor de la clase derivada.

4.5.1.

Interfaces

Denici on 4.9 Una clase que u nicamente contiene m etodos abstractos recibe el nombre de interface. Pero no confundamos, no es lo mismo que una clase abstracta, pues una clase abstracta puede estar parcialmente implementada. Adem as una clase s olo puede heredar de una u nica clase abstracta, mientras que en C# est a permitida la herencia m ultiple de interfaces3 . Todos los m etodos de una interface, adem as de ser abstractos como ya hemos dicho, deben ser p ublicos. Por lo tanto las palabras clave abstract y public se pueden omitir. Para declarar una interface se emplea la palabra clave interface; y para implementar una interface se emplea la notaci on vista hasta ahora para la herencia. Una interface puede heredar de otra interface; sin embargo, no puede contener miembros de datos, s olo m etodos.
3

Lo veremos en profundidad m as adelante

4.5 Clases Diferidas

33

Ejemplo 4.9 En el c odigo 4.4 se muestra un ejemplo de una interface IPersona, de la cual heredan otras dos interfaces IEstudiante e IProfesor. Las tres son implementadas por la clase Ayudante. Obs ervese el convenio de nombrar a las interfaces con una I may uscula delante del nombre, a diferencia de las clases, que no la llevan. La gura 4.5 muestra el esquema de herencia e implementaci on de las interfaces y la clase en cuesti on.

Figura 4.5: Herencia e implementaci on de interfaces

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33

interface IPersona { string Nombre { get ; set ; } string Telefono { get ; set ; } int Edad { get ; set ; } bool esMayorDeEdad () ; } interface IEstudiante : IPersona { double NotaMedia { get ; set ; } int A~ n oMatricula { get ; set ; } } interface IProfesor : IPersona { double Salario { get ; set ; } string Nss { get ; set ; } // N o seguridad social } class Ayudante : IPersona , IEstudiante , IProfesor { // M e todos y propiedades requeridos por IPersona public string Name { get { return name ; } set { name = value ; } } public string Telefono { get { return telefono ; } set { telefono = value ; } } public int Edad { get { return edad ; } set { edad = value ; } } public bool esMayorDeEdad () { return ( edad >= 18) ; } // M e todos y propiedades requeridos por IEstudiante public double NotaMedia { get { return notaMedia ; } set { notaMedia = value ; } } public int A~ n oMatricula { get { return a~ n oMatricula ; } set { a~ n oMatricula = value ; } }

34

Tema 4. Herencia

34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69

// M e todos y propiedades requeridos por IProfesor public double Salario { get { return salario ; } set { salario = value ; } } public string Nss { get { return nss ; } set { nss = value ; } } // Constructores para Ayudante public Ayudante ( string nom , string tlf , int ed , double nm , int am , double sal , string numSec ) { nombre = nom ; telefono = tlf ; edad = ed ; notaMedia = nm ; a~ n oMatricula = am ; salario = sal ; nss = numSec ; } // Otras implementaciones de constructores y m e todos ... public override ToString () { return String . Format (" Nombre : {0: -20} Tel e fono : {1 ,9} Edad : {2 ,2}" + "\ nSeguridad Social : {3} Salario : {4: C }" + "\ nNota media : {5} A~ n o de matriculaci o n : {6}" , nombre , telefono , edad , nss , salario , notaMedia , a~ n oMatricula ) ; } private private private private private private private } C odigo 4.4: Herencia M ultiple mediante interfaces string nombre ; string telefono ; int edad ; double notaMedia ; int a~ n oMatricula ; double salario ; string nss ;

4.6.

Herencia, reutilizaci on y extensibilidad del software

A continuaci on enumeramos cu ales son los requerimientos que debe cumplir un m odulo para facilitar la reutilizaci on: 1. Variaci on en tipos. Conseguida gracias a la genericidad. 2. Variaci on en estructuras de datos y algoritmos. Conseguida gracias a la ligadura din amica y el polimorsmo. 3. Independencia de la representaci on. Conseguida gracias a la ligadura din amica y el polimorsmo.

4.6 Herencia, reutilizaci on y extensibilidad del software

35

4. Captura de similitudes entre un un subgrupo de un conjunto de posibles implementaciones. Conseguida gracias a la herencia. 5. Agrupaci on de rutinas relacionadas. Conseguida mediante la construcci on de clases. Existen dos aspectos importantes en la reutilizaci on de c odigo: por una parte, la creaci on de componentes reutilizables, y por otra parte, la utilizaci on de dichos componentes. En C# existe una gran colecci on (a un en crecimiento) de elementos reutilizables. Nosotros trataremos ahora brevemente el contenido de System.Collections y la interface Collections.IEnumerable. La implementaci on de la interface IEnumerate por parte de una clase contenedora dota a esta de la posibilidad de emplear la instrucci on foreach, cuya sintaxis es: foreach (Tipo nombreVariable in nombreArray ) Mediante la instrucci on foreach tenemos en cada iteraci on en la variable nombreVariable el contenido de la clase contenedora. Obs ervese que este valor no puede ser modicado, s olo puede consultarse. Para construir un iterador necesitamos alguna manera de avanzar el iterador, devolver el siguiente valor en la colecci on y comprobar en cada instante si ya hemos iterado sobre todos los elementos de la colecci on. Para ello la interface IEnumerable declara el m etodo IEnumerator GetEnumerator(). No obstante, para realizar una implementaci on concreta deben implementarse (valga la redundancia) los m etodos MoveNext() y Reset(), as como el m etodo de acceso Current, todos ellos de la interface IEnumerator. Ejemplo 4.10 El c odigo 4.5 muestra un ejemplo de una clase que implementa la interface IEnumerable, de modo que se podr a emplear la instrucci on foreach con objetos de dicha clase (como puede observarse en la l nea ). Se trata de un array de 10 enteros cuyo iterador s olo indexa los elementos que ocupan posiciones impares (teniendo en cuenta que la primera posici on es la 0, la segunda la 1, ...). El resultado mostrado por pantalla ser a: 1 3 5 7 9 11 13 15 17 19 Iterando... 3 7 11 15 19
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

using System ; using System . Collections ; class ArrayImpar : IEnumerable { public int [] a = new int [10]; public IEnumerator GetEnumerator () { return ( IEnumerator ) new ArrayImparEnumerator ( this ) ; } public int this [ int i ] { get { return a [ i ]; } set { a [ i ] = value ; } } private class A r r a y I m p a r E n umerator : IEnumerator { public A r r a y I m p a r E n u m e r ator ( ArrayImpar a )

36

Tema 4. Herencia

16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48

{ this . a = a ; indice = -1; } public void Reset () { indice = -1; } public bool MoveNext () { indice += 2; return ( indice <= 9) ; } publoc object Current { get { return a [ indice ]; } } private int indice ; private ArrayImpar a ; } class ArrayImparTest { public static void Main () { ArrayImpar ai = new ArrayImpar () ; for ( int i = 0; i < 10; i ++) { ai . a [ i ] = 2 * i + 1; Console . Write ( ai . a [ i ] + " ") ; } Console . WriteLine ("\ nIterando ...") ; foreach ( int elemento in ai ) Console . Write ( elemento + " ") ; } } C odigo 4.5: Implementaci on de un iterador Tratando ahora la cuesti on referente a la ocultaci on de informaci on en los casos en los que existe herencia diremos que en tales situaciones se viola el principio de caja negra, pues puede ocurrir que una subclase apoye algunos de sus m etodos en la implementaci on concreta conocida de la clase base. Este es un factor negativo para la extensibilidad, pues si modicamos la clase base, todas sus subclases que hayan actuado de la manera anteriormente indicada se ver an afectadas y probablemente tambi en deber an modicar su implementaci on. Adem as de los tres tipos de acceso vistos en la secci on 2.1.2 existe otro m as: internal. Una propiedad con este modicador de acceso s olo puede ser accedida desde c odigo perteneciente al mismo ensamblado en el que se haya denido dicha propiedad. La herencia privada, que est a permitida en C++, no es soportada en C#. Sin embargo, s se puede inhabilitar la herencia de una clase mediante la palabra clave sealed. Este tipo de clases, llamadas selladas, pueden ser tratadas de una forma m as eciente por parte del compilador, ya que sus m etodos no pueden ser redenidos y por tanto se declaran autom aticamente como no virtuales.

4.7 Herencia m ultiple

37

4.7.

Herencia m ultiple

Denici on 4.10 Se dice que existe herencia m ultiple de clases (tambi en puede ser de interfaces) cuando una clase hereda directamente de m as de una clase. Ejemplo 4.11 La gura 4.6.(a) muestra un ejemplo de herencia simple, mientras que la gura 4.6.(b) hace lo propio con la herencia m ultiple.

(a)

(b)

Figura 4.6: Herencia simple y m ultiple En C#, a diferencia de C++, no est a permitida la herencia m ultiple de clases. Sin embargo, al igual que en Java, este hecho se solventa mediante la herencia m ultiple de interfaces y simple de clases. Ejemplo 4.12 En el c odigo 4.4 as como en la gura 4.5 se muestra un claro ejemplo de herencia m ultiple de interfaces.

4.7.1.

Problemas: Colisi on de nombres y herencia repetida

Aunque la herencia m ultiple nos ofrece varias ventajas a la hora de programar seg un la metodolog a OO, tambi en implica algunos inconvenientes. A continuaci on explicaremos dos de ellos: la colisi on de nombres y la herencia repetida (aunque esta u ltima, como veremos m as adelante, no se da en C#). La colisi on de nombres tiene lugar cuando una clase hereda de dos o m as clases un m etodo con el mismo nombre y diferente implementaci on. Ejemplo 4.13 En la gura 4.7 se muestra un ejemplo de colisi on de nombres debida a la herencia m ultiple.

Figura 4.7: Herencia m ultiple y colisi on de nombres

La herencia repetida se da cuando una misma propiedad es heredada por distintos caminos m as de una vez.

38

Tema 4. Herencia

Figura 4.8: Herencia m ultiple y herencia repetida Ejemplo 4.14 En la gura 4.8 se muestra un ejemplo de herencia repetida debida a la herencia m ultiple. En C#, como ya hemos dicho, no hay herencia repetida, pero s puede darse colisi on de nombres en el caso en el que una interface hereda de dos o m as interfaces un m etodo con el mismo nombre. En este caso existen dos posibilidades: 1. Sobrecarga, si dieren en la signatura y/o en el valor de retorno. 2. Compartici on, si tienen la misma signatura y valor de retorno.

4.7.2.

Ejemplos de utilidad

Ejemplo 4.15 En el c odigo 4.6 se muestra un ejemplo en el que una clase C hereda de dos interfaces distintas A y B un m etodo con el mismo nombre f(object o), resultando en una ambig uedad de nombres.
1 2 3 4 5 6 7 8 9 10 11 12

interface A { void f ( object o ) ; } interface B { void f ( object o ) ; } class C : A , B { // A . f () o B . f () ?? public void g () { f () ; } } C odigo 4.6: Ambig uedad de nombres debida a la herencia m ultiple Podemos resolver la potencial ambig uedad de nombres dentro de la clase C incluyendo declaraciones expl citas para cada instancia de interface heredada. Es m as, tambi en podemos implementar un m etodo f() en C para el caso en el que se manipulen directamente objetos de dicha clase. Estos cambios pueden observarse en el c odigo 4.7.

1 2 3 4 5

class C : public public public }

A, B void void void

{ f ( string s ) { ... } A . f ( object o ) { ... } B . f ( object o ) { ... } C odigo 4.7: Soluci on a la ambig uedad de nombres

4.8 C#, Java y C++: Estudio comparativo

39

Ejemplo 4.16 Sea la jerarqu a mostrada en la gura 4.9. Las letras enmarcadas representan interfaces y las no enmarcadas, clases. En esta circunstancia podr amos pensar que se dar a un caso de herencia repetida, pero no es as .

Figura 4.9: Herencia m ultiple sin herencia repetida (I) La clase Z no necesita implementar los m etodos de la interface W, pues ya hereda dichas implementaciones de la clase X. No obstante s deber a implementar aquellos m etodos de la interface Y que no est en en la interface W. Ejemplo 4.17 Sea ahora la jerarqu a mostrada en la gura 4.10. Al igual que en el ejemplo anterior, podr amos pensar que bajo estas premisas nos encontramos ante un caso de herencia m ultiple. Pero, al igual que en el ejemplo anterior esto no es as .

Figura 4.10: Herencia m ultiple sin herencia repetida (II) Si bien es cierto que la clase Z hereda los m etodos de la interface W por dos v as, no resulta esto ser un problema, pues como ya vimos en el ejemplo 4.15, se puede solucionar f acilmente. Otra soluci on distinta a la expuesta en el ejemplo 4.15 pero igualmente v alida consiste en implementar una u nica vez cada m etodo repetido.

4.8.

C#, Java y C++: Estudio comparativo

Adem as de las continuas referencias que durante todo el texto se han hecho hacia otros lenguajes OO como Java y C++, a continuaci on se muestran s olo algunas comparaciones entre dichos lenguajes y C#. C# es muy parecido a Java. Normalmente el recolector de basura (garbage collector ) se encarga autom aticamente de los objetos que ya no son necesarios (por ejemplo, los no

40

Tema 4. Herencia

referenciados por ninguna variable). Toda clase tiene como clase base de su jerarqu a a la clase object. En C# todo es un objeto. Los tipos primitivos tales como int pueden ser tratados como objetos. No es este el caso de Java donde los tipos primitivos son estrictamente tipos valor. C# permite paso por referencia en los tipos primitivos. C# incorpora la instrucci on foreach junto con la interface IEnumerator, lo cual permite la construcci on de un iterador. C# tiene verdaderos arrays multidimensionales. C# ha retenido, aunque de una forma m as l ogica y simple, la habilidad de C++ para sobrecargar operadores. En C# existe el concepto de propiedades, lo cual proporciona los m etodos de acceso get y set para consultar y modicar los valores de los miembros de datos de una clase. Un programador de C# puede convertir r apidamente su c odigo a Java, pero no a la inversa. De hecho Java es conceptualmente un subconjunto de C#. C++ es muy complejo e inseguro. Ofrece demasiadas oportunidades al programador de manejar err oneamente los punteros en aras de la eciencia. C++ es dependiente del sistema. No est a preparado para la Web. No maneja la memoria como lo hacen C# o Java. El mero hecho de que el n ucleo de C# est e desprovisto del tipo puntero simplica enormemente su compresi on por parte de un estudiante de POO.

Indice de Figuras
2.1. 2.2. 2.3. 2.4. 2.5. 2.6. 2.7. Clientela y Herencia entre A y B . . . . . . . . . . . . Variables en los lenguajes tradicionales . . . . . . . . . Variables tipo puntero en los lenguajes tradicionales Variables en los lenguajes OO . . . . . . . . . . . . . . Ejemplo de copia supercial . . . . . . . . . . . . . . . Ejemplo de copia profunda . . . . . . . . . . . . . . . Ejemplo de variables id enticas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 11 11 11 12 12 13 25 26 29 30 33 37 37 38 39 39

4.1. Jerarqu a de clases . . . . . . . . . . . . . . . . . 4.2. Herencia de especializaci on y de implementaci on 4.3. Ejemplo de estructura de datos polimorfa . . . . 4.4. Herencia y redenici on de m etodos . . . . . . . . 4.5. Herencia e implementaci on de interfaces . . . . . 4.6. Herencia simple y m ultiple . . . . . . . . . . . . . 4.7. Herencia m ultiple y colisi on de nombres . . . . . 4.8. Herencia m ultiple y herencia repetida . . . . . . 4.9. Herencia m ultiple sin herencia repetida (I) . . . . 4.10. Herencia m ultiple sin herencia repetida (II) . . .

41

42

INDICE DE FIGURAS

Indice de Tablas
2.1. 2.2. 2.3. 2.4. Tipos enteros primitivos . Tipos otantes primitivos Otros tipos primitivos . . Palabras reservadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 6 6 6 19 19 26 27 27

3.1. Algunas Excepciones est andar . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.2. Propiedades de las excepciones . . . . . . . . . . . . . . . . . . . . . . . . . . 4.1. Propiedades de las excepciones . . . . . . . . . . . . . . . . . . . . . . . . . . 4.2. Operadores unarios sobrecargables . . . . . . . . . . . . . . . . . . . . . . . 4.3. Operadores binarios sobrecargables . . . . . . . . . . . . . . . . . . . . . . .

43

44

INDICE DE TABLAS

Indice de C odigos
2.1. 2.2. 2.3. 2.4. 2.5. 2.6. 2.7. 3.1. 3.2. 3.3. 3.4. 3.5. 4.1. 4.2. 4.3. 4.4. 4.5. 4.6. 4.7. Clase Luna . . . . . . . . . . . . . . . . . . . . . . . . Ejemplo de Ocultaci on de Informaci on . . . . . . . . . Otro ejemplo de Ocultaci on de Informaci on . . . . . . Ejemplo de Clases Anidadas . . . . . . . . . . . . . . . Ejemplo de Constructores . . . . . . . . . . . . . . . . Ejemplo de implementaci on del m etodo Clone() . . . Lista gen erica doblemente enlazada . . . . . . . . . . . Ejemplo del uso de asertos . . . . . . . . . . . . . . . . Ejemplo del uso de excepciones . . . . . . . . . . . . . Ejemplo del relanzamiento de excepciones . . . . . . . Uso de excepciones con m as informaci on . . . . . . . . Ejemplo del uso de excepciones en vez de asertos . . . Ejemplo de Sobrecarga de Operadores . . . . . . . . . Redinici on de un m etodo heredado . . . . . . . . . . Clase abstracta Figura . . . . . . . . . . . . . . . . . . Herencia M ultiple mediante interfaces . . . . . . . . . Implementaci on de un iterador . . . . . . . . . . . . . Ambig uedad de nombres debida a la herencia m ultiple Soluci on a la ambig uedad de nombres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 7 8 9 12 13 14 18 18 20 21 22 28 31 31 33 35 38 38

45

46

INDICE DE CODIGOS

Bibliograf a
[Lip02] Stanley B. Lippman. C# Primer. A Practical Approach. Addison Wesley, 2002. [Mol04] J. Garc a Molina. Apuntes de programaci on orientada a objetos. 2004. [Poh03] Ira Pohl. C# by Dissection. Addison Wesley, University of California, 2003.

47

También podría gustarte