tutorial csharp.pdf

81
Tutorial de C# version 0.5

Upload: developboy

Post on 12-Aug-2015

25 views

Category:

Documents


1 download

TRANSCRIPT

Page 1: tutorial csharp.pdf

Tutorial de C#

version 0.5

Page 2: tutorial csharp.pdf

Tutorial de C#: version 0.5

Un tutorial para aprender C# a nivel inicial utilizando mono (http://mono-project.com).

Copyright (c) 2004 MonoHispano.

Se otorga permiso para copiar, distribuir y/o modificar este documento bajo los términos de la Licencia de Documentación Libre GNU, versión

1.2 o cualquier versión posterior publicada por la Free Software Foundation. No hay Secciones Invariantes ni Textos de Portada o Contraportada.

Puedes consultar una copia de la licencia en http://www.gnu.org/copyleft/fdl.html.

Page 3: tutorial csharp.pdf

Tabla de contenidos1. Introducción ...........................................................................................................................................1

1.1. El lenguaje C#.............................................................................................................................11.1.1. Introducción....................................................................................................................11.1.2. Programación basada en componentes...........................................................................11.1.3. Orientación a objetos......................................................................................................21.1.4. Librería del lenguaje.......................................................................................................21.1.5. Estandarización...............................................................................................................21.1.6. C# frente a Java..............................................................................................................21.1.7. C# frente a C++..............................................................................................................31.1.8. ¿Porqué C#?....................................................................................................................3

1.2. Primer ejemplo............................................................................................................................4

2. Tipos........................................................................................................................................................6

2.1. Tipos............................................................................................................................................62.1.1. Importancia de los tipos de datos...................................................................................62.1.2. Tipos en C#.....................................................................................................................6

2.2. Tipos por valor............................................................................................................................62.2.1. Tipos por valor................................................................................................................62.2.2. Enteros............................................................................................................................72.2.3. Tipos de coma flotante....................................................................................................82.2.4. El tipo decimal................................................................................................................92.2.5. El tipo bool.....................................................................................................................92.2.6. Tipo arreglo..................................................................................................................102.2.7. El tipo char...................................................................................................................12

3. Estructuras de control.........................................................................................................................13

3.1. Estructuras de control................................................................................................................133.1.1. Instrucción if.................................................................................................................133.1.2. Instrucción switch.........................................................................................................153.1.3. Bucle for.......................................................................................................................173.1.4. Bucle while...................................................................................................................183.1.5. Bucle do-while..............................................................................................................183.1.6. Bucle foreach................................................................................................................19

3.2. Cambiando el rumbo.................................................................................................................203.2.1. Instrucción goto............................................................................................................203.2.2. Sentencia return............................................................................................................213.2.3. La instrucción continue................................................................................................213.2.4. break para salir de bucles..............................................................................................223.2.5. throw y el manejo de excepciones................................................................................23

4. Operadores...........................................................................................................................................24

4.1. Operadores................................................................................................................................24

5. Introducción a las clases......................................................................................................................27

5.1. Introducción a las clases en C#.................................................................................................275.1.1. Métodos........................................................................................................................285.1.2. Modificadores public y static........................................................................................295.1.3. Constructores e instancias de una clase........................................................................30

iii

Page 4: tutorial csharp.pdf

5.1.4. Sobrecarga de métodos.................................................................................................325.1.5. La palabra reservada this..............................................................................................33

6. Variables y parámetros........................................................................................................................35

6.1. Variables y parámetros..............................................................................................................356.1.1. Variables.......................................................................................................................356.1.2. Parámetros....................................................................................................................366.1.3. Arreglo de parámetros..................................................................................................37

7. Propiedades e indizadores...................................................................................................................40

7.1. Propiedades e indizadores.........................................................................................................407.1.1. Propiedades...................................................................................................................407.1.2. Indexadores...................................................................................................................41

8. Clases....................................................................................................................................................43

8.1. Modificadores de acceso...........................................................................................................438.2. Herencia....................................................................................................................................43

8.2.1. La palabra reservada base.............................................................................................448.2.2. Clases Abstractas..........................................................................................................458.2.3. Miembros virtual..........................................................................................................45

8.3. Interfaces...................................................................................................................................468.4. Modificadores de Herencia.......................................................................................................46

9. Delegados..............................................................................................................................................47

9.1. Delegados..................................................................................................................................479.1.1. Llamadas a múltiples métodos.....................................................................................48

10. Eventos................................................................................................................................................51

10.1. Eventos....................................................................................................................................51

11. Programación multihilo ....................................................................................................................57

11.1. Threads -- Programación multihilo.........................................................................................5711.1.1. Controlando el tiempo de vida del hilo......................................................................59

12. Sobrecarga de operadores.................................................................................................................61

12.1. Sobrecarga de operadores.......................................................................................................6112.1.1. ¿ Qué es la sobrecarga de operadores ?......................................................................6112.1.2. Sobrecargando operadores en la practica...................................................................61

13. TRatamiento de ficheros...................................................................................................................64

13.1. Tratamiento de ficheros...........................................................................................................6413.1.1. La primera clase: System.IO.File...............................................................................6413.1.2. Obteniendo informacion sobre archivos: System.IO.FileInfo....................................67

14. Interoperabilidad con código nativo................................................................................................68

14.1. Uso de Platform Invoke...........................................................................................................6814.1.1. ¿ Que es Platform Invoke ?.........................................................................................6814.1.2. Usando Platform Invoke.............................................................................................6814.1.3. Ejemplo de uso...........................................................................................................6814.1.4. Conclusiones...............................................................................................................69

iv

Page 5: tutorial csharp.pdf

15. Introspección......................................................................................................................................70

15.1. Introspección: El ojo que todo lo ve.......................................................................................7015.1.1. Metadatos...................................................................................................................7015.1.2. Atributos.....................................................................................................................7015.1.3. Código gestionado: Integración..................................................................................7115.1.4. Reflexión.....................................................................................................................7115.1.5. Ejemplo práctico: Árboles de Mono...........................................................................71

16. Creación y uso de librerías................................................................................................................72

16.1. Compilando para una librería..................................................................................................7216.2. Usando nuestra libreria...........................................................................................................72

17. Autores................................................................................................................................................74

Bibliografía ...............................................................................................................................................75

v

Page 6: tutorial csharp.pdf

Lista de tablas2-1. Tipos por valor......................................................................................................................................7

Tabla de ejemplos9-1. Ejemplo simple de uso de delegados..................................................................................................489-2. Ejemplo de uso de delegados.............................................................................................................49

vi

Page 7: tutorial csharp.pdf

Capítulo 1. Introducción

1.1. El lenguaje C#

1.1.1. Introducción

Los primeros rumores de que Microsoft estaba desarrollando un nuevo lenguaje de programaciónsurgieron en 1998 , haciendo referencia a un lenguaje que entonces llamaban COOL y que decían eramuy similar a Java.

En junio de 2000, Microsoft despejó todas las dudas liberando la especificación de un nuevo lenguajellamado C#. A esto le siguió rápidamente la primera versión de prueba del entorno de desarrollo estándar(SDK) .Net, que incluía un compilador de C#. El nuevo lenguaje estaba diseñado por Anders Hejlsberg (creador de Turbo Pascal y arquitecto de Delphi ), Scott Wiltamuth y Peter Golde. Entonces describieronel lenguaje como "...simple, moderno, orientado a objetos, de tipado seguro y con una fuerte herencia deC/C++".

1.1.2. Programación basada en componentes

En los últimos 10 años se han asentado diferentes técnicas de programación como lo son la orientación aobjetos, la programación basada en interfaces o los componentes. A pesar de ésto, los lenguajes deprogramación siempre han ido un paso por detrás de las mejores prácticas de programación delmomento. Como resultado, los programadores tienden a depender de código específico, usarconvenciones, o simplemente a no usar las nuevas técnicas.

Por ejemplo, C++ soporta orientación a objetos, pero no tiene el concepto formal de interfaces. Entonceslos programadores recurren a una mezcla entre clases abstractas bases e interfaces para simularprogramación basada en interfaces, mientras que utilizan modelos externos de componentes, como COMy CORBA para obtener los beneficios de la programación orientada a componentes.

Aunque Java está un paso adelante de C++ al proporcionar soporte a nivel de lenguaje para interfaces ypaquetes (entre otras cosas), también tiene poco soporte para construir y mantener a la largo del tiempocompletos sistemas basados en componentes (en los que uno necesita desarrollar, desplegar,interconectar y manejar distintas versiones de varias fuentes en un extenso periodo de tiempo). Esto nosignifica que la comunidad Java no haya construido tales sistemas, simplemente que las necesidadesderivadas de la implementación de tales sistemas se han conseguido a través de convenciones denotación y código propio, no a través de una característica del lenguaje.

Por otra parte, el lenguaje C# se ha construido suponiendo que los modernos sistemas de software seconstruyen usando componentes. Por lo tanto, C# proporciona soporte a nivel de lenguaje para losconstructores básicos de los componentes, como puden ser propiedades, métodos y eventos. Esto no

1

Page 8: tutorial csharp.pdf

Capítulo 1. Introducción

significa que todo esto no se haya hecho antes, lenguajes como LISP o Smalltak hacían cosas parecidas,pero con un gran coste. C# tiene mecanismos para permitir al mismo tiempo un orientación acomponentes y un gran rendimiento.

1.1.3. Orientación a objetos

Además del profundo soporte para desarrollo de software basado en componentes, C# es un lenguajecompletamente orientado a objetos, que implementa casi todo los conceptos y abstracciones presentes enC++ y Java.

Como es de esperar en un lenguaje orientado a objetos, C# implementa conceptos como herencia,encapsulación, polimorfismo y programación basada en interfaces. Además soporta las construccionestípicas de C++ y Java, como clases, estructuras, interfaces y enumeraciones, así como algunasconstrucciones nuevas, como los delegados, que son parecidos a los punteros a funciones de C++, o losatributos, lo cual permite añadir metainformación sobre el código.

C# consigue compaginar bien orientación a objetos y rendimiento. Algunos lenguajes, como Smalltalk,se basan en que "todo es un objetos". Esta aproximación tiene la ventaja de una completa orientación aobjetos, pero tiene la desventaja de ser muy ineficiente. Para mejorar el rendimiento, otros lenguajes,como Java, separan el sistema de tipos en tipos primitivos y todo el resto, dando lugar a mejorrendimiento en los tipos primitivos, pero en una separación a veces molesta entre tipos primitivos y tiposdefinidos por el usuario. En C# se han aunado ambas aproximaciones presentando lo que se llama unsistema unificado de tipos, en el que todos los tipos, incluso los primitivos, derivan de un tipo objetocomún, a la vez que permite el uso de optimizaciones para tipos primitivos y tipos sencillos.

1.1.4. Librería del lenguaje

Contrariamente a la mayoría de lenguajes, C# no incluye una librería específica, sino que utiliza lalibrería de clases de la plataforma .NET para todas sus necesidades, desde utilización de la consola hastala programación multiproceso o el cifrado de seguridad.

1.1.5. Estandarización

Además de los méritos técnicos, uno de las razones del éxito de C# y la plataforma .NET ha sido por elproceso de estandarización que Micrsoft ha seguido (y que ha sorprendido a más de uno). Micrsoft, enlugar de reservarse todos los derechos sobre el lenguaje y la plataforma, ha publicado lasespecificaciones del lenguaje y de la plataforma, que han sido posteriormente revisadas y ratificadas porla Asociación Europea de Fabricantes de Computadoras (ECMA). Esta especifícación (que se puededescargar libremente de aquí (http://www.ecma-international.org/publications/standards/Ecma-334.htm))permite la implementación del lenguaje C# y de la plataforma .NET por terceros, incluso en entornosdistintos de Windows.

2

Page 9: tutorial csharp.pdf

Capítulo 1. Introducción

1.1.6. C# frente a Java

C# y Java son lenguajes muy similares, de sintaxis basada en C/C++, orientados a objetos y queincluyena las características más importantes de los lenguajes modernos, como gestión automática dememoria y compilación a código intermedio. Pero por supuesto, también hay diferencias. Una de lasdiferencias más importantes es que C# es mucho más cercano a C++ en cuanto a diseño se refiere. C#toma casi todos sus operadores, palabras reservadas y expresiones directamente de C++. También se hanmantenido algunas características que en Java se han desestimado. Por ejemplo las enumeraciones. Nohay enumeraciones en Java y sin embargo era un concepto muy usado en C/C++. En C# se hanmantenido las enumeraciones, y se han adaptado al nuevo lenguaje, de forma que ahora lasenumeraciones no son simplemente enteros, sino que son tipos de tipado seguro que derivan deSystem.Enum en la librería de clases base. Una enumeración de tipo "ej1" no se puede cambiar con unaenumeración de tipo "ej2" sin una conversión. Creo que esa es una diferencia importante. Toda laestructura de los espacios de nombres es mucho más cercana a C++.

Otra característica que no está presente en Java es la posibilidad de trabajar directamente con direccionesde memoria. Si bien tanto Java como .NET proporcionan recogida automática de basura, en C# esposible usar lo que se denomina "código no seguro". Cuando se usa código no seguro en C# es posibleoperar con punteros de forma muy similar a como se haría en C/C++, pero el código que utiliza punterosse queda marcado como no seguro y no se ejecuta en entornos en los que no tiene permisos.

1.1.7. C# frente a C++

Puesto que C# se ejecuta en una máquina virtual, ésta se hace cargo de la gestión de memoria y por lotanto el uso de punteros es mucho menos importante en C# que en C++. C# también es mucho másorientado a objetos que C#, hasta el punto de que todos los tipos usados derivan en última instancia eltipo ’object’. Además, muchos tipos se usan dde forma distinta. Por ejemplo, en C# se comprueba loslímites de los vectores antes de usarlos, evitando así que se pueda escribir pasado el final del vector.

Al igual que Java, C# renuncia a la idea de herencia múltiple de clases presente en C++. Sin embargo,referido a clases, C# implemente ’propiedades’ del tipo de las que existen en Visual Basic, y los métodosde las clases son accedidos mediante ’.’ en lugar de ’::’.

1.1.8. ¿Porqué C#?

La plataforma .NET acepta varios lenguajes. Por ahora, C#, Visual Basic, C++ gestionado, Nemerle,FORTRAN, Java, Python, etc. , y con capacidad para aceptar prácticamente cualquier lenguaje. Entoncesla pregunta es, ¿porqué se eligió C# en lugar de cualquier otro lenguaje?.

La razón fundamental es que C# se diseñó para la plataforma .NET y es capaz de utilizar todo supotencial. También es cierto que es un lenguaje "limpio" en el sentido de que al no tener queproporcionar compatibilidad hacia detrás se ha tenido más libertad en el diseño y se ha puesto especialincapié en la simplicidad. Por ejemplo, en C# hay un tipo de clase y siempre se le aplica el recolector de

3

Page 10: tutorial csharp.pdf

Capítulo 1. Introducción

basura mientras que en C++ gestionado hay dos tipos de clases, una a las que se le aplica el recolector yotra a la que no.

1.2. Primer ejemplo

Para empezar con C#, que mejor que con un ejemplo básico para entender la estructura básica de unprograma en C# y empezar a conocer las características del lenguaje.

El ejemplo sería el siguiente:

//Declaración del espacio de nombresusing System;

//Clase de nuestro programa principalclass PrimerEjemplo{

public static void Main(){

// Escribir a la consolaConsole.WriteLine ("Bienvenido al tutorial de C# de M-H");

}}

Todo programa que escribamos en C# va a tener una estructura similar a esta en la que declararemosuno/varios espacios de nombres a utilizar (System), una clase y el método "Main" de esa clase con lassentencias de nuestro programa. Por cierto, todos los archivos en C# llevan la extensión .cs (no hayficheros de cabecera ni nada similar ).

Una vez ya visto el primer ejemplo, para compilarlo habría que utilizar mcs (mono compiler suite), quees el compilador de mono de C#, implementado según las especificaciones del lenguaje segúnECMA-334, que será el encargado de generar los ejecutables en código intermedio (CIL) queposteriormente tendrá que ser ejecutado por mono. Para ello se procedería de la siguiente forma:

# mcs ejemplo.cs# mono ejemplo.exe

Dando el siguiente resultado:

Bienvenido al tutorial de C# de M-H

Sobre todo si estás en entornos Windows tal vez querrás que la consola no se cierre automáticamente.Entonces tendrás que escribir Console.Read() detrás de la última sentencia para que el programa espere aque pulses una tecla para poder cerrarse. Esto se hace extensible a todos los ejemplos de este tutorial.

4

Page 11: tutorial csharp.pdf

Capítulo 1. Introducción

En nuestro ejemplo, System es un espacio de nombres y con él le estaremos diciendo que podamos usartodos las clases asociadas a ese espacio de nombres, en nuestro caso la clase Console con el métodoWriteLine que es el que se encarga de escribir por pantalla el mensaje que queramos mostrar.

La clase PrimerEjemplo es la que va a contener la definición de datos y métodos que va a usar nuestroprograma al ejecutarse. Además de clases veremos que se pueden definir otros tipos diferentes deelementos tales como estructuras e interfaces con los métodos asociados a estos tipos.

Al utilizar el método Main le estaremos diciendo que nuestro programa empieza ahí y tiene losmodificadores static (sólo se va a usar en esa clase) y void diciendole que nuestro método no va adevolver nada.

Ya únicamente queda mostrar por pantalla el resultado, esto se hace utilizando la clase Console y elmétodo asociado WriteLine que es el que muestra por pantalla el mensaje de bienvenido. Para hacerreferencia a los métodos en WriteLine en C# se va a hacer con el operador ".", en diferencia a lo quepuede ser en C++ el "::".

Para los comentarios se utiliza tanto // como /* esto es un comentario */ al más puro estilo de C++ y quetodas las sentencias tienen que acabar con ; y los delimitadores de bloque son { y }.

5

Page 12: tutorial csharp.pdf

Capítulo 2. Tipos

2.1. Tipos

2.1.1. Importancia de los tipos de datos

Los tipos son la base de cualquier programa. Un tipo no es más que un espacio en el que se almacena unainformación, ya sean números, palabras o tu fecha de nacimiento.

Los tipos de datos son especialmente importantes en C# porque es un lenguaje con información de tipos.Esto significa que, en todas las operaciones, el compilador comprueba los tipos para ver sucompatibilidad. Las operaciones no válidas no se compilan. De esta forma se evitan muchos errores y seconsigue una mayor fiabilidad

2.1.2. Tipos en C#

En C# los tipos básicos no son más que alias para tipos predefinidos en la librería base de la plataformaMono/.NET . Así, el tipo enteroint, no es más que una forma rápida de escribir System.Int32 .

Los tipos del lenguaje C# son divididos en dos grandes categorías: tipos por valor y tipos por referencia.Existe una tercera categoría de tipos, disponible solo cuando se usa código no seguro: los punteros, quese discutirán más adelante.

Los tipos por valor difieren de los tipos por referencia en que las variables de los tipos por valorcontienen directamente su valor, mientras que las variables de los tipos por referencia almacenanreferencias a objetos. Con los tipos por referencia, es posible que dos variables se refieran al mismoobjeto, y por tanto es posible que las operaciones sobre una variable afecten al objeto al que hacereferencia otra variable. Con los tipos por valor, cada variable tienen su propia copia de los datos, y lasoperaciones sobre una no afectará a la otra.

2.2. Tipos por valor

2.2.1. Tipos por valor

Como hemos comentado, el término tipo por valor indica que las variables de esos tipos contienendirectamente sus valores. De esta forma, los tipos por valor actúan de forma muy parecida a los tipos de

6

Page 13: tutorial csharp.pdf

Capítulo 2. Tipos

datos de otros lenguajes de programación como C++. Los tipos por valor también se conocen comotipossencillos

Tabla 2-1. Tipos por valor

Tipo C# Nombre para laplataformaMono/.NET

Con signo? Bytes utilizados Rango

bool System.Boolean No 1 verdadero o falso

byte System.Byte No 1 0 hasta 255

sbyte System.SByte Si 1 -128 hasta 127

short System.Int16 Si 2 -32.768 hasta 32.767

ushort System.Uint16 No 2 0 hasta 65535

int System.Int32 Si 4 -2.147.483.648 hasta2.147.483.647

uint System.Uint32 No 4 0 hasta4.394.967.395

long System.Int64 Si 8 -9.223.372.036.854.775.808hasta9.223.372.036.854.775.807

ulong System.Uint64 No 8 0 hasta18446744073709551615

float System.Single Si 4 Approximadamente±1.5E-45 hasta±3.4E38 con 7cifras significativas

double System.Double Si 8 Approximadamente±5.0E-324 hasta±1.7E308 con 7cifras significativas

decimal System.Decimal Si 12 Approximadamente±1.0E-28 hasta±7.9E28 con 28 ó29 cifrassignificativas

char System.Char 2 Cualquier carácterUnicode (16 bits)

bool System.Boolean 1/2 verdadero o falso

7

Page 14: tutorial csharp.pdf

Capítulo 2. Tipos

2.2.2. Enteros

Los tipos que sirven para almacenar números enteros son: byte, sbyte. short, ushort, int, uint, long yulong. Como se aprecia en la tabla, C# define versiones con y sin signo para tipos con los mismo bytesutilizados. La diferencia entre enteros con signo y sin signo radica en el modo de interpretar el bit denivel superior del entero. Si se especifica un entero con signo, el compilador entenderá que el primer bitindica el signo: 0 si es positivo, 1 si es negativo. Sin embargo, los enteros sin signo, ese bit se puedeutilizar para almacen el número y así se consigue que los enteros sin signo puedan almacenar números eldoble de grandes que los enteros con signo.

Probablemente el tipo más utilizado es el int, pués se utiliza para controlar matrices, inidizar arreglosademás de las operaciones normales con enteros. Además, se trata de un entero de tamaño medio: máspequeño que long y ulong, pero más grande que byte, sbyte, short y ushort.

El siguiente ejemplo muestra la declaración y uso de algunos tipos enteros calculando el número desegundos en una hora, dia y en un año.

using System;

class Enteros{public static void Main(){

int Minuto = 60; //segundos por minutoint Hora = Minuto*60;int Dia = Hora*24;

long Anio = Dia*365;

Console.WriteLine("Segundos en un dia: {0}", Dia);Console.WriteLine("Segundos en un año: {0}", Anio);

}}

De nuevo hemos usado el método Console.WriteLine para imprimir los resultados por la consola. Elidentificador {0} dentro de la cadena de texto indica que se sustituye {0} por el primer argumento. sihubiera más de un argumento, se seguiría con {1}, y así sucesivamente. Por ejemplo, las dos líneas queutilizan Console.WriteLine se pueden simplificar así:

Console.WriteLine("En un dia: {0}; en un año: {1}", Dia, Anio );

La clase Console, se describe más en detalle en

8

Page 15: tutorial csharp.pdf

Capítulo 2. Tipos

2.2.3. Tipos de coma flotante

Los tipos de coma flotante sirven para representar a números con parte fraccionaria. La representaciónpor supuesto puede no ser exacta, bien por errores de la máquina, bien porque el número de decimalesque se puede alojar es finito.

Existen dos clases de tipos de punto flotante, float y double. De los dos, el más usado es double, pués esel valor que devuelven la mayoría de las funciones matemáticas de la librería base.

El siguiente ejemplo calcula la raíz cuadrada y el logaritmo de dos:

using System;

class Flotante{public static void Main(){

int a = 2;double log2 = Math.Log(2);double raiz2 = Math.Sqrt(2);

Console.WriteLine("El logaritmo de dos es {0}", log2 );Console.WriteLine("La raiz de dos es {0}", raiz2 );

}}

y la salida será la siguiente:

El logaritmo de dos es 0.693147180559945La raiz de dos es 1.4142135623731

si intentamos cambiar el tipo de log2 a otro de menos precisión, como float o int, el compiladorprotestará. Esto se debe, como hemos dicho a que el valor devuelto por Math.Log() es de tipo double y sise quiere convertir a float, pués se perderán datos. Lo mismo ocurre con la mayoría de los miembros dela clase Math, como Math.Sin(), Math.Tan(), etc.

2.2.4. El tipo decimal

El tipo decimal es un tipo "nuevo" en el sentido de que no tiene equivalente en C/C++. Es muy parecidoa los tipo de coma flotante float y double.

En la aritmética de los tipos de coma flotante ordinarios, se pueden producir leves errores de redondeo.El tipo decimal elimina estos errores y puede representar correctamente hasta 28 lugares decimales. Estacapacidad para representar valores decimales sin errores de redondeo lo hace especialmente eficaz paracálculos monetarios.

9

Page 16: tutorial csharp.pdf

Capítulo 2. Tipos

2.2.5. El tipo bool

El tipo bool sirve para expresar los valores verdadero/falso, que en C# se muestran con las palabrasreservadas true y false.

En C#, por ejemplo, una instrucción if solo puede estar gobernada por un valor bool, no como en C/C++,que lo puede estar también por un entero. De esta forma se ayuda a eliminar el error tan frecuente enprogramadores de C/C++ cuando usa "=" en lugar de "==". En definitiva, la inclusión del tipo bool en ellenguaje ayuda a la claridad del código y evita algunos errores muy comunes.

El siguiente ejemplo, muestra algunos usos del tipo bool:

using System;

class Booleano{public static void Main(){

bool b;

b = true;

Console.WriteLine("b es {0}", b);

if(b){

Console.WriteLine("esto saldrá");}b = false;

if(b){

Console.WriteLine("esto no saldrá");}

Console.WriteLine("2==2 es {0}", 2==2);}

}

En la última línea se muesta que el operador "==" también devuele un valor booleano. El resultadodebería ser el siguiente:

b es Trueesto saldrá2==2 es True

10

Page 17: tutorial csharp.pdf

Capítulo 2. Tipos

2.2.6. Tipo arreglo

En C# se pueden construir arreglos de prácticamente cualquier tipo de dato. Los arreglos, tambiénllamados vectores o arrays, no son más que una sucesión de datos. Por ejemplo, el concepto matemáticode vector es una sucesión de números y por lo tanto es un arreglo unidimensional. Así, podemosconstruir arreglos de objetos, de cadenas de texto, y, por supuesto, arreglos de enteros:

using System;

class Arreglo{public static void Main(){

int[] arr = new int[3];arr[0] = 1;arr[1] = 2;arr[2] = 3;

Console.WriteLine( arr[1] );}

}

En este ejemplo se crea un arreglo arr unidimensional con capacidad para 3 enteros, y luego se le asignaa cada valor un entero distinto (nótese que se comienza a contar a partir de 0 ). Existe una forma máscorta para declarar el arreglo y asignarle las variables:

int[] arr = {1,2,3};

También se pueden crear arreglos bidimensionales ( de la misma forma para más dimensiones). En esecaso la sintaxis para declarar un arreglo bidimensional de enterios será

int[,] arr

en contraposición a C/C++, en el que se declararía como

int[][] arr

De esta forma, un arreglo bidimensional se declararía y utilizaría de la siguiente forma:

using System;

class Arreglo2{public static void Main(){

int[,] arr = new int[2,2];arr[0,0] = 1;

11

Page 18: tutorial csharp.pdf

Capítulo 2. Tipos

arr[1,0] = 2;arr[0,1] = 3;arr[1,1] = 4;

Console.WriteLine( arr[1,1] );}

}

que, igual que el ejemplo anterior, podíamos hacer declarado todo el arreglo de la siguiente forma:

int[,] arr = {{1,2},{3,4}};

Se hablará con más detalle sobre arreglos en la sección

2.2.7. El tipo char

El tipo char permite almacenar un carácter en formato unicode de 16 bits, lo que nos garantiza que losacentos se ven de forma adecuada y además permite la representación de otros alfabetos, como el griego,cirílico, etc. Para introducir un carácter se utilizan comillas simples, de forma de declarar un caráctersigue la estructura

char letra = ’a’

De igual forma que hemos hecho con los enteros, es posible declarar un arreglo de char

char[] cadena = {’a’, ’b’, ’c’ };

aunque para almacenar algunas cadenas de caracteres, como las palabras, es más indicado usar el tipostring .

12

Page 19: tutorial csharp.pdf

Capítulo 3. Estructuras de control

3.1. Estructuras de control

En este capítulo se describen algunas sentencias que sirven para controlar la ejecución de un programa.Algunas son muy similares a las existentes en otros lenguajes, como las sentencias if, for, while, etc. yotras, como foreach, throw o continue, son algo más específicas.

3.1.1. Instrucción if

Esta sentencia sirve para ejecutar unas instrucciones en caso de que se cumpla determinada condición.La forma completa de la instrucción if es

if( condición ) {instrucción1;instrucción2;...

}else {

instrucción1;instrucción2;...

}

donde la cláusula else es opcional. Si la condición es verdadera, se ejecutarán las instrucciones dentro delbloque if, y si es falsa, se ejecutará el bloque else. El valor que controla el if debe ser de tipo bool. Elsiguiente ejemplo

//programa que determina si un valor es positivo o negativousing System;

class InstruccionIf{

public static void Main(){

double d;

Console.WriteLine("Introduce un numero");d = Double.Parse( Console.ReadLine() );

if( d>0 ){

Console.WriteLine("El numero {0} es positivo", d);}else{

Console.WriteLine("El numeros {0} es negativo", d);

13

Page 20: tutorial csharp.pdf

Capítulo 3. Estructuras de control

}}

}

te pide que introduzcas un número y dependiendo de si se cumple que dicho número es mayor que cero(condición), se ejecuta un bloque u otro.

La sentenciad = Double.Parse( Console.ReadLine() );tal vez requiera algo de explicación adicional. Enrealidad, conConsole.ReadLine()estamos leyendo lo que el usuario introduce por pantalla, que es unacadena de caractéres, y conDouble.Parselo que hacemos es interpretar esa cadena de caractéres yconventirna en un tipo numérico double, de forma qued tendrá el valor del número que introduzcamospor la consola.

Las intrucciones if se pueden anidar, y existe también una extensión de la sentencia if, la sentenciaif-else-if. Su formato es el siguiente:

if( condición ){

instrucciones;}else if( condición ){

instrucciones;}...else{

instrucciones;}

Las instrucciones condicionales se evalúan de arriba a abajo. Tan pronto como se encuentra unacondición true, se ejecuta la instrucción asociada con ella, y el resto de la escalera se omite. Si ningunade las condiciones es true, se ejecutará la última instrucción else. La última instrucción else actúa comocondición predeterminada, es decir, si no funciona ninguna de las otras pruebas condicionaes, se realizaesta última instrucción. Si no existe esta instrucción else final y el resto de de las condiciones son falsas,entonces no se realizará ninguna acción. El siguiente ejemplo

using System;

class IfElseIf{public static void Main(){

string opcion;

Console.WriteLine("Elija una opción (si/no)");opcion = Console.ReadLine();

if( opcion=="si" )

14

Page 21: tutorial csharp.pdf

Capítulo 3. Estructuras de control

{Console.WriteLine( "Muy bien, ha elegido si" );

}else if( opcion=="no" ){

Console.WriteLine( "Ha elegido no" );}else{

Console.WriteLine("No entiendo lo que ha escrito");}

}}

le pide al usuario que elija una opción si/no y la procesa usando una estructura if-else-if. Si la opción noes ni "si" ni "no", entonces se ejecuta la sentencia else por defecto, que imprime por pantalla el mensaje"No entiendo lo que ha escrito"

3.1.2. Instrucción switch

La instrucción switch es muy parecida a la estructura if-else-if, sólo que permite seleccionar entre variasalternativas de una manera más cómoda. Funciona de la siguiente manera: el valor de una expresión seprueba sucesivamente con una lista de constantes. Cuando se encuentra una coincidencia, se ejecuta lasecuencia de instrucciones asociada con esa coincidencia. La forma general de la instrucción switch es lasiguiente:

switch( expresión ){case constante1:

instrucciones;break;

case constante2:instrucciones;break;

...default:

instrucciones;break;

}

La sentencia default se ejecutará sólo si ninguna constante de las que siguen a case coincide conexpresión. Es algo similar al else final de la instrucción if-ese-if.

Sin más, vamos a por un ejemplo

using System;

class InstruccionSwitch{public static void Main(){

15

Page 22: tutorial csharp.pdf

Capítulo 3. Estructuras de control

string s;

Console.WriteLine( "Elige hacer algo con los números 2 y 3");Console.WriteLine( " + para sumarlos" );Console.WriteLine( " - para restarlos" );Console.WriteLine( " * para multiplicarlos" );Console.WriteLine( " / para dividirlos (division entera)" );

s = Console.ReadLine();

switch(s){case "+":

Console.WriteLine("El resultado es {0}", 2+3);break;

case "-":Console.WriteLine("El resultado es {0}", 2-3);break;

case "*":Console.WriteLine("El resultado es {0}", 2*3);break;

case "/":Console.WriteLine("El resultado es {0}", 2/3);break;

default:Console.WriteLine("No te entiendo");break;

}}

}

El cual solicita al usuario que inserte uno de los símbolos +-*/ , y con un switch compara los resultadospara hacer diferentes acciones dependiendo del valor de s, que es la cadena de caracteres que almacena laelección del usuario. El resultado debería de ser algo parecido a esto:

Elige hacer algo con los números 2 y 3+ para sumarlos- para restarlos* para multiplicarlos/ para dividirlos (division entera)

*El resultado es 6

Como habrá notado, al final de todo case siempre hay una sentencia break. Esto no es obligatorio, puedehaber en su lugar otra sentencia de salto, como un goto, pero siempre tiene que haber una sentencia desalto, incluso en el default final, a no ser que la sentencia case esté vacía. En caso contrario se obtiene unerror en tiempo de compilación. Otros lenguajes, como C/C++ o Java no tienen esta restricción. La razónde adoptarla en C# es doble: por un lado, elimina muchos errores comunes y en segundo lugar permite alcompilador reorganizar las sentencias de los case, y así permitir su optimización.

16

Page 23: tutorial csharp.pdf

Capítulo 3. Estructuras de control

3.1.3. Bucle for

El bucle for de C# es idéntico al encontrado en los lenguajes C/C++, Java. El formato general es

for( inicialización; condición; iteración ){

instrucciones;}

Las sentencias de inicialización se ejecutan una vez al principio y sirven principalmente para asignarvalores a las variables que servirán de contador. Las sentencias de condición, por su parte, se ejecutancada vez que el bucle vuelve al principio y sirven para controlar el bucle: éste seguirá realizándosesiempre y cuando estas codiciones sea true. Las sentencias de iteración se ejecutan también cada vez quese realiza una nueva "vuelva" en el bucle, y sirven para cambiar el estado de las variables que gobiernanlas sentencias de condición. Pero todo esto se entiende mejor con un ejemplo

using System;

class BucleFor{public static void Main(){

int i; //el contador

for( i = 0; i < 10; i++){

Console.WriteLine( i );}

}}

Este ejemplo imprime por pantalla los 10 primero enteros positivos. Es un caso muy simple y muy típicode bucle for. Por cierto, el operador ++ lo que hace es que añade una unidad a la variable a la queacompaña, de forma que, por ejemplo, 9++ es 10. De esta forma, la variable se incrementa a cada vuelta.

En el ejemplo anterior, las sentencias de inicialización y te iteración eran únicas, pero esto no tieneporqué ser así, se pueden utilizar varias sentencias separadas por comas. Por ejemplo, se pueden usar dosvariables para controlar el bucle

using System;

class BucleFor2{public static void Main(){

int i;int j;

for( i=0, j=10; i<j; i++, j--){

Console.WriteLine("( {0} , {1} )", i, j);}

17

Page 24: tutorial csharp.pdf

Capítulo 3. Estructuras de control

}}

Por su parte, la expresión condicionar del bucle for puede ser culquier expresión que genere un valorbooleano. En este caso se ha usado i>j, pero también hubiera sido válida i==5, true (el bucle se realizaráindefinidamente), false (el bucle no se realizará).

3.1.4. Bucle while

El bucle while es un bucle que se realizahastaque se cumpla determinada condición. Tiene la forma

while( condición ){

instrucciones;}

Donde la condición de nuevo tiene que ser un valor booleano. Tiene una estructura muy sencilla, así quevamos directamente a ver un ejemplo.

using System;

class BucleWhile{public static void Main(){

int i = 0;while( i<10){

Console.WriteLine( i );i = i+1;

}}

}

En el que se realiza lo mismo que en el ejemplo anterior, sólo que ahora con un bucle while.

3.1.5. Bucle do-while

Se trata de una ligera variante del bucle anterior, con la diferencia de que ahora primero se ejecutan lasinstrucciones y luego se evalúa la condición, de forma que tiene tiene una estructura:

do{instrucciones;

}

18

Page 25: tutorial csharp.pdf

Capítulo 3. Estructuras de control

while( condición );

El siguiente ejemplo

class BucleDoWhile{public static void Main(){

string s = "";

do{

Console.WriteLine( "Introduce si para salir del bucle" );s = Console.ReadLine();

}while( s != "si" );

}}

muestra un programa que ejecuta un bucle hasta que el usuario introduce "si". Por cierto, != es locontrario de ==, es decir, != devuelve true cuando los valores comparados son distintos.

3.1.6. Bucle foreach

El bucle foreach se utiliza para hacer iteraciones sobre elementos de una colección, como pueden ser losenteros dentro de un arreglo de enteros. La sintaxis sigue la siguiente estructura:

foreach( tipo in coleccion ){

instrucciones;}

Como hemos comentado, el uso más inmediato es iterar sobre un arreglo de números:

using System;

class BucleForeach{public static void Main(){

int[,] arr = {{1,2},{2,3}};

foreach( int elem in arr ){

Console.WriteLine( elem );}

}}

19

Page 26: tutorial csharp.pdf

Capítulo 3. Estructuras de control

Este ejemplo sólo imprime los valores de una matriz, pero como se puede comprobar mejora mucho laclaridad del código comparándolo con una implementación con bucles for como esta

using System;

class BucleForeach{public static void Main(){

int i, j; //seran los indexadores de la matriz

int[,] arr = {{1,2},{2,3}};

for(i = 0; i<2; i++ ){

for( j = 0; j<2; j++ ){

Console.WriteLine( arr[i,j] );}

}}

}

Además, es posible utilizar el bucle foreach con cualquier tipo que sea una colección, no solo conarreglos, como veremos más adelante.

3.2. Cambiando el rumbo

Existen varias formas de cambiar el rumbo de los programas, mediante sentencias de salto incondicional,como goto, return, así como formas de cambiar el rumbo de bucles, como continue o break

3.2.1. Instrucción goto

La instrucción goto sirve para realizar saltos incondicionales a otras partes del programa. Losprogramadores siempre advierten de los peligros de usar demasiado esta sentencia (es propensa a escribircódigo ilegible), aunque el contadas ocasiones puede ser de gran utilidad.

Como su propio nombre indica, esta sentencia nos lleva a alguna parte del programa, de forma que habráque marcar con una etiqueta la parte del programa a la que deseemos que nos envíe. Estas etiquetas sedeclaran simplemente con el nombre de la etiqueta y dos puntos. Lo vemos en el siguiente ejemplo

using System;

class Goto{public static void Main(){

goto dos;

20

Page 27: tutorial csharp.pdf

Capítulo 3. Estructuras de control

uno:Console.WriteLine("Esto no se ejecutará");

dos:Console.WriteLine("Hemos saltado directamente aquí!");

}}

Es un ejemplo muy simple con dos etiquetas: uno y dos. En la ejecución normal de un programa, primerose ejecutaría uno y después dos. Sin embargo, la instrucción goto hace que saltemos directamten a lainstrucción dos.

Seguro que se te ocurren multitud de situaciones en las que es útil la instrucción goto, así que noinsistiremos más sobre esto.

3.2.2. Sentencia return

Colocar return en alguna parte del programa provoca que el método termine y devuelva el valorespecificado. Si estamos en el método Main() , que por ahora es el único caso que conocemos, returnprovocará el final del programa. En el ejemplo

using System;

class SentenciaReturn{public static void Main(){

double a;

principio:Console.WriteLine("Introduce un numero:");a = Double.Parse( Console.ReadLine() );

if( a > 10 ){

return;}else{

goto principio;}

}}

utilizamos también la instrucción return para implementar un programa en el que se le pide al usuarioque introduzca un número y solo termina si el número es mayor que 10. Este es un mal ejemplo de usode goto y return, pués se podría haber conseguido lo mismo con un bucle while, pero por ahora sirve. Laimportancia de la sentencia return se verá con más claridad cuando veamos métodos.

21

Page 28: tutorial csharp.pdf

Capítulo 3. Estructuras de control

3.2.3. La instrucción continue

Es posible realizar la repetición temprana de un bucle omitiendo el resto del código que contenga elbucle mediante continue. Su comportamiento es muy sencillo:

using System;

class UsoContinue{public static void Main(){

int i;

for(i = 0; i<10; i++ ){

continue;Console.WriteLine("Esto nunca se ejecutará!");

}Console.WriteLine("Adios!");

}}

Al compilar el siguiente ejemplo, es posible que el compilador emita una advertencia de que hadetecatado código inalcanzable -esto es, que nunca llegará a ejecutarse. Esto no es problema, pués esjustamente lo que queremos. Por lo demás, el ejemplo es muy sencillo, dentro del bucle for lo primeroque se hace es ejecutar la instrucción continue, de forma que el bucle se repite sin llegar nunca alsiguiente Console.WriteLine().

3.2.4. break para salir de bucles

La instrucción break es el complemento lógico de continue, de forma que si continue repite el bucle,break, lo termina. Así, cuando se encuentra una sentencia break dentro de un bucle, éste inmediatamentefinaliza y se sigue la ejecución del programa.

using System;

class UsoBreak{public static void Main(){

int i;

for( i = 0; i<20; i++){

if( i> 10 ){

break;}Console.WriteLine( i );

}

22

Page 29: tutorial csharp.pdf

Capítulo 3. Estructuras de control

}}

Este ejemplo muestra el uso de break para imprimir los enteros de 0 a 10. Si no estuviera la sentenciabreak mostraría los enteros hasta 20, pero al llegar a 11 se ejcuta la instrucción if que desemboca en unbreak que hace que la ejecución salga del bucle.

3.2.5. throw y el manejo de excepciones

La sentencia throw sirve para lanzar excepciones y así modificar el flujo del programa, de mondo quepasa a otra instrucción si se captura la excepción, o se termina el programa si la excepción no escapturada. Esta sentencia se verá en el capítulo sobre manejo de excepciones

23

Page 30: tutorial csharp.pdf

Capítulo 4. Operadores

4.1. Operadores

Los operadores son símbolos que permiten realizar operaciones con uno o más datos, para dar unresultado. El ejemplo clásico y obvio de operador es el símbolo de la suma (+), aunque hay otros muchos.

Vamos a hacer un repaso a los tipos de operadores que tenemos en C#:

• Operadores Aritméticos: Son la suma (+), resta (-), producto (*), división (/) y módulo (%).

Éstos son bastante inmediatos pués funcionan de la misma manera que en álgebra. Solo hay que teneren cuenta que la división entre enteros devuele un entero, de forma que 3/2 devuelve 1 ( el resultado setrunca).

Es interesante saber que a las operaciones aritméticas les podemos añadir otros dos operadores,checked y unchecked, para controlar los desbordamientos en las operaciones. La sintaxis es esta:

variable1 = checked (34+4);

o bien

variable1 = unchecked (34+4);

Si en una operación regida por checked se produce un desbordamiento, dará un error en tiempo decompilación si los operadores son constantes, o lanzará una excepción System.OverflowException sison variables. En cambio, si la operación es unchecked, cuando se produce un desboramiento nosdevolverá el resultado truncado, para que "quepa" en el resultado esperado.

• Operadores Lógicos: Son "and" (&& y &), "or" (|| y |), "not" (!) y "xor" (^).

La diferencia entre && y &, y entre || y | es que && y || hacen lo que se llama "evaluación perezosa":si evaluando sólo la primera parte de la operacion se puede deducir el resultado, la parte derecha no seevaluará. Es decir, si tenemos por ejemplo:

false && (otra cosa)

El resultado de esta operación siempre será false, y (otra cosa) ni siquiera se evalúa. De igual forma, sitenemos

true || (otra cosa)

24

Page 31: tutorial csharp.pdf

Capítulo 4. Operadores

el resultado será true, y la parte derecha nunca se evaluará.

• Operadores relacionales: igualdad (==), desigualdad (!=), mayor que (>), menor que (<), mayor oigual que (>=) y menor o igual que (<=)

• Operadores de Manipulación de Bits: Tenemos las siguientes operaciones: and (&), or (|), not (~), xor(^), desplazamiento a la izquierda (<<), y desplazamiento a la derecha (>>). El desplazamiento a laizquierda rellena con ceros. El desplazamiento a la derecha, si se trata de un dato con signo, mantieneel signo. Si no, rellena con ceros.

• Operadores de Asignación: El operador básico de asignación es =. Además, tenemos las clásicasabreviaturas +=, -=, *=, /=, &=, |=, ^=, <<= y >>=

Estas abreviaturas se usan para evitar tecleo en operaciones como esta:

variable1 = variable1 + variable2;

Se puede escribir de esta forma abreviada:

variable1 += variable2;

También tenemos operadores de incremento (++) y decremento (--), que incrementan en una unidad elvalor de la variable sobre la que se aplican. Por tanto, estas tres líneas de código son casi iguales:

variable1 = variable1 + 1;variable1 += 1;variable1++;

El "casi iguales" lo ponemos porque en muchas máquinas, el operador ++ es más rápido que laoperación "+ 1", ya que el compilador lo traduce a una única instrucción máquina.

Hay que tener en cuenta que no es lo mismo poner variable++ que ++variable. Ambas formas soncorrectas, pero no significan lo mismo. Lo vemos con un ejemplo:

variable1 = ++variable2;variable1 = variable2++;

En el primer caso, primero se incrementa variable2 y luego se hace la asignación. En el segundo casoprimero se hace la asignación, y luego el incremento.

• Operador Condicional: Es el único operador de C# que tiene tres operandos. Su sintaxis es esta:

<condición> ? <expresión1> : <expresión2>

Quiere decir que si la condición es true, se evalúa expresión1, y si es falsa, se evalúa expresión2. Se vemás claro con un ejemplo:

b = (a>0) ? a : 0;

25

Page 32: tutorial csharp.pdf

Capítulo 4. Operadores

Esto quiere decir que si a es mayor que 0, la expresión será b = a, y si a no es mayor que 0, laexpresión será b = 0;

Ojo: No confundir con un "if". Este operador devuelve un valor, mientras que el if es sólamente unainstrucción.

• Operadores de Delegados: Para añadir métodos a un delegado se hace con + y +=, y para quitarselos,con - y -=.

• Operadores de Acceso a Objetos: El operador para acceder a los miembros de un objeto es el punto.Así esta expresión:

A.metodo1 ();

nos permite acceder al método "metodo1" del objeto A.

• Operadores de Punteros: Tenemos varios operadores. Para acceder a la dirección de memoria a la queapunta el puntero, lo hacemos con &puntero. Para acceder al contenido de la dirección de memoria,tenemos *puntero. Si lo que referencia el puntero es un objeto, podemos acceder a sus miembros conpuntero->miembro.

• Operadores de Obtención de Información sobre Tipos: Para averiguar el tipo de una variable, usamosel operador sizeof (variable). Nos devolverá un objeto de tipo System.Type. Si queremos hacer unacomparación, usamos algo como esto:

(expresion) is nombreTipo

que, como es lógico, nos devolverá un true o un false.

• Operadores de Conversión: Para convertir el tipo de un objeto en otro, precedemos el objeto quequeremos cambiar con el tipo al que queremos convertir, entre paréntesis, de esta forma:

variable1 = (int) variable2;

De esta forma, variable2 será tratada como si fuera un dato de tipo int, aunque no lo sea.

26

Page 33: tutorial csharp.pdf

Capítulo 5. Introducción a las clases

5.1. Introducción a las clases en C#

Como hemos dicho, C# es un lenguaje orientado objetos. A diferencia de lenguajes como C++ o Pythonen los que la orientación a objetos es opcional, en C# es imposible programar sin utilizar esta técnica.Una prueba de ello es que en C# cualquier método o variable está contenida dentro de un objeto. Porahora puede asumirse que un objeto y una clase son la misma cosa.

Una clase es como una plantilla que describe cómo deben ser las instancias de dicha clase, de forma quecuando creamos una intancia, ésta tendrá exactamente los mimos métodos y variables que los que tienela clase.Los datos y métodos contenidos en una clase se llaman miembros de la clase y se accede a ellossiempre mediante el operador "." . En el siguiente ejemplo, se definirá una clase, Clase1 y en el métodoMain se creará una instancia de Clase1 llamada MiClase. Una buena idea es jugar un poco con el códigopara ver que la instancia de la clase efectivamente tiene los mismos miembros que la clase Clase1 (quesería la plantilla de la que hablábamos antes)

using System;

//definimos nuestra claseclass Clase1{

public int a = 1;private double b = 3;public char c = ’a’;

}

//usamos la clase que hemos creadoclass UsoClase{

public static void Main(){

Clase1 MiClase = new Clase1(); // asi creamos una instancia de Clase1Console.WriteLine( MiClase.c ); //podemos llamar a los tipos que hay dentro de Clase1

}}

los identificadores public delante de los tipos que hay dentro de Clase1 son necesarios para luego poderser llamados desde otra clase, como en este caso, que estamos llamando a los miembros de una instanciade Clase1 desde UsoClase. Pero en las clases no solo hay variables, también podemos incluir métodos.

using System;

//definimos nuestra claseclass Clase1{

public int a = 1;public double b = 3;

27

Page 34: tutorial csharp.pdf

Capítulo 5. Introducción a las clases

public char c = ’a’;

public void Descripcion(){

Console.WriteLine("Hola, soy una clase");}

}

//usamos la clase que hemos creadoclass UsoClase{

public static void Main(){

Clase1 MiClase = new Clase1(); // asi creamos una instancia de Clase1Console.WriteLine( MiClase.c ); //podemos usar todos los tipos que hay dentro de Clase1MiClase.Descripcion();

}}

Podemos hacer más cosas con las clases, como heredar otras clases o implementar interfaces, pero eneste capítulo nos centraremos en el uso de métodos y variables.

5.1.1. Métodos

Los métodos, también llamados funciones, son trozos de código que reciben unos datos, hacen algo conesos datos, y a veces devuelven algún valor. En C#, todos los métodos se encuentran contenidas dentrode un objeto.

La estructura mínima de un método tiene las siguientes partes:

• Tipo devuelto

• Nombre del método

• Parámetros (puede ser vacío)

• Cuerpo del método

de forma que el siguiente método:

double Divide( double a, double b ){

return a/b;}

devuelve un tipo double, tiene por nombre Divide, los parámetos son dos tipo double, y el cuertpo delmétodo es simplemente "return a/2;".

28

Page 35: tutorial csharp.pdf

Capítulo 5. Introducción a las clases

Cuando queramos llamar a un método, debemos simplemente poner el nombre del método y susargumentos dentro de un paréntesis separados por comas. Para llamar al método Dive declarado antes,simplemente debemos escribir

Divide(8, 2);

Según lo que hemos visto, el ejemplo del método Divide() completo neceista tener tener una clase dondedefinirse y un método Main() donde ejecutarse.

using System;

class Metodo{double Divide( double a, double b ){

return a/b;}

}

class Principal{public static void Main(){

Metodo m = new Metodo();Console.WriteLine( m.Divide(8, 2) );

}}

5.1.2. Modificadores public y static

El modificador public lo hemos utilizado anteriormente. Se puede utilizar en la declaración de cualquiermétodo o variable, y como es de esperar, produce el efecto de que el campo afectado se vuelve&público&, esto es, se puede utilizar desde otras clases

using System;

class Metodo{public double Divide( double a, double b ){

return a/b;}

}

class Principal{public static void Main(){

29

Page 36: tutorial csharp.pdf

Capítulo 5. Introducción a las clases

Metodo m = new Metodo();Console.WriteLine( m.Divide(8, 2) );

}}

Si por ejemplo intentamos declarar el método Divide sin el modificador public, obtendremos un error entiempo de compilación. El modificadro complementario de public es private, que provoca que el métodoo dato solo sea accesible desde la clase en la que está declarado. Si no se especifica nada, se toma pordefecto el modificador private

De esta forma podríamos separar las clases Metodo y Principal en dos archivos separados, llamados porejemplo metodo.cs y principal.cs . Para compilar esto, bastará compilar ambos archivos al mismotiempo, de forma similar a esto:mcs principal.cs metodo.cs

Además, tampoco es necesario crear una instancia de la clase sólo para acceder a un método declaradoen ella. Para eso debemos anteponer a la declaración del método el modificador static. Los métodosestáticos se caracterizan por no necesitar una instancia de la clase para cumplir su función, pero comocontrapartida, no pueden acceder a datos propios de la clase.

using System;

class Metodo{public static double Divide( double a, double b ){

return a/b;}

}

class Principal{public static void Main(){

Console.WriteLine( Metodo.Divide(8, 2) );}

}

Los métodos estáticos se utilizan en multitud de situaciones. Por ejemplo, el métodoConsole.WriteLine() o las funciones de la librería matemática estándar no son más que métodos estáticosde sus respectivas clases

5.1.3. Constructores e instancias de una clase

Como hemos visto, las instancias de una clase se crean con la sintaxis

nombreclase objeto = new nombreclase( argumentos );

30

Page 37: tutorial csharp.pdf

Capítulo 5. Introducción a las clases

donde nombreclase es el nombre que le hemos dado a la definición de la clase, argumentos es una lista deargumentos posiblemente vacía y objeto es el nombre que queremos darle a la instancia de la clase.

Una vez creada una clase, sus miembros se inicializan a sus valores predeterminados ( cero para valoresnuméricos, cadena vacía para el tipo string, etc. ). La siguiente clase representa un punto sobre el plano,de forma que tiene dos valores públicos X e Y, y un método que calcula la distancia al origen del punto(módulo)

using System;

class Punto{public double X;public double Y;

public double Modulo(){

double d;d = Math.Sqrt(X*X + Y*Y); //Sqrt = raiz cuadradareturn d;

}}

class Principal{public static void Main(){

Punto A = new Punto();

A.X = 1;A.Y = -1;

Console.WriteLine("El modulo del punto (1,1) es: {0}", A.Modulo() );

}}

Ahora bien, la forma en la que se crea la instancia, es decir, inicializando los datos a cero (ejercicio:comprobar esto), se puede personalizar, de forma que podemos construir nuestro propio constructor quele diga a la clase los valores por defecto que debe tomar. Esto se realiza simplemente escribiendo dentrode la clase un método que tenga el mismo nombre que la clase y en el que no se especifica el valordevuelto. La clase Par con un constructor sería así:

using System;

class Punto{public double X;

31

Page 38: tutorial csharp.pdf

Capítulo 5. Introducción a las clases

public double Y;

public Punto() //constructor{

X = 1;Y = 1;

}

public double Modulo(){

double d;d = Math.Sqrt(X*X + Y*Y); //Sqrt = raiz cuadradareturn d;

}}

de forma que ahora al crear una instancia de la clase se crea el punto (1,1) en lugar del (0,0), que era elque se creaba por defecto. De esta forma, al crear la instancia, par ya contendrá los valores (1,1) .

En la práctica se utilizan mucho constructores con parámetos, de forma que al crear la instancia se leasignan valores según los parámetros. La siguiente implementación de Par contiene un constructor queacepta un par de valores, que servirán para inicializar los valores A y B

class Punto{public Punto( double val1, double val2)

{X = val1;

Y = val2;}...

}

También tenemos la posibilidad de clarar una clase con varios constructores (cada uno con diferenctesparámetros) Lo que hará el compilador de C# es buscar el constructor que se adecúe a los parámetros quele llegan, y ejecutarlo como si fuera un método más. Dependiendo de la llamada que se haga en el "new",usaremos un constructor u otro.

5.1.4. Sobrecarga de métodos

En C#, al igual que en C++ y en Java es posible definir varios métodos con el mismo nombre pero condistintos parámetros, de forma que el compilador decide a cuál se llama dependiedo de los parámetrosque le lleguen.

32

Page 39: tutorial csharp.pdf

Capítulo 5. Introducción a las clases

Esto es muy práctico, pués no tienes que renombrar cada función según el tipo de valor que acepta. Elsiguiente ejemplo implementa un par de métodos que elevan al cuadrado el valor que reciben, y seimplementan para tipos double y para int. En C, que es un lenguaje que no soporta sobrecarga demétodos, se tendría que haber llamado distinto a ambos métodos, por ejemplo alcuadrado_double yalcuadrado_int

using System;

class Eleva{public static double AlCuadrado( int a )

{return a*a;

}

public static double AlCuadrado( double a ){

return a*a;}

}

class Principal{public static void Main()

{Console.WriteLine("4 al cuadrado es {0}", Eleva.AlCuadrado(4) );

Console.WriteLine("3.2 al cuadrado es {0}", Eleva.AlCuadrado(3.2) );}

}

5.1.5. La palabra reservada this

La palabra reservada this sirve para hacer referencia a miembros de la clase en caso de que se quieraespecificar, ya sea por motivos de colisión de nombres o por la claridad del código. Su sitaxis es

this.campo

donde campo es la variable de la clase a la que queremos hacer referencia.

En el siguiente ejemplo, declaramos un constructor para la clase Punto, que toma dos argumentos X e Y.Entonces es obligado el uso de this para distinguir entre el X de la clase y el X tomado como parámetro

class Complejo{

double X;double Y;

Complejo(double X, double Y)

33

Page 40: tutorial csharp.pdf

Capítulo 5. Introducción a las clases

{this.X = X;this.Y = Y;

}}

34

Page 41: tutorial csharp.pdf

Capítulo 6. Variables y parámetros

6.1. Variables y parámetros

6.1.1. Variables

Las variables representan un espacio donde alojar información. Toda variable tiene un tipo que determinaqué valor puede ser almacenado en la variabe. Las variables locales son variables que son declaradasdentro de métodos, propiedades o indexadores. Una variable local queda definida especificando su tipo yuna declaración que especifica el nombre de la variable y un valor inicial opcional, como en:

int a;int b = 1;

pero también es posible declarar varias variables locales del mismo tipo de la siguente forma:

int a, b = 1;

La variable debe tener asignado un valor antes de que pueda ser utilizada. El ejemplo:

class Test{

static void Main() {int a;int b = 1;int c = a + b; // error, a no tiene valor asignado

}}

da como resultado un error en tiempo de compilación porque intenta usar la variable antes de que éstahaya tomado ningún valor.

Un campo es una variable asociada a una clase o estructura. Un campo declarado con el modificadorstatic define una variable estática, esto es, que no necesita que se haya creado una instancia de la clase enla que está contenida, mientras que un campo declarado sin este modificador define una variableinstancia. Un campo estático está asociado a un tipo, mientras que una variable instancia está asociadacon una instancia. El ejemplo

class Empleado{

private static string nombre;

35

Page 42: tutorial csharp.pdf

Capítulo 6. Variables y parámetros

public int DNI;public decimal Salario;

}

muestra la clase Empleado que tiene una variable estática privada y dos variables instancia públicas.

6.1.2. Parámetros

La declaración formal de parámetros también define variables. Hay cuatro tipos de parámetros:parámetros por valor, por referencia, parámetros de salida, y arreglos de parámetros.

Un parámetros por valor es usado para pasar parámetros a métodos en el que el no queremos que semodifique el valor original del parámetro. Cuando se pasa un parámetro por valor a una funciónrealmente se está pasando una copia de dicho parámetro, por lo que podemos modificarlo sin modificarel parámetro original. El ejemplo

using System;

class Test {static void F(int p) {

Console.WriteLine("p = {0}", p);p++;

}static void Main() {

int a = 1;Console.WriteLine("pre: a = {0}", a);F(a);Console.WriteLine("post: a = {0}", a);

}}

muestra un método F que tiene un parámetro por valor llamado p. El ejemplo produce la salida:

pre: a = 1p = 1post: a = 1

aunque el valor del parámetro p haya sido modificado dentro del método.

Un parámetro por referencia sirven para pasar argumentos directamente a una función, no como en losargumentos por valor, en el que se pasaba la copia del argumento en cuestión. Un parámetro por refereciano define en sí una variable, sino que más bien se refiere a la variable del correspondiente argumento. Unparámetro por referencia se declara con el modificador ref. El ejemplo

using System;

36

Page 43: tutorial csharp.pdf

Capítulo 6. Variables y parámetros

class Test {static void Swap(ref int a, ref int b) {// intercambia los dos valores

int t = a;a = b;b = t;

}static void Main() {

int x = 1;int y = 2;

Console.WriteLine("pre: x = {0}, y = {1}", x, y);Swap(ref x, ref y);Console.WriteLine("post: x = {0}, y = {1}", x, y);

}}

muestra un método swap que tiene dos parámetros por referencia. La salida producida es:

pre: x = 1, y = 2post: x = 2, y = 1

La palabra clave ref debe de ser usada tanto en la declaración formal de la función como en los usos quese hace de ésta.

El parámetro de salida es similar al parámetro por referencia, salvo que el valor inicial de dichoargumento carece de importancia. Un argumento de salida se declara con el modificador our. El ejemplo

using System;

class Test {static void Divide(int a, int b, out int result, out int remainder) {

result = a / b;remainder = a % b;

}static void Main() {

for (int i = 1; i < 10; i++)for (int j = 1; j < 10; j++) {

int ans, r;Divide(i, j, out ans, out r);Console.WriteLine("{0} / {1} = {2}r{3}", i, j, ans, r);

}}

}

muestra un método Divide que incluye dos parámetros de salida. Uno para el resultado de la división yotro para el resto.

37

Page 44: tutorial csharp.pdf

Capítulo 6. Variables y parámetros

6.1.3. Arreglo de parámetros

Para los parámetros descritos anteriormente hay una correspondencia unívoca entre los argumentos quepuede tomar la función y los parámetros que los representan. Un arreglo de parámetros permite guardaruna relación de varios a uno: varios argumentos pueden ser representados por un único arreglo deparámetros. En otras palabras, los arreglos de parámetros permiten listas de argumentos de tamañovariable.

Un arreglo de parámetros se declara con el modificador params. Sólo puede haber un arreglo deparámetros en cada método, y siempre debe ser el último método especificado. El tipo del arreglo deparámetros siempre es un tipo arreglo unidimensional. Al llamar a la función se puede pasar un únicoargumento de su tipo o bien cualquier número de argumentos de tipo del tipo del arreglo. El ejemplo

using System;

class Test{

static void F(params int[] args) {Console.WriteLine("n o de argumentos: {0}", args.Length);for (int i = 0; i < args.Length; i++)

Console.WriteLine("args[{0}] = {1}", i, args[i]);}static void Main() {

F();F(1);F(1, 2);F(1, 2, 3);F(new int[] {1, 2, 3, 4});

}}

muestra un método F que toma un número variable de argumentos int, y varias llamadas a este método.La salida es:

no de arguments: 0no de argumentos: 1args[0] = 1no de argumentos: 2args[0] = 1args[1] = 2no de argumentos: 3args[0] = 1args[1] = 2args[2] = 3no de argumentos: 4args[0] = 1args[1] = 2args[2] = 3args[3] = 4

38

Page 45: tutorial csharp.pdf

Capítulo 6. Variables y parámetros

La mayoría de los ejemplos presentes en esta introducción utilizan el método WriteLine de la claseConsole. El comportamiento para las sustituciones, como muestra el ejemplo

int a = 1, b = 2;Console.WriteLine("a = {0}, b = {1}", a, b);

se consigue usando un arreglo de parámetros. El método WriteLine proporciona varios métodossobrecargados para el caso común en el que se pasan un pequeño números de argumentos, y un métodoque usa un arreglo de parámetros.

namespace System{

public class Console{

public static void WriteLine(string s) {...}public static void WriteLine(string s, object a) {...}public static void WriteLine(string s, object a, object b) {...}...public static void WriteLine(string s, params object[] args) {...}

}}

39

Page 46: tutorial csharp.pdf

Capítulo 7. Propiedades e indizadores

7.1. Propiedades e indizadores

7.1.1. Propiedades

Las propiedades son una característica de C# que permiten aparentemente el acceso a un miembro de laclase mientras mantiene el control asociado al acceso mediante métodos.

Para los programadores de Java hay que decir que esto no es más que la formalización del patrón deasignación (setter) y método de lectura (getter)

Las propiedades son como métodos que se declaran dentro de un bloque asociado a una variablemediante las palabras reservadas get (se encarga de devolver algo cuando se llama al tipo que lo contiene) y set (que hace algo cuando se le asigna un valor a la variable que lo contiene. Este valor vieneespecificado en la variable value )

using System;

class TestProperties {private static string clave;public string Clave {

get{

Console.WriteLine ("Acceso a la propiedad clave");return clave;

}set{

Console.WriteLine ("Cambio del valor de clave");clave = value;

}}

}

class Test {public static void Main () {

TestProperties tp = new TestProperties();string c = "ClaveClave";tp.Clave = c;Console.WriteLine (tp.Clave);

}}

En realidad, lo que se hace es declarar una variable privada de forma que no se puede acceder de formadirecta, y se crean dos métodos ( o uno si solo se requiere acceso de lectura) que permiten acceder al

40

Page 47: tutorial csharp.pdf

Capítulo 7. Propiedades e indizadores

contenido de la variable y tal vez modificarla. Si no queremos que se pueda moficiar la variable, noincluímos el método "set" y ya tendríamos propiedades de sólo lectura.

7.1.2. Indexadores

Hemos visto, en el apartado en el que tratamos las propiedades, que podemos acceder a una variableprivada de una clase a través de eventos que nos permiten controlar la forma en la que accedemos a dichavariable.

Los indexadores nos van a permitir hacer algo parecido. Nos van a permitir acceder a una clase como sise tratara de un arreglo. Lo vemos de forma más sencilla con un ejemplo:

using System;

class PruebaIndexadores{

private int[] tabla = {1, 2, 3, 4};

public int this [int indice]{

get{

Console.WriteLine ("La posicion {0} de la tabla tiene el valor {1}", indice, tabla[indice]);return tabla[indice];

}set{

Console.WriteLine ("Escrito el valor {0} en la posición {1} de la tabla", value, indice);tabla[indice] = value;

}}

}

Tenemos una clase PruebaIndexadores en la que hay un array llamado "tabla", declarado como privado,por lo que no podremos acceder a él desde fuera de nuestra clase. Pero hemos declarado también unindexador (public int this [int indice]), que nos permitirá acceder a él de forma más controlada.

Para probar esta clase, creamos otra clase con un punto de entrada (public static void Main ()), que serádonde hagamos las pruebas.

Primero creamos un objeto de la clase PruebaIndexadores:

PruebaIndexadores obj = new PruebaIndexadores ();

Luego accedemos a una posición del indexador:

41

Page 48: tutorial csharp.pdf

Capítulo 7. Propiedades e indizadores

int a = obj[3];

Esta línea lo que hace es llamar al indexador, pasándole como parámetro el índice, en este caso 3. Al seruna consulta de lectura, se ejecuta el código que haya en la parte "get" del indexador. Una vez ejecutado,lo que nos aparece por pantalla es esto:

La posicion 3 de la tabla tiene el valor 4

Vamos ahora a hacer un cambio en la tabla:

obj[3] = 6;

Lo que se ejecuta ahora es la parte "set" del indexador. Lo que aparecerá en pantalla una vez ejecutadoesto será:

Escrito el valor 6 en la posición 3 de la tabla

Nótese que tenemos que hacer explícitamente el acceso al array (tabla[indice]=value) en el set, ya que elindexador no tiene forma de saber qué variable se supone que tiene que manejar. Si no pusiéramos esalínea, en realidad el indexador no cambiaría el valor del array.

Para comprobar que realmente se ha hecho el cambio, volvemos a acceder al indexador:

a = obj[3];

Y esta vez nos aparecerá esto:

La posicion 3 de la tabla tiene el valor 4.

42

Page 49: tutorial csharp.pdf

Capítulo 8. Clases

Como hemos dicho, las clases definen nuevos tipos por referencia. Una clase puede heredar otra clase ypuede implementar interfaces (conceptos que se verán en este capítulo).

8.1. Modificadores de acceso

Los miembros de una la clase pueden ser constantes, campos, métodos, propiedades, contructores,destructores, y tipos anidados. Como en la moyoría de lenguajes orientados a objetos, cada miembrotiene una accesibilidad asociada, que controla a qué código se le permite el acceso a nuestros miembros.Hay cinco posibles formas de accesibilidad: public, protected, internal y protected internal. Suscaracterísticas son las siguientes:

• public: Acceso no limitado. Cualquier código puede acceder a un elemento public

• protected: Acceso limitado desde la propia clase o desde tipos derivados de la propia clase

• internal: Acceso limitado al propio programa

• protected internal: Acceso limitado al propio programa o a tipos derivados de la propia clase

• private: Acceso limitado a la propia clase

Puesto que uno de los pilares principales de la programación orientada a objetos es la ocultación dedatos, y para mantener una API lo más simple posible, se recomienda siempre el uso de private salvocuando esté justificado el uso de un modificador distinto. Si no se especifica ningún modificador, laaccesibilidad por defecto es la private

8.2. Herencia

La herencia es un concepto fundamentar de la programación orientada a objetos. Cuando se dice que unacierta clase A hereda otra clase B significa que la clase A contiene todos los miembros de la clase B másalgunos que opcionalmente puede implementar ella misma

Las clases en C# soportan herencia simple, de forma que una clase puede derivar de otra, pero no devarias (como si era posible en C++). De hecho, en C# todas las clases derivan implícitamente de la claseobject.

La sintaxis que se utiliza es la siguiente:

class MiClaseDerivada : MiClaseBase{

//miembros

43

Page 50: tutorial csharp.pdf

Capítulo 8. Clases

}

En el siguiente ejemplo definimos una clase A con un método F(). Posteriormente definimos una clase Bque hereda A y además define un método G(). Finalemente creamos una clase con un método Main() quellamará a los dos métodos de B, al implementado por B y al heredado

using System;

class A{public void F()

{Console.WriteLine("Soy F() de A");

}}

class B : A{public void G()

{Console.WriteLine("Soy G() de B");

}}

class Principal{public static void Main()

{B clase_heredada = new B();

clase_heredada.F();clase_heredada.G();

}}

8.2.1. La palabra reservada base

La palabra reservada base sirve para acceder a miembros de la clase heredada de la misma forma que thissirve para acceder a miembros de la propia clase. Su sintaxis es idéntica a la de this, esto es:

base.nombre_del_miembro

En el siguiente ejemplo declaramos una clase B que hereda A y que utiliza el método F() de A.

class B : A{public void H()

44

Page 51: tutorial csharp.pdf

Capítulo 8. Clases

{base.F();

Console.WriteLine("soy H() de B");}

}

8.2.2. Clases Abstractas

Las clases abstractas son clases que contienen algún método incompleto, esto es, que está definido perono implementado. Por lo tanto, no se pueden instanciar y su único propósito es servir de clase base de lasque se derivarán otras clases.

Las clases que heredan una clase abstracta deben implementar los métodos incompletos. Las clasesabstractas se declaran con la palabra reservada abstract

using System;

abstract class A{public void F(); //metodo no implementado}

class B : A{//error en tiempo de compilación, B tiene que definir un método F()}

8.2.3. Miembros virtual

Métodos, propiedades e indexadores pueden ser virtual, lo que significa que su implementación puedeser sobreescrita en clases derivadas. El ejemplo

using System;

class A {public virtual void F()

{Console.WriteLine("A.F");

}}

class B: A {public override void F()

{

45

Page 52: tutorial csharp.pdf

Capítulo 8. Clases

base.F();Console.WriteLine("B.F");

}}

class Test {public static void Main() {

B b = new B();b.F();A a = b;a.F();

}}

muestra una clase A con un método virtual F, y una clase B que sobreescribe F. El método sobreescritoen B contiene una llamada, base.F(), el cual llama al método sobreescrito en A.

8.3. Interfaces

TODO

8.4. Modificadores de Herencia

A continuación se lista un breve resumen de los modificadores de herencia de C#

• abstract: se aplica a clases o a miembros de la clase.Sección 8.2.2

• new: confirma explícitamente la intención de ocultar un miembro heredado con el mismo nombre

• override: especifica que el miembro está redefiniendo un método heredado con la misma firma

• sealed: si se aplica a una clase se indica que esta clase no puede heredarse. Si se aplica a un métodoheredado junto a override se asegura que el método no es redefinido en clases heredadas

• virtual: se aplica a métodos. Permite que las clases derivadas redefinan los métodos en lugar deocultarlos

46

Page 53: tutorial csharp.pdf

Capítulo 9. Delegados

Los delegados son objetos que encapsulan métodos. Son muy similares a los punteros a funciones deC/C++

9.1. Delegados

Los delegados son un tipo especial de clases que derivan deSystem.Delegate . Los delegados tienencomo objetivo almacenar referencias a métodos de otras clases, de tal forma que, al ser invocados,ejecutan todos estos métodos almacenados de forma secuencial.

Los delegados pueden almacenar métodos estáticos o instanciados, indiferentemente. Además los tiposde estos métodos son comprobados para evitar errores de programación. Es decir, un delegado soloalmacenará referencias a métodos compatibles, esto es, que devuelvan el mismo tipo y tome los mismosargumentos. Aparte de eso, a los delegados no les influye cómo sea el método que están manejando.

Veamos esto con un ejemplo. Aquí podemos ver la declaración de un delegado:

delegate void DelegadoSimple( int i );

Este delegado, por ejemplo, sólo podrá guardar referencias a métodos que no devuelvan nada (void) yque reciban como parámetros un entero. Para almacenar métodos en un delegado, usamos la siguientesintaxis,

DelegadoSimple d = new DelegadoSimple( F );

, donde F es el método que queremos que almacenen (debemos de haberlo declarado antes). Como yahemos dicho, los métodos que almacena un delegado deben de ser compatibles con éste, y cualquierintento de guardar métodos no compatibles provocará una excepción.

Una vez que tenemos una instancia de nuestro delegado, para que se ejecute la función F() simplementedebemos llamar a nuestro delegado, pués encapsula dicha función, de forma que

d()

ejecuta la función F().

Expuesto así, los delegados parecen una forma absurda de complicar más las cosas (si queremso llamar auna función, pués la llamamos y punto, no hace falta encapsularla en un delegado). Sin embargo, losdelegados son fundamentales, pués son la forma de pasar funciones como parámetros a métodos.

47

Page 54: tutorial csharp.pdf

Capítulo 9. Delegados

Una vez tenemos declarado nuestro delegado, ya podemos crear instancias del mismo. Veamos estesencillo ejemplo:

Ejemplo 9-1. Ejemplo simple de uso de delegados

using System;

class Ejemplo {delegate void MiDelegado (int a);

void Imprime (int entero) {Console.WriteLine ("Llamada a Ejemplo.Imprime({0})", entero);

}

public static void Main() {Ejemplo ejemplo = new Ejemplo ();MiDelegado foo;foo = new MiDelegado (ejemplo.Imprime);foo (10);

}}

Tras crear una instancia de nuestra claseEjemplo , creamos otra de nuestro delegado. Posteriormenteinsertamos en el delegado una referencia al métodoImprime , que pertenece a la instancia de nuestraclase. Finalmente convocamos al delegado a que ejecute los métodos que contenga. Si lo compiláramosobtendríamos esto:

bash$ mcs ejemplo.csCompilation succeededbash$ mono ejemplo.exeLlamada a Ejemplo.Imprime(10)

Otra cuestión a tener en cuenta cuando programemos con delegados, es que éstos no tienen en cuenta lavisibilidad de los métodos. Esto permite llamar a métodos privados desde otros si ambos tienen acceso aldelegado. Es decir, imaginemos que una clase guarda en un delegado referencia a uno de sus métodosprivados. Si desde otra clase que tenga acceso al delegado (pero no al método privado) se convoca a éste,se ejecutará ese método. En verdad no se está violando la privacidad del método, porque no es la clasequien lo convoca, sino el delegado, que sí tiene acceso al mismo.

9.1.1. Llamadas a múltiples métodos

Hasta el momento hemos visto como hacer que un delegado guarde referencia de un sólo método. Sinembargo, existe una clase,System.MulticastDelegate , que deriva deSystem.Delegate , que sediferencia de esta última en que puede tener múltiples métodos en su lista de invocaciones.

48

Page 55: tutorial csharp.pdf

Capítulo 9. Delegados

Para poder hacer esto usaremos los operadores sobrecargados ’+=’ y ’-=’ que, respectivamente, añaden oeliminan a un método de la lista de invocaciones de un delegado.

Para intentar asimilar esto mejor, veámoslo con un ejemplo más completo.

Ejemplo 9-2. Ejemplo de uso de delegados

using System;

delegate void MiDelegado (string cadena);

class Ejemplo {public static MiDelegado delegado;

public static void Main () {delegado = new MiDelegado (ClaseA.MetodoEstatico);

ClaseA A = new ClaseA();delegado += new MiDelegado (A.MetodoPublico);

ClaseB B = new ClaseB(); // El constructor inserta MetodoPrivado// delegado += new MiDelegado (B.MetodoNoValido); // Excepción

delegado ("Hola mundo");}

}

class ClaseA {public static void MetodoEstatico (string cad) {

Console.WriteLine ("ClaseA.MetodoEstatico ha sido llamado: {0}", cad);}

public void MetodoPublico (string cad) {Console.WriteLine ("ClaseA.MetodoPublico ha sido llamado: {0}", cad);

}}

class ClaseB {void MetodoPrivado (string cad) {

Console.WriteLine ("ClaseB.MetodoPrivado ha sido llamado: {0}", cad);}

public void MetodoNoValido (int entero) {Console.WriteLine ("ClaseB.MetodoNoValido ha sido llamado: {0}", entero);

}

public ClaseB () {Ejemplo.delegado += new MiDelegado (MetodoPrivado);

}}

49

Page 56: tutorial csharp.pdf

Capítulo 9. Delegados

Podemos ver que, en este caso, nuestro delegado sólo manipula métodos que no devuelvan nada y quereciban como único parámetro una cadena. Si observamos los métodos que componenClaseA yClaseB , el denominadoMetodoNoValido no concuerda con el delegado, ya que recibe un entero y nouna cadena. Eso implica que no vamos a poder llamarlo desde ninguna instancia del delegado que hemosdeclarado. Sin embargo, con las otras no tendremos ningún problema.

Bien, observemos paso a paso lo que hace el programa. Fijemos nuestra atención en el método principal(Main ). Primero insertamos un método estático. Como sabemos, para llamar a un método de este tipo, sehace a partir del nombre de la clase y no de una instancia. Bien, hasta aquí nada que no hayamos vistoya, pero ahora insertemos un segundo módulo en el delegado.

Como hemos dicho, hemos usado el operador ’+=’ para incluir otro método más en nuestro delegado, eneste casoMetodoPublico . Si usaramos de nuevo el operador ’=’, borraríamos la antigua lista deinvocaciones y crearíamos una nueva con sólo una función referenciada. Ahora tenemos dos métodos enla lista de invocaciones de nuestro delegado. Por último, creamos una instancia deClaseB , la cual en suconstructor incluye una referencia más al delegado, en este caso aMetodoPrivado .

Si ahora compilamos y ejecutamos este código, obtendremos esto:

bash$ mcs ejemplo.csCompilation succeededbash$ mono ejemplo.exeClaseA.MetodoEstatico ha sido llamado: Hola mundoClaseA.MetodoPublico ha sido llamado: Hola mundoClaseB.MetodoPrivado ha sido llamado: Hola mundo

Como vemos, aunque hemos convocado al delegado desde la claseEjemplo , que no tiene acceso aMetodoPrivado , éste ha sido ejecutado. Como explicamos, esto es así porque realmente quien lo estáhaciendo es el delegado y no el métodoMain .

Por último, una cuestión más. Hasta el momento hemos visto a delegados que gestionan miembros queno devuelven ningún valor. Pero, ¿qué ocurre cuando los devuelven? En este caso, la ejecución deldelegado no devuelve todos esos valores, sólo el que retorne el último método de su lista de invocación.

50

Page 57: tutorial csharp.pdf

Capítulo 10. Eventos

10.1. Eventos

Mientras que antiguamente la interacción entre un programa y el usuario se limitaba a que ésteintrodujese datos en determinados momentos de la ejecución del primero en los últimos años estemodelo está siendo relegado por un crecimiento exponencial de las aplicaciones GUI (con InterfazGráfica de Usuario). En esta nueva aproximación conocida como Programación Orientada a Eventos elprograma queda a la espera de que el usuario vaya haciendo tal o cual cosa sin tener casi ningún controlsobre el orden en el que se producirán los sucesos o más aún, ni cuales son los que darán. Más aún estetipo de programación proporciona una asincronía muy útil gracias al uso de las devoluciones de llamada,tal y como se vió con los delegados.

En nuestro caso estamos de suerte porque el lenguaje en sí ¡soporta eventos!. Sin entrar en detalles sobrequé son exactamente, veremos como usarlos. Para quienes no se queden tranquilos con eso podemosdecir que los eventos son una especie de mensajes que lanza el entorno de ejecución de manera que unobjeto pueda avisar de cuando ha sucedido algo de lo que quiera avisar a otros objetos.

En .NET se usa un mecanismo conocido como de publicación/subscripción. Así que lo que tendremosserá una clase que publica un evento al que, aquellas clases que quieran estar al tanto de cuando ha sidolanzado ese evento se subscriben. De hecho, es práctica corriente no lanzar el evento si no hay ningunaclase subscrita.

Ya hemos dicho todo lo que hay que decir de momento. Como al principio es un poco lioso trabajar condelegados y eventos (normalmente se llega a esta sección sin haber practicado lo suficiente con losdelegados), haremos una receta del procedimiento general a seguir.

• Crear la clase que contendrá el evento, la que lo publica (la que lo puede lanzar). Supongamos que elevento que queremos lanzar se llama OnButtonClicked. Si queréis podéis poner un nombre genéricocomo MyEvent. Consecuentemente cambiad todos los OnButtonClicked por MyEvent si hacéis eso.

• Definimos el manejador del evento. Es el código que se asocia con el evento en tiempo de ejecución yque se invoca cuando la clase que se suscribe recibe la notificación del evento. Es un delegado de tipovoid y que recibe dos parámetros: el primero un objeto, el segundo una instancia de la clase queguarda la información del evento. Esta clase tiene que derivar de EventArgs.

[modificadores de acceso] delegate void OnButtonClickedEventHandler (object source, OnButtonClickedEventArgs e);

• Declaramos el evento con el tipo del delegado antes definido.

[modificadores de acceso] event OnButtonClickedEventHandler OnButtonCliked;

• Definimos el método que puede lanzar el evento y establecemos los valores para el objeto que guardala información del evento. Esto es código en la clase que publica el evento y es el código que seencarga de lanzar el evento con la información adecuada en el momento adecuado.

51

Page 58: tutorial csharp.pdf

Capítulo 10. Eventos

• Creamos la clase que guarda la información del evento derivándola de EventArgs:

class OnButtonClickedEventArgs : EventArgs {// código// al menos un constructor y conveniente que tenga propiedades para acceder// a las variables miembro.}

• Creamos la clase que se suscribe al evento.

• Suscribimos la clase con:

ClaseQuePublicaElEvento.OnButtonClicked += new OnButtonClickedEventHandler (callback);

Donde callback es la función de devolución de llamada (retrollamada o callback) que queráis que sellame cuando se recibe la notificación del evento. Esa función debe estar en la clase que se subscribe alevento.

Como el operador que se usa para las subscripciones es del tipo += podemos subscribir variosmanejadores de evento a un mismo evento.

ClaseQuePublicaElEvento.OnButtonClicked += new OnButtonClickedEventHandler (callback2);

Y así añadir tantos callbacks como queramos ya que no se sobreescriben los unos a los otros (lo quepasaría si se hubiese usado el operador =) si no que se añaden.

También podemos desuscribirnos de un evento mediante el operador -=. Para ello no hay más queescribir lo mismo pero con un - en vez de un +:

ClaseQuePublicaElEvento.OnButtonClicked -= new OnButtonClickedEventHandler (callback2);

Tenéis un ejemplo muy simple en el C# Reference Manual de Anders Hejlsberg y Scott Wiltamuth. Perotambién he hecho un par de ejemplos a medida un poco más elaborados.

Clase que publica. Guardadla en Dude.cs

// Un tipo tratando de ligarse a una tipa

using System;using System.Threading;

public class Dude {// Declaramos el delegado que ’envolvera’ a las posibles retrollamadas conectadas// al evento SignalEmitted.public delegate void DudeSpeechEventHandler (object s, DudeSpeechEventArgs args);

// El evento que emitiremos.public event DudeSpeechEventHandler MessageFromDude;

52

Page 59: tutorial csharp.pdf

Capítulo 10. Eventos

public void StartBrainWashing () {for (int i=0; i < 5; i++) {if (MessageFromDude != null)MessageFromDude (this, new DudeSpeechEventArgs (msgs[i], i));Thread.Sleep (3000);}}string[] msgs = {"Hey!. Garl!","What’s up, honey?","Your nick is making me real horny ;)","Do you have a webcam setup? 8)~","Uh?","FUU..."};}

public class DudeSpeechEventArgs : EventArgs {public DudeSpeechEventArgs (string msg, int index) {this.msg = msg;this.index = index;}

public string Message {get { return msg; }set { msg = value; }}

public int Index {get { return index; }}

string msg;int index;}

Clase que se subscribe, guardadla en Chic.cs

// La clase a la que le toca aguantar a los dudes.

using System;using System.Threading;

public class Chic {public Chic () {}

public void Suscribe (Dude dude) {this.dude = dude;dude.MessageFromDude += new Dude.DudeSpeechEventHandler (message_received);}

53

Page 60: tutorial csharp.pdf

Capítulo 10. Eventos

public void Unsuscribe (Dude dude) {dude.MessageFromDude -= new Dude.DudeSpeechEventHandler (message_received);}

private void message_received (object s, DudeSpeechEventArgs args) {Console.WriteLine ("&lt;dude&gt;: {0}", args.Message);if (args.Index < 3) {Thread.Sleep (4000);Console.WriteLine ("&lt;you&gt;: {0}", msgs[args.Index]);}else {Unsuscribe (dude);Console.WriteLine ("Connection reset by peer...");}}

private string[] msgs = {"hi.","nothing. Just talking with a friend.","really?"};private Dude dude;}

Otro ejemplo más. Esta clase guardadla como Emisor.cs.

// Una clase simple que cada 3 segundos envia una senal

using System;using System.Threading;

public class Emisor {// Declaramos el delegado que ’envolvera’ a las posibles retrollamadas conectadas// al evento SignalEmitted.public delegate void SignalEmittedEventHandler (object s, SignalEmittedEventArgs args);

// El evento que emitiremos.public event SignalEmittedEventHandler SignalEmitted;

public void Emit () {for (int i=0; i < 4; i++) {Thread.Sleep (3000);string time = DateTime.Now.Ticks.ToString ();if (SignalEmitted != null)SignalEmitted (this, new SignalEmittedEventArgs (time));}}}

public class SignalEmittedEventArgs : EventArgs {public SignalEmittedEventArgs (string time) {

54

Page 61: tutorial csharp.pdf

Capítulo 10. Eventos

this.time = time;}

public string Time {get { return time; }set { time = value; }}

string time;}

Mientras que esta la tenéis que meter en Satellite.cs

// Una clase que recibe las senales de instancias// de tipo Emisor.

using System;using System.Collections;

public class Satellite {public Satellite (Emisor emisor) {emisors.Add (emisor);

foreach (Emisor e in emisors)e.SignalEmitted += new Emisor.SignalEmittedEventHandler (signal_emitted);}

private void signal_emitted (object s, SignalEmittedEventArgs args) {Console.WriteLine ("Signal emitted at: {0}", args.Time);}

public void AdEmisor (Emisor emisor) {emisors.Add (emisor);}

private ArrayList emisors = new ArrayList ();

}

Ya solo os queda tener los experimentos que hacer. Os propongo estos: Experiment1.cs

// Un experimento simple que pone a prueba nuestras clases// Emisor and Satellite.

using System;

public class Experiment {static void Main () {

55

Page 62: tutorial csharp.pdf

Capítulo 10. Eventos

Emisor emisor = new Emisor ();Satellite satellite = new Satellite (emisor);emisor.Emit ();}}

y este otro:

// Una tipica charla del IRC

using System;

public class Experiment {static void Main () {Dude dude = new Dude ();Chic chic = new Chic ();chic.Suscribe (dude);dude.StartBrainWashing ();}}

Estos ejemplos los podéis compilar con este makefile:

all: experiment1 experiment2

############### Very first example of event-driven programming.experiment1:mcs -o experiment1.exe Experiment1.cs Satellite.cs Emisor.cschmod +x experiment1.exe

############### Another example of event-driven programming.experiment2:mcs -o experiment2.exe Experiment2.cs Dude.cs Chic.cschmod +x experiment2.execlean:rm -rf *.exerm -rf *~

Y eso es todo cuanto necesitáis saber de los eventos para poder usarlos con soltura en vuestrosprogramas y en particular para los que queráis dar el salto y empezar a hacer aplicaciones GUI en Gtk#,GNOME# o Glade#.

56

Page 63: tutorial csharp.pdf

Capítulo 11. Programación multihilo

11.1. Threads -- Programación multihilo

La programación con hilos, también conocida como multiproceso, tiene la ventaja de poder trabajar demanera asíncrona. Esto permite que aquellos procesos que pueden requerir un tiempo más o menos largoen llevarse a cabo se pongan a trabajar ’paralelamente’ al proceso principal, de manera que la aplicaciónpueda retomar el control y así el usuario seguir trabajando con ella.

Nota1: la programación multihilo no es siempre la solución correcta para todas las aplicaciones e inclusoen algunos casos puede relentizar la aplicación aunque parezca que no es así o cosas aún peores comopérdida de datos, etc.

Nota2: la implementación que hace Mono de las clases del espacio de nombres System.Threading estábasada en los pthreads (los hilos POSIX que tan bien implementados están en Linux) y es fácilcomprobar que el paso de trabajar con unos a trabajar con otros es casi inmediato. Una buena referenciaes el libro "Programación Linux al descubierto" de Kurt Wall, así como la propia pthreads.h. Igualmenteel recolector de basura que se usa hasta ahora en Mono (el GC de Bohem puede ser y debe ser compiladopasándole la opción--enable-threads=pthreads

Veamos los conceptos generales:

• Hilo: un hilo de ejecución es una unidad de procesamiento.

• Multitarea: la ejecución simultánea de varios hilos.

La multitarea puede ser de dos tipos, uno de los cuales está bastante obsoleto y además queda fuera de.NET de manera que no podremos usar los hilos de la máquina virtual en sistemas operativos que useneste clase primitiva de multitarea conocida como ’preferente’. El que vamos a usar nosotros es el tipo’cooperativo’. La diferencia fundamental radica en que en el caso de la multitarea preferente elprogramador tiene que encargarse de liberar los hilos, etc, mientras que en la multitarea preferente elprocesador asigna fracciones de tiempo de procesado a cada hilo y salta de hilo en hilo cada tiempo.

Nota: los hilos y la forma de trabajar con ellos tal y como lo vamos a hacer aquí no es algo propio de C#sino que es extensible a todo el Framework de .NET (i.e. a cualquier lenguaje en .NET). Si en algúnmomento se está tratando alguna característica exclusiva de C#, se comentará explicítamente. Así, quiénya posea conocimientos de Threads en .NET puede saltarse el resto de este tema.

Nuestra receta para crear un hilo simple es como sigue:

• Metemos el espacio de nombres System.Theading en nuestra clase.

• Asignamos qué método ejecutará el hilo, para ello usamos un delegado de tipo ThreadStart queencapsule el método.

57

Page 64: tutorial csharp.pdf

Capítulo 11. Programación multihilo

• Creamos el hilo pasándole al constructor el delegado anteriormente creado.

• Ponemos el hilo en ejecución.

Ya tenemos el procedimiento a seguir para crear una aplicación multitarea elemental. Veamos el código:

// Incluid esto en un método cualquiera que queráis que lance el nuevo// hilo. Por ejemplo en un Main. ¡De hecho podemos hacer que Main solo// haga eso!.

// metodo es un método que queremos poner en un hilo aparte.ThreadStart delegadoQueGuardaElMetodo = new ThreadStart (metodo);

// creamos el hilo pasándole el delegado al constructor.Thread nuevoHilo = new Thread (delegadoQueGuardaElMetodo);

// Empezamos a ejecutar el hilo.nuevoHilo.Start ();

Otra forma de instanciar un hilo es consiguiendo una referencia al hilo actual de ejecución, esto se hacesin más que llamar a la propiedad estática Thread.CurrentThread.

Thread t = Thread.CurrentThread;

Dejadme un segundo que muestre el equivalente en pthreads de este ejemplo sencillo para quecomprobéis por vosotros mismos los que he comentado antes de la similitud Threads/pthreads:

#include <stdio.h>#include <stdlib.h>;#include <pthread.h>;

void funcion();

intmain (int argc, char *argv[]){

pthread_t pthrd1; /* declaramos el hilo */int ret; /* el valor de retorno para gestionar el

/* estado de lacreación del hilo */

ret = pthread_create (&pthrd1, NULL, (void *) funcion, NULL);if (ret) {

perror ("No se pudo crear el primer hilo");exit (EXIT_FAILURE);

58

Page 65: tutorial csharp.pdf

Capítulo 11. Programación multihilo

}

pthread_join (pthrd1, NULL);exit (EXIT_SUCCESS);

}

voidfuncion (){

/* Ponle lo que quieras hacer aqui */}

11.1.1. Controlando el tiempo de vida del hilo

Los métodos fundamentales que se deben conocer si se quiere tener un control absoluto de los hilos en.NET son cinco: Thread.Sleep, Thread.Suspend, Thread.Resume, Thread.Interrupt y Thread.Abort. Esmás, para casi todas las aplicaciones que vayáis a desarrollar, os bastará con conocer Thread.Sleep!.

Antes de ver cómo funcionan, presentémoslos:

• Thread.Sleep (int time) -- Para el hilo durante ’time’ milisegudos.

• Thread.Interrupt -- Interrumpe el hilo parada para que vuelva a la ejecución antes de que se acabe’time’.

• Thread.Suspend -- El hilo se queda suspendido hasta que otro hilo lo llame con Thread.Resume.

• Thread.Resume -- Recupera un hilo suspendido.

• Thread.Abort -- Destruye un hilo.

La utilización de esos métodos es muy sencilla. Thread.Sleep acepta como parámetro el tiempo que sequiere que la hebra (o hilo, es lo mismo) permanezca dormida. Si le decimos Thread.Sleep (5000) sedetendrá durante cinco segundos o lo que es lo mismo, 5000ms.

// más codigo por aquí...// Llamada a Thread.Sleep para parar la hebra durante 3 segundos

Thread.Sleep (3000);

// seguimos poniendo código...// Para ver ejemplos completos mirar la sección de Eventos de este mismo tutorial.

Si el valor pasado es 0 la hebra devolverá devolverá el resto del timeslice que le quedaba. Si por elcontratio se le pasa Timeout.Infinite, el hilo se nos para indefinidamente hasta que alguna otra hebrallame al metodo Interrupt de la hebra suspendida. La diferencia fundamental entre Thread.Sleep y la otra

59

Page 66: tutorial csharp.pdf

Capítulo 11. Programación multihilo

manera de detener una hebra, llamando a Thread.Suspend, es que este último puede ser invocado desdela hebra actual o desde otra. Además, en caso de detener una hebra con Suspend, no podremos volver aponerla en ejecución hasta que no se haga desde otra con el método Thread.Resume. (Nota: cuando seescribía este párrafo los métodos Thread.Suspend y Thread.Resume estaban parcialmentementeimplementados en Mono y cabe la posibilidad de que lo sigan estando cuando leáis esto. Si véis quecódigo que los usa no funciona como debiera,i.e. los hilos no se suspenden cuando debieran, comprobadque no se lanzó una excepción del tipo NotImplementedException o ningún WARNING. Hace poco esosmétodos no hacían nada pero después de intentar probar estos ejemplos añadimos esos avisos a la claseThread). Por último nos queda ver Thread.Abort.

Thread.Abort es un método un tanto particular. En caso de ser llamado, el CLI (o CLR), aborta el hilolanzando una excepción ThreadAbortException que no puede ser recogida. El CLI no permite recogeresa excepción y lo más que podremos hacer, si queremos hacer un cleanup, será llevar a cabo las medidasoportunas dentro de un bloque finally. Hay que tener en cuenta que mono no detendrá la ejecución delhilo inmediatamente. Se esperará alcanzar un punto seguro para hacer esto y ese punto lo escogerá mono.Si queréis que el hilo deje de ejecutarse inmediatamente, podéis hacer una llamada a Thread.Join quesiendo una llamada síncrona dentendrá el hilo hasta que se finalize la ejecución.

60

Page 67: tutorial csharp.pdf

Capítulo 12. Sobrecarga de operadores

12.1. Sobrecarga de operadores

12.1.1. ¿ Qué es la sobrecarga de operadores ?

La sobrecarga de operadores es la capacidad para transformar los operadores de un lenguaje como porejemplo el +, -, etc, cuando se dice transformar se refiere a que los operandos que entran en juego notienen que ser los que admite el lenguaje por defecto. Mediante esta tecnica podemos sumar dos objetoscreados por nosotros o un objeto y un entero, en vez de limitarnos a sumar numeros enteros o reales, porejemplo.

La sobrecarga de operadores ya era posible en c++ y en otros lenguajes, pero sorprendentemente java nolo incorpora asi que podemos decir que esta caracteristica es una ventaja de c# respesto a java, aunquemucha gente esta posibilidad no lo considera una ventaja por que complica el codigo.

A la hora de hablar de operadores vamos a distinguir entre dos tipos, los unarios y los binarios. Losunarios son aquellos en que solo se requiere un operando, por ejemploa++ , en este caso el operando es’a’ y el operador ’++’. Los operadores binarios son aquellos que necesitan dos operadores, por ejemploa+c , ahora el operador es ’+’ y los operandos ’a’ y ’c’. Es importante esta distincion ya que laprogramacion se hara de forma diferente

Los operadores que podemos sobrecargar son los unarios, +, -, !, ~, ++, --; y los binarios +, -, *, /, %, &,|, ^, <<, >>. Es importante decir que los operadores de comparacion, ==, !=, <, >, <=, >=, se puedensobrecargar pero con la condicion que siempre se sobrecargue el complementario, es decir sisobrecargamos el == debemos sobrecargar el !=.

12.1.2. Sobrecargando operadores en la practica

Para mostrar la sobrecarga vamos a usar el repetido ejemplo de los numeros complejos, ( aunque tambienvaldria el de las coordenadas cartesianas ). Como se sabe los numeros complejos tienen dos partes, lareal y la imaginaria, cuando se suma dos numeros complejos su resultado es la suma de las dos partes,para ello se va a crear una clase llamada ComplexNum que contendra ambas partes. Sin esta técnica nose podria sumar dos objetos de este tipo con este practico método, ya que esta clase no es válida comooperando de los operadores de c#.

Empecemos con el código de la clase de numeros imaginarios.

public class ComplexNum{private float _img;private float _real;

61

Page 68: tutorial csharp.pdf

Capítulo 12. Sobrecarga de operadores

public ComplexNum(float real, float img) {_img = img;_real = real;

}

public ComplexNum() {_img = 0;_real = 0;

}

public float getReal(){return this._real;

}

public float getImg() {return this._img;

}

public String toString() {

if (_img >= 0 )return _real + "+" + _img +"i";

elsereturn _real + "" + _img + "i";

}

}

En el ejemplo hemos puesto la clase, con un par de constructores , dos getters para obtener los datosprivados de la clase y un metodo que nos transfoma el numero complejo a cadena para que se puedavisualizarlo facilmente, a esta clase la iremos añadiendo métodos para que tenga capacidad de usaroperadores sobrecargados.

12.1.2.1. Operadores binarios

Para empezar vamos a sobrecargar el operador suma(’+’) para que al sumar dos objetos de la claseComplexNum, es decir dos numeros complejos obtengamos otro numero complejo que sera la suma deambas partes. Cabe destacar que los prototipos para sobrecargar operadores seran:

public static Operando operator+(Operando a, Operando b)

Este es el prototipo para el operador +, el resto de operadores binarios van a seguir el mismo patron. Portanto el código del método de sobrecarga será:

public static ComplexNum operator+(ComplexNum a, ComplexNum b) {return new ComplexNum(a.getReal() + b.getReal(), a.getImg() + b.getImg());

}

62

Page 69: tutorial csharp.pdf

Capítulo 12. Sobrecarga de operadores

Este método sobrecarga el operador suma para que podamos sumar dos numeros complejos. Un dato atener en cuenta es que los métodos que sobrecargan operadores deben serstatic. Como se ve en el códigolos operandos son ’a’ y ’b’, que se reciben como parametro y el resultado de la operacion es otro numerocomplejo que es el que retorna el método. Por tanto se limita a crear un nuevo numero complejo conambas partes operadas. De la misma forma podemos crear la sobrecarga del operador resta(’-’) para quelleve a cabo la misma función

public static ComplexNum operator-(ComplexNum a, ComplexNum b) {return new ComplexNum(a.getReal() - b.getReal(), a.getImg() - b.getImg());

}

Como vemos el metodo es identico solo q sustituyendo los + por -. En este caso el trabajo que hacemosdentro del metodo es trivial pero podria ser tan complejo como se quiera.

12.1.2.2. Operadores Unarios

En esta sección se vera como sobrecargar los operadores unarios, es decir aquellos que toman un solooperando, como por ejemplo a++. El prototipo de los métodos que van a sobrecargar operadores unariosserá:

public static Operando operator++(Operando a)

Como antes sustituyendo el ++ por cualquier operador unario. El ejemplo dentro de nuestra clase denumeros complejos sería:

public static ComplexNum operator++(ComplexNum a) {

float auximg = a.getImg();float auxreal = a.getReal();

return new ComplexNum(++auxreal, ++auximg);}

A primera vista puede quedar la duda si estamos sobrecargando la operacion ++a o a++. Este aspecto seencarga el compilador de resolverlo, es decir, se sobrecarga la operacion ++ y el compilador se encargade sumar y asignar o asignar y sumar. Este problema no ocurria en C++, cosa que teniamos que manejarnosotros

Como hemos dicho antes la operacion que hagamos dentro del metodo que sobrecarga el operador estotalmente libre, se puede poder el ejemplo de multiplicar dos matrices lo que es mas complejo quesumar dos numeros complejos

63

Page 70: tutorial csharp.pdf

Capítulo 13. TRatamiento de ficheros

13.1. Tratamiento de ficheros

13.1.1. La primera clase: System.IO.File

Esta clase estatica nos provee de las operaciones basicas a realizar con ficheros a nivel externo. Es unaclase sellada, por lo que ninguna clase puede derivar de ella. Es una clase que hace los tests de permisosy seguridad en cada invocacion.

13.1.1.1. Metodo System.IO.File.AppendText()

StreamReader System.IO.File.AppendText(string camino)

Este metodo devuelve un StreamWriter (Explicado mas adelante) que nos permite añadir texto enformato UTF-8 al fichero especificado en la cadena"camino"

13.1.1.2. Metodo System.IO.File.Copy()

void System.IO.File.Copy(string org, string dest);void System.IO.File.Copy(string org, string dest,bool sobreescribe);

Este metodo nos permite copiar un fichero a otro lugar, el fichero org se copia en dest, el tercer parametroes si se debe sobreescribir o no el fichero destino

13.1.1.3. Metodo System.IO.File.Create()

TextWriter System.IO.File.Create(string camino);TextWriter System.IO.File.Create(string camino; int buffer);

Este metodo devuelve un TextWriter, crea o sobreescribe el fichero identificado por camino, el segundoparametro especifica el tamaño del buffer a usar en el TextWriter creado

13.1.1.4. System.IO.File.CreateText()

StreamWriter System.IO.File.CreateText(string camino);

Este metodo devuelve un StreamWriter usando la codificacion UTF-8.

64

Page 71: tutorial csharp.pdf

Capítulo 13. TRatamiento de ficheros

13.1.1.5. System.IO.File.Delete()

void System.IO.File.Delete(string camino);

Este metodo borra el fichero especificado en camino.

13.1.1.6. System.IO.File.Exists()

bool System.IO.File.Exists(string camino);

Este metodo borra el fichero especificado en camino.

13.1.1.7. System.IO.File.GetAttributes()

FileAttributes System.IO.File.GetAttributes(string ruta);

Este metodo devuelve la enumeracion FileAttributes y -1 si no existe la ruta.

13.1.1.8. System.IO.File.GetCreationTime()

DateTime System.IO.File.GetCreationTime(string ruta)

Devuelve un tipo DateTime que contiene la ficha del fichero asociado a esa ruta

13.1.1.9. System.IO.File.GetLastAccessTime()

DateTime System.IO.File.GetLastAccessTime(string ruta);

Devuelve un DateTime que contiene la fecha del ultimo acceso al fichero asociado a la ruta

13.1.1.10. System.IO.File.GetLastWriteTime()

DateTime System.IO.File.GetLastAccessTime(string ruta);

Devuelve un DateTime que contiene la fecha del ultimo acceso al fichero asociado a la ruta

13.1.1.11. System.IO.File.Move()

void System.IO.File.Move(string origen, string destino);

Mueve el fichero identificado por la cadena origen a destino.

65

Page 72: tutorial csharp.pdf

Capítulo 13. TRatamiento de ficheros

13.1.1.12. System.IO.File.Open()

FileStream System.IO.File.Open(string ruta, FileMode modo);FileStream System.IO.File.Open(string ruta, FileMode modo,FileAccess modo_a);FileStream System.IO.File.Open(string ruta, FileMode modo,FileAccess modo_a, FileShare modo_s);

Abre el fichero especificado por ruta, devuelve un FileStream asociado a el en los modos especificados

13.1.1.13. System.IO.File.OpenRead()

FileStream System.IO.File.OpenRead(string ruta);

Abre el fichero especificado para lectura y devuelve un FileStream asociado a el

13.1.1.14. System.IO.File.OpenText()

StreamReader System.IO.File.OpenText(string ruta);

Abre el fichero especificado para lectura y devuelve un StreamReader asociado a el.

13.1.1.15. System.IO.File.OpenWrite()

FileStream System.IO.File.OpenWrite(stream ruta);

Abre un fichero para escritura y devuelve un FileStream asociado a el.

13.1.1.16. System.IO.File.SetAttributes()

void System.IO.File.SetAttributes(string ruta,FileAttributesatribs);

Pone los atributos especificados en atribs al fichero especificado por la ruta.

13.1.1.17. System.IO.File.SetCreationTime()

void System.IO.File.SetCreationTime(string ruta, DateTimecr_tm);

Pone la fecha de creacion del archivo especificado en ruta a lo especificado en cr_tm

66

Page 73: tutorial csharp.pdf

Capítulo 13. TRatamiento de ficheros

13.1.1.18. System.IO.File.SetLastAccessTime()

void System.IO.File.SetLastAccessTime(string ruta, DateTimela_tm);

Pone la fecha de ultimo acceso especificada en la_tm al fichero asociado a ruta.

13.1.1.19. System.IO.File.SetLastWriteTime()

void System.IO.File.SetLastWriteTime(string ruta, DateTimelw_tm);

Pone la fecha de la ultima escritura del archivo identificado en ruta, al valor especificado en lw_tm

13.1.1.20. Ejemplo de uso de la clase System.IO.File

TODO

13.1.2. Obteniendo informacion sobre archivos: System.IO.FileInfo

Una de las primeras cosas que interesa conseguir de un archivo es la informacion que el sistema operativopuede proveernos, tamaño, localizacion, fechas de creacion, modificacion, acceso...; para ello la BCLnos provee de una clase (System.IO.FileInfo) que nos permite obtener informacion sobre un archivo.

67

Page 74: tutorial csharp.pdf

Capítulo 14. Interoperabilidad con códigonativo

14.1. Uso de Platform Invoke

14.1.1. ¿ Que es Platform Invoke ?

Platform Invoke o su abreviatura PInvoke es un metodo que tenemos en C# para poder llamar afunciones de otras librerias desde nuestros programas, como por ejemplo gtk+ en el caso de gtk# o a lapropia api de windows desde un programa en C# para windows.

El metodo que usa PInvoke es importar la funcion de la libreria donde reside declarandola dentro denuestro programa y usando nuestros parametros de C#

14.1.2. Usando Platform Invoke

Para usar PInvoke primeramente como hemos dicho debemos importar la funcion, para ello usaremos lasiguente sintaxis

[DllImport("libreria desde la que importamos la funcion")]-- Declaracion de la funcion --;

La forma de uso es sencilla y se ve que mediante la llamada DllImport indicamos la libreria donde seencuentra la funcion q pasamos a declarar para su siguiente uso

14.1.3. Ejemplo de uso

Para ver como funciona lo mas facil es ver un ejemplo

[DllImport("glade-2.0")]static extern IntPtr glade_xml_new(string fname, string root, string domain);

En el ejemplo lo que se hace es importar de la libreria glade-2.0 la funcion glade_xml_new. Como hemosdicho antes declaramos la funcion para nuestro uso, mientras que el prototipo original de la funciondentro de libglade es

GladeXML* glade_xml_new (const char *fname, const char *root, const char *domain)

68

Page 75: tutorial csharp.pdf

Capítulo 14. Interoperabilidad con código nativo

Con PInvoke lo declaramos para su uso en C# asi const char se convierte en string y el puntero aGladeXML en un puntero general en c#. Tambien es importante notar que la funcion la definimos comostatic y extern, denotando que puede ser llamada sin definir una clase y que esta definida externamente alcodigo del programa

Finalmente vamos a ver un ejemplo de importacion y llamada

[DllImport("gtk-x11-2.0")]static extern IntPtr gtk_button_new_with_mnemonic(string label);

public Button(string label){

Raw = gtk_button_new_with_mnemonic(label);}

El ejemplo esta tomado de la clase Button de la libreria Gtk#. Como vemos el metodo que se define es unconstructor al que le pasamos el nombre que llevara el propio boton, dentro de gtk+ usamos la funciongtk_button_new_with_mnemonic, por lo tanto la importamos desde la libreria gtk para llamarloposteriormente. Al igual que antes los parametros cambian y el const gchar * que recibe la funcion engtk+ lo cambiamos por el tipo string

Una vez que tenemos importada la funcion y declarada en nuestro entorno el siguiente paso natural esusarla. Para ello en el ejemplo dentro del constructor llamamos a la funcion importada con el parametroque nos pasan.

14.1.4. Conclusiones

Para terminar es importante resaltar la gran utilidad que tiene este prodecimiento dentro de C# ya quepodemos llamar a funciones dentro de otras librerias que estan escritas en otro lenguaje de programacion( realmente no importa cual ). Esto es de una gran utilidad dentro de librerias que hacen de wrapperscomo es el caso de gtk# y gtk+

69

Page 76: tutorial csharp.pdf

Capítulo 15. Introspección

15.1. Introspección: El ojo que todo lo ve

Código gestionado (managed code), la nueva palabra de moda dentro del mundo de la programación,hace referencia a un nuevo tipo de código que se ejecuta bajo el escrutinio de una máquina virtual, alcual nos permite conocer muchas cosas sobre ese código, saber que va a hacer, de que cosas depende ymuchas otras cosas. En realidad todos estos conceptos ya los teníamos en Java, por lo que se podría decirque el código Java también es código gestionado.

Vamos a ver en esta sección del tutorial de C# como se nos abren grandes posibilidades con este tipo decódigo, que vamos a poder relacionarlo de forma sencilla con otras herramientas o que incluso vamos apoder modificar el código que se ejecuta en función del contexto de ejecución en el que se encuentre elprograma.

Lo primero que vamos a realizar es la presentación de algunos conceptos que vamos a utilizar dentro dela sección, para luego ir buceando en todos ellos.

15.1.1. Metadatos

El primer concepto con el que vamos a jugar es con el de metadatos, que se refiere al conjunto dedescripciones que se hacen sobre los datos, lo que muchas veces se llama "los datos sobre los datos".

En nuestro caso, los datos que vamos a describir con metadatos son el código de nuestra aplicación.Vamos a ir añadiendo al código características que enriquezcan la interpretación posterior y lo que sepuede hacer con ese código posteriormente.

Los detalles sobre los datos, metadatos, también pueden ser detallados con otras descripciones, con loque tenemos otro nivel de metadatos, es decir, "meta"-"metadatos".

Como vemos el concepto de metadatos es algo abstracto pero vamos a comenzar a ver ejemplos realesque nos permitan tocar el suelo, y ver su gran utilidad.

15.1.2. Atributos

Los atributos son el pilar de los metadatos en C#. Con ellos vamos a poder especificar características dediferentes partes del código C#. Según donde situemos el atributo, se aplicará a una zona u otra (clase,método, ensamblado ...). Tenemos atributos que ya están predefinidos dentro de C#, otros dentro de.NET, otros dentro de Mono y otros que nos podemos crear nosotros mismos.

70

Page 77: tutorial csharp.pdf

Capítulo 15. Introspección

Los atributos no dejan de ser objetos de C#.

15.1.3. Código gestionado: Integración

15.1.4. Reflexión

15.1.5. Ejemplo práctico: Árboles de Mono

71

Page 78: tutorial csharp.pdf

Capítulo 16. Creación y uso de librerías

Usando las opciones correctas en la compilación, C# genera librerías con sufijo dll que se puedenacceder directamente desde una aplicación cliente.

16.1. Compilando para una librería

Esto es muy sencillo, simplemente especifícale al compilador la opción -targe:library y te generará unarchivo dll. Supongamos que nuestra librería es un archivo MisMetodos.cs con el siguiente código:

namespace MisMetodos{public class Mates{

public static int Factorial( int n ){

int fact = n;for( int i = n-1; i> 0; i-- ){

fact = fact*i;}return fact;

}}

}

que simplemente contiene una función para calcular el factorial de un entero. Lo compilamos ahora con

mcs MisMetodos.cs -target:library

y obtendremos nuestra librería MisMetodos.cs

16.2. Usando nuestra libreria

Ahora nos falta construir una aplicación cliente que utilize los métodos definidos en nuestra librería. Elcódigo de esta aplicación podría ser la seguiente (supongamos que está en un archivos llamadoMiApliCliente.cs):

using System;using MisMetodos;

class MiApliCliente{

public static void Main(){

int n = Mates.Factorial(5);Console.WriteLine("5! = {0}", n );

72

Page 79: tutorial csharp.pdf

Capítulo 16. Creación y uso de librerías

}}

Ahora para compilar éste archivo debemos de referenciar la librería que acabamos de compilar. esnecesario que la librería y nuestra aplicación estén en el mismo directorio. Referenciar una libreíasiempre se consigue dándole al compilador la opción -r:nombre_de_la_libreria.dll. El nuestro caso seríaalgo así:

mcs MiApliCliente.cs -r:MisMetodos.dll

y cuando lo ejecutemos (mono MiAplicliente o ./MiApliCliente ) debería de producir la siguiente salida

5! = 120

73

Page 80: tutorial csharp.pdf

Capítulo 17. Autores

Para la versión 0.6 (octubre-2004):

• Fabian Seoane (http://fseoane.net), (fseoane), <[email protected] >

Para la versión 0.5 (agosto-2004):

• Fabian Seoane (http://fseoane.net), (fseoane), <[email protected] >

En versiones anteriores han colaborado las siguientes personas

• Alejando Sánchez, <[email protected] >

• Alvaro del Castillo, <[email protected] >

• Eduardo García Cebollero, <[email protected] >

• César García Tapia, <[email protected] >

• Sergio Gómez Bachiller, <[email protected] >

• Roberto Pérez Cubero, <[email protected] >

• Jaime Anguiano Olarra, <[email protected] >

74

Page 81: tutorial csharp.pdf

Bibliografía

LibrosHerbert Schild, Mc Graw Hill,Manual de referencia C#.

Ecma-International,Estándar Ecma-334.

ArtículosDare Obsanjo, MSDN,Understanding XML

(http://msdn.microsoft.com/XML/Understanding/default.aspx?pull=/library/en-us/dnxml/html/understxml.asp).

75