Vous êtes sur la page 1sur 80

C#

David Gan Jimnez


P08/B0036/01627

FUOC P08/B0036/01627

C#

ndice

Introduccin.......................................................................................... Objetivos................................................................................................. 1. Conceptos bsicos de C#............................................................... 1.1. Estructura de un programa en C# ............................................ 1.2. Compilacin y ejecucin .......................................................... 1.3. Elementos bsicos de sintaxis ................................................... 1.3.1. Fin de instruccin .......................................................... 1.3.2. Case Sensitive ................................................................ 1.3.3. Comentarios ................................................................... 1.3.4. Bloques de cdigo .......................................................... 1.3.5. Variables ......................................................................... 1.3.6. Constantes ..................................................................... 1.3.7. Operadores ..................................................................... 1.3.8. Expresiones .................................................................... 1.3.9. Tipos de datos ................................................................ 1.3.10. Tipos parciales ................................................................ 1.3.11. Tipos anulables (nullable types) .................................... 1.3.12. Arrays ............................................................................. 1.3.13. Conversiones entre tipos de datos ................................ 1.3.14. Tipos y mtodos genricos ............................................ 1.4. Instrucciones del lenguaje ........................................................ 1.4.1. Sentencias de decisin ................................................... 1.4.2. Sentencias de iteracin .................................................. 1.4.3. Sentencias de salto ........................................................ 2. Orientacin a objetos en C#........................................................ 2.1. Definicin de clases .................................................................. 2.2. Instancin de clases .................................................................. 2.3. Herencia ..................................................................................... 2.3.1. Interfaces ........................................................................ 2.4. Miembros de datos .................................................................... 2.5. Miembros de funcin ............................................................... 2.5.1. Mtodos ......................................................................... 2.5.2. Propiedades .................................................................... 2.5.3. Constructores ................................................................. 2.5.4. Destructores ................................................................... 2.5.5. Operadores ..................................................................... 2.5.6. Indexadores ....................................................................

5 6 7 7 9 10 10 10 10 11 12 14 15 16 16 22 23 24 27 28 31 31 35 39 41 41 42 44 45 46 47 48 53 54 56 57 57

FUOC P08/B0036/01627

C#

3. Conceptos avanzados.................................................................... 3.1. Tratamiento de excepciones ..................................................... 3.1.1. Excepciones definidas por el usuario ............................ 3.2. Delegate y eventos .................................................................... 3.2.1. Eventos ........................................................................... 3.2.2. Mtodos annimos ........................................................ 3.3. Atributos .................................................................................... 3.3.1. Atributos personalizados ............................................... 4. Novedades de C# 3.0...................................................................... 4.1. Variables locales de tipo implcito ............................................ 4.2. Tipos annimos ......................................................................... 4.3. Mtodos de extensin ............................................................... 4.4. Inicializadores de objetos .......................................................... 4.5. Expresiones lambda .................................................................. 4.6. Expresiones de consulta ............................................................ Actividades............................................................................................. Bibliografa............................................................................................

59 59 62 64 66 68 69 70 73 73 73 74 74 75 76 77 79

FUOC P08/B0036/01627

C#

Introduccin

Microsoft desarroll C#, junto con la plataforma .NET, como un lenguaje moderno, fcil de utilizar y totalmente orientado a objetos. El lenguaje C# es adems un Standard ECMA, igual que el motor de ejecucin de .NET. C# consolida la experiencia de programacin en otros lenguajes como C++ o java, adaptando las bondades de estos lenguajes y mejorando sus limitaciones. Es por eso que los programadores de C, C++ o java encontrarn la sintaxis del lenguaje muy familiar. En este tema, se pretende presentar el lenguaje C#, estudiando la sintaxis concreta de cada una de las estructuras de programacin, as como los elementos nuevos y diferenciadores de este lenguaje.

FUOC P08/B0036/01627

C#

Objetivos

El material que se os presenta a continuacin ha sido elaborado teniendo en cuenta los siguientes objetivos especficos:

1. Aprender los conceptos y sintaxis bsica de C#. 2. Conocer las caractersticas de programacin orientada a objetos de C#. 3. Profundizar en otros conceptos ms avanzados de programacin en C#.

FUOC P08/B0036/01627

C#

1. Conceptos bsicos de C#

1.1. Estructura de un programa en C# La mejor forma de empezar a aprender un lenguaje de programacin nuevo suele ser analizando algn ejemplo de cdigo sencillo. Y para no romper la tradicin, empezaremos con el tpico HolaMundo:.
using System;

namespace HolaMundo { class HolaMundo { [STAThread] static void Main(string[] args) { Console.WriteLine ("Hola Mundo"); } } }

A continuacin, analizaremos los diferentes elementos del anterior programa uno por uno: Importacin de libreras using System ; Al inicio de cada fichero de cdigo fuente C# podemos incluir varias instrucciones using. La utilidad de esta instruccin es la de importar libreras de clases ya existentes, ya sea de la FCL (framework class library del .NET Framework), desarrolladas por otras empresas, o por nosotros mismos. Al importar una librera, podremos utilizar desde el cdigo todas las funcionalidades que sta proporciona. Como ya hemos visto anteriormente, las libreras de clases se organizan en carpetas lgicas, llamadas namespaces. Cada instruccin using debe ir acompaada del nombre del namespace que se quiera importar; en el ejemplo anterior se est importando el namespaceSystem de la FCL, que contiene la clase Console que se va a utilizar en el cdigo. Definicin del namespace namespace HolaMundo { ... }

FUOC P08/B0036/01627

C#

Todas las clases estn incluidas dentro de un namespace concreto, que se indica dentro del cdigo fuente mediante la instruccin namespace, seguida del nombre del namespace. Pueden existir dos namespaces con el mismo nombre, pero no si estn dentro del mismo namespace. El nombre completo de un namespace es el resultado de concatenar los nombres de todos los namespaces que hay que atravesar hasta llegar hasta l, separados por puntos (.), por ejemplo: System.Drawing Todos los elementos e instrucciones incluidos dentro de las llaves pertenecen al namespaceHolaMundo. Si se omite la definicin del namespace, los elementos se incluirn en el namespace por defecto de la aplicacin. Definicin de la clase class HolaMundo { ... } Aprenderemos a definir clases ms adelante. La forma general de definir una clase es mediante la palabra clave class, seguida del nombre que queremos dar a la clase, y entre las llaves todos los elementos e instrucciones que pertenecen a la definicin de la clase. Pueden existir dos clases con el mismo nombre, pero no si estn dentro del mismo namespace. El nombre completo de una clase es el resultado de concatenar los nombres de todos los namespaces que hay que atravesar hasta llegar hasta la clase, separados por puntos (.), seguida del nombre corto de la clase, por ejemplo: System.Drawing.Rectangle El mtodo main static void Main(string[] args) { ... } El mtodo Main es el mtodo principal de una aplicacin, es el punto de entrada. Cuando se ejecuta una aplicacin, se ejecutan nica y exclusivamente las instrucciones que se encuentran dentro de este mtodo. Por eso, es en este mtodo donde hay que escribir las instrucciones y llamadas a otros mtodos, necesarias para llevar a cabo los objetivos de la aplicacin. El tipo de retorno del mtodo Main es por defecto void (no devuelve ningn valor), pero tambin se puede definir como int (un entero que indica el estado en que finaliza el programa). En cuanto a los parmetros, el mtodo Main acepta un nico parmetro opcional, que es un vector de cadenas de caracteres, correspondiente a los argumentos del programa en la lnea de comandos. Estos argumentos permiten aadir opciones de configuracin detrs del nombre de la aplicacin al ejecutarla desde la consola de comandos del sistema, como las que proporciona el compilador de C# La clase Console Console.WriteLine ("Hola Mundo"); La funcionalidad de la instruccin anterior es la de mostrar por la consola de comandos el mensaje 'Hola Mundo'. La clase Console incluye diversos

FUOC P08/B0036/01627

C#

mtodos de lectura y escritura para trabajar con la consola de comandos del sistema. Los mtodos Write y WriteLine permiten escribir informacin en la consola (WriteLine aade un salto de lnea al final), mientras que los mtodos Read y ReadLine capturan los datos que se introducen por el teclado (ReadLine lee todo lo que se introduce hasta que se introduce un salto de lnea, mientras que Read slo lee un carcter del teclado).

1.2. Compilacin y ejecucin Una vez escrito nuestro programa en C# debemos compilarlo. Para hacerlo, podemos utilizar el compilador del .NET Framework , o una herramienta ms avanzada como Visual Studio. El compilador de C# es una utilidad de consola de comandos. Para utilizarlo debemos abrir una ventana de la consola y escribir: csc Esto producir un error, ya que no hemos especificado ninguna opcin, ni hemos seleccionado los archivos que queremos compilar. Si escribimos: csc /? aparecer la sintaxis del comando, y una lista de las opciones disponibles. Por ejemplo, para compilar nuestro programa HolaMundo introduciremos:
csc HolaMundo.cs
1 (1)

El .NET Framework Redistributable y el SDK se pueden descargar desde el sitio de descargas de Microsoft. El .NET Redistributable adems, se puede instalar automticamente desde el sitio de actualizaciones de Windows. Con Visual Studio instalado no es necesario instalar el .NET framework por separado (se instala por defecto).

La instruccin anterior compilar nuestro programa, y crear un fichero HolaMundo.exe ejecutable. Para ejecutar este archivo en un ordenador, ser necesario que est instalado el .NET Framework Redistributable o el SDK. Si est instalado, podremos ejecutar la aplicacin escribiendo en la consola de comandos:
HolaMundo

o bien haciendo doble clic encima del archivo en el explorador de Windows. El programa de ejecuta y muestra el mensaje 'Hola Mundo' por pantalla.

FUOC P08/B0036/01627

10

C#

1.3. Elementos bsicos de sintaxis La sintaxis de un lenguaje es la definicin de las palabras clave, los elementos y las combinaciones vlidas entre ellos en ese lenguaje. A continuacin, analizaremos los diferentes elementos e instrucciones que permite el lenguaje C#, y cul es su sintaxis. 1.3.1. Fin de instruccin

Todas las instrucciones de C# finalizan con un punto y coma (;) al final de la lnea, salvo en algunas excepciones, como en algunas partes determinadas de una sentencia, o al final de un bloque de cdigo. 1.3.2. Case Sensitive

Las palabras clave y los identificadores en C# son case sensitive, es decir, que diferencia ente minsculas y maysculas, por ejemplo holamundo y HolaMundo son dos identificadores distintos, y si escribimos Namespace en vez de namespace, el compilador dar un error de sintaxis. 1.3.3. Comentarios

Los comentarios son anotaciones del programador dentro del propio cdigo fuente, tiles para documentar qu hace cada parte del cdigo, y que sea ms fcil realizar modificaciones en el futuro. Estos comentarios son ignorados por el compilador durante el proceso de compilacin, y no se incluyen en el archivo ejecutable. Existen tres tipos de comentarios en C#:

Comentarios de una sola lnea // ... Cualquier carcter desde las dos barras invertidas hasta el final de la lnea se considera como comentario.

Comentarios de mltiples lneas /* ... */ Cualquier carcter entre el smbolo /* y el smbolo */, se considera como parte del comentario, aunque ste abarque varias lneas.

Comentarios XML /// Este tipo de comentario permite, posteriormente, generar de modo automtico un archivo XML con la documentacin de nuestro cdigo fuente. Para ello se utilizan una serie de tags XML que permiten definir los diferentes componentes del cdigo fuente, como por ejemplo:

FUOC P08/B0036/01627

11

C#

Ejemplo <summary> ... </summary>. Permite realizar una descripcin breve <remarks> ... </remarks>. Descripcin detallada <example> ... </example>. Permite incluir un ejemplo de utilizacin

Adems, se pueden incluir elementos HTML para formatear la informacin en tablas o listas. Se pueden consultar todos los tags XML que se pueden utilizar en la ayuda de Visual Studio, buscando 'XML Documentation comments'. Para generar la documentacin, podemos utilizar la opcin /doc del compilador de consola de comandos de C#. 1.3.4. Bloques de cdigo

Todos los programas estn compuestos por lneas de cdigo, pero a veces es necesario agruparlas en bloques para organizar el cdigo fuente. Estos bloques se definen en C# mediante los caracteres { y } (llaves). Todas las instrucciones que se encuentran en el interior de las llaves se consideran un bloque.
{ // instrucciones del bloque }

Los bloques de cdigo pueden anidarse entre s, es decir, puede haber un bloque de cdigo dentro de otro bloque de cdigo ms grande:
{ // instrucciones del bloque principal

{ // instrucciones del bloque anidado

} // ms instrucciones del bloque principal }

Como veremos ms adelante, las diferentes instrucciones del lenguaje utilizan bloques para separar las diferentes partes de la instruccin unas de otras. Adems de los bloques definidos por llaves, en Visual Studio se pueden definir regiones que, posteriormente, se pueden ocultar o mostrar mediante el plegado de cdigo. Estas regiones son muy tiles a la hora de organizar los elementos de cdigo segn su funcionalidad. Adems, se les puede asignar un nombre que describa la funcionalidad de las instrucciones que contiene. Para definir un bloque, utilizamos las palabras clave region y endregion:
#region <nombre>

FUOC P08/B0036/01627

12

C#

// instrucciones dentro de la regin

#endregion

1.3.5.

Variables

Para la realizacin de sus funciones, los programas necesitan manejar cierta informacin. Estos datos se almacenan en memoria, y son accesibles desde el programa mediante el uso de variables. Una variable es un nombre que se le asigna a un dato concreto, e indica en qu posicin de memoria se encuentra ese dato, por lo que nos permite acceder a l para consulta o modificacin. Normalmente se suelen asignar nombres de variables que recuerden el objetivo del dato que almacenan. Para utilizar una variable en un programa es necesario declararla, es decir, reservar el espacio necesario en memoria para almacenar el valor de la variable. Para declarar una variable es necesario especificar su nombre y su tipo de dato. Por ejemplo, para declarar una variable entera de 32 bits, con nombre i, deberamos escribir: int i; Esta instruccin reserva 32 bits de memoria para almacenar el valor entero representado por i. Una vez declarada una variable, podemos asignarle un valor, mediante la operacin de asignacin (=): i = 4; La instruccin anterior modifica el valor de la variable i a 4. Tambin se puede asignar a una variable el valor de otras variables, escribiendo expresiones ms complejas.
Ejemplo Por ejemplo, la siguiente instruccin asigna a i el valor de la variable j ms el valor de la variable k, todo dividido entre 2: i = (j + k) / 2;

La instruccin de asignacin permite tambin encadenar varias variables a las cuales se quiere asignar el mismo valor.
Ejemplo Por ejemplo, la siguiente instruccin asigna el valor 10 a las variables i y j: i = j = 10;

FUOC P08/B0036/01627

13

C#

La declaracin y la asignacin de variables se pueden unificar en un nico paso, para dar un valor inicial a las variables recin creadas. La siguiente instruccin declara e inicializa la variable i con el valor 4: int i = 4; Adems, se pueden inicializar varias variables de un mismo tipo en una sola instruccin, e incluso asignarles el mismo valor inicial, como en las siguientes instrucciones:
int i, j; // declaracin de varias variables del mismo tipo

int i = 4, j = 5; // declaracin e inicializacin de varias variables del mismo tipo int i = j = 4; // declaracin e inicializacin de varias variables del mismo tipo // con el mismo valor inicial

Visibilidad La visibilidad de una variable es un parmetro adicional que se puede aadir a la declaracin de una variable, para indicar desde qu puntos del programa se tendr acceso a ella: visibilidad tipo_dato nombre_var; La visibilidad puede ser una de las siguientes: public La variable es accesible desde cualquier punto del programa y desde otros programas. private La variable slo es accesible internamente al tipo de dato al que est asociada. protected La variable slo es accesible internamente al tipo de dato al que est asociada o a alguna de sus subclases (veremos el concepto de herencia en el apartado de orientacin a objetos). internal La variable es accesible solamente desde puntos del programa situados dentro del propio ensamblado (no puede ser utilizado por otros programas). protected internal La variable es accesible desde puntos del programa situados dentro del propio ensamblado, o desde alguna de las subclases del tipo al que est asociado.

FUOC P08/B0036/01627

14

C#

mbito de las variables Dependiendo del punto de un programa en el que se declare una variable, sta puede tener los dos siguientes tipos de alcance: Global. La variable est disponible desde cualquier punto del programa, siempre y cuando la visibilidad lo permita. Local. La variable slo est disponible dentro del bloque de cdigo en el que se declara, por ejemplo, dentro de un mtodo o dentro de un bucle (veremos estas estructuras ms adelante). Las variables definidas dentro de un bloque son locales a ese bloque, es decir, no se puede acceder a ellas desde fuera de ese bloque, aunque s que son accesibles desde bloques definidos dentro de l. Por ejemplo:
{ // bloque A { // bloque B

int i; { // bloque C } } }

La variable i definida en el bloque B, no es accesible desde el bloque A, pero s desde el bloque C. 1.3.6. Constantes

Una constante es una variable cuyo valor no puede ser modificado, es decir, es slo de lectura. Para declarar una constante, escribimos la palabra clave const delante de la declaracin de la variable. Es necesario (dado que su valor no se puede modificar a posteriori) especificar el valor inicial de la variable en la misma instruccin de declaracin. const int i = 4

FUOC P08/B0036/01627

15

C#

1.3.7.

Operadores

Los operadores son smbolos especiales que permiten realizar clculos sobre las variables o valores que tienen como parmetro. El tipo de datos sobre el que acta cada operador y el tipo de resultado dependen del tipo de operador: Aritmticos. +, -, *, /, % (mdulo) Lgicos. & (and bit a bit), | (or bit a bit), ~ (not bit a bit) && (and lgico), || (or lgico), ! (not lgico) Concatenacin de cadenas de caracteres. + Incremento / decremento. ++, Comparacin. ==, != (desigual), <, >, <=, >= Asignacin. =, +=, -=, *=, /=, %=, &=, |=, <<=, >>= Las combinaciones +=, -=, *=, etc., permiten asignar a una variable el resultado de realizar la operacin indicada delante del smbolo =, entre la variable y el valor indicado a continuacin. Por ejemplo x += 3 es equivalente a x = x + 3 Condicional. (condicin)?(expr_cierto):(expr_falso) El operador condicional es un operador ternario que recibe tres parmetros: una condicin, la expresin que se devuelve como resultado si la condicin es cierta, y la que se devuelve si es falsa. Por ejemplo: string x = (y==5)?("y es igual a 5"):("y es diferente de 5"); Si y equivale a 5, x contendr la cadena "y es igual a 5" y si no, la cadena "y es deferente de 5". Acceso a miembros. El punto permite acceder a los miembros o elementos internos de un tipo de dato, es decir a sus atributos, propiedades, mtodos, etc., que ese tipo de dato pueda contener. Para acceder a un miembro, se escribe el nombre de la variable seguida del punto y seguida del nombre del miembro, sin espacios:
string s = "Hola"; s.Length // s es una cadena de caracteres (&lt;i&gt;string&lt;/i&gt;). // Length devuelve el nmero de caracteres de la cadena

Adems, se pueden encadenar varios operadores '.' si el miembro contiene a su vez otros miembros:

FUOC P08/B0036/01627

16

C#

s.Trim().LastIndexOf ("H"); El mtodo Trim devuelve el mismo string contenido en s, eliminando espacios al principio y al final de la cadena . A continuacin, se ejecuta el mtodo LastIndexOf, que devuelve el ultimo ndice de la subcadena indicada, en el caso anterior la posicin 0. 1.3.8. Expresiones
2 (2)

Las posiciones de una cadena de caracteres empiezan en 0, es decir, van de 0 a N-1, donde N es la longitud de la cadena.

Una expresin es una secuencia de variables, valores y operaciones que se puede evaluar. El tipo de valor de la expresin depende de los tipos de las variables y/o valores que la forman, as como de los operadores utilizados.
Ejemplo A continuacin, se muestran algunos ejemplos de expresiones: 3 + 4: i - 4: true && b: 'a' + 'b': // // // // Expresin Expresin Expresin Expresin entera numrica booleana alfanumrica

1.3.9.

Tipos de datos

Existen diferentes tipos de datos. Un tipo de dato es una estructura que define cmo se almacena una determinada informacin en la memoria, as como las operaciones que se pueden realizar sobre ese tipo. Por ejemplo, el tipo de dato entero de 32 bits se almacena en memoria como un nmero binario de 32 bits y permite realizar operaciones aritmticas, de comparacin e igualdad, etc. Dependiendo de la zona de memoria donde se almacenan los datos de un tipo de datos, stos pueden ser tipos valor o tipos referencia. Los tipos valor son aquellos que se almacenan directamente en la pila de ejecucin del programa (stack). Los tipos referencia son aquellos que se almacenan en una zona de memoria auxiliar llamada heap o montculo. Para poder acceder a estos datos, se coloca un puntero (direccin de memoria) en la pila de ejecucin3, que indica la posicin de memoria del heap donde se encuentran los datos. Los tipos, adems, pueden ser predefinidos o definidos por el usuario. Los tipos predefinidos representan valores simples como enteros, reales, caracteres o valores booleanos. Los tipos definidos por el usuario o estructuras de datos son tipos complejos definidos como composicin de tipos predefinidos. Tipos valor por defecto Los tipos de valor por defecto son los siguientes:
(3)

La pila de ejecucin de un programa es la zona de memoria donde se guardan los datos necesarios para poder ejecutar las instrucciones del programa: variables, valores parciales, instrucciones de llamadas a mtodos, etc.

FUOC P08/B0036/01627

17

C#

Enteros. Son valores que representan valores numricos enteros. En la siguiente tabla, se muestran los diferentes tipos de datos enteros, as como su tamao en memoria, y el rango de nmeros que permiten representar.
Tamaoenmemoria Rango -2 : 2 - 1 -2 -2 2
15 7 7

sbyte short int long byte ushort uint ulong

8 bits con signo 16 bits con signo 32 bits con signo 64 bits con signo 8 bits sin signo 16 bits sin signo 32 bits sin signo 64 bits sin signo

:2 :2

15

-1 -1

31

31

63

:2
8

63

-1

0:2 -1 0:2 0:2 0:2


16

-1 -1 -1

32

64

A veces, es necesario en una expresin desambiguar el tipo de entero al que pertenece un determinado valor. Para ello, se aaden letras al final del valor que identifican su tipo de dato, en concreto para nmeros enteros, la letra U identifica un valor sin signo, y la letra L un valor de 64 bits: uint ui = 1234U; long l = 1234L; ulong ul = 1234UL; Reales. Son valores que representan valores numricos reales. Existen dos tipos de reales de coma flotante, uno de precisin simple (float) y otro de precisin doble (double ). Por ltimo, existe un tipo especial (decimal ) para operaciones que requieren una gran precisin (ms posiciones decimales).
Tamaoenmemoria 32 bits real de coma flotante de precisin simple 64 bits real de coma flotante de precisin doble Nmeromximodedgitos 7 Rango

float

1.5 x 10 x 10
38

-43

: 3.4

double

15 / 16

5.0 x 10 x 10
308

-324

: 1.7

decimal

128 bits real de notacin decimal 28 de alta precisin

1.0 x 10 x 10
28

-28

: 7.9

FUOC P08/B0036/01627

18

C#

De la misma forma que con los valores enteros, se pueden desambiguar los valores reales mediante las letras F, para valores float, y M para valores decimal: float f = 12.3F; decimal d = 12.3M; Booleanos. El tipo de dato bool representa un dato booleano, cuyo valor puede ser cierto (true) o falso (false). Caracteres. El tipo char representa caracteres unicode de 16 bits. Los valores de tipo carcter se expresan entre comillas simples ('), por ejemplo: 'a', 'b', '0', etc. Existen, adems, una serie de caracteres especiales llamados caracteres de escape, que realizan alguna funcin especial. A continuacin, se muestra una tabla con los caracteres de escape y su funcionalidad:
Carcterdeescape \' Funcionalidad Permite representar el carcter '. Este carcter no se puede representar de otro modo ya que la expresin: char c = ''' ; No es sintcticamente correcta. Permite representar el carcter " dentro de una cadena de caracteres encerrada entre comillas dobles: "Hola \" esto es una cadena" equivale a la cadena: Hola " esto es una cadena Permite representar el carcter \, ya que por si solo se interpretara como el inicio de un carcter de escape. "Hola \\ esto es una cadena" equivale a la cadena: Hola \ esto es una cadena Carcter nulo Backspace. Introduce un espacio. Provoca que se termine de escribir en la pgina actual y se contine en la siguiente. Traslada el cursor a la lnea siguiente Retorno de carro. Devuelve el cursor al inicio de la lnea Tabulador. Introduce una tabulacin horizontal Tabulador vertical. Introduce una fabulacin vertical

\"

\\

\0 \b \f

\n \r \t \v

Tipos valor definidos por el usuario Enumeraciones. La enumeracin es una estructura de datos que permite definir una lista de constantes y asignarles un nombre. Este tipo de dato se utiliza para definir secuencias de constantes relacionadas. Por ejemplo, la siguiente enumeracin define los das de la semana:
enum DiasSemana

FUOC P08/B0036/01627

19

C#

{ Lunes, Martes, Miercoles, Jueves, Viernes, Sabado, Domingo }

Para acceder a los elementos de la enumeracin utilizamos su nombre seguido del nombre del elemento, separados por un punto (acceso a miembros): DiasSemana ds = DiasSemana.Lunes; De ese modo, el cdigo es mucho ms legible, ya que en vez de utilizar un entero del 1 al 7 para representar el da, se utiliza una constante y un tipo especfico de datos para el da de la semana, de modo que se evitan problemas adicionales como, por ejemplo, controlar que el valor de una variable que represente el da est entre 1 y 7.

Actividad 1 Ejecutad las siguientes acciones: Cread la enumeracin Meses que represente los meses del ao. Declarad una variable de tipo Meses y asignadle como valor el mes Agosto.

Estructuras (structs). Un struct permite definir una estructura compuesta de uno o ms elementos o atributos, cada uno de ellos con su correspondiente tipo de dato. Los struct tambin pueden tener otros elementos como mtodos, constructores o propiedades, que como veremos ms adelante tambin forman parte de la definicin de las clases. La diferencia fundamental entre un struct y una clase, es que el primero es un tipo de dato por valor, mientras que el segundo es un tipo por referencia. Por otro lado, existen algunas restricciones de los struct respecto a las clases, por ejemplo los struct no soportan herencia. En C# los structs se declaran mediante la palabra clave struct, seguida del nombre que queremos asignar a la estructura, y de un bloque de instrucciones con los elementos que lo definen:
struct Punto { int x; int Y;

// otros elementos }

Las estructuras se declaran igual que cualquier tipo por valor, y para acceder a sus elementos utilizamos el operador de acceso a miembros:

FUOC P08/B0036/01627

20

C#

Punto p; p.x = 0; p.y = 0;

Actividad 2 Ejecutad las siguientes acciones: Escribid la estructura Producto con los siguientes atributos: nombre, precio, categora. Cread un Producto con los siguientes valores: "Tomate", 1.5, "Verdura". Copiad el Producto "Tomate" en el Producto "Zanahoria", y modificad el nombre y el precio a "2".

Tipos referencia por defecto Existen, bsicamente, dos tipos de datos por referencia predefinidos y son los siguientes: Object. El tipo de dato object (System.Object) es el tipo de dato principal del cual derivan el resto de tipos de datos, tanto si son por valor o por referencia, como si son por defecto o definidos por el usuario. El tipo object define una serie de caractersticas y funcionalidades comunes para todos los tipos de datos .NET, entre los cuales encontramos mtodos para comparar objetos entre s (Equals), para consultar el tipo de un objeto concreto (GetType), o para obtener una representacin del objeto en forma de cadena de caracteres (ToString). Cada subtipo que deriva del tipo object puede redefinir estos mtodos para adecuarlos a sus necesidades. String. El tipo string (System.String) es un tipo especial, ya que por una parte se comporta como un tipo valor, aunque en realidad es un tipo referencia. Los valores de tipo string se expresan entre comillas dobles ("): string s = "Hola esto es un string" Dentro de un string se pueden adems utilizar caracteres de escape: string s = "Hola esto \\ es un \' string\n" En C# tambin es posible evitar los caracteres de escape para escribir caracteres como \, ', o incluso \n (las comillas (") no, ya que son el carcter de final de cadena), si delante de la cadena de caracteres ponemos el carcter @. La siguiente cadena es equivalente a la anterior: string s = @"Hola esto \ es un ' string

FUOC P08/B0036/01627

21

C#

" La clase string tiene multitud de mtodos para trabajar con cadenas de caracteres.
Ejemplo CompareTo. Compara dos cadenas alfanumricamente IndexOf. Devuelve la posicin de una subcadena Replace. Cambia una subcadena por otra Substring. Devuelve la subcadena que empieza en la posicin especificada y que tiene la longitud indicada. ToLower. Devuelve la misma cadena pasada a minsculas ToUpper. Devuelve la misma cadena pasada a maysculas Trim. Devuelve la misma cadena eliminando los espacios al principio y final de la cadena.

Actividad 3 Ejecutad las siguientes acciones: Cread una cadena que contenga la frase "La lluvia en Sevilla es una maravilla". Buscad la primera y la ltima posicin en que aparece la subcadena "ll", y almacenadlas en dos variables de tipo entero. Obtened la subcadena que va desde la primera hasta la ltima posicin en la que aparece la subcadena "ll" (ambas posiciones incluidas). Cul es la subcadena resultado? Pasad la cadena del apartado anterior a maysculas.

Tipos referencia definidos por el usuario Existen, bsicamente, dos tipos referencia que se pueden definir: las clases y las interfaces. Una clase es una estructura de datos que contiene una serie de miembros, ya sean atributos o mtodos (acciones que se pueden realizar sobre una instancia de la clase). Veremos con ms detalle los tipos referencia definidos por el usuario en el apartado de orientacin a objetos. Son los siguientes: Clases. Los objetos son instancias concretas de una clase, con unos valores concretos para cada uno de los atributos que la componen. Para crear un objeto, debemos primero declararlo como en el resto de tipos de datos, y a continuacin inicializarlo, mediante la palabra clave new: MiClase mc = new MiClase (); Para inicializar una variable sin crear un objeto en concreto, utilizamos la palabra clave null:

FUOC P08/B0036/01627

22

C#

MiClase mc = null; La palabra null tambin se utiliza para eliminar la referencia que contiene la variable hacia el objeto: MiClase mc = new MiClase (); // instrucciones que utilizan mc mc = null; // utilizar mc a partir de esta // instruccin produce un error de ejecucin Interfaces. Las interfaces son como patrones o plantillas de comportamiento para las clases. Una clase que cumpla una determinada interfaz debe implementar todas las funcionalidades que se indican en sta.

1.3.10. Tipos parciales Un tipo parcial no es ms que un tipo definido por el usuario (bsicamente estructuras, clases o interfaces), cuya definicin se extiende a lo largo de ms de un fichero de cdigo. Esta separacin de un tipo en partes facilita el desarrollo en equipo (cada miembro del equipo trabaja con una parte) y el mantenimiento (las actualizaciones se almacenan en una nueva parte). En Visual Studio, se utilizan tipos parciales para separar el cdigo que generan los diferentes diseadores (WinForms, WebForms, DataSets, etc.), del cdigo que escribe el usuario. Para declarar que un tipo es parcial, se indica con la palabra clave partial:
public partial class nombreClase { // cdigo 1 } public partial class nombreClase { // cdigo 2 }

Una vez compilado, el cdigo resultante es el mismo que si se hubiera escrito el siguiente cdigo:
public class nombreClase { // cdigo 1

// cdigo 2 }

FUOC P08/B0036/01627

23

C#

Todas las partes de un tipo parcial se deben compilar a la vez, es decir, no se puede aadir una nueva parte a un tipo ya compilado. 1.3.11. Tipos anulables (nullable types) Los tipos valor, aunque son objetos y heredan de la clase object como ya hemos dicho, no admiten el valor nulo (null). Esto generalmente no es un problema, aunque en determinadas ocasiones vendra bien asignar a una variable un valor que no fuera ninguno de los del rango posible, para indicar que ha habido un error, o simplemente que no hay resultado. Los tipos anulables (nullable types), son versiones anulables de los tipos valor ya existentes. Estos tipos se definen escribiendo el smbolo ? detrs del nombre del tipo original, por ejemplo: int? i = null; Estos tipos tienen un mtodo llamado HasValue de tipo booleano, que indica si el valor de la variable es nulo o no, y la propiedad Value que devuelve el valor. Si HasValue es cierto, la propiedad Value devuelve el valor contenido, si HasValue es falso, acceder a la propiedad Value produce una excepcin. Por otro lado, los tipos anulables son convertibles a sus correspondientes versiones no anulables y viceversa. La conversin de un tipo no anulable a un tipo anulable es implcita. La conversin de una variable anulable a una no anulable debe hacerse explcitamente (mediante la operacin de cast) y puede producir una excepcin. Las conversiones entre tipos de diferente tamao se propagan a los tipos anulables, es ms, se pueden dar combinaciones diferentes de origen anulable o no y destino anulable o no, siempre teniendo en cuenta que la conversin de anulable a no anulable es implcita, pero la inversa no. Los operadores de los tipos no anulables son tambin aplicables a los tipos anulables. Los operadores aritmticos devuelven el valor nulo si alguno de los parmetros es nulo, independientemente del valor del resto de valores. Los operadores de comparacin devuelven falso si alguno de los parmetros es nulo.
Ejemplo Por ejemplo x<y si y es nulo el resultado es falso, pero no quiere decir que x sea menor que y, ya que no son comparables.

Los tipos anulables tienen el operador adicional ??, que permite convertir implcitamente un valor anulable en uno no anulable, indicando el valor que se debe asignar en caso de que el tipo anulable tenga valor nulo:

FUOC P08/B0036/01627

24

C#

int? i = null; int j = i ?? -1; En tipo anulable booleano bool?, pasa a tener tres valores posibles: true, false y null. Las operaciones booleanas se modifican para adaptarse al nuevo valor de la siguiente forma: &&. Si alguno de los parmetros es false, el resultado es false. Si no, si alguno de los parmetros es null, el resultado es null. Si no, el resultado es true (los dos parmetros son true). ||. Si alguno de los parmetros es true, el resultado es true. Si no, si alguno de los parmetros es null, el resultado es null. Si no, el resultado es false (los dos parmetros son false).

1.3.12. Arrays Los arrays o vectores son estructuras de datos que contienen una lista de elementos de otros tipos de datos, de forma que podemos acceder a esos elementos indicando la posicin concreta dentro del array. Para declarar un array utilizamos la siguiente sintaxis: tipo [] var; Y para inicializar el array, utilizamos la instruccin new: var = new tipo [N] N es el nmero de posiciones o elementos que podr almacenar el array. Por ejemplo, para declarar e inicializar un array de 4 posiciones de tipo string, utilizamos la siguiente instruccin: string [] stringsArray = new string [4]; Para acceder a los elementos de un array utilizamos los smbolos [ y ], indicando la posicin que queremos consultar o a la que queremos asignar un valor: // asignacin de un valor en la posicin 0 stringsArray[0] = "Otro string"; // consulta del valor de la posicin 2 string s = stringsArray[2]; Como se puede ver en el ejemplo, las posiciones de un array se numeran del 0 a N-1 (N es el nmero de posiciones del array). Intentar acceder a una posicin del array que no existe, produce un error. Para saber cul es el nmero de

FUOC P08/B0036/01627

25

C#

elementos, las variables array contienen la propiedad Length. Esta propiedad es til, por ejemplo, a la hora de realizar un recorrido sobre los elementos de un array:
for (int i=0;i<stringsArray.Length;i++) { Console.WriteLine (stringsArray[i]); }

Es importante tener en cuenta que un array no es ms que un contenedor de otros objetos. Por eso, la instruccin de inicializacin de un array solamente inicializa las posiciones en las que se ubicarn dichos objetos, pero no los objetos en s. Por ejemplo, la variable stringsArray del ejemplo anterior contiene un array de tipo string de 4 posiciones, pero cada una de esas posiciones tendr valor null (ya que no estn inicializadas). Para inicializar una posicin es necesario asignarle un valor, por ejemplo: stringsArray [0] = "string1"; Otra posibilidad es inicializar directamente los elementos de un array en la misma instruccin de inicializacin, como se muestra en el siguiente ejemplo: string [] stringsArray = { "string1", "string2", "string3", "string4" }; No obstante, hay una excepcin en la que los elementos del array s que se inicializan en la instruccin de creacin del mismo, y es en el caso de arrays de tipos valor, ya que los tipos valor no pueden ser null (excepto los tipos anulables). En este caso, las posiciones del array se inicializan con el valor por defecto del tipo de dato, por ejemplo en un array de int, todas las posiciones se inicializaran a 0 (en el caso de int? se inicializaran a null ). Arrays multidimensionales Los arrays multidimensionales, tal y como indica su nombre, son arrays de ms de una dimensin. Cada dimensin del array se puede entender como un array de arrays de dimensin menor, es decir, un array de N dimensiones con N > 1, es un array en que cada posicin contiene a su vez otro array de N-1 dimensiones. Para localizar una posicin de un array multidimensional, es necesario, por lo tanto, especificar N ndices, siendo N el nmero de dimensiones del array. Existen dos tipos de arrays multidimensionales: rectangulares y ortogonales.

FUOC P08/B0036/01627

26

C#

Los arrays multidimensionales rectangulares tienen un nmero fijo de posiciones en cada dimensin. Por ejemplo, un array rectangular de 2 dimensiones equivaldra a una matriz de N x M con N filas de M columnas cada una. Podemos declarar un array de este tipo de la siguiente forma: string [,] stringsArray = new string [10, 4]; string [,,] stringsArray2 = new string [10, 10, 10]; En la declaracin del array se indica el nmero de dimensiones (el nmero de espacios entre comas), y en la inicializacin se indica el nmero de posiciones concretas de cada dimensin entre comas. Tambin podemos inicializar los elementos del array al crearlo, el siguiente ejemplo crea e inicializa un array de 4x2:
string [,] stringsArray = { { "string11", "string12" } , { "string21", "string22" } , { "string31", "string32" } , { "string41", "string42" } };

Para acceder a una posicin del array, se utiliza la misma notacin: // accede a la posicin 3,2 del array stringsArray[3,2] = "Otro string"; Los arrays multidimensionales ortogonales a diferencia de los rectangulares, pueden tener un nmero diferente de columnas para cada fila. La declaracin de este otro tipo de arrays se realiza de esta forma: string [][] stringsArray; string [][][] stringsArray2; Dado que cada fila puede tener un nmero diferente de columnas, no se pueden inicializar todas sus dimensiones inicialmente, tan slo la primera: stringsArray = new string [3][]; La inicializacin de cada una de las otras dimensiones se realiza especficamente para cada una de las posiciones de la dimensin anterior, por ejemplo: stringsArray[0] = new string [5]; stringsArray[1] = new string [6]; stringsArray[2] = new string [4]; El acceso a una posicin concreta se realiza de forma similar, siguiendo esta notacin:

FUOC P08/B0036/01627

27

C#

// accede a la posicin 3,2 del array stringsArray[3][2] = "Otro string";

Actividad 4 Ejecutad las siguientes acciones: Declarad un array de tipo Producto. Cread e inicializad los elementos de un array de 4 posiciones de tipo Meses en una misma lnea. Cread una matriz de enteros de 4 X 4 e inicializad los valores de cada posicin, uno a uno, con los nmeros del 1 al 16.

1.3.13. Conversiones entre tipos de datos Algunos tipos de datos son compatibles entre s, como por ejemplo los tipos numricos. Por ello, es posible realizar conversiones de valores de un tipo a otro. A continuacin, veremos cmo hacerlo: Conversiones implcitas. Si los tipos de datos son compatibles, y en la conversin no es posible perder informacin, la conversin se realiza de forma implcita: int i = 3; long l = i; La conversin implcita es posible si se pasa de un tipo a otro con un rango de valores posibles mayor, de forma que no se pierdan datos en la conversin. Conversin explicita (cast). En cualquier otro caso, es necesario especificar que queremos forzar la conversin, aunque ello pueda comportar, en segn qu casos, una prdida de informacin. Para ello, utilizamos la operacin de cast: long l = 3000000000; int i = (int)l; Las instrucciones anteriores se ejecutarn y no darn ningn error, pero el resultado no ser correcto, ya que ese nmero no ser representable en una variable de tipo int. Para que el programa detecte el error en tiempo de ejecucin y lance un error, podemos utilizar la instruccin checked: long l = 3000000000; int i = checked((int)l); Si queremos extender esta comprobacin a un conjunto de instrucciones, podemos utilizar la instruccin checked en forma de bloque:
long l = 3000000000; checked

FUOC P08/B0036/01627

28

C#

{ int i = (int)l; int j = (int)(l*2); }

La operacin de cast es tambin vlida para convertir variables de tipos referencia, por ejemplo: string s = "Hola"; object o = (object)s; Esta conversin es vlida porque todos los tipos de datos (incluida la clase string, son a su vez subclases de object. Boxing/unboxing. Los tipos valor tambin descienden de la clase object, como ya dijimos anteriormente. Por ello, es posible convertir un tipo valor a su correspondiente tipo referencia, y viceversa, mediante las operaciones de boxing y unboxing respectivamente. Para realizar el boxing, convertimos el tipo valor a tipo referencia, asignndolo a una variable de tipo object: int i = 3; object o = i; Para realizar el unboxing, es necesario hacer un cast de la variable que contiene el tipo referencia, al tipo valor correspondiente: int j = (int)o;

1.3.14. Tipos y mtodos genricos Los tipos genricos son tipos de datos que utilizan en su definicin tipos de dato comodn, es decir, que pueden utilizarse como uno u otro tipo de dato segn convenga. Por ejemplo, el siguiente fragmento de cdigo define la clase genrica Par, con dos elementos de un mismo tipo, representado por el identificador T:
public class Par<T> { T e1; T e2;

public T E1 { get { return e1; } set { e1 = value; } } public T E2 { get { return e2; } set { e2 = value; }

FUOC P08/B0036/01627

29

C#

} }

Como se puede ver en el ejemplo anterior, el tipo genrico Par tiene un parmetro que es el tipo T que se utiliza dentro de su definicin. En este caso, slo hay un parmetro, pero podra haber ms de uno, por ejemplo, el mismo tipo Par pero con elementos de tipo distinto se puede definir de la siguiente forma:
public class Par2 { T e1; S e2;

public T E1 { get { return e1; } set { e1 = value; } }

public S E2 { get { return e2; } set { e2 = value; } } }

La eleccin del tipo de dato que, finalmente, tendr cada parmetro del tipo genrico se especifica a la hora de instanciar, por ejemplo, para crear una instancia de la clase genrica Par2 que contenga elementos de tipo entero y string respectivamente, utilizamos la siguiente instruccin de inicializacin: Par2<int, string> par_int_str = new Par2<int, string>(); Tras la ejecucin de la anterior inicializacin, el tipo de dato de par_enteros ser equivalente al siguiente tipo de dato, resultante de substituir T y S por int y string:
public class Par2_int_string { int e1; string e2;

public int E1 { get { return e1; } set { e1 = value; } }

FUOC P08/B0036/01627

30

C#

public string E2 { get { return e2; } set { e2 = value; } } }

Los mtodos genricos son similares a los tipos genricos, ya que tambin utilizan parmetros de tipo para crear algoritmos o fragmentos de cdigo independientes del tipo de dato con el que trabajan (por ejemplo un algoritmo de bsqueda u ordenacin). El siguiente ejemplo muestra la sintaxis concreta:
public static bool Equals <T>(T t1, T t2) { return t1.Equals(t2); }

De nuevo, vemos en el ejemplo cmo el mtodo genrico tiene un parmetro que es el tipo que se va a utilizar en su definicin. Al invocar el mtodo debemos especificar el tipo de dato concreto que se va a utilizar, de forma similar a cuando se inicializa un tipo genrico: bool b = Equals<int>(i, 5); Los parmetros de tipo tambin se pueden utilizar para declarar campos, variables o parmetros de tipo array, por ejemplo:
public static T[] QuickSort <T> (T[] t1, T[] t2) { ... }

Es importante tener en cuenta que utilizando parmetros de tipo no es necesario realizar conversiones implcitas de tipos a la hora de pasar los parmetros o de recuperar los resultados. Sin parmetros de tipo el anterior mtodo QuickSort se habra definido de la siguiente forma:
public static object[] QuickSort(object[] t1, object[] t2) { ... }

Y una llamada a dicho mtodo para un array de enteros se realizara de la siguiente forma, con la correspondiente conversin de tipos (cast):

FUOC P08/B0036/01627

31

C#

int[] ordered = (int[])QuickSort (array1, array2); Mientras que la llamada al mtodo QuickSort genrico sera: int[] ordered = QuickSort<int[]>(array1, array2); Por ltimo, hay determinados casos en los que los parmetros de tipo de los tipos y mtodos genricos no pueden ser intercambiados con cualquier tipo de dato. Un ejemplo es el mtodo QuickSort utilizado en los ejemplos anteriores, en el que se requiere que los elementos de los arrays que se pasan como parmetro permitan compararse entre s para poder realizar la ordenacin. Este requisito se puede imponer obligando a que el tipo de dato utilizado herede la interfaz IComparable, que contiene el mtodo CompareTo que permite comparar elementos entre s. La definicin correcta del mtodo QuickSort genrico seria por lo tanto la siguiente:
public T[] QuickSort<T>(T[] t1, T[] t2) where T : IComparable { ... }

Actividad 5 Ejecutad las siguientes acciones: Cread el tipo genrico Par con dos elementos de tipos diferentes. Cread diferentes instancias de dicho tipo, con diferentes tipos de datos para cada elemento. Cread el mtodo genrico Comparar que compare los elementos de dos arrays de tipo genrico y devuelva un array de enteros con los resultados de cada comparacin.

1.4. Instrucciones del lenguaje A continuacin, veremos las diferentes instrucciones del lenguaje C# y su correspondiente sintaxis. 1.4.1. Sentencias de decisin

Las sentencias de decisin, tambin llamadas sentencias condicionales o de control de flujo, permiten escoger el cdigo a ejecutar en un momento dado dependiendo de una condicin booleana.

FUOC P08/B0036/01627

32

C#

Existen dos sentencias o instrucciones de decisin: la instruccin if, y la instruccin switch. If. La sintaxis general de la instruccin if es la siguiente:
if (condicion) { // Sentencias a ejecutar si la condicin es cierta } else { // Sentencias a ejecutar si la condicin es falsa }

La condicin expresada entre parntesis debe ser una expresin booleana. Si esta expresin es cierta, se ejecuta el primer bloque de instrucciones. En cambio, si es falsa, se ejecuta el segundo bloque. Si alguno de los bloques de sentencias a ejecutar est compuesto por una nica instruccin, se pueden omitir las llaves, aunque es recomendable escribirlas siempre para mayor claridad del cdigo. El bloque de sentencias a ejecutar en caso de que la condicin sea falsa (else) es opcional. Si no se define un bloque else, en caso de que la condicin sea falsa, no se ejecuta ninguna sentencia, y se contina la ejecucin en la siguiente instruccin despus del if. Por otro lado, podemos aadir ms comprobaciones a la instruccin if en caso de que la primera sea falsa, mediante los bloques else if:
if (condicion1) { // Sentencias a ejecutar si la condicion1 es cierta } else if (condicion2) { // Sentencias a ejecutar si la condicion1 es falsa y la condicion2 es cierta } ... else if (condicionN) { // Sentencias a ejecutar si las N-1 condiciones anteriores son falsas // y la condicinN es cierta } else { // Sentencias a ejecutar si todas las condiciones son falsas

FUOC P08/B0036/01627

33

C#

Solamente se ejecutar uno de los bloques. En caso de que haya ms de una condicin cierta, solamente se ejecuta el bloque de la que est definida antes. Se pueden encadenar tantos else if como sea necesario, pero slo puede haber un bloque else (opcional), y siempre al final de la instruccin if. switch. La instruccin switch es una forma especfica de instruccin condicional, en la que se evala una variable, y en funcin de su valor, se ejecuta un bloque u otro de instrucciones. La sintaxis general de la instruccin switch es la siguiente:
switch (var) { case 1: // sentencias a ejecutar en caso de que la variable var tenga valor 1 break;

case 2:

// sentencias a ejecutar en caso de que la variable var tenga valor 2 break;

... default: // sentencias a ejecutar en caso de que la variable var tenga un valor // diferente a los que se reflejan en los casos anteriores break; }

Cada bloque o caso (case) de una sentencia switch define las sentencias o instrucciones que deben ejecutarse en caso de que la variable var del switch tenga el valor especificado en ese case. Los valores especificados despus de la palabra clave case deben ser valores o expresiones constantes, de tipos integrales4 (sbyte, byte, short, ushort, int, uint, long, ulong y cha), cadenas de caracteres (string) o enumeraciones, no se pueden utilizar variables. No se pueden repetir valores en los bloques case, por ejemplo, la siguiente instruccin switch es incorrecta, ya que los tres primeros case tienen valor 2:
switch (var) { case 2: // bloque A break; case 3-1: // bloque B break; case 4/2: // bloque B break; default: // bloque C break; }

(4)

Encontrares una definicin de tipos integrales buscando el trmino 'tipo integral' en la ayuda de Visual Studio.

FUOC P08/B0036/01627

34

C#

El caso default es parecido al bloque else, define el bloque de instrucciones que se ejecuta si el valor de la variable var no coincide con ninguno de los valores definidos en los diferentes bloques case, y tambin es opcional. La diferencia es que este bloque se puede definir en cualquier punto de la instruccin switch, ya que el orden de los casos no importa. La palabra clave break indica que el bloque de sentencias de un caso ha acabado, y traslada la ejecucin a la siguiente instruccin despus de la instruccin switch. Si omitiramos la instruccin break, se seguiran ejecutando las instrucciones del siguiente bloque case, hasta encontrar un break o el final de la instruccin switch. Por ejemplo:
switch (var) { case 1: case 2: // bloque A // bloque B break; default: // bloque C break; }

Si la variable var tiene valor 1 se ejecutar el bloque de sentencias A y el bloque de sentencias B, ya que despus del primero no hay ninguna instruccin break. Esto es til tambin para definir un mismo bloque de instrucciones para varios casos, por ejemplo:
switch (var) { case 1: case 2: case 3: // bloque A break; case 4: case 5: // bloque B break; default: // bloque C break; }

En este ejemplo, el bloque A se ejecuta si var tiene valor 1, 2 o 3, el bloque B si tiene valor 4 o 5, y el bloque C en cualquier otro caso.

FUOC P08/B0036/01627

35

C#

Actividad 6 Ejecutad las siguientes acciones: Escribid una instruccin condicional que en funcin del precio de un Producto p, escriba por pantalla "Barato" (de 0 a 1 ), "Normal" (de 1 a 4), o caro (ms de 4). En caso de que sea negativo, escribid por pantalla "Error". Escribid una instruccin condicional que en funcin del valor de una variable m de tipo Meses muestra por pantalla el nombre del mes correspondiente.

1.4.2.

Sentencias de iteracin

Las sentencias de iteracin, o bucles, permiten repetir una serie de instrucciones mientras se cumpla una determinada condicin. Cuando la condicin es falsa, el bucle termina y la ejecucin contina en la siguiente instruccin despus del bucle. En C# existen 4 instrucciones de iteracin diferentes: while, for, do while y foreach 1)for. La sintaxis de la instruccin for es la siguiente:
for (inicializacion; condicion; iterador) { // bloque de sentencias }

La inicializacin es una instruccin que se ejecuta una nica vez al principio de la instruccin for. Se utiliza generalmente para inicializar la variable de control que indica cuando ha de finalizar el bucle. El iterador es una instruccin que se ejecuta al final de cada iteracin, y que modifica el valor de la variable de control, de forma que en un momento dado la condicin del bucle sea falsa y ste finalice. En otro caso, si la condicin nunca fuese falsa, se creara un bucle infinito. La condicin de la instruccin for es una condicin booleana que se evala a cada iteracin del bucle. Si la condicin es cierta, se ejecuta el bloque de sentencias del for, despus se ejecuta la instruccin de iteracin, y a continuacin se vuelve a evaluar la condicin. Si la condicin es falsa, se contina la ejecucin en la instruccin siguiente despus del for. El siguiente ejemplo ejecuta el bloque A de instrucciones 10 veces:
for (int i = 0; i<10; i++) {

FUOC P08/B0036/01627

36

C#

// bloque A }

2) while. La instruccin while se diferencia de la instruccin for en que solamente tiene una condicin de finalizacin. La inicializacin e iteracin se deben realizar justo antes del while y dentro del while respectivamente. La sintaxis concreta es la siguiente:
while (condicion) { // bloque de instrucciones a ejecutar }

Mientras la condicin sea cierta, se ejecutar el bloque de instrucciones. Por eso, es necesario que haya alguna instruccin dentro del bloque que modifique alguna de las variables que intervienen en la condicin, de forma que, en un momento dado, sta pase a ser falsa y, por consiguiente, finalice el bucle. El siguiente ejemplo es equivalente al ejemplo del for:
int i = 0;

while (i<10) { // bloque A i++; }

3)do-while. As como en las instrucciones for y while, primero se comprobaba la condicin y despus se ejecutaba el bucle, en la instruccin do-while es al contrario. Su sintaxis es la siguiente:
do { // bloque de sentencias a ejecutar } while (condicin);

La diferencia fundamental es que, mientras que en un bucle for o while, si la condicin es falsa de entrada, no se ejecuta ninguna iteracin, en un bucle do-while siempre se ejecuta como mnimo una iteracin. 4)foreach. La instruccin foreach es un tipo de bucle especfico para colecciones de objetos. Permite recorrer todos los elementos de una coleccin desde el primero al ltimo. La sintaxis concreta es la siguiente:
foreach (tipo var in col) {

FUOC P08/B0036/01627

37

C#

// bloque de sentencias a ejecutar }

Donde tipo es el tipo de dato de los elementos que hay en la coleccin, var es una variable que representa en cada iteracin el elemento actual de la coleccin, y col es la variable que almacena la coleccin. Una coleccin no es ms que un tipo de dato que implementa la interfaz IEnumerable. Esta interfaz define el mtodo GetEnumerator, que devuelve una instancia de la interfaz IEnumerator, que a su vez define la propiedad Current, y los mtodos MoveNext y Reset, que se utilizan para recorrer los elementos de una secuencia. Un ejemplo de coleccin son los arrays:
int [] nums = new int [] { 4,2,5,7,3,7,8 };

foreach (int i in nums) { ... }

Tambin es posible crear tipos de datos que se puedan utilizar dentro de un bucle foreach, utilizando iteradores. Los iteradores son sentencias que permiten implementar el mtodo GetEnumerator fcilmente, indicando que valores se deben devolver en cada iteracin de la instruccin foreach. Por ejemplo, supongamos que tenemos una clase que contiene un array de enteros a, y que queremos que las variables de esa clase se puedan utilizar dentro de un foreach. En primer lugar, indicamos que la clase va a implementar la interfaz IEnumerable:

public class Clase : IEnumerable { int [] a; ... }

A continuacin, implementamos el mtodo GetEnumerator del siguiente modo:


public void IEnumerator GetEnumerator() { for (int i = 0;i<a.Length;i++) { yield return a[i]; } }

Entonces, podemos utilizar una variable de la clase en una instruccin foreach, como si de una coleccin se tratase:

FUOC P08/B0036/01627

38

C#

Clase c = new Clase ();

foreach (int i in c) { Console.WriteLine(i); }

Los iteradores tambin son aplicables a mtodos. En vez de implementar el mtodo GetEnumerator, podramos haber utilizado un mtodo auxiliar con tipo de retorno IEnumerable:
public static IEnumerable GetNumbers() { for (int i = 0;i<a.Length;i++) { yield return a[i]; } }

Recurso web En la siguiente pgina web, se puede encontrar ms informacin acerca de los iteradores: <http://msdn.microsoft.com/ en-us/library/dscyy5s0.aspx>

En este caso, no podremos utilizar una variable de la clase en el foreach, sino el resultado del mtodo, es decir:
foreach (int i in P.GetNumbers()) { Console.WriteLine(i); }

FUOC P08/B0036/01627

39

C#

Actividad 7 Ejecutad las siguientes acciones: Cread un bucle que realice la suma de dos matrices de enteros a1 y a2. Sabemos que las dos matrices tienen el mismo nmero de filas y de columnas, pero no sabemos cuntas exactamente. El resultado debe de almacenarse en una tercera matriz a, que hay que declarar e inicializar. Inicializad una variable i a 0, mostrad por pantalla el valor de i e incrementad i en uno, mientras i sea menor que N (N>=0). Mostrad todas las formas posibles de implementar este bucle. Buscad todas las posiciones en las que se encuentra la subcadena "ll" dentro de la cadena "La lluvia en Sevilla es una maravilla", y mostradlas por pantalla. No se puede modificar la cadena, ni utilizar variables string auxiliares. Mostrar todas las formas posibles de implementar este bucle.

1.4.3.

Sentencias de salto

Las instrucciones de salto permiten pasar o 'saltar' inmediatamente desde una instruccin a otra lnea del programa no necesariamente a continuacin de la lnea actual. Las instrucciones de salto de C# son las siguientes: Break. La instruccin break se utiliza dentro de instrucciones de bucle y en la instruccin switch. Si dentro de un bucle o de un switch encontramos una instruccin break, la ejecucin 'sale' inmediatamente del bucle o del switch, y contina en la siguiente lnea despus de la instruccin en la que se encontraba el break. Continue. La instruccin continue es similar al break, pero se utiliza slo dentro de instrucciones de bucle. Al encontrar un continue dentro de un bucle, finaliza la ejecucin de la iteracin actual del bucle, y se contina en la siguiente. Hay que tener un especial cuidado con esto, ya que puede provocar bucles infinitos, por ejemplo, en el siguiente bucle la instruccin continue se encuentra justo antes de la instruccin que modifica la variable de control, por lo que la variable i siempre valdr 10 y el bucle nunca terminar:
int i = 10;

while (i>0) { // instrucciones del bucle continue;

FUOC P08/B0036/01627

40

C#

i--; }

Return. La instruccin return es la instruccin de salto que se utiliza para abandonar un mtodo y volver a la instruccin siguiente a la que llam a ese mtodo. Si el mtodo devuelve valores, la instruccin return ir acompaada del valor que se ha de devolver. En caso contrario, la instruccin return es opcional (se realiza implcitamente). Hablaremos sobre mtodos ms adelante.

FUOC P08/B0036/01627

41

C#

2. Orientacin a objetos en C#

C# es un lenguaje orientado a objetos puros, es decir, todas sus caractersticas son orientadas a objetos. La orientacin a objetos es una tcnica de programacin que consiste en tratar cada elemento del programa como un objeto, e intentar abstraer las caractersticas de cada objeto para clasificarlos en diferentes familias o clases. El desarrollo de una aplicacin orientada a objetos consiste en definir un conjunto de clases de objetos, especificando las caractersticas comunes que tienen todos los elementos u objetos que pertenecen a esa clase, as como las acciones que se pueden realizar sobre un objeto concreto de la clase. Para conseguir el objetivo de la aplicacin a desarrollar, los objetos interaccionan entre s, pasndose informacin. Cada objeto tiene su funcionalidad y sabe a qu objeto tiene que pedir cierta informacin necesaria para realizar sus funciones. A continuacin, veremos los diferentes conceptos de orientacin a objetos y su utilizacin en C#. 2.1. Definicin de clases En C# podemos definir una clase mediante la palabra clave class, seguido del nombre de la clase:
class nombre { // definicion de la clase }

Todas las instrucciones contenidas dentro de las llaves {} pertenecen a la definicin de la clase. Dentro de esa definicin, podemos encontrar dos tipos de elementos o miembros: miembros de datos y miembros de funcin. Los miembros de datos son aquellos que describen el estado de un objeto de la clase, mientras que los miembros de funcin son los que definen su comportamiento y funcionalidad. Los miembros de una clase definen las caractersticas de los objetos o instancias de la clase. Cada instancia tendr asignados diferentes valores para los miembros de datos, y, por consiguiente, los miembros de funcin tendrn un resultado distinto dependiendo de en qu instancia de la clase se ejecuten.

FUOC P08/B0036/01627

42

C#

Existe un tipo especial de miembros, los miembros estticos, que se comportan igual para todas las instancias de la clase, porque actan globalmente, a nivel de clase en vez de a nivel de instancia como el resto de miembros. Para indicar que un miembro es esttico, se aade el modificador static en la definicin del miembro, como veremos para cada caso concreto.

Actividad 8 Ejecutad las siguientes acciones: Cread la clase Coche.

2.2. Instancin de clases La declaracin de variables de una clase es idntica a la declaracin de variables de cualquier otro tipo de dato, por ejemplo: MiClase mc; La inicializacin de una variable de clase implica la creacin de un espacio en el heap para los datos de la nueva instancia, y la asignacin de la referencia correspondiente a la variable. Esta operacin se realiza mediante la instruccin new: mc = new MiClase (); Y como en el resto de tipos, se puede unir la definicin y la inicializacin en una nica lnea: MiClase mc = new MiClase (); El mtodo que se utiliza despus de la palabra clave new, que tiene el mismo nombre que la clase, se denomina mtodo constructor. Hablaremos de los constructores de la clase ms adelante. El valor por defecto de una variable es el valor nulo, es decir, la variable no est asignada a ningn objeto. El valor nulo se representa mediante la palabra clave null. El valor nulo sirve para inicializar una variable a la que, de momento, no queremos asignar un objeto (no podemos dejarla inicializada porque se produce un error de compilacin): MiClase mc = null;

FUOC P08/B0036/01627

43

C#

Otra utilidad de null es 'eliminar' la referencia contenida en la variable, por ejemplo: MiClase mc = new MiClase (); mc = null; Despus de la ltima instruccin, la variable mc no se puede volver a utilizar para acceder al objeto creado anteriormente. De todas formas, hay que tener en cuenta que esto no elimina el objeto que se encuentra en el heap. El encargado de eliminar el objeto es el Garbage Collector, que detecta aquellos objetos situados en el heap que han dejado de ser referenciados por alguna variable, y que por lo tanto pueden ser eliminados sin provocar problemas. Hay que tener claro, por lo tanto, que una cosa es el objeto que reside en el heap, y otra la variable que contiene una referencia a ese objeto. Las variables no tienen por qu estar siempre relacionadas con el mismo objeto en memoria. Ni un objeto tiene por qu estar relacionado siempre con la misma variable, ni con una sola. Por ejemplo: MiClase mc = new MiClase (); MiClase mc1 = mc; La variable mc1 referencia exactamente al mismo objeto que mc, no se crea un nuevo objeto, por lo que si se modifica el objeto utilizando la variable mc1, al acceder con la variable mc observaremos los cambios realizados. Otra cosa a tener en cuenta de los objetos y las variables es que dos objetos no se pueden comparar, comparando simplemente las variables, ya que lo que se comparan son las referencias. Si la referencia es la misma (el mismo objeto), son iguales, sino no. Por ejemplo:
MiClase mc1 = new MiClase (); MiClase mc2 = m1; MiClase mc3 = new MiClase ();

if (mc1==mc3) { // estas instrucciones no se ejecutan nunca, porque // mc1 y mc3 referencian dos objetos diferentes, // independientemente de que entre ellos sean // equivalentes. }

if (mc1==mc2) { // estas instrucciones se ejecutan siempre, porque

FUOC P08/B0036/01627

44

C#

// mc1 y mc2 contienen la misma referencia. }

Existe una excepcin de tipo por referencia que s se puede comparar con la operacin de comparacin, como siempre el tipo string. El resto de tipos referencia deben de utilizar el mecanismo de comparacin para objetos, redefiniendo el mtodo Equals, definido en la clase Object, para que devuelva true si los dos objetos son equivalentes (aunque no sean la misma instancia).

Actividad 9 Ejecutad las siguientes acciones: Cread una instancia de la clase Coche.

2.3. Herencia Las clases pueden organizarse en jerarquas complejas que permiten extender y aprovechar las funcionalidades de unas clases en otras. Estas jerarquas se crean mediante la relacin de herencia, que relaciona dos clases: superclase y subclase (o clase padre y clase hija), de forma que la subclase hereda toda la funcionalidad de la superclase (atributos, mtodos, etc.). Cualquier objeto del tipo de la subclase es a la vez del tipo de la superclase, pero no a la inversa. Es decir, un objeto de tipo B (que es subclase de A), se puede convertir a tipo A de la siguiente forma: B varB = B(); A varA = (A)varB; Sin embargo, mediante la variable varA no es posible acceder a los miembros especficos del tipo B, ya que en esta variable el objeto est desempeando la funcin del tipo A (trabaja como si fuera directamente un objeto del tipo A). Todas las clases que descienden de una clase (aunque no sean clases hijas directamente), son subclases de esa clase. Todas las clases que hay que recorrer en la jerarqua de herencia desde la clase hasta la clase objec t, son superclases de esa clase. Para indicar que una clase hereda de otra, aadimos dos puntos detrs del nombre de la clase, y a continuacin el nombre de la superclase:
class nombre : superclase { }

FUOC P08/B0036/01627

45

C#

Si no se especifica alguna superclase, la clase heredar por defecto de la clase object. 2.3.1. Interfaces

En C#, y en .NET en general, una clase slo puede heredar de una nica clase (herencia simple). Existe otro tipo de herencia que permite que una clase pueda heredar de ms de una clase (herencia mltiple), pero este tipo de herencia conlleva una serie de complicaciones que hacen que su implementacin sea demasiado costosa. La solucin que ofrecen los lenguajes de .NET al problema de la herencia mltiple es la utilizacin de interfaces. Una interfaz es una definicin o plantilla de las funcionalidades (mtodos) que debe de implementar toda clase que herede de esa interfaz. As como no es posible heredar de ms de una clase, s que es posible heredar de ms de una interfaz. Una interfaz tiene una estructura similar a la de una clase:
interface nombre { }

Dentro de las llaves slo puede haber definiciones de mtodos. Cada definicin consiste en la cabecera del mtodo (visibilidad, tipo de retorno, nombre y parmetros), pero sin incluir una implementacin. Cada definicin finaliza con un punto y coma (;). Las interfaces tambin pueden heredar a su vez de otras interfaces (una o varias). De este modo, la clase que herede de una interfaz debe de implementar todos los mtodos definidos en esa interfaz y en todas las interfaces de las que esta hereda, recursivamente hasta llegar a la clase object (que aunque pueda parecer una contradiccin, es la clase de la que hereda cualquier interfaz si no se indica lo contrario).

Actividad 10 Ejecutad las siguientes acciones: Cread la subclase CocheGasolina de la clase Coche. Cread una interfaz llamada Vehiculo. Haced que la clase Coche herede de la interfaz Vehiculo.

FUOC P08/B0036/01627

46

C#

2.4. Miembros de datos Los miembros de datos son los siguientes: Campos (fields). Los campos o atributos de una clase son las caractersticas que definen a los objetos de esa clase. Contienen los datos que almacenan y con los que trabajan los objetos de esa clase. En cualquier punto de la definicin de la clase (excepto dentro de un miembro de funcin), se puede definir un campo como la declaracin de una variable e incluso definir su visibilidad, por ejemplo: int i; public string s; protected MiClase mc; Las variables i, s y mc a partir de este momento son variables globales dentro del mbito de la clase. Para acceder a los campos desde la propia clase, basta con escribir el nombre de la variable, ya que los campos son globales en todo el mbito de la clase. A veces, es necesario desambiguar entre una variable local con el mismo nombre y el propio campo. Para ello, existe la variable especial this, que representa el objeto actual. Usando this, podemos acceder a todos los campos de la propia clase:
int i; // variable local i = this.i; // asignamos el valor del campo i a la // variable local i

Para acceder a los campos pblicos desde fuera de la clase, hay que tener una variable que haga referencia a un objeto de la clase. Escribimos el nombre de la variable, seguida de un punto y del nombre del campo, suponiendo que la clase MiClase tiene un campo con nombre i: MiClase mc = MiClase (); mc.i = 4; Aparte de los campos que definen las caractersticas que diferencian los distintos objetos de una clase, existen campos que definen caractersticas comunes a todos los objetos de la clase. Estos campos se denominan campos estticos, y se definen aadiendo la palabra clave static entre la visibilidad y el tipo de dato: public static MiClase mc; Para acceder a los campos estticos desde la propia clase, podemos escribir el nombre de la variable como si se tratara de campos normales. Para acceder desde fuera de la clase a los campos estticos pblicos, utilizamos el nombre de la clase, seguido de un punto y del nombre de la variable. Suponiendo que la clase tiene un campo esttico de tipo string llamado s: string s = MiClase.s; Constantes. Las constantes son variables que contienen un valor que se establece en tiempo de compilacin y que no se puede modificar. Para

FUOC P08/B0036/01627

47

C#

declarar una constante utilizamos la palabra clave const, seguida de la definicin e inicializacin de la variable: const int N = 30; Otro mecanismo para definir constantes son las variables de slo lectura. Estas variables se pueden inicializar dentro del constructor de la clase (al crear un objeto de la clase), de forma que no tienen por qu tener un valor fijo constante asignado en tiempo de compilacin, sino que depende de algn parmetro en tiempo de ejecucin. Una vez inicializadas dentro del constructor, slo pueden ser consultadas, no asignarlas un valor. Para definir una variable de slo lectura, utilizamos la palabra clave readonly: readonly string saludo; Eventos. Los eventos son unos miembros especiales de la clase que permiten almacenar una lista de objetos interesados en un determinado evento. Cuando ese evento ocurre en el objeto, todos los objetos subscritos en el evento son avisados. Veremos con detalle la definicin y utilizacin de eventos en el apartado de conceptos avanzados.

Actividad 11 Ejecutad las siguientes acciones: Aadid el campo caballos de tipo entero a la clase Coche y el campo carburante de tipo string a la clase CocheGasolina. Comprobad que no se pueden aadir campos o constantes a la interfaz Vehiculo. Haced que el campo caballos de la clase Coche sea accesible desde la clase CocheGasolina. Comprobad que se puede acceder al campo caballos desde CocheGasolina, pero no se puede acceder al campo carburante desde la clase Coche.

2.5. Miembros de funcin Los miembros de funcin son los que definen el comportamiento de los objetos de una clase. A continuacin, veremos los diferentes tipos de miembros de funcin que se pueden definir y cul es su utilidad.

FUOC P08/B0036/01627

48

C#

2.5.1.

Mtodos

Los mtodos son los miembros de una clase que encapsulan su funcionalidad. Las aplicaciones orientadas a objetos se basan en la ejecucin de los mtodos adecuados de los objetos con los que trabaja, para obtener el resultado esperado. La definicin de un mtodo indica, aparte de su nombre, los parmetros que necesita para realizar su funcionalidad, y cul es el tipo de resultado que devuelve, si es el caso. La estructura general de definicin de un mtodo, tambin llamada signatura, es la siguiente:
visibilidad modificador tipo_retorno nombre (parmetros) { // instrucciones del mtodo return x; // instruccin que devuelve el valor de la variable // o expresin x como resultado del mtodo. // Si el tipo de retorno es void (no devuelve nada), // no hay que poner esta instruccin. }

Visibilidad. La visibilidad indica desde que puntos del programa se podr acceder a ese mtodo. Existen los siguientes tipos de visibilidad: Public. Se puede utilizar desde cualquier punto del programa. Prvate. Slo se puede utilizar dentro del tipo (clase o estructura) en la que est definido. Protected. Se puede ejecutar dentro del propio tipo o de sus subclases. Internal. Se puede ejecutar en cualquier clase dentro del mismo ensamblado. Protected internal. Combinacin de las dos anteriores.

Modificador. El modificador es una palabra clave opcional que identifica el tipo de mtodo: New. Esta definicin del mtodo oculta un mtodo heredado de la superclase, con la misma signatura. Static. El mtodo es un mtodo de clase. Los mtodos de clase no se ejecutan sobre una instancia en concreto de la clase. Virtual. El mtodo puede ser redefinido por alguna subclase. Abstract. El mtodo debe de de ser redefinido en alguna de las subclases. No se incluye implementacin, solo su signatura. Override. Esta definicin del mtodo sobrescribe un mtodo heredado de la superclase. Sealed. Prohbe que el mtodo pueda ser sobrescrito (override), por subclases de esta clase.

FUOC P08/B0036/01627

49

C#

Extern. El mtodo esta implementado externamente, en otro lenguaje distinto.

Tipo de retorno. El tipo de retorno es el tipo de dato del valor que se obtiene al ejecutar el mtodo. Si el mtodo slo realiza operaciones y no devuelve ningn valor, el tipo de retorno se indica como void.

Nombre del mtodo. El nombre del mtodo es el identificador mediante el cual se acceder a este mtodo, por lo que es importante que el nombre escogido permita comprender su funcionalidad fcilmente.

Utilizacin de un mtodo. Los mtodos en general necesitan una variable de la clase para ser ejecutados, a excepcin de los mtodos estticos, que veremos a continuacin, y de los abstractos, que no se pueden ejecutar. Para ejecutar o invocar un mtodo, escribimos el nombre de la variable, seguido de un punto y del nombre del mtodo. A continuacin, escribimos entre parntesis una lista de 0 a N variables o expresiones, que vayamos a pasar como parmetros, en total tantos como parmetros indique la definicin del mtodo que vamos a ejecutar, con el mismo tipo de dato, y en el mismo orden. El resultado de la ejecucin del mtodo (si tiene tipo de retorno), se puede asignar a una variable, utilizar dentro de una expresin o como parmetro de otro mtodo. Por ejemplo:
string s = "Hola"; string s1 = s.ToString(); s1 = s1 + s.SubString (1, 2); int i = s1.IndexOf (s.SubString (0, 2));

El mtodo ToString no tiene parmetros, por lo que no debemos especificar ninguno. El resultado lo asignamos a la variable s1. A continuacin, utilizamos el mtodo SubString dentro de una expresin de concatenacin. En este caso, indicamos indicar el ndice de inicio de la subcadena y la longitud. Es importante darse cuenta de que hay dos versiones del mtodo SubString: una recibe dos parmetros, pero hay otra que slo recibe uno, la posicin de inicio (devuelve la subcadena desde esta posicin hasta el final del string). Esto es porque, como veremos un poco ms adelante, se pueden escribir varias definiciones de un mismo mtodo, con diferentes tipos de parmetros (El mtodo en s hace lo mismo, pero hay diferentes formas de pasarle los parmetros como en el caso del SubString. En la tercera instruccin, estamos utilizando el resultado de la operacin SubString como parmetro del mtodo SubString que devuelve la primera posicin en la que aparece la subcadena indicada. Mtodos estticos. Por otro lado, tenemos los mtodos estticos (static) o mtodos de clase. Estos mtodos no se ejecutan mediante una variable concreta de la clase, y que son mtodos globales. Para ejecutarlos, es ne-

FUOC P08/B0036/01627

50

C#

cesario especificar el nombre de la clase, seguida de un punto, el nombre del mtodo y la lista de variables o expresiones a pasar como parmetro: String.CompareTo ("Hola", "Adios"); Dentro de la definicin de un mtodo esttico slo se pueden utilizar atributos estticos de la propia clase. No se puede acceder al resto de atributos, ya que no existe una instancia de la clase (el mtodo no se est ejecutando sobre una instancia, sino globalmente). Un ejemplo de mtodo esttico bastante claro es el mtodo Main, que es el mtodo principal que se ejecuta al iniciar el programa. Es un mtodo esttico porque no se ejecuta sobre ninguna instancia de clase, ya que al ser el primer mtodo que se ejecuta an no se ha creado ninguna. Parmetros. Los parmetros o argumentos del mtodo son una lista de 0 a N variables, locales al mtodo, que son necesarias para la ejecucin del mismo. En el momento de ejecucin del mtodo, estos parmetros reciben unos valores determinados mediante el mecanismo de paso de parmetros. Los parmetros se pasan siempre por valor, es decir, se copia el valor de la variable o expresin especificadas en la llamada al mtodo, en la variable local representada por el parmetro. De ese modo, si se modifica el valor de las variables de los parmetros, no se modifica el valor de las variables que se pasaron como parmetro. No obstante, debido a que las variables de los tipos referencia slo contienen la referencia a la direccin de memoria donde estn los datos del tipo, al pasarlo como parmetro, lo que se pasa es simplemente la direccin y por lo tanto si se accede al tipo dentro del mtodo mediante la variable del parmetro, se est modificando directamente el tipo que se pas como parmetro. Por ejemplo:
int i = 0; int [] a = { 0, 1, 2, 3 }; El string como parmetro El string es una excepcin, ya que al pasar un string como parmetro se copia la cadena, es decir, se comporta como un tipo de valores.

PasoDeParmetros (i, a);

public void PasoDeParmetros (int j, int [] b) { j = 3; b[0] = 3; }

Una vez ejecutado el mtodo, la variable i seguir teniendo valor 0, mientras que la posicin 0 del vector a tendr valor 3, en vez de 0 como al principio.

FUOC P08/B0036/01627

51

C#

Tambin es posible pasar por referencia las variables de tipos por valor, de forma que se pueda modificar su valor dentro del mtodo. Para ello, es necesario aadir la palabra clave ref delante del parmetro en la definicin del mtodo, y delante de la variable (no es vlido con expresiones) en la llamada al mtodo. En el ejemplo siguiente, despus de la ejecucin del mtodo, la variable i tendr valor 3:
int i = 0;

PasoDeParmetros (ref i);

public void PasoDeParmetros (ref int j) { j = 3; }

Retorno. Los mtodos que especifican un tipo de retorno diferente a void, deben especificar como ultima instruccin cul es el valor que se debe devolver. Para ello utilizamos la instruccin return, seguida de la variable o expresin cuyo valor queremos que se devuelva:
public int Retorno1 () { return 3; }

public int Retorno2 (int i) { return i * 2; }

Este mecanismo slo permite devolver un nico valor de un mtodo. Si es necesario devolver ms de un valor, podemos utilizar parmetros de salida. Los parmetros de salida se definen aadiendo la palabra clave out delante de la definicin del parmetro en la signatura del mtodo, y antes de la variable que se va a utilizar para recuperar el valor en la llamada del mtodo:
int i;

Parametrosalida (out i);

public void Parametrosalida (out int j) { j = 3; }

As como los parmetros normales deben estar inicializados antes de ser pasados como parmetros (de otro modo se produce un error de compila-

FUOC P08/B0036/01627

52

C#

cin), los parmetros de salida no requieren que la variable que se pasa este inicializada. Sobrecarga de mtodos. A veces, un mismo mtodo puede ser invocado con un nmero de parmetros distinto, ya sea porque algunos parmetros son opcionales o porque hay varias formas de proporcionar la informacin necesaria para ejecutar el mtodo. Esto es posible gracias a la sobrecarga de mtodos. En C# y .NET en general, se puede escribir ms de un mtodo con el mismo nombre, siempre que todos tengan el mismo tipo de retorno, y diferentes parmetros. Sobrescritura de mtodos. Cuando una clase hereda de otra, puede sobrescribir los mtodos virtuales, abstractos o sobrescritos en la clase padre, para que su resultado sea el adecuado para instancias de la subclase. Al sobrescribir un mtodo de la superclase, debemos indicar el modificador override:
public override string ToString () { ... }

Para acceder a las versiones de los mtodos de la superclase, podemos utilizar la variable especial base, que identifica la instancia actual, convertida explcitamente al tipo de la superclase:
base.ToString (); // devolver el resultado del mtodo // ToString de la superclase.

Ocultacin de mtodos. Si lo que queremos es ocultar un mtodo de la superclase, debemos especificar el modificador new. De ese modo, el nuevo mtodo ocultar la definicin del mismo mtodo de la superclase en la subclase:
public new string ToString () { ... }

FUOC P08/B0036/01627

53

C#

Actividad 12 Ejecutad las siguientes acciones: Cread el mtodo MarchaAtras en la clase Coche con el parmetro metros de tipo entero, que devuelva un booleano. Declarad el mtodo anterior en la interfaz Vehiculo. Cread el mtodo esttico ActualizarCoches en la clase Coche sin valor de retorno, con el parmetro fichero de tipo string. Llamad a los mtodos anteriores desde el programa principal.

2.5.2.

Propiedades

Las propiedades son un caso muy concreto de mtodos, que permiten acceder o modificar el valor de un atributo de la clase. El motivo de utilizar propiedades, en vez de acceder directamente a los atributos de la clase, es doble. Por un lado, se garantiza la encapsulacin y ocultacin de tipos, que consiste en no permitir que se pueda acceder directamente a la informacin para evitar modificaciones inadecuadas. Por otro lado, las propiedades permiten aadir cdigo adicional, por ejemplo, para realizar comprobaciones sobre si el valor que se quiere modificar es correcto. Las propiedades tambin pueden devolver o modificar un valor que no se corresponda con ningn atributo concreto. Para definir una propiedad, utilizamos la siguiente sintaxis:
visibilidad tipo_retorno Nombre_Propiedad { get { return var; }

set { var = value; } }

Las instrucciones contenidas dentro del bloque get son las que se ejecutan para acceder al valor de la propiedad. Al final del bloque, debe haber una instruccin return que devuelva el valor correspondiente. Las instrucciones del bloque set se ejecutan cuando se asigna un valor a la propiedad mediante la instruccin de asignacin. La palabra clave value identifica el valor que se

FUOC P08/B0036/01627

54

C#

asigna a la propiedad dentro del bloque set. Si omitimos el bloque set, tendremos una propiedad de slo lectura. Si omitimos el bloque get, tendremos una propiedad de slo escritura. Podemos utilizar las propiedades de una clase directamente como si fueran atributos de la misma, tanto a la izquierda de una asignacin (si el bloque set est definido), como dentro de una expresin (si el bloque get est definido).

Actividad 13 Ejecutad las siguientes acciones: Aadid la propiedad Matricula de tipo string a la clase Coche. Cread una instancia de Coche y asignadle un valor a la propiedad anterior. Asignad el valor de la propiedad anterior a una variable.

2.5.3.

Constructores

Los constructores son una serie de mtodos especiales que se ejecutan al crear un objeto de la clase. La funcionalidad de estos mtodos es la de inicializar los atributos de la clase con valores predefinidos o bien con valores que se pasan como parmetros del constructor. Puede haber tantos constructores como sea necesario, pero cada uno de ellos debe tener tipos de parmetros distintos. La sintaxis de un constructor es la siguiente:
visibilidad nombreClase (parmetros) { }

Los mtodos constructores no tienen tipo de retorno (lo que hacen es crear una instancia de una clase y ese es el resultado del constructor), y todos ellos se llaman igual que el nombre de la clase. La definicin de constructores es opcional, ya que por defecto hay siempre un constructor vaco sin parmetros. Si se define explcitamente algn constructor sin parmetros, ste sobrescribe al constructor por defecto. Los constructores se utilizan en el momento de crear una instancia de una clase, junto con la palabra clave new: Clase var = new Clase (parmetros);

FUOC P08/B0036/01627

55

C#

Esta sera la instruccin de creacin de la clase Clase, que llamar al mtodo constructor que corresponda segn el nmero de parmetros que se especifiquen. Si no hay ningn constructor para la clase con ese nmero y tipos de parmetros, se produce un error de compilacin. A veces, los constructores de una subclase realizan las mismas instrucciones que un constructor de la superclase, ms algunas otras. Es posible especificar que, antes de ejecutar el constructor, se ejecute tambin uno de los constructores de la superclase, de la siguiente forma:
public MiClase (parmetros) : base (parmetros_base) { }

Antes de ejecutar el constructor anterior, se llamar al constructor de la correspondiente superclase que tenga el mismo nmero y tipo de parmetros especificados en parmetros base. Esto tambin se puede utilizar para que un constructor ejecute inicialmente las instrucciones de otro constructor de la propia clase. En este caso, en vez de utilizar la palabra clave base, utilizamos la palabra clave this:
public MiClase (parmetros) : this (parmetros_this) { }

Los constructores de una clase generalmente son pblicos. No obstante, tambin se pueden definir constructores privados. La utilidad de un constructor privado es la de evitar que se creen instancias de una clase, por ejemplo, porque todos sus mtodos sean estticos. Para ello, definimos un nico constructor, sin parmetros, con visibilidad private. Otra utilidad es la de crear un constructor para uso interno de otros constructores de la clase, pero que no debe de ser pblico. Los constructores estticos no son constructores para instancias de clase, sino que se utilizan para inicializar los valores estticos de una clase. Antes de que se cree una instancia de esa clase, tenemos asegurado que se ejecutar el mtodo constructor esttico, si est definido. El mtodo constructor esttico de una clase va precedido de la palabra static, no se puede definir visibilidad, y no puede tener parmetros, ya que no puede ser invocado directamente (se invoca antes de instanciar la clase por primera vez, y solo entonces).

FUOC P08/B0036/01627

56

C#

Actividad 14 Ejecutad las siguientes acciones: Aadid a la clase Coche un constructor que acepte la matricula correspondiente como parmetro. Cread una instancia de la clase Coche utilizando el nuevo constructor.

2.5.4.

Destructores

En .NET, cuando un objeto deja de ser utilizado (porque se eliminan todas las referencias a este objeto), no es realmente eliminado de memoria, hasta que el Garbage Collector realiza una comprobacin de memoria destruye los objetos no usados. Es en este momento cuando el Garbage Collector llama a los mtodos destructores de los objetos que va a destruir (si es que hay algn mtodo destructor definido). Esto quiere decir que la llamada a los mtodos destructores no es determinista (no podemos saber a priori cundo se va a producir). Por otro lado, la utilizacin de destructores hace bajar el rendimiento de la aplicacin porque el Garbage Collector realiza dos inspecciones hasta poder eliminar un objeto que tiene destructor definido (una para llamar al destructor, y la otra para destruir el objeto definitivamente). Por eso, es importante razonar si es necesario o no definir mtodos destructores, y tener en cuenta la caracterstica indeterminista de los mismos. Estos mtodos destructores pueden ser necesarios (y se recomiendan slo), si el objeto maneja recursos no administrados (conexiones de bases de datos, objetos COM, etc., para liberar estos recursos. Otra forma de liberar recursos no administrados, sin tener que definir destructores es la de implementar la interfaz IDisposable, y su correspondiente mtodo Dispose. Cuando un objeto de la clase deja de ser necesario, se llama al correspondiente mtodo Dispose, para que libere los recursos que est utilizando. De este modo, se dispone de un mecanismo determinista para destruir el objeto (eso s, es necesario no olvidarse de llamar al mtodo Dispose). Dado que se invocan de forma indeterminista, los mtodos destructores no pueden tener parmetros, y slo hay uno por clase. Para definir el mtodo destructor de la clase, utilizamos la siguiente sintaxis:

FUOC P08/B0036/01627

57

C#

~MyClass () { // instrucciones del destructor }

2.5.5.

Operadores

Como ya hemos comentado, los operadores son una serie de smbolos que realizan clculos con los parmetros que reciben y devuelven un resultado. Estos operadores slo funcionan generalmente sobre tipos numricos, caracteres, booleanos y cadenas de caracteres. No obstante, es posible redefinir estos operadores en otras clases para definir su comportamiento cuando sus parmetros no son de los tipos anteriores. Para redefinir el significado de un operador O cuando sus parmetros son del tipo de la clase actual, incluimos una definicin de operador con la siguiente sintaxis:
public static tipo_ret operator O(parmetros) { // Instrucciones del operador }

Igual que con los mtodos normales, es posible sobrecargar los operadores de una clase. La diferencia es que los operadores pueden especificar diferentes tipos de retorno en funcin de los parmetros especificados.

Actividad 15 Ejecutad las siguientes acciones: Sobrescribid el operador + para la clase Coche. Utilizad el operador creado con dos instancias de la clase Coche.

2.5.6.

Indexadores

Los indexadores son muy similares a las propiedades; la diferencia es que los indexadores permiten acceder al objeto como si se tratara de un array, indicando una o ms posiciones. Para definir un indexador, utilizamos la sintaxis siguiente:
visibilidad tipo_ret this [tipo_indice nombre_indice] { get { ... }

FUOC P08/B0036/01627

58

C#

set { ... } }

En vez de un ndice, podemos especificar ms de uno, como en los arrays. Por ejemplo, en la clase Matriz podemos definir un indexador que nos permita acceder al valor de una posicin concreta, de la siguiente forma:
public int this [int fila, int col] { get { return matriz [fila, col]; } set { matriz [fila, col] = value; } }

La utilizacin de un indexador permite acceder a l como si el objeto de la clase fuese un array, por ejemplo: Matriz m = new Matriz (); M[0,0] = 4;

Actividad 16 Ejecutad las siguientes acciones: Cread un indexador de tipo booleano en la clase Coche. Cada posicin N del indexador debe indicar si el asiento N-essimo est ocupado (true) o no (false). Consultad una de las posiciones del indexador. Cambiad una de las posiciones del indexador.

FUOC P08/B0036/01627

59

C#

3. Conceptos avanzados

En este apartado, veremos algunos aspectos ms avanzados del lenguaje C#. 3.1. Tratamiento de excepciones Los programas pueden provocar errores en tiempo de ejecucin. Los errores provocados por un comportamiento incorrecto son difciles de detectar, ya que se deben a un error en el cdigo fuente del programa, pero no producen errores ni al compilar ni al ejecutarse; simplemente, el programa no funciona bien y devuelve resultados incorrectos. Existen otros errores que s que son detectables y no son evitables a priori. Son errores producidos por circunstancias ajenas al programa, como por ejemplo un fallo de acceso a un fichero o a una base de datos, un fallo de comunicaciones, un desbordamiento de pila, etc. Los errores o excepciones, se pueden gestionar en C# para que no termine el programa bruscamente o aparezcan mensajes de error del sistema, y para intentar solventar el error en la medida de lo posible. Para ello, C# y .NET en general tratan las excepciones como objetos. El .NET Framework define una serie de objetos Exception generales, pero como veremos a continuacin se pueden definir nuevos objetos excepcin por defecto. Las excepciones se producen en instrucciones concretas, ya sean instrucciones de nuestro propio programa o de un mtodo que hemos invocado de otra librera de clases (por ejemplo de la FCL del .NET Framework). Para capturar una excepcin cuando se produzca, utilizamos la instruccin try/catch:
try { // instrucciones que potencialmente pueden producir // excepciones } catch { // Gestion de las excepciones encontradas } finally { // instrucciones a ejecutar tanto si se producen // excepciones como sino }

FUOC P08/B0036/01627

60

C#

Dentro del bloque try se incluyen las instrucciones que pueden producir errores. Si se produce una excepcin dentro del bloque, automticamente el flujo de ejecucin pasa a las instrucciones del bloque catch (el resto de instrucciones del bloque try se ignoran). Si no hay ningn bloque, el programa contina ejecutndose En el bloque catch se incluyen las instrucciones necesarias para controlar el error: informar al usuario, subsanar el error, restaurar el flujo de ejecucin del programa, etc. En principio, el bloque catch sin ningn parmetro se ejecuta para cualquier excepcin que se produzca en el bloque try. Si nos interesa discernir entre diferentes excepciones, podemos especificar varios bloques catch, indicando el tipo de excepcin que gestiona cada uno:
try { } catch (Exception1) { // gestion de la excepcin Exception1 } catch (Exception2 e) { // gestion de la excepcin Exception2 // declaramos la variable e para poder acceder a las // propiedades del error desde este bloque catch } ...

Cuando se produce una excepcin, se revisan los bloques catch secuencialmente hasta que se encuentra uno que gestione el tipo de excepcin que se ha producido. Si no se encuentra ninguno, la excepcin se propaga, es decir, se cancela la ejecucin del mtodo actual y se devuelve el control del programa al mtodo que lo llam. Si este mtodo tampoco gestiona el error, se vuelve a propagar, y as recursivamente hasta que se llega al mtodo principal. Si el mtodo principal no gestiona el error, el programa se aborta y provoca una excepcin de sistema. Una vez gestionada una excepcin en un bloque catch, contina la ejecucin del programa en la lnea siguiente al bloque try/catch en el que se gestion. Por otro lado, hay que tener en cuenta que el primer bloque catch que se encuentra es el que se ejecuta; por ejemplo, si tenemos un catch que gestiona la excepcin IOException y otro que gestiona la excepcin FileNotFoundException, deberemos colocar el segundo antes que el primero para que se ejecute si se produce una excepcin de tipo FileNotFoundException, ya que una excepcin de este tipo es a la vez del tipo IOException por herencia.

FUOC P08/B0036/01627

61

C#

Asimismo, si tenemos un bloque catch sin parmetros, lo deberemos colocar en ltimo lugar, porque se ejecuta independientemente de la excepcin que se produzca. El bloque finally contiene instrucciones que se han de ejecutar independientemente de si se han producido o no excepciones dentro del bloque catch. Es un bloque opcional, que se suele utilizar si es necesario liberar algn recurso como una conexin a una base de datos o cerrar un canal de lectura o escritura. Si se produce un error y no hay un bloque finally definido, el flujo de ejecucin sale del mtodo actual, sin ejecutar ninguna de las instrucciones que hay despus de la instruccin try/catch. Veamos un ejemplo de funcionamiento del bloque try/match:
static void Main() { try { method(); } catch (System.MissingFieldException) { MessageBox.Show("Hola4"); }

MessageBox.Show("Hola5"); }

public static void method () { try { // En esta linea se produce una excepcion

MessageBox.Show("Hola1"); } catch (System.TimeoutException) { MessageBox.Show("Hola2"); }

MessageBox.Show("Hola3"); }

El mtodo principal llama al mtodo method, que lanza una excepcin en la lnea indicada. Si la excepcin fuese de tipo TimeoutException seria gestionada por el catch del mtodo method y se mostrara el mensaje Hola2 por

FUOC P08/B0036/01627

62

C#

pantalla. A continuacin, el programa continuara la ejecucin en la siguiente instruccin despus del try/catch, mostrando por pantalla el mensaje Hola3 y posteriormente Hola5 del mtodo principal. En cambio si la excepcin fuese de tipo MissingFieldException, el bloque try/catch del mtodo method no gestionara el error y lo propagara al mtodo main. El mtodo main s que gestiona el error, mostrando el mensaje Hola4 y posteriormente Hola5. Por ltimo, si la excepcin fuese excepcin de otro tipo diferente (tal que no fuese superclase de ninguno de los dos anteriores), dado que no hay ningn bloque catch que la gestione en el mtodo method, sta se propaga al mtodo principal. Como el mtodo principal tampoco puede gestionar la excepcin, se produce un error en la aplicacin y finaliza su ejecucin. 3.1.1. Excepciones definidas por el usuario

Como ya hemos dicho, las excepciones en C# se tratan como objetos. El .NET Framework define una jerarqua de clases de excepcin que parten de la clase Exception como raz. Todas las clases que heredan de Exception son tratadas como excepciones (y por lo tanto se pueden utilizar en un bloque try/ catch). Existen dos subclases de la clase Exception que son las que se deberan utilizar para crear excepciones nuevas: SystemException y ApplicationException. La primera se utilizara para definir nuevas excepciones de sistema, mientras que la segunda es la que se utiliza normalmente para definir una jerarqua personalizada de excepciones para una aplicacin. Para crear una excepcin nueva, por lo tanto, creamos una clase que herede de alguna otra excepcin, por ejemplo ApplicationException. Las clases excepcin definen una serie de mtodos que permiten obtener informacin acerca del error que se ha producido. Podemos sobreescribir algunos de estos mtodos para adecuarlos a la nueva excepcin que estamos definiendo, entre otros: Message. Devuelve un mensaje de error. Este mtodo se suele sobrescribir siempre para adecuar el mensaje al nuevo tipo de excepcin que se est creando. Source. Devuelve el nombre del programa u objeto que produjo la excepcin StackTrace. Devuelve un string que contiene la pila de ejecucin hasta el punto en el que se produjo el error. Es una informacin muy til para encontrar bugs o errores dentro del cdigo fuente.

FUOC P08/B0036/01627

63

C#

Por ltimo, nos falta ver cmo provocar una excepcin. Esto es til cuando el programa se encuentra una situacin que le impide realizar las acciones que debe realizar, por ejemplo, el usuario no ha indicado algn valor en un formulario. En este caso, podemos crear una excepcin especial llamada, por ejemplo FieldCannotBeEmptyException, y lanzar esta excepcin cuando detectemos que nos falta algn valor. La interfaz grfica capturar este error y avisar al usuario de que debe de rellenar ese dato. De este modo, evitamos que la capa de lgica de programa tenga que avisar directamente a la capa de interfaz (con lo que estaramos rompiendo la distribucin en capas de la aplicacin). Para lanzar una excepcin, utilizamos la instruccin throw, indicando a continuacin un objeto del tipo de la excepcin que queramos provocar, por ejemplo: throw new FieldCannotBeEmptyException ("nombre"); Como se puede ver en el ejemplo anterior, los objetos excepcin pueden tener parmetros, en este caso pasamos el nombre del campo que est vaco, de modo que el mensaje producido por la propiedad Message de la excepcin puede ser algo genrico para cualquier campo como: "El campo <nombre campo> no puede estar vacio". Dependiendo del tamao de la aplicacin se puede decidir si implementar excepciones personalizadas o no. Si la aplicacin es pequea se puede utilizar directamente la clase Exception o ApplicationException: throw new Exception("El campo nombre no puede estar vacio"); throw new ApplicationException("El campo nombre no puede estar vacio"); La instruccin throw lanza la excepcin saliendo automticamente del mtodo actual (ignorando el resto de instrucciones), y propagndose desde el mtodo actual hacia el mtodo que lo invoco, hasta encontrar algn mtodo que haya definido un bloque try/catch que gestione la excepcin, y en cuyo bloque try se encuentre la instruccin que provoc la excepcin. Si no se encuentra ningn bloque try/catch, la excepcin llega al Main. Si no se trata tampoco en el mtodo principal, sta llega al CLR, que termina la ejecucin del programa y muestra un mensaje del error que se ha producido al usuario.

FUOC P08/B0036/01627

64

C#

Actividad 17 Ejecutad las siguientes acciones: Cread la excepcin NoHayGasolinaException. Cread el mtodo ComprobarNivelGasolina en la clase CocheGasolina que lance la excepcin NoHayGasolinaException. Llamad al mtodo anterior desde el programa principal, capturando la excepcin.

3.2. Delegate y eventos Un delegate es un tipo de dato especial, que permite utilizar un mtodo como si fuera un objeto, es decir, permite almacenarlo en una variable, pasarlo como parmetro de otro mtodo, etc. Para definir un delegate, utilizamos la palabra clave delegate, y a continuacin especificamos la signatura del mtodo correspondiente: visibilidad tros); La definicin anterior crea un nuevo tipo llamado NombreDelegado, que representa un mtodo con la signatura especificada, es decir, que devuelve el tipo de dato tipo_retorno y tiene los parmetros indicados. De ese modo, podemos declarar variables del tipo, como si de un tipo de datos cualquiera se tratase: NombreDelegado variableDelegado; Para inicializar una variable de tipo delegate es necesario indicar un mtodo que tenga la misma signatura que dicho tipo delegate. Una vez inicializada, la variable representa al mtodo indicado en la inicializacin. Por ejemplo:
delegate void UnDelegado(int g);

delegate

tipo_retorno

NombreDelegado

(parame-

UnDelegado ud1 = new UnDelegado(Metodo1); UnDelegado ud2 = new UnDelegado(Metodo2);

public void Metodo1(int k) { }

public void Metodo2(double l) {

FUOC P08/B0036/01627

65

C#

La inicializacin de la variable ud2 produce un error de compilacin, porque el mtodo Metodo2 no cumple con la signatura definida por el delegate. Las variables de tipo delegate se pueden utilizar para invocar el mtodo asignado en la inicializacin, por ejemplo la instruccin siguiente: ud1 (4); Seria equivalente a esta otra: Metodo1 (4); Los tipos delegate se pueden utilizar tambin como parmetros de otros mtodos, por ejemplo el mtodo OperarArray recibe un mtodo que opera con dos enteros (definido por el delegateOperacionInt), y lo aplica a todos los elementos de dos arrays para formar un tercer array de enteros:
public delegate int OperacionInt(int i1, int i2);

public int [] OperarArray (int[] a1, int[] a2, OperacionInt op) { int [] a3 = new int [Math.Min (a1.Length, a2.Length)];

for (int count = 0;count < a1 &&&& count < a2;count++) { a3[count] = op(a1[count], a2[count]); }

return a3; }

Un ejemplo de utilizacin del mtodo anterior podra ser mediante el mtodo SumarInt:

public static int SumarInt(int i1, int i2) { return i1 + i2; }

La llamada al mtodo OperarArray quedara de la siguiente forma: OperarArray(a1, a2, SumarInt);

FUOC P08/B0036/01627

66

C#

3.2.1.

Eventos

Los eventos son circunstancias concretas en las que se producen cambios en el estado de un objeto, es decir, se modifica el valor de alguna de sus propiedades. Por ejemplo, la clase Coche puede definir el evento NivelCombustibleBajo, que se produce cuando el valor de la propiedad NivelCombustible desciende por debajo de una cierta cantidad. Algunos objetos pueden estar interesados en saber cundo se produce un determinado evento en otro objeto, para realizar alguna determinada accin. Siguiendo con el ejemplo anterior, un objeto Conductor puede estar interesado en saber cundo se produce el evento NivelCombustibleBajo de su coche, para as ejecutar la accin LlenarDeposito. Para controlar los eventos de los diferentes objetos y avisar a los objetos interesados cuando stos se producen, el .NET Framework proporciona un mecanismo de gestin de eventos basado en el patrn observador. Este patrn de diseo define un objeto Sujeto (el objeto que produce el evento), y uno o ms objetos Observador (los objetos interesados en el evento). Los objetos Observador se registran en el objeto Sujeto para ser avisados en caso de que se produzca un determinado evento en ste. Un Sujeto puede producir ms de un evento, y un Observador puede estar registrado en uno o ms eventos del Sujeto. Cuando se produce un evento en el Sujeto, ste avisa a todos los Observadores registrados a ese evento llamando a un mtodo del Observador que gestiona el evento producido (mtodo de gestin del evento). En C# los eventos se definen como variables de un tipo delegate (dentro de la clase que acta como Sujeto), mediante la palabra clave event, por ejemplo: public delegate void NivelCombustibleBajoHandler (int nivel); public event NivelCombustibleBajoHandler NivelCombustibleBajo; La definicin anterior crea un evento llamado NivelCombustibleBajo. El delegado NivelCombustibleBajoHandler sobre el que se define este evento, indica la signatura que debe tener el mtodo de gestin del evento en los objetos Observador que se registren en el evento. Para poder registrarse a un evento es necesario definir un mtodo que gestione el evento en la clase que acta como Observador. Este mtodo ha de tener la misma signatura que el delegate asociado al evento correspondiente, por ejemplo para el caso del evento NivelCombustibleBajo un posible mtodo de gestin podra ser:
private void gestionar_NivelCombustibleBajo (int nivel)

FUOC P08/B0036/01627

67

C#

{ LlenarDeposito (nivelmax - nivel); }

Una vez definido el mtodo de gestin del evento, podemos registrar el objeto observador al evento de una instancia concreta del Sujeto, mediante la operacin +=, por ejemplo:
micoche.NivelCombustibleBajo += new NivelCombustibleBajoHandler (gestionar_NivelCombustibleBajo);

Finalmente, debemos determinar en qu punto de la clase Sujeto se puede producir el evento que hemos creado, en el ejemplo del coche el momento en que puede verse modificado el nivel de combustible es en los mtodos de acceso de la propiedad NivelCombustible. En este punto, deberemos llamar a los eventos de gestin de los diferentes Observadores registrados. Para ello, utilizamos el nombre del evento como si fuese un mtodo:
if (NivelCombustible < nivelBajo) { NivelCombustibleBajo (NivelCombustible); }

La anterior instruccin genera, automticamente, una llamada a cada uno de los mtodos de gestin del evento de todos los observadores registrados, y les pasa como parmetro los datos que se indiquen, en este caso el valor del nivel de combustible actual. Si no hay ningn observador registrado, se produce un error por valor nulo, por lo que se recomienda hacer una comprobacin adicional:
if (NivelCombustible < nivelBajo) { if (NivelCombustibleBajo !=null) { NivelCombustibleBajo (NivelCombustible); } }

El mecanismo de eventos es muy til para pasar informacin entre capas de una aplicacin, sin necesidad de que las capas inferiores deban conocer directamente con qu elementos de las capas superiores deben de comunicarse (y evitar as el acoplamiento entre capas). Un ejemplo claro de esto es el mecanismo utilizado por Visual Studio para la comunicacin entre los elementos de la interfaz grfica de usuario (botones, campos de texto, listas, etc.), y la capa de lgica de programa, que es donde se implementan los mtodos que se ejecutan cuando se produce un evento en alguno de estos elementos.

FUOC P08/B0036/01627

68

C#

Actividad 18 Ejecutad las siguientes acciones: Aadid el evento NivelAceiteBajo en la clase Coche. La signatura del mtodo de gestin del evento debe aceptar un parmetro de tipo entero (el nivel de aceite), y debe devolver un booleano (true si se decide continuar, o false si se quiere parar el coche para corregir el problema). Cread una nueva clase Conductor, y cread en ella el mtodo de gestin del evento. Registrad una instancia de Conductor al evento de una instancia de la clase Coche en el programa principal. Cread el mtodo ComprobarNivelAceite en la clase Coche que desencadene el evento. Llamad al mtodo anterior desde el programa principal, y comprobad que se gestiona el evento producido.

3.2.2.

Mtodos annimos

Los mtodos annimos permiten especificar la implementacin del mtodo que gestiona un evento, sin necesidad de crear un mtodo adicional, simplemente especificando las instrucciones a ejecutar directamente al registrarse al evento, por ejemplo:
micoche.NivelCombustibleBajo += delegate { LlenarDeposito (); };

En este caso, hemos ignorado el parmetro int de la definicin del delegate, pero tambin se puede aadir si se necesita ese parmetro en las instrucciones del mtodo:
micoche.NivelCombustibleBajo += delegate (int nivel) { LlenarDeposito (nivelmax - nivel); };

FUOC P08/B0036/01627

69

C#

3.3. Atributos Los atributos son tags adicionales que se aaden a la definicin de un elemento del programa, modificando su comportamiento, provocando alguna accin especial en el compilador sobre ese elemento o proporcionando informacin adicional sobre el elemento que se pueda consultar a posteriori con las utilidades de Reflection del .NET Framework. Para aplicar un atributo a un elemento del programa, aadimos la siguiente instruccin en la lnea anterior a la definicin del elemento: [nombreAtributo(parametros)] El .NET Framework define varios atributos con diferentes funcionalidades. En la siguiente tabla se muestran los ms comunes:
Atributo Aplicablea Descripcin Si el smbolo indicado como parmetro est definido en tiempo de ejecucin, el mtodo se ejecuta normalmente; si no, no se ejecuta. Indica que el mtodo esta implementado en una librera de cdigo no administrado. El nombre de la librera se pasa como parmetro. Especifica la propiedad por defecto del componente

Conditional mtodos

DllImport

mtodos

DefaultPro- clases perty DefaultValue propiedades

Indica que la propiedad es el valor por defecto de un componente Permite indicar una descripcin del elemento, que aparecer en la ventana de propiedades del diseador de Visual Studio

Description propiedades, eventos

A continuacin, veremos algunos ejemplos de utilizacin de estos atributos: Conditional


[Conditional ("DEBUGGING")] public static void Metodo() { ... }

El mtodo anterior slo se ejecutar si el smbolo DEBUGGING est definido. Para definir un smbolo, debemos utilizar la siguiente instruccin del preprocesador5 de C#: #define DEBUGGING DllImport [DllImport("libreria.dll", EntryPoint="Metodo")]

(5)

Las instrucciones del preprocesador se tratan antes de compilar el cdigo fuente. Estas instrucciones permiten modificar el comportamiento del compilador a la hora de tratar el cdigo fuente. Para ms informacin sobre las instrucciones del preprocesador, consultar la ayuda de Visual Studio

FUOC P08/B0036/01627

70

C#

public static extern int Metodo(parametros); La anterior definicin de mtodo indica que la implementacin del mismo se encuentra en la librera libreria.dll y se corresponde con el mtodo de nombre mtodo (EntryPoint). DefaultProperty [DefaultProperty("Property")] public class Class1 Permite definir la propiedad por defecto de una clase. DefaultValue y Description
[DefaultValue(0)] [Description("Esta propiedad sirve para ...")] public int Property { get { return prop; } set { prop = value; } }

El atributo DefaultValue permite definir el valor por defecto del elemento; en este caso, el valor 0. El atributo Description permite especificar una descripcin de la propiedad. En los componentes visuales, es la descripcin que aparece en la ventana de propiedades del diseador de Visual Studio.

3.3.1.

Atributos personalizados

Aparte de los atributos definidos en el .NET Framework, podemos crear otros atributos personalizados que almacenen cierta informacin acerca de un elemento del programa. Para definir un nuevo atributo, deberemos crear una clase que herede de la clase System.Atribute, o de alguna clase derivada de la anterior. Adems, deberemos indicar mediante un atributo especial llamado AttributeUsage, los elementos a los cuales se aplicar el atributo que estamos definiendo:
[AttributeUsage(&lt;i&gt;elementos_aplicables&lt;/i&gt;)] public class UnAtributo: System.Attribute { ... }

Los elementos aplicables se especifican mediante la enumeracin AttributeTargets, que est compuesta por los siguientes valores, que corresponden con los diferentes elementos sobre los que se puede aplicar un atributo: Class,

FUOC P08/B0036/01627

71

C#

Constructor, Delegate, Enum, Event, Field, Interface, Method, Module, Parameter, Property, ReturnValue, Struct, Assembly, All (para cualquier elemento). Por ejemplo, para especificar que el atributo se aplicar slo a mtodos, utilizaremos el elemento AttributeTargets.Method de la enumeracin. Si el atributo se aplica sobre ms de un elemento, debemos especificar los elementos de la enumeracin que correspondan, separados por el smbolo '|', por ejemplo: AttributeTargets.Method | AttributeTargets.Property, indica que el atributo se puede aplicar a mtodos o a propiedades. Veamos un ejemplo de atributo personalizado que permite almacenar informacin acerca de los cambios que se han producido en el mtodo. En concreto, almacenaremos los desarrolladores que han modificado el mtodo y en qu fechas. Para ello, creamos una clase que herede de la clase Attribute:
[AttributeUsage(AttributeTargets.Method, AllowMultiple=true)] public class InfoDesarrolloAttribute: System.Attribute { string desarrollador; string fecha;

public InfoDesarrolloAttribute (string d, string f) { desarrollador = d; fecha = f; }

public string Desarrollador { get { return desarrollador; } set { desarrollador = value; } } public string Fecha { get { return fecha; } set { fecha = value; } } }

El parmetro adicional AllowMultiple=true indica que puede ser que un mtodo tenga ms de un atributo de este tipo (ya que un mismo mtodo puede ser modificado varias veces o por varios desarrolladores). Una vez definido el atributo, podemos utilizarlo como los atributos predefinidos:
[InfoDesarrolloAttribute("David", "10-01-2005")] [InfoDesarrolloAttribute("Juan", "12-01-2005")]

FUOC P08/B0036/01627

72

C#

[InfoDesarrolloAttribute("David", "02-02-2005")] public void mtodo () { ... }

La informacin proporcionada en los atributos se aade en los metadatos del ensamblado en el que se encuentra el elemento en el cual se definen los atributos. Esta informacin se puede consultar en tiempo de ejecucin mediante el mecanismo de Reflection. No trataremos Reflection directamente, pero veremos a continuacin un ejemplo de cmo acceder a la informacin que hemos aadido mediante el atributo InfoDesarrolloAttribute:
// recuperamos la informacin de todos los miembros de // la clase que queramos inspeccionar (en este caso // Class1) System.Reflection.MemberInfo[] memberInfoArray ; memberInfoArray = typeof(Class1).GetMembers();

// para cada elemento MemberInfo... foreach (MemberInfo mi in memberInfoArray) { // recuperamos los atributos del tipo // InfoDesarrolloAttribute existentes // y para cada uno de ellos... foreach (InfoDesarrolloAttribute ida in mi.GetCustomAttributes (typeof(InfoDesarrolloAttribute), false)) { // mostramos por pantalla el nombre del mtodo, el nombre // del desarrollador, y la fecha de modificacin Console.WriteLine (mi.Name + " " + ida.Desarrollador + " " + ida.Fecha);

} } }

El trozo de cdigo anterior aplicado a la clase que contiene el mtodo en el que hemos definido los atributos, genera el siguiente resultado por pantalla: mtodo David 10-01-2005 mtodo Juan 12-01-2005 mtodo David 02-02-2005

FUOC P08/B0036/01627

73

C#

4. Novedades de C# 3.0

La versin actual del lenguaje C# es la 2.0. La nueva versin 3.0 vendr incluida dentro del .NET Framework 3.5, junto con la aparicin de Visual Studio 2008. En este apartado, veremos algunas de las novedades ms importantes que incorpora esta nueva versin. 4.1. Variables locales de tipo implcito La nueva versin de C# permite declarar variables locales mediante la palabra clave var, de forma que no es necesario especificar el tipo de datos de la misma; se infiere automticamente de la expresin que se utiliza para inicializar la variable. Por ejemplo, en las siguientes inicializaciones la variable i ser de tipo entero, y la variable b de tipo booleano: var i = 1; var b = b1 || b2; La palabra clave var se puede utilizar tambin con variables de tipo array, por ejemplo la siguiente instruccin crea una variable a de tipo array de caracteres: var a = new[] { 'a', 'b', 'c' }; Una vez inicializada una variable de tipo implcito, no es posible modificar su tipo de dato. Por otro lado, es necesario inicializar la variable en la misma lnea en la que se declara (no se puede declarar e inicializar por separado), y no puede inicializarse al valor null. 4.2. Tipos annimos Gracias a la caracterstica de tipos annimos de la nueva versin de C#, es posible crear objetos, sin necesidad de declarar una clase que defina su estructura. Por ejemplo, la siguiente instruccin crea un objeto con dos campos de tipo entero: var c = new { x = 4, y = 5 }; Como se puede comprobar en el ejemplo, se utiliza una variable de tipo implcito para crear la instancia de tipo annimo, ya que no existe ninguna clase definida para dicha instancia. En realidad, lo que hace el compilador es crear una clase automticamente a partir de los datos proporcionados en la inicializacin; en el caso anterior, la clase creada tendra una estructura equivalente a la siguiente:

FUOC P08/B0036/01627

74

C#

public class __Anonymous1 { private int x; private int y;

public int X { get { return x; } set { x = value; }} public int Y { get { return y; } set { y = value; }} }

Como se puede comprobar, el compilador crea automticamente mtodos de acceso para los campos del tipo annimo. Por otro lado, si se detectan dos o ms tipos annimos con la misma estructura, el compilador reutiliza la misma clase generada automticamente, de forma que todos ellos son compatibles entre s. 4.3. Mtodos de extensin Los mtodos de extensin permiten extender tipos de datos ya existentes con mtodos estticos adicionales. Estos mtodos slo se pueden definir dentro de clases estticas, y se identifican por la palabra clave this que precede al primer parmetro del mtodo. El siguiente ejemplo aade el mtodo esttico Sumar al tipo de dato int:
public static int Sumar(this int i1, int i2) { return i1 + i2; }

De esta forma, se extiende la clase Int32 con el mtodo Sumar, que se puede utilizar como cualquiera de sus mtodos por defecto (siempre y cuando la clase en la que est definida el mtodo Sumar se haya importado mediante la correspondiente instruccin using), por ejemplo: int i = 5; i = i.Sumar(4); 4.4. Inicializadores de objetos Otra de las novedades de la nueva versin de C# permite inicializar los campos de un objeto al inicializarlo, sin necesidad de crear un constructor explcito en la clase correspondiente. La sintaxis es parecida a la de la creacin de los tipos annimos, aunque en este caso s que indicamos el nombre de la clase a la cual pertenece el objeto: Coordenada c = new Coordenada { x = 4, y = 5 };

FUOC P08/B0036/01627

75

C#

Es importante remarcar que la clase Coordenada no tiene porque tener ningn constructor definido (a parte del constructor por defecto). 4.5. Expresiones lambda Las expresiones lambda provienen del paradigma de programacin funcional. Dicho paradigma se basa en clculos funcionales llamados "Lambda calculus". Las expresiones lambda en la nueva versin de C# se pueden utilizar en vez de los delegate y mtodos annimos para reducir el tamao del cdigo fuente. Por ejemplo, el siguiente fragmento de cdigo que veamos en el subapartado de eventos:
micoche.NivelCombustibleBajo += new NivelCombustibleBajo (gestionar_NivelCombustibleBajo);

private void gestionar_NivelCombustibleBajo (int nivel) { LlenarDeposito (); }

Se puede sustituir por el siguiente, utilizando mtodos annimos:


micoche.NivelCombustibleBajo += delegate { LlenarDeposito (); };

Con expresiones lambda, el cdigo equivalente sera mucho ms simple: micoche.NivelCombustibleBajo += (int nivel) => LlenarDeposito (nivel); La sintaxis general de una expresin lambda es la siguiente: (parametros) => <expresin o bloque de cdigo entre {}> Las expresiones lambda tambin se pueden asignar a una variable o pasarse como parmetro de otro mtodo: Func<int> f = () => { return 0; }; l.Select (c => c.Length < 4); El tipo de dato genrico Func utilizado en la primera instruccin permite crear variables que contengan expresiones lambda. En la segunda instruccin, la variable l es una lista de string. La expresin lambda que se pasa como parmetro del mtodo Select devuelve cierto si la longitud del string es in-

FUOC P08/B0036/01627

76

C#

ferior a 4. El mtodo Select aplica dicha expresin lambda a cada uno de los elementos de la lista, de forma que se devuelven todos los string con longitud inferior a 4. 4.6. Expresiones de consulta Una de las mejoras ms importantes de la nueva versin del lenguaje C# es la sintaxis de LINQ (Language Integration Query). El siguiente ejemplo muestra un avance de lo que esta nueva caracterstica permite realizar:
var cochesAzules = from c in coches where c.color == "azul" select (c);

En el ejemplo anterior coches es una coleccin que contiene objetos de tipo Coche. El resultado instruccin anterior almacena en la variable cochesAzules todos los coches de la lista coches que tienen color azul.

FUOC P08/B0036/01627

77

C#

Actividades
1.Ejecutadlassiguientesacciones: Cread la enumeracin Meses que represente los meses del ao. Declarad una variable de tipo Meses y asignadle como valor el mes Agosto. 2.Ejecutadlassiguientesacciones: Escribid la estructura Producto con los siguientes atributos: nombre, precio, categora. Cread un Producto con los siguientes valores: "Tomate", 1.5, "Verdura". Copiad el Producto "Tomate" en el Producto "Zanahoria", y modificad el nombre y el precio a "2". 3.Ejecutadlassiguientesacciones: Cread una cadena que contenga la frase "La lluvia en Sevilla es una maravilla". Buscad la primera y la ltima posicin en que aparece la subcadena "ll", y almacenadlas en dos variables de tipo entero. Obtened la subcadena que va desde la primera hasta la ltima posicin en la que aparece la subcadena "ll" (ambas posiciones incluidas). Cul es la subcadena resultado? Pasad la cadena del apartado anterior a maysculas. 4.Ejecutadlassiguientesacciones: Declarad un array de tipo Producto. Cread e inicializad los elementos de un array de 4 posiciones de tipo Meses en una misma lnea. Cread una matriz de enteros de 4 X 4 e inicializad los valores de cada posicin, uno a uno, con los nmeros del 1 al 16. 5.Ejecutadlassiguientesacciones: Cread el tipo genrico Par con dos elementos de tipos diferentes. Cread diferentes instancias de dicho tipo, con diferentes tipos de datos para cada elemento. Cread el mtodo genrico Comparar que compare los elementos de dos arrays de tipo genrico y devuelva un array de enteros con los resultados de cada comparacin. 6.Ejecutadlassiguientesacciones: Escribid una instruccin condicional que en funcin del precio de un Producto p, escriba por pantalla "Barato" (de 0 a 1 ), "Normal" (de 1 a 4), o caro (ms de 4). En caso de que sea negativo, escribid por pantalla "Error". Escribid una instruccin condicional que en funcin del valor de una variable m de tipo Meses muestra por pantalla el nombre del mes correspondiente. 7.Ejecutadlassiguientesacciones: Cread un bucle que realice la suma de dos matrices de enteros a1 y a2. Sabemos que las dos matrices tienen el mismo nmero de filas y de columnas, pero no sabemos cuntas exactamente. El resultado debe almacenarse en una tercera matriz a, que hay que declarar e inicializar. Inicializad una variable i a 0, mostrad por pantalla el valor de i e incrementad i en uno, mientras i sea menor que N (N>=0). Mostrad todas las formas posibles de implementar este bucle. Buscad todas las posiciones en las que se encuentra la subcadena "ll" dentro de la cadena "La lluvia en Sevilla es una maravilla", y mostradlas por pantalla. No se puede modificar la cadena, ni utilizar variables string auxiliares. Mostrad todas las formas posibles de implementar este bucle. 8.Ejecutadlassiguientesacciones: Cread la clase Coche. 9.Ejecutadlassiguientesacciones: Cread una instancia de la clase Coche. 10.Ejecutadlassiguientesacciones: Cread la subclase CocheGasolina de la clase Coche. Cread una interfaz llamada Vehiculo. Haced que la clase Coche herede de la interfaz Vehiculo. 11.Ejecutadlassiguientesacciones: Aadid el campo caballos de tipo entero a la clase Coche y el campo carburante de tipo string a la clase CocheGasolina. Comprobad que no se pueden aadir campos o constantes a la interfaz Vehiculo. Haced que el campo caballos de la clase Coche sea accesible desde la clase CocheGasolina.

FUOC P08/B0036/01627

78

C#

Comprobad que se puede acceder al campo caballos desde CocheGasolina, pero no se puede acceder al campo carburante desde la clase Coche.

12.Ejecutadlassiguientesacciones: Cread el mtodo MarchaAtras en la clase Coche con el parmetro metros de tipo entero, que devuelva un booleano. Declarad el mtodo anterior en la interfaz Vehiculo. Cread el mtodo esttico ActualizarCoches en la clase Coche sin valor de retorno, con el parmetro fichero de tipo string. Llamad a los mtodos anteriores desde el programa principal. 13.Ejecutadlassiguientesacciones: Aadid la propiedad Matricula de tipo string a la clase Coche. Cread una instancia de Coche y asignadle un valor a la propiedad anterior. Asignad el valor de la propiedad anterior a una variable. 14.Ejecutadlassiguientesacciones: Aadid a la clase Coche un constructor que acepte la matricula correspondiente como parmetro. Cread una instancia de la clase Coche utilizando el nuevo constructor. 15.Ejecutadlassiguientesacciones: Sobrescribid el operador + para la clase Coche. Utilizad el operador creado con dos instancias de la clase Coche. 16.Ejecutadlassiguientesacciones: Cread un indexador de tipo booleano en la clase Coche Cada posicin N del indexador debe indicar si el asiento N-essimo est ocupado (true) o no (false). Consultad una de las posiciones del indexador. Cambiad una de las posiciones del indexador. 17.Ejecutadlassiguientesacciones: Cread la excepcin NoHayGasolinaException. Cread el mtodo ComprobarNivelGasolina en la clase CocheGasolina que lance la excepcin NoHayGasolinaException. Llamad al mtodo anterior desde el programa principal, capturando la excepcin. 18.Ejecutadlassiguientesacciones: Aadid el evento NivelAceiteBajo en la clase Coche. La signatura del mtodo de gestin del evento debe aceptar un parmetro de tipo entero (el nivel de aceite), y debe devolver un booleano (true si se decide continuar, o false si se quiere parar el coche para corregir el problema). Cread una nueva clase Conductor, y cread en ella el mtodo de gestin del evento. Registrad una instancia de Conductor al evento de una instancia de la clase Coche en el programa principal. Cread el mtodo ComprobarNivelAceite en la clase Coche que desencadene el evento. Llamad al mtodo anterior desde el programa principal, y comprobad que se gestiona el evento producido.

FUOC P08/B0036/01627

79

C#

Bibliografa
Nagel, C.; Evjen, B.; Glynn, J. y otros (2005). Professional C# with 3.0. Wrox. Microsoft Visual C# Delevoper Center. http://msdn.microsoft.com/vcsharp/

Vous aimerez peut-être aussi