Вы находитесь на странице: 1из 153

Curso de C# Introduccin al lenguaje de programacin C#

Caractersticas del lenguaje C# Aspectos Lxicos E/S bsica Tipos de datos Variables y constantes Operadores y expresiones Estructuras de control C# (ledo en ingls "C Sharp" y en espaol "C Almohadilla") es el nuevo lenguaje de propsito general diseado por Microsoft para su plataforma .NET. Aunque es posible escribir cdigo para la plataforma .NET en muchos otros lenguajes, C# es el nico que ha sido diseado especficamente para ser utilizado en ella, por lo que programarla usando C# es mucho ms sencillo e intuitivo que hacerlo con cualquiera de los otros lenguajes ya que C# carece de elementos heredados innecesarios en .NET. Por esta razn, se suele decir que C# es el lenguaje nativo de .NET

Caractersticas del lenguaje C#


Aunque es pronto para entrar con detenimiento en el lenguaje C# podemos adelantar las caractersticas ms relevantes de este lenguaje, caractersticas que se describen con profundidad posteriormente, durante el estudio detallado de los elementos del lenguaje. Es autocontenido. Un programa en C# no necesita de ficheros adicionales al propio cdigo fuente, como los ficheros de cabecera (.h) de C++, lo que simplifica la arquitectura de los proyectos software desarrollados con C++. Es homogneo. El tamao de los tipos de datos bsicos es fijo e independiente del compilador, sistema operativo o mquina en la que se compile (no ocurre lo que en C++), lo que facilita la portabilidad del cdigo.

Es actual. C# incorpora en el propio lenguaje elementos que se han demostrado ser muy tiles para el desarrollo de aplicaciones como el tipo bsico decimal que representa valores decimales con 128 bits, lo que le hace adecuado para clculos financieros y monetarios, incorpora la instruccin foreach, que permite una cmoda iteracin por colecciones de datos, proporciona el tipo bsico string, permite definir cmodamente propiedades (campos de acceso controlado), etc. Est orientado a objetos. C# soporta todas las caractersticas propias del paradigma de la programacin orientada a objetos: encapsulacin, herencia y polimorfismo. o Encapsulacin: adems de los modificadores de accceso convencionales: public, private y protected, C# aade el modificador internal, que limita el acceso al proyecto actual. o C# slo admite herencia simple. o Todos los mtodos son, por defecto, sellados, y los mtodos redefinibles han de marcarse, obligatoriamente, con el modificador virtual. Delega la gestin de memoria. Como todo lenguaje de .NET, la gestin de la memoria se realiza automticamente ya que tiene a su disposicin el recolector de basura del CLR. Esto hace que el programador se desentienda de la gestin directa de la memoria (peticin y liberacin explcita) evitando que se cometan los errores habituales de este tipo de gestin en C++, por ejemplo. En principio, en C# todo el cdigo incluye numerosas restricciones para asegurar su seguridad no permite el uso de punteros, por ejemplo. Sin embargo, y a diferencia de Java, en C# es posible saltarse dichas restricciones manipulando objetos a travs de punteros. Para ello basta marcar regiones de cdigo como inseguras (modificador unsafe) y podrn usarse en ellas punteros de forma similar a cmo se hace en C++, lo que puede resultar vital para situaciones donde se necesite una eficiencia y velocidad procesamiento muy grandes. Emplea un sistema de tipos unificado. Todos los tipos de datos (incluidos los definidos por el usuario) siempre derivarn, aunque sea de manera implcita, de una clase base comn llamada System.Object, por lo que dispondrn de todos los miembros definidos en sta clase. Esto tambin es aplicable, lgicamente, a los tipos de datos bsicos. Proporciona seguridad con los tipos de datos. C# no admiten ni funciones ni variables globales sino que todo el cdigo y datos han de definirse dentro de definiciones de tipos de datos, lo que reduce problemas por conflictos de nombres y facilita la legibilidad del cdigo.

C# incluye mecanismos que permiten asegurar que los accesos a tipos de datos siempre se realicen correctamente: No pueden usarse variables que no hayan sido inicializadas. Slo se admiten conversiones entre tipos compatibles Siempre se comprueba que los ndices empleados para acceder a los elementos de una tabla (vector o matriz) se encuentran en el rango de valores vlidos. o Siempre se comprueba que los valores que se pasan en una llamada a mtodos que pueden admitir un nmero indefinido de parmetros (de un cierto tipo) sean del tipo apropiado. Proporciona instrucciones seguras. En C# se han impuesto una serie de restricciones para usar las instrucciones de control ms comunes. Por ejemplo, toda condicin est controlada por una expresin condicional, los casos de una instruccin condicional mltiple (switch) han de terminar con una instruccin break o goto, etc. Facilita la extensibilidad de los operadores. C# permite redefinir el significado de la mayora de los operadores -incluidos los de conversin, tanto para conversiones implcitas como explcitascuando se aplican a diferentes tipos de objetos. Permite incorporar modificadores informativos sobre un tipo o sus miembros. C# ofrece, a travs del concepto de atributos, la posibilidad de aadir, a los metadatos del mdulo resultante de la compilacin de cualquier fuente, informacin sobre un tipo o sus miembros a la generada por el compilador que luego podr ser consultada en tiempo ejecucin a travs de la biblioteca de reflexin de .NET. Esto, que ms bien es una caracterstica propia de la plataforma .NET y no de C#, puede usarse como un mecanismo para definir nuevos modificadores. Facilita el mantenimiento (es "versionable"). C# incluye una poltica de versionado que permite crear nuevas versiones de tipos sin temor a que la introduccin de nuevos miembros provoquen errores difciles de detectar en tipos hijos previamente desarrollados y ya extendidos con miembros de igual nombre a los recin introducidos. Apuesta por la compatibilidad. C# mantiene una sintaxis muy similar a C++ o Java que permite, bajo ciertas condiciones, incluir directamente en cdigo escrito en C# fragmentos de cdigo escrito en estos lenguajes. o o o En resumen, podemos concluir que: Es un lenguaje orientado al desarrollo de componentes (mdulos independientes de granularidad mayor que los objetos) ya que los componentes son objetos que se caracterizan por sus propiedades, mtodos y eventos y estos aspectos de los componentes estn presentes de manera natural en C#. En C# todo son objetos: desaparece la distincin entre tipos primitivos y objetos de lenguajes como Java o C++ (sin penalizar la eficiencia como en LISP o Smalltalk).

El software es robusto y duradero: el mecanismo automtico de recoleccin de basura, la gestin de excepciones, la comprobacin de tipos, la imposibilidad de usar variables sin inicializar y hacer conversiones de tipo (castings) no seguras, gestin de versiones, etc. ayudan a desarrollar software fcilmente mantenible y poco propenso a errores. Adems, no hay que olvidar el aspecto econmico: la posibilidad de utilizar C++ puro (cdigo no gestionado o inseguro), la facilidad de interoperabilidad (XML, SOAP, COM, DLLs...) junto con un aprendizaje relativamente sencillo (para los que ya conocen otros lenguajes de programacin) hace que el dominio y uso del lenguaje junto a otras tecnologas sea muy apreciado.

Aspectos Lxicos
En esta seccin presentaremos las reglas sintcticas bsicas que deben cumplir los programas escritos en C# y veremos los elementos fundamentales de cualquier programa en C# (identificadores, comentarios, palabras reservadas, etc.). Se trata, en definitiva, de la parte instrumental y bsica, adems de imprescindible, de cualquier manual de programacin. Es costumbre desde la poca dorada del lenguaje C (quiz una de las pocas costumbres que se mantienen en el mundo de la informtica) que se presente un lenguaje de programacin empleando un programa que muestra en la consola el mensaje Hola, mundo! (para ser ms precisos deberamos decir Hello, world!). Sigamos manteniendo esta costumbre: Hola, mundo! /* Fichero: Saludo.cs Fecha: Enero de 2004 Autores: F. Berzal, F.J. Cortijo y J.C.Cubero Comentarios: Primer programa escrito en C# */ using System; public class SaludoAlMundo { public static void Main( ) { // Mostrar en la consola el mensaje: Hola, mundo! Console.WriteLine("Hola, mundo!"); Console.ReadLine(); // Enter para terminar. } }

Lo primero que hay que resaltar es que C# es un lenguaje sensible a las maysculas, por lo que, por ejemplo, Main es diferente a main, por lo que deber prestar atencin a la hora de escribir el cdigo ya que la confusin entre maysculas y minsculas provocar errores de compilacin. Todas las rdenes acaban con el smbolo del punto y coma ( ;). Los bloques de rdenes (parte iterativa de un ciclo, partes dependientes de una instruccin condicional -parte if y parte else-, cdigo de un mtodo, etc.) se encierran entre llaves { } y no se escribe el ; despus de la llave de cierre (observar en el ejemplo el mtodo Main). Si el lector ha programado en C++ no habr tenido dificultad en localizar los comentarios que hemos insertado en el programa ya que la sintaxis es idntica en ambos lenguajes: existen comentarios de lnea, cuyo comienzo se especifica con los caracteres // y comentarios de formato libre, delimitados por /* y */. C# es un lenguaje orientado a objetos y todo est encapsulado en clases, incluso la funcin Main que es un mtodo (especial) del objeto aplicacin. En el ejemplo la clase se llama SaludoAlMundo. La lnea using System declara que se va a usar el espacio de nombres ( namespace) llamado System. Esta declaracin no es igual a un #include de C#, tan solo evita escribir el prefijo System cada vez que se use un elemento de ese espacio de nombres. Por ejemplo, Console es un objeto del espacio de nombres System; en lugar de escribir su nombre completo (System.Console) podemos escribir solamente Console al haber declarado que se va a emplear el espacio de nombres System. Cuando este programa se compile y se proceda a su ejecucin, la primera funcin (estrictamente hablando, mtodo) en ejecutarse ser Main. Los programadores de C++ debern tener especial cuidado y no confundirlo con main(). La funcin Main() tiene los siguientes prototipos vlidos: Si la funcin no devuelve ningn valor, puede usarse: public static void Main( ) public static void Main(string [] args) La diferencia est en la posibilidad de suministrar argumentos en la llamada a la ejecucin del programa. Main() procesan los argumentos tomndolos de la lista de cadenas args (ms adelante se detalla cmo). Si la funcin devuelve un valor al proceso que invoca la ejecucin del programa, el valor debe ser entero (int) y, como en el caso anterior, puede usarse: public static int Main( ) public static int Main(string [] args) La convencin es que el valor 0 indica que el programa termina correctamente.

En cuanto a las instrucciones que efectivamente se ejecutan, el mtodo Main() llama a los mtodos WriteLine() y ReadLine() del objeto Console. El primero se encargan de mostrar una cadena en la consola (Smbolo de Sistema, en Windows XP) y el segundo de tomar una cadena de la consola (teclado). Aunque esta ltima instruccin pueda parecer innecesaria, de no escribirla se mostrara la cadena Hola, mundo! y se cerrara inmediatamente la consola, por lo que no podramos contemplar el resultado de la ejecucin. La ltima instruccin detiene la ejecucin del programa hasta que se introduce una cadena (pulsar ENTER es suficiente) y a continuacin termina la ejecucin del programa y se cierra la consola. As, cuando usemos aplicaciones de consola siempre terminaremos con esta instruccin. A continuacn detallaremos ciertos aspectos lxicos importantes de los programas escritos en C#, algunos de los cuales ya se han comentado brevemente como explicacin al programa introductorio.

Comentarios
Los comentarios tienen como finalidad ayudar a comprender el cdigo fuente y estn destinados, por lo tanto, a los programadores. No tienen efecto sobre el cdigo ejecutable ya que su contenido es ignorado por el compilador (no se procesa). La sintaxis de los comentarios en C# es idntica a la de C++ y se distinguen dos tipos de comentarios: Comentarios de lnea. Estn precedidos de la construccin // y su efecto (mbito) termina en la lnea en la que est inmerso. Comentarios de formato libre. Estn delimitados por las construcciones /* y */ y pueden extenderse por varias lneas. Ejemplos de comentarios // En una lnea, al estilo de C++ /* En mltiples lneas, como se viene haciendo desde "los tiempos de C" */ /* Este tipo de comentario ya no es habitual */

Identificadores
Un identificador es un nombre con el que nos referimos a algn elemento de nuestro programa: una clase, un objeto, una variable, un mtodo, etc. Se imponen algunas restricciones acerca de los nombres que pueden emplearse: Deben comenzar por una letra letra o con el carcter de subrayado (_), que est permitido como carcter inicial (como era tradicional en el lenguaje C). No pueden contener espacios en blanco.

Pueden contener caracteres Unicode, en particular secuencias de escape Unicode. Son sensibles a maysculas/minsculas. No pueden coincidir con palabras reservadas (a no ser que tengan el prefijo @ que habilita el uso de palabras clave como identificadores). Los identificadores con prefijo @ se conocen como identificadores literales. Aunque el uso del prefijo @ para los identificadores que no son palabras clave est permitido, no se recomienda por regla de estilo.

Palabras reservadas
Las palabras reservadas son identificadores predefinidos reservados que tienen un significado especial para el compilador por lo que no se pueden utilizar como identificadores en un programa a menos que incluyan el carcter @ como prefijo. Las palabras reservadas en C# son, por orden alfabtico:

abstract, as, base, bool, break, byte, case, catch, char, checked, class, const, continue, decimal, default, delegate, do, double, else, enum, event, explicit, extern, false, finally, fixed, float, for, foreach, goto, if, implicit, in, int, interface, internal, is, lock, long, namespace, new, null, object, operator, out, override, params, private, protected, public, readonly, ref, return, sbyte, sealed, short, sizeof, stackalloc, static, string, struct, switch, this, throw, true, try, typeof, uint, ulong, unchecked, unsafe, ushort, using, virtual, void, volatile, while

Literales
Un literal es una representacin en cdigo fuente de un valor. Todo literal tiene asociado un tipo, que puede ser explcito (si se indica en el literal, mediante algn sufijo, por ejemplo) o implcito (se asume uno por defecto). Los literales pueden ser: Literales lgicos. Existen dos valores literales lgicos: true y false. El tipo de un literal lgico es bool. Literales enteros. Permiten escribir valores de los tipos enteros: int, uint, long y ulong. Los literales enteros tienen dos formatos posibles: decimal y hexadecimal. Los literales hexadecimales tienen el sufijo 0x.

El tipo de un literal entero se determina como sigue: o o o o Si Si Si Si no tiene sufijo, su tipo es int. tiene el sufijo U o u, su tipo es uint o ulong. tiene el sufijo L o l, su tipo es long o ulong. tiene el sufijo UL, Ul, uL, ul, LU, Lu, lU o lu es de tipo ulong. A la hora de escribir literales de tipo long se recomienda usar L en lugar de l para evitar confundir la letra l con el dgito 1. Literales enteros 123 0x7B 123U 123ul 123L // // // // // int hexadecimal unsigned unsigned long long

Literales reales. Los literales reales permiten escribir valores de los tipos float, double y decimal. El tipo de un literal real se determina como sigue: o o o o Si Si Si Si no se especifica sufijo, el tipo es double. el sufijo es F o f es de tipo float. el sufijo es D o d es de tipo double. el sufijo es M o m es de tipo decimal.

Hay que tener en cuenta que, en un literal real, siempre son necesarios dgitos decimales tras el punto decimal. Por ejemplo, 3.1F es un literal real, pero no as 1.F. Literales reales 1f, 1.5f, 1e10f, 123.456F, 123f y 1.23e2f // float 1d, 1.5d, 1e10d, 123.456D, 123.0 y 123D // double 1m, 1.5m, 1e10m, 123.456M, 123.456m y 12.3E1M // decimal. Literales de caracteres. Un literal de caracteres representa un carcter nico y normalmente est compuesto por un carcter entre comillas simples, por ejemplo 'A'.

Una secuencia de escape sencilla representa una codificacin de caracteres Unicode y est formada por el carcter \ seguido de otro carcter. Las secuencias vlidas se describe en la siguiente tabla. Secuencia de escape \' \" \\ \0 \a \b \f \n \r \t \v Nombre del carcter Comilla simple Comilla doble Barra invertida Null Alerta Retroceso Avance de pgina Nueva lnea Retorno de carro Tabulacin horizontal Tabulacin vertical Codificacin Unicode 0x0027 0x0022 0x005C 0x0000 0x0007 0x0008 0x000C 0x000A 0x000D 0x0009 0x000B

El tipo de un literal de caracteres es char. Literales de caracteres 'A' '\u0041' '\x0041' '\n' // // // // caracter caracter unsigned caracter sencillo Unicode short hexadecimal de escape: CR+LF

Literales de cadena. C# admite dos formatos de literales de cadena: literales de cadena tpicos y literales de cadena textuales. El tipo de un literal de cadena es string. Un literal tpico de cadena consta de cero o ms caracteres entre comillas dobles y puede incluir secuencias de escape sencillas y secuencias de escape hexadecimales y Unicode.

Literales tipicos de cadena "!Hola, mundo!" "!Hola, \t mundo!" "" // !Hola, mundo! // !Hola, mundo! // La cadena vacia

Un literal de cadena textual consta del carcter @ seguido de un carcter de comillas dobles, cero o ms caracteres y un carcter de comillas dobles de cierre. Por ejemplo, @"Hola". En estos literales los caracteres se interpretan de manera literal y no se procesan las secuencias de escape, con la nica excepcin de la secuencia \". Un literal de cadena textual puede estar en varias lneas. Literales de cadena textuales @"!Hola, \t mundo!" "Me dijo \"Hola\" y me asust" me asust @"Me dijo ""Hola"" y me asust" me asust "\\\\servidor\\share\\file.txt" \\servidor\share\file.txt @"\\servidor\share\file.txt" \\servidor\share\file.txt @"uno cadena distribuida dos" Literal null. Su nico valor es null y su tipo es el tipo null. // !Hola, \t mundo! // Me dijo "Hola" y // Me dijo "Hola" y // // // // Esta es una en dos lineas.

rdenes
Delimitadas por punto y coma (;) como en C, C++ y Java. Los bloques { ... } no necesitan punto y coma al final.

E/S bsica
Las operaciones de entrada y salida tienen como objetivo permitir que el usuario pueda introducir informacin al programa (operaciones de entrada) y que pueda obtener informacin de ste (operaciones de salida). En definitiva, tratan de la comunicacin entre el usuario y el programa.

La manera ms simple de comunicacin es mediante la consola. La consola ha sido el modo tradicional de comunicacin entre los programas y el usuario por su simplicidad. Las aplicaciones basadas en ventanas resultan mucho ms atractivas y cmodas para el usuario y es ste, sin duda, el tipo de comunicacin que deberemos emplear para productos finales. Los programas que no requieran una mucha interaccin con el usuario, no obstante, se construyen y se ponen en explotacin mucho ms rpidamente si se utiliza la consola como medio de comunicacin.

Aplicaciones en modo consola


Estas aplicaciones emplean la consola para representar las secuencias de entrada, salida (y error) estndar. Una aplicacin de consola se crea en Visual Studio .NET seleccionando Archivo, Nuevo y Proyecto. Cuando aparece la ventana Nuevo proyecto se selecciona Proyectos de Visual C# y Aplicacin de consola:

El acceso a la consola lo facilita la clase Console, declarada en el espacio de nombres System. Esa clase proporciona la compatibilidad bsica para aplicaciones que leen y escriben caracteres en la consola. No es necesario realizar ninguna accin para poder obtener datos de la consola a partir de la entrada estndar (teclado) o presentarlos en la salida estndar (consola) ya que estos flujos (junto con el del error estndar) se asocian a la consola de manera automtica, como ocurre en C++, por ejemplo, con cin, cout y cerr.

Los mtodos bsicos de la clase Console son WriteLine y ReadLine, junto con sus variantes Write y Read: WriteLine escribe una lnea en la salida estndar, entendiendo que escribe el terminador de lnea actual (por defecto la cadena "\r\n"). La versin ms simple de este mtodo recibe un nico argumento (una cadena) cuyo valor es el que se muestra: Console.WriteLine ("!Hola, " + "mundo!"); // Escribe: !Hola, mundo! Otra versin permite mostrar variables de diferentes tipos (sin necesidad de convertirlas a string. La llamada tiene un nmero indeterminado de argumentos: el primero es una cadena de formato en la que las variables a mostrar se indican con {0}, {1}, etc. y a continuacin se enumeran las variables a mostrar, entendiendo que la primera se "casa" con {0}, la segunda con {1}, etc. Esta manera de mostrar los datos recuerda a la instruccin printf de C, que cay en desuso con C++ ...

int TuEdad = 25; string TuNombre = "Pepe"; Console.WriteLine ("Tienes {0} aos, TuNombre); // Escribe: Tienes 25 aos, Pepe.

{1}.",

TuEdad,

El mtodo Write hace lo mismo que WriteLine aunque no escribe el terminador de lnea. ReadLine lee la siguiente lnea de caracteres de la secuencia de entrada estndar (el teclado, por defecto), eliminando del buffer de entrada el terminador de lnea. Devuelve la cadena leida, que no contiene el carcter o los caracteres de terminacin. Read lee el siguiente carcter de la secuencia de entrada estndar y devuelve un valor de tipo int. La lectura se realiza del buffer de entrada y no se termina (no devuelve ningn valor) hasta que se encuentra al caracter de terminacin (cuando el usuario presion la tecla ENTER). Si existen datos disponibles en el buffer, la secuencia de entrada contiene los datos introducidos por el usuario, seguidos del carcter de terminacin.

Veamos un sencillo ejemplo sobre el uso de estos mtodos. E/S simple using System; class PideNombre { static void Main(string[] args) { Console.Write ("Introduzca su nombre: "); // 1 string nombre = Console.ReadLine(); // 2 Console.WriteLine ("Su nombre es: " + nombre); // 3 Console.ReadLine(); // 4 } } La instruccin 1 muestra la cadena Introduzca su nombre: pero no avanza a la siguiente lnea de la consola, por lo que cuando se ejecuta la instruccin 2 lo que escribe el usuario se muestra a continuacin, en la misma lnea. La cadena que escribe el usuario se guarda en la variable nombre y se elimina del buffer de entrada el terminador de lnea. Cuando se valida la entrada (al pulsar ENTER) se avanza a la siguiente lnea. La instruccin 3 muestra una cadena, resultado de concatenar un literal y la cadena introducida por el usuario. Finalmente, la instruccin 4 es necesaria para detener la ejecucin del programa (realmente, la finalizacin del mismo) hasta que el usuario pulse ENTER. Observar que aunque el mtodo Readline devuelva una cadena, ste valor devuelto no es usado. En la siguiente figura mostramos dos ejemplos de ejecucin.

Aplicaciones Windows
Una aplicacin basada en ventanas (aplicacin Windows, en lo que sigue) utilizan ventanas y componentes especficos para interactuar con el usuario. Las peticiones de datos se realizan con componentes de entrada de texto (por ejemplo, con un TextBox) o mediante la seleccin en una lista de posibilidades (por ejemplo, con un ComboBox). Las salidas pueden realizarse de mltiples maneras, empleando componentes Label, ventanas de mensajes MessageBox, etc. Por ejemplo, en la figura siguiente mostramos una aplicacin que responde mostrando una ventana de mensaje (MessageBox) cuando se pincha sobre el botn titulado Saludo. Basta con ejecutar este cdigo cada vez que se pinche en dicho botn: MessageBox.Show ("Hola, mundo!", "Un saludo tpico"); (en realidad, System.Windows.Forms.MessageBox.Show (...);)

Una aplicacin ms compleja podra pedir el nombre del usuario en un componente TextBox y mostrarlo empleando un componente Label cuando se pincha en el botn titulado Saludo:

Una aplicacin de ventanas se crea fcilmente en Visual Studio .NET seleccionando Archivo, Nuevo y Proyecto. En la ventana Nuevo proyecto se selecciona ahora Proyectos de Visual C# y Aplicacin para Windows.

Tipos de datos
Los tipos de datos ofrecidos por C# al programador forman parte de un sistema unificado en el que todos los tipos de datos (incluidos los definidos por el usuario) derivan, aunque sea de manera implcita, de la clase System.Object. Por herencia dispondrn de todos los miembros definidos en sta clase, en particular los tiles mtodos Equals(), GetHashCode(), GetType() y ToString() que describiremnos ms adelante. C# proporciona seguridad con los tipos de datos. C# no admiten ni funciones ni variables globales sino que todo el cdigo y datos han de definirse dentro de definiciones de tipos de datos, lo que reduce problemas por conflictos de nombres y facilita la legibilidad del cdigo. C# incluye mecanismos que permiten asegurar que los accesos a tipos de datos siempre se realicen correctamente: No pueden usarse variables que no hayan sido iniciadas. El tipo asignado restringe los valores que puede almacenar y las operaciones en las que puede intervenir. Siempre se comprueba que los ndices empleados para acceder a los elementos de una tabla (vector o matriz) se encuentran en el rango de valores vlidos. Slo se admiten conversiones de tipo entre tipos compatibles y entre aquellos que se hayan definido explcitamente el mecanismo de conversin (En C# puede implementarse la manera en que se realiza la conversin implcita y explcita entre tipos) Los tipos de datos en C# pueden clasificarse en dos grandes categoras: tipos valor tipos referencia y pueden caracterizarse como sigue: Tipos valor La variable contiene un valor El dato se almacena en la pila El dato siempre tiene valor Tipos referencia La variable contiene una referencia El dato se almacena en el heap El dato puede no tener valor

null

Una asignacin copia el valor Una asignacin copia la referencia int i = 123; string s = "Hello world"; // tipo valor // tipo referencia

El comportamiento cuando se copian o modifican objetos de estos tipos es muy diferente.

Tipos valor
Los tipos bsicos son tipos valor. Si una variable es de un tipo valor contiene nicamente un valor del tipo del que se ha declarado. Los tipos predefinidos de C# son tipos disponibles en la plataforma .NET y que, por comodidad, en C# se emplean usando un alias. En la tabla siguiente enumeramos los tipos simples detallando su nombre completo, su alias, una breve descripcin, el nmero de bytes que ocupan y el rango de valores. Nombre (.NET Framework) Tamao (bytes) 1 2 4

Alias

Descripcin Bytes con signo Enteros cortos Enteros Enteros largos Bytes (sin signo) Enteros cortos (sin signo) Enteros (sin signo) Enteros largos (sin signo) Reales (7 decimales) Reales (1516 decimales) Reales (28-

Rango -128 ... 127 -32.768 ... 32.767 -2.147.483.648 ... 2.147.483.647 -9.223.372.036.854.775.808 ... 9.223.372.036.854.775.807 0 ... 255

System.Sbyte System.Int16 System.Int32

sbyte short int

System.Int64

long

System.Byte

byte

System.Uint16

ushort

0 ... 65.535 0 ... 18.446.744.073.709.551.615 0 ... 18.446.744.073.709.551.615 1.5 x 10-45 ... 3.4 x 10+38 5.0 x 10-324 ... 1.7 x 10+308 1.0 x 10-28 ... 7.9 x 10+28 Cualquier carcter Unicode

System.UInt32

uint

System.Uint64

ulong

System.Single

float

System.Double

double

System.Decimal decimal 29
decimales)

12

System.Char

char

Caracteres Unicode

System.Boolean bool

Valores lgicos

true false

El comportamiento de los datos de tipos valor es el esperado cuando se inician o reciben un valor por asignacin a partir de otro dato de tipo valor (son independientes).

Tipos referencia
Si un dato es de un tipo referencia contiene la direccin de la informacin, en definitiva, una referencia al objeto que contiene los datos y/o mtodos. En definitiva, distinguimos: La referencia o un nombre por el que nos referimos al objeto y que utilizamos para manipularlo. El objeto referenciado, que ocupa lugar en memoria (en el heap) y que almacenar el valor efectivo del objeto. En definitiva: la variable y su contenido "lgico" estn en posiciones de memoria diferentes. El valor almacenado en una variable de tipo referencia es la direccin de memoria del objeto referenciado (es una referencia) o tiene el valor null (no referencia a nada). Observe que pueden existir dos variables que referencien al mismo objeto (pueden existir dos referencias a la misma zona de memoria). C# proporciona dos tipos referencia predefinidos: object y string. Todos los dems tipos predefinidos son tipos valor. El tipo object es el tipo base del cual derivan todos los tipos bsicos predefinidos y los creados por el usuario. Pueden crearse nuevos tipos referencia usando declaraciones de clases (class), interfaces (interface) y delegados (delegate), y nuevos tipos valor usando estructuras struct. Los objetos de las clases creadas por el usuario son siempre de tipo referencia. El operador new permite la creacin de instancias de clases definidas por el usuario. new es muy diferente en C# y en C++: En C++ indica que se pide memoria dinmica. En C# indica que se llama al constructor de una clase. El efecto, no obstante, es similar ya que como la variable es de un tipo referencia, al llamar al constructor se aloja memoria en el heap de manera implcita. Considere el siguiente fragmento de cdigo, en el que todas las variables son del mismo tipo: ObjetoDemo).

Tipos referencia class ObjetoDemo { public int Valor; } class AppDemoRef { static void Main(string[] args) { ObjetoDemo o1 = new ObjetoDemo(); // new llama a un constructor o1.Valor = 10; ObjetoDemo o2 = new ObjetoDemo(); // new llama a un constructor o2 = o1; // La memoria que ocupaba el objeto refernciado por "o2" // se pierde: actuar el recolector de basura. PintaDatos ("o1", "o2", o1, o2); ObjetoDemo o3 = new ObjetoDemo();// new llama a un constructor o3.Valor = 10; ObjetoDemo o4 = o3; // "o4" contiene la misma direccion de memoria que "o3" o4.Valor = 20; // Igual que hacer o3.Valor = 20; PintaDatos ("o3", "o4", o3, o4); ObjetoDemo un constructor o5.Valor = ObjetoDemo un constructor o6.Valor = PintaDatos } static void PintaDatos (string st1, string st2, ObjetoDemo ob1, ObjetoDemo ob2) { Console.Write ("{0} = {1}, {2} = {3} ", st1, ob1.Valor, st2, ob2.Valor); if (ob1==ob2) Console.WriteLine ("{0} == {1}", st1, st2); else Console.WriteLine ("{0} != {1}", st1, st2); } } o5 = new ObjetoDemo(); // new llama a 10; o6 = new ObjetoDemo(); // new llama a o5.Valor; ("o5", "o6", o5, o6);

Console.ReadLine();

El tipo string es un tipo especial de tipo referencia. De hecho, parece ms un tipo valor ante la asignacin. Observe el ejemplo:

string s1 = "Hola"; string s2 = s1; En este punto s2 referencia al mismo objeto que s1. Sin embargo, cuando el valor de s1 es modificado, por ejemplo con:

s1 = "Adios"; lo que ocurre es que se crea un nuevo objeto string referenciado por s1. De esta forma, s1 contiene "Adios" mientras que s2 mantiene el valor "Hola". Esto es as porque los objetos string son immutables, por lo que, para cambiar lo que referencia una variable string debe crearse un nuevo objeto string.

Variables y constantes
Variables
Una variable permite el almacenamiento de datos en la memoria. Es una abstraccin que permite referirnos a una zona de memoria mediante un nombre (su identificador). Todas las variables tienen asociadas un tipo que determina los valores que pueden almacenarse y las operaciones que pueden efectuarse con los datos de ese tipo. Adems, el trmino variable indica que el contenido de esa zona de memoria puede modificarse durante la ejecucin del programa. Nombres de variables Los nombres que pueden asignarse a las variables deben regirse por unas normas bsicas: Pueden contener letras, dgitos y el caracter de subrayado (_). No pueden empezar con un nmero: deben comenzar por una letra letra o con el carcter de subrayado (_).

Finalmente, recordar que, como identificador que es, el nombre de una variable es sensible a las maysculas y no pueden coincidir con una palabra reservada a no ser que tenga el prefijo @, aunque no es una prctica recomendada. Declaracin de variables Antes de usar una variable se debe declarar. La declaracin de una variable indica al compilador el nombre de la variable y su tipo. Una declaracin permite que se pueda reservar memoria para esa variable y restringir el espacio (cantidad de memoria) que requiere, los valores que pueden asignarsele y las operaciones en las que puede intervenir. La sintaxis de una declaracin es sencilla: tan slo hay que especificar el tipo de la variable y el nombre que se le asocia. La declaracin debe concluir con el carcter punto y coma. Por ejemplo, si vamos a emplear una variable para guardar en ella el valor del rea de un crculo debemos: Darle un nombre significativo: Area Asociarle un tipo: dado que puede tener decimales y no se requiere una gran precisin, bastar con el tipo float.

float Area; Cuando se van a declarar mltiples variables del mismo tipo no es necesario que cada declaracin se haga por separado, pueden agruparse en la misma lnea compartiendo el tipo. Por ejemplo, las declaraciones:

float Radio; float Area; pueden simplificarse en una lnea:

float Radio, Area; De la misma manera pueden declararse e inicializarse variables en una sola lnea:

int a=1, b=2, c, d=4; No existe ninguna zona predeterminada en el cdigo para la declaracin de variables, la nica restriccin es que la declaracin debe realizarse antes de su uso.

No es conveniente abusar de la declaracin mltiple de variables en una lnea. Desde el punto de vista de la legibilidad es preferible, por regla general, que cada variable se declare separadamente y que la declaracin vaya acompaada de un breve comentario: float Radio; el area. float Area; Acceso a variables Una variable se usa para asignarle un valor (acceso de escritura) o para utilizar el valor almacenado (acceso de lectura). Una vez declarada una variable debe recibir algn valor (es su misin, despus de todo). Este valor lo puede recibir de algn dispositivo (flujo de entrada) o como resultado de evaluar una expresin. La manera ms simple de proporcionar un valor a una variable es emplear la instruccin de asignacin: // Radio del circulo del cual se calcula // Area del circulo

Radio = 10.0F; En el ejemplo se asigna el valor (literal entero) 10 a la variable Radio. El valor que tuviera almacenado la variable Radio se pierde, quedando fijado a 10. En la misma lnea de la declaracin puede asignarse un valor a la variable, por lo que declaracin e inicializacin:

float Radio; // Declaracion Radio = 10.0F; // Inicializacion pueden simplificarse en una sola lnea:

float Radio = 10.0F;

// Declaracion e Inicializacion

La manera ms simple de leer el valor de una variable es emplearla como parte de una expresin, por ejemplo, en la parte derecha de una instruccin de asignacin:

Area = 2 * 3.1416F * Radio * Radio; La variable Radio (su valor) se emplea en la expresin 2 * 3.1416 * Radio * Radio para calcular el rea de un crculo de radio Radio. Una vez calculado el valor de la expresin, ste se asigna a la variable Area.

Otra manera de acceder al valor de una variable para lectura es emplearla como el argumento de una instruccin de escritura WriteLine. Por ejemplo, Console.WriteLine(Area); mostrar en la consola el valor de la variable Area. Leer el valor de una variable no modifica el contenido de la variable. A modo de resumen, un programa que calcula y muestra el rea de un crulo de radio 10 es el siguiente: Calculo del rea de un crculo (1) using System; class Area1 { static void Main(string[] args) { float Radio = 10.0F; float Area = 2 * 3.1416F * Radio * Radio; Console.WriteLine(Area); Console.ReadLine(); } } Como puede parecer evidente, emplear una variable que no ha sido declarada produce un error en tiempo de compilacin. En C#, adems, hay que asignarle un valor antes de utilizarla. Si no se hace se genera un error en tiempo de compilacin ya que esta comprobacin (igual que ocurre en Java) se efecta por el compilador.

void f() { int i; Console.WriteLine(i); local no asignada 'i' }

// Error: uso de la variable

Constantes
Una constante se define de manera parecida a una variable: modeliza una zona de memoria que puede almacenar un valor de un tipo determinado. La diferencia reside en que esa zona de memoria (su contenido) no puede modificarse en la ejecucin del programa. El valor de la constante se asigna en la declaracin. Sintcticamente se especifica que un dato es constante al preceder con la palabra reservada const su declaracin. Por ejemplo, para declarar un a constante de tipo float llamada PI y asignarle el valor (constante) 3.1416 se escribir:

const float PI = 3.1416F; Solo se puede consultar el valor de una constante, nunca se debe intentar modificarlo porque se producira un error en tiempo de compilacin. Por ejemplo, la siguiente instruccin: Area = 2 * PI * Radio * Radio; utiliza la constante PI declarada anteriormente, usando su valor para evaluar una expresin. Podemos modificar el programa anterior para que utilice la constante PI: Calculo del rea de un crculo (2) using System; class Area2 { static void Main(string[] args) { const float PI = 3.1416F; float Radio = 10.0F; float Area = 2 * PI * Radio * Radio; Console.WriteLine(Area); Console.ReadLine(); } }

mbito de variables y constantes


El mbito (del ingls scope) de una variable y/o constante indica en qu partes del cdigo es lcito su uso. En C# el mbito abarca desde el lugar de su declaracin hasta donde termina el bloque en el que fue declarada.

En el mbito de una variable no puede declararse otra variable con el mismo nombre (aunque sea en un bloque interno, algo que si est permitido en C++):

static void Main(string[] args) { float Radio = 10.0F; ... if (Radio > 0){ float Radio = 20.0F; // Error: No se puede declarar una variable // local denominada 'Radio' en este mbito. } ... }

Operadores y expresiones
Un operador est formado por uno o ms caracteres y permite realizar una determinada operacin entre uno o ms datos y produce un resultado. Es una manera simblica de expresar una operacin sobre unos operandos. C# proporciona un conjunto fijo, suficiente y completo de operadores. El significado de cada operador est perfectamente definido para los tipos predefinidos, aunque algunos de ellos pueden sobrecargarse, es decir, cambiar su significado al aplicarlos a un tipo definido por el usuario. C# dispone de operadores aritmticos, lgicos, relacionales, de manipulacin de bits, asignacin, para acceso a tablas y objetos, etc. Los operadores pueden presentarse por diferentes criterios, por ejemplo, por su funcionalidad: Categoras Aritmticos Lgicos (booleanos y bit a bit) Concatenacin de cadenas Incremento y decremento Desplazamiento Relacionales Operadores

+ % & || + ++ << == >= = %= |= |

^ !

* ~

/ &&

->> != += &= ^= < -= <<= > *= >>= <= /=

Asignacin

Acceso a miembros Acceso por ndices Conversin de tipos explcita Conditional Creacin de objetos Informacin de tipos Control de excepciones de desbordamiento Direccionamiento indirecto y direccin

. [] () ? : new as is typeof checked * -> sizeof

unchecked [] &

Los operadores aritmticos de C# son los que se emplean comnmente en otros lenguajes: + (suma), - (resta), * (multiplicacin), / (divisin) y % (mdulo o resto de la divisin). Son operadores binarios y se colocan entre los argumentos sobre los que se aplican, proporcionando un resultado numrico (Por ejemplo, 7+3.5, 66 % 4). Los operadores lgicos proporcionan un resultado de tipo lgico (bool) y los operadores a nivel de bit actan sobre la representacin interna de sus operandos (de tipos enteros) y proporcionan resultados de tipo numrico. Los operadores binarios son: & (operacin Y lgica entre argumentos lgicos u operacin Y bit a bit entre operandos numricos), | (operacin O, lgica bit a bit, dependiendo del tipo de los argumentos) , ^ (O exclusivo, lgico bit a bit), && (Y lgico, que evala el segundo operando solo cuando es necesario) y || (O lgico, que evala el segundo operando solo cuando es necesario). Los operadores unarios son: ! (negacin o complemento de un argumento lgico) y ~ (complemento bit a bit de un argumento numrico). El operador + para la concatenacin de cadenas es un operador binario. Cuando al menos uno de los operandos es de tipo string este operador acta uniendo las representaciones de tipo string de los operandos. Operadores de incremento y decremento. El operador de incremento (++) incrementa su operando en 1 mientras que el de decremento (--) decrementa su operando en 1. Puede aparecer

antes de su operando: ++v (incremento prefijo) o despus: v++ (incremento postfijo). El incremento prefijo hace que el resultado sea el valor del operando despus de haber sido incrementado y el postfijo hace que el resultado sea valor del operando antes de haber sido incrementado. Los tipos numricos y de enumeracin poseen operadores de incremento y decremento predefinidos. Los operadores de desplazamiento son operadores binarios. Producen un desplazamiento a nivel de bits de la representacin interna del primer operando (de un tipo entero), a la izquierda ( <<) o a la derecha (>>) el nmero de bits especificado por su segundo operando. Los operadores relacionales proporcionan un resultado lgico, dependiendo de si sus argumentos son iguales ( ==), diferentes (!=), o del orden relativo entre ellos (<, >, <= y >=). La asignacin simple (=) almacena el valor del operando situado a su derecha en una variable (posicin de memoria) indicada por el operando situado a su izquierda. Los operandos deben ser del mismo tipo o el operando de la derecha se debe poder convertir implcitamente al tipo del operando de la izquierda). El operador de asignacin = produce los siguientes resultados: o En tipos simples el funcionamiento es similar al de C++, copia el contenido de la expresin de la derecha en el objeto que recibe el valor. En datos struct realiza una copia directa del contenido, como en C++. En clases se copia la referencia, esto es, la direccin del objeto, provocando que el objeto sea referenciado por ms de una referencia. Este comportamiento es distinto al que se produce en C++, en el que se copia el contenido del objeto. Si el objeto contiene estructuras ms complejas, C++ requiere normalmente la sobrecarga del operador para personalizar la manera en que se realiza la copia para que cada instancia de la clase (fuente y destino) maneje su propia zona de memoria. En C# no se permite la sobrecarga del operador = Los otros operadores de esta categora realizan, adems de la asignacin otra operacin previa a la asignacin. Por ejemplo, a += 22;

o o

equivale a a = a + 22; o sea, primero se calcula la expresin a+22 y posteriormente, ese valor se almacena en a. De la misma manera actan los dems operadores de asignacin: =, *=, /=, %=, &=, |=, ^=, << = y >>=. Para asignar instancias de clases (en el sentido clsico de C++) se debe redefinir el mtodo MemberwiseCopy() que es heredado por todas las clases desde System.Object (todas las clases heredan, en ltima instancia de System.Object). El operador de acceso a miembros es el operador punto (.) y se emplea para acceder a los miembros (componentes) de una clase, estructura o espacio de nombres. El operador de acceso por ndices es el tradicional, formado por la pareja de caracteres [ y ]. En C# tambin se emplea para especificar atributos. El operador de conversin explcita de tipos (casting) es el clsico, formado por la pareja de caracteres ( y ). El operador ternario condicional evala una condicin lgica y devuelve uno de dos valores. Se utiliza en expresiones de la forma: cond ? expr1 : expr2 de manera que si cond es verdad, se evala expr1 y se devuelve como resultado; si cond es falsa se evala expr1 y se devuelve como resultado. Creacin de objetos (new). El operador new permite crear instancias de una clase definida por el usuario. Por ejemplo: ObjetoDemo o1 = new ObjetoDemo() declara una referencia (llamada o1) a un objeto de la clase ObjetoDemo y crea una instancia de esa clase, referenciada por o1. En realidad, la creacin de un objeto implica la llamada a un constructor de la clase. Control de excepciones de desbordamiento: checked y unchecked C# proporciona la posibilidad de realizar operaciones de moldeado (casting) y aritmticas en un contexto verificado. En otras palabras, el entorno de ejecucin .NET detecta cualquier situacin de desbordamiento y lanza una excepcin (OverFlowException) si sta se manifiesta.

En un contexto verificado (checked), si una expresin produce un valor fuera del rango del tipo de destino, el resultado depende de si la expresin es constante o no. Las expresiones constantes producen errores de compilacin, mientras que las expresiones no constantes se evalan en tiempo de ejecucin y producen excepciones. Si no se especifican ni checked ni unchecked, las expresiones constantes utilizan la verificacin de desbordamiento predeterminada en tiempo de compilacin, que es checked. De lo contrario, si la expresin no es constante, la verificacin de desbordamiento en tiempo de ejecucin depende de otros factores tales como las opciones del compilador y la configuracin del entorno. En el siguiente ejemplo, el valor por defecto es unchecked:

using System; class PruebaDesbordamiento { static short x = 32767; static short y = 32767;

// Maximo valor short

public static int UnCh() { int z = unchecked((short)(x + y)); return z; // -2 } public static int UnCh2() { int z = (short)(x + y); // Por defecto es unchecked return z; // -2 } public static void Main() { Console.WriteLine("Valor -unchecked-: {0}", UnCh()); Console.WriteLine("Valor por defecto: {0}", UnCh2()); Console.ReadLine(); } } El resultado es: Valor -unchecked-: -2 Valor por defecto: -2 Si aadimos la funcin:

public static int Ch() { int z = checked((short)(x + y)); return z; } y la llamada: Console.WriteLine("Valor -checked: {0}", Ch());

la ejecucin del programa provoca el lanzamiento de una excepcin System.OverflowException que detiene la ejecucin del programa, al no estar controlada.

Operadores especiales: as is typeof


El operador as El operador as se utiliza para realizar conversiones entre tipos compatibles (al estilo de los casts dinmicos de C++). El operador as se utiliza en expresiones de la forma:

<expresion> as type
donde <expresion> es una expresin de un tipo de referencia, y type es un tipo de referencia. Si la conversin de tipo no es posible, el resultado es null.

// Ejempo de uso del operador as using System; class Clase1 {} class Clase2 {} public class Demo { public static void Main() { object [] Objetos = new object[6]; Objetos[0] = new Clase1(); Objetos[1] = new Clase1(); Objetos[2] = "Hola"; Objetos[3] = 123; Objetos[4] = 123.4; Objetos[5] = null; for (int i=0; i<Objetos.Length; ++i) { string s = Objetos[i] as string;

Console.Write ("{0}:", i); if (s != null) Console.WriteLine ( "'" + s + "'" ); else Console.WriteLine ( "no es string" ); } Console.ReadLine(); } } El resultado es: 0:no es string 1:no es string 2:'Hola' 3:no es string 4:no es string 5:no es string En el siguiente ejemplo, la funcin DoSomething recibe cualquier argumento, pero slo cuando es de tipo Car se ejecuta el mtodo Drive sobre el objeto c (de clase Car).

static void DoSomething(object o) { Car c = o as Car; if (c != null) c.Drive(); } El operador is El operador is se utiliza para comprobar en tiempo de ejecucin si el tipo de un objeto es compatible con un tipo dado. El operador is se utiliza en expresiones de la forma:

<expresion> is type
donde <expresion> es una expresin de un tipo de referencia, y type es un tipo de referencia. El siguiente ejemplo realiza la misma tarea que comentamos en el ejemplo sobre el operador as solo que ahora no hay ningn objeto local con referencia explcita sobre el que se ejecute el mtodo Drive.

static void DoSomething(object o) { if (o is Car) ((Car)o).Drive(); }

El operador typeof El operador typeof devuelve el objeto derivado de System.Type correspondiente al tipo especificado. Una expresin typeof se presenta de la siguiente forma:

typeof (tipo)
donde

tipo es el tipo cuyo objeto System.Type se desea obtener.

De esta forma se puede hacer reflexin para obtener dinmicamente informacin sobre los tipos (como en Java).

... Console.WriteLine(typeof(int).FullName); Console.WriteLine(typeof(System.Int).Name); Console.WriteLine(typeof(float).Module); Console.WriteLine(typeof(double).IsPublic); Console.WriteLine(typeof(Point).MemberType); ...

Un ejemplo en el que se emplean los operadores is, as y typeof para discriminar el tipo del argumento recibido en una funcin polimrfica y trabajar con l es el siguiente:

// Ejempo de uso de los operadores is, as y typeof using System; class Clase1 {} class Clase2 {} public class Demo { public static void Prueba (object o) { Clase1 a; Clase2 b;

Type t; if (o is Clase1) { Console.WriteLine ("\no es de Clase1"); a = (Clase1)o; Console.WriteLine ("--> {0}", typeof(Clase1).FullName); t = typeof(Clase1); } else if (o is Clase2) { Console.WriteLine ("\no es de Clase2"); b = o as Clase2; t = typeof(Clase2); } else { Console.WriteLine ("\no no es ni de Clase1 ni de Clase2."); t = o.GetType(); } Console.WriteLine ("Su tipo es " + t.FullName); } public static void Main() { Clase1 c1 = new Clase1(); Clase2 c2 = new Clase2(); Prueba (c1); Prueba (c2); Prueba ("Un string"); Console.ReadLine(); } } El resultado es:

o es de Clase1 --> Clase1 Su tipo es Clase1 o es de Clase2 Su tipo es Clase2 o no es ni de Clase1 ni de Clase2. Su tipo es System.String

Precedencia
Tambin podran presentarse por precedencia. En la tabla siguiente los enumeramos de mayor a menor precedencia: Categoras Parntesis: (x) Acceso a miembros: x.y Llamada a mtodos: f(x) Acceso con ndices: a[x] Post-incremento: x++ Primarios Post-decremento: x-Llamada a un constructor: new Consulta de tipo: typeof Control de desbordamiento activo: Operadores

checked
Control de desbordamiento inactivo:

unchecked
Valor positivo: + Valor negative: No: ! Unarios Complemento a nivel de bit: ~ Pre-incremento: ++x Post-decremento: --x Conversin de tipo -cast-: (T)x Multiplicacin: * Multiplicativos Divisin: / Resto: %

Suma: + Aditivos Resta: Desplazamiento de bits a la izquierda: << Desplazamiento Desplazamiento de bits a la derecha: >> Menor: < Mayor: > Menor o igual: <= Relacionales Mayor o igual: >= Igualdad o compatibilidad de tipo: is Conversin de tipo: as Igualdad Desigualdad Bitwise AND Bitwise XOR Bitwise OR Logical AND Logical OR Condicional ternario Asignacin

== != & ^ | && || ?: =, *=, /=, %=, +=, -=, <<=, >>=, &=, ^=, |=

Asociatividad
Como siempre, es mejor utilizar parntesis para controlar el orden de evaluacin

x = y = z x + y + z

se evala como se evala como

x = (y = z) (x + y) + z

Estructuras de control
Las estructuras de control de C# son similares a las de C y C++. La diferencia ms notable radica en que la instruccin condicional if y los ciclos while y do estn controlados por una expresin lgica (tipo Boolean). Esta restriccin hace que las instrucciones sean ms seguras al evitar posibles fuentes de error, o al menos, facilitan la legibilidad del cdigo. Por ejemplo, en la siguiente instruccin (vlida en C++):

if (a) la expresin a podra ser una expresin boolean pero tambin de tipo int, char, float *... y la condicin se evala como true cuando a es distinto de cero (valor entero 0, carcter 0 puntero nulo, en los ejemplos). En C# se clarifica esta ambigedad y slo se admiten expresiones lgicas. De esta manera, la instruccin anterior ser vlida slo cuando a sea una expresin boolean. A modo de resumen, las caractersticas propias de las estructuras de control de C# son: goto no puede saltar dentro de un bloque (da igual, de todas formas no lo usaremos NUNCA). switch funciona como en Pascal (no como en C). Se aade una nueva estructura de control iterativa: foreach.

Estructuras condicionales
if, if-else La estructura condicional tiene la sintaxis clsica, con la diferencia indicada anteriormente acerca del tipo de la expresin. Si debe ejecutar ms de una instruccin, se encierran en un bloque, delimitado por las llaves { y }.

Si slo se acta cuando la condicin es cierta:

if (a > 0) { Console.WriteLine ("Positivo"); Console.WriteLine ("Positivo"); ContPositivos++;

if (a > 0)

} Si se acta cuando la condicin es falsa se emplea la palabra reservada else:

if (a > 0) { Console.WriteLine ("Positivo"); Console.WriteLine ("Positivo"); else ContPositivos++; Console.WriteLine ("No Positivo"); Console.WriteLine ("No Positivo"); ContNoPositivos++;

if (a > 0)

} else {

} Puede escribirse una instruccin condicional dentro de otra instruccin condicional, lgicamente:

if (a > 0) { Console.WriteLine ("Positivo"); ContPositivos++; } else { if (a < 0) { Console.WriteLine ("Negativo"); ContNegativos++; } else { Console.WriteLine ("Cero"); ContCeros++; } } La concordancia entre if y else se establece de manera sencilla: cada else se asocia al ltimo if que no tenga asociado un bloque else.

switch La estructura de seleccin mltiple switch funciona sobre cualquier tipo predefinido (incluyendo string) o enumerado (enum) y debe indicar explcitamente cmo terminar cada caso (generalmente, con break en situaciones "normales" throw en situaciones "anormales", aunque es posible -pero no recomendable- emplear goto case return ):

using System; class HolaMundoSwitch { public static void Main(String[] args) { if (args.Length > 0) switch(args[0]) { case "Jos": Console.WriteLine("Hola Jos. Buenos das"); break; case "Paco": Console.WriteLine("Hola Paco. Me alegro de verte"); break; default: Console.WriteLine("Hola {0}", args[0]); break; } else Console.WriteLine("Hola Mundo"); } } Especificar los parmetros al programa en Visual Studio: Utilizar el explorador de soluciones para configurar las propiedades del proyecto.

Un ejemplo que usa un datos string para controlar la seleccin:

using System; namespace ConsoleApplication14 { class Class1 { static int Test(string label) { int res; switch(label) { case null: goto case "A"; // idem case "B" o case "A" case "B": case "C": res = 1; break; case "A": res = 2; break; default: res = 0; break; } return res; } static void Main(string[] args) { Console.WriteLine (Test("")); Console.WriteLine (Test("A")); Console.WriteLine (Test("B")); Console.WriteLine (Test("C")); Console.WriteLine (Test("?")); Console.ReadLine(); } } }

// // // // //

0 2 1 1 0

Estructuras repetitivas
Las estructuras repetitivas de C# (while, do...while, for) no presentan grandes diferencias respecto a las de otros lenguajes, como C++. La aportacin fundamental es la de la estructura iterativa en colecciones foreach. while

int i = 0; while (i < 5) { ... i++; } do...while

int i = 0; do { ... i++; } while (i < 5); for

int i; for (i=0; i < 5; i++) { ... }

foreach Un ciclo foreach itera seleccionando todos los miembros de un vector, matriz u otra coleccin sin que se requiera explicitar los ndices que permiten acceder a los miembros. El siguiente ejemplo muestra todos los argumentos recibidos por el programa cuando se invoca su ejecucin (argumentos en la lnea de rdenes):

public static void Main(string[] args) { foreach (string s in args) Console.WriteLine(s); } El vector (la coleccin) que se utiliza para iterar es args. Es un vector de datos string. El ciclo foreach realiza tantas iteraciones como cadenas contenga el vector args, y en cada iteracin toma una y la procesa a travs de la variable de control s. El ejemplo siguiente realiza la misma funcin:

public static void Main(string[] args) { for (int i=0; i < args.Lenght; i++) Console.WriteLine(args[i]); } El ciclo foreach proporciona acceso de slo lectura a los elementos de la coleccin sobre la que se itera. Por ejemplo, el cdigo de la izquierda no compilar, aunque el de la derecha s lo har (v es un vector de int):

foreach (int i in v) v.Length; i++) i *= 2;

for (int i=0; i < v[i] *= 2;

El ciclo foreach puede emplearse en vectores y colecciones. Una coleccin es una clase que implementa el interfaz IEnumerable. Sobre las colecciones dicutiremos ms adelante.

Saltos
goto Aunque el lenguaje lo permita, nunca escribiremos algo como lo que aparece a continuacin:

using System; namespace DemoGoto { class MainClass { static void Busca(int val, int[,] vector, out int fil, out int col) { int i, j; for (i = 0; i < vector.GetLength(0); i++) for (j = 0; j < vector.GetLength(1); j++) if (vector[i, j] == val) goto OK; throw new InvalidOperationException("Valor no encontrado"); OK: fil = i; col = j; } static void Main(string[] args) { int [,] coleccion = new int [2,3] {{1,0,4},{3,2,5}}; int f,c; int valor = Convert.ToInt32(args[0]); Busca (valor, coleccion, out f, out c); Console.WriteLine ("El valor {0} esta en [{1},{2}]", valor,f,c); Console.ReadLine(); } } } break Lo usaremos nicamente en sentencias switch. continue Mejor no lo usamos. return Procuraremos emplearlo slo al final de un mtodo para facilitar la legibilidad del cdigo.

Tipos de datos
Tipos bsicos El sistema unificado de tipos. El tipo Object Cadenas de caracteres Vectores y matrices Estructuras Enumeraciones Cuando definimos un objeto debemos especificar su tipo. El tipo determina qu valores puede almacenar ese objeto (clase y rango) y las operaciones que pueden efectuarse con l. Como cualquier lenguaje de programacin, C# proporciona una serie de tipos predefinidos (int, byte, char, string, ...) y mecanismos para que el usuario cree sus propios tipos (class y struct). La estructura de tipos de C# es una gran novedad ya que establece una relacin jerrquica entre stos, de manera que todos los tipos son clases y se construyen por herencia de la clase base Objet. Esta particularidad hace que la creacin y gestin de tipos de datos en C# sea una de las principales novedades y potencialidades del lenguaje.

Tipos bsicos
Los tipos de datos bsicos son los tipos de datos ms comnmente utilizados en programacin. Los tipos predefinidos en C# estn definidos en el espacio de nombres System, que es el espacio de nombres ms numeroso (e importante) de la plataforma .NET. Por ejemplo, para representar nmeros enteros de 32 bits con signo se utiliza el tipo de dato System.Int32 y a la hora de crear un objeto a de este tipo que represente el valor 2 se usa la siguiente sintaxis:

System.Int32 a = 2; Al ser un tipo valor no se utiliza el operador new para crear objetos System.Int32, sino que directamente se indica el literal que representa el valor a crear, con lo que la sintaxis necesaria para crear entero de este tipo se reduce considerablemente. Es ms, dado lo frecuente que es el uso de este tipo tambin se ha predefinido en C# el alias int para el mismo, por lo que la definicin de variable anterior queda as de compacta:

int a = 2;

System.Int32 no es el nico tipo de dato bsico incluido en C#. En el espacio de nombres System se han incluido los siguientes tipos:
C# sbyte byte short ushort int uint long ulong float double Tipo en System System.Sbyte System.Byte System.Short System.UShort System.Int32 System.UInt32 System.Int64 Caractersticas entero, 1 byte con signo entero, 1 byte sin signo entero, 2 bytes con signo entero, 2 bytes sin signo entero, 4 bytes con signo entero, 4 bytes sin signo entero, 8 bytes con signo U L UL F D M Smbolo

System.ULong64 entero, 8 bytes sin signo System.Single System.Double real, IEEE 754, 32 bits real, IEEE 754, 64 bits real, 128 bits (28 dgitos significativos) (Verdad/Falso) 1 byte Carcter Unicode, 2 bytes Cadenas de caracteres Unicode Cualquier objeto (ningn tipo concreto)

decimal System.Decimal bool char string object System.Boolean System.Char System.String System.Object

""

Los tipos estn definidos de manera muy precisa y no son dependientes del compilador o de la plataforma en la que se usan. La tabla anterior incorpora la columna Smbolo para indicar cmo debe interpretarse un literal. Por ejemplo, 28UL debe interpretarse como un entero largo sin signo (ulong). Todos los tipos enumerados son tipos valor, excepto string (que no debe confundirse con un vector de caracteres) y Object que son tipos referencia. Recuerde, no obstante, que los datos string se comportaban como un tipo valor ante la asgnacin.

El sistema unificado de tipos. El tipo Object


En C# desaparecen las variables y funciones globales: todo el cdigo y todos los datos de una aplicacin forman parte de objetos que encapsulan datos y cdigo (como ejemplo, recuerde cmo Main() es un mtodo de una clase). Como en otros lenguajes orientados a objetos, en C# un tipo puede incluir datos y mtodos. De hecho, hasta los tipos bsicos predefinidos incluyen mtodos, que, como veremos, heredan de la clase base object, a partir de la cual se construyen implcita o explcitamente todos los tipos de datos. Por ejemplo: int i = 10; string c = i.ToString(); e incluso: string c = 10.ToString(); Esta manera por la que podemos manipular los datos bsicos refleja la ntima relacin entre C# y la biblioteca de clase de .NET. De hecho, C# compila sus tipos bsicos asocindolos a sus correspondientes en .NET; por ejemplo, hace corresponder al tipo string) con la clase System.String, al tipo int) con la clase System.Int32, etc. As, se confirma que todo es un objeto en C# (por si an haba alguna duda). Aunque no comentaremos todos los mtodos disponibles para los tipos bsicos, destacaremos algunos de ellos. Todos los tipos tienen un mtodo ToString() que devuelve una cadena (string) que representa su valor textual. char tiene propiedades acerca de su contenido ( IsLetter, IsNumber, etc.) adems de mtodos para realizar conversiones (ToUpper(), ToLower(), etc.) El tipo bsico string dispone, como puede suponerse, de muchos mtodos especficos, an ms que la clase string de la biblioteca estndar de C++. Algunos mtodos estticos y propiedades particularmente interesantes son: Los tipos numricos enteros tipos tienen las propiedades MinValue y MaxValue (p.e. int.MaxValue). Los tipos float y double tienen la propiedad Epsilon, que indica el mnimo valor positivo que puede representarse en un dato de su tipo (p.e. float.Epsilon). Los tipos float y double tienen definidos los valores NaN (no es un nmero, no est definido), PositiveInfinite y NegativeInfinity, valores que son pueden ser devueltos al realizar ciertos clculos. Muchos tipos, incluyendo todos los tipos numricos, proporcionan el mtodo Parse() que permite la conversin desde un dato string (p.e. double d = double.Parse("20.5"))

Conversiones de tipos
Una conversin de tipo (casting) puede ser implcita o explcita. Implcitas Ocurren automticamente Siempre tienen xito No se pierde informacin int x = 123456; long y = x; short z = (short) x; float f1 = 40.0F; long l1 = (long) f1; redondeo) short s1 = (short) l1; desbordamiento) int i1 = s1; uint i2 = (uint) i1; por signo) Explcitas Requieren un casting Pueden fallar Se puede perder informacin

// implicita // explicita (riesgo) // explicita (riesgo por // explicita (riesgo por // implicita, no hay riesgo // explicita (riesgo de error

En C# las conversiones de tipo (tanto implcitas como explcitas) en las que intervengan tipos definidos por el usuario pueden definirse y particularizarse. Recordemos que pueden emplearse los operadores checked y unchecked para realizar conversiones (y operaciones aritmticas) en un contexto verificado: el entorno de ejecucin .NET detecta cualquier situacin de desbordamiento y lanza una excepcin OverFlowException si sta se manifiesta.

El tipo object
Por el sistema unificado de tipos de C#, todo es un objeto. C# tiene predefinido un tipo referencia llamado object y cualquier tipo (valor o referencia, predefinido o definido por el usuario) es en ltima instancia, de tipo object (con otras palabras puede decirse que hereda todas las caractersticas de ese tipo). El tipo object se basa en System.Object de .NET Framework. Las variables de tipo object pueden recibir valores de cualquier tipo. Todos los tipos de datos, predefinidos y definidos por el usuario, heredan de la clase System.Object. La clase Object es la superclase fundamental de todas las clases de .NET Framework; la raz de la jerarqua de tipos.

Normalmente, los lenguajes no precisan una clase para declarar la herencia de Object porque est implcita. Por ejemplo, dada la siguiente declaracin: object o; todas estas instrucciones son vlidas: o = 10; // int o = "Una cadena para el objeto"; // cadena o = 3.1415926; // double o = new int [24]; // vector de int o = false; // boolean Dado que todas las clases de .NET Framework se derivan de Object, todos los mtodos definidos en la clase Object estn disponibles en todos los objetos del sistema. Las clases derivadas pueden reemplazar, y de hecho reemplazan, algunos de estos mtodos, entre los que se incluyen los siguientes: public void Object() Inicializa una nueva instancia de la clase Object. Los constructores llaman a este constructor en clases derivadas, pero ste tambin puede utilizarse para crear una instancia de la clase Object directamente. public bool Equals(object obj) Determina si el objeto especificado es igual al objeto actual. protected void Finalize() Realiza operaciones de limpieza antes de que un objeto sea reclamado automticamente por el recolector de elementos no utilizados. public int GetHashCode() Sirve como funcin hash para un tipo concreto, apropiado para su utilizacin en algoritmos de hash y estructuras de datos como las tablas hash. public string ToString() Devuelve un objeto string que representa al objeto actual. Se emplea para crear una cadena de texto legible para el usuario que describe una instancia de la clase. La implementacin predeterminada devuelve el nombre completo del tipo del objeto Object. En el ltimo ejemplo, si cada vez que asignamos un valor a o ejecutamos Console.WriteLine (o.ToString()) o simplemente Console.WriteLine (o); obtendremos este resultado: 10 Una cadena para el objeto 3,1415926 System.Int32[] False

public Type GetType() Obtiene el objeto Type que representa el tipo exacto en tiempo de ejecucin de la instancia actual. En el ltimo ejemplo, si cada vez que asignamos un valor a o ejecutamos Console.WriteLine (o.getType()) obtendremos este resultado: System.Int32 System.String System.Double System.Int32[] System.Boolean protected object MemberwiseClone() Crea una copia superficial del objeto Object actual. No se puede reemplazar este mtodo; una clase derivada debe implementar la interfaz ICloneable si una copia superficial no es apropiada. MemberwiseClone() est protegido y, por tanto, slo es accesible a travs de esta clase o de una clase derivada. Una copia superficial crea una nueva instancia del mismo tipo que el objeto original y, despus, copia los campos no estticos del objeto original. Si el campo es un tipo de valor, se realiza una copia bit a bit del campo. Si el campo es un tipo de referencia, la referencia se copia, pero no se copia el objeto al que se hace referencia; por lo tanto, la referencia del objeto original y la referencia del punto del duplicado apuntan al mismo objeto. Por el contrario, una copia profunda de un objeto duplica todo aquello a lo que hacen referencia los campos del objeto directa o indirectamente.

Polimorfismo -boxing y unboxingBoxing (y su operacin inversa, unboxing) permiten tratar a los tipos valor como objetos (tipo referencia). Los tipos de valor, incluidos los struct y los predefinidos, como int, se pueden convertir al tipo object (boxing) y desde el tipo object (unboxing). La posibilidad de realizar boxing permite construir funciones polimrficas: pueden realizar una operacin sobre un objeto sin conocer su tipo concreto.

void Polim(object o) { Console.WriteLine(o.ToString()); } ... Polim(42); Polim("abcd"); Polim(12.345678901234M); Polim(new Point(23,45));

Boxing Boxing es una conversin implcita de un tipo valor al tipo object. Cuando se realiza boxing de un valor, se asigna una instancia de objeto y se copia el valor en el nuevo objeto. Por ejemplo, considere la siguiente declaracin de una variable de tipo de valor: int i = 123; La siguiente instruccin aplica implcitamente la operacin de boxing sobre la variable i: object o = i; El resultado de esta instruccin es crear un objeto o en la pila que hace referencia a un valor del tipo int alojado en el heap. Este valor es una copia del valor del tipo de valor asignado a la variable i. La diferencia entre las dos variables, i y o se muestra en la siguiente figura:

En definitiva, el efecto del boxing es el de cualquier otro tipo de casting pero: el contenido de la variable se copia al heap se crea una referencia a sta copia Aunque no es necesario, tambin es posible realizar el boxing explcitamente como en el siguiente ejemplo: int i = 123; object o = (object) i; El siguiente ejemplo convierte una variable entera i a un objeto o mediante boxing. A continuacin, el valor almacenado en la variable i se cambia de 123 a 456. El ejemplo muestra que el objeto mantiene la copia original del contenido, 123.

// Boxing de una variable int using System; class TestBoxing { public static void Main() { int i = 123; object o = i; // boxing implicito i = 456; // Modifica el valor de i Console.WriteLine("Valor (tipo valor) = {0}", i); Console.WriteLine("Valor (tipo object)= {0}", o); } } El resultado de su ejecucin es: Valor (tipo valor) = 456 Valor (tipo object)= 123 Unboxing Una vez que se ha hecho boxing sobre un dato y disponemos de un object no puede hacerse demasiado con l ya que no pueden emplearse mtodos o propiedades del tipo original: el compilador no puede conocer el tipo original sobre el que se hizo boxing . string s1 = "Hola"; object o = s1; // boxing ... if (o.Lenght > 0) // ERROR Una vez que se ha hecho boxing puede deshacerse la conversin ( unboxing) haciendo casting explcito al tipo de dato inicial. string s2 = (string) o; // unboxing

Afortunadamente es posible conocer el tipo, de manera que si la conversin no es posible se lanza una excepcin. Pueden utilizarse los operadores is y as para determinar la correccin de la conversin: string s2; if (o is string) s2 = (string) o; // unboxing o alternativamente: string s2 = o as string; // conversion if (s2 != null) // OK, la conversion funciono

Conclusiones
Ventajas del sistema unificado de tipos: las colecciones funcionan sobre cualquier tipo.

Hashtable t = new Hashtable(); t.Add(0, "zero"); t.Add(1, "one"); t.Add(2, "two"); string s = string.Format("Your total was {0} on {1}", total, date); Desventajas del sistema unificado de tipos: Eficiencia. La necesidad de utilizar boxing disminuir cuando el CLR permita genricos (algo similar a los templates en C++).

Cadenas de caracteres
Una cadena de caracteres no es ms que una secuencia de caracteres Unicode. En C# se representan mediante objetos del tipo string, que no es ms que un alias del tipo System.String incluido en la BCL. El tipo string es un tipo referencia. Representa una serie de caracteres inmutable. Se dice que una instancia de String es inmutable porque no se puede modificar su valor una vez creada. Los mtodos que aparentemente modifican una cadena devuelven en realidad una cadena nueva que contiene la modificacin. Recuerde la particularidad de este tipo sobre el operador de asignacin: su funcionamiento es como si fuera un tipo valor. Este es, realmente, el funcionamiento obtenido con el mtodo Copy(). Si no se desea obtener una copia (independiente) sino una copia en el sentido de un tipo valor emplee el mtodo Clone(). Puede accederse a cada uno de los caracteres de la cadena mediante ndice, como ocurre habitualmente en otros lenguajes, siendo la primera posicin asociada al ndice cero. Este acceso, no obstante, slo est permitido para lectura. string s = "!!Hola, mundo!!";; for (int i=0; i < s.Length; i++) Console.Write (s[i]); Este ejemplo muestra todos los caracteres que componen la cadena, uno a uno. El ciclo realiza tantas iteraciones como nmero de caracteres forman la cadena (su longitud) que se consulta usando la propiedad Length.

Por definicin, un objeto String, incluida la cadena vaca (""), es mayor que una referencia nula y dos referencias nulas son iguales entre s. El carcter null se define como el hexadecimal 0x00. Puede consultarse si una cadena es vaca empleando la propiedad esttica (slo lectura) Empty: el valor de este campo es la cadena de longitud cero o cadena vaca, "". Una cadena vaca no es igual que una cadena cuyo valor sea null. Los procedimientos de comparacin y de bsqueda distinguen maysculas de minsculas de forma predeterminada. Pueden emplearse los mtodos Compare() y Equals() para realizar referencias combinadas y comparaciones entre valores de instancias de Object y String. Los operadores relacionales == y != se implementan con el mtodo Equals(). El mtodo Concat() concatena una o ms instancias de String o las representaciones de tipo String de los valores de una o ms instancias de Object. El operador + est sobrecargado para realizar la concatenacin. Por ejemplo, el siguiente cdigo: string string string string string s1 s2 s3 s4 s5 = = = = = "Esto es una cadena... como en C++"; "Esto es una cadena... "; "como en C++"; s2 + s3; String.Concat(s2, s3);

Console.WriteLine ("s1 = {0}", s1); Console.WriteLine ("s4 = {0}", s4); Console.WriteLine ("s5 = {0}", s5); if ((s1 == s4) && (s1.Equals(s5))) Console.WriteLine ("s1 == s4 == s5"); produce este resultado: s1 = Esto es una cadena... como en C++ s4 = Esto es una cadena... como en C++ s5 = Esto es una cadena... como en C++ s1 == s4 == s5 Un mtodo particularmente til es Split(), que devuelve un vector de cadenas resultante de "partir" la cadena sobre la que se aplica en palabras: string linea; string [] palabras; ... palabras = linea.Split (null); // null indica dividir por espacios

En definitiva, la clase String incluye numerosos mtodos, que pueden emplease para: Realizar bsquedas en cadenas de caracteres:

IndexOf(),

LastIndexOf(), StartsWith(), EndsWith()


Eliminar e insertar espacios en blanco: Trim(), PadLeft(), PadRight() Manipular subcadenas: Insert(), Remove(), Replace(),

Substring(), Join()
Modificar cadenas de caracteres: ToLower(), ToUpper(), Format() (al estilo del printf de C, pero seguro).

Vectores y matrices
Un vector (matriz) en C# es radicalmente diferente a un vector (matriz) en C++: ms que una coleccin de variables que comparten un nombre y accesibles por ndice, en C# se trata de una instancia de la clase System.Array, y en consecuencia se trata de una coleccin que se almacena en el heap y que est bajo el control del gestor de memoria. Todas las tablas que definamos, sea cual sea el tipo de elementos que contengan, son objetos que derivan de System.Array. Ese espacio de nombres proporciona mtodos para la creacin, manipulacin, bsqueda y ordenacin de matrices, por lo tanto, sirve como clase base para todas las matrices de la CLR (Common Language Runtime). En C# las tablas pueden ser multidimensionales, se accede a los elementos por ndice, siendo el ndice inicial de cada dimensin 0. Siempre se comprueba que se est accediendo dentro de los lmites. Si se intenta acceder a un elemento de un vector (matriz) especificando un ndice fuera del rango, se detecta en tiempo de ejecucin y se lanza una excepcin IndexOutOfBoundsException. La sintaxis es ligeramente distinta a la del C++ porque las tablas son objetos de tipo referencia:

double [] array;

// Declara un a referencia // (no se instancia ningn

objeto) array = new double[10]; // Instancia un objeto de la clase // System.Array y le asigna 10 casillas. que combinadas resulta en (lo habitual):

double [] array = new double[10]; El tamao del vector se determina cuando se instancia, no es parte de la declaracin. string [] texto; // OK string [10] texto; // Error La declaracin emplea los parntesis vacos [ ] entre el tipo y el nombre para determinar el nmero de dimensiones (rango). string [] Mat1D; // 1 dimension string [,] Mat2D; // 2 dimensiones string [,,] Mat3D; // 3 dimensiones string [,,,] Mat4D; // 4 dimensiones ...... En C# el rango es parte del tipo (es obligatorio en la declaracin). El nmero de elementos no lo es (est asociado a la instancia concreta). Otros ejemplos de declaracin de vectores:

string[] a = new string[10]; // "a" es un vector de 10 cadenas int[] primes = new int[9]; // "primes" es un vector de 9 enteros Un vector puede inicializarse a la misma vez que se declara. Las tres definiciones siguientes son equivalentes:

int[] prime1 = new int[10] {1,2,3,5,7,11,13,17,19,23}; int[] prime2 = new int[] {1,2,3,5,7,11,13,17,19,23}; int[] prime3 = {1,2,3,5,7,11,13,17,19,23};

Los vectores pueden dimensionarse dinmicamente (en tiempo de ejecucin). Por ejemplo, el siguiente cdigo:

Console.Write ("Num. casillas: "); string strTam = Console.ReadLine(); int tam = Convert.ToInt32(strTam); int[] VecDin = new int[tam]; for (int i=0; i<tam; i++) VecDin[i]=i; Console.Write ("Contenido de VecDin = "); foreach (int i in VecDin) Console.Write(i + ", "); Console.ReadLine(); produce este resultado: Num. casillas: 6 Contenido de VecDin = 0, 1, 2, 3, 4, 5, Esta facilidad es una gran ventaja, aunque una vez que el constructor acta y se crea una instancia de la clase System.Array no es posible redimensionarlo. Si se desea una estructura de datos con esta funcionalidad debe emplearse una estructura coleccin disponible en System.Collections (por elemplo, System.Collections.ArrayList). En el siguiente ejemplo observe cmo el vector palabras se declara como un vector de string. Se instancia y se inicia despus de la ejecucin del mtodo Split(), que devuelve un vector de string.

string frase = "Esto es una prueba de particion"; string [] palabras; // vector de cadenas (no tiene tamao asignado) palabras = frase.Split (null); Console.WriteLine ("Frase = {0}", frase); Console.WriteLine ("Hay = {0} palabras", palabras.Length); for (int i=0; i < palabras.Length; i++) Console.WriteLine (" Palabra {0} = {1}", i, palabras[i]);

El resultado es: Frase = Esto es una prueba de particion Hay = 6 palabras Palabra 0 = Esto Palabra 1 = es Palabra 2 = una Palabra 3 = prueba Palabra 4 = de Palabra 5 = particion

Matrices
Las diferencias son importantes respecto a C++ ya que C# permite tanto matrices rectangulares (todas las filas tienen el mismo nmero de columnas) como matrices dentadas o a jirones.

int [,] array2D;

// Declara un a referencia // (no se instancia ningn

objeto) array2D = new int [2,3]; // Instancia un objeto de la clase // System.Array y le asigna 6 casillas // (2 filas con 3 columnas cada una) que combinadas (y con inicializacin) podra quedar:

int [,] array2D = new int [2,3] {{1,0,4}, {3,2,5}}; El nmero de dimensiones puede ser, lgicamente, mayor que dos:

int [,,] array3D = new int [2,3,2]; El acceso se realiza con el operador habitual [ ], aunque el recorrido se simplica y clarifica en C# con el ciclo foreach:

for (int i= 0; i< vector1.Length; i++) vector1[i] = vector2[i]; ... foreach (float valor in vector2) Console.Wtite (valor);

Una matriz dentada no es ms que una tabla cuyos elementos son a su vez tablas, pudindose as anidar cualquier nmero de tablas. Cada tabla puede tener un nmero propio de casillas. En el siguiente ejemplo se pide el nmero de filas, y para cada fila, el nmero de casillas de sa.

Console.WriteLine ("Introduzca las dimensiones: "); // Peticion de numero de filas (num. vectores) Console.Write ("Num. Filas: "); string strFils = Console.ReadLine(); int Fils = Convert.ToInt32(strFils); // Declaracion de la tabla dentada: el numero de // casillas de cada fila es deconocido. int[][] TablaDentada = new int[Fils][]; // Peticion del numero de columnas de cada vector for (int f=0; f<Fils; f++) { Console.Write ("Num. Cols. de fila {0}: ", f); string strCols = Console.ReadLine(); int Cols = Convert.ToInt32(strCols); // Peticion de memoria para cada fila TablaDentada[f] = new int[Cols]; } // Rellenar todas las casillas de la tabla dentada for (int f=0; f<TablaDentada.Length; f++) for (int c=0; c<TablaDentada[f].Length; c++) TablaDentada[f][c]=((f+1)*10)+(c+1); // Mostrar resultado Console.WriteLine ("Contenido de la matriz: "); for (int f=0; f<TablaDentada.Length; f++) { for (int c=0; c<TablaDentada[f].Length; c++) Console.Write (TablaDentada[f][c] + " "); Console.WriteLine(); } Console.ReadLine();

El resultado es: Introduzca las dimensiones: Num. Filas: 4 Num. Cols. de fila 0: 2 Num. Cols. de fila 1: 5 Num. Cols. de fila 2: 3 Num. Cols. de fila 3: 7 Contenido de la matriz: 11 12 21 22 23 24 25 31 32 33 41 42 43 44 45 46 47

Estructuras
Una estructura (struct) se emplea para definir nuevos tipos de datos. Los nuevos tipos as definidos son tipos valor (se almacenan en la pila). La sintaxis para la definicin de estructuras es similar a la empleada para las clases (se emplea la palabra reservada struct en lugar de class). No obstante, La herencia y aspectos relacionados (p.e. mtodos virtuales, mtodos abstractos) no se admiten en los struct. No se puede especificar (explcitamente) un constructor sin parmetros. Los datos struct tienen un constructor sin parmetros predefinido y no puede reemplazarse, slo se permiten constructores con parmetros. En este sentido son diferentes a los struct en C++. En el caso de especificar algn constructor con parmetros deberamos asegurarnos de iniciar todos los campos. En el siguiente ejemplo se proporciona un nico constructor con parmetros:

public struct Point { public int x, y; public Point(int x, int y) { this.x = x; this.y = y; } public string Valor() { return ("[" + this.x + "," + this.y + "]"); } }

Sobre esta declaracin de tipo Point, observe estas declaraciones de datos Point:

Point p = new Point(2,5); Point p2 = new Point(); Ambas crean una instancia de la clase Point en la pila y asigna los valores oportunos a los campos del struct con el constructor: En el primer caso acta el constructor suministrado en la implementacin del tipo. En el segundo acta el constructor por defecto, que inicia los campos numricos a cero, y los de tipo referencia a null. Puede comprobarse fcilmente: Console.WriteLine ("p = " + p.Valor()); Console.WriteLine ("p2 = " + p2.Valor()); produce como resultado: p = [2,5] p2 = [0,0] En cambio, la siguiente declaracin: Point p3; crea una instancia de la clase Point en la pila sin iniciar (cuidado: no inicia p3 a null, no es un tipo referencia). Por lo tanto, la instruccin: Console.WriteLine ("p3 = " + p3.Valor()); produce un error de compilacin, al intentar usar una variable no asignada. Observe las siguientes operaciones con struct. Su comportamiento es previsible:

p.x += 100; int px = p.x; p3.x = px; p2 = p; p2.x += 100; p3.y = p.y + p2.y;

// p.x==102 // p3.x==102 // p2.x==102, p2.y==5 // p2.x==202, p2.y==5 // p3.y==10

Console.WriteLine ("p = " + p.Valor()); Console.WriteLine ("p2 = " + p2.Valor()); Console.WriteLine ("p3 = " + p3.Valor()); el resultado es: p = [102,5] p2 = [202,5] p3 = [102,10]

Enumeraciones
Una enumeracin o tipo enumerado es un tipo especial de estructura en la que los literales de los valores que pueden tomar sus objetos se indican explcitamente al definirla. La sintaxis es muy parecida a la empleada en C++: enum State { Off, On }; aunque el punto y coma final es opcional ya que una enumeracin es una definicin de un struct y sta no requiere el punto y coma: enum State { Off, On } Al igual que en C++ y en C, C# numera a los elementos de la enumeracin con valores enteros sucesivos, asignando el valor 0 al primero de la enumeracin, a menos que se especifique explcitamente otra asignacin: enum Tamanio { Pequeo = 1, Mediano = 3, Grande = 5 Inmenso } En el ejemplo, el valor asociado a Inmenso es 6. Para acceder a los elementos de una enumeracin debe cualificarse completamente: Tamanio Ancho = Tamanio.Grande; lo que refleja que cada enumeracin es, en ltima instancia, un struct. Si no se declara ningn tipo subyacente de forma explcita, se utiliza Int32. En el siguiente ejemplo se especifica el tipo base byte: enum Color: byte { Red = 1, Green = 2, Blue = 4, Black = 0, White = Red | Green | Blue }

La clase Enum (System.Enum) proporciona la clase base para las enumeraciones. Proporciona mtodos que permiten comparar instancias de esta clase, convertir el valor de una instancia en su representacin de cadena, convertir la representacin de cadena de un nmero en una instancia de esta clase y crear una instancia de una enumeracin y valor especificados. Por ejemplo: Color c1 = Color.Black; Console.WriteLine((int) c1); Console.WriteLine(c1); Console.WriteLine(c1.ToString()); // 0 // Black // Black

Pueden convertirse explcitamente en su valor entero (como en C). Admiten determinados operadores aritmticos (+,-,++,--) y lgicos a nivel de bits (&,|,^,~). ejemplo: Color c2 = Color.White; Console.WriteLine((int) c2); // 7 Console.WriteLine(c2.ToString()); // White Otro ejemplo ms complejo: enum ModArchivo { Lectura = 1, Escritura = 2, Oculto = 4, Sistema = 8 } ... ModArchivo st = ModArchivo.Lectura | ModArchivo.Escritura; ... Console.WriteLine (st.ToString("F")); Lectura, Escritura Console.WriteLine (Enum.Format(typeof(ModArchivo), st, "G")); // 3 Console.WriteLine (Enum.Format(typeof(ModArchivo), st, "X")); 00000003 Console.WriteLine (Enum.Format(typeof(ModArchivo), st, "D")); // 3

// //

Clases (1)
Introduccin Modificadores de acceso Variables de instancia y miembros estticos Campos, constantes, campos de slo lectura y propiedades Mtodos Constructores y "destructores" Sobrecarga de operadores Conversiones de tipo Clases y structs

Introduccin
Un objeto es un agregado de datos y de mtodos que permiten manipular dichos datos, y un programa en C# no es ms que un conjunto de objetos que interaccionan unos con otros a travs de sus mtodos. Una clase es la definicin de las caractersticas concretas de un determinado tipo de objetos: cules son los datos y los mtodos de los que van a disponer todos los objetos de ese tipo. Se dice que los datos y los mtodos son los miembros de la clase. En C# hay muchos tipos de miembros, que podemos clasificar de manera muy genrica en las dos categoras antes mencionadas: datos y mtodos. Datos o campos o constantes y campos de slo lectura o propiedades Mtodos o mtodos generales o mtodos constructores o mtodos de sobrecarga de operadores e indexadores Un campo es un dato comn a todos los objetos de una clase. La declaracin de un dato se realiza, sintcticamente, como cualquier variable. Un mtodo es una funcin, un conjunto de instrucciones al que se le asocia un nombre. La palabra reservada this es una variable predefinida disponible dentro de las funciones no estticas de un tipo que se emplea, en un mtodo de una clase, para acceder a los miembros del objeto sobre el que se est ejecutando el mtodo en cuestin. En definitiva, permite acceder al objeto "activo". El siguiente ejemplo muestra de manera clara su aplicacin:

Una clase muy sencilla (uso de this) class Persona // Clase Persona { private string nombre; // campo privado public Persona (string nombre) // Constructor { this.nombre = nombre; // acceso al campo privado } public void Presenta (Persona p) // Metodo { if (p != this) // Una persona no puede presentarse a si misma Console.WriteLine("Hola, " + p.nombre + ", soy " + this.nombre); } } ... Persona yo = new Persona ("YO"); Persona tu = new Persona ("TU"); yo.Presenta(tu); tu.Presenta(yo); yo.Presenta(yo); // Sin efecto tu.Presenta(tu); // Sin efecto La ejecucin del cdigo anterior produce el siguiente resultado: Hola, TU, soy YO Hola, YO, soy TU En el siguiente ejemplo mostramos ms miembros de una clase: Una clase sencilla (CocheSimple)

public class CocheSimple { private int VelocMax; // Campo private string Marca; // Campo private string Modelo; // Campo // Mtodo constructor sin argumentos public CocheSimple () { this.VelocMax = 0; this.Marca = "Sin marca"; this.Modelo = "Sin modelo"; } // Mtodo constructor con argumentos public CocheSimple (string marca, string mod,

int velMax) { this.VelocMax = velMax; this.Marca = marca; this.Modelo = mod; } // Mtodo public void MuestraCoche () { Console.WriteLine (this.Marca + " " + this.Modelo + " (" + this.VelocMax + " Km/h)"); } } // class CocheSimple

recordemos que el operador new se emplea para crear objetos de una clase especificada. Cuando se ejecuta se llama a un mtodo especial llamado constructor y devuelve una referencia al objeto creado. Si no se especifica ningn constructor C# considera que existe un constructor por defecto sin parmetros. Una buena costumbre es proporcionar siempre algn constructor. Una aplicacin que usa la clase CocheSimple

class CocheSimpleApp { static void Main(string[] args) { // "MiCoche" y "TuCoche" son variables de tipo "CocheSimple" // que se inicializan llamando al constructor. CocheSimple MiCoche = new CocheSimple ("Citren", "Xsara", 220); CocheSimple TuCoche = new CocheSimple ("Opel", "Corsa", 190); Console.Write ("Mi coche: "); MiCoche.MuestraCoche(); // LLamada al mtodo "MuestraCoche()" Console.Write ("El tuyo: "); TuCoche.MuestraCoche(); // LLamada al mtodo "MuestraCoche()" Console.ReadLine (); } // Main } // class CocheSimpleApp

Modificadores de acceso
Los modificadores de acceso nos permiten especificar quin puede usar un tipo o un miembro del tipo, de forma que nos permiten gestionar la encapsulacin de los objetos en nuestras aplicaciones: Los tipos de nivel superior (aqullos que se encuentran directamente en un namespace) pueden ser public o internal Los miembros de una clase pueden ser public, private, protected, internal o protected internal Los miembros de un struct pueden ser public, private o internal Modificador de acceso Un miembro del tipo T definido en el assembly A es accesible... desde cualquier sitio slo desde dentro de T (por defecto) desde T y los tipos derivados de T desde los tipos incluidos en A desde T, los tipos derivados de T y los tipos incluidos en A

public private (por


defecto)

protected internal protected internal

Variables de instancia y miembros estticos


Por defecto, los miembros de una clase son variables de instancia: existe una copia de los datos por cada instancia de la clase y los mtodos se aplican sobre los datos de una instancia concreta. Se pueden definir miembros estticos que son comunes a todas las instancias de la clase. Lgicamente, los mtodos estticos no pueden acceder a variables de instancia, ni a la variable this que hace referencia al objeto actual.

using System; class Mensaje { public static string Bienvenida = "Hola!, Cmo est?"; public static string Despedida = "Adios!, Vuelva pronto!"; } class MiembrosStaticApp { static void Main() {

Console.WriteLine(Mensaje.Bienvenida); Console.WriteLine(" Bla, bla, bla ... "); Console.WriteLine(Mensaje.Despedida); Console.ReadLine(); } }

De cualquier forma, no conviene abusar de los miembros estticos, ya que son bsicamente datos y funciones globales en entornos orientados a objetos.

Campos, constantes, campos de slo lectura y propiedades


Campos
Un campo es una variable que almacena datos, bien en una una clase, bien en una estructura.

Constantes (const)
Una constante es un dato cuyo valor se evala en tiempo de compilacin y, por tanto, es implcitamente esttico (p.ej. Math.PI).

public class MiClase { public const string public const string public const int i3 public const double public const double ... }

version = "1.0.0"; s1 = "abc" + "def"; = 1 + 2; PI_I3 = i3 * Math.PI; s = Math.Sin(Math.PI);

//ERROR

Campos de slo lectura (readonly)


Similares a las constantes, si bien su valor se inicializa en tiempo de ejecucin (en su declaracin o en el constructor). A diferencia de las constantes, si cambiamos su valor no hay que recompilar los clientes de la clase. Adems, los campos de slo lectura pueden ser variables de instancia o variables estticas.

public class MiClase { public static readonly double d1 = Math.Sin(Math.PI); public readonly string s1; public MiClase(string s) { s1 = s; } } ...... MiClase mio = new MiClase ("Prueba"); Console.WriteLine(mio.s1); Console.WriteLine(MiClase.d1); ...... Produce como resultado:

Prueba 1,22460635382238E-16

Propiedades
Las propiedades son campos virtuales, al estilo de Delphi o C++Builder. Su aspecto es el de un campo (desde el exterior de la clase no se diferencian) pero estn implementadas con cdigo, como los mtodos. Pueden ser de slo lectura, de slo escritura o de lectura y escritura. Considere de nuevo la clase CocheSimple. Podemos aadir la propiedad MaxSpeed: // Propiedad public float MaxSpeed { get { return VelocMax / 1.6F; } set { VelocMax = (int) ((float) value * 1.6F);} } de manera que si las siguientes lneas se aaden al final del mtodo main en CocheSimpleApp: Console.WriteLine (); Console.WriteLine ("My car's Max Speed: " + MiCoche.MaxSpeed +" Mph" ); // get

Console.WriteLine ("Tunning my car..."); MiCoche.MaxSpeed = 200; // set Console.WriteLine ("After tunning my car (Incr. max Speed to 200 Mph"); Console.WriteLine ("My car's Max Speed: " + MiCoche.MaxSpeed + " Mph"); // get Console.WriteLine (); Console.Write ("Mi coche: "); MiCoche.MuestraCoche(); // LLamada al mtodo "MuestraCoche()" el resultado obtenido es: Mi coche: Citren Xsara (220 Km/h) El tuyo: Opel Corsa (190 Km/h) My car's Max Speed: 137,5 Mph Tunning my car... After tunning my car (Incr. max Speed to 200 Mph My car's Max Speed: 200 Mph Mi coche: Citren Xsara (320 Km/h)

Un ejemplo con campos, mtodos y propiedades

using System; class Coche { // Campos protected double velocidad=0; public string Marca; public string Modelo; public string Color; public string NumBastidor; // Mtodo constructor public Coche(string marca, string modelo, string color, string numbastidor) { this.Marca=marca; this.Modelo=modelo; this.Color=color; this.NumBastidor=numbastidor; } // Propiedad (solo lectura) public double Velocidad { get { return this.velocidad; } }

// Mtodo public void Acelerar(double c) { Console.WriteLine("--> Incrementando veloc. en {0} km/h", c); this.velocidad += c; } // Mtodo public void Girar(double c) { Console.WriteLine("--> Girando {0} grados", c); } // Mtodo public void Frenar(double c) { Console.WriteLine("--> Reduciendo veloc. en {0} km/h", c); this.velocidad -= c; } // Mtodo public void Aparcar() { Console.WriteLine("-->Aparcando coche"); this.velocidad = 0; } } // class Coche class EjemploCocheApp { static void Main(string[] args) { Coche MiCoche = new Coche("Citren", "Xsara Picasso", "Rojo","1546876"); Console.WriteLine("Los datos de mi coche son:"); Console.WriteLine(" Marca: {0}", MiCoche.Marca); Console.WriteLine(" Modelo: {0}", MiCoche.Modelo); Console.WriteLine(" Color: {0}", MiCoche.Color); Console.WriteLine(" Nmero de bastidor: {0}", MiCoche.NumBastidor); Console.WriteLine(); MiCoche.Acelerar(100); Console.WriteLine("La velocidad actual es de {0} km/h", MiCoche.Velocidad);

MiCoche.Frenar(75); Console.WriteLine("La velocidad actual es de {0} km/h", MiCoche.Velocidad); MiCoche.Girar(45); MiCoche.Aparcar(); Console.WriteLine("La velocidad actual es de {0} km/h", MiCoche.Velocidad); Console.ReadLine(); } } // class EjemploCocheApp

Mtodos
Implementan las operaciones que se pueden realizar con los objetos de un tipo concreto. Constructores, destructores y operadores son casos particulares de mtodos. Las propiedades y los indexadores se implementan con mtodos ( get y set). Como en cualquier lenguaje de programacin, los mtodos pueden tener parmetros, contener rdenes y devolver un valor (con return). Por defecto, los parmetros se pasan por valor (por lo que los tipos "valor" no podran modificarse en la llamada a un mtodo). El modificador ref permite que pasemos parmetros por referencia. Para evitar problemas de mantenimiento, el modificador ref hay que especificarlo tanto en la definicin del mtodo como en el cdigo que realiza la llamada. Adems, la variable que se pase por referencia ha de estar inicializada previamente.

void RefFunction (ref int p) { p++; } ...... int x = 10; RefFunction (ref x); // x vale ahora 11

El modificador out permite devolver valores a travs de los argumentos de un mtodo. De esta forma, se permite que el mtodo inicialice el valor de una variable. En cualquier caso, la variable ha de tener un valor antes de terminar la ejecucin del mtodo. Igual que antes, Para evitar problemas de mantenimiento, el modificador out hay que especificarlo tanto en la definicin del mtodo como en el cdigo que realiza la llamada.

void OutFunction(out int p) { p = 22; } ...... int x; // x an no est inicializada

OutFunction (out x); Console.WriteLine(x); // x vale ahora 22 Sobrecarga de mtodos: Como en otros lenguajes, el identificador de un mtodo puede estar sobrecargado siempre y cuando las signaturas de las distintas implementaciones del mtodo sean nicas (la signatura tiene en cuenta los argumentos, no el tipo de valor que devuelven).

void void void void

Print(int i); Print(string s); Print(char c); Print(float f); // Error: Signatura duplicada

int Print(float f);

Vectores de parmetros: Como en C, un mtodo puede tener un nmero variable de argumentos. La palabra clave params permite especificar un parmetro de mtodo que acepta un nmero variable de argumentos. No se permiten parmetros adicionales despus de la palabra clave params, ni varias palabras clave params en una misma declaracin de mtodo.

El siguiente cdigo emplea una funcin que suma todos los parmetros que recibe. El nmero de stos es indeterminado, aunque debe asegurarse que sean de tipo int: public static int Suma(params int[] intArr) { int sum = 0; foreach (int i in intArr) sum += i; return sum; } ...... int sum1 = Sum(13,87,34); // sum1 vale 134 Console.WriteLine(sum1); int sum2 = Sum(13,87,34,6); // sum2 vale 140 Console.WriteLine(sum2); produce el siguiente resultado: 134 140 El siguiente cdigo es algo ms complejo. public static void UseParams1(params int[] list) { for ( int i = 0 ; i < list.Length ; i++ ) Console.Write(list[i] + ", "); Console.WriteLine(); } public static void UseParams2(params object[] list) { for ( int i = 0 ; i < list.Length ; i++ ) Console.Write((object)list[i] + ", "); Console.WriteLine(); } ...... UseParams1(1, 2, 3); UseParams2(1, 'a', "test"); int[] myarray = new int[3] {10,11,12}; UseParams1(myarray); Observe su ejecucin: 1, 2, 3, 1, a, test, 10, 11, 12,

Constructores y "destructores"
Los constructores son mtodos especiales que son invocados cuando se instancia una clase (o un struct). Se emplean habitualmente para inicializar correctamente un objeto. Como cualquier otro mtodo, pueden sobrecargarse. Si una clase no define ningn constructor se crea un constructor sin parmetros (mplcito). No se permite un constructor sin parmetros para los struct. C# permite especificar cdigo para inicializar una clase mediante un constructor esttico. El constructor esttico se invoca una nica vez, antes de llamar al constructor de una instancia particular de la clase o a cualquier mtodo esttico de la clase. Slo puede haber un constructor esttico por tipo y ste no puede tener parmetros. Destructores: Se utilizan para liberar los recursos reservados por una instancia (justo antes de que el recolector de basura libere la memoria que ocupe la instancia). A diferencia de C++, la llamada al destructor no est garantizada por lo que tendremos que utilizar una orden using e implementar el interfaz IDisposable para asegurarnos de que se liberan los recursos asociados a un objeto). Slo las clases pueden tener destructores (no los struct).

class Foo { ~Foo() { Console.WriteLine("Destruido {0}", this); } }

Sobrecarga de operadores
Como en C++, se pueden sobrecargar (siempre con un mtodo static) algunos operadores unarios (+, -, !, ~, ++, --, true, false) y binarios (+, -, *, /, %, &, |, ^, ==, !=, <, >, <=, >=, <, >). No se puede sobrecargar el acceso a miembros, la invocacin de mtodos, el operador de asignacin ni los operadores sizeof, new, is, as, typeof, checked, unchecked, &, || y ?:. Los operadores && y || se evalan directamente a partir de los operadores & y |. La sobrecarga de un operador binario (v.g. *) sobrecarga implcitamente el operador de asignacin correspondiente (v.g. *=).

using System; class OverLoadApp { public class Point { int x, y; // Campos public Point() // Constructor sin parmetros { this.x = 0; this.y = 0; } public Point(int x, int y) // Constructor comn { this.x = x; this.y = y; } public int X // Propiedad { get { return x; } set { x = value; } } public int Y // Propiedad { get { return y; } set { y = value; } } // Operadores de igualdad public static bool operator == (Point p1, Point p2) { return ((p1.x == p2.x) && (p1.y == p2.y)); } public static bool operator != (Point p1, Point p2) { return (!(p1==p2)); } // Operadores aritmticos public static Point operator + (Point p1, Point p2) { return new Point(p1.x+p2.x, p1.y+p2.y); } public static Point operator - (Point p1, Point p2) { return new Point(p1.x-p2.x, p1.y-p2.y);

} } static void Main(string[] args) { Point p1 = new Point(10,20); Point p2 = new Point(); p2.X = p1.X; p2.Y = p1.Y; Point p3 = new Point(22,33); Console.WriteLine ("p1 es: ({0},{1})", p1.X, p1.Y); Console.WriteLine ("p2 es: ({0},{1})", p2.X, p2.Y); Console.WriteLine ("p3 es: ({0},{1})", p3.X, p3.Y); if (p1 == p2) Console.WriteLine ("p1 y p2 son iguales"); else Console.WriteLine ("p1 y p2 son diferentes"); if (p1 == p3) Console.WriteLine ("p1 y p3 son iguales"); else Console.WriteLine ("p1 y p3 son diferentes"); Console.WriteLine (); Point p4 = p1 + p3; Console.WriteLine ("p4 (p1+p3) es: ({0},{1})", p4.X, p4.Y); Point p5 = p1 - p1; Console.WriteLine ("p5 (p1-p1) es: ({0},{1})", p5.X, p5.Y); Console.WriteLine Console.WriteLine p1.Y); Console.WriteLine p2.Y); Console.WriteLine p3.Y); Console.WriteLine p4.Y); Console.WriteLine p5.Y); } } (); ("p1 es: ({0},{1})", p1.X, ("p2 es: ({0},{1})", p2.X, ("p3 es: ({0},{1})", p3.X, ("p4 es: ({0},{1})", p4.X, ("p5 es: ({0},{1})", p5.X,

Console.ReadLine ();

Para asegurar la compatibilidad con otros lenguajes de .NET:

// Operadores aritmticos public static Point operator + (Point p1, Point p2) { return SumaPoints (p1, p2); } public static Point operator - (Point p1, Point p2) { return RestaPoints (p1, p2); } public static Point RestaPoints (Point p1, Point p2) { return new Point(p1.x-p2.x, p1.y-p2.y); } public static Point SumaPoints (Point p1, Point p2) { return new Point(p1.x+p2.x, p1.y+p2.y); }

Y respecto a los operadores de comparacin:

// Operadores de igualdad public static bool operator == (Point p1, Point p2) { return (p1.Equals(p2)); } public static bool operator != (Point p1, Point p2) {

return (!p1.Equals(p2)); } public override bool Equals (object o) { // Por valor if ( (((Point) o).x == this.x) && (((Point) o).y == this.y) ) return true; else return false; } public override int GetHashCode() { return (this.ToString().GetHashCode()); }

El operador de asignacin (=) cuando se aplica a clases copia la referencia, no el contenido. Si se desea copiar instancias de clases lo habitual en C# es redefinir (overrride) el mtodo MemberwiseCopy() que heredan, por defecto, todas las clases en C# de System.Object.

Conversiones de tipo
Pueden programarse las conversiones de tipo, tanto explcitas como implcitas):

using System; class ConversionesApp { public class Euro { private int cantidad;

// Campo

public Euro (int v) // Constructor comn { cantidad = v;} public int valor // Propiedad { get { return cantidad; } } // Conversin implcita "double <-- Euro" public static implicit operator double (Euro x) { return ((double) x.cantidad); } // Conversin explcita "Euro <-- double" public static explicit operator Euro(double x) { double arriba = Math.Ceiling(x); double abajo = Math.Floor(x); int valor = ((x+0.5 >= arriba) ? (int)arriba : (int)abajo);

return new Euro(valor); } } // class Euro static void Main(string[] args) { double d1 = 442.578; double d2 = 123.22; Euro "Euro" Euro "Euro" e1 = (Euro) d1; // Conversin explcita a e2 = (Euro) d2; // Conversin explcita a

Console.WriteLine ("d1 es {0} y e1 es {1}", d1, e1.valor); Console.WriteLine ("d2 es {0} y e2 es {1}", d2, e2.valor); double n1 = e1; // Conversin implcita "double <--Euro" double n2 = e2; // Conversin implcita "double <--Euro" Console.WriteLine ("n1 es {0}", n1); Console.WriteLine ("n2 es {0}", n2); Console.ReadLine (); } }

C# no permite definir conversiones entre clases que se relacionan mediante herencia. Dichas conversiones estn ya disponibles: de manera implcita desde una clase derivada a una antecesora y de manera explcita a la inversa.

Clases y structs
Tanto las clases como los structs permiten al usuario definir sus propios tipos, pueden implementar mltiples interfaces y pueden contener datos (campos, constantes, eventos...), funciones (mtodos, propiedades, indexadores, operadores, constructores, destructores y eventos) y otros tipos internos (clases, structs, enums, interfaces y delegados). Observe las similitudes entre ambas en el siguiente ejemplo. struct SPoint - class CPoint using System; struct SPoint { private int x, y; // Campos public SPoint(int x, int y) // Constructor { this.x = x; this.y = y; } public int X // Propiedad { get { return x; } set { x = value; } } public int Y // Propiedad { get { return y; } set { y = value; } } } class CPoint { private int x, y; // Campos public CPoint(int x, int y) // Constructor { this.x = x; this.y = y; } public int X // Propiedad { get { return x; } set { x = value; } } public int Y // Propiedad { get { return y; } set { y = value; }

} } class Class2App { static void Main(string[] args) { SPoint sp = new SPoint(2,5); sp.X += 100; int spx = sp.X; // spx = 102 CPoint cp = new CPoint(2,5); cp.X += 100; int cpx = cp.X; // cpx = 102 Console.WriteLine ("spx es: {0} ", spx); // 102 Console.WriteLine ("cpx es: {0} ", cpx); // 102 Console.ReadLine (); } } Aunque las coincidencias son muchas, existen, no obstante, existen algunas diferencias entre ellos: Cuando se crea un objeto struct mediante el operador new, se crea y se llama al constructor apropiado. A diferencia de las clases, se pueden crear instancias de las estructuras sin utilizar el operador new. Si no se utiliza new, los campos permanecern sin asignar y el objeto no se podr utilizar hasta haber inicializado todos los campos. Clase Tipo referencia Tipo valor Para las estructuras no existe herencia: una estructura no puede heredar de otra estructura o clase, ni puede ser la base de una clase. Sin embargo, las estructuras heredan de la clase base Object. Una estructura puede implementar interfaces del mismo modo que las clases. No puede tener un constructor sin parmetros Se pueden crear instancias de las estructuras sin utilizar el operador new, pero los campos permanecern sin asignar y el objeto no se podr utilizar hasta haber iniciado todos los campos. No puede tener destructor Struct

Puede heredar de otro tipo (que no est "sellado")

Puede tener un constructor sin parmetros No pueden crearse instancias sin emplear el operador new. Puede tener un destructor

Herencia
Concepto de herencia Clases abstractas Clases selladas Tipos anidados

Concepto de herencia
El mecanismo de herencia es uno de los pilares fundamentales en los que se basa la programacin orientada a objetos. Es un mecanismo que permite definir nuevas clases a partir de otras ya definidas. Si en la definicin de una clase indicamos que sta deriva de otra, entonces la primera -a la que se le suele llamar clase hija o clase derivada- ser tratada por el compilador automticamente como si su definicin incluyese la definicin de la segunda -a la que se le suele llamar clase padre o clase base. Las clases que derivan de otras se definen usando la siguiente sintaxis:

class <claseHija> : <clasePadre> { <miembrosHija> }

A los miembros definidos en la clase hija se le aadirn los que hubisemos definido en la clase padre: la clase derivada "hereda" de la clase base. La palabra clave base se utiliza para obtener acceso a los miembros de la clase base desde una clase derivada. C# slo permite herencia simple.

Herencia de constructores
Los objetos de una clase derivada contarn con los mismos miembros que los objetos de la clase base y adems incorporarn nuevos campos y/o mtodos. El constructor de una clase derivada puede emplear el constructor de la clase base para inicializar los campos heredados de la clase padre con la construccin base. En realidad se trata de una llamada al constructor de la clase base con los parmetros adecuados.

: base(<parametrosBase>)

Si no se incluye el compilador considerara que vale :base(), lo que provocara un error si la clase base carece de constructor sin parmetros. Ejemplo de "herencia" de constructores public class B { private int h; // Campo public B () { // Constructor sin parmetros this.h = -1; } public B (int h) // Constructor con parmetro { this.h = h; } public int H // Propiedad { get { return h; } set { h = value; } } } // class B public class D : B // "D" hereda de "B" { private int i; // Campo public D () : this(-1) {} // Constructor sin parmetros public D (int i) { // Constructor con un parmetro this.i = i; } public D (int h, int i) : base(h) { // Constructor con this.i = i; // dos parmetros } public int I // Propiedad { get { return i; } set { i = value; } } } // class D ...... B varB1 = new B(); // Const. sin parmetros de B B varB2 = new B(5); // Const. con 1 parmetro de B Console.WriteLine("varB1 : (H={0})", varB1.H); Console.WriteLine("varB2 : (H={0})\n", varB2.H); D varD1 = new D(); D // Const. sin parmetros de

D varD2 = new D(15); // Const. con 1 parmetro de D D varD3 = new D(25, 11); // Const. con 2 parmetros de D Console.WriteLine("varD1 : (I={0},H={1})", varD1.I, varD1.H); Console.WriteLine("varD2 : (I={0},H={1})", varD2.I, varD2.H); Console.WriteLine("varD3 : (I={0},H={1})", varD3.I, varD3.H); Console.ReadLine(); ......

En el siguiente ejemplo se muestra cmo puede extenderse la clase CocheSimple vista anteriormente para construir, a partir de ella, la clase Taxi. Observar como se emplea la construccin base para referenciar a un constructor de la clase base y que cuando acta el constructor sin parmetros de la clase Taxi se llama implcitamente al constructor sin parmetros de la clase CocheSimple. Ejemplo: herencia sobre la clase CocheSimple

using System; namespace DemoHerencia { class CocheSimple { private int VelocMax; private string Marca; private string Modelo; public CocheSimple () { this.VelocMax = 0; this.Marca = "??"; this.Modelo = "??"; } public CocheSimple (string marca, string mod, int velMax)

{ this.VelocMax = velMax; this.Marca = marca; this.Modelo = mod; } public void MuestraCoche () { Console.WriteLine (this.Marca + " " + this.Modelo + " (" + this.VelocMax + " Km/h)"); } } // class CocheSimple class Taxi : CocheSimple { private string CodLicencia; public Taxi () {} public Taxi (string marca, string mod, int vel, string lic) : base (marca, mod, vel) { this.CodLicencia = lic; } public string Licencia { get { return this.CodLicencia; } } } // class Taxi class DemoHerenciaApp { static void Main(string[] args) { CocheSimple MiCoche = new CocheSimple ("Citren", "Xsara Picasso", 220); CocheSimple TuCoche = new CocheSimple ("Opel", "Corsa", 190); CocheSimple UnCoche = new CocheSimple (); Console.Write ("Mi coche: "); MiCoche.MuestraCoche(); Console.Write ("El tuyo: "); TuCoche.MuestraCoche(); Console.Write ("Un coche sin identificar: "); UnCoche.MuestraCoche(); Console.WriteLine(); Taxi ElTaxiDesconocido = new Taxi (); Console.Write ("Un taxi sin identificar: "); ElTaxiDesconocido.MuestraCoche(); Taxi NuevoTaxi= new Taxi ("Citren", "C5", 250,

"GR1234"); Console.Write ("Un taxi nuevo: "); NuevoTaxi.MuestraCoche(); Console.Write (" Licencia: {0}", NuevoTaxi.Licencia); Console.ReadLine (); } // Main } // class DemoHerenciaApp } // namespace DemoHerencia

Redefinicin de mtodos
Siempre que se redefine un mtodo que aparece en la clase base, hay que utilizar explcitamente la palabra reservada override y, de esta forma, se evitan redefiniciones accidentales (una fuente de errores en lenguajes como Java o C++). Sabemos que todos los objetos (incluidas las variables de los tipos predefinidos) derivan, en ltima instancia, de la clase Object. Esta clase proporciona el mtodo ToString que crea una cadena de texto legible para el usuario que describe una instancia de la clase. Si dejamos sin redefinir este mtodo y empleando la clase CocheSimple las siguientes instrucciones:

CocheSimple MiCoche = new CocheSimple ("Citren", "Xsara Picasso", 220); Console.WriteLine ("Mi coche: " + MiCoche.ToString());

producen el siguiente resultado: Mi coche: DemoHerencia.CocheSimple

lo que nos invita a redefinir el mtodo ToString en la clase CocheSimple:

class CocheSimple { ... public override string ToString() { return (this.Marca + " " + this.Modelo + " (" + this.VelocMax + " Km/h)"); } ... } Las dos instrucciones siguientes son equivalentes:

Console.WriteLine ("Mi coche: " + MiCoche.ToString()); Console.WriteLine ("Mi coche: " + MiCoche); por lo que podemos sutituir las instrucciones que muestran los datos de los objetos CocheSimple por:

Console.WriteLine ("Mi coche: " + MiCoche); Console.WriteLine ("El tuyo: " + TuCoche); Console.WriteLine ("Un coche sin identificar: " + UnCoche); y eliminamos el (innecesario) mtodo MuestraCoche, el resultado de la ejecucin del programa anterior es:

La palabra reservada base sirve para hacer referencia a los miembros de la clase base que quedan ocultos por otros miembros de la clase actual. Por ejemplo, podramos redefinir tambin el mtodo ToString de la clase Taxi empleando el mtodo redefinido ToString de la clase base CocheSencillo:

class CocheSimple { ... public override string ToString() { return (this.Marca + " " + this.Modelo + " (" + this.VelocMax + " Km/h)"); } ... } class Taxi : CocheSimple { ... public override string ToString() { return (base.ToString() + "\n Licencia: " + this.Licencia); } ... } ...... Taxi ElTaxiDesconocido = new Taxi (); Console.WriteLine ("Un taxi sin identificar: " + ElTaxiDesconocido); Taxi NuevoTaxi= new Taxi ("Citren", "C5", 250, "GR1234"); Console.WriteLine ("Un taxi nuevo: " + NuevoTaxi); ...... y el resultado es:

En la seccin dedicada a la sobrecarga de operadores introdujimos la clase Point. No haba ningn mtodo que mostrara los datos de inters de un objeto de tipo Point. Podemos sobreescribir el mtodo ToString de manera que fuera:

public class Point { ... public override string ToString() { return ("["+this.X+", "+this.Y+"]"); } ... } Ahora las instrucciones de escritura se convierten en llamadas a este mtodo, por ejemplo:

Console.WriteLine ("p1 es: " + p1); // Console.WriteLine ("p1 es: " + p1.ToString() El resultado de la ejecucin de ese programa ser:

Mtodos virtuales
Un mtodo es virtual si puede redefinirse en una clase derivada. Los mtodos son no virtuales por defecto. Los mtodos no virtuales no son polimrficos (no pueden reemplazarse) ni pueden ser abstractos. Los mtodos virtuales se definen en una clase base (empleando la palabra reservada virtual) y pueden ser reemplazados (empleando la palabra reservada override) en las subclases (stas proporcionan su propia -especfica- implementacin). Generalmente, contendrn una implementacin por defecto del mtodo (si no, se deberan utilizar mtodos abstractos).

class Shape // Clase base { // "Draw" es un mtodo virtual public virtual void Draw() { ... } } class Box : Shape { // Reemplaza al mtodo Draw de la clase base public override void Draw() { ... } } class Sphere : Shape { // Reemplaza al mtodo Draw de la clase base public override void Draw() { ... } } void HandleShape(Shape s) { ... s.Draw(); // Polimorfismo ... } HandleShape(new Box()); HandleShape(new Sphere()); HandleShape(new Shape()); NOTA: Propiedades, indexadores y eventos tambin pueden ser virtuales.

Clases abstractas
Una clase abstracta es una clase que no puede ser instanciada. Se declara empelando la palabra reservada abstract. Permiten incluir mtodos abstractos y mtodos no abstractos cuya implementacin hace que sirvan de clases base (herencia de implementacin). Como es lgico, no pueden estar "selladas".

Mtodos abstractos
Un mtodo abstracto es un mtodo sin implementacin que debe pertenecer a una clase abstracta. Lgicamente se trata de un mtodo virtual forzoso y su implementacin se realizar en una clase derivada.

abstract class Shape // Clase base abstracta { public abstract void Draw(); // Mtodo abstracto } class Box : Shape { public override void Draw() { ... } } class Sphere : Shape { public override void Draw() { ... } } void HandleShape(Shape s) { ... s.Draw(); ... } HandleShape(new Box()); HandleShape(new Sphere()); HandleShape(new Shape());

// Error !!!

Clases selladas
Una clase sellada (sealed), es una clase de la que no pueden derivarse otras clases (esto es, no puede utilizarse como clase base). Obviamente, no puede ser una clase abstracta. Los struct en C# son implcitamente clases selladas. Para qu sirve sellar clases? Para evitar que se puedan crear subclases y optimizar el cdigo (ya que las llamadas a las funciones de una clase sellada pueden resolverse en tiempo de compilacin).

Tipos anidados
C# permite declarar tipos anidados, esto es, tipos definidos en el mbito de otro tipo. El anidamiento nos permite que el tipo anidado pueda acceder a todos los miembros del tipo que lo engloba (independientemente de los modificadores de acceso) y que el tipo est oculto de cara al exterior (salvo que queramos que sea visible, en cuyo caso habr que especificar el nombre del tipo que lo engloba para poder acceder a l).

Clases (2)
Indexadores (indexers) Interfaces Delegados

Indexadores (indexers)
C# no permite, hablado con rigor, la sobrecarga del operador de acceso a tablas [ ]. Si permite, no obstante, definir lo que llama un indexador para una clase que permite la misma funcionalidad. Los indexadores permiten definir cdigo a ejecutar cada vez que se acceda a un objeto del tipo del que son miembros usando la sintaxis propia de las tablas, ya sea para leer o escribir. Esto es especialmente til para hacer ms clara la sintaxis de acceso a elementos de objetos que puedan contener colecciones de elementos, pues permite tratarlos como si fuesen tablas normales. A diferencia de las tablas, los ndices que se les pase entre corchetes no tiene porqu ser enteros, pudindose definir varios indexadores en un mismo tipo siempre y cuando cada uno tome un nmero o tipo de ndices diferente. La sintaxis empleada para la definicin de un indexador es similar a la de la definicin de una propiedad.

public class MiClase { ... public string this[int x] { get { // Obtener un elemento } set { // Fijar un elemento } } ... } ... MiClase MiObjeto = new MiClase(); El cdigo que aparece en el bloque get se ejecuta cuando la expresin MiObjeto[x] aparece en la parte derecha de una expresin mientras que el cdigo que aparece en el bloque set se ejecuta cuando MiObjeto[x] aparece en la parte izquierda de una expresin.

Algunas consideraciones finales: Igual que las propiedades, pueden ser de slo lectura, de slo escritura o de lectura y escritura. El nombre dado a un indexador siempre ha de ser this. Lo que diferenciar a unos indexadores de otros ser el nmero y tipo de sus ndices. Ejemplo de indexador

using System; namespace IndexadorCoches { public class Coche { private int VelocMax; private string Marca; private string Modelo; public Coche (string marca, string modelo, int velocMax) { this.VelocMax = velocMax; this.Marca = marca; this.Modelo = modelo; } public override string ToString () { return (this.Marca + " " + this.Modelo + " (" + this.VelocMax + " Km/h)"); } } // class Coche public struct DataColeccionCoches { public int numCoches; public int maxCoches; public Coche[] vectorCoches; public DataColeccionCoches (int max) { this.numCoches = 0; this.maxCoches = max; this.vectorCoches = new Coche[max]; } } // struct DataColeccionCoches public class Coches { // Los campos se encapsulan en un struct

DataColeccionCoches data; // Constructor sin parmetros public Coches() { this.data = new DataColeccionCoches(10); // Si no se pone un valor se llamar al // constructor sin parmetros (por defecto) del // struct y tendremos problemas: no se puede // explicitar el constructor sin parmetros // para un struct. } // Constructor con parmetro public Coches(int max) { this.data = new DataColeccionCoches(max); } public int MaxCoches { set { data.maxCoches = value; } get { return (data.maxCoches); } } public int NumCoches { set { data.numCoches = value; } get { return (data.numCoches); } } public override string ToString () { string str1 = " --> Maximo= " + this.MaxCoches; string str2 = " --> Real = " + this.NumCoches; return (str1 + "\n" + str2); } // El indexador devuelve un objeto Coche de acuerdo // a un ndice numrico public Coche this[int pos] { // Devuelve un objeto del vector de coches get { if(pos < 0 || pos >= MaxCoches) throw new IndexOutOfRangeException("Fuera de rango"); else return (data.vectorCoches[pos]); } // Escribe en el vector set { this.data.vectorCoches[pos] = value;} }

} // class Coches class IndexadorCochesApp { static void Main(string[] args) { // Crear una coleccin de coches Coches MisCoches = new Coches (); // Por defecto (10) Console.WriteLine ("***** Mis Coches *****"); Console.WriteLine ("Inicialmente: "); Console.WriteLine (MisCoches); // Aadir coches. Observar el acceso con [] ("set") MisCoches[0] = new Coche ("Opel", "Zafira", 200); MisCoches[1] = new Coche ("Citren", "Xsara", 220); MisCoches[2] = new Coche ("Ford", "Focus", 190); MisCoches.NumCoches = 3; Console.WriteLine ("Despues de insertar 3 coches: "); Console.WriteLine (MisCoches); // Mostrar la coleccin Console.WriteLine (); for (int i=0; i<MisCoches.NumCoches; i++) { Console.Write ("Coche Num.{0}: ", i+1); Console.WriteLine (MisCoches[i]); // Acceso ("get") } Console.ReadLine (); // ********************************************* // Crear una coleccin de coches Coches TusCoches = new Coches (4); Console.WriteLine ("***** Tus Coches *****"); Console.WriteLine ("Inicialmente: "); Console.WriteLine (TusCoches); // Aadir coches. Observar el acceso con [] TusCoches[TusCoches.NumCoches++] = new Coche ("Opel", "Corsa", 130); TusCoches[TusCoches.NumCoches++] =

new Coche ("Citren", "C3", 140); Console.WriteLine ("Despues de insertar 2 coches: "); Console.WriteLine (TusCoches); // Mostrar la coleccin Console.WriteLine (); for (int i=0; i<TusCoches.NumCoches; i++) { Console.Write ("Coche Num.{0}: ", i+1); Console.WriteLine (TusCoches[i]); // Acceso ("get") } Console.ReadLine (); } // Main } // class IndexadorCochesApp } // namespace IndexadorCoches

Interfaces
Un interfaz define un contrato semntico que ha de respetar cualquier clase (o struct) que implemente el interfaz. La interfaz no contiene implementacin alguna. La clase o struct que implementa el interfaz es la que tiene la funcionalidad especificada por el interfaz. Una interfaz puede verse como una forma especial de definir clases que slo cuenten con miembros abstractos. Sin embargo, todo tipo que derive de una interfaz ha de dar una implementacin de todos los miembros que hereda de esta, y no como ocurre con las clases abstractas donde es posible no darla si se define como abstracta tambin la clase hija. La especificacin del interfaz puede incluir mtodos, propiedades, indexadores y eventos, pero no campos, operadores, constructores o destructores. Aunque solo se permite la herencia simple de clases, como ocurre en Java, se permite y herencia mltiple de interfaces. Esto significa que es posible definir tipos que deriven de ms de una interfaz. Los interfaces (como algo separado de la implementacin) permiten la existencia del polimorfismo, al poder existir muchas clases o structs que implementen el interfaz. Ejemplo de polimorfismo

using System; namespace Interface1 { class Interface1App { // Definicin de una interface public interface IDemo { void MetodoDeIDemo (); } // "Clase1" y "Clase2" implementan la interface public class Clase1 : IDemo { public void MetodoDeIDemo() { Console.WriteLine ("Mtodo de Clase1"); } } public class Clase2 : IDemo

{ public void MetodoDeIDemo() { Console.WriteLine ("Mtodo de Clase2"); } } static void Main(string[] args) { Clase1 c1 = new Clase1(); Clase2 c2 = new Clase2(); IDemo demo; // objeto de una interface // Ejemplo de polimorfismo demo = c1; demo.MetodoDeIDemo(); demo = c2; demo.MetodoDeIDemo(); Console.ReadLine(); } } }

Otro ejemplo: Ejemplo de interface "heredada"

using System; class InterfaceApp { interface IPresentable { void Presentar(); } class Triangulo : IPresentable {

private double b, a; public Triangulo(double Base, double altura) { this.b=Base; this.a=altura; } public double Base { get { return b; } } public double Altura { get { return a; } } public double Area { get { return (Base*Altura/2); } } public void Presentar() { Console.WriteLine("Base del tringulo: {0}", Base); Console.WriteLine("Altura del tringulo: {0}", Altura); Console.WriteLine("rea del tringulo: {0}", Area); } } class Persona : IPresentable { private string nbre, apell, dir; public Persona (string nombre, string apellidos, string direccion) { this.nbre = nombre; this.apell = apellidos; this.dir = direccion; } public string Nombre { get { return nbre; } } public string Apellidos { get { return apell; } } public string Direccion { get { return dir; } } public void Presentar() { Console.WriteLine("Nombre: {0}", Nombre); Console.WriteLine("Apellidos: {0}", Apellidos); Console.WriteLine("Direccin: {0}", Direccion); }

} static void VerDatos(IPresentable IP) { IP.Presentar(); } static void Main(string[] args) { Triangulo t=new Triangulo(10,5); Persona p=new Persona ("Paco", "Prez", "su casa"); Console.WriteLine("Ya se han creado los objetos"); Console.WriteLine("\nINTRO para VerDatos(triangulo)"); Console.ReadLine(); VerDatos(t); Console.WriteLine("\nINTRO para VerDatos(proveedor)"); Console.ReadLine(); VerDatos(p); Console.ReadLine(); } }

El principal uso de las interfaces es indicar que una clase implementa ciertas caractersticas. Por ejemplo, el ciclo foreach trabaja internamente comprobando que la clase sobre la que se aplica implementa el interfaz IEnumerable y llamando a los mtodos definidos en esa interfaz.

La sobrecarga de los operadores relacionales puede hacerse implementando la interface IComparable, concretamente el mtodo CompareTo. La interface IComparable define un mtodo de comparacin generalizado, implementado por un tipo de valor o clase para crear un mtodo de comparacin especfico del tipo. Esta interfaz la implementan tipos cuyos valores se pueden ordenar, como por ejemplo, las clases numricas o de tipo cadena (las clases enum y string implementan esta interface). Un tipo de valor o clase implementa el mtodo CompareTo para crear un mtodo de comparacin especfico. El siguiente ejemplo anteriormente. ampla la especificacin de la clase

Point introducida

public class Point : IComparable { ...... // Operadores relacionales public int CompareTo(object o) { Point tmp = (Point) o; if (this.x > tmp.x) return 1; else if (this.x < tmp.x) return -1; else return 0; } public static bool operator < (Point p1, Point p2) { IComparable ic1 = (IComparable) p1; return (ic1.CompareTo(p2) < 0); } public static bool operator > (Point p1, Point p2) { IComparable ic1 = (IComparable) p1; return (ic1.CompareTo(p2) > 0); } public static bool operator <= (Point p1, Point p2) { IComparable ic1 = (IComparable) p1; return (ic1.CompareTo(p2) <= 0); } public static bool operator >= (Point p1, Point p2) { IComparable ic1 = (IComparable) p1; return (ic1.CompareTo(p2) >= 0); } ...... } // class Point

static void Main(string[] args) { ...... if (p1 > p2) Console.WriteLine ("p1 > p2"); if (p1 >= p2) Console.WriteLine ("p1 >= p2"); if (p1 < p2) Console.WriteLine ("p1 < p2"); if (p1 <= p2) Console.WriteLine ("p1 <= p2"); if (p1 == p2) Console.WriteLine ("p1 == p2"); Console.WriteLine (); if (p1 > p3) Console.WriteLine ("p1 > p3"); if (p1 >= p3) Console.WriteLine ("p1 >= p3"); if (p1 < p3) Console.WriteLine ("p1 < p3"); if (p1 <= p3) Console.WriteLine ("p1 <= p3"); if (p1 == p3) Console.WriteLine ("p1 == p3"); Console.WriteLine (); } }

Herencia mltiple
La plataforma .NET no permite herencia mltiple de implementacin, aunque s se puede conseguir herencia mltiple de interfaces. Clases, structs e interfaces pueden heredar de mltiples interfaces (como en Java). Herencia de interfaces

using System; namespace Interface2 { class Interface2App { // Definicin de interfaces public interface IDemo1 { void Metodo1DeInterface1 (); string Metodo2DeInterface1 (); } public interface IDemo2 { void Metodo1DeInterface2 (); } public interface IDemo3 : IDemo1 { void Metodo1DeInterface3 (string mensaje); }

// "Clase1" implementan la interface "IDemo1" public class Clase1 : IDemo1 { public void Metodo1DeInterface1() { Console.WriteLine ("Mt1 de Int1 en Clase1"); } public string Metodo2DeInterface1() { return ("En Mt2 de Int1 en Clase1"); } } // "Clase1" implementan las interfaces // "IDemo1" e "IDemo2" public class Clase2 : IDemo1, IDemo2 { public void Metodo1DeInterface1() { Console.WriteLine ("Mt1 de Int1 en Clase2"); } public string Metodo2DeInterface1() { return ("En Mt2 de Int1 en Clase2"); } public void Metodo1DeInterface2() { Console.WriteLine ("Mt1 de Int2 en Clase2"); } } // "Clase3" implementan la interface "IDemo3", la // cual ha heredado de "IDemo1" public class Clase3 : IDemo3 { public void Metodo1DeInterface1() { Console.WriteLine ("Mt1 de Int1 en Clase3"); } public string Metodo2DeInterface1() { return ("En Mt2 de Int1 en Clase3"); } public void Metodo1DeInterface3 (string m) { Console.WriteLine (m + "Mt1 de Int3 en Clase3"); } } static void { Clase1 c1 Clase2 c2 Clase3 c3 Main(string[] args) = new Clase1(); = new Clase2(); = new Clase3();

IDemo1 i1; IDemo2 i2; IDemo3 i3;

c1.Metodo1DeInterface1(); Console.WriteLine(c1.Metodo2DeInterface1()); Console.WriteLine(); i1 = c3; Console.WriteLine("Cuando i1 = c3 "); i1.Metodo1DeInterface1(); Console.WriteLine(i1.Metodo2DeInterface1()); Console.WriteLine(); i3 = c3; Console.WriteLine("Cuando i3 = c3 "); i3.Metodo1DeInterface1(); Console.WriteLine(i3.Metodo2DeInterface1()); i3.Metodo1DeInterface3("Aplicado a i3: "); Console.WriteLine(); i1 = c2; Console.WriteLine("Cuando i1 = c2 "); i1.Metodo1DeInterface1(); Console.WriteLine(i1.Metodo2DeInterface1()); i2 = c2; Console.WriteLine("Ahora i2 = c2 "); i2.Metodo1DeInterface2(); Console.WriteLine(); Console.ReadLine(); } } }

Resolucin de conflictos de nombres Si dos interfaces tienen un mtodo con el mismo nombre, se especifica explcitamente el interfaz al que corresponde la llamada al mtodo para eliminar ambigedades

interface IControl { void Delete(); } interface IListBox: IControl { void Delete(); } interface IComboBox: ITextBox, IListBox { void IControl.Delete(); void IListBox.Delete(); }

Delegados
Un delegado es un tipo especial de clase que define la signatura y el tipo de retorno de un mtodo. Un delegado crea un nuevo tipo al que debe amoldarse un mtodo para que pueda ser asignado al delegado. Su funcin es similar a la de los punteros a funciones en lenguajes como C y C++ (C# no soporta punteros a funciones) slo que los delegados son a prueba de tipos, orientados a objetos y seguros. Los delegados pueden pasarse a mtodos y pueden usarse para llamar a los mtodos de los que contienen referencias. Los delegados proporcionan polimorfismo para las llamadas a funciones: Un "tipo" delegado

using System; class Delegate1App { // Declaracin del "tipo delegado" llamado "Del": // funciones que devuelven un double y reciben un double. delegate double Del (double x); static double incremento (double x) { return (++x); }

static void Main(string[] args) { // Instanciacin Del del1 = new Del (Math.Sin); Del del2 = new Del (Math.Cos); Del del3 = new Del (incremento); // Llamadas Console.WriteLine (del1(0)); // 0 Console.WriteLine (del2(0)); // 1 Console.WriteLine (del3(10)); // 11 Console.ReadLine(); } }

En el ejemplo anterior Sin, Cos e incremento son mtodos con la misma signatura (todos reciben un nico argumento de tipo double) y devuelven un valor de tipo double. Los objetos del1, del2 y del3 son objetos delegados del tipo Del (Sin, Cos e incremento delegan en stos) que invocan a los mtodos a los que representan. Para hacer ms evidente el polimorfismo el mtodo Main podra escribirse como:

static void Main(string[] args) { Del f1 = new Del (Math.Sin); Del f2 = new Del (Math.Cos); Del f3 = new Del (incremento); Del f = f = f = } f; f1; Console.WriteLine (f(0)); // 0 f2; Console.WriteLine (f(0)); // 1 f3; Console.WriteLine (f(10)); // 11

En este caso f referencia, por turno, a los mtodos representados por f1, f2 y f3.

Finalmente, observe cmo el polimorfismo es ms evidente en el siguiente cdigo:

static void Main(string[] args) { Del[] d = new Del[3]; d[0] = new Del (Math.Sin); d[1] = new Del (Math.Cos); d[2] = new Del (incremento); for (int i=0; i<3; i++) Console.WriteLine (d[i](0)); } // 0, 1, 1

Los delegados son muy tiles ya que permiten disponer de objetos cuyos mtodos puedan ser modificados dinmicamente durante la ejecucin de un programa. En general, son tiles en todos aquellos casos en que interese pasar mtodos como parmetros de otros mtodos. Observe el siguiente ejemplo:

using System; using System.IO; class DelegateTest { public delegate void Print (string s); public static void EnConsola (string str) { Console.WriteLine(str); } public static void EnFichero (string str) { StreamWriter fo = new StreamWriter ("Resultado.txt"); fo.WriteLine(str); fo.Close(); } public static void Display (Print Metodo) { Metodo ("Este es el mensaje que se muestra"); }

static void Main() { Print dc = new Print (EnConsola); Print df = new Print (EnFichero); Display (dc); Display (df); Console.ReadLine(); } }

En el ejemplo anterior EnFichero y EnConsola son mtodos que reciben un argumento string y no devuelven nada. El tipo delegado Print representa a mtodos que reciben un argumento string y no devuelven nada, por lo que los mtodos EnFichero y EnConsola pueden delegar en objetos de tipo Print. En este ejemplo, no obstante, el mtodo Display recibe un argumento del tipo delegado Print, esto es, recibe la referencia a un mtodo que recibe un argumento string y no devuelve nada (EnFichero y EnConsola son buenos candidatos). Las instrucciones: Print dc = new Print (EnConsola); Print df = new Print (EnFichero); hacen que los mtodos EnConsola y EnFichero deleguen en los objetos dc y df, respectivamente. As, podemos usarlos como argumentos al mtodo Display sin problemas: Display (dc); Display (df);

Delegados multimiembro (Multicasting)


Un delegado multimiembro es un delegado individual compuesto de ms delegados. En definitiva: permite almacenar referencias a uno o ms mtodos, de tal manera que a travs del objeto delegado sea posible solicitar la ejecucin en cadena de todos ellos. Los delegados multimiembro tienen dos restricciones: No pueden devolver ningn valor (deben ser void). No pueden tener parmetros de salida (out). Se aaden delegados a un delegado multimiembro con el operador + y se eliminan delegados de un delegado multimiembro con el operador -. Cada delegado tiene una lista ordenada de mtodos que se invocan secuencialmente (en el mismo orden en el que fueron aadidos al delegado).

"Multicasting"

using System; class Delegate2App { delegate void SomeEvent (void); static void Func1(void) { Console.WriteLine(" Desde Func1"); } static void Func2(void) { Console.WriteLine(" Desde Func2"); } static void Main(string[] args) { SomeEvent func = new SomeEvent(Func1); func += new SomeEvent(Func2); Console.WriteLine("Llamada a func"); func(); // Se llama tanto a Func1 como a Func2 func -= new SomeEvent(Func1); Console.WriteLine("Llamada a func"); func(); // Slo se llama a Func2 Console.ReadLine(); } }

El siguiente ejemplo ilustra de una manera ms compleja la adicin y eliminacin de delegados a un delegado multimiembro. Observe como interviene un objeto de la clase EjemploDelegado que encapsula dos delegados.

"Multicasting" (2)

using System; class Multicasting { public delegate void Calculo (int n1, int n2, ref int res); public class EjemploDelegado { public Calculo Calculo1; public Calculo Calculo2; public void Suma (int n1, int n2, ref int res) { res = n1 + n2; Console.WriteLine("Suma ({0},{1}) = {2}", n1, n2, res); } public void Resta (int n1, int n2, ref int res) { res = n1 - n2; Console.WriteLine("Suma ({0},{1}) = {2}", n1, n2, res); } } static void Main(string[] args) { int resultado = 0; EjemploDelegado Del = new EjemploDelegado (); Del.Calculo1 = new Calculo (Del.Suma); Del.Calculo2 = new Calculo (Del.Resta); Del.Calculo1 (5, 9, ref resultado); Del.Calculo2 (5, 9, ref resultado); Console.WriteLine(); Calculo MultiCalc = Del.Calculo1; MultiCalc += Del.Calculo2; MultiCalc (5, 9, ref resultado); Console.WriteLine(); MultiCalc -= Del.Calculo1; MultiCalc (5, 9, ref resultado); Console.ReadLine(); } }

Delegados vs. interfaces


Siempre se pueden utilizar interfaces en vez de delegados. Los interfaces son ms verstiles, ya que pueden encapsular varios mtodos y permiten herencia, si bien los delegados resultan ms adecuados para implementar manejadores de eventos. Con los delegados se escribe menos cdigo y se pueden implementar fcilmente mltiples manejadores de eventos en una nica clase.

Eventos
Los delegados son la base sobre la que se monta la gestin de eventos en la plataforma .NET. Muchas aplicaciones actuales se programan en funcin de eventos. Cuando se produce algn hecho de inters para nuestra aplicacin, ste se notifica mediante la generacin de un evento, el cual ser procesado por el manejador de eventos correspondiente (modelo "publish-subscribe"). Los eventos nos permiten enlazar cdigo personalizado a componentes creados previamente (mecanismo de "callback"). El callback consiste en que un cliente notifica a un servidor que desea ser informado cuando alguna accin tenga lugar. C# usa los eventos de la misma manera que Visual Basic usa los mensajes. Las aplicaciones en Windows se programan utilizando eventos, pues los eventos resultan especialmente indicados para la implementacin de interfaces interactivos. Cuando el usuario hace algo (pulsar una tecla, hacer click con el ratn, seleccionar un dato de una lista...), el programa reacciona en funcin de la accin del usuario. El uso de eventos, no obstante, no est limitado a la implementacin de interfaces. Tambin son tiles en el desarrollo de aplicaciones que deban realizar operaciones peridicamente o realizar operaciones de forma asncrona (p.ej. llegada de un correo electrnico, terminacin de una operacin larga...).

El lenguaje C# da soporte a los eventos mediante el uso de delegados. Al escribir nuestra aplicacin, un evento no ser ms que un campo que almacena un delegado. Los usuarios de la clase podrn registrar delegados (mediante los operadores += y =), pero no podrn invocar directamente al delegado. Eventos en C# public delegate void EventHandler ( object sender, EventArgs e); public class Button { public event EventHandler Click; protected void OnClick (EventArgs e) { // This is called when button is clicked if (Click != null) Click(this, e); } } public class MyForm: Form { Button okButton; static void OkClicked(object sender, EventArgs e) { ShowMessage("You pressed the OK button"); } public MyForm() { okButton = new Button(...); okButton.Caption = "OK"; okButton.Click += new EventHandler(OkClicked); } }

Aspectos avanzados de C#
Excepciones Reflexin Atributos

Excepciones
Las excepciones ofrecen varias ventajas respecto a otros mtodos de notificacin de error, como los cdigos devueltos (rdenes return) ya que ningn error pasa desapercibido (las excepciones no pueden ser ignoradas) y no tienen por qu tratarse en el punto en que se producen. Los valores no vlidos no se siguen propagando por el sistema. No es necesario comprobar los cdigos devueltos. Es muy sencillo agregar cdigo de control de excepciones para aumentar la confiabilidad del programa. Una excepcin es cualquier situacin de error o comportamiento inesperado que encuentra un programa en ejecucin. Las excepciones se pueden producir a causa de un error en el cdigo o en cdigo al que se llama (como una biblioteca compartida), que no estn disponibles recursos del sistema operativo, condiciones inesperadas que encuentra Common Language Runtime (por ejemplo, cdigo que no se puede comprobar), etc. La aplicacin se puede recuperar de algunas de estas condiciones, pero de otras no. Las excepciones pueden generarse en un proceso o hebra de nuestra aplicacin (con la sentencia throw) o pueden provenir del entorno de ejecucin de la plataforma .NET. En .NET Framework, una excepcin es un objeto derivado de la clase Exception. El mecanismo de control de excepciones en C# es muy parecido al de C++ y Java: la excepcin se inicia en un rea del cdigo en que se produce un problema. La excepcin asciende por la pila hasta que la aplicacin la controla o el programa se detiene. El proceso es el siguiente: La sentencia throw lanza una excepcin (una instancia de una clase derivada de System.Exception, que contiene informacin sobre la excepcin: Message, StackTrace, HelpLink, InnerException...). El bloque try delimita cdigo que podra generar una excepcin. El bloque catch indica cmo se manejan las excepciones. Se puede relanzar la excepcin capturada o crear una nueva si fuese necesario. Se pueden especificar distintos bloques catch para capturar distintos tipos de excepciones. En ese caso, es recomendable poner primero los ms especficos (para asegurarnos de que capturamos la excepcin concreta).

El bloque finally incluye cdigo que siempre se ejecutar (se produzca o no una excepcin). Ejemplo 1 El siguiente ejemplo pone de manifiesto el flujo de ejecucin que sigue un programa que lanza y procesa una excepcin.

try { Console.WriteLine("try"); throw new Exception("Mi excepcion"); } catch { Console.WriteLine("catch"); } finally { Console.WriteLine("finally"); } La ejecucin de este programa produce el siguiente resultado: try catch finally Ejemplo 2 Se puede profundizar en el tratamiento de la excepcin, por ejemplo, comprobando alguna propiedad del objeto Exception generado. La clase Exception es la clase base de la que derivan las excepciones. La mayora de los objetos de excepcin son instancias de alguna clase derivada de Exception, pero se puede iniciar cualquier objeto derivado de la clase Object como excepcin. En casi todos los casos, es recomendable iniciar y detectar slo objetos Exception. La clase Exception tiene varias propiedades que facilitan la comprensin de una excepcin. Entre stas destacamos la propiedad Message. Esta propiedad proporciona informacin sobre la causa de una excepcin. Veamos cmo se utiliza:

try { Console.WriteLine("try"); throw new Exception("Mi excepcion"); } catch (Exception e) { Console.WriteLine("catch"); Console.WriteLine("Excepcin detectada: " + e.Message); } catch { Console.WriteLine("catch"); } finally { Console.WriteLine("finally"); } La ejecucin de este programa produce el siguiente resultado:

try catch Excepcin detectada: Mi excepcion finally La mayora de las clases derivadas de la clase Exception no implementan miembros adicionales ni proporcionan ms funcionalidad, simplemente heredan de Exception. Por ello, la informacin ms importante sobre una excepcin se encuentra en la jerarqua de excepciones, el nombre de la excepcin y la informacin que contiene la excepcin.

Ejemplo 3 El siguiente ejemplo muestra cmo el uso de execpciones puede controlar un nmero importante de situaciones de error.

static void Main(string[] args) { int numerador = 10; Console.WriteLine ("Numerador es = {0}", numerador); Console.Write ("Denominador = "); string strDen = Console.ReadLine(); int denominador, cociente; try { Console.WriteLine("--> try"); denominador = Convert.ToInt16(strDen); cociente = numerador / denominador; Console.WriteLine ("Cociente = {0}", cociente); } catch (ArithmeticException e) { Console.WriteLine("--> catch"); Console.WriteLine("Excep. aritmtica"); Console.WriteLine("ArithmeticException Handler: {0}", e.ToString()); } catch (ArgumentNullException e) { Console.WriteLine("--> catch"); Console.WriteLine("Excep. de argumento nulo"); Console.WriteLine("ArgumentNullException Handler: {0}", e.ToString()); } catch (Exception e) { Console.WriteLine("--> catch"); Console.WriteLine("generic Handler: {0}", e.ToString()); } finally { Console.WriteLine("--> finally"); } Console.ReadLine(); } }

Cuando todo funciona sin problemas:

Cuando se intenta dividir por cero:

Cuando se produce desbordamiento:

Cuando se produce otro problema (cadena vaca, por ejemplo):

Ejemplo 4 Hemos visto que pueden conocerse los detalles de la excepcin que se haya producido. Podemos conocer ms detalles usando la propiedad StackTrace. Esta propiedad contiene un seguimiento de pila que se puede utilizar para determinar dnde se ha producido un error. El seguimiento de pila contiene el nombre del archivo de cdigo fuente y el nmero de la lnea del programa si est disponible la informacin de depuracin.

using System; using System.Diagnostics; class ExcepApp { static void Main(string[] args) { int numerador = 10; Console.WriteLine ("Numerador es = {0}", numerador); Console.Write ("Denominador = "); string strDen = Console.ReadLine(); int denominador, cociente; try { Console.WriteLine("--> try"); denominador = Convert.ToInt16(strDen); cociente = numerador / denominador; Console.WriteLine ("Cociente = {0}", cociente); } catch (Exception e) { Console.WriteLine("--> catch");

Console.WriteLine("Generic Handler: {0}", e.ToString()); Console.WriteLine(); StackTrace st = new StackTrace(e, true); Console.WriteLine("Traza de la pila:"); for (int i = 0; i < st.FrameCount; i++) { StackFrame sf = st.GetFrame(i); Console.WriteLine(" Pila de llamadas, Mtodo: {0}", sf.GetMethod() ); Console.WriteLine(" Pila de llamadas, Lnea : {0}", sf.GetFileLineNumber()); } Console.WriteLine(); } finally { Console.WriteLine("--> finally"); } Console.ReadLine(); } }

Reflexin
La capacidad de reflexin de la plataforma .NET (similar a la de la plataforma Java) nos permite explorar informacin sobre los tipos de los objetos en tiempo de ejecucin. La instruccin GetType() obtiene el objeto Type de la instancia actual sobre el que se aplica. El valor devuelto es representa el tipo exacto, en tiempo de ejecucin, de la instancia actual.

Un sencillo ejemplo con Type y GetType() public class Test { private int n; public Test (int n) { this.n = n; } } // Acceso a informacin acerca de una clase public static void Main(string[] args) { Type tipoClase = typeof(Test); Console.WriteLine("El nombre del tipo de tipoClase es: {0}", tipoClase.Name); Test t = new Test(0); Type tipoVariable = t.GetType(); Console.WriteLine("El tipo de la variable t es: {0}", tipoVariable.Name); } El programa anterior muestra como resultado:

El nombre del tipo de tipoClase es: Test El tipo de la variable t es: Test Otro ejemplo del uso de Type y GetType(): Un ejemplo ms complejo con Type y GetType()

Using System; public class ClaseBase : Object {} public class ClaseDerivada : ClaseBase {} public class Test { public static void Main() { ClaseBase ibase = new ClaseBase(); ClaseDerivada iderivada = new ClaseDerivada();

Console.WriteLine("ibase: Type is {0}", ibase.GetType()); Console.WriteLine("iderivada: Type is {0}", iderivada.GetType()); object o = iderivada; ClaseBase b = iderivada; Console.WriteLine("object o = iderivada: Type is {0}", o.GetType()); Console.WriteLine("ibase {0}", b.GetType()); Console.ReadLine(); } } b = iderivada: Type is

La reflexin puede emplearse para examinar los mtodos, propiedades, ... de una clase:

Mtodos de una clase

using System; using System.Reflection; class ReflectApp { public class Test { private int n; public Test (int n) {

this.n = n; } public void Metodo1DeTest (int n) { // ..... } public int Metodo2DeTest (int a, float b, string c) { // ..... return 0; } } public static void Main(string[] args) { Type t = typeof(Test); MethodInfo[] MetInf = t.GetMethods(); foreach (MethodInfo m in MetInf) { Console.WriteLine (); Console.WriteLine ("Mtodo: " + m.Name ); Console.WriteLine (" Caractersticas: " + ((m.IsPublic) ? " (public)" : "") + ((m.IsVirtual) ? " (virtual)" : "")); // Parmetros ParameterInfo[] ParInf = m.GetParameters(); if (ParInf.Length > 0) { Console.WriteLine (" Parmetros: " ); foreach (ParameterInfo p in ParInf) Console.WriteLine(" " + p.ParameterType + " " + p.Name); } } Console.ReadLine (); } }

Atributos
Un atributo es informacin que se puede aadir a los metadatos de un mdulo de cdigo. Los atributos nos permiten "decorar" un elemento de nuestro cdigo con informacin adicional. C# es un lenguaje imperativo, pero, como todos los lenguajes de esta categora, contiene algunos elementos declarativos. Por ejemplo, la accesibilidad de un mtodo de una clase se especifica mediante su declaracin como public, protected, private o internal. C# generaliza esta capacidad permitiendo a los programadores inventar nuevas formas de informacin declarativa, anexarlas a distintas entidades del programa y recuperarlas en tiempo de ejecucin. Los programas especifican esta informacin declarativa adicional mediante la definicin y el uso de atributos. Esta informacin puede ser referente tanto al propio mdulo o el ensamblado al que peretenezca, como a los tipos de datos definidos en l, sus miembros, los parmetros de sus mtodos, los bloques set y get de sus propiedades e indexadores o los bloques add y remove de sus eventos. Se pueden emplear en ensamblados, mdulos, tipos, miembros, valores de retorno y parmetros.

Atributos predefinidos
Si bien el programador puede definir cuantos atributos considere necesarios, algunos atributos ya estn predefinidos en la plataforma .NET. Atributo Descripcin Propiedades y eventos que deben mostrarse en el inspector de objetos. Clases y estructuras que pueden "serializarse"

Browsable

Serializable (esto es, volcarse en algn dispositivo de


salida, p.ej. disco), como en Java. El compilador se quejar si alguien los utiliza (deprecated en Java). COM Prog ID Caractersticas transaccionales de una clase.

Obsolete ProgId Transaction

Observar como al marcar como obsoleta la clase A se genera un error al compilar el mdulo ya que se emplea en la lnea comentada.

... [Obsolete("Clase A desfasada. Usar B en su lugar")] class A { public void F() {} } class B { public void F() {} } class SimpleAtrPredefApp { static void Main(string[] args) { A a = new A(); a.F(); ... } } // Avisos

Declarar una clase atributo


Declarar un atributo en C# es simple: se utiliza la forma de una declaracin de clase que hereda de System.Attribute y que se ha marcado con el atributo AttributeUsage, como se indica a continuacin:

// La clase HelpAttribute posee un parmetro posicional (url) // de tipo string y un parmetro con nombre -opcional(Topic) // de tipo string. [AttributeUsage(AttributeTargets.All)] public class HelpAttribute: Attribute { public string Topic = null; private string url; public HelpAttribute(string url) { this.url = url; } public string Url { get { return url; } } public override string ToString() { string s1 = " Url = " + this.Url; string dev = (this.Topic != null) ? (s1 + " - Topic: " + this.Topic) : s1; return (dev); } } // class HelpAttribute

El atributo AttributeUsage especifica los elementos del lenguaje a los que se puede aplicar el atributo. Las clases de atributos son clases pblicas derivadas de System.Attribute que disponen al menos de un constructor pblico. Las clases de atributos tienen dos tipos de parmetros: o Parmetros posicionales, que se deben especificar cada vez que se utiliza el atributo. Los parmetros posicionales se especifican como argumentos de constructor para la clase de atributo. En el ejemplo anterior, url es un parmetro posicional. o Parmetros con nombre, los cuales son opcionales. Si se especifican al usar el atributo, debe utilizarse el nombre del parmetro. Los parmetros con nombre se definen mediante un campo o una propiedad no estticos. En el ejemplo anterior, Topic es un parmetro con nombre. Los parmetros de un atributo slo pueden ser valores constantes de los siguientes tipos: o Tipos simples (bool, byte, char, short, int, long, float y double) o string o System.Type o enumeraciones o object (El argumento para un parmetro de atributo del tipo object debe ser un valor constante de uno de los tipos anteriores.) o Matrices unidimensionales de cualquiera de los tipos anteriores Parmetros para el atributo AttributeUsage El atributo AttributeUsage proporciona el mecanismo subyacente mediante el cual los atributos se declaran.

AttributeUsage tiene un parmetro posicional:


AllowOn, que especifica los elementos de programa a los que se puede asignar el atributo (clase, mtodo, propiedad, parmetro, etc.). Los valores aceptados para este parmetro se pueden encontrar en la enumeracin System.Attributes.AttributeTargets de .NET Framework. El valor predeterminado para este parmetro es el de todos los elementos del programa (AttributeElements.All).

AttributeUsage tiene un parmetro con nombre:


AllowMultiple, valor booleano que indica si se pueden especificar varios atributos para un elemento de programa. El valor predeterminado para este parmetro es False.

Utilizar una clase atributo


A continuacin, se muestra un breve ejemplo de uso del atributo declarado en la seccin anterior:

[Help("http://decsai.ugr.es/Clase1.htm")] class Clase1 { /* Bla, bla, bla... */ } [Help("http://decsai.ugr.es/Clase2.htm", Topic="Atributos")] class Clase2 { /* Bla, bla, bla... */ }

En este ejemplo, el atributo HelpAttribute est asociado con las clases Clase1 y Clase2. Nota: Por convencin, todos los nombres de atributo finalizan con la palabra "Attribute" para distinguirlos de otros elementos de .NET Framework. No obstante, no tiene que especificar el sufijo de atributo cuando utiliza atributos en el cdigo (vase el ejemplo).

Acceder a los atributos por reflexin


Los atributos de un tipo o de un miembro de un tipo pueden ser examinados en tiempo de ejecucin (reflexin), heredan de la clase System.Attribute y sus argumentos se comprueban en tiempo de compilacin. Los principales mtodos de reflexin para consultar atributos se encuentran en la clase System.Reflection.MemberInfo. El mtodo clave es GetCustomAttributes, que devuelve un vector de objetos que son equivalentes, en tiempo de ejecucin, alos atributos del cdigo fuente.

Ejemplo 1 El siguiente ejemplo muestra la manera bsica de utilizar la reflexin para obtener acceso a los atributos:

class AtributosSimpleApp { static void Main(string[] args) { MemberInfo info1 = typeof(Clase1); object[] attributes1 = info1.GetCustomAttributes(true); for (int i = 0; i < attributes1.Length; i ++) { System.Console.WriteLine(attributes1[i]); } MemberInfo info2 = typeof(Clase2); object[] attributes2 = info2.GetCustomAttributes(true); for (int i = 0; i < attributes2.Length; i ++) { System.Console.WriteLine(attributes2[i]); } Console.ReadLine(); } // Main () } // class AtributosSimpleApp

Ejemplo 2 Este ejemplo ampla el anterior aadiendo muchas ms posibilidades. Atributos y reflexin

using System; using System.Diagnostics; using System.Reflection; // La clase IsTested es una clase de atributo // definida por el usuario. // Puede aplicarse a cualquier definicin, incluyendo: // - tipos (struct, class, enum, delegate) // - miembros (mtodos, campos, events, properties, indexers) // Se usa sin argumentos. [AttributeUsage(AttributeTargets.All)] public class IsTestedAttribute : Attribute { public override string ToString() { return (" REVISADO"); } } // La clase HelpAttribute posee un parmetro posicional (url) // de tipo string y un parmetro con nombre -opcional(Topic) // de tipo string. [AttributeUsage(AttributeTargets.All)] public class HelpAttribute: Attribute { public string Topic = null; private string url; public HelpAttribute(string url) { this.url = url; } public string Url { get { return url; } } public override string ToString() { string s1 = " Url = " + this.Url; string dev = (this.Topic != null) ? (s1 + ". Topic = " + this.Topic) : s1; return (dev); } } // La clase CodeReviewAttribute es una clase de atributo

// definida por el usuario. // Puede aplicarse en clases y structs nicamente. // Toma dos argumentos de tipo string (el nombre del // revisor y la fecha de revisin) adems de permitir otro // argumento opcional (Comment) de tipo string. [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)] public class CodeReviewAttribute: System.Attribute { public CodeReviewAttribute(string reviewer, string date) { this.reviewer = reviewer; this.date = date; this.comment = ""; } public string Comment { get { return(comment); } set { comment = value; } } public string Date { get { return(date); } } public string Reviewer { get { return(reviewer); } } public override string ToString() { string st1 = " Revisor : " + Reviewer + "\n"; string st2 = " Fecha: " + Date; string st3; if (Comment.Length != 0) st3 = "\n" + " NOTAS: " + Comment; else st3 = ""; return (st1 + st2 + st3); } string reviewer; string date; string comment; } [CodeReview("Pepe", "01-12-2002", Comment="Codigo mejorable")] [Help("http://decsai.ugr.es/Clase1.htm")] [IsTested] class Clase1 { int c1c1; int c2c1;

public Clase1 (int n1, int n2) { this.c1c1 = n1; this.c2c1 = n2; } [IsTested] public override string ToString() { return (this.c1c1.ToString() + this.c2c1.ToString()); } } [CodeReview("Juani", "12-11-2002", Comment="Excelente")] [Help("http://decsai.ugr.es/Clase3.htm", Topic="Atributos")] class Clase2 { string c1c2; public Clase2 (string s) { this.c1c2 = s; } [IsTested] public char Met1Clase2 () { return (this.c1c2[0]); } } [CodeReview("Pepe", "12-11-2002"), IsTested()] class Clase3 { int c1c3; [IsTested] public Clase3 (int n1) { this.c1c3 = n1; } } class Atributos1App { private static bool IsMemberTested (MemberInfo member) { foreach (object attr in member.GetCustomAttributes(true)) if (attr is IsTestedAttribute) return true; return false; }

private static string InfoRevision (MemberInfo member) { if (IsMemberTested(member)) return ("REVISADO"); else return ("NO REVISADO"); } private static void DumpAttributes(MemberInfo member) { Console.WriteLine(); Console.WriteLine("Informacin de: " + member.Name); /* object[] arr = member.GetCustomAttributes(typeof(HelpAttribute), true); if (arr.Length == 0) Console.WriteLine("Esta clase no tiene ayuda."); else { HelpAttribute ha = (HelpAttribute) arr[0]; Console.WriteLine (ha.ToString()); } */ foreach (object attribute in member.GetCustomAttributes(true)) { if (attribute is HelpAttribute) Console.WriteLine (" Atributos de ayuda:"); if (attribute is CodeReviewAttribute) Console.WriteLine (" Atributos de Revisin:"); if (attribute is IsTestedAttribute) Console.WriteLine (" Atributos de Actualizacin:"); Console.WriteLine(attribute); } } static void Main(string[] args) { // t es un vector de tipos Type [] t = new Type[3]; t[0] = typeof(Clase1); t[1] = typeof(Clase2); t[2] = typeof(Clase3); for (int i=0; i<3; i++) { DumpAttributes(t[i]);

Console.WriteLine (" mtodos:"); {

Informacin de los

foreach (MethodInfo m in (t[i]).GetMethods()) if (IsMemberTested(m)) { Console.WriteLine(" REVISADO", m.Name); } else { Console.WriteLine(" REVISADO", m.Name); } } } Console.ReadLine(); } } Mtodo {0}

Mtodo {0} NO

Programas en C#
Organizacin lgica de los tipos Organizacin fsica de los tipos Ejecucin de aplicaciones Cdigo no seguro Preprocesador Documentacin El compilador de C# se ocupa de abstraer al programador de la localizacin (ficheros) de los tipos y otros elementos. Es irrelevante el hecho de colocar el cdigo en un nico fichero o de disponerlo en varios, as como de usar una clase antes de declararla: el compilador se encargar de encontrar la localizacin de los elementos. La consecuencia es que no existe, propiamente, el concepto de enlace (linking): el compilador compila el cdigo a un ensamblado (o simplemente a un mdulo).

Organizacin lgica de los tipos


Del mismo modo que los ficheros se organizan en directorios, los tipos de datos se organizan en espacios de nombres (del ingls, namespaces). Los espacios de nombres son mecanismos para controlar la visibilidad (mbito) de los nombres empleados e un programa. Su propsito es el de facilitar la combinacin de los componentes de un programa (que pueden provenir de varias fuentes) minimizando los conflictos entre identificadores. Por un lado estos espacios permiten tener ms organizados los tipos de datos, lo que facilita su localizacin. As es como est organizada la BCL: todas las clases ms comnmente usadas en cualquier aplicacin pertenecen al espacio de nombres llamado System, las de acceso a bases de datos en System.Data, las de realizacin de operaciones de entrada/salida en System.IO, etc. Por otro lado, los espacios de nombres tambin permiten poder usar en un mismo programa clases homnimas, siempre que pertenezcan a espacios de nombres diferentes y queden perfectamente cualificadas. En definitiva: Los espacios de nombres proporcionan una forma unvoca de identificar un tipo. Eliminan cualquier tipo de ambigedad en los nombres de los smbolos empleados en un programa.

El siguiente ejemplo trabaja con un espacio de nombres que incluye la declaracin de un clase:

namespace Graficos2D { public class Point { ... } } Los componentes del espacio de nombres no son visibles directamente desde fuera del espacio en el que estn inmersos (al igual que los componentes de un struct, de una enumeracin, de una clase, ...) a no ser que se cualifiquen completamente: namespace Graficos2D { public class Point { ... } } class MainClass { Graficos2D.Point p; static void Main(string[] args) { ... } } La declaracin Point p; producira un error de compilacin. En el siguiente ejemplo se trabaja con dos espacios de nombres que incluyen una clase homnima (Point) en cada uno de ellos. Observe el uso correcto de cada una de las dos clases cuando se cualifican completamente: using System; namespace Graficos2D { public class Point { ... } } namespace Graficos3D { public class Point { ... } } class MainClass { Graficos2D.Point p1; Graficos3D.Point p2; static void Main(string[] args) { ... } }

No existe relacin entre espacios de nombres y ficheros (a diferencia de Java). Los espacios de nombres se pueden anidar. Observe en el siguiente ejemplo cmo la cualificacin evita cualquier duda acerca de la clase de los objetos:

namespace N1 { public class C1 { public class C2 {} } namespace N2 { public class C2 {} } } class MainClass { N1.C1 o1; N1.C1.C2 o2; N1.N2.C2 o3; static void Main(string[] args) { ... } }

La directiva using
La directiva using se utiliza para permitir el uso de tipos en un espacio de nombres, de modo que no sea necesario especificar el uso de un tipo en ese espacio de nombres (directiva using).

using System; namespace N1 { public class C1 { public class C2 {} } namespace N2 { public class C2 {} } }

namespace DemoNamespace { using N1; class MainClass { C1 o1; N1.C1 o1_bis; //C2 c; definida en N1 C1.C2 o2; N1.N2.C2 o3; cualificado N2.C2 o3_bis;

// N1 es implcito (N1.C1) // Tipo totalmente cualificado // Error! C2 no est // Idem a: N1.C1.C2 o2; // Tipo totalmente // Idem a: N1.N2.C2 o3_bis;

static void Main(string[] args) { ... } } } La directiva using tambin puede usarse para crear un alias para un espacio de nombres (alias using).

using Cl2 = N1.N2.C2; using NS1 = N1.N2; Cl2 ob1; NS1.C2 ob2; // O sea, N1.N2.C2 // O sea, N1.N2.C2

Observe cmo, en ocasiones, el uso de alias simplifica y clarifica el cdigo: Uso de alias using System; namespace Graficos2D { public class Point { } } namespace Graficos3D { public class Point { } }

namespace DemoNamespace { using Point2D = Graficos2D.Point; using Point3D = Graficos3D.Point; class MainClass { Point2D p1; Point3D p2; static void Main(string[] args) { } } } Cualquier declaracin -propia- que use un nombre empelado en un espacio de nombres oscurece la declaracin del espacio de nombres en el bloque en el que ha sido declarada.

using System; namespace Graficos2D { public class Point { } public class Almacen {} } namespace DemoNamespace { using Graficos2D; class MainClass { Point p1; int[] Almacen = new int[20]; static void Main(string[] args) { } } }

Organizacin fsica de los tipos

Los tipos se definen en ficheros Un fichero puede contener mltiples tipos. Cada tipo est definido en un nico fichero (declaracin y definicin coinciden). No existen dependencias de orden entre los tipos. Los ficheros se compilan en mdulos. En .NET existen dos tipos de mdulos de cdigo compilado: Ejecutables (extensin .exe) Bibliotecas de enlace dinmico (extensin .dll) Ambos son ficheros que contienen definiciones de tipos de datos. Se diferencian en que slo los primeros (.exe) disponen de un mtodo especial que sirve de punto de entrada a partir del que es posible ejecutar el cdigo que contienen haciendo una llamada desde la lnea de rdenes del sistema operativo. Los mdulos se agrupan en ensamblados o assemblies: Un ensamblado consiste en un bloque constructivo reutilizable, versionable y autodescriptivo de una aplicacin de tipo Common Language Runtime. Los ensamblados proporcionan la infraestructura que permite al motor de tiempo de ejecucin comprender completamente el contenido de una aplicacin y hacer cumplir las reglas del control de versiones y de dependencia definidas por la aplicacin. Estos conceptos son cruciales para resolver el problema del control de versiones y para simplificar la implementacin de aplicaciones en tiempo de ejecucin. Un ensamblado es una agrupacin lgica de uno o ms mdulos o ficheros de recursos (ficheros .GIF, .HTML, etc.) que se engloban bajo un nombre comn. Un programa puede acceder a informacin o cdigo almacenados en un ensamblado sin tener que conocer cul es el fichero en concreto donde se encuentran, por lo que los ensamblados nos permiten abstraernos de la ubicacin fsica del cdigo que ejecutemos o de los recursos que usemos.

Hay dos tipos de ensamblados: ensamblados privados y ensamblados compartidos. Tambin para evitar problemas, se pueden mantener mltiples versiones de un mismo ensamblado. As, si una aplicacin fue compilada usando una cierta versin de un determinado ensamblado compartido, cuando se ejecute slo podr hacer uso de esa versin del ensamblado y no de alguna otra ms moderna que se hubiese instalado. De esta forma se soluciona el problema del infierno de las DLL.

Referencias
En Visual Studio se utilizan referencias para identificar assemblies particulares, p.ej. compilador de C# csc HelloWorld.cs /reference:System.WinForms.dll Los espacios de nombres son una construccin del lenguaje para abreviar nombres, mientras que las referencias son las que especifican qu assembly utilizar.

Un ejercicio
Sobre un proyecto nuevo, trabajaremos con dos ficheros de cdigo: uno contendr el mtodo Main() y el otro un espacio de nombres con una clase que se empelar en Main(). 1. Aadir al proyecto un mdulo de cdigo (clase). Escribir una clase "til" e insertarla en un espacio de nombres. Supongamos se llama

MiClase.cs
2. Compilar el espacio de nombres y obtener una DLL: csc /t:library MiClase.cs

3. Escribir en Main() cdigo que use la clase. Supongamos que el fichero se llama Ppal.cs 4. Compilar el mdulo principal con el espacio de nombres y crear un ejecutable: csc /r:MiClase.dll Ppal Observar el uso de una referencia en la llamada al compilador.

Ejecucin de aplicaciones
Gestin de memoria
C# utiliza un recolector de basura para gestionar automticamente la memoria, lo que elimina quebraderos de cabeza y una de las fuentes ms comunes de error pero conlleva una finalizacin no determinsitica (no se ofrece ninguna garanta respecto a cundo se llama a un destructor, ni siquiera podemos afirmar que llegue a llamarse el destructor). Los objetos que deban eliminarse tras ser utilizados deberan implementar la interfaz System.IDisposable (escribiendo en el mtodo Dispose todo aquello que haya de realizarse para liberar un objeto). El mtodo Dispose siempre se invoca al terminar una sentencia using:

public class MyResource : IDisposable { public void MyResource() { // Acquire valuble resource } public void Dispose() { // Release valuble resource } public void DoSomething() { ... } } using (MyResource r = new MyResource()) { r.DoSomething(); } // se llama a r.Dispose()

Cdigo no seguro
En ocasiones necesitamos tener un control total sobre la ejecucin de nuestro cdigo (cuestiones de rendimiento, compatibilidad con cdigo existente, uso de DLLs...), por lo que C# nos da la posibilidad de marcar fragmentos de cdigo como cdigo no seguro (unsafe) y as poder emplear C/C++ de forma nativa: punteros, aritmtica de punteros, operadores -> y *, ... sin recoleccin de basura. La instruccin stackalloc reserva memoria en la pila de manera similar a como malloc lo hace en C o new en C++.

public unsafe void MiMetodo () // Mtodo no seguro { ... } unsafe class MiClase { ... } seguros // Clase (struct) no segura // Todos los miembros son no

struct MiStruct { private unsafe int * pX; // Campo de tipo puntero no seguro ... } unsafe { // Instrucciones que usan punteros }

En caso de que la compilacin se vaya a realizar a travs de Visual Studio .NET, la forma de indicar que se desea compilar cdigo inseguro es activando la casilla Proyecto | Propiedades de (proyecto) | Propiedades de Configuracin | Generar | Permitir bloques de cdigo no seguro | True. class StackallocApp { public unsafe static void Main() { const int TAM = 10; int * pt = stackalloc int[TAM]; for (int i=0; i<TAM; i++) pt[i] = i; for(int i=0; i<TAM; i++) System.Console.WriteLine(pt[i]); Console.ReadLine (); } }

Para asegurarnos de que el recolector de basura no mueve nuestros datos tendremos que utilizar la sentencia fixed. El recolector puede mover los datos de tipo referencia, por lo que si un puntero contiene la direccin de un dato de tipo referencia podra apuntar a una direccin incorrecta despus de que el recolector de basura trabajara. Si un conjunto de instrucciones se encierra en un bloque fixed se previene al recolector de basura para que no mueva el objeto al que se referencia mientras dura el bloque fixed. Esta capacidad tiene su coste: al emplear punteros, el cdigo resultante es inseguro ya que ste no se puede verificar. De modo que tendremos que extremar las precauciones si alguna vez tenemos que usar esta capacidad del lenguaje C#.

Preprocesador
C# proporciona una serie de directivas de preprocesamiento con distintas funciones. Aunque se le sigue llamando preprocesador (como en C o C++), el preprocesador no es independiente del compilador y se han eliminado algunas directivas como #include (para mejorar los tiempos de compilacin, se utiliza el esquema de lenguajes como Java o Delphi en lugar de los ficheros de cabecera tpicos de C/C++) o las macros de #define (para mejorar la claridad del cdigo). Directiva Descripcin Definicin de smbolos para la compilacin condicional. Compilacin condicional. Emisin de errores y avisos. Delimitacin de regiones. Especificacin de nmeros de lnea.

#define, #undef #if, #elif, #else, #endif #error, #warning #region, #end #line

Aserciones
Las aserciones nos permiten mejorar la calidad de nuestro cdigo. Esencialmente, las aserciones no son ms que pruebas de unidad que estn incluidas en el propio cdigo fuente. Las aserciones nos permiten comprobar precondiciones, postcondiciones e invariantes. Las aserciones slo se habilitan cuando se compila el cdigo para depurarlo, de forma que su correcto funcionamiento se compruebe continuamente. Cuando distribuyamos nuestras aplicaciones, las aserciones se eliminan para no empeorar la eficiencia de nuestro cdigo. El mtodo Assert() comprueba una condicin y muestra un mensaje si sta es falsa. Puede emplearse cualquiera de estas versiones: public static void Assert(bool) comprueba una condicin y enva la pila de llamadas si sta es falsa. public static void Assert(bool, string) Comprueba una condicin y muestra un mensaje si sta es falsa. ublic static void Assert(bool, string, string Comprueba una condicin y muestra ambos mensajes si es false. Compilacin condicional: Aserciones

public class Debug { public static void Assert(bool cond, String s) { if (!cond) { throw new AssertionException(s); } } void DoSomething() { ... Assert((x == y), "X debera ser igual a Y"); ... } }

Documentacin
A los programadores no les suele gustar documentar cdigo, por lo que resulta conveniente suministrar un mecanismo sencillo que les permita mantener su documentacin actualizada. Al estilo de doxygen o Javadoc, el compilador de C# es capaz de generarla automticamente a partir de los comentarios que el progamador escriba en los ficheros de cdigo fuente. Los comentarios a partir de los cuales se genera la documentacin se escriben en XML. El hecho de que la documentacin se genere a partir de los fuentes permite evitar que se tenga que trabajar con dos tipos de documentos por separado (fuentes y documentacin) que deban actualizarse simultneamente para evitar incosistencias entre ellos derivadas de que evolucionen de manera separada ya sea por pereza o por error. El compilador genera la documentacin en XML con la idea de que sea fcilmente legible para cualquier aplicacin. Para facilitar su legibilidad a humanos bastara aaderle una hoja de estilo XSL o usar alguna aplicacin especfica encargada de leerla y mostrarla de una forma ms cmoda para humanos. Los comentarios XML se denotan con una barra triple ( ///) y nos permiten generar la documentacin del cdigo cuando compilamos con la opcin /doc.

csc programa.cs /doc:docum_programa.xml El formato de los comentarios viene definido en un esquema XML, si bien podemos aadir nuestras propias etiquetas para personalizar la documentacin de nuestras aplicaciones. Algunas de las etiquetas predefinidas se verifican cuando generamos la documentacin (parmetros, excepciones, tipos...). Estos comentarios han preceder las definiciones de los elementos a documentar. Estos elementos slo pueden ser definiciones de miembros, ya sean tipos de datos (que son miembros de espacios de nombres) o miembros de tipos datos, y han de colocarse incluso incluso antes que sus atributos.

Etiqueta XML

Descripcin Descripcin breve de tipos y miembros. Descripcin detallada de tipos y miembros. Delimita prrafos. Ejemplo de uso. Referencias cruzadas. Usa el atributo cref Cdigo de ejemplo (verbatim). Parmetros de mtodos. Usa el atributo name. Referencia a parmetros de metodos. Usa el atributo name. Valor devuelto por el mtodo. Descripcion de Excepciones. Descripcin de propiedades. Generar listas. Usa el atriibuto (opcional) type. Generar listas. Usa el atriibuto (opcional) type (puede ser: bullet, number o table). Permisos.

<summary> <remarks> <para> <example> <see> <seealso> <c> <code> <param> <paramref> <returns> <exception> <value> <list>

<item>

<permission>

Veamos un ejemplo detallado:

using System; namespace Geometria { /// <summary> /// Clase Punto. /// </summary> /// <remarks> /// Caracteriza a los puntos de un espacio bidimensional. /// Tiene mltiples aplicaciones.... /// </remarks> class Punto { /// /// /// /// /// /// <summary> Campo que contiene la coordenada X de un punto </summary> <remarks> Es de solo lectura </remarks>

public readonly uint X; /// /// /// /// /// /// <summary> Campo que contiene la coordenada Y de un punto </summary> <remarks> Es de solo lectura </remarks>

public readonly uint Y; /// /// /// /// /// <summary> Constructor de la clase </summary> <param name="x">Coordenada x</param> <param name="y">Coordenada y</param>

public Punto(uint x, uint y) { this.X=x; this.Y=y; } } // fin de class Punto /// <summary> /// Clase Cuadrado. Los objetos de esta clase son polgonos /// cerrados de cuatro lados de igual longitud y que /// forman ngulos rectos.

/// </summary> /// <remarks> /// Los cuatro vrtices pueden numerarse de manera que /// el vrtice 1 es el que tiene los menores valores de /// las coordenadas X e Y. /// /// Los dems vrtices se numeran a partir de ste recorriendo /// el cuadrado en sentido antihorario. /// </remarks> class Cuadrado { /// <summary> /// Campo que contiene las coordenadas del vrtice 1. /// </summary> /// protected Punto vertice1; /// <summary> /// Campo que contiene la longitud del lado. /// </summary> protected uint lado; /// <summary> /// Constructor de la clase. /// Construye un cuadrado a partir del vrtice 1 y de /// la longitud del lado. /// </summary> /// <param name="vert1">Coordenada del vrtice 1</param> /// <param name="lado">Longitud del lado</param> /// public Cuadrado(Punto vert1, uint lado) { this.vertice1=vert1; this.lado=lado; } /// <summary> /// Constructor de la clase. /// Construye un cuadrado a partir de los vrtices 1 y 3. /// </summary> /// <param name="vert1">Coordenada del vrtice 1</param> /// <param name="vert3">Coordenada del vrtice 3</param> /// <remarks> /// Habra que comprobar si las componentes del vrtice 3 /// son mayores o menores que las del vrtice1. /// Vamos a presuponer que las componentes del

vrtice 3 son /// siempre mayores que las del uno. /// </remarks> public Cuadrado(Punto vert1, Punto vert3) { this.vertice1=vert1; this.lado=(uint) Math.Abs(vert3.X-vert1.X); } /// <summary> /// Propiedad que devuelve el punto que representa a /// las coordenadas del vrtice 1. /// </summary> public Punto Vertice1 { get { return this.vertice1; } } /// <summary> /// Propiedad que devuelve el punto que representa a /// las coordenadas del vrtice 2. /// </summary> public Punto Vertice2 { get { Punto p=new Punto(this.vertice1.X + this.lado,this.vertice1.Y); return p; } } ...... } // Fin de class Cuadrado } // Fin de namespace Geometria namespace PruebaGeometria { using Geometria; class GeometriaApp { ....

Para generar la documentacin en Visual Studio .NET seleccionaremos el proyecto en el explorador de soluciones y daremos el nombre del fichero XML que contendr la documentacin: Ver | Pginas de propiedades | Propiedades de configuracin | Generar | Archivo de documentacin XML y darle el nombre: DocumentacionGeometia, por ejemplo. Para ver el resultado: Herramientas | Generar pginas Web de comentarios. Unos ejemplos:

Conceptos bsicos de programacin orientada a objetos


Conceptos bsicos Referencias

Conceptos bsicos
Objetos, instancias y clases: Un objeto es una estructura de datos en tiempo de ejecucin, formada por uno o ms valores (campos) y que sirve como representacin de un objeto abstracto. Todo objeto es instancia de una clase. Una clase es un tipo abstracto de datos implementado total o parcialmente, que encapsula datos y operaciones. Las clases sirven de mdulos y de tipos (o patrones de tipos si son genricas). Mdulo: Unidad lgica que permite descomponer el software. En programacin orientada a objetos, las clases proporcionan la forma bsica de mdulo. Para facilitar el desarrollo de software y su posible reutilizacin, las dependencias entre mdulos deberan reducirse al mximo para conseguir sistemas dbilmente acoplados. Tipo: Cada objeto tiene un tipo, que describe un conjunto de operaciones con las que estn equipados todos los objetos de una misma clase. Interfaz: Contrato perfectamente definido que especifica completamente las condiciones precisas que gobiernan las relaciones entre una clase proveedora y sus clientes (rutinas exportadas). Adems, es deseable conocer las precondiciones, postcondiciones e invariantes que sean aplicables. Identidad: Cada objeto (instancia de una clase) tiene una identidad nica, independientemente de su contenido actual (los datos almacenados en sus campos). Encapsulacin (u ocultacin de informacin): Capacidad de evitar que ciertos aspectos sean visibles desde el exterior. De esta forma, se ocultan detalles de implementacin y el usuario puede emplear objetos sin tener que conocer su estructura interna. Herencia: Los tipos se organizan de forma jerrquica (clases base y derivadas, superclases y subclases). La herencia proporciona un mecanismo simple mediante el cual se pueden definir unos tipos en funcin de otros, a los que aade sus caractersticas propias. Hay que distinguir entre herencia de interfaz y herencia de implementacin (que aumenta el acoplamiento entre los mdulos de un programa). Polimorfismo: Capacidad de usar un objeto sin saber su tipo exacto. Formalmente, el polimorfismo es la capacidad de que un elemento de cdigo pueda denotar, en tiempo de ejecucin, objetos de dos o ms tipos distintos.

Referencias
Bertrand Meyer: "Construccin de software orientado a objetos" , [2 ed.], Prentice Hall, 1999, ISBN 84-8322-040-7

Вам также может понравиться