You are on page 1of 450

La plataforma .

NET

Introduccin
La plataforma .NET es la propuesta de Microsoft para competir con la plataforma
Java. Mientras que Java se caracteriza por la mxima "write once, run anywhere",
la plataforma .NET de Microsoft est diseada para que se puedan desarrollar
componentes software utilizando casi cualquier lenguaje de programacin, de
forma que lo que escribamos en un lenguaje pueda utilizarse desde cualquier otro
de la manera ms transparente posible (utilizando servicios web como
middleware). Esto es, en vez de estar limitados a un nico lenguaje de
programacin, permitimos cualquier lenguaje de programacin, siempre y cuando
se adhiera a unas normas comunes establecidas para la plataforma .NET en su
conjunto. De hecho, existen compiladores de mltiples lenguajes para la
plataforma .NET: Visual Basic .NET, C#, Managed C++, Oberon, Component
Pascal, Eiffel, Smalltalk, Cobol, Fortran, Scheme, Mercury, Mondrian/Haskell, Perl,
Python, SML.NET...
La plataforma .NET apuesta por un futuro en el que las aplicaciones se ejecutan
de manera distribuida en Internet. As, una aplicacin se ejecuta en un solo
servidor y no existen mltiples copias de la misma. Adems, una misma aplicacin
puede "adornarse" con distintas interfaces para que, desde diferentes dispositivos
(telfonos mviles, PDAs, porttiles, etc.) pueda accederse a la misma. La
plataforma .NET no es ms que un conjunto de tecnologas para desarrollar y
utilizar componentes que nos permitan crear formularios web, servicios web y
aplicaciones Windows.

Para crear aplicaciones para la plataforma .NET, tanto servicios Web como
aplicaciones tradicionales (aplicaciones de consola, aplicaciones de ventanas,
servicios de Windows NT, etc.), Microsoft ha publicado el denominado kit de
desarrollo de software conocido como .NET Framework. Contiene el CLR
(Common Languaje Runtime), el .NET Framework Clases y caractersticas
avanzadas como ADO.NET (para acceso a bases de datos), ASP.NET (para
generar pginas activas) y WinForms (para construir aplicaciones Windows).
Adicionalmente puede emplearse Visual Studio.NET que permite hacer todo la
anterior desde una interfaz visual basada en ventanas. Ambas herramientas
pueden descargarse gratuitamente desde http://www.msdn.microsoft.com/net.

CLR [Common Language Runtime]
El Common Language Runtime (CLR) es el ncleo de la plataforma .NET ya que
es el encargado de gestionar la ejecucin de cdigo compilado para la plataforma
.NET. Puede asimilarse a la mquina virtual de Java.

Las dos principales caractersticas del CLR son:
Ejecucin multiplataforma: El CLR acta como una mquina
virtual, encargndose de ejecutar las aplicaciones diseadas para la
plataforma .NET. Su especificacin est abierta, por lo que cualquier
plataforma para la que exista una versin del CLR podr ejecutar
cualquier aplicacin .NET. Microsoft ha desarrollado versiones del
CLR para la mayora de las versiones de Windows. Por otro lado
Microsoft ha firmado un acuerdo con Corel para portar el CLR a
Linux y tambin hay terceros que estn desarrollando de manera
independiente versiones de libre distribucin del CLR para Linux.
Asimismo, dado que la arquitectura del CLR est totalmente abierta,
es posible que en el futuro se diseen versiones del mismo para
otros sistemas operativos.
Integracin de lenguajes: Desde cualquier lenguaje para el que
exista un compilador que genere cdigo para la plataforma .NET es
posible utilizar cdigo generado para la misma usando cualquier otro
lenguaje tal y como si de cdigo escrito usando el primero se tratase.
Otras caractersticas destacables son:
Modelo de programacin consistente: A todos los servicios y
facilidades ofrecidos por el CLR se accede de la misma forma: a
travs de un modelo de programacin orientado a objetos.
Eliminacin del "infierno de las DLLs": En la plataforma .NET
desaparece el problema conocido como "infierno de las DLLs" que
se da en los sistemas operativos actuales de la familia Windows ya
que en la plataforma .NET las versiones nuevas de las DLLs pueden
coexistir con las viejas.
Gestin de memoria: El CLR incluye un recolector de basura que
evita que el programador tenga que tener en cuenta cundo ha de
destruir los objetos que dejen de serle tiles.
Este recolector es una aplicacin que se activa cuando se quiere
crear algn objeto nuevo y se detecta que no queda memoria libre
para hacerlo. Entonces el recolector recorre la memoria dinmica
asociada a la aplicacin, detecta qu objetos hay en ella que no
puedan ser accedidos por el cdigo de la aplicacin, y los elimina
para limpiar la memoria de "objetos basura" y permitir la creacin de
otros nuevos. Gracias a este recolector se evitan errores de
programacin muy comunes como intentos de borrado de objetos ya
borrados, agotamiento de memoria por olvido de eliminacin de
objetos intiles o solicitud de acceso a miembros de objetos ya
destruidos.
Seguridad de tipos: El CLR facilita la deteccin de errores de
programacin difciles de localizar comprobando que toda conversin
de tipos que se realice durante la ejecucin de una aplicacin .NET
se haga de modo que los tipos origen y destino sean compatibles.
Aislamiento de procesos: El CLR asegura que desde cdigo
perteneciente a un determinado proceso no se pueda acceder a
cdigo o datos pertenecientes a otro, ni se permite acceder a
posiciones arbitrarias de memoria.
Tratamiento de excepciones: En el CLR todo los errores que se
puedan producir durante la ejecucin de una aplicacin se propagan
de igual manera: mediante excepciones.
Soporte multihilo: El CLR es capaz de trabajar con aplicaciones
divididas en mltiples hilos de ejecucin que pueden ir
evolucionando por separado en paralelo o intercalndose, segn el
nmero de procesadores de la mquina sobre la que se ejecuten.
Las aplicaciones pueden lanzar nuevos hilos, destruirlos,
suspenderlos por un tiempo o hasta que les llegue una notificacin,
enviarles notificaciones, sincronizarlos, etc.
Distribucin transparente: El CLR ofrece la infraestructura
necesaria para crear objetos remotos y acceder a ellos de manera
completamente transparente a su localizacin real, tal y como si se
encontrasen en la mquina que los utiliza.
Seguridad avanzada: El CLR proporciona mecanismos para
restringir la ejecucin de ciertos cdigos o los permisos asignados a
los mismos segn su procedendecia o el usuario que los ejecute.
Como se puede deducir de las caractersticas comentadas, el CLR lo que hace es
gestionar la ejecucin de las aplicaciones diseadas para la plataforma .NET. Por
esta razn, al cdigo de estas aplicaciones se le suele llamar cdigo gestionado,
y al cdigo no escrito para ser ejecutado directamente en la plataforma .NET se le
suele llamar cdigo no gestionado.
Assemblies
Constituyen la unidad lgica de despliegue en la plataforma .NET. Un assembly
incluye metadatos acerca de los componentes incluidos en el assembly
(versiones, tipos, dependencias...), metadatos acerca de los tipos incluidos
(propiedades, atributos, mtodos, signaturas, clases base...), el cdigo intermedio
MSIL (Microsoft Intermediate Language, similar a los bytecodes de Java) y los
recursos adicionales que sean necesarios (imgenes, textos...). En definitiva,
viene a ser algo parecido a los ficheros JAR (Java archives) de Java.
En la plataforma .NET, una aplicacin est formada por uno o varios assemblies.
Al poder coexistir distintas versiones de un assembly, se eliminan muchos de los
problemas que caracterizan a las aplicaciones tpicas de Windows, facilitando el
despliegue, actualizacin y eliminacin de aplicaciones. De hecho, una aplicacin
concreta podra utilizar simultneamente varias versiones de un assembly.
Biblioteca de clases .NET






ADO.NET
Similar a ADO, proporciona acceso a datos de forma independiente al lenguaje de
programacin que utilicemos. Los datos se pueden ver y procesar de forma
relacional (tablas) o jerrquica (XML).
Desarrollo de aplicaciones
Formularios Windows
Los formularios Windows estn construidos sobre la base de la plataforma .NET y
permiten construir complejas aplicaciones Windows en un entorno de desarrollo
visual de aplicaciones (RAD: Rapid Application Development), tal como hasta
ahora se vena haciendo con lenguajes del estilo de Visual Basic o Delphi.
Formularios web
Los formularios web, que se construyen con ASP.NET, constituyen la evolucin
natural y lgica de ASP. Siguiendo el mismo estilo que su antecesor (editar una
pgina y listo), ASP.NET permite utilizar controles complejos, facilita la gestin de
sesiones, permite separar la interfaz de la lgica interna, elimina la distincin entre
ASP e ISAPI y nos permite emplear cualquier lenguaje de programacin que est
soportado por la plataforma .NET.
Soporte para mltiples lenguajes
Para permitir el desarrollo de componentes utilizando mltiples lenguajes de
programacin, la plataforma .NET establece un sistema de tipos comn (CTS:
Common Type System) y una especificacin que permite que puedan interactuar
fragmentos de cdigo escritos en distintos lenguajes (CLS: Common Language
Specification).
La plataforma .NET permite utilizar una amplia gama de lenguajes de
programacin, como es el caso de
C#: Un nuevo lenguaje creado para la plataforma .NET. Se puede
considerar una versin "segura" de C++ y viene a ser un hbrido
entre Java (de Sun) y Delphi (de Borland). Es un lenguaje de
programacin orientado a objetos que pretende facilitar el desarrollo
de componentes software robustos y duraderos que preserven la
inversin realizada en su desarrollo (en palabras de Microsoft).
Visual Basic .NET: Moderniza y simplifica el lenguaje de
programacin Visual Basic, con algunas novedades sintcticas,
herencia simple, tratamiento de hebras y manejo de excepciones.
Ejecucin de cdigo
Para que un lenguaje de programacin sea soportado por la plataforma .NET, ha
de existir un compilador que traduzca de este lenguaje a MSIL ("managed code").
A la hora de ejecutar el cdigo intermedio, ste es siempre compilado a cdigo
nativo.

El SDK para la plataforma .NET
Microsoft pone a disposicin de todo aqul que est interesado el kit de desarrollo
de software para la plataforma .NET: The Microsoft .NET Framework Software
Development Kit (SDK). Este kit incluye la plataforma .NET y todo lo necesario
para desarrollar, compilar, probar y distribuir aplicaciones para la plataforma .NET
(as como documentacin, ejemplos, herramientas en lnea de comandos y
compiladores).
Para utilizar el kit de desarrollo de software se necesita tener uno de los siguientes
sistemas operativos:
Microsoft Windows NT 4.0 (Service Pack 6a)
Microsoft Windows 2000 (SP 2 recomendado)
Microsoft Windows XP Professional
y tambin se recomienda tener instalado el navegador web Microsoft Internet
Explorer 5.01 o posterior.
El Visual Studio .NET ya incluye la plataforma .NET, por lo que, si ya tiene
instalado el Visual Studio .NET no tendr que instalar el SDK por separado.
Material complementario
El mismsimo Bill Gates se jacta de que la plataforma .NET es el producto software
sobre el que ms se ha escrito y al que se le han otorgado ms premios antes
incluso de que existiese realmente (Software Development, mayo 2002, pgina 17:
"3.5 millones de usuarios beta, 200 libros y 764 grupos de usuarios"), por lo que
no le ser difcil al lector encontrar material ms que de sobra para pasarse
leyendo el resto de su vida (y alguna de las siguientes). Dada la gran abundancia
de material disponible, se recomienda al lector que comience a trabajar con la
plataforma .NET lo antes posible y recurra al material complementario nicamente
cuando tenga que resolver alguna duda concreta.
Enlaces
http://www.microsoft.com/net/
http://msdn.microsoft.com/net/
http://www.gotdotnet.com
nntp://msnews.microsoft.com
Picking a Winner: .NET vs. J2EE, Jim Farley, Software Development,
March 2001
.NET vs. J2EE [Java 2 Enterprise Edition]

.NET J2EE
Lenguaje de
programacin
C#... Java
Plataforma Windows ...
Mquina virtual
CLR [Common
Language
Runtime]
JVM [Java Virtual
Machine] / JRE [Java
Runtime Environment]
Lenguaje
interpretado
MSIL Java bytecodes
Clientes Windows .NET Forms Swing
Clientes Web ASP.NET JSP / Servlets
Servidores de
aplicaciones
???
EJB [Enterprise Java
Beans]
Acceso a bases
de datos
ADO.NET JDBC / EJB-SQL
Paso de mensajes MSMQ
JMS [Java Messaging
Service] / Msg EJBs
Integracin con
sistemas previos
COM TI
JCA [Java Connector
Architecture]

Visual Studio .NET

El entorno de desarrollo
Al arrancar por primer vez Visual Studio 2005 nos aparece la siguiente ventana:

Nuestro inters es trabajar en C#, as que seleccionaremos esa configuracin
predeterminada:

Visual Studio mostrar la pantalla inicial:



Primeros pasos
Configuracin
El entorno de Visual Studio 2005 es completamente configurable, pudindose
adaptar a las necesidades y gustos de todo el mundo. La configuracin se realiza
a travs del men "Herramientas", y dentro de l, "Personalizar" y "Opciones". Por
ejemplo, dentro de "Opciones" podemos establecer la apariencia de la pgina de
inicio:

Mantenindose al da...
En la seccin "Introduccin", podremos acceder a las ltimas novedades y noticias
relacionadas con el desarrollo de aplicaciones con VS.NET (siempre que estemos
conectados a Internet, obviamente). Podemos acceder a ejemplos de cdigo,
grupos de noticias y componentes listos para su uso en la plataforma .NET.
Incluso podemos buscar informacin en la biblioteca de la red de desarrolladores
de Microsoft (MSDN Library), descargar las ltimas actualizaciones, etc.
Proyectos recientes
Esta seccin proporciona un rpido acceso a los proyectos recientes con los que
hayamos estado trabajando, as como acceso a sitios web (accesibles por FTP,
HTTP, IIS) o al sistema de archivos local.
Aprovechando al mximo el espacio disponible
Cuando tengamos mltiples ficheros abiertos y queramos ver dos
simultneamente, slo tendremos que hacer "drag&drop" para que el
espacio disponible se reparta equitativamente ("tiling") entre los
documentos que deseemos visualizar. Slo hay que pinchar en el tab
correspondiente al nombre del documento que queramos ver y llevarlo
hasta el espacio en el que se visualizan los documentos. En funcin de
dnde soltemos el botn del ratn se crear una divisin horizontal o
vertical en nuestra pantalla. Para volver al estado inicial slo tenderemos
que arrastrar los documentos de vuelta a donde estaban.
Las distintas ventanas visibles en nuestro monitor tambin pueden
arrastrarse para colocarlas donde ms nos interese, como parte de nuestra
ventana principal ("dockable") o como ventanas independientes ("floating").
El estado de una ventana tambin podemos cambiarlo si pinchamos con el
botn derecho sobre su barra de ttulo.
Algunas ventanas slo se utilizan en determinadas situaciones, por lo que
podemos fijar su estado a "auto-hide" para que no ocupen parte de nuestro
preciado espacio en pantalla de forma innecesaria. De hecho, ste es el
estado inicial de las ventanas que aparecen en la parte izquierda de nuestro
monitor ("Server Explorer" y "Toolbox"). Es estado "auto-hide" se puede
establecer seleccionando la opcin correspondiente del men contextual
que aparece asociado a las barras de ttulo de las distintas ventana y
tambin mediante el botoncito con forma de pin que aparece junto al aspa
para cerrar la ventana.
Como ejercicio configure su entorno para que tenga este aspecto:

Personalizacin del entorno
Las opciones de personalizacin del entorno de desarrollo VS.NET se encuentra
centralizadas en la opcin "Opciones" del men "Herramientas". Las distintas
opciones de configuracin estn agrupadas por categoras de forma jerrquica.
Entre la gran cantidad de opciones disponibles, son de especial inters las
siguientes categoras:

Entorno: Aspectos generales del entorno, como visualizacin de
documentos y arranque inicial del entorno ("General"), opciones sobre el
manejo de ficheros ("Documentos") tales como uso de ficheros que sean
modificados fuera del entorno; tipos de letra, colores e impresin ("Fuentes
y Colores"); sistema de ayuda ("Ayuda"), configuracin del teclado
("Teclado"), etc.
Control de cdigo fuente: Configuracin del entorno para el uso de
sistemas de control de versiones que permitan que varios programadores
trabajen concurrentemente en un proyecto. Principalmente, las opciones
disponibles permiten establecer qu acciones se realizarn
automticamente (vg: check-out de un fichero que se vaya a modificar) y
cules requerirn una accin explcita por parte del usuario (vg: check-in de
un fichero una vez que hayamos comprobado que las modificaciones
realizadas funcionan correctamente).
Editor de texto: Configuracin del editor de documentos, en general y en
particular para determinados tipos de archivos (como los escritos en alguno
de los lenguajes de programacin soportados por el VS.NET).
Adems de las categoras anteriores, las distintas herramientas instaladas
en el VS.NET incorporan pginas de opciones para su configuracin.

La opcin "Personalizar" del men "Herramientas" nos permite modificar las
opciones que aparecen en los mens y barras de botones del VS .NET. Adems,
cuando el dilogo "Personalizar" est abierto, podemos modificar los mens y
barras de botones del VS.NET directamente, de una forma mucho ms cmoda
para nosotros.
Herramientas
Visual Studio .NET incluye una serie de herramientas tiles en el desarrollo de
aplicaciones:

Explorador de Soluciones

El explorador de soluciones, nombre que se le da en Visual Studio .NET a un
conjunto de proyectos relacionados, es la herramienta que nos permite gestionar
proyectos en VS.NET. Desde l podemos acceder a los distintos componentes de
un proyecto, ver sus propiedades y ejecutar acciones sobre ellos (p.ej. control de
versiones).









Vista de Clases

La ventana "Vista de Clases" muestra las clases de un proyecto, sus miembros y
su estructura jerrquica de herencia. Esta vista de un proyecto se va actualizando
conforme escribimos cdigo.

Lista de Tareas

La lista de tareas es el sitio donde podemos ir anotando las cosas que tengamos
pendientes (idetificadas por el token "TODO"). Cuando empleamos asistentes
("wizards"), stos se encargan de recordarnos lo que tengamos pendiente
aadiendo tareas a esta lista. Por otro lado, los errores y warnings del compilador
tambin aparecern en esta lista.
Si una tarea de la lista de tareas est asociada a algn fichero, haciendo doble
click sobre ella pasamos de forma instantnea a la posicin correspondiente del
fichero asociado.
Examinador de objetos
El examinador de objetos nos permite explorar espacios de nombres, clases,
estructuras, interfaces y dems componentes de nuestros proyectos, as como
todos aquellos componentes externos a los que hagan referencia los componentes
de nuestro proyecto.

El editor
El editor de textos de VS.NET incluye funciones muy tiles para el programador
tales como bsqueda incremental (con CONTROL+I), "code outlining" (para
expandir y contraer bloques de cdigo), portapapeles mltiple ("clipboard ring",
CONTROL+SHIFT+V), seleccin de columnas (seleccionando con el ratn
mientras se mantiene pulsada la tecla ALT), numeracin de lneas, impresin en
color, etc..


Regiones
En Visual Studio se pueden definir regiones de cdigo fcilmente para que luego
resulte ms sencillo analizar su funcionamiento. Aparte de las funciones (que
podemos expandir y contraer), tambin podemos especificar cmo agrupar
fragmentos de nuestro cdigo mediante la directiva #region.


#region "Identificador del fragmento de cdigo"
...
#endregion
Macros
Visual Studio.NET viene equipado con un avanzado sistema de macros que nos
permiten personalizar y automatizar las tareas que realicemos de forma repetitiva.
El IDE para macros nos permite desarrollar y depurar macros con las que
manipular de forma automtica casi cualquier elemento de Visual Studio .NET
(proyectos, editor de cdigo, ventanas de herramientas, compilacin y despliegue
de nuestras aplicaciones...).
El submen 'Macros' de la opcin "Herramientas" incluye utilidades como 'Grabar
TemporaryMacro' que graban las acciones que realicemos en el entorno hasta que
pulsemos el botn 'Detener Grabacin' (el central en la barra de botones para
macros).

Tras finalizar la grabacin de la macro temporal, podemos ejecutarla con
CONTROL+SHIFT+P. Adems, si es de utilidad para nosotros, podemos
guardarla y editarla a nuestro antojo (para lo cual podemos emplear la herramienta
"Explorador de Macros" que aparece en "Ver | Otras Ventanas | Explorador de
Macros").

Una vez que hayamos diseado nuestras macros, podemos asociarlas a
combinaciones de teclas ("Herramientas | Opciones | Entorno | Teclado") e incluso
aadirlas a los mens del Visual Studio o ponerlas en las barras de botones del
IDE.
Ayuda
Como no poda ser menos, el VS.NET incluye un potente sistema de ayuda que
pone a nuestra disposicin ayuda en funcin del contexto en el que nos
encontremos y las acciones que realicemos con el ratn y el teclado (ventana
"Ayuda Dinmica"):

Como es habitual en cualquier aplicacin Windows, podemos acceder en todo
momento a la ayuda si utilizamos la tecla F1. Como particularidad del VS.NET, lo
que nos muestra la ayuda viene filtrado por la configuracin que hayamos
seleccionado para nuestro entorno.
Enlaces de inters
http://msdn.microsoft.com/vstudio/
http://code.msdn.microsoft.com/


El lenguaje de programacin C#

Introduccin al lenguaje de programacin C#
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:
o No pueden usarse variables que no hayan sido inicializadas.
o Slo se admiten conversiones entre tipos compatibles
o 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 explcitas-
cuando 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.
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 Si no tiene sufijo, su tipo es int.
o Si tiene el sufijo U o u, su tipo es uint o ulong.
o Si tiene el sufijo L o l, su tipo es long o ulong.
o Si 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 // int
0x7B // hexadecimal
123U // unsigned
123ul // unsigned long
123L // 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 Si no se especifica sufijo, el tipo es double.
o Si el sufijo es F o f es de tipo float.
o Si el sufijo es D o d es de tipo double.
o Si 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
Nombre del
carcter
Codificacin
Unicode
\' Comilla simple 0x0027
\" Comilla doble 0x0022
\\ Barra invertida 0x005C
\0 Null 0x0000
\a Alerta 0x0007
\b Retroceso 0x0008
\f Avance de pgina 0x000C
\n Nueva lnea 0x000A
\r Retorno de carro 0x000D
\t
Tabulacin
horizontal
0x0009
\v Tabulacin vertical 0x000B
El tipo de un literal de caracteres es char.
Literales de caracteres
'A' // caracter sencillo
'\u0041' // caracter Unicode
'\x0041' // unsigned short hexadecimal
'\n' // caracter 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, mundo!
"!Hola, \t 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!" // !Hola, \t mundo!
"Me dijo \"Hola\" y me asust" // Me dijo "Hola" y me asust
@"Me dijo ""Hola"" y me asust" // Me dijo "Hola" y me
asust
"\\\\servidor\\share\\file.txt" // \\servidor\share\file.txt
@"\\servidor\share\file.txt" // \\servidor\share\file.txt
@"uno // Esta es una cadena distribuida
dos" // en dos lineas.
Literal null.
Su nico valor es null y su tipo es el tipo null.
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, {1}.", TuEdad, TuNombre);
// Escribe: Tienes 25 aos, Pepe.
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 Tipos referencia
La variable contiene un
valor
La variable contiene una
referencia
El dato se almacena en la
pila
El dato se almacena en el heap
El dato siempre tiene valor El dato puede no tener valor null
Una asignacin copia el
valor
Una asignacin copia la
referencia


int i = 123; // tipo valor

string s = "Hello world"; // 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)
Alias Descripcin
Tamao
(bytes)
Rango
System.Sbyte sbyte
Bytes con
signo
1 -128 ... 127
System.Int16 short
Enteros
cortos
2 -32.768 ... 32.767
System.Int32 int Enteros 4
-2.147.483.648 ...
2.147.483.647
System.Int64 long
Enteros
largos
8
-9.223.372.036.854.775.808
...
9.223.372.036.854.775.807
System.Byte byte
Bytes (sin
signo)
1 0 ... 255
System.Uint16 ushort
Enteros
cortos (sin
signo)
2 0 ... 65.535
System.UInt32 uint
Enteros (sin
signo)
4
0 ...
18.446.744.073.709.551.615
System.Uint64 ulong
Enteros
largos (sin
signo)
8
0 ...
18.446.744.073.709.551.615
System.Single float
Reales (7
decimales)
4 1.5 x 10-45 ... 3.4 x 10+38
System.Double double
Reales (15-
16
decimales)
8
5.0 x 10-324 ... 1.7 x
10+308
System.Decimal decimal
Reales (28-
29
decimales)
12 1.0 x 10-28 ... 7.9 x 10+28
System.Char char
Caracteres
Unicode
2 Cualquier carcter Unicode
System.Boolean bool
Valores
lgicos
1 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 o5 = new ObjetoDemo(); // new llama a un
constructor
o5.Valor = 10;
ObjetoDemo o6 = new ObjetoDemo(); // new llama a un
constructor
o6.Valor = o5.Valor;
PintaDatos ("o5", "o6", o5, o6);

Console.ReadLine();
}

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);
}
}

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; // Radio del circulo del cual se calcula el area.
float Area; // Area del circulo
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 = 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); // Error: uso de la variable local no
asignada 'i'
}
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 Operadores
Aritmticos + - * / %
Lgicos (booleanos y bit a bit) & | ^ ! ~ && ||
Concatenacin de cadenas +
Incremento y decremento ++ --
Desplazamiento << >>
Relacionales == != < > <= >=
Asignacin
= += -= *= /= %= &= |= ^=
<<= >>=
Acceso a miembros .
Acceso por ndices []
Conversin de tipos explcita ()
Conditional ? :
Creacin de objetos new
Informacin de tipos as is sizeof typeof
Control de excepciones de
desbordamiento
checked unchecked
Direccionamiento indirecto y
direccin
* -> [] &
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.
o En datos struct realiza una copia directa del contenido, como
en C++.
o 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;
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; // Maximo valor short
static short y = 32767;

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 Operadores
Primarios
Parntesis: (x)

Acceso a miembros: x.y

Llamada a mtodos: f(x)

Acceso con ndices: a[x]

Post-incremento: x++

Post-decremento: x--

Llamada a un constructor: new

Consulta de tipo: typeof

Control de desbordamiento activo:
checked

Control de desbordamiento inactivo:
unchecked

Unarios
Valor positivo: +

Valor negative: -

No: !

Complemento a nivel de bit: ~

Pre-incremento: ++x

Post-decremento: --x

Conversin de tipo -cast-: (T)x

Multiplicativos
Multiplicacin: *

Divisin: /

Resto: %

Aditivos
Suma: +

Resta: -

Desplazamiento
Desplazamiento de bits a la izquierda: <<

Desplazamiento de bits a la derecha: >>

Relacionales
Menor: <

Mayor: >

Menor o igual: <=

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 se evala como x = (y = z)

x + y + z se evala como (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) if (a > 0) {
Console.WriteLine ("Positivo"); Console.WriteLine
("Positivo");
ContPositivos++;
}
Si se acta cuando la condicin es falsa se emplea la palabra reservada else:
if (a > 0) if (a > 0) {
Console.WriteLine ("Positivo"); Console.WriteLine
("Positivo");
else ContPositivos++;
Console.WriteLine ("No Positivo"); }
else {
Console.WriteLine("No Positivo");
ContNoPositivos++;
}
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("")); // 0
Console.WriteLine (Test("A")); // 2
Console.WriteLine (Test("B")); // 1
Console.WriteLine (Test("C")); // 1
Console.WriteLine (Test("?")); // 0
Console.ReadLine();
}
}
}
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) for (int i=0; i < v.Length; i++)
i *= 2; 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

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# Tipo en System Caractersticas Smbolo
sbyte System.Sbyte entero, 1 byte con signo

byte System.Byte entero, 1 byte sin signo

short System.Short entero, 2 bytes con signo

ushort System.UShort entero, 2 bytes sin signo

int System.Int32 entero, 4 bytes con signo

uint System.UInt32 entero, 4 bytes sin signo U
long System.Int64 entero, 8 bytes con signo L
ulong System.ULong64 entero, 8 bytes sin signo UL
float System.Single real, IEEE 754, 32 bits F
double System.Double real, IEEE 754, 64 bits D
decimal System.Decimal
real, 128 bits (28 dgitos
significativos)
M
bool System.Boolean (Verdad/Falso) 1 byte

char System.Char Carcter Unicode, 2 bytes
string System.String
Cadenas de caracteres
Unicode
" "
object System.Object
Cualquier objeto (ningn
tipo concreto)

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 Explcitas
Ocurren automticamente Requieren un casting
Siempre tienen xito Pueden fallar
No se pierde informacin Se puede perder informacin
int x = 123456;
long y = x; // implicita
short z = (short) x; // explicita (riesgo)

float f1 = 40.0F;
long l1 = (long) f1; // explicita (riesgo por redondeo)
short s1 = (short) l1; // explicita (riesgo por desbordamiento)
int i1 = s1; // implicita, no hay riesgo
uint i2 = (uint) i1; // explicita (riesgo de error por signo)
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 unboxing-
Boxing (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 s1 = "Esto es una cadena... como en C++";
string s2 = "Esto es una cadena... ";
string s3 = "como en C++";
string s4 = s2 + s3;
string s5 = 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; // p.x==102
p3.x = px; // p3.x==102

p2 = p; // p2.x==102, p2.y==5
p2.x += 100; // p2.x==202, p2.y==5
p3.y = p.y + p2.y; // 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); // 0
Console.WriteLine(c1); // Black
Console.WriteLine(c1.ToString()); // 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
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...
public desde cualquier sitio
private (por
defecto)
slo desde dentro de T (por defecto)
protected desde T y los tipos derivados de T
internal desde los tipos incluidos en A
protected internal
desde T, los tipos derivados de T y los
tipos incluidos en A
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 version = "1.0.0";
public const string s1 = "abc" + "def";
public const int i3 = 1 + 2;
public const double PI_I3 = i3 * Math.PI;
public const double 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 Print(int i);
void Print(string s);
void Print(char c);
void Print(float f);

int Print(float f); // Error: Signatura duplicada
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 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);
Console.WriteLine ("p4 es: ({0},{1})", p4.X, p4.Y);
Console.WriteLine ("p5 es: ({0},{1})", p5.X, p5.Y);

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 e1 = (Euro) d1; // Conversin explcita a "Euro"
Euro e2 = (Euro) d2; // Conversin explcita a "Euro"

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 Struct
Tipo referencia Tipo valor
Puede heredar
de otro tipo (que
no est
"sellado")
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.
Puede tener un
constructor sin
parmetros
No puede tener un constructor sin
parmetros
No pueden
crearse
instancias sin
emplear el
operador new.
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.
Puede tener un
destructor
No puede tener destructor

Herencia

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(); // Const. sin parmetros de D
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)
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 ampla la especificacin de la clase Point introducida
anteriormente.
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 Main(string[] args)
{
Clase1 c1 = new Clase1();
Clase2 c2 = new Clase2();
Clase3 c3 = 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 = f1; Console.WriteLine (f(0)); // 0
f = f2; Console.WriteLine (f(0)); // 1
f = 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
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 b = iderivada: Type is {0}",
b.GetType());
Console.ReadLine();
}
}

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
Browsable
Propiedades y eventos que deben mostrarse en
el inspector de objetos.
Serializable
Clases y estructuras que pueden "serializarse"
(esto es, volcarse en algn dispositivo de salida,
p.ej. disco), como en Java.
Obsolete
El compilador se quejar si alguien los utiliza
(deprecated en Java).
ProgId COM Prog ID
Transaction Caractersticas transaccionales de una clase.
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(); // Avisos
a.F();
...
}
}

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 (" Informacin de los mtodos:");

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

}
Console.ReadLine();
}
}





Programas en C#

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 es implcito (N1.C1)
N1.C1 o1_bis; // Tipo totalmente cualificado

//C2 c; // Error! C2 no est definida en N1
C1.C2 o2; // Idem a: N1.C1.C2 o2;
N1.N2.C2 o3; // Tipo totalmente cualificado
N2.C2 o3_bis; // 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; // O sea, N1.N2.C2
NS1.C2 ob2; // 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 // Clase (struct) no segura
{ ... } // Todos los miembros son no seguros

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
#define, #undef
Definicin de smbolos para la compilacin
condicional.
#if, #elif, #else,
#endif
Compilacin condicional.
#error, #warning Emisin de errores y avisos.
#region, #end Delimitacin de regiones.
#line Especificacin de nmeros de lnea.
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
<summary> Descripcin breve de tipos y miembros.
<remarks> Descripcin detallada de tipos y miembros.
<para> Delimita prrafos.
<example> Ejemplo de uso.
<see>
<seealso>
Referencias cruzadas. Usa el atributo cref
<c> <code> Cdigo de ejemplo (verbatim).
<param>
Parmetros de mtodos. Usa el atributo
name.
<paramref>
Referencia a parmetros de metodos. Usa el
atributo name.
<returns> Valor devuelto por el mtodo.
<exception> Descripcion de Excepciones.
<value> Descripcin de propiedades.
<list>
Generar listas. Usa el atriibuto (opcional)
type.
<item>
Generar listas. Usa el atriibuto (opcional) type
(puede ser: bullet, number o table).
<permission> Permisos.
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
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.
La biblioteca de clases de la plataforma .NET

Esta seccin describe algunas clases e interfaces de la plataforma .NET con el
objetivo de que el lector sea capaz de utilizarlas al desarrollar sus propios
programas.
Antes de que existiese la plataforma .NET, cada lenguaje de programacin tena
su propia biblioteca de clases, lo que provocaba que no todos los lenguajes
dispusiesen de la misma funcionalidad (p.ej. algunos APIs no estaban soportados
en Visual Basic, por lo que haba que recurrir a C/C++ para desarrollar
determinados tipos de aplicaciones). Adems, la funcionalidad proporcionada por
distintas clases estaba repartida en componentes COM/COM+, controles ActiveX,
DLLs del sistema... lo cual dificultaba su organizacin (adems de hacer casi
imposible la implementacin de extensiones de las clases disponibles).
La plataforma .NET incluye una coleccin de clases bien organizada cuya
parte independiente del sistema operativo ha sido propuesta para su
estandarizacin (http://msdn.microsoft.com/net/ecma/).
La biblioteca de clases de la plataforma .NET integra todas las tecnologas
Windows en un marco nico para todos los lenguajes de programacin
(Windows Forms, GDI+, Web Forms, Web Services, impresin, redes...).
La biblioteca de clases .NET proporciona un modelo orientado a objetos
que sustituye a los componentes COM.
System
El espacio de nombres System incluye la clase base para todos los objetos
(System.Object), los tipos de datos primitivos y algunos no tan primitivos (cadenas,
textos, fechas, horas y calendarios), adems de un conjunto de interfaces
estndar y soporte para la E/S a travs de consola.
System.Object
La clase System.Object es la clase base para todos los tipos de la plataforma
.NET. Cualquier tipo hereda implcitamente de esta clase, de forma que se unifica
el sistema de tipos. Esto facilita que las colecciones se puedan usar para
almacenar cualquier cosa y es menos propenso a errores que el tipo VARIANT
utilizado en COM (al ser un sistema fuertemente tipificado).
Mtodos de System.Object
System.Object.Equals(Object o) comprueba si dos objetos son iguales.
System.Object.ReferenceEquals(Object o) comprueba si dos referencias
apuntan al mismo objeto.
System.Object.Finalize() es invocado por el recolector de basura para
liberar el objeto.
System.Object.GetHashCode() se emplea en la implementacin
colecciones de objetos (del tipo System.Collections.HashTable) y debe
implementarse en las subclases de System.Object para crear buenas
funciones hash (por defecto, se utiliza la identidad del objeto).
System.Object.GetType() devuelve el tipo de un objeto (punto de entrada
para el sistema de reflexin de la plataforma .NET).
System.Object.MemberwiseClone() crea un clon del objeto utilizando la
capacidad de reflexin de la plataforma .NET
System.Object.ToString() devuelve una representacin textual del objeto.
Por defecto, devuelve el nombre de la clase, por lo que deberemos
implementarlo nosotros. No est diseado para mostrar mensajes al
usuario (para esa funcin se debera utilizar el interfaz IFormattable)
public class Person
{
String name;

public override string ToString()
{
return name;
}
}

...

Name n1 = new Name("Fred");
Name n2 = new Name("Fred");
Name n3 = n2; // n2 & n3 apuntan al mismo objeto

if (n1 == n2) ... // false
if (n2 == n3) ... // true
if (n1.Equals(n2)) ... // true
if (n2.Equals(n3)) ... // true
Tipos primitivos
Se elimina la distincin entre los tipos primitivos del lenguaje y los dems objetos
(al estilo de Smalltalk). Los tipos primitivos son comunes para toda la plataforma
.NET, si bien se muestran en cada lenguaje segn su sintaxis:
C#: bool, int, long, string, double, float...
Visual Basic.NET: Boolean, Integer, String...
Algunos tipos primitivos de inters son: System.Byte, System.Char,
System.Boolean (valores lgicos: true o false), System.Guid (identificador
universal de 128 bits, que adems incluye un generador System.Guid.NewGuid())
y los valores nulos (System.DBNull para valores NULL en bases de datos,
System.Empty para representar el valor VT_EMPTY en COM y System.Missing
para trabajar con parmetros opcionales).
Cadenas de caracteres
La clase System.String es la utilizada en todos los lenguajes de la plataforma .NET
para representar cadenas de caracteres, que se almacenan utilizando UNICODE.
El tipo System.String es inmutable, por lo que los mtodos que parecen modificar
una cadena en realidad lo que hacen es crear una nueva. La inmutabilidad de los
objetos de este tipo hace recomendable el uso de String.Format o StringBuilder
para manipular cadenas de caracteres (como en Java).
La clase String incluye mtodos 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(),
Split()
Modificar cadenas de caracteres: ToLower(), ToUpper(), Format() (al estilo
del printf de C, pero seguro).
Comparacin de cadenas
public static int Main()
{
string s = "abc";
string s1 = s; // s1 y s hacen referencia al mismo objeto
string s2 = "abc";

if (s1 == s2)
Console.WriteLine("OK");
else
Console.WriteLine("Esto no debera pasar");

return 0
}
Fechas
System.DateTime permite representar fechas (100 d.C. - 9999 d.C.) y
realizar operaciones aritmticas con ellas, as como darles formato y leerlas
de una cadena en funcin de la configuracin local.
System.TimeSpan sirve para trabajar con duraciones de tiempo (que se
pueden expersar en distintas unidades).
System.TimeZone permite trabajar con husos horarios.
Consola
La clase System.Console proporciona la funcionalidad bsica de E/S (equivalente
a stdin, stdout y stderr en C). Para escribir se puede utilizar Write() o WriteLine()
empleando la sintaxis de String.Format, mientras Read() lee un caracter y
ReadLine() lee una lnea completa.
Console.Write("Snow White and the {0} dwarfs", 7);
Clases tiles de System
Clase Funcin
System.URI Identificadores universales de recursos
System.Random Generador de nmeros aleatorios
System.Convert Conversiones de tipos bsicos
Interfaces de System
IFormattable
El interfaz IFormattable nos permite dar formato a la representacin del valor de
un objeto:
interface IFormattable
{
String Format(String format, IServiceObjectProvider sop);
}
El mtodo Format da formato al valor de la instancia actual tal como se le
especifique:
String.Format("Please order {0} widgets at {1} each.", i, f);
String.Format("{0:U}", DateTime.Now);
El parmetro IServiceProvider se puede emplear para obtener caractersticas
especiales de la configuracin local (p.ej. delimitadores para nmeros y fechas).
Incluso se puede implementar el interfaz ICustomFormatter para redefinir el
formato en el que se muestran los valores de los tipos predefinidos.
IDisposable
Este interfaz nos permite controlar explcitamente la liberacin de recursos:
class ResourceWrapper : IDisposable
{
private IntPrt handle; // Puntero a un recurso externp
private OtherResource otherRes;

bool disposed = false;

private void free ()
{
if (!disposed) {
CloseHandle (handle);
dispose = true;
}
}

public void Dispose
{
free();
OtherRes.Dispose();
GC.Suppress.Finalization(this);
}

public void Finalize ()
{
free();
Base.Finalize();
}
}
Colecciones
Arrays
Los arrays, representados mediante la clase System.Array, constituyen la nica
coleccin de datos que queda fuera del espacio de nombres System.Collections.
La clase System.Array corresponde a los arrays en cualquier lenguaje de
programacin de la plataforma .NET y sirven para almacenar objetos de forma
polimrfica (esto es, sirven para almacenar instancias de cualquier clase que
herede de System.Object [todas]).
Los arrays pueden tener un nmero arbitrario de dimensiones y su tamao se
especifica al crearlos (CreateInstance). Una vez creado el array, su tamao es fijo.
Entre sus cualidades ms destacadas destaca el hecho de que los arrays pueden
ordenarse (siempre y cuando se comparen objetos que implementen el interfaz
IComparable o se indique un comparador IComparer). Adems, si el array est
ordenado, se pueden realizar bsquedas binarias en l.
public static void Main()
{
// Creacin e inicializacin arrays
int[] intArray = new int[5] { 1, 2, 3, 4, 5 };
Object[] objArray = new Object[5] { 26, 27, 28, 29, 30 };

// Copia de los dos primeros elementos de intArray en
objArray
Array.Copy( intArray, objArray, 2 );

// Volcado en consola de los valores actuales de los arrays
Console.Write( "intArray: " ); PrintValues( intArray );
Console.Write( "objArray: " ); PrintValues( objArray );

// Copia de los dos ltimos elementos de objArray en
intArray
Array.Copy( objArray, objArray.GetUpperBound(0) - 1,
intArray, intArray.GetUpperBound(0) - 1, 2 );
}


Interfaces
Aparte de los arrays, la plataforma .NET tambin suministra una serie de
interfaces estndar para la implementacin de colecciones de objetos de distintos
tipos (similar a las colecciones de Java).
IEnumerable & IEnumerator
El interfaz System.Collections.IEnumerable permite iterar sobre una coleccin de
datos, proporcionando un mecanismo estndar para todas las colecciones:
public interface IEnumerable
{
IEnumerator GetEnumerator();
}
El mtodo GetEnumerator() devuelve un iterador que implementa el interfaz
System.Collections.IEnumerator:
public interface IEnumerator
{
Boolean MoveNext();
Object Current { get; }
void Reset();
}
SetType st = new SetType(...);

// Se obtiene un iterador para enumerar los elementos de la
coleccin

IEnumerator e = st.GetEnumerator();

// Se recorre la coleccin utilizando el iterador como cursor

while (e.MoveNext()) {
// Se lee un elemento de la coleccin
ItemType it = (ItemType) e.Current;
// Se emplea el elemento para hacer lo que haga
falta
Console.WriteLine(it);
}
ICollection
El interfaz System.Collections.ICollection se deriva de IEnumerable y proporciona
un interfaz bsico para todas las colecciones: Count(), CopyTo(), IsSynchronized()
IList
El interfaz System.Collections.IList tambin deriva de ICollection y permite trabajar
con listas. Proporciona un indexador Item para acceder a los elementos de la lista
en funcin de su posicin y una serie de mtodos para trabajar con el contenido
de la lista: Add(), Remove(), Contains(), Clear()
IDictionary
El interfaz System.Collections.IDictionary especializa el interfaz ICollection y se
utiliza para implementar diccionarios (conjuntos de pares clave-valor, a modo de
memoria asociativa). Este interfaz proporciona un indexador Item para consultar
un valor dada su clave y los mismos mtodos que IList para trabajar con el
diccionario: Add(), Remove(), Contains(), Clear() .
Clases
Aparte de los interfaces estndar que sirven como base para la implementacin de
colecciones, la biblioteca de clases de la plataforma .NET tambin incluye algunas
colecciones ya implementadas:
ArrayList
La clase System.Collections.ArrayList implementa el interfaz IList para
proporcionar arrays dinmicos cuyo tamao puede variar dinmicamente (a
diferencia de System.Array, que mantiene fijo su tamao una vez que ha sido
creado).
using System;
using System.Collections;

public class SampleArrayList
{
public static void Main()
{
// Creacin
ArrayList myAL = new ArrayList();

// Inicializacin
myAL.Add("Hello");
myAL.Add("World");
myAL.Add("!");

// Visualizacin
Console.WriteLine( "myAL" );
Console.WriteLine( "\tCount: {0}", myAL.Count );
Console.WriteLine( "\tCapacity: {0}", myAL.Capacity );

Console.Write( "\tValues:" );
PrintValues( myAL );
}

public static void PrintValues( IEnumerable myList )
{
IEnumerator myEnumerator = myList.GetEnumerator();

while ( myEnumerator.MoveNext() )
Console.Write("\t{0}", myEnumerator.Current );

Console.WriteLine();
}
}
BitArray
La clase System.Collections.BitArray proporciona una implementacin eficiente y
compacta de arrays de bits.
HashTable
La clase System.Collections.HashTable implementa un diccionario (IDictionary)
mediante una tabla hash.
SortedList
La clase System.Collections.SortedList implementa una lista ordenada sin
duplicados que puede indexada por enteros (posicin) y por cadenas (contenido).
Stack
La clase System.Collections.Stack implementa una pila LIFO con sus mtodos
Push() y Pop(). Al implementar el interfaz IEnumerable, podemos enumerar sus
elementos:
using System;
using System.Collections;

public class SampleStack
{
public static void Main()
{
Stack myStack = new Stack();

myStack.Push("Hello");
myStack.Push("World");
myStack.Push("!");

Console.WriteLine( "myStack" );
Console.WriteLine( "\tCount: {0}", myStack.Count );
Console.Write( "\tValues:" );
PrintValues( myStack );

Console.Write( "\tLIFO:" );
EmptyStack ( myStack );
}

public static void PrintValues( IEnumerable myCollection )
{
IEnumerator myEnumerator =
myCollection.GetEnumerator();

while ( myEnumerator.MoveNext() )
Console.Write( "\t{0}", myEnumerator.Current );

Console.WriteLine();
}

public static void EmptyStack( Stack myStack )
{
object value = myStack.Pop();

while (value!=null) {
Console.Write ("\t{0}", value);
value = myStack.Pop();
}

Console.WriteLine();
}
}
Queue
La clase System.Collections.Queue implementa una cola FIFO con sus mtodos
Enqueue() y Dequeue(). Igual que sucede con las pilas, las colas tambin son
enumerables:
using System;
using System.Collections;

public class SampleQueue
{
public static void Main()
{
Queue myQ = new Queue();

myQ.Enqueue("Hello");
myQ.Enqueue("World");
myQ.Enqueue("!");

Console.WriteLine( "myQ" );
Console.WriteLine( "\tCount: {0}", myQ.Count );
Console.Write( "\tValues:" );
PrintValues( myQ );

Console.Write( "\tFIFO:" );
EmptyQueue (myQ);
}

public static void PrintValues ( Ienumerable myCollection )
{
IEnumerator myEnumerator =
myCollection.GetEnumerator();

while ( myEnumerator.MoveNext() )
Console.Write( "\t{0}", myEnumerator.Current );

Console.WriteLine();
}

public static void EmptyQueue( Queue myQ )
{
object value = myQ.Dequeue();

while (value!=null) {
Console.Write ("\t{0}", value);
value = myQ.Dequeue();
}

Console.WriteLine();
}
}
Entrada / Salida
Ficheros y directorios
La biblioteca de clases de la plataforma .NET proporciona una serie de clases que
nos permiten trabajar con el sistema de archivos:
System.IO.Directory y System.IO.File proporcionan mtodos estticos para
manipular directorios y ficheros, respectivamente.
System.IO.DirectoryInfo Y System.IO.FileInfo incluyen mtodos para
manipular instancias de directorios y ficheros, respectivamente.
System.IO.DirectoryInfo representa un directorio concreto, a partir del cual
se pueden obtener sus subdirectorios (con GetDirectories([mask])) y los
ficheros que incluye (con GetFiles([mask]). Por su parte, System.IO.FileInfo
representa un fichero concreto, que se puede obtener directamente
especificando su path o a partir de la enumeracin de ficheros de un
directorio con GetFiles().
Una vez que tenemos un fichero con el que trabajar, utilizamos uno de sus
mtodos Open... para poder acceder y modificar su contenido: Open(),
OpenRead(), OpenWrite(), OpenText(). Cualquiera de los mtodos anteriores
devuelve una instancia de System.IO.Stream.
Streams
La clase base de los streams en la plataforma .NET es la clase abstracta
System.IO.Stream, que proporciona funciones de acceso sncrono (Read(),
Write()) y asncrono (BeginRead(), BeginWrite(), EndRead(), EndWrite()).
System.IO.FileStream se utiliza para acceder directamente al contenido de
ficheros. De hecho es el tipo devuelto por una llamada a File.Open().
System.IO.MemoryStream permite construir streams en memoria.
Lectura
Los stream readers proporcionan acceso de lectura a un stream:
System.IO.BinaryReader permite leer datos en binario: ReadInt16(),
ReadBoolean(), ReadDouble(), etc.
System.IO.TextReader es una clase abstracta utilizada como base comn
de las dos siguientes:
System.IO.StreamReader hereda de TextReader e implementa los mtodos
ReadLine() para leer una lnea de texto y ReadToEnd() para leer lo que
quede de stream.
System.IO.StringReader tambin hereda de TextReader y se utiliza para
simular streams a partir de cadenas de caracteres.

Lectura de un fichero de texto
static void Main(string[] args)
{
string filename = @"../../AssemblyInfo.cs";
string line;

Console.WriteLine ("*****************************");

StreamReader stream = new StreamReader(filename);
// StreamReader stream = File.OpenText (filename);

do {
line = stream.ReadLine();
Console.WriteLine (line);
} while (line != null);

stream.Close();

Console.WriteLine ("*****************************");

Console.ReadLine();
}
Escritura
Los stream writers sirven para escribir en streams:
System.IO.BinaryWriter nos permite escribir datos en binario, mediante la
utilizacin del mtodo sobrecargado Write().
System.IO.TextWriter es la clase abstracta que sirve de base para
StreamWriter y StringWriter.
System.IO.StreamWriter hereda de TextWriter y sirve para escribir cadenas
de texto en streams.
System.IO.StringWriter tambin hereda de TextWriter y simula streams en
cadenas de caracteres.
public class MyWriter
{
private Stream stream;

public MyWriter(Stream stream)
{
this.stream = stream;
}

// Almacena la representacin binaria de un double

public void WriteDouble(double myData)
{
byte[] b = myData.GetBytes();
stream.Write(b,0,b.Length);
}

public void Close()
{
stream.Close();
}
}
static void Main(string[] args)
{
string filenameI = @"../../AssemblyInfo.cs";
string filenameO = @"../../COPIA_AssemblyInfo.cs";
string line;

StreamReader streamI = new StreamReader(filenameI);
StreamWriter streamO = new StreamWriter(filenameO);

while ((line = streamI.ReadLine()) != null) {
streamO.WriteLine (line);
}
streamI.Close();
streamO.Close();
}
System.Net
El espacio de nombres System.Net contiene todas las clases relativas al uso de
protocolos de red para transmitir datos. Proporciona todo lo necesario para utilizar
los protocolos de red IP (sockets) e IPX, as como otros protocolos de aplicacin
(p.ej. HTTP, incluidos sus mecanismos de autentificacin [Basic, Digest, NTLM
Challenge/Reponse] y el uso de cookies).
HTTP
La clase abstracta System.Net.WebRequest es la clase base para el uso de
protocolos de red como HTTP. Esta clase sirve de punto de acceso comn para
distintos protocolos. Adems, permite registrar nuevos protocolos mediante el
mtodo RegisterPrefix().
La clase HttpWebRequest da soporte a los protocolos HTTP y HTTPS. El
contenido de la solicitud HTTP[S] se rellena con un stream
(WebRequest.GetRequestStream()) y la solicitud se ejecuta con GetResponse().
Los datos devueltos tras ejecutar GetResponse() se pueden leer mediante el
mtodo WebResponse.GetReponseStream().
HttpWebRequest HttpWReq =
(HttpWebRequest)
WebRequestFactory.Create("http://elvex.ugr.es");

HttpWebResponse HttpWResp =
(HttpWebResponse)HttpWReq.GetResponse();
SMTP
MailMessage MyMessage = new MailMessage();
MyMessage.To = "berzal@acm.org";
MyMessage.From = "MyApplication";
MyMessage.Subject = "Unhandled Error!!!";
MyMessage.BodyFormat = MailFormat.Html;
MyMessage.Body = "<html><body><h1> ERROR
</h1></body></html>";
SmtpMail.Send(MyMessage);
Serializacin
La serializacin es el proceso de convertir un objeto, o un grafo conexo de objetos,
en una secuencia de bytes.
using System;
using System.IO;
using System.Collections;
using System.Serialization;
using System.Serialization.Formatters.Binary;

class SerializeExample
{
public static void Main(String[] args)
{
ArrayList l = new ArrayList();

for (int x=0; x < 100; x++)
l.Add (x);

FileStream s = File.Create("foo.bin");
BinaryFormatter b = new BinaryFormatter();

b.Serialize(s, l);
}
}
using System;
using System.IO;
using System.Collections;
using System.Serialization;
using System.Serialization.Formatters.Binary;

class DeserializeExample
{
public static void Main(String[] args)
{
FileStream s = File.Open("foo.bin");
BinaryFormatter b = new BinaryFormatter();

ArrayList p = (ArrayList) b.Deserialize(s);
p.ToString();
}
}
Un tipo no es serializable salvo que se marque especficamente con el atributo
Serializable:
[Serializable] public class MyClass {}

[Serializable] public class MyClass
{
[NotSerialized] int _size;
}
El proceso de serializacin puede personalizarse si implementamos el interfaz
ISerializable, el interfaz IDeserializationEventListener o creamos nuestros propios
"formateadores" (custom formatters).
ISerializable
Un nico mtodo:
void GetObjectData (SerializationInfo info,
StreamingContext context);
y un constructor que puede ser privado:
private <T> (SerializationInfo info,
StreamingContext context)
IFormatter
public interface IFormatter
{
// Propiedades
SerializationBinder Binder { get; set; }
StreamingContext Context { get; set; }
ISurrogateSelector SurrogateSelector { get; set; }

// Mtodos
object Deserialize(Stream serializationStream);
void Serialize(Stream serializationStream, object graph);
}

Acceso a bases de datos con ADO.NET

Bases de datos
Cualquier aplicacin de inters requiere el almacenamiento y posterior
recuperacin de los datos con los que trabaje (pedidos en aplicaciones de
comercio electrnico, datos de personal para las aplicaciones de recursos
humanos, datos de clientes en sistemas CRM, etc.). Los sistemas de gestin de
bases de datos (DBMSs) nos permiten almacenar, visualizar y modificar datos, as
como hacer copias de seguridad y mantener la integridad de los datos,
proporcionando una serie de funciones que facilitan el desarrollo de nuevas
aplicaciones.
Desde un punto de vista intuitivo, una base de datos no es ms que un fondo
comn de informacin almacenada en una computadora para que cualquier
persona o programa autorizado pueda acceder a ella, independientemente de su
lugar de procedencia y del uso que haga de ella. Algo ms formalemente, una
base de datos es un conjunto de datos comunes a un "proyecto" que se
almacenan sin redundancia para ser tiles en diferentes aplicaciones.
El Sistema de Gestin de Bases de Datos (DBMS) es el software con capacidad
para definir, mantener y utilizar una base de datos. Un sistema de gestin de
bases de datos debe permitir definir estructuras de almacenamiento, as como
acceder a los datos de forma eficiente y segura. Ejemplos: Oracle, IBM DB2,
Microsoft SQL Server, Interbase...
En una base de datos, los datos se organizan independientemente de las
aplicaciones que los vayan a usar (independencia lgica) y de los ficheros en los
que vayan a almacenarse (independencia fsica). Adems, los datos deben ser
accesibles a los usuarios de la manera ms amigable posible, generalmente
mediante lenguajes de consulta como SQL o Query-by-example. Por otro lado, es
esencial que no exista redundancia (esto es, los datos no deben estar duplicados)
para evitar problemas de consistencia e integridad.
Bases de datos relacionales
Tabla o relacin: Coleccin de registros acerca de entidades de un tipo
especfico (p.ej. alumnos).
Atributo, campo o columna: Propiedad asociada a una entidad (p.ej.
nombre, apellidos...). Cada atributo tiene un tipo asociado (p.ej. entero,
cadena de caracteres...) y puede tomar el valor nulo (null).
Tupla, registro o fila: Datos relativos a un objeto distinguible de otros (p.ej.
un alumno concreto).
Se pueden estrablecer relaciones entre las tablas de una base de datos relacional
mediante el uso de claves primarias y claves externas (p.ej. cada libro tiene, al
menos, un autor).
Clave primaria: Conjunto de atributos que nos permiten identificar
unvocamente a una entidad dentro de un conjunto de entidades (p.ej.
nmero de matrcula). La clave primaria garantiza la unicidad de una tupla,
pues no se permite la existencia de varios registros que compartan su clave
primaria.
Clave externa: Conjunto de atributos que hacen referencia a otra tabla, lo
que nos permite establecer relaciones lgicas entre distintas tablas. Los
valores de los atributos de la clave externa han de coincidir con los valores
de los atributos de una clave (usualmente la primaria) en una de las tuplas
de la tabla a la que se hace referencia (integridad referencial).

SQL
Lenguaje estndar para acceder a una base de datos relacional, estandarizado
por el American National Standards Institute (ANSI): SQL-92. En gran parte, los
distintos DBMS utilizan todos el mismo SQL, si bien cada vendedor le ha aadido
sus propias extensiones. El lenguaje SQL se divide en:
DDL (Data Definition Language), utilizado para crear y modificar la
estructura de la base de datos (p.ej. CREATE TABLE).
DML (Data Manipulation Language), empleado para manipular los datos
almacenados en la base de datos (p.ej. consultas con la sentencia
SELECT).
DCL (Data Control Language), para establecer permisos de acceso
(GRANT, REVOKE, DENY) y gestionar transacciones (COMMIT y
ROLLBACK).
Interfaces de acceso a bases de datos
Evolucin histrica de los "estndares" propuestos por Microsoft:
ODBC (Open Database Connectivity): API estndar ampliamente
utilizado, disponible para mltiples DBMSs, utiliza SQL para acceder a los
datos.
DAO (Data Access Objects): Interfaz para programar con bases de datos
JET/ISAM, utiliza automatizacin OLE y ActiveX.
RDO (Remote Data Objects): Fuertemente acoplado a ODBC, orientado al
desarrollo de aplicaciones cliente/servidor.
OLE DB: Construido sobre COM, permite acceder a bases de datos tanto
relacionales como no relacionales (no est restringido a SQL). Se puede
emplear con controladores ODBC y proporciona un interfaz a bajo nivel en
C++.
ADO (ActiveX Data Objects): Ofrece un interfaz orientado a objetos y
proporciona un modelo de programacin para OLE DB accesible desde
lenguajes distintos a C++ (p.ej. Visual Basic).

ADO se dise para su uso en arquitecturas cliente/servidor con bases de datos
relacionales (no jerrquicas, como es el caso de XML). Su diseo no est
demasiado bien factorizado (ya que existen muchas formas de hacer las cosas y
algunos objetos acaparan demasiadas funciones) y ADO no estaba pensado para
arquitecturas multicapa en entornos distribuidos.
ADO .NET es una coleccin de clases, interfaces, estructuras y tipos enumerados
que permiten acceder a los datos almacenados en una base de datos desde la
plataforma .NET. Si bien se puede considerar una versin mejorada de ADO, no
comparte con ste su jerarqua de clases (aunque s su funcionalidad).
ADO .NET combina las capas ADO y OLE DB en una nica capa de proveedores
(managed providers). Cada proveedor contiene un conjunto de clases que
implementan interfaces comunes para permitir el acceso uniforme a distintas
fuentes de datos. Ejemplos: ADO Managed Provider (da acceso a cualquier fuente
de datos OLE DB), SQL Server Managed Provider (especfico para el DBMS de
Microsoft), Exchange Managed Provider (datos almacenados con Microsoft
Exchange)...

ADO .NET usa XML. De hecho, los conjuntos de datos se almacenan
internamente en XML, en vez de almacenarse en binario como suceda en ADO.
Al estar los datos almacenados en XML, se simplifica el acceso a los datos a
travs de HTTP (algo que ocasiona problemas en ADO si los datos tienen que
pasar cortafuegos). Por otro lado, se simplifica la comunicacin entre aplicaciones
al ser XML un formato estndar (p.ej. comunicacin con applets Java).
Con ADO .NET se puede acceder a los datos de dos formas distintas:
Acceso conectado: Acceso slo de lectura con cursores unidireccionales
("firehose cursors"). La aplicacin realiza una consulta y lee los datos
conforme los va procesando con la ayuda de un objeto DataReader.
Acceso desconectado: La aplicacin ejecuta la consulta y almacena los
resultados de la misma para procesarlos despus accediendo a un objeto
de tipo DataSet. De esta forma, se minimiza el tiempo que permanece
abierta la conexin con la base de datos.
Al proporcionar conjuntos de datos de forma desconectada, se utilizan mejor los
recursos de los servidores y se pueden construir sisyemas ms escalables que
con ADO (que mantena abierta la conexin con la base de datos la mayor parte
del tiempo). Este enfoque resulta ms adecuado en sistemas distribuidos como
Internet.
Arquitectura ADO.NET
El funcionamiento de ADO.NET se basa esencialmente en utilizar los siguientes
componentes:
Data Provider (proveedor de datos): Proporciona un acceso uniforme a
conjuntos de datos (bases de datos relacionales o informacin ID3 de
ficheros MP3). Su papel el similar al de un controlador ODBC o JDBC.
DataSet: El componente ms importante, puede almacenar datos
provenientes de mltiples consultas (esto es, mltiples tablas).
DataAdapter: Sirve de enlace entre el contenedor de conjuntos de datos
(DataSet) y la base de datos (Data Provider).
Los componentes anteriores se completan con DataReader (para realizae
eficientemente lecturas de grandes cantidades de datos que no caben en
memoria), DataRelation (la forma de establecer una reunin entre dos tablas),
Connection (utilizada por DataAdapter para conectarse a la base de datos) y
Command (que permite especificar las rdenes, generalmente en SQL, que nos
permiten consultar y modificar el contenido de la base de datos: select, insert,
delete y update).
Un proveedor de datos debe proporcionar una implementacin de Connection,
Command, DataAdapter y DataReader.
El modo de funcionamiento tpico de ADO.NET es el siguiente:
Se crean un objeto Connection especificando la cadena de conexin.
Se crea un DataAdapter.
Se crea un objeto Command asociado al DataAdapter, con la conexin
adecuada y la sentencia SQL que haya de ejecutarse.
Se crea un DataSet donde almacenar los datos.
Se abre la conexin.
Se rellena el DataSet con datos a travs del DataAdapter.
Se cierra la conexin.
Se trabaja con los datos almacenados en el DataSet.
Como los conjuntos de datos se almacenan en memoria y trabaja con ellos de
forma desconectada, cuando hagamos cambios sobre ellos (inserciones, borrados
o actualizaciones) debemos actualizar el contenido de la base de datos llamando
al mtodo Update del DataAdapter y, posteriormente, confirmar los cambios
realizados en el DataSet (con AcceptChanges) o deshacerlos (con
RejectChanges).
Clases ADO.NET
ADO .NET define una serie de interfaces que proporcionan la funcionalidad bsica
comn a las distintas fuentes de datos accesibles a travs de ADO .NET. La
implementacin de estos interfaces por parte de cada proveedor proporciona
acceso a un tipo concreto de fuentes de datos y puede incluir propiedades y
mtodos adicionales.
Interfaz IDbConnection
Establece una sesin con una fuente de datos. Permite abrir y cerrar conexiones,
as como comenzar transacciones (que se finalizan con los mtodos Commit y
Rollback de IDbTransaction. Las clases SqlDbConnection y OleDbConnection
implementan el interfaz de IDbConnection.


Interfaz IDbCommand
Representa una sentencia que se enva a una fuente de datos (usualmente en
SQL, aunque no necesariemente). Las clases SqlDbCommand y OleDbCommand
implementan el interfaz de IDbCommand.
IDbCommand nos permite definir la sentencia que se ha de ejecutar, ejecutar la
sentencia, pasarle parmetros y prepararla (crear una versin "compilada" de la
misma para que su ejecucin sea ms eficiente cuando ha de repetirse varias
veces). El mtodo ExecuteReader devuelve un conjunto de tuplas (vase el
interfaz IDataReader), mientras que ExecuteScalar devuelve un valor nico (p.ej.
ejecucin de procedimientos almacenados) y ExecuteNonQuery no devuelve nada
(p.ej. borrados y actualizaciones).
string connectionString = "Persist Security Info=False;" +
"User ID=sa;Initial Catalog=MYDB;" +
"Data Source=MYSERVER";

SqlConnection connection = new
SqlConnection(connectionString);

// Ejecucin de sentencias SQL
// ---------------------------

string sqlInsert = "INSERT INTO
Department(DepartmentName) VALUES
(@DepartmentName)";

SqlCommand insertCommand = new SqlCommand(sqlInsert,
connection);


SqlParameter param = insertCommand.Parameters.Add (
new SqlParameter("@DepartmentName",
SqlDbType.VarChar, 100));

param.Value = ...

connection.Open();

insertCommand.ExecuteNonQuery();

connection.Close();


// Llamadas a procedimientos almacenados
// -------------------------------------

// C#

string spName = "CREATE_DEPARTMENT"

SqlCommand command = new SqlCommand(spName,
connection);

command.CommandType =
CommandType.StoredProcedure;
SqlParameter in = command.Parameters.Add (
new SqlParameter("@DepartmentName",
SqlDbType.VarChar, 100));
in.Value = ...

SqlParameter out = command.Parameters.Add (
new SqlParameter("RETVAL", SqlDbType.Int));
out.Direction = ParameterDirection.ReturnValue;

connection.Open();

insertCommand.ExecuteNonQuery();

int newID = command.Parameters("RETVAL").Value;

connection.Close();



// SQL Server
// ----------

CREATE TABLE [dbo].[Department] (
[DepartmentID] [int] IDENTITY (1, 1) NOT NULL ,
[DepartmentName] [varchar] (100),
[CreationDate] [datetime] NULL
) ON [PRIMARY]
GO

ALTER TABLE [dbo].[Department] WITH NOCHECK ADD
CONSTRAINT [PK_Department] PRIMARY KEY
CLUSTERED
(
[DepartmentID]
) ON [PRIMARY]
GO


CREATE PROCEDURE dbo.CreateDepartment
@DepartmentName varchar(100),
AS
INSERT INTO Department (DepartmentName,
CreationDate)
VALUES (@DepartmentName, GetDate())
RETURN scope_identity()
GO
Interfaz IDataReader
Proporciona acceso secuencial de slo lectura a una fuente de datos. Las clases
SqlDataReader y OleDbDataReader implementan el interfaz de IDataReader. Al
utilizar un objeto IDataReader, las operaciones sobre la conexin IDbConnection
quedan deshabilitadas hasta que se cierre el objeto IDataReader.

string connectionString = "Provider=SQLOLEDB.1;" +
"User ID=sa;Initial Catalog=Northwind;" +
"Data Source=MYSERVER";

OleDbConnection connection = new
OleDbConnection(connectionString);


string sqlQuery = "SELECT CompanyName FROM
Customers";

OleDbCommand myCommand = new
OleDbCommand(sqlQuery, connection);

connection.Open();

OleDbDataReader myReader =
myCommand.ExecuteReader();

while (myReader.Read()) {
Console.WriteLine(myReader.GetString(0));
}

myReader.Close();

connection.Close();
Clase DataSet
Un objeto DataSet encapsula un conjunto de tablas independientemente de su
procedencia y mantiene las relaciones existentes entre las tablas. El contenido de
un DataSet puede serializarse en formato XML. Adems, se permite la
modificacin dinmica de los datos y metadatos del conjunto de datos
representado por el objeto DataSet.
El interfaz IDataAdapter implementado OleDbDataAdapter y SqlDataAdapter se
utiliza para construir el conjunto de datos y actualizarlo cuando sea necesario. Los
conjuntos de datos con los que se trabaja de esta forma utilizan un enfoque
asncrono en el que no se mantiene abierta la conexin con la base de datos a la
que se est accediendo. Al trabajar con conjuntos de datos de esta forma, se
dispone de un superconjunto de los comandos que se permiten cuando se emplea
el interfaz IDataReader. De hecho se pueden realizar operaciones de consulta
(select), insercin (insert), actualizacin (update) y borrado (delete).
string connectionString = "Persist Security Info=False;" +
"User ID=sa;Initial Catalog=Northwind;" +
"Data Source=MYSERVER";

SqlConnection connection = new
SqlConnection(connectionString);

SqlDataAdapter myDataAdapter = new SqlDataAdapter();

DataSet myDataSet = new DataSet();

string sqlQuery = "SELECT * FROM Customers";

myDataAdapter.SelectCommand = new
SqlCommand(sqlQuery, connection);


connection.Open();

myDataAdapter.Fill(myDataSet);

conn.Close();

Clase DataTable
Representa una tabla en memoria (Columns & Rows) cuyo esquema viene
definido por su coleccin de columnas Columns. La integridad de los datos se
conserva gracias a objetos que representan restricciones (Constraint) y dispone de
eventos pblicos que se producen al realizar operaciones sobre la tabla (p.ej.
modificacin o eliminacin de filas).
Clase DataColumn
Define el tipo de una columna de una tabla (va su propiedad DataType) e incluye
las restricciones (Constraints) y las relaciones (Relations) que afectan a la
columna. Adems, posee propiedades tiles como AllowNull, Unique o ReadOnly.
Clase DataRow
Representa los datos de una tabla (almacenados en la coleccin Rows de un
objeto DataTable), de acuerdo con el esquema definido por las columnas de la
tabla (Columns). Adems, incluye propiedades para determinar el estado de una
fila/tupla particular (p.ej. nuevo, cambiado, borrado, etc.).
Clase DataRelation
Relaciona dos DataTables va DataColumns y sirve para mantener restricciones
de integridad referencial. Obviamente, el tipo de las columnas relacionadas ha de
ser idntico. Para acceder a los datos relacionados con un registro concreto basta
con emplear el mtodo GetChildRecords de la tupla correspondiente (DataRow).
Creacin de una base de datos
// Creacin de las tablas, columnas y claves
primarias/externas

DataTable authors = new DataTable("Author");
DataTable books = new DataTable("Book");

DataColumn id = authors.Columns.Add("ID", typeof(Int32));
DataColumn name = new
authors.Columns.Add("Name",typeof(String));
authors.PrimaryKey = new DataColumn[] {id};
id.AutoIncrement = true;

DataColumn isbn = books.Columns.Add("ISBN",
typeof(String));
DataColumn title = books.Columns.Add("Title",
typeof(String));
DataColumn authid =
books.Columns.Add(AuthID,typeof(Int32));
books.PrimaryKey = new DataColumn[] {isbn};

DataColumn[] foreignkey = new DataColumn[] {authid};
DataRelation bookauth = new DataRelation("BookAuthors",
authors.PrimaryKey,
foreignkey);

// Creacin del DataSet: tablas y relaciones

DataSet dataset = new DataSet();
dataset.DataSetName = "BookAuthors";
dataset.Tables.Add (authors);
dataset.Tables.Add (books);

dataset.Relations.Add (bookauth);

// Insercin de datos

DataRow shkspr = authors.NewRow();
shkspr["Name"] = "William Shakespeare";
authors.Rows.Add(shkspr);

DataRow row = books.NewRow();
row["AuthID"] = shkspr["ID"];
row["ISBN"] = "1000-XYZ";
row["Title"] = "MacBeth";
books.Rows.Add(row);

// Commit

dataset.AcceptChanges();
Transacciones en ADO.NET
Las transacciones son conjuntos de operaciones que han de efectuarse de forma
atmica. La acidez de una transaccin hace referencia a sus propiedades
deseables: atomicidad, consistencia, aislamiento y durabilidad (ACID = Atomicity,
Consistency, Isolation, Durability).
En ADO.NET, los lmites de las transacciones se indican manualmente. Los
objetos de la clase Connection tienen un mtodo BeginTransaction que devuelve
una transaccin (objeto de tipo Transaction). La transaccin finaliza cuando se
llama al mtodo Commit o Rollback del objeto Transaction devuelto por
BeginTransaction.
Data Binding
ste es el nombre por el que se conoce el mecanismo que nos permite asociar el
contenido de un conjunto de datos a los controles de la interfaz de nuestra
aplicacin, algo que facilitan los entornos de programacin visual (como es el caso
del Visual Studio .NET).
Data Binding en Visual Studio .NET
Se puede optar por asistentes del tipo de DataForm wizard...
o, cuando stos no nos ofrecen la funcionalidad suficiente
para nuestras aplicaciones, podemos programarlo nosotros
mismos (algo que no es difcil y nos da bastante ms control
sobre nuestra aplicacin):
Se crean los objetos ADO.NET necesarios
(Connection, DataSet, Command y DataAdapter).
Se enlazan los controles de la interfaz con las
columnas del DataSet, lo que se consigue aadiendo
un objeto System.Windows.Forms.Binding a la
propiedad DataBindings del control. Los objetos de tipo
Binding nos permiten enlazar una propiedad de un
control a una columna de un DataSet.
Se implementa la forma de recorrer los registros,
haciendo uso del objeto BindingContext asociado al
formulario (cuyas propiedades Position y Count nos
permiten movernos por el conjunto de datos).
Se implementan las operaciones sobre la base de
datos (con los comandos asociados al DataAdapter.
NOTA: Cuando los conjuntos de datos sean grandes, es recomendable utilizar
ADO.NET con paginacin para no tener que leer todos los datos de la base de
datos (vase "ADO.NET data paging"). En situaciones como sa, tambin suele
ser recomendable aadir capacidades de bsqueda a nuestras aplicaciones
(vase el mtodo Find).
XML

Qu es XML?
XML es el acrnimo de eXtensible Markup Language, un formato estndar del
World Wide Web Consortium (W3C) diseado a partir de SGML para representar
datos estructurados de forma jerrquica (en un rbol).
Los documentos XML incluyen una serie de etiquetas que permiten crear
documentos autocontenidos, en los que los datos van siempre acompaados de
sus metadatos correspondientes.
XML no es, como su nombre puede sugerir, un lenguaje de marcado: XML es un
metalenguaje que permite definir lenguajes de marcado adecuados a usos
especficos.
Aunque a primera vista un docuento XML puede parecer similar a HTML hay una
diferencia fundamental: un documento XML contiene, exclusivamente, datos que
se autodefinen. Un documento HTML contiene datos "mal" definidos, mezclados
con elementos de formato. En XML, sin embargo, se separa el contenido de la
presentacin de forma total.

XML, al que algunos consideran el Esperanto de los sistemas de informacin, se
emplea principalmente para representar documentos (texto con etiquetas que
identifican porciones del documento, como es el caso de estos apuntes) y
conjuntos de datos (estructuras de datos jerrquicas, para ser ms precisos).
Entre sus ventajas se encuentra su aceptacin casi universal, su legibilidad y su
carcter autocontenido (a diferencia de los formatos binarios propios de cada
aplicacin), si bien el tamao de los documentos XML es mayor que el de sus
equivalentes binarios y su procesamiento requiere ms recursos, por lo que no
resulta adecuado en aplicaciones en las que la eficiencia sea un objetivo
prioritario.
En resumen, XML permite representar datos de forma homognea en entornos
heterogneos, lo que facilita la interoperabilidad entre distintos sistemas. Adems,
hoy en da goza de gran popularidad, pues todas las grandes empresas
promueven su utilizacin y se han desarrollado numerosas tecnologas basadas
en este formato estndar (SOAP, ebXML...).
Un pequeo ejemplo
Representacin de datos en formato ASCII, antes de que existiese XML (ms
concretamente, en formato CSV, comma-separated values):
"PO-1234","CUST001","X9876","5","14.98"
Representacin de los mismos datos en XML:
<pedido>
<id>PO-1234</id>
<cliente>CUST001</cliente>
<producto>X9876</producto>
<cantidad>5</cantidad>
<precio>14.98</precio>
</pedido>
Sintaxis de XML
Antes de ver los distintos componentes que pueden aparecer en un documento
XML, hay que resaltar que XML es sensible a maysculas. A diferencia de
HTML, <tag> y <TAG> representan cosas diferentes.
Elementos
Los elementos XML estn delimitados por etiquetas de comienzo y fin entre las
que se escribe su contenido:
<tag_name> ... </tag_name>

Tambin pueden estar vacos (elementos sin contenido):
<tag_name />
La primera lnea es correcta en HTML, no en XML. La segunda s lo es:
<LI>Esto es HTML<BR> que es muy permisivo.</LI>

<LI>XML es <BR/> mucho ms restrictivo.</LI>
El contenido de un elemento puede ser complejo: Un elemento puede contener a
otros elementos.

Atributos
Los distintos elementos de un documento XML pueden incluir atributos que
describen al elemento en cuestin.
Dichos atributos han de aparecer en la etiqueta de comienzo del elemento y el
valor del atributo debe especificarse entre comillas dobles (") o simples ('):
<tag_name attribute="value"> ... </tag_name>

Instrucciones de procesamiento
En su prlogo, un documento XML puede incluir una serie de instrucciones de
procesamiento, delimitadas por: <? ... ?>, en las que se puede indicar el sistema
de codificacin empleado (Unicode por defecto), especificar la hoja de estilo XSLT
que se emplear para visualizar el documento, declarar espacios de nombres,
definir el esquema del documento, etctera.
Slo es obligatorio especificar que se trata de un documento XML usando la
instruccin xml:
<?xml version="1.0"?>
Esta misma instruccin de procesamiento es la que se utiliza para especificar la
codificacin del documento:
UTF-8 (8-bit UCS/Unicode Transformation Format) coincide con
ASCII a la hora de representar caracteres del estndar americano,
mientras que emplea 2 bytes para representar letras latinas
acentuadas (vocales con tilde o la ee, en el caso del castellano) y
otros smbolos:
<?xml version="1.0" encoding="utf-8"?>
ISO-8859-1 es el estndar de codificacin, tambin conocido como
Latin-1, que se utiliza para transmitir documentos va HTTP de tipo
MIME "text/...":
<?xml version="1.0" encoding="ISO-8859-1"?>
La codificacin Windows-1252 utilizada por defecto en Windows
coincide con la ISO-8859-1 salvo para los caracteres 0x80 - 0x9F,
que Windows-1252 interpreta como letras y signos de puntuacin
mientras que el estndar ISO-8859-1 los procesa como caracteres
de control (http://en.wikipedia.org/wiki/ISO-8859-1)
Debajo se muestra un ejemplo ms detallado en el que, aparte de indicar la
codificacin empleada, se especifica la hoja de estilo que se ha de aplicar al
documento para su presentacin:
<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="template.xsl"?>

<root>
<element1>
<subelement1 />
<subelement2 />
</element1>
<element2>...</element2>
<element3>...</element3>
<element4>...</element4>
</root>


Entidades
Las entidades en XML proporcionan un mecanismo de substitucin textual:
Entidad Sustitucin
&lt; <
&gt; >
&amp; &
&apos; '
&quot; "
XML permite que se puedan definir nuevas entidades. Algunas (parsed entities)
pueden contener texto y etiquetas XML, mientras que otras (unparsed entities)
sirven para almacenar cualquier tipo de datos (imgenes, sonidos...).
Entidades predefinidas
<!ENTITY lt "&#38;#60;">
<!ENTITY gt "&#62;">
<!ENTITY amp "&#38;#38;">
<!ENTITY apos "&#39;">
<!ENTITY quot "&#34;">
Comentarios y secciones CDATA
Finalmente, un documento XML tambin puede incluir comentarios y secciones
CDATA:
Comentarios, delimitados por las construcciones <!-- y -->
(exactamente igual que en HTML) y
<!-- Esto es un comentario en XML -->
Secciones CDATA, que sirven para incluir cualquier cosa (contenido
textual) en el documento XML y vienen delimitadas por las
construcciones <![CDATA[ y ]]> (como <PRE> ... </PRE> en HTML).
<!-- Este documento XML no usa CDATA -->

<ejemplo>
&lt;HTML&gt;
&lt;HEAD&gt;
&lt;TITLE&gt;Rock &amp; Roll&lt;/TITLE&gt;
&lt;/HEAD&gt;
&lt;/HTML&gt;
</ejemplo>
<!-- Este documento XML usa CDATA -->

<ejemplo>
<![CDATA[

<HTML>
<HEAD>
<TITLE>Rock & Roll</TITLE>
</HEAD>
</HTML>

]]>
</ejemplo>
Documentos XML bien formados
Un documento bien formado en XML tiene que reunir las siguientes cualidades:
Debe haber un y slo un elemento raz.
Los subelementos deben estar adecuadamente anidados. Esto es,
un elemento ha de terminar con la misma etiqueta con la que
comenz.
Los atributos son opcionales (y se definen en un esquema que
tambin es opcional).
Los valores de los atributos han de estar delimitados por comillas
dobles (") o comillas simples (').
Las instrucciones de procesamiento son opcionales.
XML es sensible a maysculas y minsculas. Es decir, <tag> y
<TAG> no hacen referencia al mismo tipo de elemento.
Teniendo en cuenta las estrictas reglas anteriores, el siguiente documento XML no
es vlido porque sus elementos no estn anidados correctamente:
<?xml version="1.0" ?>
<parent>
<child1> 1 </child1>
<child2> <child3> 2 3? </child2> </child3>
</parent>
El siguiente documento XML tampoco es vlido porque tiene dos races:
<?xml version="1.0" ?>
<parent>
<child> ... </child>
</parent>
<parent>
<child> ... </child>
</parent>
Sin embargo, el siguiente documento XML s que es vlido:
<?xml version="1.0" ?>
<parent>
<child1> ... </child1>
<child2 />
<child3> ... </child3>
</parent>
En la siguiente figura se muestra un ejemplo de un documento XML bien formado
donde se distinguen claramente sus distintas partes:

Una versin "castellanizada" del mismo podra tener el siguiente aspecto:
Un documento XML bien formado
<?xml version='1.0' encoding="utf-8"?>
<libreria>
<libro genero='Poesa' fechaPublicacion='1932'
ISBN='1-861003-11-0'>
<titulo>Poeta en Nueva York</titulo>
<autor>
<nombre>Federico</nombre>
<apellidos>Garca Lorca</apellidos>
</autor>
<precio>8.99</precio>
</libro>
<libro genero='Novela' fechaPublicacion='1967'
ISBN='0-201-63361-2'>
<titulo>The Confidence Man</titulo>
<autor>
<nombre>Herman</nombre>
<apellidos>Melville</apellidos>
</autor>
<precio>11.99</precio>
</libro>
</libreria>
Un documento XML bien formado
Espacios de nombres
Los espacios de nombres (namespaces) permiten que XML sea extensible,
permitiendo diferenciar entre etiquetas con el mismo nombre utilizando prefijos. De
esta forma se pueden evitar conflictos de nombres y el que disea un documento
XML puede centrarse en los datos que tiene y cmo describirlos mejor. Adems, el
uso de identificadores universales URI (Uniform Resource Identifier) para hacer
referencia a estndares ampliamente aceptados permite que se puedan combinar
documentos escritos independientemente.
Sintaxis
xmlns:prefijo = URI
En la declaracin anterior:
URI es el identificador universal del espacio de nombres, su nombre
real (cuando se usa una URL, sta no tiene porqu hacer referencia
a un servidor activo).
prefijo es el identificador por el que nos referiremos a los distintos
elementos del espacio de nombres en el documento actual, usndolo
como prefijo.
Ejemplos de declaracin de namespaces
xmlns:bk = "http://www.example.com/bookinfo/"

xmlns:bk = "urn:mybookstuff.org:bookinfo"
Ejemplos de uso de namespaces
<libro xmlns:bk="http://www.bookstuff.org/bookinfo">
<bk:titulo>All About XML</bk:titulo>
<bk:autor>Joe Developer</bk:autor>
<bk:precio currency='US Dollar'>19.99</bk:precio>
</libro>


<bk:libro xmlns:bk="http://www.bookstuff.org/bookinfo"
xmlns:money="urn:finance:money">
<bk:titulo>All About XML</bk:titulo>
<bk:autor>Joe Developer</bk:autor>
<bk:precio money:currency='US Dollar'>19.99</bk:precio>
</bk:libro>
Al combinar documentos XML de distintas fuentes, se pueden producir conflictos
entre los nombres de los elementos y/o atributos.
Supongamos que disponemos del siguiente fichero XML con nuestra coleccin de
libros:
libros.xml
<?xml version="1.0"?>
<biblioteca>
<item estado="disponible">
<titulo>The Adventures of Huckleberry Finn</titulo>
<autor>Mark Twain</autor>
<precio>$5.49</precio>
</item>
<item estado="prestado">
<titulo>Leaves of Grass</titulo>
<autor>Walt Whitman</autor>
<precio>$7.75</precio>
</item>
<item estado="prestado">
<titulo>The Legend of Sleepy Hollow</titulo>
<autor>Washington Irving</autor>
<precio>$2.95</precio>
</item>
<item estado="disponible">
<titulo>The Marble Faun</titulo>
<autor>Nathaniel Hawthorne</autor>
<precio>$10.95</precio>
</item>
</biblioteca>

Una coleccin de libros.
Tambin disponemos del siguiente documento XML con nuestra coleccin de
discos:
discos.xml
<?xml version="1.0"?>
<discoteca>
<item>
<titulo>Violin Concerto in D</titulo>
<compositor>Beethoven</compositor>
<precio>$14.95</precio>
</item>
<item>
<titulo>Violin Concertos Numbers 1, 2, and 3</titulo>
<compositor>Mozart</compositor>
<precio>$16.49</precio>
</item>
</discoteca>

Una coleccin de discos.
Queremos combinar estos documentos en uno solo y, adems, queremos
gestionarlo con una nica aplicacin. El problema surge al haber elementos
repetidos (con el mismo nombre). Por ejemplo, cmo hacer una lista de todos los
libros? cmo calcular e precio medio de los discos?
El mecanismo de los espacios de nombres facilita esta tarea: basta con definir un
espacio de nombres para diferenciar cada elemento. En el documento combinado,
cada elemento de un libro se asigna al espacio de nombres libro (libro:item,
libro:titulo, libro:autor y libro:precio) y cada uno de los elementos de un disco se
asigna al espacio de nombres cd (cd:item, cd:titulo, cd:compositor y cd:precio).
biblioteca.xml
<?xml version="1.0"?>

<biblioteca
xmlns:libro="http://csharp.ikor.org/libros"
xmlns:cd="http://csharp.ikor.org/discos">

<libro:item estado="disponible">
<libro:titulo>
The Adventures of Huckleberry Finn
</libro:titulo>
<libro:autor>Mark Twain</libro:autor>
<libro:precio>$5.49</libro:precio>
</libro:item>

<cd:item>
<cd:titulo>Violin Concerto in D</cd:titulo>
<cd:compositor>Beethoven</cd:compositor>
<cd:precio>$14.95</cd:precio>
</cd:item>

<libro:item estado="prestado">
<libro:titulo>Leaves of Grass</libro:titulo>
<libro:autor>Walt Whitman</libro:autor>
<libro:precio>$7.75</libro:precio>
</libro:item>

<cd:item>
<cd:titulo>
Violin Concertos Numbers 1, 2, and 3
</cd:titulo>
<cd:compositor>Mozart</cd:compositor>
<cd:precio>$16.49</cd:precio>
</cd:item>

<libro:item estado="prestado">
<libro:titulo>
The Legend of Sleepy Hollow
</libro:titulo>
<libro:autor>Washington Irving</libro:autor>
<libro:precio>$2.95</libro:precio>
</libro:item>

<libro:item estado="disponible">
<libro:titulo>The Marble Faun</libro:titulo>
<libro:autor>Nathaniel Hawthorne</libro:autor>
<libro:precio>$10.95</libro:precio>
</libro:item>

</biblioteca>

Una coleccin de libros y discos.
Espacios de nombres por defecto
Un espacio de nombres XML declarado sin prefijo se convierte automticamente
en el espacio de nombres por defecto para todos los subelementos del elemento
en el que aparece la declaracin.
Ejemplo de uso de namespaces por defecto
<libro xmlns="http://csharp.ikor.org/libros">
<titulo>All About XML</titulo>
<autor>Joe Developer</autor>
</libro>
En esta situacin, todos los elementos que aparezcan sin prefijo harn referencia
al espacio de nombres por defecto (y no har falta repetir el prefijo para los
elementos del espacio de nombres por defecto).
En el siguiente ejemplo, el espacio de nombres por defecto es
http://csharp.ikor.org/libros:
default.xml
<?xml version="1.0"?>

<biblioteca
xmlns="http://csharp.ikor.org/libros"
xmlns:cd="http://csharp.ikor.org/discos">

<item estado="disponible">
<titulo>
The Adventures of Huckleberry Finn
</titulo>
<autor>Mark Twain</autor>
<precio>$5.49</precio>
</item>

<cd:item>
<cd:titulo>Violin Concerto in D</cd:titulo>
<cd:compositor>Beethoven</cd:compositor>
<cd:precio>$14.95</cd:precio>
</cd:item>

<item estado="prestado">
<titulo>Leaves of Grass</titulo>
<autor>Walt Whitman</autor>
<precio>$7.75</precio>
</item>

<cd:item>
<cd:titulo>
Violin Concertos Numbers 1, 2, and 3
</cd:titulo>
<cd:compositor>Mozart</cd:compositor>
<cd:precio>$16.49</cd:precio>
</cd:item>

<item estado="prestado">
<titulo>
The Legend of Sleepy Hollow
</titulo>
<autor>Washington Irving</autor>
<precio>$2.95</precio>
</item>

<item estado="disponible">
<titulo>The Marble Faun</titulo>
<autor>Nathaniel Hawthorne</autor>
<precio>$10.95</precio>
</item>
</biblioteca>
Una coleccin de libros y discos.
mbito de los espacios de nombres
Los elementos no cualificados (esto es, sin prefijo relativo a un namespace) se
consideran pertenecientes al namespace por defecto ms interno. En el siguiente
ejemplo, libro, titulo y autor corresponden al espacio de nombres por defecto (el de
libro), mientras que editorial y nombre pertenecen al espacio de nombres ms
interno (urn:publishers:pubinfo):
mbito de los namespaces
<libro xmlns="http://csharp.ikor.org/libros">
<titulo>All About XML</titulo>
<autor>Joe Developer</autor>
<editorial xmlns="urn:publishers:pubinfo">
<nombre>Microsoft Press</nombre>
</editorial>
</libro>
Espacios de nombres y atributos
A diferencia de los elementos (que, salvo que se indique lo contrario, pertenecen
al espacio de nombres por defecto), los atributos NO pertenecen a ningn espacio
de nombres, incluso aunque exista un espacio de nombres por defecto.
Tecnologas relacionadas
XML en s es bastante simple. Sin embargo, existen mltiples tecnologas
relacionadas cuyo aprendizaje requiere algo ms de esfuerzo:
XML
eXtensible Markup
Language
Definicin de documentos
XML
DTD
Document Type
Definition
Definicin del esquema de
documentos XML (en un
formato distinto a XML)
XSD XML Schema
Definicin del esquema de
documentos XML (en XML)
XDR XML Data Reduced
Precursor de XML Schema
(Microsoft)
XSL
eXtensible
Stylesheet
Language
Definicin de hojas de estilo
(XSLT + XSL-FO)
XSLT
XSL
Transformations
Transformacin de
documentos XML
XSL-FO
XSL Formatting
Objects
Descripcin del layout de un
documento
XPath XML Path language
Acceso a partes de un
documento XML (usado en
XSLT y XPointer)
XPointer
XML Pointer
language
Acceso a la estructura interna
de un documento XML
XLink
XML Linking
language
Descripcin de enlaces entre
documentos XML
XQuery
XML Query
language
Mecanismo para hacer
consultas en documentos
XML
XMLEnc XML Encryption
Criptografa para documentos
XML
XMLDSig XML-Signature Firmas digitales en XML
XKMS
XML Key
Management
Gestin de claves en XML
DOM
Document Object
Model
API para crear, acceder y
modificar documentos XML
SAX Simple API for XML
API para trabajar con
documentos XML
XHTML XML HTML
Versin de HTML compatible
con XML
XForms XML Forms Formularios web en XML
Data
Island
Inclusin de datos XML en un
documento HTML
Data
Binding
Generacin automtica de
HTML a partir de documentos
XML

Esquemas XML

Para qu sirven los esquemas XML?
XML es un formato abierto muy til para enviar informacin de un sitio a otro. En
ocasiones, sin embargo, la flexibilidad de XML puede resultar contraproducente.
Cmo sabemos que los datos contenidos en un documento XML son
consistentes con los que esperbamos recibir? Los esquemas XML nos permiten
verificarlo.
Un esquema XML es, bsicamente, un conjunto de reglas predefinidas que
describe una clase de documentos XML. Un esquema define los elementos que
pueden aparecer en un documento XML, as como los atributos que pueden
asociarse a stos. Tambin define informacin estructural tal como enumerar los
descendientes de un elemento, la secuencia en que pueden aparecer, sus tipos,
etc. En definitiva, un esquema permite especificar el formato correcto de un
documento XML.
Los esquemas XML tienen, pues, dos cometidos fundamentales:
Publicar cmo se han de construir documentos XML correctos (de forma
similar a como se publica la interfaz de un componente software) y,
Permitir la validacin de un documento conforme a un esquema particular
(para, por ejemplo, comprobar que los datos que nos llegan estn en el
formato correcto). Se habla entonces de que el documento XML es vlido
respecto a un esquema, lo que no debe confundirse con estar bien
formado.
Desde su creacin, se han propuesto distintos estndares para la especificacin
de esquemas XML, siendo DTD y XSD los ms utilizados. En la actualidad, DTD
(Document Type Definition) est cayendo en desuso, ya que los esquemas XSD
(XML Schema Definition) se definen utilizando documentos XML, mientras que
DTD utiliza un formato que no es XML.
Los esquemas XML y las definiciones DTD suelen especificarse en ficheros
independientes, lo que facilita la tarea de mantenimiento ya que un solo fichero (de
esquema o DTD) puede servir de referencia a muchos ficheros de datos XML.
Nociones sobre DTDs.
Para utilizar una DTD como mecanismo de restriccin, se especifica lo siguiente
en el documento XML:
<?xml version="1.0"?>
<!DOCTYPE Libro SYSTEM "libro.dtd">
.....
SYSTEM sirve para DTDs "personales". Se puede espicificar un fichero local o un
fichero accesible a travs de una URL. Se puede especificar una DTD pblica con
PUBLIC, en la que queda reflejado el propietario de la misma, una descripcin y el
idioma.
<!DOCTYPE elem_raiz PUBLIC "-//W3C//DTD XHTML 1.0
Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
DTD para un ejemplo que contenga elementos de tipo libro:
<?xml version="1.0"?>
<!ELEMENT Libro
(Titulo, Catalogo:Seccion, Catalogo:SubSeccion,
Contenido, Compra, Copyright)>
<!ATTLIST Libro
xmlns CDATA #REQUIRED
xmlns:Catalogo CDATA #REQUIRED
>
<!ELEMENT Titulo (#PCDATA)>
<!ELEMENT Catalogo:Seccion (#PCDATA)>
<!ELEMENT Catalogo:SubSeccion (#PCDATA)>
<!ELEMENT Contenido ((Capitulo+)|(Capitulo+,
Separacion?)+)>
<!ELEMENT Capitulo (Tema, Seccion+)>
<!ATTLIST Capitulo
materia (XML|Java) "Java"
>
<!ELEMENT Tema (#PCDATA)>
<!ELEMENT Seccion (#PCDATA)>
<!ATTLIST Seccion
apartados CDATA #REQUIRED
dificil (si|no) "no"
>
<!ELEMENT Separacion EMPTY>
<!ELEMENT Compra (#PCDATA)>
<!ELEMENT Copyright (#PCDATA)>
Elementos
Los elementos permitidos se especifican con ELEMENT, seguido del nombre y el
tipo del elemento. Los elementos que se pueden anidar dentro de otros se
especifican entre parntesis y separados por comas. Importa el orden. El tipo
menos restrictivo es ANY, que permite cualquier contenido para un elemento. Para
datos de tipo texto, se usa #PCDATA. Para elementos vacos, EMPTY.
Modificadores de nmero de ocurrencias:
?: Una vez o ninguna
+: Al menos una vez
*: Cualquier nmero de veces o ninguna
(nada): Exactamente una vez
Para opciones alternativas: separar con |.
Atributos
Los atributos permitidos para un elemento se especifican con ATTLIST y el
nombre del elemento seguido de los nombres de los atributos, con un tipo y
modificador obligatorios. El tipo del atributo puede ser CDATA para cualquier
valor, o una enumeracin de los valores permitidos.
Otros posibles tipos son: NMTOKEN para restringir el valor a un nombre XML
vlido (es decir, que empiece con una letra o guin de subrayado y contenga slo
letras, nmeros, guiones de subrayado, guiones y puntos, sin espacios) ID,
adems de las restricciones que impone NMTOKEN, impone que el valor sea
nico en todo el documento. El modificador puede ser #REQUIRED para atributos
obligatorios, #IMPLIED para opcionales, o #FIXED valor_fijo para valores fijos.
Tambin puede ser un valor por defecto.
Sintaxis de XML Schema
El siguiente ejemplo muestra un sencillo esquema XML que podra ser til para
gestionar los productos existentes en un almacn:
<?xml version="1.0" encoding="utf-8"?>
<xsd:schema id="stock"
xmlns:xsd="http://www.w3c.org/2001/XMLSchema"
targetNamespace="http://elvex.ugr.es/stock.xsd">

<xsd:complexType>

<xsd:sequence>

<xsd:element name="ID" type="xsd:unsignedInt" />
<xsd:element name="description" type="xsd:string" />
<xsd:element name="price" type="xsd:decimal" />
<xsd:element name="quantity" type="xsd:integer" />

</xsd:sequence>

</xsd:complexType>

</xsd:schema>
La etiqueta xsd:schema del documento XML anterior est definida en el estndar
XSD y es la que nos permite definir esquemas de acuerdo con el estndar del
W3C. El atributo targetNamespace nos permite asociar el esquema al espacio de
nombres indicado (para diferenciarlo de otros esquemas stock).
A continuacin, la etiqueta xsd:complexType nos permite definir tipos de forma
similar a como se especifican los tipos definidos por el usuario en un lenguaje de
programacin, indicando los elementos correspondientes a cada dato almacenado
acerca de los productos de nuestro almacn, su identificador y su tipo.
Elementos
Un elemento (etiqueta xsd:element) se utiliza para describir datos.
Los elementos se utilizan para especificar las etiquetas vlidas en un documento
XML (name) y su tipo (type), tal como aparece en el ejemplo anterior del almacn.
Adems, el orden en que aparecen los elementos en el esquema XML determina
el orden en que han de aparecen dentro de un documento XML que se ajuste al
esquema.
La siguiente tabla muestra algunos de los tipos permitidos y su equivalencia con
los tipos de la plataforma .NET:
Tipo XSD Tipo .NET
anyType object
Boolean bool
Byte sbyte
date | dateTime | time DateTime
decimal decimal
double double
duration Timespan
float single
ID | Name string
int Int32
integer | long Int64
short Int16
string string
unsignedByte Byte
unsignedInt UInt32
unsignedLong UInt64
unsignedShort UInt16
Adems de poder indicar su nombre y su tipo, podemos especificar restricciones
adicionales para los elementos de un documento XML.minOccurs y maxOccurs
nos permiten especificar el nmero mnimo y el nmero mximo de veces que un
elemento puede aparecer en el documento (por defecto, 1).
Atributos
Los atributos son similares a los elementos, si bien un atributo ha de ser de un tipo
simple y tiene declararse justo antes de cerrar la etiqueta xsd:complexType. A
diferencia de los elementos, los atributos pueden aparecer en cualquier orden y no
pueden incluir otros elementos (al ser de tipos simples). No obstante, su
caracterstica ms interesante es que pueden ser opcionales y se les puede
asignar un valor por defecto:
<xsd:attribute name="rebate" type="xsd:decimal" />
El atributo use de xsd:attribute puede utilizarse para especificar si la presencia del
atributo es esencial ("required"), opcional ("optional") o incluso si est prohibida
("prohibited"), aunque esta ltima opcin no resulta especialmente til.
Facetas
Las facetas forman parte de la definicin de elementos y atributos de un esquema
XML y nos permiten especificar restricciones adicionales sobre los datos que
pueden aparecer en un documento XML vlido:
Por ejemplo, podemos definir un tipo de dato que slo permita almacenar valores
enteros entre 0 y 10:
Rango de valores
<xsd:simpleType name="nota">
<xsd:restriction base="decimal">
<xsd:minInclusive value="0" fixed="true" />
<xsd:maxInclusive value="10" fixed="true" />
</xsd:restriction>
</xsd:simpleType>
El tipo nota que hemos definido est basado en el tipo intrnseco decimal y, en su
definicin, se especifican dos facetas (su valor mnimo y su valor mximo). El uso
de fixed evita que alguien pueda modificar las facetas especificadas en la
definicin del tipo (por ejemplo, al declarar un nuevo tipo basado en nota).
Tambin podemos especificar el conjunto de valores permitidos para un tipo de
dato:


Tipo enumerado
<xsd:simpleType name="sexo">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="hombre"/>
<xsd:enumeration value="mujer"/>
<xsd:enumeration value="???"/>
</xsd:restriction>
</xsd:simpleType>
O incluso especificar un patrn al que han de atenerse los valores vlidos de un
tipo (utilizando expresiones regulares):
SKU (Stock Keeping Unit): Cdigo para identificar
productos (p.ej. 976-FB)
<xsd:simpleType name="SKU">
<xsd:restriction base="xsd:string">
<xsd:pattern value="\d{3}-[A-Z]{2}"/>
</xsd:restriction>
</xsd:simpleType>
Adems de poder especificar tipos enumerados y expresiones regulares, las
facetas incluidas en el estndar XSD permiten especificar la longitud de una
cadena o de una lista (length, minLength y maxLength), si se permite la presencia
de espacios en blanco (whiteSpace), el intervalo de valores permitido
(minInclusive, minExclusive, maxInclusive, maxExclusive) y el nmero de dgitos
de un valor decimal (totalDigits y fractionDigits).
Estructuras de datos
Los tipos complejos como el utilizado en el ejemplo del almacn son similares a
los tipos enumerados y, en concreto, suelen utilizarse para representar tablas. Si
le asignamos un nombre al tipo especificado, lo que estamos haciendo es definir
un tipo abstracto que se podr utilizar en la definicin de otros elementos, como
en:
<xsd:schema
xmlns:xsd="http://www.w3.org/2001/XMLSchema">

<xsd:element name="purchaseOrder"
type="PurchaseOrderType"/>

<xsd:element name="comment" type="xsd:string"/>

<xsd:complexType name="PurchaseOrderType">
<xsd:sequence>
<xsd:element name="shipTo" type="Address"/>
<xsd:element name="billTo" type="Address"/>
<xsd:element ref="comment" minOccurs="0"/>
<xsd:element name="items" type="Items"/>
</xsd:sequence>
<xsd:attribute name="orderDate" type="xsd:date"/>
</xsd:complexType>

<xsd:complexType name="Address">
<xsd:sequence>
<xsd:element name="name" type="xsd:string"/>
<xsd:element name="street" type="xsd:string"/>
<xsd:element name="city" type="xsd:string"/>
<xsd:element name="zip" type="xsd:decimal"/>
</xsd:sequence>
</xsd:complexType>

<xsd:complexType name="Items">
<xsd:sequence>
<xsd:element name="item"
minOccurs="0" maxOccurs="unbounded">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="product" type="xsd:string"/>
<xsd:element name="quantity" type="xsd:integer" />
<xsd:element name="price" type="xsd:decimal"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>

</xsd:schema>
Cuando nos interesa permitir que en dentro de un elemento aparezcan elementos
alternativos en vez de una secuencia de ellos, podemos utilizar un bloque choice:
<xsd:complexType name="PurchaseOrderType">
<xsd:sequence>
<xsd:choice>
<xsd:group ref="shipAndBill"/>
<xsd:element name="singleAddress" type="Address"/>
</xsd:choice>
<xsd:element ref="comment" minOccurs="0"/>
<xsd:element name="items" type="Items"/>
</xsd:sequence>
<xsd:attribute name="orderDate" type="xsd:date"/>
</xsd:complexType>

<xsd:group name="shipAndBill">
<xsd:sequence>
<xsd:element name="shipTo" type="Address"/>
<xsd:element name="billTo" type="Address"/>
</xsd:sequence>
</xsd:group>
Aparte de definir tipos estructurados, XSD nos permite definir listas como si fuesen
tipos de datos simples. Por ejemplo, en el documento XML:
<lista>20003 15037 95977 95945</lista>
y en el esquema XSD:
<xsd:simpleType name="lista">
<xsd:list itemType="xsd:integer"/>
</xsd:simpleType>
Adems, los esquemas XML nos permiten definir claves, tanto primarias como
externas:
<xsd:schema targetNamespace="http://elvex.ugr.es/informe"
xmlns="http://elvex.ugr.es/informe"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
attributeFormDefault="unqualified"
elementFormDefault="qualified">

<xsd:annotation>
<xsd:documentation xml:lang="es">
Informe de proveedores y piezas
</xsd:documentation>
</xsd:annotation>

<xsd:element name="informe">
<xsd:complexType>
<xsd:sequence>

<xsd:element name="proveedores"
type="Proveedores">
<xsd:keyref name="refPieza" refer="keyPieza">
<xsd:selector xpath="proveedor/pieza" />
<xsd:field xpath="@sku" />
</xsd:keyref>
</xsd:element>

<xsd:element name="piezas" type="Piezas" />

</xsd:sequence>
<xsd:attribute name="fecha" type="xsd:date" />
</xsd:complexType>

<xsd:unique name="uniqProveedor">
<xsd:selector xpath="proveedores/proveedor" />
<xsd:field xpath="@id" />
</xsd:unique>

<xsd:key name="keyPieza">
<xsd:selector xpath="piezas/pieza" />
<xsd:field xpath="@sku" />
</xsd:key>

</xsd:element>

<xsd:complexType name="Proveedores">
<xsd:sequence>
<xsd:element name="proveedor"
maxOccurs="unbounded">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="suministro"
maxOccurs="unbounded">
<xsd:complexType>
<xsd:complexContent>
<xsd:restriction
base="xsd:anyType">
<xsd:attribute name="sku"
type="SKU" />
<xsd:attribute name="cantidad"
type="xsd:positiveInteger" />
</xsd:restriction>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
<xsd:attribute name="id"
type="xsd:positiveInteger" />
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>

<xsd:complexType name="Piezas">
<xsd:sequence>
<xsd:element name="pieza" maxOccurs="unbounded">
<xsd:complexType>
<xsd:simpleContent>
<xsd:extension base="xsd:string">
<xsd:attribute name="sku" type="SKU" />
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>

<xsd:simpleType name="SKU">
<xsd:restriction base="xsd:string">
<xsd:pattern value="\d{3}-[A-Z]{2}" />
</xsd:restriction>
</xsd:simpleType>

</xsd:schema>
Dado el esquema anterior, el siguiente documento XML sera vlido:
<informe xmlns="http://elvex.ugr.es/informe"
fecha="2002-12-31">
<proveedores>
<proveedor id="95819">
<suministro sku="872-AA" cantidad="1" />
<suministro sku="926-AA" cantidad="1" />
<suministro sku="833-AA" cantidad="1" />
<suministro sku="455-BX" cantidad="1" />
</proveedor>
<proveedor id="63143">
<suministro sku="455-BX" cantidad="4" />
</proveedor>
</proveedores>
<piezas>
<pieza sku="872-AA">Monitor</pieza>
<pieza sku="926-AA">Impresora</pieza>
<pieza sku="833-AA">Escner</pieza>
<pieza sku="455-BX">CD-R</pieza>
</piezas>
</informe>
Informacin adicional
http://www.w3c.org/TR/xmlschema-0/
XML en Visual Studio .NET

Herramientas de diseo para XML
Visual Studio incluye una herramienta (el diseador XML) que nos permite hacer
casi de todo con documentos XML y esquemas XSD de una forma similar a como
se trabaja con bases de datos en Access.
Crear un esquema XML con el diseador XML
Vamos a ilustrar cmo crear un esquema XML para un problema en el que se
gestionan pedidos.
1. Crear proyecto y agregar esquema XML.
Crear un proyecto de Aplicacin para Windows.
En el men Archivo, elija Nuevo y, a continuacin, haga clic en Proyecto para
mostrar el cuadro de dilogo Nuevo proyecto. Seleccione Proyectos de Visual C#
en el panel Plantillas y, a continuacin, seleccione Aplicacin para Windows.
Asigne al proyecto el nombre EjemploEsquema.
Agregar un esquema XML al proyecto.
En el men Proyecto, seleccione Agregar nuevo elemento (o situados en el
explorador de soluciones, pinchar con el botn derecho sobre EjemploEsquema y
seleccionar Agregar y Agregar nuevo elemento) y, a continuacin, haga doble clic
en el icono Esquema XML en el cuadro de dilogo Agregar nuevo elemento.
Cambiar el nombre por EsquemaPedido. Aparecer el Diseador XML.
2. Definir los tipos de datos.
Antes de crear la tabla relacional, primero crear definiciones de tipos simple y
complejo que utilizar para dar formato a elementos especficos del esquema de
pedido. Los nuevos tipos se crean utilizando tipos de datos XML existentes, como
string e integer.
En primer lugar definir un tipo simple, que se denominar CodigoProvincia. Este
tipo simple se utilizar para limitar el tamao de una cadena a dos caracteres.
Agregar un objeto simpleType al proyecto.
Si an no est abierto, haga doble clic en el archivo
EsquemaPedido.xsd para abrir el Diseador XML.
Haga clic en la ficha Esquema XML del Cuadro de herramientas y
arrastre un objeto simpleType hasta la superficie del diseador.

Cambie el nombre del objeto simpleType haciendo clic en el primer
cuadro de texto del encabezado y reemplazando simpleType1 por
CodigoProvincia.
Establezca el tipo base del tipo CodigoProvincia haciendo clic en la
lista desplegable del encabezado y seleccionando string.
Colquese en la primera columna de la fila siguiente y seleccione
facet en la lista desplegable.
Colquese en la siguiente celda, seleccione lenght en la lista
desplegable y establezca el valor 2 en la siguiente columna.
De esta manera se exige que el valor escrito en el campo
CodigoProvincia tenga dos caracteres.
CodigoProvincia debe tener este aspecto en la vista de esquema:

Haga clic en la ficha XML en la parte inferior izquierda del Diseador
XML, para ver el cdigo XML que se ha agregado:
<xs:simpleType name="CodigoProvincia">
<xs:restriction base="xs:string">
<xs:length value="2" />
</xs:restriction>
</xs:simpleType>

Este tipo simple CodigoProvincia se utilizar para definir el elemento
Provincia del tipo complejo que crear en la siguiente seccin.
Agregar objetos complexType al proyecto.
El tipo complejo TipoDireccion define un conjunto de elementos que aparecern
en cualquier elemento con tipo TipoDireccion. Por ejemplo, un elemento del tipo
FacturarA incluir informacin de nombres y direccin cuando se establece que un
componente suyop sea de tipo TipoDireccion. Mediante la creacin del tipo
complejo y utilizndolo en un elemento, se genera una relacin anidada.
Haga clic en la ficha Esquema del Diseador XML.
Haga clic en la ficha Esquema XML del Cuadro de herramientas y
arrastre un objeto complexType hasta la superficie del diseador.
Cambie el nombre del tipo por TipoDireccion.
Aada un elemento al tipo complejo haciendo clic en la primera celda
de la primera fila y seleccionando element en la lista desplegable. En
la segunda columna, cambie element1 por Nombre. En la tercera
columna, acepte el valor predeterminado string.
Aada nuevos elementos al tipo complejo de manera que tenga un
aspecto similar al siguiente en la vista de esquema:

Para ver el cdigo XML que se ha agregado al archivo .xsd, haga clic
en la ficha XML en la parte inferior del diseador. Ver el siguiente
cdigo XML:
<xs:complexType name="TipoDireccion">
<xs:sequence>
<xs:element name="Nombre" type="xs:string" />
<xs:element name="Calle" type="xs:string" />
<xs:element name="Ciudad" type="xs:string" />
<xs:element name="Provincia"
type="CodigoProvincia" />
<xs:element name="CodPostal" type="xs:integer"/>
</xs:sequence>
</xs:complexType>

3. Crear una tabla relacional.
Cuando se arrastra el objeto element del Cuadro de herramientas a la superficie
de diseo, realmente se agrega un elemento que contiene un complexType sin
nombre. Al incluir el tipo complejo sin nombre se define el elemento para que sea
una tabla relacional.
A continuacin, es posible agregar elementos adicionales bajo complexType para
definir los campos (o columnas) de la relacin. Si define uno de estos nuevos
elementos como un nuevo complexType sin nombre, est creando una relacin
anidada dentro de la relacin primaria con sus propias columnas nicas.
Se trata de crear una taba relacional llamada Pedidos, agregar un elemento Items
a esa tabla, especificando que Items sea, a su vez, de tipo complexType sin
nombre, lo que permite la repeticin de objetos Item para un slo registro de
pedido.
Como se est definiendo una nueva tabla relacional hace que aparezca un nuevo
elemento en la superficie de diseo. En la relacin de nuevos Items , al agregar el
elemento Item y establecer su tipo en complexType sin nombre, se crea otra tabla
relacional, que tambin aparece en el superficie de diseo.
Agregar un elemento al proyecto.
Haga clic en el Cuadro de herramientas y, en la ficha Esquema XML,
arrastre un objeto element hasta la superficie de diseo.
Cambie element1 a Pedidos para asignar un nombre al elemento.
Puede dejar el tipo de datos como queda por defecto: (Pedidos).
Agregue un elemento a Pedidos haciendo clic en la primera celda de
la primera fila y seleccionando element en la lista desplegable. Dele
al elemento el nombre EnviarA y establezca que su tipo es
TipoDireccion. El tipo Pedidos tendr este aspecto en la vista de
esquema:

Aada otro elemento llamado FacturarA y establezca que su tipo es
TipoDireccion. El tipo Pedidos tendr ahora este aspecto en la vista
de esquema:

y se habr aadido el siguiente cdigo XML:
<xs:element name="Pedidos">
<xs:complexType>
<xs:sequence>
<xs:element name="EnviarA"
type="TipoDireccion" />
<xs:element name="FacturarA"
type="TipoDireccion" />
</xs:sequence>
</xs:complexType>
</xs:element>
Crear otra tabla relacional anidada.
Aadir un nuevo elemento a Pedidos. Llmele Items y establezca
que su tipo es Unnammed complexType.

En el elemento Items, agregue un elemento, dle el nombre Item y
establezca que su tipo es Unnammed complexType. Cuando escriba
el elemento Item como annimo, se agrega un elemento adicional a
la superficie de diseo, que es otra tabla relacional:

Aada los siguientes elementos al objeto Item: Cantidad (tipo
integer), Precio (tipo decimal) e IDProducto (tipo integer).

Finamente, como el elemento Item puede repetirse indefinidamente
para un elemento Items seleccionar en el diseador de esquemas el
elemento Item pinchando sobre su nombre y en la ventana de
Propiedades establecer que minOccurs sea 1 y que maxOccurs sea
unbounded.
El cdigo XML (completo) asociado a este esquema ser, finamente:
<?xml version="1.0" encoding="utf-8" ?>
<xs:schema id="EsquemaPedido"
targetNamespace="http://tempuri.org/EsquemaPedido.xsd"
elementFormDefault="qualified"
xmlns="http://tempuri.org/EsquemaPedido.xsd"
xmlns:mstns="http://tempuri.org/EsquemaPedido.xsd"
xmlns:xs="http://www.w3.org/2001/XMLSchema">

<xs:simpleType name="CodigoProvincia">
<xs:restriction base="xs:string">
<xs:length value="2" />
</xs:restriction>
</xs:simpleType>

<xs:complexType name="TipoDireccion">
<xs:sequence>
<xs:element name="Nombre" type="xs:string" />
<xs:element name="Calle" type="xs:string" />
<xs:element name="Ciudad" type="xs:string" />
<xs:element name="Provincia"
type="CodigoProvincia" />
<xs:element name="CodPostal"
type="xs:integer" />
</xs:sequence>
</xs:complexType>

<xs:element name="Pedidos">
<xs:complexType>
<xs:sequence>
<xs:element name="EnviarA"
type="TipoDireccion" />
<xs:element name="FacturarA"
type="TipoDireccion" />
<xs:element name="Items">
<xs:complexType>
<xs:sequence>
<xs:element name="Item" minOccurs="1"
maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="Cantidad"
type="xs:integer" />
<xs:element name="Precio"
type="xs:decimal" />
<xs:element name="IDProducto"
type="xs:integer" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>

Crear ficheros de datos XML, asociar esquemas y validar datos.
Los ficheros XML son ficheros de texto y como tales pueden crearse con cualquier
editor que guarde los documentos en formato texto. Nosotros usaremos, por
comodidad, Visual Studio .NET para crear un fichero de datos XML.
En el men Proyecto, seleccione Agregar nuevo elemento (o situados en el
explorador de soluciones, pinchar con el botn derecho sobre EjemploEsquema y
seleccionar Agregar y Agregar nuevo elemento) y, a continuacin, haga doble clic
en el icono Archivo XML en el cuadro de dilogo Agregar nuevo elemento.
Cambiar el nombre por PedidosSeptiembre. Aparecer una ventana de edicin
conteniendo nicamente:
<?xml version="1.0" encoding="utf-8" ?>
listo para empezar a trabajar con l.
En primer lugar asociaremos a este fichero XML el esquema que creamos
anteriormente. En la ventana de Propiedades, seleccionar DOCUMENT, y en la
propiedad targetSchema seleccionar el esquema de la lista desplegable:

que hace que el fichero XML tenga el siguiente contenido:
<?xml version="1.0" encoding="utf-8" ?>
<Pedidos xmlns="http://tempuri.org/EsquemaPedido.xsd">
</Pedidos>
Observar que automticamente se ha escrito la etiqueta correspondiente al
elemento raiz Pedidos. Visual Studio .NET ayuda a la introduccin de datos, ya
que al escribir el ngulo de apertura (<) nos indica qu elementos podemos
utilizar:

Esta ayuda es contextual, por lo que si estamos completando el elemento EnviarA,
la ayuda ofrecida ser:

Completar hasta escribir lo siguiente:
<?xml version="1.0" encoding="utf-8" ?>
<Pedidos xmlns="http://tempuri.org/EsquemaPedido.xsd">
<EnviarA>
<Calle>C/ Recogidas, 23</Calle>
<Ciudad>Granada</Ciudad>
<CodPostal>18003</CodPostal>
<Nombre>Joaqun Nez Argelles</Nombre>
<Provincia>Granada</Provincia>
</EnviarA>
<FacturarA>
<Calle>C/ Motril, 44</Calle>
<Ciudad>Albolote</Ciudad>
<CodPostal>18220</CodPostal>
<Nombre>VISTUDNET S.A.</Nombre>
<Provincia>Granada</Provincia>
</FacturarA>
<Items>
<Item>
<Cantidad>1</Cantidad>
<IDProducto>11111</IDProducto>
<Precio>200.45</Precio>
</Item>
<Item>
<Cantidad>3</Cantidad>
<IDProducto>11122</IDProducto>
<Precio>100.22</Precio>
</Item>
</Items>
</Pedidos>

Si cambiamos el modo de vista a Datos encontramos una interfaz cmoda que
permite aadir, modificar, borrar, etc:

Finalmente, si queremos validar los datos XML (PedidosSeptiembre.xml) respecto
al esquema (EsquemaPedido.xsd) seleccionaremos XML en el men principal y
Validar datos XML. Si todo es correcto en la barra de estado se indica que No se
encontraron errores de validacin.
Un ejercicio: disear un esquema XML
Se trata de realizar un esquema XML y de crear un fichero de datos para guardar
los datos de nuestra biblioteca personal. La biblioteca consta de una serie
(indeterminada en nmero) de libros. Cada libro se identifica por: ttulo, autor,
nmero de pginas, precio y tipo de encuadernacin. Para hacer ms fcil su
clasificacin se quieren registrar los gneros (literarios) en los que puede
encuadrarse el libro, teniendo en cuenta que un libro puede tener asociado un
nmero mximo de cinco gneros. Finalmente, queremos tener en cuenta si el
libro est disponible o no (se supone entonces que lo hemos prestado).
Antes de empezar con el esquema debemos tener en cuenta algunas
consideraciones que nos facilitarn la tarea de diseo:
El precio de un libro debe ser de tipo real (con decimales...) y el valor
mnimo debe ser 0.
Queremos que la etiqueta de cada gnero tenga una longitud mnima
de 1 carcter.
El nmero de pginas debe ser un valor entero y positivo.
1. Crear proyecto y agregar esquema XML.
Crear un proyecto de Visual C# llamado Bibioteca y agregar un esquema XML
llamado EsquemaBiblioteca.
2. Definir los tipos de datos.
Se trata de definir en primer lugar los tipos simples y a continuacin los tipos
complejos que hagan uso de los tipos simples ya definidos. Dejaremos los
gneros para el final.
Arrastre un objeto simpleType hasta la superficie del diseador y
cambie el nombre del tipo por TipoPrecio, establezca el tipo base a
float y restrinja su valor mnimo a 0.0: colquese en la primera
columna de la fila siguiente, seleccione facet en la lista desplegable,
colquese en la siguiente celda, seleccione minInclusive en la lista
desplegable y establezca el valor 0.0 en la siguiente columna.

Agregar un tipo complejo para representar a cada libro. El tipo
complejo TipoLibro define el conjunto de elementos que aparecer
en cualquier elemento con tipo TipoLibro. Arrastre un objeto
complexType hasta la superficie del diseador y cambie el nombre
del tipo por TipoLibro, aada los elementos al tipo complejo
siguiendo las indicaciones de la figura:

3. Crear una tabla relacional.
La biblioteca consta de una serie o secuencia indefinida de elementos de tipo
TipoLibro. Se trata de aadir un elemento al esquema arrastrando un objeto de
tipo element a la superficie de dieo
Cambie element1 a Biblioteca para asignar un nombre al elemento. Puede dejar el
tipo de datos como queda por defecto: (Biblioteca).

En el elemento Biblioteca, agregue un elemento, dle el nombre Libro y establezca
que su tipo es TipoLibro:

Como el elemento Libro puede repetirse indefinidamente para un elemento
Biblioteca seleccionar en el diseador de esquemas el elemento Libro pinchando
sobre su nombre y en la ventana de Propiedades establecer que minOccurs sea 1
y que maxOccurs sea unbounded.
4. Gestin de los gneros.
Recordemos que cada libro tiene, al menos, un gnero asociado y que queremos
restringir el nmero mximo de gneros a 5. La idea es que si un libro tiene, por
ejemplo, dos gneros, su cdigo XMl ser as:
...
<Biblioteca>
<Libro>
<Autor>Arturo Prez Reverte</Autor>
<Titulo>El capitn Alatriste</Titulo>
...
<Generos>
<Genero>Novela</Genero>
<Genero>Historica</Genero>
</Generos>
</Libro>
...
</Biblioteca>

Cree el tipo simple TipoGenero que est basado en el tipo string
restringiendo la longitud mnima a 1.

Aada al tipo complejo TipoLibro un nuevo elemento lamado
Generos de tipo Unnamed conplexType. Observar que al aadir el
elemento Generos como annimo, se agrega un elemento adicional
a la superficie de diseo, que es otra tabla relacional:

Aada un elemento al objeto Generos llamdo Genero de tipo
TipoGenero. Finamente, como el elemento Genero puede repetirse
entre 1 y 5 veces seleccionar en el diseador de esquemas el
elemento Genero pinchando sobre su nombre y en la ventana de
Propiedades establecer que minOccurs es 1 y que maxOccurs es 5:

El cdigo XML (completo) asociado a este esquema (EsquemaBiblioteca.xsd)
ser, finamente:
<?xml version="1.0" encoding="utf-8" ?>
<xs:schema id="EsquemaBiblioteca"

targetNamespace="http://tempuri.org/EsquemaBiblioteca.xsd"
elementFormDefault="qualified"
xmlns="http://tempuri.org/EsquemaBiblioteca.xsd"
xmlns:mstns="http://tempuri.org/EsquemaBiblioteca.xsd"
xmlns:xs="http://www.w3.org/2001/XMLSchema">

<xs:simpleType name="TipoPrecio">
<xs:restriction base="xs:float">
<xs:minInclusive value="0.0" />
</xs:restriction>
</xs:simpleType>

<xs:simpleType name="TipoGenero">
<xs:restriction base="xs:string">
<xs:minLength value="1" />
</xs:restriction>
</xs:simpleType>

<xs:complexType name="TipoLibro">
<xs:sequence>
<xs:element name="Titulo" type="xs:string" />
<xs:element name="Autor" type="xs:string" />
<xs:element name="NumPags"
type="xs:positiveInteger" />
<xs:element name="Precio" type="TipoPrecio"/>
<xs:element name="Generos">
<xs:complexType>
<xs:sequence>
<xs:element name="Genero"
type="TipoGenero"
maxOccurs="5" minOccurs="1" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="Disponible" type="xs:boolean" />
</xs:complexType>

<xs:element name="Biblioteca">
<xs:complexType>
<xs:sequence>
<xs:element name="Libro" type="TipoLibro"
minOccurs="1" maxOccurs="unbounded" />
</xs:sequence>
</xs:complexType>
</xs:element>

</xs:schema>

5. Creacin de un fichero XMl coherente con el esquema.
Aadir al proyecto un fichero XML (men Proyecto, Agregar nuevo elemento,
Archivo XML) llamado LibrosDeCasa.xml.
Asociar a este fichero XML el esquema EsquemaBiblioteca.xsd: En la ventana de
Propiedades, seleccionar DOCUMENT, y en la propiedad targetSchema
seleccionar el esquema de la lista desplegable.
Escribir el contenido del fichero y validar los datos contra el esquema. Utilizar
estos datos para el ejemplo, aadiendo ms si lo considera oportuno.
<?xml version="1.0" encoding="utf-8" ?>
<Biblioteca
xmlns="http://tempuri.org/EsquemaBiblioteca.xsd">

<Libro Disponible="false">
<Titulo>El Rabino</Titulo>
<Autor>Noah Gordon</Autor>
<NumPags>650</NumPags>
<Precio>100.0</Precio>
<Generos>
<Genero>Religioso</Genero>
<Genero>Novela</Genero>
<Genero>Moderna</Genero>
</Generos>
</Libro>

<Libro Disponible="true">
<Titulo>El Mdico</Titulo>
<Autor>Noah Gordon</Autor>
<NumPags>890</NumPags>
<Precio>200.0</Precio>
<Generos>
<Genero>Novela</Genero>
<Genero>Histrica</Genero>
</Generos>
</Libro>

<Libro Disponible="true">
<Titulo>La Sangre de Dios</Titulo>
<Autor>Nicholas Wilcox</Autor>
<NumPags>300</NumPags>
<Precio>300.0</Precio>
<Generos>
<Genero>Novela</Genero>
<Genero>Ciencia Ficcin</Genero>
</Generos>
</Libro>

<Libro Disponible="true">
<Titulo>El Capitn Alatristre</Titulo>
<Autor>Arturo Prez Reverte</Autor>
<NumPags>450</NumPags>
<Precio>100.0</Precio>
<Generos>
<Genero>Histrica</Genero>
<Genero>Novela</Genero>
</Generos>
</Libro>

</Biblioteca>

Presentacin y transformacin de documentos XML

Hojas de estilo CSS
CSS es el acrnimo de Cascading Style Sheet (hoja de estilo en cascada).
CSS es un lenguaje de hojas de estilo que se utiliza normalmente para controlar la
presentacin de documentos HTML, pero tambin se puede utilizar con
documentos XML. Su principal caracterstica, en contraste con el lenguaje XSLT,
es su sencillez.
Ventajas:
Fcil de aprender y utilizar. Muchos desarrolladores ya lo conocen.
No requiere la creacin de una pgina HTML para visualizar cdigo XML.
Consume poca memoria y tiempo de proceso, pues no construye una
representacin en rbol del documento.
Muestra el documento segn se va procesando.
Desventajas:
Utiliza una sintaxis diferente a la del XML.
Slo sirve para visualizar documentos en un navegador.
No es muy flexible:
o No permite realizar manipulaciones sobre el documento, tales como
aadir y borrar elementos, realizar ordenaciones, etc.
o Slo permite acceder al contenido de los elementos, no a los
atributos, no permite instrucciones de proceso, etc.
Para usar una hoja de estilo CSS para presentar el contenido de un documento
XML hay que aadir la siguiente lnea en el prlogo:
<?xml-stylesheet type="text/css" href="fichero.css" ?>
donde fichero.css es el nombre del fichero CSS que contiene las reglas de
formato.
Un ejemplo sencillo
Un fichero de estilo sencillo MuySencillo.css asociado al fichero LibrosDeCasa.xml
podra ser:
/* Fichero: MuySencillo.css */

Titulo
{
display:block;
margin-top:12pt;
font-size:15pt
}
Autor
{
display:block;
color:Aqua;
font-style:italic
}
Precio
{
font-weight:bold
}
NumPags
{
font-size: 10pt;
left: 30pt;
color: maroon;
font-style: italic;
font-family: Arial;
position: static;
top: 10pt;
}
Generos
{
display: none;
}
Si se aade la lnea oportuna en LibrosDeCasa.xml para usar el fichero de estilo
MuySencillo.css:
<?xml version="1.0" encoding="utf-8" ?>

<?xml-stylesheet type="text/css"
href="MuySencillo.css" ?>

<Biblioteca
xmlns="http://tempuri.org/EsquemaBiblioteca.xsd">

<Libro Disponible="false">
<Titulo>El Rabino</Titulo>
<Autor>Noah Gordon</Autor>
<NumPags>650</NumPags>
......

el resultado, visto en un navegador ser:

Hojas de estilo CSS en Visual Studio .NET
Visual Studio .NET proporciona facilidades para asociar a un proyecto hojas de
estilo CSS y para construirlas empleando un asistente. Su uso es muy sencillo y
pueden construirse hojas de estilo complejas sin necesidad de conocer nada
acerca de CSS.
En primer lugar veremos cmo crear una hoja de estilo con Visual Studio .NET.
En el men Proyecto, seleccione Agregar nuevo elemento y, a continuacin, haga
clic en el icono Hoja de estilos en el cuadro de dilogo Agregar nuevo elemento.
Cambiar el nombre por MiHojaCSS.css. Aparecer el Diseador de hojas CSS:

Se trata de especificar el formato de cada elemento del documento XML
especificando una regla para cada elemento.
Como no hay ningn elemento llamado body lo borramos y escribimos, por
ejemplo, Titulo. Observar como se modifica el explorador de elementos en la parte
izquierda del diseador CSS:

Ahora podremos especificar la transformacin asociada al elemento Titulo. El
formato genrico es el que se muestra en el ejemplo:

Para establecer el formato de los elementos Titulo tenemos tres posibilidades:
A) Pinchar con el botn derecho sobre Titulo en el explorador de elementos
y seleccionar Generar estilo.
B) En la ventana Propiedades, en la propiedad Style pinchar en el botn de
elipsis.
C) Escribir manualmente las transformaciones a aplicar. Requiere mucha
experiencia.

Se abrir la ventana de dilogo Generador de estilos que nos permite definir
fcilmente el estilo del elemento seleccionado. El usuario selecciona categoras de
formato (fuente, color, etc) y especifica valores y Visual Studio .NET escribe en el
fichero CSS el cdigo CSS correspondiente.
Dominar este asistente es cuestin de experimentar con l... Sin embargo debe
saber dos cosas importantes:
Solo aparecen con formato los elementos que se han especificado y
configurado.
Aparecern todos los elementos y aquellos que no hatan sido configurado
se mostrarn de cualquier manera.
Si no quiere que aparezca un elemento debe especifcarlo en el formato. Por
ejemplo, si solo quisiera mostrar los autores de los libros se aadira el
elemento Autor y se definira el formato siguiente: Diseo, Mostrar y No
mostrar
Completar la definicin de formatos en MiHojaCSS.css para que quede as:
/* Fichero: MiHojaCSS.css */

Titulo
{
display: block;
font-weight: normal;
font-size: 15pt;
text-transform: capitalize;
color: red;
}
Generos
{
display: none;
}
Autor
{
display: none;
}
Precio
{
display: none;
}
NumPags
{
display: none;
}

y comprobar que el resultado es:

Transformaciones XSLT
Introduccin
XSLT es el acrnimo de EXtensible Stylesheet Languaje Transformation.
XSL es un lenguaje que nos permite definir una representacin o formato pra un
documento XML. Un mismo documento XML puede tener varias hoas de estilo
XSL que lo muestren en diferentes formatos (HTML, PDF, RTF, PostScript, etc.)
Bsicamente, XSL es un lenguaje que define la transformacin entre un
documento XML de entrada y otro documento XML de salida.
La aplicacin de una hoja de estilo XSL a un documento XML puede ocurrir tanto
en el origen (por ejemplo, un servlet que convierta de XML a HTML para que sea
mostrado en el navegador de un ordenador conectado a un servidor web) o en el
mismo navegador (por ejemplo, Internet Explorer 6).
Para aclarar terminologa, XSL es un estndar que consiste en:
XSLT: Lenguaje para transformar documentos XML en otro formato (otro
XML, HTML, DHTML, texto plano, PDF, RTF, Word, etc.)
XSL-FO (XSL Formatting Objects): Especificacin que trata cmo deben
ser los objetos de formato para convertir XML a formatos binarios (PDF,
Word, imgenes, etc.). Todava no ha alcanzado el estado de
"recomendado por la W3C".
XPath: Lenguaje de consulta genrico para identificar (y seleccionar)
elementos de un documento XML.
La principal caracterstica del lenguaje XSLT es su potencia. No es slo un
lenguaje para visualizar documentos, sino en general para transformarlos y
manipularlos. Esta manipulacin la gestiona un programa especial que se llama
procesador XSLT.
Ventajas y desventajas
Ventajas:
La salida no tiene por qu ser HTML para visualizacin en un navegador,
sino que puede estar en muchos formatos.
Permite manipular de muy diversas maneras un documento XML: reordenar
elementos, filtrar, aadir, borrar, etc.
Permite acceder a todo el documento XML, no slo al contenido de los
elementos.
XSLT es un lenguaje XML, por lo que no hay que aprender nada especial
acerca de su sintaxis.
Desventajas:
Su utilizacin es ms compleja.
Consume cierta memoria y capacidad de proceso, pues se construye un
rbol con el contenido del documento.
Formas de uso
Visualizar directamente en un navegador el documento XML que tiene
asociada una hoja XSLT. El navegador debe tener incorporado un
procesador XSLT.
Ejecutar el procesador XSLT independientemente del navegador. Se le
pasan las entradas necesarias (fichero origen y hoja XSLT a utilizar) y
genera la salida en un fichero, con el que podemos hacer lo que queramos.
Realizar las transformaciones dentro de un programa en el servidor y enviar
a los clientes slo el resultado de la transformacin.


Crear documentos XSLT en Visual Studio .NET
En el men Proyecto, seleccione Agregar nuevo elemento y, a continuacin, haga
clic en el icono Archivo XSLT en el cuadro de dilogo Agregar nuevo elemento.
Cambiar el nombre por XSLTSencillo.xslt.
Muy importante: Siempre se emplea el espacio de nombres
xmlns="http://www.w3.org/1999/XSL/Transform". Modificar el cdigo XML que
escribe Visual Studio .NET en el fichero XSLTSencillo.xslt para que el prefijo del
espacio de nombres sea xsl:
<!-- Fichero: XSLTSencillo.XSLT -->

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

</xsl:stylesheet>

Para usar un fichero XSLT desde un documento XML hay que aadir la siguiente
lnea en el prlogo:
<?xml-stylesheet type="text/xsl" href="fichero.xslt" ?>
donde fichero.xslt es el nombre del fichero XSLT.
Por ejemplo, modificaremos el fichero LibrosDeCasa.xml para que quede como
sigue:
<!-- Fichero: LibrosDeCasa.xml -->

<?xml version="1.0" encoding="utf-8" ?>

<?xml-stylesheet type="text/xsl" href="XSLTSencillo.xslt" ?>

<Biblioteca>

<Libro Disponible="false">
<Titulo>El Rabino</Titulo>
<Autor>Noah Gordon</Autor>
<NumPags>650</NumPags>
......
Solo falta escribir las transformaciones a aplicar en el fichero XSLT ...
Escribir transformaciones XSLT
La idea es muy sencilla y todo est basado en considerar que los
elementos que forman el documento XML mantienen una relacin
jerrquica: son nodos del rbol que determina la relacin entre los
elementos del documento XML.
Una hoja de estilo XSL consta de una serie de reglas que determinan cmo
va a ocurrir la transformacin. Cada regla se compone de:
o patrn (pattern).
o plantilla (template) o accin.
Cada regla afecta a uno o varios elementos del documento XML.
Sintcticamente, las reglas tienen tres partes:
o La marca de apertura que contiene un atributo match que describe a
qu partes del documento se aplica la regla (qu nodos estn
afectados). La sintaxis del patrn (valor del atributo match) debe
seguir las especificaciones del lenguaje XPath. En definitiva: un
patrn es una expresin XPath.
o La parte central describe qu debe hacerse cuando se produce una
coincidencia.
o La marca de cierre.
<xsl:template match="PATRON"> <!-- Apertura -->
<!-- Esta es la parte central -->
</xsl:template> <!-- Cierre -->
As, cada elemento template se asocia con un fragmento del documento
XML (que puede ser un elemento o un conjunto de elementos) y se
transforma en otro fragmento de XML o HTML, de acuerdo a lo que se
especifique en su interior.
RESUMEN: Cmo se realiza la transformacin?
El documento origen se pasa al procesador XSLT.
o (El procesador carga una hoja de estilo XSLT)
El procesador entonces:
o Carga los patrones especificadas en la hoja de estilo...
o Recorre el documento XML origen, nodo por nodo...
o Si un nodo se ajusta a un patrn:
Aplica las acciones especificadas, y
Proporciona el resultado en un nuevo documento en formato
XML o HTML.
Otros elementos de XSL
Adems de xsl:template, los elementos del espacio de nombres xsl
(instrucciones XSL?) que nos ayudan a escribir transformaciones son:
xsl:apply-templates hace que se apliquen las reglas que siguen a todos los
nodos seleccionados.
<xsl:apply-templates />
Puede restringirse con el atributo select para especificar un subconjunto de
nodos.
<xsl:apply-templates select="PATRON">
.....
<xsl:apply-templates />
xsl:value-of extrae un valor concreto (literal) del documento.
xsl:for-each aplica una accin repetidamente para cada nodo de un
conjunto. En definitiva, se usa para iterar sobre una serie de elementos.
<xsl:for-each select="PLANTILLA">
.....
<xsl:for-each />

xsl:if sirve para evaluar condiciones sobre valores de atributos o elementos.
<xsl:if test="EXPRESIN LGICA">
.....
<xsl:if />
xsl:choose sirve para evaluar condiciones mltiples (tipo switch).
<xsl:choose>
<xsl:when test="EXPRESIN 1"> ... </xsl:when />
<xsl:when test="EXPRESIN 2"> ... </xsl:when />
.....
<xsl:otherwise> ... </xsl:otherwise />
<xsl:choose />
xsl:sort ordena un conjunto de nodos de acuerdo a algn elemento. Por
ejemplo, para ordenar de forma creciente segn el nmero de pginas:
<xsl:sort select="/Biblioteca/Libro/NumPags"
order="ascending" />
En este ejemplo hemos hecho uso de un patrn algo complejo que
responde a una expresin de XPath que puede interpretarse as: De la raiz
del documento XML (todo el documento) seleccionar los elementos
Biblioteca y de stos, los que tengan como etiqueta Libro. Finalmente, de
todos stos seleccionaremos nicamente los elementos NumPags.
XPath
Los patrones pueden ser muy complejos y, como hemos indicado, se especifican
en un lenguaje llamado XPath. Es un lenguaje de consulta usado para identificar y
seleccionar nodos (elementos) de un documento XML. Se caracteria por:
Es declarativo (vs. procedimental).
Es contextual ya que los resultados dependen del nodo "actual".
Admite expresiones comunes: operadores de comparacin, lgicos y
matemticos (=, <, and, or, *, +, etc.)
Los operados empleados habitualmente para formar los patrones se describen en
la siguiente tabla:
Operador Descripcin
/
Seleccin de hijo. Selecciona nicamente a los
descendientes directos. Al principio del patrn el
contexto es la raz del documento.
//
Seleccin de descendientes. Selecciona todos los
descendientes.
. Seleccin del elemento actual (el contexto).
* Todos (en el sentido habitual de este operador)
@ Prefijo que se antepone al nombre de un atributo.
[ ] Filtro sobre el conjunto de nodos seleccionado.
Algunos ejemplos:
./AUTOR Selecciona todos los elementos AUTOR dentro del contexto actual
(todos los hijos del nodo actual que tengan como etiqueta AUTOR).
/LIBROS Selecciona los elementos (posiblemente uno solo) con etiqueta LIBROS
que cuelgan directamente de la raiz.
//AUTOR Selecciona todos los elementos AUTOR en cualquier parte del
documento.
/LIBROS[@TEMA="XML"] Selecciona todos los elementos LIBROS de la raiz (los
seleccionados antes) y de stos selecciona aquellos que tengan el valor XML en el
atributo TEMA.
Ejemplo 1
Se trata de mostrar una lista con todos los libros. Se incluye un encabezado y solo
se muestra el ttulo de cada libro.
<!-- Fichero: XSLTSencillo.xslt -->

<?xml version="1.0" encoding="UTF-8" ?>

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="/Biblioteca">
<h2>LISTA DE LIBROS</h2>
<xsl:apply-templates />
<hr/>
</xsl:template>

<xsl:template match="Libro">
<p><xsl:value-of select="Titulo" /></p>
</xsl:template>
</xsl:stylesheet>
Recordemos que en el fichero XML (LibrosDeCasa.xml) debemos indicar el fichero
de transformacin:
<!-- Fichero: LibrosDeCasa.xml -->

<?xml version="1.0" encoding="utf-8" ?>

<?xml-stylesheet type="text/xsl"
href="XSLTSencillo.xslt"?>

<Biblioteca>
......

Ejemplo 2
Vamos a generar como salida cdigo HTML. La informacin a mostrar es el ttulo
del libro y el autor. Usaremos el siguiente fichero XSLT:
<!-- Fichero: XSLTSencilloHTML.xslt -->

<?xml version="1.0" encoding="UTF-8" ?>

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="/">
<HTML>
<HEAD><TITLE>Lista de libros</TITLE></HEAD>
<BODY BGCOLOR="#FFFF00">
<xsl:apply-templates />
</BODY>
</HTML>
</xsl:template>

<xsl:template match="Biblioteca">
<H2>Mis libros de casa:</H2>
<xsl:apply-templates />
<hr/>
</xsl:template>

<xsl:template match="Libro">
<P>
<xsl:apply-templates select="Titulo"/>
(<xsl:apply-templates select="Autor"/>)
</P>
</xsl:template>
<xsl:template match="Titulo">
<FONT COLOR="#0000FF">
<xsl:value-of select="." />
</FONT>
</xsl:template>

<xsl:template match="Autor">
<FONT COLOR="#FF0000">
<xsl:value-of select="." />
</FONT>
</xsl:template>

</xsl:stylesheet>
El fichero XML quedar ahora:
<!-- Fichero: LibrosDeCasa.xml -->

<?xml version="1.0" encoding="utf-8" ?>

<?xml-stylesheet type="text/xsl"
href="XSLTSencilloHTML.xslt"?>

<Biblioteca>
......


Ejemplo 3
En este ejemplo practicamos con la construccin iterativa xsl:for-each para
mostrar un informe detallado de cada libro.
<!-- Fichero: InformeGeneros.xslt -->

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<HTML>
<HEAD><TITLE>Informe por gneros</TITLE></HEAD>
<BODY BGCOLOR="#D2D2D2">
<H2>Informe detallando los gneros</H2>
<xsl:for-each select="/Biblioteca/Libro">
<HR />
<B>Titulo: </B>
<I><xsl:value-of select="Titulo" /></I><BR />
<B>Autor : </B>
<I><xsl:value-of select="Autor" /></I><BR/>
<B>Temas: </B>
<xsl:for-each select="Generos/Genero">
<xsl:value-of select="." />,
</xsl:for-each>
<HR />
</xsl:for-each>
</BODY>
</HTML>
</xsl:template>
</xsl:stylesheet>

Ejemplo 4
En este ejemplo practicamos con la construccin de seleccin condicional xsl:if
para mostrar una lista de libros voluminosos.
<!-- Fichero: ListaVoluminosos.xslt -->

<?xml version="1.0" encoding="UTF-8" ?>

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<!-- *********************************************** -->

<xsl:template match="/">
<HTML>
<xsl:apply-templates select="Biblioteca" />
</HTML>
</xsl:template>

<!-- *********************************************** -->

<xsl:template match="Biblioteca">
<HEAD><TITLE>Libros gordos</TITLE></HEAD>
<BODY BGCOLOR="#FAFAFA">
<H2>Mis libros voluminosos</H2>

<P>Los libros de la biblioteca que tienen ms de
500 pginas son: </P>

<OL>
<xsl:apply-templates select="Libro" />
</OL>
</BODY>
</xsl:template>

<!-- *********************************************** -->

<xsl:template match="Libro">

<xsl:if test="NumPags &gt; 500">
<LI><B><xsl:value-of select="Titulo"/></B>
(<I><xsl:value-of select="Autor"/></I>):
<xsl:value-of select="NumPags"/> pgs.
</LI>
</xsl:if>

</xsl:template>

<!-- *********************************************** -->

</xsl:stylesheet>

Si quisiramos hacer ms verstil esta hoja de estilo y quisiramos que el nmero
de pginas mnimo fuera fcilmente modificable, de manera que solo hubiera que
hacer un cambio para generar una nueva lista podramos usar una variable o, en
la terminologa adecuada, un elemento xsl:parameter. Los cambios a realizar
seran los siguientes:
"Declarar" una variable y darle un valor:
<!-- Fichero: ListaVoluminosos.xslt -->

<?xml version="1.0" encoding="UTF-8" ?>

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:param name="NumPagsVol" select="500" />
......
Cambiar las referencias explcitas al valor por su nombre:
o Si va a mostrarse textualmente:
......
<P>Los libros de la biblioteca que tienen ms de
<xsl:value-of select="$NumPagsVol"/>
pginas son: </P>
......
o Si se va a usar en una expresin:
......
<xsl:if test="NumPags &gt; $NumPagsVol">
......
Ejemplo 5
En este ejemplo practicamos con casi todos los elementos para construir una tabla
que recopila toda la informacin del documento XML.
<!-- Fichero: TablaResumen.xslt -->

<?xml version="1.0" encoding="UTF-8" ?>

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<!-- *********************************************** -->

<xsl:template match="/">

<HTML>
<HEAD><TITLE>Tabla ordenada de
libros</TITLE></HEAD>

<xsl:apply-templates select="Biblioteca" />
</HTML>

</xsl:template>

<!-- *********************************************** -->

<xsl:template match="Biblioteca">

<BODY BGCOLOR="#FAFAFA">

<H2 ALIGN="CENTER">Mis libros ordenados segn
el nmero de paginas</H2>

<TABLE ALIGN="CENTER" BORDER="2">
<encabezado-tabla>
<TR
BGCOLOR="#FFFF00"><TH>Titulo</TH><TH>Autor</TH>

<TH>Paginas</TH><TH>Precio</TH><TH>Disponible</TH>
<TH>Generos</TH></TR>
</encabezado-tabla>

<xsl:for-each select="Libro">

<xsl:sort select="NumPags" order="descending" />

<cuerpo-tabla>
<TR>
<TD><xsl:value-of select="Titulo"/></TD>
<TD><xsl:value-of select="Autor"/></TD>
<TD><xsl:value-of select="NumPags"/></TD>
<TD><xsl:value-of select="Precio"/></TD>
<TD ALIGN="CENTER">
<xsl:if test="@Disponible='true'">
<FONT COLOR="#00FF00"><B>SI</B></FONT>
</xsl:if>
<xsl:if test="@Disponible='false'">
<FONT
COLOR="#FF0000"><B>NO</B></FONT>
</xsl:if>
</TD>
<TD>
<xsl:for-each select="Generos/Genero">
<xsl:value-of select="." /> -
</xsl:for-each>
</TD>
</TR>
</cuerpo-tabla>

</xsl:for-each>

</TABLE>

</BODY>

</xsl:template>

<!-- *********************************************** -->

</xsl:stylesheet>






XML en la plataforma .NET

Modelo de objetos de documento (DOM)
Introduccin
DOM (Document Object Model o modelo de objetos de documento) es un
estndard de la W3C que especifica la forma de acceder y manipular los datos de
un documento XML mediante un programa.
DOM especifica una representacin en la memoria de un documento XML de
manera jerrquica mediante un rbol de nodos (elementos, comentarios,
entidades, atributos, etc).

En la figura siguiente se muestra cmo se estructura la memoria cuando se leen
los datos de la biblioteca (XML) en la estructura DOM.

Dentro de la estructura de los documentos XML, cada elipse de esta ilustracin
representa un nodo, que se denomina objeto XmlNode. El objeto XmlNode es el
objeto bsico del rbol DOM. La clase XmlDocument, que extiende la clase
XmlNode, admite mtodos para realizar operaciones en el documento en conjunto,
por ejemplo, cargarlo en la memoria o guardar el cdigo XML en un archivo.
Adems, la clase XmlDocument proporciona un medio para ver y manipular los
nodos de todo el documento XML.

Tipos de nodos
A medida que el contenido XML se lee en el DOM las partes se traducen en
nodos que mantienen metadatos adicionales acerca de s mismos, como su tipo y
valores. Esto es, cuando se leen varios datos, se asigna a cada nodo un tipo. Esto
es importante ya que no todos los nodos son del mismo tipo y e tipo determina las
caractersticas y funcionalidad del nodo (qu acciones pueden realizarse y qu
propiedades pueden establecerse o recuperarse).
En la tabla siguiente se muestran algunos tipos de nodo, el objeto asociado a
dicho tipo y una breve dscripcin.
Tipo de nodo
DOM
Objeto (clase
.NET)
Descripcin
Document XmlDocument
Contenedor de todos los
nodos del rbol. Tambin
se conoce como la raz del
documento, que no
siempre coincide con el
elemento raz.
Element XmlElement
Representa un nodo de
elemento.
Attr XmlAttribute Atributo de un elemento.
Comment XmlComment Nodo de comentario.
Text XmlText
Texto que pertenece a un
elemento o atributo.
CDATASection XmlCDataSection Representa CDATA.
Los objetos DOM tienen un conjunto de mtodos y propiedades, as como
caractersticas bsicas y bien definidas. Algunas de estas caractersticas son:
Un nodo tiene un nico nodo primario, que se encuentra
directamente encima de l. El nico nodos que no tiene un nodo
primario es la raz.
La mayor parte de los nodos pueden tener varios nodos secundarios,
que son los que estn situados inmediatamente debajo de ellos. Los
tipos de nodo que pueden tener nodos secundarios son:
o Document
o DocumentFragment
o EntityReference
o Element
o Attribute
Los nodos que se encuentran en el mismo nivel son nodos
relacionados.
Acceso a los atributos
La forma de controlar los atributos es una caracterstica de DOM. Los atributos no
son elementos secundarios de un elemento sino propiedades del elemento y estn
formados por un par nombre-valor.
Es importante hacer esta distincin, debido a los mtodos utilizados para
desplazarse por los nodos relacionados, principales y secundarios del DOM. Por
ejemplo, los mtodos PreviousSibling y NextSibling no se utilizan para desplazarse
de un elemento a un atributo, ni entre atributos. En su lugar, un atributo es una
propiedad de un elemento y pertenece a un elemento, tiene una propiedad
OwnerElement y no una propiedad parentNode, y tiene mtodos de
desplazamiento distintos.
Conclusiones
DOM resulta til para leer datos XML en la memoria y cambiar su estructura,
agregar o quitar nodos, o modificar los datos mantenidos en un nodo como
en el texto contenido en un elemento. No obstante, hay otras clases disponibles
que son ms rpidas que DOM en otros escenarios:
Para tener un acceso rpido, slo hacia delante y sin
almacenamiento en cach a secuencias de XML, utilice XmlReader
y XmlWriter.
Si necesita acceso aleatorio con un modelo de cursor y XPath,
utilice la clase XPathNavigator.
Espacio de nombres System.Xml
El espacio de nombres System.Xml proporciona compatibilidad basada en normas
para procesar XML. Se admiten las siguientes normas:
XML 1.0: http://www.w3.org/TR/1998/REC-xml-19980210, incluida
compatibilidad con DTD.
Espacios de nombres XML: http://www.w3.org/TR/REC-xml-
names/, tanto a nivel de secuencias como DOM.
Esquemas XSD: http://www.w3.org/2001/XMLSchema
Expresiones XPath: http://www.w3.org/TR/xpath
Transformaciones XSLT: http://www.w3.org/TR/xslt
Core DOM Level 1: http://www.w3.org/TR/REC-DOM-Level-1/
Core DOM Level 2: http://www.w3.org/TR/DOM-Level-2/
Ms informacin en: System.Xml (Espacio de nombres)
La plataforma .NET proporciona soporte para XML en el espacio de nombres
System.Xml y los subespacios:
System.Xml.Xsl.
El espacio de nombres System.Xml.Xsl proporciona compatibilidad
con transformaciones XSLT (Extensible Stylesheet Transformation).
Admite la Recomendacin XSL Transformations (XSLT) Version 1.0
del Consorcio W3C (www.w3.org/TR/xslt).
Ms informacin en: System.Xml.Xsl (Espacio de nombres)
System.Xml.XPath.
El espacio de nombres System.Xml.XPath contiene el motor de
evaluacin y el analizador XPath. Es compatible con la
Recomendacin XML Path Language (XPath) Version 1.0 del
Consorcio W3C (www.w3.org/TR/xpath).
Ms informacin en: System.Xml.XPath (Espacio de nombres)

System.Xml.Schema.
El espacio de nombres System.Xml.Schema contiene las clases XML
que proporcionan compatibilidad basada en normas para los
esquemas de XSD lenguaje de definicin de esquemas XML
(www.w3.org/XML/Schema).
Ms informacin en: System.Xml.Schema (Espacio de nombres)
System.Xml.Serialization.
El espacio de nombres System.Xml.Serialization contiene clases que
se utilizan para serializar objetos en secuencias o documentos con
formato XML.
Ms informacin en: System.Xml.Serialization (Espacio de nombres)
System.Xml proporciona clases para crear, modificar y navegar en documentos
XML. Tambin facilita el poder leer y escribir XML usando DOM proporcionando
clases como XPathNavigator, XmlDataDocument y XmlDocument. Adems,
permite manipular elementos XML con las clases XmlElement, XmlAttribute,
XmlComment, etc.

La clase base XmlNode representa un nodo individual de la jerarqua XML y
proporciona propiedades y mtodos para:
Recorrer jerrquicamente un documento XML.
Consulta y seleccin de nodos.
Modificar nodos.
Borrar nodos.
Ms informacin en: XmlNode (Clase)
Breve descripcin de las clases derivadas de XmlNode:
XmlDocument. Representa un documento XML segn el modelo
DOM y posibilita la exploracin y edicin del documento.
XmlLinkedNode. Recupera el nodo que antecede o sigue al nodo
actual. Es una clase abstracta de la que deriva la clase XmlElement.
XmlElement. Representa un elemento del rbol DOM. Los elementos
pueden tener atributos asociados. Dispone de muchos mtodos para
tener acceso a los atributos (GetAttribute, SetAttribute,
RemoveAttribute, GetAttributeNode, etc.). Adems, puede utilizar la
propiedad Attributes que devuelve una XmlAttributeCollection, que le
permite tener acceso a los atributos por nombre o por ndice de la
coleccin.
XmlAttribute. Representa un atributo de un XmlNode. Los valores
vlidos y predeterminados del atributo se definen en una DTD o un
esquema. Utilice la propiedad OwnerElement para obtener el
XmlElement al que pertenece el atributo.
XmlDataDocument. Extiende XmlDocument y permite que los datos
estructurados se almacenen, recuperen y manipulen mediante un
DataSet relacional. Esta clase permite que los componentes
combinen vistas XML y relacionales de los datos subyacentes.
La clase XmlDocument
La clase XmlDocument implementa la clase XmlNode y representa un documento
XML completo segn el modelo DOM y posibilita la exploracin y edicin del
documento.
Cuando se instancia la clase XmlDocument puede leerse un documento XML
utilizando el mtodo Load.
Dado que XmlDocument implementa la interfaz IXPathNavigable, tambin se
puede utilizar como documento de origen de la clase XslTransform.
Ms informacin en: XmlDocument (Clase)
Miembros de XmlDocument
Constructores

XmlDocument
Inicializa una nueva instancia de la clase
XmlDocument.
Propiedades
pblicas
Attributes
Obtiene un objeto XmlAttributeCollection
que contiene los atributos de este nodo.
ChildNodes
Obtiene todos los nodos secundarios del
nodo.
DocumentElement
Obtiene el XmlElement raz del
documento.
FirstChild
Obtiene el primer nodo secundario del
nodo.
HasChildNodes
Obtiene un valor que indica si este nodo
tiene nodos secundarios.
LatChild
Obtiene el ltimo nodo secundario del
nodo.
LocalName / Name
Obtiene el nombre local/completo del
nodo.
NextSibling
Obtiene el nodo inmediatamente
siguiente a ste.
NodeType Obtiene el tipo del nodo actual.
OwnerDocument
Obtiene el XmlDocument al que
pertenece el nodo actual.
ParentNode
Obtiene el nodo primario de este nodo
(para nodos que pueden tener nodos
primarios).
PreviousSibling
Obtiene el nodo inmediatamente anterior
a ste.
Value Obtiene o establece el valor del nodo.
Mtodos pblicos

AppendChild
Agrega el nodo especificado al final
de la lista de nodos secundarios de
este nodo.
CreateAttribute
Crea un XmlAttribute con el nombre
especificado.
CreateComment
Crea un objeto XmlComment que
contiene los datos especificados.
CreateElement Crea un objeto XmlElement.
CreateNode Crea un objeto XmlNode.
CreateTextNode
Crea un objeto XmlText con el texto
especificado.
GetElementsByTagName
Devuelve un objeto XmlNodeList
que contiene una lista de todos los
elementos descendientes que
coinciden con el nombre
especificado.
InsertAfter
Inserta el nodo especificado
inmediatamente detrs del nodo de
referencia igualmente especificado.
InsertBefore
Inserta el nodo especificado
inmediatamente antes del nodo de
referencia igualmente especificado.
Load
Carga los datos XML
especificados.
Nota: El mtodo Load conserva
siempre bastante espacio en
blanco. La propiedad
PreserveWhitespace determina si
se conserva o no espacio en
blanco. El valor predeterminado es
false, es decir, el espacio en blanco
no se conserva.
LoadXML
Carga el documento XML desde la
cadena especificada.
PrependChild
Agrega el nodo especificado al
principio de la lista de nodos
secundarios de este nodo.
RemoveAll
Quita todos los atributos y nodos
secundarios del nodo actual.
RemoveChild
Quita el nodo secundario
especificado.
ReplaceChild Reemplaza un nodo secundario.
Save
Guarda el documento XML en la
ubicacin especificada.
SelectNodes
Selecciona una lista de nodos que
coinciden con la expresin XPath.

SelectSingleNode
Selecciona el primer XmlNode que
coincide con la expresin XPath.
El siguiente cdigo copia en la consola el contenido del fichero XML
LibrosDeCasa.xml:
XmlDocument doc = new XmlDocument();
doc.Load ("../../LibrosDeCasa.xml");
doc.Save (Console.Out);
Ejemplo: cculo de estadsticas
El siguiente cdigo muestra algunas estadsticas calculadas a partir de los datos
XML:
using System;
using System.IO;
using System.Xml;

class BibliotecaApp
{
static void Main(string[] args)
{
// Crear el documento en memoria y cargarlo

XmlDocument doc = new XmlDocument();
doc.Load ("../../LibrosDeCasa.xml");

// Seleccin de libros usando expresion XPATH

XmlNodeList ListaLibros =
doc.SelectNodes ("/Biblioteca/Libro");

// Calcular el numero de libros

int nl = ListaLibros.Count;
Console.WriteLine ("Numero total de libros = {0}", nl);

double SumPrecio = 0.0;
double SumPags = 0;

// Calcular precio medio (Observar la seleccion
// que se realiza dentro de la seleccion)

foreach (XmlNode libro in ListaLibros)
{
string sP = libro.SelectSingleNode("Precio").InnerText;
double PrecioLibro = XmlConvert.ToDouble(sP);
SumPrecio += PrecioLibro;
}
Console.WriteLine (" Precio medio = {0} Euros",
SumPrecio / nl);

// Calcular tamao medio (Observar que se hace
// una nica seleccin -preferible-)

XmlNodeList ListaPrecios =
doc.SelectNodes ("/Biblioteca/Libro/NumPags");

for (int i=0; i < ListaPrecios.Count; i++)
{
int PagsLibro =
XmlConvert.ToInt16(ListaPrecios[i].InnerXml);
SumPags += PagsLibro;
}
Console.WriteLine (" Tamao medio = {0} pgs.",
SumPags / nl);


// Contar y mostrar los titulos de los libros
// disponibles. Observar la seleccion con atributo

string filtro = "/Biblioteca/Libro[@Disponible='true']";
XmlNodeList ListaDisponibles = doc.SelectNodes (filtro);

Console.WriteLine ("Libros disponibles = {0}",
ListaDisponibles.Count);

foreach (XmlNode LibroDisponible in ListaDisponibles)
{
string tit =
LibroDisponible.SelectSingleNode("Titulo").InnerText;
string aut =
LibroDisponible.SelectSingleNode("Autor").InnerText;
Console.Write (" " + tit);
Console.WriteLine (" (" + aut + ")");
}

Console.ReadLine();
}
}


Acceso a los atributos
Cuando el nodo actual es un elemento, utilice el mtodo HasAttribute para ver si
hay algn atributo asociado a dicho elemento. Una vez que se sabe que un
elemento tiene atributos, existen mltiples mtodos de acceso a atributos. Para
recuperar un nico atributo de un elemento, utilice los mtodos GetAttribute y
GetAttributeNode de XmlElement, u obtenga todos los atributos en una coleccin.
La obtencin de la coleccin resulta til si es necesario recorrerla en iteracin. Si
desea obtener todos los atributos del elemento, utilice la propiedad Attributes del
elemento para recuperar todos los atributos en una coleccin.
Recuperar todos los atributos en una coleccin.
Si desea obtener todos los atributos de un nodo de elemento en una coleccin,
llame a la propiedad XmlElement.Attributes. De este modo se obtiene la
XmlAttributeCollection que contiene todos los atributos de un elemento. Cada
elemento de la coleccin de atributos representa un nodo XmlAttribute. Para
buscar el nmero de atributos de un elemento, obtenga el XmlAttributeCollection y
utilice la propiedad Count para saber cuntos nodos XmlAttribute hay en la
coleccin.
La informacin de una coleccin de atributos puede recuperarse por nombre (ver
la lnea de cdigo XmlAttribute atr = atrCol["ISBN"] en el siguiente ejemplo) o por
nmero de ndice (sustituir la anterior por XmlAttribute atr = atrCol[1]).
using System;
using System.IO;
using System.Xml;


class BibliotecaApp
{
static void Main(string[] args)
{
// Crear el documento en memoria

XmlDocument doc = new XmlDocument();
doc.LoadXml("<Libro Genero='Novela' " +
"ISBN='1-861001-57-5' " +
"Comentario='mal estado'>" +
"<Titulo>El oro del Rey</Titulo>" +
"<Precio>14.95</Precio>" +
"</Libro>");

// Colocarse en la raiz del documento.

XmlElement Raiz = doc.DocumentElement;

// Recuperar los atributos en una coleccion.

XmlAttributeCollection atrCol = Raiz.Attributes;

// De manera resumida:
// XmlAttributeCollection atrCol =
// doc.DocumentElement.Attributes;

// Mostrar los atributos de la coleccion iterando
// sobre los elementos de la coleccion.

Console.WriteLine("Atributos en la coleccion: ");

for (int i=0; i < atrCol.Count; i++)
{
Console.Write(" {0} = ", atrCol[i].Name);
Console.Write(" {0}", atrCol[i].Value);
Console.WriteLine();
}

// Recuperar un unico atributo (el llamado ISBN)

if (Raiz.HasAttribute("ISBN"))
{
// Seleccionar nombre
XmlAttribute atr = atrCol["ISBN"];
// Seleccionar valor
String ValorISBN = atr.InnerXml;

Console.WriteLine();
Console.Write ("Valor del atributo ISBN: ");
Console.WriteLine(ValorISBN);
}
Console.ReadLine();
}
}


Recuperar un nodo de atributo individual.
Para recuperar un solo nodo de atributo de un elemento, se utiliza el mtodo
XmlElement.GetAttributeNode. Este mtodo devuelve un objeto de tipo
XmlAttribute. Una vez que se tiene el objeto XmlAttribute, todos los mtodos y
propiedades de la clase de miembros XmlAttribute estn disponibles en ese
objeto, como OwnerElement.
..........

// Colocarse en la raiz del documento.

XmlElement Raiz = doc.DocumentElement;

// Recuperar el atributo ISBN.

XmlAttribute atr = Raiz.GetAttributeNode("ISBN");


// Seleccionar valor
String ValorISBN = atr.InnerXml;

// Mostrar su valor
Console.Write ("Valor del atributo ISBN: ");
Console.WriteLine(ValorISBN);

// De otra manera:

String ValorISBNAlt = Raiz.GetAttribute("ISBN");

Console.Write ("Valor del atributo ISBN (alt): ");
Console.WriteLine(ValorISBNAlt);

// Calcular y mostrar nodo propietario

XmlNode node= atr.OwnerElement;
String cad = node.Name;
Console.WriteLine ("Pertenece a un nodo: '{0}'", cad);

Console.ReadLine();
}
}

Las clases XmlReader y XmlWriter
XmlReader es una clase base abstracta que proporciona acceso de slo avance y
de slo lectura sin almacenamiento en cach. Esto significa, por ejemplo, que no
hay funciones para editar los valores de un atributo o contenido de un elemento, ni
la posibilidad de agregar y quitar nodos. La lectura se realiza por el mtodo de
primero en profundidad (como se lee textualmente XML).
El nodo actual hace referencia al nodo en el que est situado el lector. Para
avanzar el lector, utilice cualquiera de los mtodos de lectura. Las propiedades
reflejan el valor del nodo actual.
La clase XmlReader contiene mtodos que permiten:
Leer contenido XML cuando ste est disponible en su totalidad, por
ejemplo, un archivo de texto XML.
Buscar la dimensin de la pila de elementos XML.
Determinar si un elemento tiene contenido o est vaco.
Leer y explorar atributos.
Omitir elementos y su contenido.
La clase XmlReader tiene propiedades que devuelven informacin, como:
Tipo y nombre del nodo actual.
Contenido del nodo actual.
La clase XmlTextReader es una implementacin de la clase base XmlReader que
permite tomar texto de entrada de flujos y ofrece mtodos y propiedades para tratr
elementos y atributos adems de posibilitar la validacin del texto XML.
Otras clases derivadas de XmlReader son:
XmlNodeReader. Representa un lector que proporciona acceso
rpido a datos XML, sin almacenamiento en cach y con
desplazamiento slo hacia delante en un XmlNode. XmlNodeReader
puede leer un subrbol DOM XML. Esta clase no admite la validacin
de DTD ni de esquemas. Para realizar la validacin de datos, utilice
XmlValidatingReader.
XmlValidatingReader. Representa un lector que proporciona
validacin de esquemas para DTD, esquemas reducidos de datos
XML (esquemas XDR) y esquemas del lenguaje de definicin de
esquemas XML (esquemas XSD).
Ms informacin en: XmlReader (Clase)
Xmlwriter es una clase base abstracta que proporciona acceso de slo avance y
de slo escritura sin almacenamiento en cach para escribir cdigo XML.
En la lista siguiente se muestra el propsito de los mtodos y propiedades que
incluye la clase XmlWriter:
Especificar si se debe permitir el uso de espacios de nombres.
Escribir cdigo XML con un formato correcto.
Administrar la salida, lo que incluye a los mtodos para determinar
su progreso, con la propiedad WriteState.
Escribir varios documentos en una secuencia de salida.
Vaciar o cerrar la secuencia de salida.
Escribir nombres vlidos, nombres completos y smbolos (tokens) de
nombres.
La clase XmlTextWriter es una implementacin de la clase base XmlWriter que
incorpora propiedades y mtodos para escribir XML sintcticamente correcto.
Ms informacin en: XmlWriter (Clase)
El espacio de nombres System.Xml.Xsl
Copmo indicamos anteriormente System.Xml.Xsl proporciona clases y soporte
para realizar transformaciones XSLT. El objetivo de XSLT (Extensible Stylesheet
Language Transformation) es transformar el contenido de un documento XML de
origen en otro documento con un formato o estructura diferentes.
Ms informacin en: System.Xml.Xsl (Espacio de nombres)
La clase XslTransform (se encuentra en este espacio de nombres) es el
procesador de XSLT que implementa la funcionalidad de esta especificacin. Otra
clase til de System.Xml.Xsl es:
XsltException. Excepcin que se produce cuando ocurre algn error
mientras se procesa una transformacin XSL (Extensible Stylesheet
Language).
En la ilustracin siguiente se muestra la arquitectura de transformacin de .NET
Framework.

La recomendacin de XSLT utiliza XPath para seleccionar componentes de un
documento XML. XPath es un lenguaje de consulta utilizado para explorar los
nodos de un rbol de documentos. Tal y como se muestra en el diagrama, la
implementacin de XPath en .NET Framework se utiliza para seleccionar partes
de XML almacenadas en varias clases, como XmlDocument, XmlDataDocument y
XPathDocument. XPathDocument es un almacn de datos optimizado de XSLT y,
si se utiliza con XslTransform, proporciona transformaciones XSLT de alto
rendimiento.
La transformacin se aplica con el mtodo Transform que transforma los datos
XML utilizando la hoja de estilos XSLT que se ha cargado.
Ejemplo 1
Empleando el fichero de transformacin TransfSimple.xslt:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="/Biblioteca">
<ResumenBiblioteca>
<xsl:apply-templates />
</ResumenBiblioteca>
</xsl:template>

<xsl:template match="Libro">
<item>
<xsl:value-of select="Titulo" />
</item>
</xsl:template>

</xsl:stylesheet>
transformaremos el fichero LibrosDeCasa.xml de manera que el resultado de la
transformacin se muestre en la consola.
using System;
using System.IO;
using System.Xml;
using System.Xml.Xsl;

class BibliotecaApp
{
static void Main(string[] args)
{
// Crear objeto XSLT y leer fichero XSLT.

XslTransform xslt = new XslTransform();
xslt.Load("../../TransfSimple.xslt");

// Crear objeto XML y leer fichero XML.

XmlDocument doc = new XmlDocument();
doc.Load("../../LibrosDeCasa.xml");

// Aplicar la transformacin y presentar el
// resultado en la consola.

xslt.Transform(doc, null, Console.Out, null);

Console.ReadLine();
}
}

Para que el resultado de la transformacin se escriba en un fichero se emplea una
versin sobrecargada del mtodo Transform:
.........
// Aplicar la transformacin y guardar el
// resultado en un fichero.

xslt.Transform("../../LibrosDeCasa.xml",
"../../Resumen.xml", null);
.........
Aunque el resultado, al igual que en la consola, no queda formateado
correctamente:

Podemos hacer que el resultado quede con el formato adecuado si empleamos un
XmlTextWriter y empleamos sobre l la propiedad Formatting. Por ejemplo:
using System;
using System.IO;
using System.Xml;
using System.Xml.Xsl;

class BibliotecaApp
{
static void Main(string[] args)
{
// Crear objeto XSLT y leer fichero XSLT.

XslTransform xslt = new XslTransform();
xslt.Load("../../TransfSimple.xslt");

// Crear objeto XML y leer fichero XML.

XmlDocument doc = new XmlDocument();
doc.Load("../../LibrosDeCasa.xml");

// Aplicar la transformacin y presentar el
// resultado en la consola.

XmlTextWriter writer =
new XmlTextWriter (Console.Out);
writer.Formatting = Formatting.Indented;
writer.Indentation = 4;
writer.WriteStartDocument(true);

xslt.Transform(doc, null, writer, null);

Console.ReadLine();
}
}
Que produce como resultado:

Para que el resultado con formato se guarde en un fichero modificamos el
programa para que quede como sigue:
using System;
using System.IO;
using System.Xml;
using System.Xml.Xsl;

class BibliotecaApp
{
static void Main(string[] args)
{
// Crear objeto XSLT y leer fichero XSLT.

XslTransform xslt = new XslTransform();
xslt.Load("../../TransfSimple.xslt");

// Crear objeto XML y leer fichero XML.

XmlDocument doc = new XmlDocument();
doc.Load("../../LibrosDeCasa.xml");

XmlTextWriter rdo = new
XmlTextWriter("../../Resumen2.xml", null);
rdo.Formatting = Formatting.Indented;

rdo.WriteStartDocument(true);

xslt.Transform(doc, null, rdo, null);

Console.ReadLine();
}
}
y el resultado es:

La modificacin propuesta ahora:
......
XmlTextWriter rdo =
new XmlTextWriter("../../Resumen3.xml",
System.Text.Encoding.UTF8);
......
hace que se modifique el prlogo del fichero XML incorporando la codificacin
UTF-8:

El espacio de nombres System.Xml.XPath
El espacio de nombres System.Xml.XPath contiene el motor de evaluacin y el
analizador XPath. Es compatible con la Recomendacin XML Path Language
(XPath) Version 1.0 del Consorcio W3C (www.w3.org/TR/xpath).
Las clases ms importantes proporcionadas en el espacio de nombres
System.Xml.XPath son:
XPathDocument. Proporciona una cach rpida y de slo lectura
para el procesamiento de documentos XML mediante XSLT. Esta
clase est optimizada para el procesamiento XSLT y el modelo de
datos XPath.
XPathNavigator. Lee datos de cualquier almacn de datos mediante
un modelo de cursor.
XPathNodeIterator. Proporciona un iterador para un conjunto de
nodos seleccionados.
Ms informacin en: System.Xml.XPath (Espacio de nombres)
El siguiente ejemplo usa el modelo de cursor para calcular el precio medio de los
libros. Utilice el XPathNodeIterator para establecer una iteracin en un conjunto de
nodos.
using System;
using System.IO;
using System.Xml;
using System.Xml.XPath;

class BibliotecaApp
{
static void Main(string[] args)
{

// Abre para lectura el fichero XML
XPathDocument doc =
new XPathDocument ("../../LibrosDeCasa.xml");

// Crea un objeto XPathNavigator (nav) para
// desplazarse por doc
XPathNavigator nav = doc.CreateNavigator();

// Selecciona un conjunto de nodos utilizando la
// expresin XPath especificada.
XPathNodeIterator it =
nav.Select("/Biblioteca/Libro/Precio");

int nl = it.Count;
Console.WriteLine ("Numero total de libros= {0}",nl);

// Iterador con MoveNext()
double SumPrecio1 = 0.0;
while (it.MoveNext())
SumPrecio1 +=
XmlConvert.ToDouble(it.Current.Value);

Console.WriteLine (" Precio medio (1) = {0} Euros",
SumPrecio1 / nl);

Console.ReadLine();
}
}

El mtodo Evaluate evala la cadena que representa a una expresin XPath y
devuelve el resultado de tipo (nmero, valor booleano, cadena o conjunto de
nodos).
using System;
using System.IO;
using System.Xml;
using System.Xml.XPath;

class BibliotecaApp
{
static void Main(string[] args)
{
// Abre para lectura el fichero XML
XPathDocument doc =
new XPathDocument ("../../LibrosDeCasa.xml");

// Crea un objeto XPathNavigator (nav) para
// desplazarse por doc
XPathNavigator nav = doc.CreateNavigator();

// Selecciona un conjunto de nodos utilizando la
// expresin XPath especificada.
XPathNodeIterator it =
nav.Select("/Biblioteca/Libro/Precio");

int nl = it.Count;
Console.WriteLine ("Numero total de libros= {0}",nl);

// Compila una cadena que representa a una expresin
// XPath y devuelve una expresion XPath
XPathExpression expr =
nav.Compile("sum(/Biblioteca/Libro/Precio)");

// Evalua la expresion
double SumPrecio2 = (double) nav.Evaluate(expr);

Console.WriteLine (" Precio medio (2) = {0} Euros",
SumPrecio2 / nl);

Console.ReadLine();
}
}
El siguiente ejemplo recorre el fichero de libros mostrando la lista de ttulos y
nmero de pginas. Despues muestra el nmero medio de pginas.
using System;
using System.IO;
using System.Xml;
using System.Xml.XPath;

class BibliotecaApp2
{

static void Main(string[] args)
{

Console.WriteLine ("Lista de libros\n");

// Lee en doc un fichero XML
XmlDocument doc = new XmlDocument ();
doc.Load("../../MisLibros.xml");

// Crea un objeto XPathNavigator (nav) para
// desplazarse por doc
XPathNavigator nav = doc.CreateNavigator();

// Accede al primer hijo de "doc": <Biblioteca>
nav.MoveToFirstChild();

int nl= 1;
int sum = 0;

// Accede al primer <Libro>
nav.MoveToFirstChild();

// Escibe los datos del primer libro

Console.Write ("Libro {0}: ", nl);

// Accede y pinta el primer campo (<Titulo>)
nav.MoveToFirstChild();
Console.Write (nav.Value);

// Saltar campo <Autor> y colocarse en <NumPags>
for (int i=0; i<2; i++)
nav.MoveToNext();

Console.WriteLine (" ({0} pgs.)", nav.Value);
sum += XmlConvert.ToInt16(nav.Value);

// Volver al nodo <Libro>
nav.MoveToParent();


// Repetir para los restantes nodos <Libro>
while (nav.MoveToNext())
{
Console.Write ("Libro {0}: ", ++nl);

// Accede y pinta el primer campo <Titulo>
nav.MoveToFirstChild();
Console.Write (nav.Value);

// Saltar campo <Autor> y colocarse en <NumPags>
for (int i=0; i<2; i++)
nav.MoveToNext();

Console.WriteLine (" ({0} pgs.)", nav.Value);
sum += XmlConvert.ToInt16(nav.Value);

// Volver al nodo <Libro>
nav.MoveToParent();
}

Console.WriteLine ("\nTotal de pginas = {0}", sum);
Console.WriteLine ("Tamao medio = {0}",
(float)sum / nl);

Console.ReadLine();
}
}


"Serializacin" de documentos XML
Introduccin
El objetivo primario de la serializacin en .NET es permitir la conversin de
documentos XML y flujos (streams) en objetos CLR y viceversa. Esta conversin
se realizar en tiempo de ejecucin. Esta conversin supone que la manipulacin
de los objetos ser ms sencilla que hacerlo con otras tcnicas comunes
(programas que hacen uso de DOM, por ejemplo).
En lugar de serializacin deba llamarse transformacin:
No serializa cualquier objeto CLR en XML.
Est diseada para transformar clases a esquemas e instancias de
clases a instancias de esquemas.
El objetivo es representar cualquier XML, no cualquier objeto.
La clase fundamental para esta tarea es XmlSerializer. Esta clase se emplea para
serializar y deserializar objetos en y desde documentos XML. XmlSerializer
permite controlar el modo en que se codifican los objetos en XML.
Los mtodos fundamentales de la clase XmlSerializer son: Serialize y Deserialize.
Otro mtodo importante es CanDeserialize.
Ms informacin en: XmlSerializer (Clase)
using System;
using System.IO;
using System.Xml.Serialization;

namespace XSDSample
{
public class Test
{
public String cad;
}

class SerialApp {

static void Main(string[] args)
{
Test t = new Test();
t.cad = "Cadena de prueba";

XmlSerializer ser = new XmlSerializer (typeof(Test));
FileStream stream = new FileStream ("test.xml",
FileMode.OpenOrCreate);

ser.Serialize (stream, t);
}
}
}

Algunos puntos a tener en cuenta son:
Solo pueden serializarse las clases que tienen un constructor pblico
y predeterminado.
// Clase serializable
public class Test
{
public Test() {} // Igual que no ponerlo
public String cad;
}

// Clase NO serializable
public class Test2
{
public Test2(String cadena)
{
this.cad = cadena;
}
public String cad;
}
Solo pueden serializarse campos y propiedades public.
Por ejemplo, la siguiente clase tiene todos sus miembros private:
// Clase serializable sin miembros serializables
public class Cliente
{
private String Nombre;
private String Direccion;
private String Telefono;
}
y al serializarla se obtiene el siguiente resultado:
<?xml version="1.0"?>
<Cliente
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
/>
Sin embargo, cuando los campos de la clase son public
// Clase serializable con miembros serializables
public class Cliente
{
public String Nombre;
public String Direccion;
public String Telefono;
}
.....
Cliente c = new Cliente();
c.Nombre = "Pepe";
c.Direccion = "Casa de Pepe";
c.Telefono = "123456789";
XmlSerializer ser = new XmlSerializer (typeof(Cliente));
FileStream stream = new FileStream ("cliente.xml",
FileMode.OpenOrCreate);

ser.Serialize (stream, c);
.....
el resultado es muy diferente:
<?xml version="1.0"?>
<Cliente
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-
instance">
<Nombre>Pepe</Nombre>
<Direccion>Casa de Pepe</Direccion>
<Telefono>123456789</Telefono>
</Cliente>

Los campos de solo lectura (readonly) y las propiedades no son
serializables.
Los mtodos y cualquier informacin de tipos no son serializables.
Ejemplo
using System;
using System.IO;
using System.Xml;
using System.Xml.Serialization;

namespace SerDeserSample
{
public class Cliente
{
public string Nombre;
public string Direccion;
public string Telefono;

public override string ToString() {
string s1 = "Un cliente:"+"\n";
string s2 = " Nombre: " + this.Nombre + "\n";
string s3 = " Direccin: " + this.Direccion + "\n";
string s4 = " Telfono: " + this.Telefono+ "\n";
return (s1+s2+s3+s4);
}
}

class SerDeserSampleApp {

static void Main(string[] args)
{
Cliente c = new Cliente();
c.Nombre = "Juan";
c.Direccion = "Casa de Juan S/N";
c.Telefono = "333444555";

Console.WriteLine ("Antes de guardar");
Console.WriteLine (c);

// Serializar
XmlSerializer ser = new
XmlSerializer (typeof(Cliente));
FileStream OStream = new FileStream ("Juan.xml",
FileMode.OpenOrCreate);

ser.Serialize (OStream, c);
OStream.Close();

// Deserializar
Cliente c2;

XmlSerializer ser2 = new
XmlSerializer(typeof(Cliente));
FileStream IStream = new
FileStream("Juan.xml", FileMode.Open);
XmlReader reader = new XmlTextReader(IStream);

if(ser2.CanDeserialize(reader)) {
c2 = (Cliente) ser2.Deserialize (reader);
Console.WriteLine ("Despues de recuperar");
Console.WriteLine (c2);
}
else
Console.WriteLine("No puedo deserializar");

IStream.Close();

Console.ReadLine();

} // Main

} // class SerDeserSampleApp

} // manespace SerDeserSample

El fichero Juan.xml contiene:
<?xml version="1.0"?>
<Cliente
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-
instance">
<Nombre>Juan</Nombre>
<Direccion>Casa de Juan S/N</Direccion>
<Telefono>333444555</Telefono>
</Cliente>
y en la consola se muestra este resultado:

Personalizar la serializacin
Pueden emplearse atributos en las clases y sus miembros que modifican el XML
resultante de la serializacin.
Los atributos XmlRoot y XmlElement se emplean para especificar el
nombre de un elemento XML. Si no se especifican, las etiquetas del
documento XML son los nombres de los campos de la clase.
Observar el siguiente ejemplo:
using System;
using System.IO;
using System.Xml;
using System.Xml.Serialization;

namespace SerDeserSample
{
[XmlRoot("cliente")]
public class Cliente
{
public string Nombre;
public string Direccion;
public string Telefono;

public override string ToString() {
string s1 = "Un cliente:"+"\n";
string s2 = " Nombre: " + this.Nombre + "\n";
string s3 = " Direccin: " + this.Direccion + "\n";
string s4 = " Telfono: " + this.Telefono+ "\n";
return (s1+s2+s3+s4);
}
}

[XmlRoot("cliente")]
public class BuenCliente {
public string Telefono;
public string Direccion;
public string Nombre;

public override string ToString() {
string s1 = "Un buen cliente:"+"\n";
string s2 = " Nombre: " + this.Nombre + "\n";
string s3 = " Direccin: " + this.Direccion + "\n";
string s4 = " Telfono: " + this.Telefono+ "\n";
return (s1+s2+s3+s4);
}

}
class SerDeserSampleApp {

static void Main(string[] args)
{
Cliente c = new Cliente();
c.Nombre = "Pepe";
c.Direccion = "Casa de Pepe";
c.Telefono = "123456789";

Console.WriteLine (c);

// Serializar
XmlSerializer ser = new
XmlSerializer (typeof(Cliente));
FileStream OStream = new FileStream ("cli.xml",
FileMode.OpenOrCreate);

ser.Serialize (OStream, c);
OStream.Close();

// Deserializar
BuenCliente bc;

XmlSerializer ser2 = new
XmlSerializer(typeof(BuenCliente));
FileStream IStream = new
FileStream("cli.xml", FileMode.Open);
XmlReader reader = new XmlTextReader(IStream);

if(ser2.CanDeserialize(reader)) {
bc = (BuenCliente) ser2.Deserialize (reader);
Console.WriteLine (bc);
}
else
Console.WriteLine("No puedo deserializar");

IStream.Close();

Console.ReadLine();

} // Main

} // class SerDeserSampleApp

} // manespace SerDeserSample

El fichero cli.xml contiene:
<?xml version="1.0"?>
<cliente
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-
instance">
<Nombre>Pepe</Nombre>
<Direccion>Casa de Pepe</Direccion>
<Telefono>123456789</Telefono>
</cliente>

y en la consola se muestra este resultado:

Hay que tener ciertas precauciones en el proceso de deserializacin,
ya que ste no es directo:
o La clase destino, si es diferente a la origen, debe conocer el
nombre del elemento raz del XML. Por ejemplo, en la
siguiente situacin no puede deserializarse hacia la clase
BuenCliente:
public class Cliente {
public string Nombre;
public string Direccion;
public string Telefono;
......
}

public class BuenCliente {
public string Telefono;
public string Direccion;
public string Nombre;
......
}
o Para poder hacerlo podramos aadir un atributo a la clase
destino:
[XmlRoot("Cliente")]
public class BuenCliente {
public string Telefono;
......
}
o o bien homogeneizar las clases origen y destino:
[XmlRoot("cliente")]
public class BuenCliente {
public string Nombre;
......
}
[XmlRoot("cliente")]
public class BuenCliente {
public string Telefono;
......
}
o El mismo criterio se sigue para los dems elementos XML que
se copian a la clase destino: o coinciden en nombre con los
campos de la clase o se especifica un atributo XmlElement en
la declaracin de la clase:
[XmlRoot("cliente")]
public class Cliente {
public string Nombre;
public string Direccion;
public string Telefono;
......
}
[XmlRoot("cliente")]
public class BuenCliente {
[XmlElement("Telefono")]
public string Tfno;
public string Dir;
[XmlElement("Nombre")]
public string Nbre;
......
}
o En este ejemplo slo se deserializan los elementos Telefono y
Nombre (del documento XML) en los campos Tfno y Nbre (de
la clase BuenCliente).
Puede modificarse el espacio de nombres predeterminado con el
parmetro Namespace
[XmlRoot("MiCliente", Namespace="http://rocco.ugr.es")]
public class Cliente
{
public string Nombre;
public string Direccion;
public string Telefono;
}

Al serializar esta clase obtenemos (fichero cli3.xml):
<?xml version="1.0"?>
<MiCliente
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://rocco.ugr.es">
<Nombre>Pepe</Nombre>
<Direccion>Casa de Pepe</Direccion>
<Telefono>123456789</Telefono>
</MiCliente>
Puede modificarse el espacio de nombres para algunos elementos:
[XmlRoot("MiCliente",
Namespace="http://rocco.ugr.es/Clis")]
public class Cliente {
public string Nombre;
[XmlElement(Namespace="http://elvex.ugr.es/Dirs")]
public TDireccion Direccion;
public string Telefono;
}

public class TDireccion {
public string Calle;
public int Numero;
public string Ciudad;
public string CP;
}
......
Cliente c = new Cliente();
c.Direccion = new TDireccion();
c.Nombre = "Pepe";
c.Direccion.Calle = "Calle Fina";
c.Direccion.Numero = 4;
c.Direccion.Ciudad = "Metrpolis";
c.Direccion.CP = "12345";
c.Telefono = "123456789";
......
Al serializar esta clase obtenemos (fichero cli4.xml):
<?xml version="1.0"?>
<MiCliente
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://rocco.ugr.es/Clis">
<Nombre>Pepe</Nombre>
<Direccion
xmlns="http://elvex.ugr.es/Dirs">
<Calle>Calle Fina</Calle>
<Numero>4</Numero>
<Ciudad>Metrpolis</Ciudad>
<CP>12345</CP>
</Direccion>
<Telefono>123456789</Telefono>
</MiCliente>
Un elemento puede serializarse como un atributo XML.
[XmlRoot("MiCliente",
Namespace="http://rocco.ugr.es/Clis")]
public class Cliente {
public string Nombre;
[XmlElement(Namespace="http://elvex.ugr.es/Dirs")]
public TDireccion Direccion;
[XmlAttribute]
public string Telefono;
}

public class TDireccion {
public string Calle;
public int Numero;
public string Ciudad;
[XmlAttribute]
public string CP;
}
Al serializar esta clase obtenemos (fichero cli5.xml):
<?xml version="1.0"?>
<MiCliente
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
Telefono="123456789"
xmlns="http://rocco.ugr.es/Clis">
<Nombre>Pepe</Nombre>
<Direccion
CP="12345"
xmlns="http://elvex.ugr.es/Dirs">
<Calle>Calle Fina</Calle>
<Numero>4</Numero>
<Ciudad>Metrpolis</Ciudad>
</Direccion>
</MiCliente>
Al serializar vectores cada entrada del vector se transforma en un
elemento de un elemento complejo:
public string[] Telefono;
<Telefono>
<string>123456789</string>
<string>987654321</string>
......
</Telefono>
o se puede cambiar el nombre al conjunto y a cada elemento:
[XmlArray("TelefonosContacto")]
[XmlArrayItem("NumTelefono")]
public string[] Telefono;
<TelefonosContacto>
<NumTelefono>123456789</NumTelefono>
<NumTelefono>987654321</NumTelefono>
......
</TelefonosContacto>
Un ejemplo complejo de serializacin
En este ejemplo describimos cmo se crea un documento XML complejo desde
una aplicacin Windows. Se trata, concretamente, de crear el fichero de datos de
la biblioteca, con el que hemos trabajado frecuentemente.
La introduccin de datos se realiza en una ventana diseada con Visual Studio
.NET cuyo aspecto es el siguiente:

Las estructuras de datos bsicas para la aplicacin son:
La clase CLibro, que caracteriza a cada objeto libro de la coleccin.
Observar que pensando en la serializacin de los objetos de tipo
CLibro para que se conviertan en los elementos XML ya conocidos
hemos indicado que el campo Disponible se codifique como un
atributo y que el vector de cadenas llamado Generos se codifique
como el elemento sinnimo mientras que cada elemento del vector
se codifique como un elemento de nombre Genero:
public class CLibro {

public string Titulo;
public string Autor;
public int NumPags;
public double Precio;

[XmlAttribute]
public bool Disponible;

[XmlArray("Generos")]
[XmlArrayItem("Genero")]
public string[] Generos;
}
La clase Lista, que caracteriza a una coleccin de libros,
implementada como un vector de elementos de tipo CLibro.
Observar que hemos indicado que se sea el elemento raz del
documento XML y se codifique como Biblioteca:
[XmlRoot("Biblioteca",
Namespace="http://tempuri.org/EsquemaBiblioteca.xsd")]
public class Lista {
[XmlElement]
public CLibro[] Libro = new CLibro[10];
}

Lista lista = new Lista();
El cdigo que permite serializar la clase Lista es el siguiente:
......
XmlSerializer ser = new XmlSerializer (typeof(Lista));
FileStream OStream = new FileStream (filename,
FileMode.Create);

ser.Serialize (OStream, lista);

OStream.Close();
......
Conjuntos de datos
DataSet myDataSet = new DataSet();
...

// -------------------------------------------
// Generacin de un documento XML y su esquema
// XSD a partir de un DataSet
// -------------------------------------------

myDataSet.Namespace = "http://elvex.ugr.es/";
myDataSet.DataSetName = "datos";

myDataSet.WriteXml("fichero.xml");
myDataSet.WriteXmlSchema("fichero.xsd");

// Lectura de un documento XML
// ---------------------------

myDataSet.ReadXml("fichero.xml");

// Validacin de un documento XML
// ------------------------------

XmlDocument doc = new XmlDocument();
XmlTextReader tr = new XmlTextReader("fichero.xml");
XmlValidatingReader vr = new XmlValidatingReader(tr);
XmlSchemaCollection collection = new
XmlSchemaCollection();

collection.Add("http://elvex.ugr.es/","fichero.xsd");
vr.Schemas.Add(collection);
vr.ValidationType = ValidationType.Schema;
reader.ValidationEventHandler +=
new ValidationEventHandler (this.ValidationEventHandle);

doc.Load(vr);


// Errores de validacin (callback)

private void ValidationEventHandle (object sender,
ValidationEventArgs args)
{
error = true;
Console.WriteLine("\r\n\tError de validacin: "
+ args.Message );
}
Programacin web

Desarrollo de aplicaciones para Internet

Actualmente, se observa una tendencia a utilizar los estndares de Internet (ms
concretamente, la web) para desarrollar aplicaciones de gestin en medianas y
grandes empresas. Este tipo de aplicaciones utiliza software que se descarga de
un servidor web, sin necesidad de instalarlo localmente en cada mquina, lo que
se facilita las tareas de mantenimiento y actualizacin.
Evolucin de las aplicaciones web
HTML esttico
Crear un sitio web es bastante fcil. Slo se necesita un servidor web que atienda
peticiones HTTP y un conjunto de ficheros HTML con informacin. El
inconveniente de los ficheros HTML es que son estticos, mientras que
generalmente nos interesa algo ms que mostrar siempre la misma informacin (y
actualizar a mano peridicamente los ficheros HTML no parece una gran idea).

Aplicaciones web
La creacin de aplicaciones web requiere software ejecutndose en el servidor
que genere automticamente los ficheros HTML que se visualizan en el navegador
del cliente. La comunicacin entre el cliente y el servidor se realiza a travs de
HTTP, un protocolo simple en el que se establece una conexin TCP
independiente para cada par solicitud-respuesta. Esto implica que el entorno de
programacin que empleemos debera facilitarnos de alguna forma el
mantenimiento de sesiones de usuario.
En el caso del estndar CGI (Common Gateway Interface), se escriben programas
estndar en lnea de comandos que aceptan una serie de parmetros y generan
un fichero HTML en su canal de salida estndar (stdout en el caso de C), lo que no
facilita demasiado nuestro trabajo a la hora de construir aplicaciones de cierta
envergadura.
En el IIS (Internet Information Server), el servidor web de Microsoft, las DLLs de
ISAPI (Internet Services Application Programming Interface) mejoran el
rendimiento de los programas CGI y poco ms.
Las tecnologas que nos permiten incorporar fragmentos de cdigo a nuestras
pginas (tipo ASP o JSP tradicional) resultan algo ms cmodas para el
programador, que puede centrarse en la lgica de su aplicacin sin tener que
preocuparse en exceso de los detalles de HTTP y HTML. No obstante, el diseo
de las aplicaciones resultantes suele no ser demasiado elegante, pues tiende a
mezclar la interfaz de usuario con la lgica de la aplicacin.
En cualquier caso, en las aplicaciones web, el navegador del cliente se limita a
mostrar una pgina HTML estndar que habr sido generada dinmicamente en el
servidor y, por tanto, el cliente ser independiente de la tecnologa utilicemos en el
servidor. El cliente en una aplicacin web se limita a mostrar la pgina tal cual le
llega (thin client).

Servicios web
Los servicios web, bsicamente, establecen un lenguaje comn para el
intercambio de datos: XML (eXtensible Markup Language). Este lenguaje comn
permite que distintos sistemas pueden comunicarse entre s de una forma sencilla
y, de esta forma, se facilita la construccin de sistemas heterogneos:

En el cliente
En principio, una aplicacin web se puede desarrollar de forma que todo el trabajo
lo realice el servidor web y el usuario final slo necesite un navegador web, si bien
esta opcin no resulta demasiado atractiva por las limitaciones de los formularios
HTML. Debido a estas limitaciones, han surgido numerosas alternativas que
permiten ejecutar cdigo en el cliente: DHTML/JavaScript, COM (controles
ActiveX), applets Java, plug-ins especficos (p.ej. Adobe Flash)...
Estas tecnologas para el cliente permiten mejorar la escalabilidad de las
aplicaciones (ya que se realiza menos trabajo en el servidor), as como la
productividad del usuario final (al permitir la construccin de interfaces de usuario
ms sofisticadas). Adems, el uso de estas tecnologas "facilita" la construccin de
aplicaciones ms atractivas de cara al mercado y al usuario final (interfaces con
drag&drop, presentaciones y animaciones Flash...).
HTML dinmico (DHTML): Se escriben macros (scripts) que se
incrustan en los ficheros HTML. Usualmente, se utiliza Javascript por
cuestiones de portabilidad, si bien navegadores como el Internet
Explorer tambin permiten otros lenguajes como VBScript. En HTML
dinmico, cada etiqueta HTML se convierte en un objeto con sus
propiedades y eventos asociados. Los scripts le proporcionan al
navegador el cdigo correspondiente a la respuesta prevista por el
programador para los distintos eventos que se pueden producir.

ActiveX: Disponible nicamente en el Internet Explorer (al menos, en
principio), es una tecnologa basada en COM. Permite desarrollar
cdigo eficiente, si bien su utilizacin se suele limitar a intranets por
cuestiones de seguridad.
Applets: Son aplicaciones escritas en Java en las que se distribuye
el cdigo intermedio correspondiente a la mquina virtual Java
(bytecodes). Tienen la ventaja de ser portables (a cualquier
plataforma que disponga de un intrprete de bytecodes) y destacan
por su seguridad (cada aplicacin se ejecuta en un espacio
independiente [sandbox] que, en principio, no puede acceder al
hardware de la mquina del cliente). Algunos problemas de
rendimiento (y, sobre todo, cuestiones de mercado) han limitado su
utilizacin.
En el servidor
Tambin existen numerosas alternativas que se pueden utilizar para desarrollar
aplicaciones web en el servidor. Cuando el software se ejecuta en el servidor, ste
suele recibir datos provenientes de URLs, formularios HTML, cookies y cabeceras
HTTP. Una vez procesados estos datos (y tras acceder, posiblemente, a bases de
datos, ficheros, mainframes, etc.), en el servidor se genera dinmicamente una
respuesta personalizada en HTML para cada cliente concreto.
Entre las ventajas del software desarrollado de esta forma destacan su
accesibilidad (desde cualquier punto de Internet), su facilidad de mantenimiento
(no hay que distribuir el cdigo de las aplicaciones ni sus actualizaciones), su
seguridad (el cdigo no puede manipularlo el usuario) y su escalabilidad
(utilizando "granjas" de servidores y arquitecturas multicapa).


Tecnologas
CGI (Common Gateway Interface)
ISAPI (Internet Server API) @ Microsoft IIS
NSAPI (Netscape Server API)
ASP (Active Server Pages) & ASP.NET (para la plataforma .NET)
JSP (Java Server Pages) & servlets
PHP (Personal Home Page)
ColdFusion (CFM)
Ruby on Rails
Django (Python)
GWT (Google Web Toolkit)
...
Ejemplo: ASP y JSP
Las pginas ASP permiten crear aplicaciones web utilizando, usualmente, un
lenguaje como VBScript. Una pgina ASP contiene HTML esttico intercalado con
scripts que se encargan de generar HTML de forma dinmica. JSP funciona de
forma anloga, si bien utiliza el lenguaje de programacin Java. Tanto ASP como
JSP gozan de gran aceptacin a pesar de que no fuerzan uno de los principios
bsicos de diseo de software: la separacin entre la interfaz de una aplicacin y
su lgica interna.


Aplicaciones web con ASP.NET

ASP: Active Server Pages
ASP permite desarrollar aplicaciones para Internet que se ejecutan en el servidor.
Bsicamente, desarrollar pginas ASP consiste en intercalar documentos HTML
estticos y macros dinmicas, mezclando la interfaz con la lgica de la aplicacin
(lo que, por cierto, no suele ser demasiado recomendable).

Una pgina ASP no es ms que un fichero HTML con extensin .asp (.aspx en el
caso de ASP.NET). Cuando alguien accede a la pgina, en el servidor se procesa
el cdigo que aparece en la pgina ASP. El resultado de la ejecucin de este
cdigo, junto con la parte esttica de la pgina, es lo que se devuelve al
navegador del cliene como una pgina web ms (de ah el AS de ASP).
La tecnologa ASP, igual que JSP en Java y otras muchas alternativas, incluye un
conjunto de objetos intrnsecos que proporcionan distintos servicios tiles en el
desarrollo de aplicaciones web (Request, Response, Server, Application, Session),
adems de facilidades para acceder a componentes COM (p.ej. uso de ADO
[ActiveX Data Objects] para acceder a bases de datos).
Las pginas ASP se pueden escribir utilizando distintos lenguajes interpretados
(usualmente una variante de Visual Basic conocida como VBScript), por lo que
basta con escribir la pgina, guardarla y acceder a ella a travs del Internet
Information Server (el servidor HTTP de Microsoft), sin tener que compilarla
previamente.
El siguiente ejemplo muestra cmo se pueden crear pginas ASP que generen su
contenido dinmicamente:
<html>
<head>
<title>Hora.asp</title>
</head>
<body>
<h2> Hora actual </h2>
<% Response.Write(Now()) %>
</body>
</html>
El cdigo que aparece entre las etiquetas <% y %> se interpreta en el servidor.
Response.Write sirve para escribir algo en la pgina HTML de salida y Now() es
una funcin que devuelve la fecha y hora actuales.
El ejemplo anterior podemos modificarlo un poco para que el usuario pueda
visualizar la hora actual en el momento en que le interese, no slo cuando carga la
pgina por primera vez:
<html>
<head>
<title>Hora.Request.asp</title>
</head>
<body>
<form method="post">
<input type="submit" id=button name=button
value="Pulse el botn para consultar la hora" />
<%
if (Request.Form("button") <> "") then
Response.Write "<p>La hora actual es " & Now()
end if
%>
</form>
</body>
</html>
En esta ocasin, hemos utiizado el objeto Request que nos permite aceptar datos
de entrada en nuestra pgina ASP.
Para comprobar que los ejemplos anteriores funcionan, slo hay que guardar los
ficheros ASP correspondientes en algn sitio que cuelque del directorio wwwroot
del IIS y acceder a l desde el navegador utilizando una URL de la forma
http://localhost/... (o http://127.0.0.1/... si no tenemos definido el nombre localhost
en el fichero \WINDOWS\system32\drivers\etc\hosts de nuestra mquina).
Las versiones de ASP anteriores a ASP.NET (ASP Clsico, como algunos las
llaman) requieren escribir bastante cdigo, como por el ejemplo el necesario para
mantener el estado de la pgina. Este cdigo, adems, es poco legible y difcil de
mantener al estar mezclado con el HTML de la interfaz de usuario. En ASP, el
fragmento de cdigo ha de colocarse exactamente en el sitio donde queremos que
su salida aparezca, haciendo imposible separar la interfaz de la lgica de la
aplicacin. Este hecho dificulta la reutilizacin de cdigo y complica el
mantenimiento de las aplicaciones web. Adems, el ASP Clsico presenta algunos
inconvenientes a la hora de implantar sistemas reales (configuracin, eficiencia,
depuracin...), muchos de los cuales provienen de las limitaciones de los
lenguajes interpretados que se utilizan para escribir las macros ASP.
Las limitaciones e inconvenientes mencionados dieron lugar a una serie de
cambios en ASP.NET. ASP.NET, sin embargo, no es completamente compatible
con ASP, si bien la mayor parte de las pginas ASP slo requieren pequeos
cambios para pasarlas a ASP.NET. De hecho, el primero de los ejemplos funciona
correctamente como pgina ASP.NET (cambindole la extensin a .aspx) y el
segundo funciona si tenemos en cuenta que en Visual Basic .NET los parntesis
son obligatorios en las llamadas a mtodos.
ASP.NET: Aplicaciones web en la plataforma .NET
ASP.NET es el nombre con el que se conoce la parte de la plataforma .NET que
permite el desarrollo y ejecucin tanto de aplicaciones web como de servicios web.
Igual que suceda en ASP, ASP.NET se ejecuta en el servidor.
En ASP.NET, las aplicaciones web se suelen desarrollar utilizando formularios
web, que estn diseados para hacer la creacin de aplicaciones web tan sencilla
como la programacin en .NET de aplicaciones para Windows (usando C# o
Visual Basic .NET).
Para hacernos una idea de cmo es ASP.NET, retomemos el ejemplo de la
seccin anterior, que en ASP.NET queda como sigue si empleamos el lenguaje de
programacin C#:
<%@ Page language="c#" %>
<html>
<head>
<title>Hora.aspx</title>
</head>
<script runat="server">
public void Button_Click (object sender, System.EventArgs
e)
{
LabelHora.Text = "La hora actual es " + DateTime.Now;
}
</script>
<body>
<form method="post" runat="server">
<asp:Button onclick="Button_Click" runat="server"
Text="Pulse el botn para consultar la hora"/>
<p>
<asp:Label id=LabelHora runat="server" />
</form>
</body>
</html>
Para probar el ejemplo anterior slo tenemos que guardar la pgina con la
extensin .aspx y acceder a ella a travs del IIS, si lo tenemos instalado en
nuestro PC. El Visual Studio 2005 incorpora un "servidor de desarrollo ASP.NET",
por lo que no necesitamos disponer del IIS. Nos basta con crear un nuevo "Sitio
Web" (Archivo > Nuevo > Sitio Web... > Sitio Web ASP.NET) o un proyecto de tipo
"Aplicacin Web ASP.NET" (Archivo > Nuevo > Proyecto... > Visual C# >
Aplicacin Web ASP.NET).
El ejemplo anterior ilustra algunas de las caractersticas ms relevantes de
ASP.NET: su integracin en la plataforma .NET, el uso de controles y eventos; y,
por ltimo, su independencia respecto al navegador que utilicemos.
ASP.NET est construido sobre la plataforma .NET y se ejecuta sobre Internet
Information Server (IIS), el servidor web de Microsoft (o bien sobre el servidor de
desarrollo que trae incorporado el Visual Studio 2005):

La programacin en ASP.NET est basada en el uso de controles y eventos
(como en cualquier entorno de programacin visual para Windows), no en aceptar
datos de entrada y generar la salida en HTML (como ASP), lo que le proporciona
un mayor nivel de abstraccin, requiere menos cdigo y permite crear aplicaciones
mejor modularizadas, ms legibles y, por tanto, ms fciles de mantener.

El cdigo se ejecuta en el servidor web en funcin de los manejadores de eventos
que definamos para los controles y las pginas de nuestra aplicacin. Los
controles derivan todos de la clase System.Web.UI.Control y la pgina web en s
tambin es un control (derivado de System.Web.UI.Page, una subclase de
System.Web.UI.Control).
ASP.NET se encarga de garantizar la compatibilidad de los controles con los
distintos navegadores que pueda utilizar el usuario:

Adems, ASP.NET permite dos estilos para la creacin de pginas ASP.NET:
Incluir tanto controles como cdigo en un fichero .aspx, como
hicimos en el ejemplo (a pesar de que esto nos impide aprovechar
muchas de las ventajas de ASP.NET frente a ASP).
Mantener los controles en un fichero .aspx y dejar el cdigo aparte
(code-behind page), lo que permite separar fsicamente la interfaz de
usuario del cdigo de la aplicacin:
En la pgina ASP.NET (fichero Hora.aspx, Visual Studio
2002/2003):

<%@ Page Language="c#" CodeBehind="Hora.aspx.cs"
Inherits="HoraWebForm" %>
<html>
<head>
<title>Hora.aspx</title>
</head>
<body>
<form method="post" runat="server">
<asp:Button id="ButtonHora" runat="server"
Text="Pulse el botn para consultar la hora" />
<p>
<asp:Label id="LabelHora" runat="server" />
</form>
</body>
</html>
En un fichero de cdigo aparte (fichero Hora.aspx.cs, Visual
Studio 2002/2003):

public class HoraWebForm : System.Web.UI.Page
{
protected System.Web.UI.WebControls.Button ButtonHora;
protected System.Web.UI.WebControls.Label LabelHora;

override protected void OnInit(EventArgs e)
{
this.ButtonHora.Click += new
System.EventHandler(this.ButtonHora_Click);
base.OnInit(e);
}

private void ButtonHora_Click(object sender,
System.EventArgs e)
{
LabelHora.Text = "La hora actual es "+DateTime.Now;
}
}
Visual Studio 2003 - Descargar cdigo fuente
Podramos simplificar algo la implementacin del fichero de
cdigo asociado a la anterior pgina mediante el uso del
atributo AutoEventWireup en la directiva <%@ Page ...> con
la que comienza cualquier pgina ASP.NET.
En ASP.NET 2.0, adems, tendremos que utilizar el atributo
CodeFile en vez de CodeBehind, tal como se muestra a
continuacin:
Fichero Hora.aspx (ASP.NET 2.0, Visual Studio 2005):

<%@ Page Language="C#" AutoEventWireup="true"
CodeFile="Hora.aspx.cs" Inherits="HoraWebForm" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0
Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-
transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Hora.aspx</title>
</head>
<body>
<form id="formHora" runat="server">
<div>
<asp:Button ID="ButtonHora" runat="server"
Text="Pulse el botn para consultar la hora"
OnClick="ButtonHora_Click" />
<br />
<br />
<asp:Label ID="LabelHora" runat="server"></asp:Label>
</div>
</form>
</body>
</html>
Fichero Hora.aspx.cs (ASP.NET 2.0, Visual Studio 2005):

public partial class HoraWebForm : System.Web.UI.Page
{
protected void ButtonHora_Click(object sender, EventArgs
e)
{
LabelHora.Text = "La hora actual es " + DateTime.Now;
}
}
En ASP.NET 2.0, se ha de utilizar el atributo CodeFile para
especificar el nombre del archivo de cdigo fuente (junto con
el atributo Inherits para especificar el nombre completo de la
clase asociada al formulario web). El atributo CodeBehind se
mantiene nicamente para asegurar la compatibilidad con las
versiones anteriores de ASP.NET.
Visual Studio 2005 - Descargar cdigo fuente
Como es lgico, lo anterior lo haremos normalmente con la
ayuda del entorno de desarrollo, que se encargar de rellenar
muchos huecos de forma automtica para que nos podamos
centrar en la funcionalidad de nuestra aplicacin...
Formularios web

Formularios
Visual Studio .NET proporciona un entorno de programacin visual orientado a
objetos mediante el cual podemos crear aplicaciones web utilizando componentes
ASP.NET (por derivacin y por composicin). Esto nos permite desarrollar
aplicaciones web prestando poca atencin al HTML en s, ya que este no es ms
que el mecanismo a travs del cual los distintos controles de nuestra interfaz se
presentan al usuario final de nuestras aplicaciones.
ASP.NET, en realidad, es una DLL ISAPI que encapsula al Common Language
Runtime (CLR) de la plataforma .NET y permite utilizar sta en el desarrollo de
aplicaciones web para el Internet Information Server (IIS) de Microsoft:

Las aplicaciones web ASP.NET estn formadas por formularios web, que
usualmente se dividen en un fichero .aspx, en el que se especifica la interfaz del
formulario, y un fichero de cdigo aparte .aspx.cs, en el que se implementa la
lgica de la aplicacin. A partir de la versin 2.0 de la plataforma .NET, que
permite la implementacin parcial de clases (con la palabra reservada partial), el
fichero de cdigo se divide en dos: un fichero .aspx.cs en el que el programador
implementa sus manejadores de eventos y un fichero .aspx.designer.cs en el que
se recoge todo el cdigo generado automticamente por el Visual Studio. De esta
forma, se elimina una fuente de error bastante comn en versiones del Visual
Studio anteriores al VS2005; a saber, el borrado accidental del cdigo generado
automticamente, lo que provocaba que la aplicacin dejase de funcionar
correctamente.
Para poder acceder a una aplicacin web, basta con poner esos los ficheros
necesarios en algn lugar accesible a travs del IIS (el directorio raz wwwroot, por
ejemplo). Al acceder a la pgina .aspx, el cdigo se compila automticamente y se
genera un assembly en la cach del CLR. Si el texto de la pgina cambia, el
cdigo se recompila automticamente. Si no cambia, las solicitudes que se reciban
a continuacin utilizarn directamente la versin compilada que se halla en la
cach, lo que mejora notablemente la eficiencia de las aplicaciones web ASP.NET
con respecto a las versiones previas de ASP.
NOTA: En el Visual Studio 2005, podemos usar el servidor web de desarrollo que
lleva integrado para probar nuestras aplicaciones sin tener que recurrir al IIS.
Fichero .aspx

<% @Page Language="C#" Inherits="TodayPage"
Src="Today.cs" %>

<html>
<body>
<h1 align="center">
Hoy es <% OutputDay(); %>
</h1>
</body>
</html>
Fichero .cs

using System;
using System.Web.UI;

public class TodayPage:Page
{
protected void OutputDay()
{
Response.Write(DateTime.Now.ToString("D"));
}
}
Cdigo fuente (Visual Studio .NET 2003)
Cdigo fuente (Visual Studio .NET 2005)
En el fichero .aspx se puede incluir cdigo, si bien lo habitual ser utilizar controles
predefinidos. Tcnicamente, la pgina .aspx hereda de la clase definida en el
fichero de cdigo, la cual a su vez hereda de System.Web.UI.Page. De forma que
basta con definir los mtodos de esta clase como protected para poder acceder a
ellos desde el fichero .aspx.
Controles
Las aplicaciones web ASP.NET, usualmente, emplean controles predefinidos de la
biblioteca de clases de la plataforma .NET. Estos controles proporcionan un
modelo orientado a objetos de los formularios web ASP.NET.
Los controles se indican en el fichero .aspx utilizando etiquetas de la forma <asp:...
/>, mientras que la lgica de la aplicacin se programa especificando la respuesta
de nuestra interfaz a los distintos eventos que puedan producirse (exactamente
igual que en cualquier entorno de programacin visual). El servidor web se
encargar de interpretar las etiquetas correspondientes a los controles ASP.NET
para que stos se visualicen correctamente en el navegador del usuario. Nosotros
no tenemos que preocuparnos de cmo generar el documento HTML que se
visualiza en el navegador web del usuario.
Aparte de proporcionar un modelo orientado a objetos de la aplicacin, que evita el
cdigo "spaghetti" tpico de ASP, los controles web proporcionan compatibilidad
automtica con distintos tipos de navegadores (aprovechando la funcionalidad de
los navegadores modernos, como JavaScript o HTML dinmico, sin dejar de
funcionar en navegadores ms antiguos, los que se limitan a soportar HTML 3.2).
ASP.NET generar el HTML que resulte ms apropiado para el navegador
concreto que utilice cada usuario.
Bsicamente, existen tres tipos de controles ASP.NET:
Controles HTML (que representan etiquetas HTML tradicionales).
Controles web (los controles asociados a las etiquetas ASP.NET).
Controles de validacin (que permiten validar entradas de una
forma cmoda, aunque no siempre resulte la ms adecuada).
Los controles anteriores se pueden agrupar para construir controles definidos
por el usuario, que nos permitirn reutilizar con comodidad fragmentos de
nuestra interfaz de usuario.
Controles HTML
Las etiquetas HTML estndar, por defecto, se tratan como texto en el servidor y se
envan tal cual al cliente. Para hacerlas programables hay que aadirles un
atributo runat="server". En el siguiente ejemplo podemos ver cmo podemos hacer
que un enlace HTML (control HtmlAnchor) apunte dinmicamente a la URL que
nos convenga: slo tenemos que establecer un valor adecuado para su propiedad
HRef en el cdigo asociado a alguno de los eventos de la pgina ASP.NET (p.ej.
Page_Load):
En la pgina ASP.NET:

<html>
...
<body>
<form id="HTMLControl" method="post" runat="server">
<a id="enlace" runat="server">Visite nuestra
pgina!</a>
</form>
</body>
<html>
En el fichero de cdigo que hay detrs:

public class HTMLControl : System.Web.UI.Page
{
protected System.Web.UI.HtmlControls.HtmlAnchor
enlace;

private void Page_Load(object sender, System.EventArgs
e)
{
enlace.HRef = "http://csharp.ikor.org/";
}

override protected void OnInit(EventArgs e)
{
this.Load += new System.EventHandler(this.Page_Load);
base.OnInit(e);
}
}
En Visual Studio .NET, para poder utilizar un control HTML en
el servidor slo tenemos que seleccionar la opcin "Ejecutar
como control del servidor" en el men contextual asociado a
la etiqueta HTML en el diseador de formularios web. Esto
hace que se aada la declaracin correspondiente a la clase
que define nuestro formulario, con lo cual ya podemos
programar el comportamiento del control HTML.
Cdigo fuente (Visual Studio .NET 2003)
Cdigo fuente (Visual Studio .NET 2005)
Todos los controles en una pgina ASP.NET deben estar dentro de una etiqueta
<form> con el atributo runat="server".
Adems, ASP.NET requiere que todos los elementos HTML estn correctamente
anidados y cerrados (como en XML). De hecho, una pgina ASP.NET es un
fichero XHTML (Extensible HyperText Markup Language), un estndar de HTML
compatible con la sintaxis de XML.
Control HTML
Etiqueta
HTML
Descripcin
HtmlAnchor <a> Enlace
HtmlButton <button> Botn
HtmlForm <form> Formulario
HtmlGenericControl

Cualquier elemento HTML no
cubierto por un control HTML
especfico
HtmlImage <image> Imagen
HtmlInput...
<input
type="...">
Distintos tipos de entradas en
un formulario HTML: botones
("button", "submit" y "reset"),
texto ("text" y "password"),
opciones ("checkbox" y
"radio"), imgenes ("image"),
ficheros ("file") y entradas
ocultas ("hidden").
HtmlSelect <select>

HtmlTable...
<table>
<tr> <td>
Tablas, filas y celdas
HtmlTextArea <textarea>

Controles web
Los controles web corresponden las etiquetas ASP.NET <asp:...> y, como es
lgico, tambin requieren el atributo runat="server" para funcionar. La sintaxis de
las etiquetas ASP.NET es la siguiente:
<asp:control id="identificador" runat="server" />
donde control especifica el tipo de control web (etiquetas, botones, listas, etc.) e
identificador especifica el identificador que le asociamos a la variable mediante la
cual accederemos al control en nuestro cdigo.
Creamos una aplicacin web ASP.NET con un formulario web
al que denominamos WebControl.aspx. A continuacin,
aadimos un botn usando el control Button que aparece en
la seccin "Web Forms" del "Cuadro de herramientas". Esto
da lugar a algo similar a lo siguiente en la pgina ASP.NET:

...
<form id="WebControl" method="post" runat="server">
<asp:Button id="Button" runat="server" Text="Pulse el
botn"></asp:Button>
</form>
...
Haciendo doble click sobre el botn podemos especificar la
respuesta de este control al evento que se produce al pulsar
el botn:

private void Button_Click(object sender, System.EventArgs
e)
{
Button.Text = "Ha pulsado el botn";
}
Cdigo fuente (Visual Studio .NET 2003)
Cdigo fuente (Visual Studio .NET 2005)
Como se puede comprobar, el desarrollo de aplicaciones web con controles
ASP.NET es completamente anlogo al desarrollo de aplicaciones Windows. Slo
tenemos que seleccionar los controles adecuados para nuestra interfaz e
implementar la respuesta de nuestra aplicacin a los eventos que deseemos
controlar.
Control Descripcin
AdRotator
Muestra una secuencia de imgenes (p.ej.
banners)
Button Botn estndar
Calendar Calendario mensual
CheckBox
Check box (como en los formularios
Windows)
CheckBoxList Grupo de check boxes
DataGrid Rejilla de datos
DataList
Muestra una lista utilizando plantillas
(templates)
DropDownList Lista desplegable
HyperLink Enlace
Image Imagen
ImageButton Botn dibujado con una imagen
Label Etiqueta (texto esttico)
LinkButton Botn con forma de enlace
ListBox Lista (como en los formularios Windows)
Literal Texto esttico (similar a Label)
Panel
Contenedor en el que se pueden colocar
otros controles
PlaceHolder
Reserva espacio para controles aadidos
dinmicamente
RadioButton
Botn de radio (como en los formularios
Windows)
RadioButtonList Grupo de botones de radio
Repeater
Permite mostrar listas de controles (vase la
seccin "Data Binding")
Table Tabla
TextBox Caja de edicin
Xml
Muestra un fichero XML o el resultado de
una transformacin XSL
Controles de validacin
Los controles de validacin son un tipo especial de controles ASP.NET que
permiten validar las entradas de nuestra aplicacin web. Cuando la entrada no
verifica la condicin que le imponemos a travs de un control de validacin, se le
muestra un mensaje de error al usuario.
La validacin de las entradas se realiza automticamente cuando se pulsa un
botn (ya tenga ste la forma de un botn estndar, Button, de una imagen,
ImageButton, o de un enlace, LinkButton). No obstante, se puede desactivar la
validacin si establecemos la propiedad CausesValidation del botn a false.
Por ejemplo, podemos forzar que una entrada est dentro de
un rango vlido de valores con un control de tipo
RangeValidator:

Slo tenemos que establecer la propiedad ControlToValidate
(igual al TextBox que usamos como entrada), el mensaje de
error que se mostrar (ErrorMessage) y la condicin que ha
de verificar la entrada (en este caso, su tipo, Type=Integer, y
el rango de valores permitido, entre MinimumValue y
MaximumValue).
Cdigo fuente (Visual Studio .NET 2003)
Cdigo fuente (Visual Studio .NET 2005)
Cuando los datos introducidos por el usuario son vlidos, la aplicacin prosigue su
ejecucin. Cuando no se verifica alguna condicin de validacin, se muestra el
mensaje de error asociado y se le vuelve a pedir al usuario que introduzca
correctamente los datos de entrada.
Obviamente, la validacin la podramos haber realizado implementando la
respuesta de nuestra aplicacin a los eventos asociados utilizando controles web
estndar, aunque de esta forma, ASP.NET se encargar de generar
automticamente las rutinas de validacin. En el caso de que nuestro navegador
acepte JavaScript, por ejemplo, la validacin se realizar en el cliente,
disminuyendo de esta forma la carga que ha de soportar el servidor de
aplicaciones web.
Control de validacin Descripcin
CompareValidator
Compara el valor de una entrada
con el de otra o un valor fijo.
CustomValidator
Permite implementar un mtodo
cualquiera que maneje la
validacin del valor introducido.
RangeValidator
Comprueba que la entrada est
entre dos valores dados.
RegularExpressionValidator
Valida el valor de acuerdo a un
patrn establecido como una
expresin regular.
RequiredFieldValidator
Hace que un valor de entrada
sea obligatorio.
ValidationSummary
Muestra un informe con todos los
errores de validacin de la
pgina.
Controles definidos por el usuario
ASP.NET permite encapsular un conjunto de controles en un nico control .ascx
definido por el usuario ("Control de usuario Web") usando las opciones "Agregar
nuevo elemento..." o "Agregar componente..." que aparecen tanto en el men
Proyecto como en el men contextual asociado a un proyecto ASP.NET en el
Explorador de Soluciones del Visual Studio.
En el fichero .ascx se define la presentacin visual del
control, como si se tratase de una pginas .aspx, salvo que la
directiva que aparece al comienzo del fichero es @Control:
<%@ Control Language="c#" AutoEventWireup="false"
Codebehind="ContactViewer.ascx.cs"...

En el fichero de cdigo .ascx.cs definimos una subclase de
System.Web.UI.UserControl, con todas las propiedades que
deseemos:
public class ContactViewer : System.Web.UI.UserControl
{
protected System.Web.UI.WebControls.Label LabelAddress;
protected System.Web.UI.WebControls.Label LabelFax;
protected System.Web.UI.WebControls.Label LabelMobile;
protected System.Web.UI.WebControls.Label
LabelTelephone;
protected System.Web.UI.WebControls.Image ImagePhoto;
protected System.Web.UI.WebControls.Label LabelName;
protected System.Web.UI.WebControls.Label
LabelComments;
protected System.Web.UI.WebControls.HyperLink
LinkEMail;


public Contact DisplayedContact {
get { return (Contact) ViewState["contact"]; }
set { ViewState["contact"] = value; UpdateUI(); }
}

private void UpdateUI () {

Contact contact = (Contact) ViewState["contact"];

if (contact!=null) {
LabelName.Text = contact.Name;
LinkEMail.Text = contact.EMail;
LinkEMail.NavigateUrl = "mailto:"+contact.EMail;
LabelTelephone.Text = contact.Telephone;
LabelMobile.Text = contact.Mobile;
LabelFax.Text = contact.Fax;
LabelAddress.Text =
contact.Address.Replace("\n","<br>");
LabelComments.Text = contact.Comments;

ImagePhoto.ImageUrl =
"contacts/"+contact.ImageURL;
}
}

...
}
Esto nos permite reutilizar el control en la creacin de distintas pginas ASP.NET
sin tener que repetir todos los detalles asociados a la implementacin del conjunto
de de componentes encapsulados por el control .ascx (p.ej. layout y presentacin
estndar, validacin de datos...).

Para utilizar el control .ascx en nuestro formulario, podemos utilizar la directiva
@Register e incluir nuestro control como si de un control predefinido se tratase:
<%@ Register TagPrefix="user" TagName="ContactViewer"
Src="ContactViewer.ascx" %>
...

<form runat="server">
...
<user:contactviewer id="ContactView" runat="server"
Visible="False">
</user:contactviewer>
...
</form>
Tambin podemos arrastrar el control .ascx directamente del Explorador de
Soluciones al Diseador de Formularios Web. Curiosamente, los proyectos
"Aplicacin Web ASP.NET" de Visual Studio 2005 no permiten arrastrar un control
definido por el usuario desde el explorador de soluciones hasta el diseador de
formularios, algo que s podremos hacer si hemos creado nuestro proyecto usando
la opcin "Nuevo Sitio Web" (alguien lo entiende?).
En tiempo de diseo, el control aparece como un simple botn en el Visual Studio
2003. En el Visual Studio 2005 s podemos ver, en el diseador de formularios,
cmo quedar el control en tiempo de ejecucin.

En cualquiera de los casos, al ejecutar nuestra aplicacin, esto es lo que nos
encontraremos:

Agenda de contactos: Cdigo fuente de la aplicacin (Visual Studio 2003)
Agenda de contactos: Cdigo fuente de la aplicacin (Visual Studio 2005, como
"Aplicacin Web ASP.NET")
Agenda de contactos: Cdigo fuente de la aplicacin (Visual Studio 2005, como
"Sitio Web")
Funcionamiento de las pginas ASP.NET
En una pgina ASP.NET, todos los controles cuyo funcionamiento haya de
controlarse en el servidor deben estar incluidos dentro de nu formulario HTML
(etiqueta <form>) con el atributo runat="server", el cual le indica al IIS (o al
servidor de desarrollo del Visual Studio) que el formulario ha de procesarse en el
servidor antes de envirselo al cliente:
<form runat="server">

... HTML y controles ASP.NET ...

</form>
Este formulario, que ha de ser necesariamente nico, es el que se encarga de
facilitar la interaccin del servidor con el cliente. De hecho, las pginas ASP.NET
funcionan de una forma similar a los formularios Windows, si bien el modo de
interaccin de las interfaces web introduce algunas limitaciones (al ser cada
solicitud recibida por el servidor independiente de las anteriores).
Postbacks
Al solicitar una pgina ASP.NET desde un cliente, en el servidor se dispara el
evento Page_Load asociado a la pgina antes de generar ninguna salida. Es en
este evento donde podemos realizar las tareas de inicializacin de la pgina
(rellenado de listas de valores, establecimiento de valores por defecto...).
El evento Page_Load se dispara CADA VEZ que se accede a la pgina. Si lo que
queremos es ejecutar algo SLO LA PRIMERA VEZ, podemos emplear la
propiedad Page.IsPostBack. Esta propiedad es false cuando el cliente carga por
primera vez la pgina y true cuando la pgina se devuelve al servidor ("post back")
al pulsar el usuario un botn del formulario web. El uso de "postbacks" es una
tcnica comn para manejar los datos de un formulario web y consiste en enviar
los datos del formulario a la misma pgina que lo gener dinmicamente.
Estado de la pgina
ASP.NET nos ahorra tener que escribir mucho cdigo al encargarse
automticamente de mantener el estado de los controles de los formularios web
(ViewState).
En ASP clsico, al enviar un formulario, todos sus valores se vacan,
por lo que, si se ha producido un pequeo error en uno de los
valores, tendremos que programar nosotros el cdigo que se
encargue de rellenar los valores que s eran correctos (siempre y
cuando queramos evitarle al usuario tener que introducir de nuevo
todos los datos, claro est). Esto resulta bastante tedioso y propenso
a errores.
En ASP.NET, el formulario reaparece en el navegador del cliente con
los valores que tuviese en el momento de enviarlo al servidor.
El estado de la pgina se define mediante un campo oculto denominado
__VIEWSTATE que se le aade a cada pgina que tenga un formulario con el
atributo runat="server", de forma que es el cliente el que se encarga de mantener
el estado. Si visualizamos el cdigo fuente de la pgina HTML que se muestra en
el cliente, podemos ver algo as:
<input type="hidden" name="__VIEWSTATE"

value="dDwtMjEwNjQ1OTkwMDs7PiTPnxCh1VBUIX3K2htmyD8Dq6oq"
/>
El mantenimiento del estado de la pgina es automtico salvo que explcitamente
indiquemos lo contrario:
A nivel de la pgina, podemos emplear la directiva <%@ Page
EnableViewState="false" %> en la cabecera del fichero .aspx.
A nivel de un control particular, podemos establecer su propiedad
EnableViewState a false.
Antes tenamos que haber escrito algo como...

<%@ Page language="c#" %>
<html>
<body>
<form runat="server" method="post">
Tu nombre: <input type="text" name="nombre" size="20">
<input type="submit" value="Enviar">
</form>
<%
string name = Request.Form["nombre"];

if (name!=null && name!="") {
Response.Write("Hola, " + name + "!");
}
%>
</body>
</html>
En ASP.NET, no obstante, podemos teclear lo siguiente y
evitar que el valor introducido desaparezca:
<%@ Page language="c#" %>
<html>
<script runat="server">
void enviar (object sender, EventArgs e)
{
label.Text = "Hola, " + textbox.Text + "!";
}
</script>
<body>
<form runat="server" method="post">
Tu nombre: <asp:TextBox id="textbox" runat="server" />
<asp:Button OnClick="enviar" Text="Enviar" runat="server"
/>
<p><asp:Label id="label" runat="server" /></p>
</form>
</body>
</html>
Cdigo fuente
Data binding

Introduccin
Se pueden asociar datos a las propiedades de los controles de la interfaz grfica
mediante tres mecanismos diferentes:
Podemos especificar los datos que deseemos en las etiquetas
asociadas a los controles (<asp:... />), aunque slo de forma
esttica, no dinmica.
Podemos implementar fragmentos de cdigo que hagan uso del
modelo orientado a objetos asociado a los controles de nuestros
formularios. sta es la opcin habitual que se emplea para rellenar
valores o listas sencillas (por ejemplo, en el evento Form_Load del
formulario web).
En situaciones ms complejas que requieran algo ms sofisticado
podemos utilizar un mecanismo denominado "data binding". En
primer lugar, debemos disponer de un objeto que contenga los datos
(DataSet, Array...). A continuacin, asociamos ese objeto al control.
o De esta forma, separamos claramente el cdigo de nuestra
aplicacin de la interfaz de usuario.
o Podemos enlazar el control a una amplia variedad de fuentes
de datos: colecciones (Array, Hashtable, etc.), conjuntos de
datos (DataSet, DataTable, DataView, DataReader), ficheros
XML, propiedades, expresiones, llamadas a mtodos...
o Hemos de tener en cuenta que deberemos introducir cdigo
para actualizar en enlace (esto es, el mecanismo por defecto
rellena una nica vez las propiedades que hayamos enlazado
["one way snapshot model"]).
o En las pginas ASP.NET podemos incluir expresiones de
enlace a datos de la forma <%# expresin %> (de forma
anloga a las etiquetas personalizadas de JSP en Java).
Listas de opciones
Se puede utilizar "data binding" para rellenar listas con elementos que provengan
de alguna fuente de datos, ya sea sta una base de datos, un fichero XML o un
script. Los siguientes controles permiten utilizar listas en un formulario web
ASP.NET: asp:RadioButtonList, asp:CheckBoxList, asp:DropDownList y
asp:Listbox. Los elementos de dichas listas se suelen definir utilizando uno o
varios componentes asp:ListItem, como en el siguiente ejemplo:
<html>
<body>
<form runat="server">
<asp:RadioButtonList id="provincia" runat="server">
<asp:ListItem value="GR" text="Granada" />
<asp:ListItem value="AL" text="Almera" />
<asp:ListItem value="MA" text="Mlaga" />
<asp:ListItem value="J" text="Jan" />
<asp:ListItem value="CO" text="Crdoba" />
<asp:ListItem value="SE" text="Sevilla" />
<asp:ListItem value="CA" text="Cdiz" />
<asp:ListItem value="HU" text="Huelva" />
</asp:RadioButtonList>
</form>
</body>
</html>
Sin embargo, resulta ms adecuado utilizar una fuente de datos independiente
para rellenar la lista. De esta forma, los datos se separan del HTML y cualquier
cambio que se produzca en ellos ser ms fcil de hacer (al no tener que
modificar los elementos de la lista en todos los sitios de la interfaz donde
aparezcan, sino slo en la fuente de datos, que debera ser nica).
ArrayList
La coleccin ArrayList contiene una lista de objetos y podemos utilizarla para
rellenar los elementos de una lista en nuestra interfaz.
Al cargar la pgina, creamos la lista y aadimos los elementos que nos hagan falta
utilizando el mtodo Add(). Una vez que tenemos los elementos en la lista,
podemos ordenarlos con el mtodo Sort(). Si los quisiramos en orden inverso, no
tendramos ms que llamar al mtodo Reverse una vez que los tengamos
ordenados. Finalmente, una vez que tenemos la lista con los elementos
ordenados, slo tenemos que enlazar la lista al ArrayList utilizando la propiedad
DataSource del control y llamar al mtodo DataBind() del control para rellenar
nuestra lista:
private void Page_Load(object sender, System.EventArgs e)
{
if (!Page.IsPostBack) {
ArrayList list = new ArrayList();

list.Add("Granada");
list.Add("Almera");
list.Add("Mlaga");
list.Add("Jan");
list.Add("Crdoba");
list.Add("Sevilla");
list.Add("Cdiz");
list.Add("Huelva");
list.Sort();

provincia.DataSource = list;
provincia.DataBind();
}
}

En este caso, los elementos del array se utilizan como texto (Text) y como valor
(Value) asociado a los distintos elementos de la lista en nuestra interfaz, si bien
esto no tiene por qu ser as siempre. Para emplear valores internos diferentes a
las etiquetas que visualiza el usuario, podemos emplear colecciones de tipo
Hashtable o SortedList.
Hashtable
Las tablas hash (Hashtable) contienen pares clave-valor y podemos utilizarlas si
queremos emplear los cdigos correspondientes a las provincias en vez de sus
nombres completos, para lo cual hemos de indicar qu vamos a emplear como
etiqueta (DataTextField) y qu como valor (DataValueField):
private void Page_Load(object sender, System.EventArgs e)
{
if (!Page.IsPostBack) {
Hashtable table = new Hashtable();

table.Add("GR", "Granada");
table.Add("AL", "Almera");
table.Add("MA", "Mlaga");
table.Add("J", "Jan");
table.Add("CO", "Crdoba");
table.Add("SE", "Sevilla");
table.Add("CA", "Cdiz");
table.Add("HU", "Huelva");

provincia.DataSource = table;
provincia.DataValueField="Key";
provincia.DataTextField="Value";
provincia.DataBind();
}
}
El nico inconveniente de las tablas hash es que no se puede elegir el orden de
los elementos de la lista. Para poder ordenar los elementos deberemos emplear la
coleccin SortedList, que se puede utilizar exactamente igual que la tabla hash y
se mantiene ordenada automticamente.
Ejercicio
Utilizar una lista ordenada en vez de la tabla hash del ejemplo
anterior.
Ficheros XML
Incluso podemos rellenar las listas de nuestra interfaz utilizando un fichero XML
auxiliar como el siguiente:
<?xml version="1.0" encoding="ISO-8859-1"?>
<provincias>
<provincia>
<id>AL</id>
<nombre>Almera</nombre>
</provincia>
<provincia>
<id>CA</id>
<nombre>Cdiz</nombre>
</provincia>
<provincia>
<id>CO</id>
<nombre>Crdoba</nombre>
</provincia>
<provincia>
<id>GR</id>
<nombre>Granada</nombre>
</provincia>
<provincia>
<id>HU</id>
<nombre>Huelva</nombre>
</provincia>
<provincia>
<id>J</id>
<nombre>Jan</nombre>
</provincia>
<provincia>
<id>MA</id>
<nombre>Mlaga</nombre>
</provincia>
<provincia>
<id>SE</id>
<nombre>Sevilla</nombre>
</provincia>
</provincias>
Para hacer el enlace, construimos un DataSet a partir del fichero XML y
enlazamos dicho DataSet al control:
private void Page_Load(object sender, System.EventArgs e)
{
if (!Page.IsPostBack) {
DataSet dataset = new DataSet();
dataset.ReadXml(MapPath("provincias.xml"));

provincia.DataSource = dataset;
provincia.DataValueField="id";
provincia.DataTextField="nombre";
provincia.DataBind();
}
}
PD: Por cierto, la propiedad SelectedItem de la lista es la que nos indica la opcin
seleccionada por el usuario, mientras que el evento SelectedItemChanged se
produce cuando el usuario selecciona una de las opciones de la lista.
Cdigo fuente de los ejemplos de esta seccin
Conjuntos de datos
El control asp:Repeater sirve para mostrar conjuntos de datos:

En primer lugar, aadimos un control asp:Repeater al formulario web. A
continuacin, podemos utilizar un conjunto de datos construido a partir del fichero
XML que usamos en el ltimo ejemplo de la seccin anterior. Dicho conjunto de
datos hemos de enlazarlo al control asp:Repeater:
protected System.Web.UI.WebControls.Repeater provincia;
...

private void Page_Load(object sender, System.EventArgs e)
{
if (!Page.IsPostBack) {
DataSet dataset = new DataSet();
dataset.ReadXml(MapPath("provincias.xml"));
provincia.DataSource = dataset;
provincia.DataBind();
}
}
Finalmente, hemos de especificar cmo se visualizar cada dato de los que
hemos enlazado al control asp:Repeater, para lo cual no nos queda ms remedio
que editar el cdigo HTML de la pgina ASP.NET. Mediante el uso de plantillas
definimos el aspecto visual de nuestro conjunto de datos:
<asp:Repeater id="provincia" runat="server">
<HeaderTemplate>
<table border="3" cellpadding="5">
<tr>
<th>
Cdigo</th>
<th>
Nombre</th>
</tr>
</HeaderTemplate>
<ItemTemplate>
<tr>
<td>
<%# DataBinder.Eval(Container.DataItem, "id") %>
</td>
<td>
<%# DataBinder.Eval(Container.DataItem, "nombre")
%>
</td>
</tr>
</ItemTemplate>
<FooterTemplate>
</table>
</FooterTemplate>
</asp:Repeater>
La plantilla <HeaderTemplate> se muestra al comienzo de la salida
asociada al control asp:Repeater.
Para cada "registro" de nuestro conjunto de datos se utiliza la
plantilla <ItemTemplate>. Esta plantilla la rellenamos con las
etiquetas HTML que deseemos acompaadas de expresiones del
tipo <%# DataBinder.Eval(Container.DataItem, "campo") %> para
visualizar los valores de los datos de nuestro conjunto de datos.
Por ltimo, la plantilla <FooterTemplate> cierra la salida asociada al
control asp:Repeater.
Opcionalmente, se puede utilizar una plantilla
<AlternatingItemTemplate> para que las filas pares e impares se
visualicen de forma distinta (p.ej. utilizando colores alternos).
Adems, el control asp:Repeater incluye la posibilidad de utilizar una
plantilla adicional (<SeparatorTemplate>) para describir lo que
queremos que aparezca entre registro y registro.
Ejercicio
Modificar el ejemplo anterior de forma que la tabla se muestre
empleando colores alternos con ayuda de una plantilla de tipo
<AlternatingItemTemplate>.

Cdigo fuente de los ejemplos de esta seccin
En ASP.NET existen otros controles aparte de
System.Web.UI.WebControls.Repeater que se pueden emplear para visualizar e
incluso modificar conjuntos de datos. En concreto, los controles
System.Web.UI.WebControls.DataList y System.Web.UI.WebControls.DataGrid (o
asp:GridView) pueden ser de gran utilidad en el desarrollo de aplicaciones web.
El control asp:DataList es similar a asp:Repeater, si bien incluye por defecto una
tabla alrededor de los datos y resulta ms adecuado para su diseo desde el
Visual Studio .NET, desde el que se pueden especificar sus distintas plantillas y
definir su amplia gama de propiedades de forma visual (p.ej. estilos CSS). A las
plantillas de asp:Repeater hay que aadir otras que permiten, por ejemplo, resaltar
un elemento seleccionado dentro del conjunto de datos o editar su valor.
Finalmente, los controles asp:DataGrid y asp:GridView son an ms verstiles a la
hora de manipular conjuntos de datos (y ms complejos a la hora de disear la
aplicacin, si bien es cierto el entorno nos ayuda mucho al poder disear el
contenido de la tabla de forma "visual").
Ejercicio
Construir una tabla como la del ejemplo anterior utilizando el
control asp:DataGrid o asp:GridView.
Sesiones de usuario

HTTP
El protocolo HTTP [HyperText Transfer Protocol] es un protocolo simple de tipo
solicitud-respuesta, de modo que se establece una conexin diferente cada vez
que accedemos a una pgina:

Cuando tecleamos la direccin de una pgina, el navegador web establece una
conexin TCP con el servidor (usualmente a travs del puerto 80). A continuacin,
el cliente enva un mensaje al servidor (solicitud) y ste le responde con otro
mensaje (respuesta). Tras esto, la conexin se cierra y el ciclo vuelve a empezar.
No obstante, hay que mencionar que, por cuestiones de eficiencia (reduccin de la
congestin en la red), HTTP/1.1 mantiene conexiones persistentes, lo cual no
quiere decir que la interaccin entre cliente y servidor vare desde el punto de vista
lgico.
HTTP slo distingue dos tipos de mensajes (solicitudes y respuestas) que se
diferencian nicamente en su primera lnea. Tanto solicitudes como respuestas
pueden incluir distintas cabeceras (del tipo clave: valor) y un texto como cuerpo
del mensaje.
El formato de una solicitud es de la siguiente forma:
GET http://elvex.ugr.es/index.html
If-Modified-Since: Thu, 31 Oct 2002 19:41:00 GMT
La primera lnea de la solicitud, aparte de indicar la versin de HTTP utilizada,
tambin determina el mtodo utilizado para acceder al recurso identificado
mediante un URI [Universal Resource Identifier], tal como se define en el RFC
2396. Los mtodos ms usados son GET y POST (que slo se diferencian en la
forma de pasar los parmetros de los formularios), as como HEAD (que slo
devuelve metadatos acerca del recurso solicitado).
La respuesta del servidor ser del tipo:
HTTP/1.1 200 OK
Server: Microsoft-IIS/5.0
Date: Sun, 17 Aug 2003 10:35:30 GMT
Content-Type: text/html
Last-Modified: Tue, 27 Mar 2001 10:34:52 GMT
Content-Length: XXX
<html>
--- Aqu se enva el texto de la pgina HTML
</html>
Como se puede apreciar, en la primera lnea de la respuesta aparece la versin de
HTTP empleada, un cdigo de estado de tres dgitos y una descripcin de ese
cdigo de estado. La siguiente tabla resume las distintas categoras de los cdigos
devueltos en las respuestas HTTP:
Cdigo Significado
1xx Mensaje informativo
2xx xito (vg: 200 OK)
3xx Redireccin (vg: 302 Resource temporarily moved)
4xx
Error en el cliente (vg: 400 Bad request, 401
Unauthorized, 403 Forbidden)
5xx Error en el servidor (vg: 500 Internal Server Error)
Cookies
HTTP, por definicin, es un protocolo sin estado. Sin embargo, al desarrollar
aplicaciones web, mantener el estado resulta imprescindible. Por ejemplo, en un
sistema de comercio electrnico debemos ser capaces de almacenar de alguna
forma el carrito de la compra de un cliente concreto.
Una primera solucin a este problema (no demasiado acertada, por cierto)
consiste en gestionar sesiones utilizando cookies (RFC 2965). Conforme
navegamos vamos generando cookies ["galletitas"] en el cliente que luego se
encargar de consumir el servidor. La idea es utilizar una cookie por servidor o
grupo de servidores que se encargue de almacenar en el cliente la informacin
que pueda necesitar el servidor (p.ej. los artculos que llevamos en el cesto de la
compra).

Obsrvese que los datos asociados a una cookie se almacenan en la mquina
cliente (usualmente sin ningn tipo de proteccin) y, por tanto, no deberan nunca
incluir informacin cuya privacidad sera deseable mantener. Adems, el uso de
cookies lo provoca el servidor y no el cliente (Set-Cookie2), por lo que en
determinadas situaciones puede resultar conveniente configurar el cliente para
que ignore las cookies (no devolviendo la cabecera Cookie2 que le sirve al
servidor para mantener la sesin).
Sesiones en ASP.NET
ASP.NET proporciona un conjunto de objetos que nos permiten gobernar la
interaccin entre el cliente y el servidor al desarrollar aplicaciones web. En
realidad, todo ASP.NET se construye a partir del interfaz IHttpHandler:
IHttpHandler {
void ProcessRequest (HttpContext context);
bool IsReusable ();
}
Para escribir nuestra aplicacin web bastara con construir una clase que
implementase este interfaz y utilizar sentencias del tipo
context.Response.Write("<html>...");. Como es lgico, esto no sera mucho mejor
que programar CGIs y nosotros aprovecharemos los distintos objetos que nos
proporciona ASP.NET para facilitarnos el trabajo. ASP.NET se encargar de
compilar nuestra pgina construyendo una clase derivada de
System.Web.UI.Page, la cual implementa la interfaz IHttpHandler
Los objetos ms importantes con los que trabajamos en ASP.NET son los
siguientes:
Objeto Representa...
HttpContext El entorno en el que se atiende la peticin
Request La peticin HTTP realizada por el cliente
Response La respuesta HTTP devuelta por el servidor
Server Algunos mtodos tiles
Application
Variables globales a nivel de la aplicacin
(comunes a todas las solicitudes recibidas desde
cualquier cliente)
Session
Variables globales a nivel de una sesin de
usuario (comunes a todas las solicitudes de un
cliente concreto)
Los dos ltimos objetos son los que nos permiten manejar con comodidad las
sesiones de usuario en ASP.NET y mantener el contexto en el que un usuario
interacta con una aplicacin web. Mientras el usuario siempre interacte con la
misma pgina, ASP.NET se encarga de mantener el estado de sta de forma
automtica. No obstante, en cuanto el usuario cambie de pgina (algo habitual en
cualquier aplicacin web real), tendremos que almacenar la informacin de su
sesin de alguna forma:
Manualmente en el cliente, usando cookies a bajo nivel (una solucin
no demasiado buena).
Manualmente en el servidor, usando ficheros o, preferiblemante,
bases de datos (la solucin ptima en entornos distribuidos y
sistemas tolerantes a fallos).
Automticamente, usando las colecciones Session y Application
facilitadas por ASP.NET. Dichas colecciones las podemos inicializar
utilizando los mtodos Session_Start y Application_Start del fichero
Global.asax asociado a la aplicacin web (o su fichero de cdigo
asociado Global.asax.cs).
En el caso de la coleccin Session, sta se comparte entre todas las solicitudes de
clientes que utilicen el mismo identificador de sesin. Dicho identificador se genera
automticamente cuando el cliente accede por primera vez a la aplicacin web y
se transmite desde el cliente cada vez que ste vuelve acceder a una pgina de la
aplicacin web.
El identificador de la sesin se puede transmitir utilizando la cookie
ASP.NET_SessionId:
cqcgvjvjirmizirpld0dyi5
o sin emplear cookies, incluyendo el identificador en la URL:

http://servidor/aplicacin/(uqwkag45e35fp455t2qav155)/pgina.aspx
Para acceder a la informacin de la sesin no tenemos ms que utilizar la
propiedad Session de nuestra pgina ASP.NET, ya que Session est definida en
System.Web.UI.Page, la clase de la que derivan todas las pginas ASP.NET:
Inicializacin en el fichero Global.asax (o Global.asax.cs):

void Session_Start()
{
Session["UserName"] = "";
}
En la pgina de entrada a la aplicacin:

void Page_Load (Object Src, EventArgs e)
{
Session["UserName"] = TextBoxUser.Text;
}
Desde las pginas internas de la aplicacin:

labelUser.Text = (string) Session["UserName"];
NOTA: El fichero Global.asax.cs se crea automticamente en
Visual Studio 2003 cuando creamos un proyecto de tipo
"aplicacin web ASP.NET". En Visual Studio 2005 hemos de
crearlo explcitamente aadiendo un nuevo elemento de tipo
"Clase de aplicacin global" a nuestro proyecto web. En el
caso de un "sitio web", se crear un nico fichero Global.asax
en el que incluiremos el cdigo correspondiente a los eventos
globales de la aplicacin, mientras que se crearn dos
ficheros independientes (Global.asax y Global.asax.cs) si
nuestro proyecto es de tipo "aplicacin web ASP.NET".
Utilizando las colecciones Application y Session, por ejemplo, podemos aadirle a
una pgina ASP.NET un "contador" de visitas que nos permita contabilizar el
trfico que recibe nuestra pgina (saber cuntas veces se visualiza la pgina y
cuntas veces accede a la pgina un mismo usuario en una nica sesin):

En primer lugar, inicializamos los contadores que usaremos en el fichero
Global.asax (o Global.asax.cs):
protected void Application_Start(Object sender, EventArgs e)
{
Application["contador"] = 0;
}

protected void Session_Start(Object sender, EventArgs e)
{
Session["contador"] = 0;
}
A continuacin, nos asegurarnos de que, cada vez que se accede a la pgina, los
contadores contabilizan el acceso a la pgina. Esto lo podemos hacer, por
ejemplo, como respuesta al evento Page_Load (respuesta que definiremos en el
fichero aspx.cs asociado a nuestra pgina):
protected System.Web.UI.WebControls.Label visitas;

private void Page_Load(object sender, System.EventArgs e)
{
Application["contador"] = (int)Application["contador"] + 1;
Session["contador"] = (int)Session["contador"] + 1;

visitas.Text = Application["contador"] + " hits"
+ " (" + Session["contador"] + " del usuario actual)";
}
Cdigo fuente del ejemplo para Visual Studio 2003
Cdigo fuente del ejemplo para Visual Studio 2005
A diferencia de ASP, ASP.NET no requiere cookies y, an mejor, permite
configurar la forma en la que se gestionan las sesiones en la seccin
<sessionState ... /> del fichero de condiguracin Web.config (que se crea
automticamente en Visual Studio 2003 pero hemos de aadirlo explcitamente en
Visual Studio 2005). Este fichero de configuracin nos permite, entre otras cosas,
no tener que modificar el cdigo de nuestra aplicacin cuando, en vez de un nico
servidor, utilizamos un cluster y no queremos forzar la afinidad de un cliente a un
servidor fijo.
Alternativas para gestionar las sesiones de usuario en ASP.NET
InProc: En el proceso del servidor web (aspnet_wp.exe).

StateServer: En un proceso aparte compartido entre varios servidores web
(aspnet_state.exe).

SqlServer: En un servidor SQL*Server.

La seleccin de una u otra alternativa implicar un compromiso entre la eficiencia
y la fiabilidad en la gestin de sesiones para nuestra aplicacin web.
Seguridad en .NET

Aunque la seguridad sea generalmente un tema obviado por la mayor parte de los
programadores, en una aplicacin web resulta un aspecto esencial. Planificar los
mecanismos necesarios de seguridad para evitar accesos no autorizados a
nuestras aplicaciones y servicios web se convierte, por tanto, en algo que todo
programador debera saber hacer correctamente.
Cuando hablamos de seguridad en las aplicaciones web realizadas con la
plataforma .NET, en realidad estamos hablando acerca de cmo restringir el
acceso a determinados recursos gestionados por el Internet Information Server
(IIS) de Microsoft. Sin entrar en detalles de inters para los aficionados a las
tcnicas criptogrgicas de proteccin de datos, a continuacin veremos cmo
implementar mecanismos de identificacin de usuarios a travs de contraseas
para acceder a sitios web desarrollados con pginas ASP.NET.

Autentificacin y autorizacin en IIS
El funcionamiento habitual de una aplicacin web que requiere la autentificacin
del usuario es redirigir a ste a un formulario de login cuando intenta acceder a un
rea restringida de nuestra aplicacin web. Para lograrlo, nos basta con modificar
un fichero de configuracin y el IIS se encargar de hacerlo por nosotros.
En el directorio de una aplicacin web se incluye un fichero XML de configuracin
llamado Web.config, cuya estructura se corresponde con la del siguiente ejemplo:
<configuration>
<system.web>

<authentication mode="Forms">
<forms loginUrl="login.aspx"
name=".ASPXFORMSAUTH"></forms>
</authentication>

<authorization>
<deny users="?" />
</authorization>


</system.web>
</configuration>
La seccin de autentificacin (authentication) se utiliza para establecer la poltica
de identificacin de usuarios que utilizar nuestra aplicacin:
Forms se emplea para utilizar formularios de autentificacin en los
que seremos nosotros los que decidamos quin accede a nuestra
aplicacin.
Passport permite que nuestra aplicacin utilice el sistema de
autentificacin Passport de Microsoft (ms informacin en
http://www.passport.com).
Windows se utiliza para delegar en el sistema operativo las tareas de
autentificacin de usuarios, con lo cual slo podrn acceder a
nuestra aplicacin los usuarios que existan en nuestro sistema.
Finalmente, None deshabilita los mecanismos de autentificacin, con
lo que cualquiera puede acceder a ella desde cualquier lugar del
mundo sin ninguna restriccin de acceso.
Cuando selecccionamos el modo de autentificacin Forms hemos de indicar
tambin cul ser el formulario encargado de que nuestros usuarios se
identifiquen al usar el sistema (login.aspx) en el ejemplo de arriba.
A continuacin, la seccin de autorizacin (authorization) se utiliza, en el mismo
ejemplo, para restringir el acceso a los usuarios no identificados.
Un fichero Web.config restringe el acceso a un directorio y a todos sus
subdirectorios, si bien en los subdirectorios se pueden incluir otros ficheros
Web.config que redefinan las restricciones de acceso a los subdirectorios del
directorio principal de nuestra aplicacin web.
Por ejemplo, si en una parte de nuestra aplicacin queremos que cualquier
persona pueda acceder (incluso sin identificarse), basta con incluir la siguiente
autorizacin en el fichero de configuracin correspondiente:
<authorization>
<allow users="*" />
</authorization>
Si lo que quisiramos es restringir el acceso a un usuario particular, slo
rendramos que incluir la siguiente seccin de autorizacin en nuestro fichero
Web.config:
<authorization>
<allow users="administrador" />
<deny users="*" />
</authorization>
Incluso podramos haber puesto una lista de nombres de usuario separados por
comas.
Podemos crear una aplicacin web a la cual slo tendrn acceso algunos
usuarios de nuestra mquina si utilizamos "Windows" como mecanismo de
autentificacin y restringimos el acceso annimo al directorio donde alojamos
la aplicacin en IIS (Panel de control > Herramientas administrativas >
Administracin de equipos > Servicios y Aplicaciones > Servicios de Internet
Information Server):


Una vez hecho esto, modificamos el fichero Web.config de acuerdo a
nuestras necesidades y, cuando intentemos acceder a la aplicacin nos
aparecer una ventana como la siguiente:

Si tras varios intentos no introducimos un nombre de usuario vlido y su
contrasea correcta, el servidor web nos devolver un error de autentificacin
"HTTP 401.3 - Access denied by ACL on resource":

Ejemplo de autentificacin mediante el sistema operativo (Visual Studio
2003)
Ejemplo de autentificacin mediante el sistema operativo (Visual Studio
2005)
Formularios de autentificacin en aplicaciones web ASP.NET
En determinadas ocasiones no nos podemos permitir el lujo de crear un usuario en
el sistema operativo para cada usuario que pueda acceder a nuestra aplicacin,
por lo que seremos nosotros los encargados de gestionar los usuarios y el acceso
de stos a las distintas partes de nuestras aplicaciones.
Cuando un usuario intente acceder a una pgina cuyo acceso est restringido, el
usuario ser redirigido a un formulario especfico de login, que, al menos, incluir
dos campos para que el usuario indique su nombre y su clave (dos controles de
tipo TextBox, especificando la propiedad TextMode=Password para la caja de
edicin correspondiente a la contrasea de acceso). En dicho formulario para
comprobaremos el identificador y la clave del usuario:
if ( textBoxID.Text.Equals("usuario") &&
textBoxPassword.Text.Equals("clave") ) {


FormsAuthentication.RedirectFromLoginPage(textBoxID.Text,false);

} else {

// Error de autentificacin...
}
Cuando el usuario se identifica correctamente, lo nico que hacemos es indicarle
al IIS que le permita al usuario acceder a la pgina a la que inicialmente deseaba
acceder. Para ello utilizamos un mtodo de la clase FormsAuthentication que est
incluida en el espacio de nombres System.Web.Security. El IIS se encargar de
todo lo dems por nosotros.
Cuando el usuario no introduzca una clave de acceso correcta o su identificador
no exista en nuestra base de datos de usuarios registrados, podemos mostrarle un
mensaje de error informativo mediante una etiqueta (labelMessage.Text=...) o
redirigir al usuario a la pgina que nos interese utilizando el mtodo
Response.Redirect("http://...");. Esto ltimo lo podramos utilizar si estamos
construyendo un sistema en el cual permitimos que los usuarios puedan
registrarse ellos mismos.
Creamos una aplicacin web con dos formularios, uno de los
cuales lo utilizaremos como formulario de identificacin:

En primer lugar, debemos configurar correctamente el fichero
Web.config, tal como se muestra a continuacin:
<authentication mode="Forms">
<forms loginUrl="Login.aspx"
name=".ASPXFORMSAUTH"></forms>
</authentication>

<authorization>
<deny users="?" />
</authorization>
Al intentar acceder a cualquier formulario de nuestra aplicacin, si
no estamos identificados se nos redirecciona al formulario de
login:

Si no introducimos incorrectamente la clave de acceso, se nos
debera mostrar un mensaje informativo de error:

Obviamente, en una aplicacin real no deberamos ser tan
explcitos ;-)
Slo cuando nos identifiquemos correctamente podremos acceder
a la aplicacin:

Ejemplo de autentificacin mediante formularios web (Visual
Studio 2003)
Ejemplo de autentificacin mediante formularios web (Visual
Studio 2005)
IMPORTANTE: Con lo que hemos visto, conseguimos controlar el acceso a
nuestras aplicaciones web, aunque stas an no son realmente seguras porque
los datos se transmiten tal cual, sin encriptar. Para proteger los datos que se
transmiten entre el servidor web y los clientes que acceden a l debemos
configurar el servidor web para que pueda utilizar el protocolo HTTPS. Este
protocolo utiliza tcnicas criptogrficas de clave pblica y nos har falta instalar un
certificado (un par clave pblica-clave privada) para que podamos acceder al
servidor usando https://...
HTML

El servicio de Internet ms utilizado es la World Wide Web (WWW). Para escribir
documentos para la web se utiliza el lenguaje HTML [HyperText Markup
Language].
Estructura de los documentos HTML
Un documento HTML es un fichero de texto plano (ASCII) que incluye ciertas
marcas o etiquetas [tags] que le indican al navegador, entre otras cosas, cmo
debe visualizarse el documento. Los tags se escriben encerrados entre ngulos
("<" y ">") y usualmente van por parejas ("<tag>" y "</tag>").
Un documento HTML, delimitado por la pareja de etiquetas <HTML> y </HTML>,
tiene dos partes principales:
La cabecera (entre <HEAD> y </HEAD>) contiene informacin general
sobre el documento que no se muestra en pantalla: ttulo, autor,
descripcin...
Las etiquetas <BODY> y </BODY> definen la parte principal o cuerpo del
documento.
p.ej.

<HTML>
<HEAD>
<TITLE> Ttulo del documento </TITLE>
<META NAME="Author" CONTENT="Fernando
Berzal">
<META NAME="Keywords" CONTENT="Internet,
HTML">
<META NAME="Description" CONTENT="Introduccin
al uso de Internet">
</HEAD>
<BODY>
Cuerpo del documento...
</BODY>
</HTML>
El cuerpo del documento puede incluir, entre otros elementos:
Prrafos (<P>)
Encabezados para definir ttulos y subttulos (de <H1> a <H6>, de mayor a
menor nivel)
Hiperenlaces: <A HREF="url">Texto</A>
Imgenes (GIF o JPG): <IMG SRC="fichero" BORDER=0 ALT="Texto
descriptivo">
Listas numeradas (<OL> ... </OL>) o no numeradas (<UL> ... </UL>) cuyos
elementos se indican con la etiqueta <LI>
Con el fin de personalizar la presentacin de la informacin, HTML incluye:
<B> ... </B> pone el texto en negrita.
<I> ... </I> pone el texto en cursiva.
<U> ... </U> subraya el texto.
<CENTER> ... </CENTER> sirve para centrar el texto.
<FONT SIZE="+1" COLOR="#rrggb"> ... </FONT> se emplea para cambiar
el tamao del tipo de letra (p.ej. +2 +1 -1 -2) y su color representado en
hexadecimal utilizando RGB: #000000 (negro), #ffffff (blanco), #ff0000
(rojo), #00ff00 (verde), #0000ff (azul)...
<BR> introduce saltos de lnea.
<HR> introduce una lnea horizontal a modo de separador.
Caracteres especiales
Las vocales acentuadas, las ees y otros caracteres "no estndar" (incluyendo los
ngulos que se utilizan para las etiquetas HTML) requieren secuencias especiales
de caracteres para representarlos. La siguiente tabla recoge algunas de ellas:
Carcter Secuencia HTML
& &amp;
< &lt;
> &gt;
" &quot;
&copy;
&reg;
&trade;
&euro;
&aacute;
&egrave;
&ecirc;
&uuml;
&szlig;
&Oslash;
&aelig;
&ntilde;
&Ntilde;
Tablas
Las tablas se delimitan con las etiquetas <TABLE> y </TABLE>. Entre estas dos
etiquetas se han de incluir una serie de filas delimitadas por <TR> y </TR>. Cada
fila, a su vez, incluye una serie de celdas <TD> y </TD>. Por ejemplo:

<TABLE border=2>
<TR bgcolor="#ccccee">
<TH COLSPAN=2> <IMG SRC="cogs.gif"> Tabla en
HTML </TH>
</TR>
<TR bgcolor="#e0e0e0">
<TH> Datos</TH>
<TH> Valores</TH>
</TR>
<TR>
<TD> Dato 1</TD>
<TD> Valor 1</TD>
</TR>
<TR>
<TD> Dato 2</TD>
<TD> Valor 2</TD>
</TR>
<TR>
<TD> Dato 3</TD>
<TD> Valor 3</TD>
</TR>
</TABLE>
Aparece en el navegador como:
Tabla en HTML
Datos Valores
Dato 1 Valor 1
Dato 2 Valor 2
Dato 3 Valor 3
Formularios
HTML tambin permite que el usuario no se limite a leer el contenido de la pgina,
sino que tambin puede introducir datos mediante formularios (la base del
comercio electrnico). Por ejemplo, el siguiente formulario:
<FORM METHOD="POST"
ACTION="mailto:berzal@acm.org">
<INPUT TYPE="text" NAME="NOMBRE" SIZE=30
MAXLENGTH=40>
<TEXTAREA NAME="COMENTARIOS" ROWS=6
COLS=40> </TEXTAREA>
<INPUT TYPE="submit" VALUE="Enviar sugerencias
por e-mail">
</FORM>
quedara como se muestra a continuacin dentro de una tabla:
Nombre

Comentarios

En los formularios HTML se pueden introducir:
Cuadros de texto (<INPUT TYPE="TEXT"...>).
Cuadros de texto que no muestran lo que el usuario escribe (<INPUT
TYPE="PASSWORD"...>).
Textos de varias lneas (<TEXTAREA ...> ... </TEXTAREA>).
Opciones (<INPUT TYPE="CHECKBOX"...>).
Opciones mutuamente excluyentes (<INPUT TYPE="RADIO"...>).
Listas para seleccionar valores (<SELECT> <OPTION> <OPTGROUP>).
Ficheros adjuntos (<INPUT TYPE="FILE"...>).
Parmetros ocultos para el usuario (<INPUT TYPE="HIDDEN"...>).
Para enviar los datos del formulario a la URL especificada en FORM ACTION, se
puede utilizar un botn (<INPUT TYPE="SUBMIT"...>) o emplear una imagen
(<INPUT TYPE="IMAGE"...>), de tal forma que la accin que se realice pueda
depender de la zona de la imagen que seleccione el usuario.
Hojas de estilo
Para facilitar el mantenimiento de nuestras pginas HTML y asegurarnos de que
se mantiene cierta coherencia en su presentacin de cara al usuario, podemos
utilizar hojas de estilo CSS (Cascading Style Sheets).
Para emplear una hoja de estilo en la presentacin de nuestra pgina web, slo
tenemos que incluir la siguiente etiqueta antes del cuerpo de nuestro documento
HTML:

<link REL=STYLESHEET TYPE="text/css"
HREF="style.css">
donde style.css es el fichero que contiene la hoja de estilo que se emplear para
visualizar el documento HTML.
El texto de la hoja de estilo ha de escribirse de acuerdo a la siguiente sintaxis:

ETIQUETA {
propiedad1: valor1;
propiedad2: valor2;
}

ETIQUETA1, ETIQUETA2 {
propiedad: valor;
}

.CLASE {
propiedad: valor;
}
donde las etiquetas y las clases son las que se utilican en los documentos HTML,
mientras que las propiedades aplicables a cada elemento y los valores que
pueden tomar dichas propiedades estn definidas en un estndar emitido por el
W3C.
Por ejemplo, el cuerpo de esta pgina se visualiza con el siguiente estilo para
dejar mrgenes y mostrar una imagen de fondo:

BODY
{
background-image: url(http://elvex.ugr.es/decsai/internet/image/internet.jpg);
color: #000000;
margin-left: 10%;
margin-right: 10%;
margin-top: 5%;
margin-bottom: 5%;
}
De hecho, el ejemplo que acabamos de ver se muestra utilizando un estilo
especial (definido con la clase example) de la siguiente forma:
En el documento HTML:
<table class="example">
...
En la hoja de estilo CSS:
.example
{
background-color: #e0e0e0;
}
Adems, todo el texto de este documento aparece justificado a ambos mrgenes
porque en la hoja de estilo hemos escrito lo siguiente:

P, BLOCKQUOTE, LI, TD
{
text-align: justify;
}
Por otro lado, tambin hemos modificado la forma en la que se visualizan los
enlaces en una pgina (y cmo cambian al pasar el cursor del ratn sobre ellos, al
menos si utilizamos como navegador el Internet Explorer de Microsoft):
A
{
text-decoration: none;
}

A:hover
{
color: #009999;
}
Jugando un poco con las posibilidades que nos ofrecen las hojas de estilo CSS se
puede conseguir que nuestras pginas HTML estndar tengan buen aspecto sin
tener que plagarlas de etiquetas auxiliares como FONT, las cuales lo nico que
consiguen es que el texto de nuestro documento HTML sea menos legible y ms
difcil de mantener.
Informacin adicional
W3C: World Wide Web Consortium
Index DOT (CSS & HTML)
Concurrencia
Programacin concurrente

Programacin concurrente
Se entiende por programacin concurrente el conjunto de tcnicas y notaciones
que sirven para expresar el paralelismo potencial en los programas, as como
resolver problemas de comunicacin y sincronizacin.
Un proceso es un programa en ejecucin con un estado asociado. Las distintas
aplicaciones que se ejecutan en un sistema operativo multitarea son procesos
independientes. Cada una de ellas tiene asociado un contexto (prioridad, estado
del procesador, lista de interrupciones y seales que admite, pginas de memoria
que ocupa, etc.).
Una aplicacin concurrente est formada por un conjunto de procesos
concurrentes. En ella existen distintas hebras de control independientes, vas
simultneas de ejecucin. Dichas hebras de control pueden ser procesos
independientes en el sistema operativo o hebras dentro de un proceso. Una
aplicacin multihebra est constituida por distintas hebras que comparten el
espacio de un proceso en el sistema operativo.
Consideraciones
El diseo de aplicaciones concurrentes es ms complejo que el de
aplicaciones secuenciales, ya que hemos de descomponer el
programa en un conjunto de tareas ms o menos independientes con
el fin de aprovechar el paralelismo que pueda existir. Si no existe ese
paralelismo potencial, no tiene sentido que intentemos descomponer
nuestra aplicacin en tareas independientes.
La implementacin de aplicaciones concurrentes es tambin ms
compleja que la de aplicaciones secuenciales convencionales porque
hemos de garantizar la coordinacin de las distintas hebras o
procesos con los mecanismos de comunicacin adecuados, adems
de velar por la integridad de los datos con los que stas trabajan
simultneamente (para lo cual hemos de sincronizar el acceso a los
mismos).
La depuracin de las aplicaciones concurrentes es extremadamente
difcil, dado que la ejecucin de los distintos procesos/hebras se
realiza de forma independiente y las operaciones que realizan se
pueden entrelazar de cualquier forma en funcin de cmo les asigne
la CPU el sistema operativo.
Cada hebra/proceso supone una carga adicional para el sistema
(p.ej. los cambios de contexto son costosos, especialmente en el
caso de los procesos). Hay que tener en cuenta la eficiencia de la
implementacin resultante, que puede medirse en funcin del tiempo
de respuesta del sistema o de la cantidad de trabajo que realiza por
unidad de tiempo [throughput].
Por qu usar hebras y procesos?
El uso de paralelismo (mltiples procesos y hebras, en particular) proporciona una
serie de ventajas frente a las limitaciones de los sistemas monotarea.
Ejemplo
Supongamos que nuestra aplicacin tiene que ocuparse de la
realizacin de copias de seguridad de los datos con los que
trabaja. Con una nica hebra tendramos que programar las
copias de seguridad fuera del horario habitual de trabajo (y
si tiene que funcionar las 24 horas del da?). Con varias
hebras, podemos aprovechar los perodos de inactividad del
sistema.
El diseo correcto de una aplicacin concurrente puede permitir que un programa
complete una mayor cantidad de trabajo en el mismo perodo de tiempo (como
suceda en el ejemplo antes mencionado) pero tambin sirve para otros
menesteres ms mundanos, desde la creacin de interfaces que respondan mejor
a las rdenes del usuario hasta la creacin de aplicaciones que den servicio a
varios clientes (como puede ser cualquier aplicacin web a la que varios usuarios
pueden acceder simultneamente).
En la interfaz de usuario
Cuando una aplicacin tiene que realizar alguna tarea larga, su interfaz debera
seguir respondiendo a las rdenes que el usuario efecte.
Hebras o procesos independientes pueden encargarse de realizar las operaciones
costosas mientras que la hebra principal de la aplicacin sigue gestionando los
eventos procedientes de la interfaz de usuario.
Establecimiento de prioridades
Como es lgico, se le asigna mayor prioridad a las tareas
ms importantes (vg: las que requieran una respuesta ms
rpida).
Aprovechamiento de los recursos del sistema
Cuando se utiliza una sola hebra, el programa debe detener completamente la
ejecucin mientras espera a que se realice cada tarea. La CPU permanece
ocupada completamente (o inactiva) hasta que el proceso actual termine. Si se
utilizan varias hebras, el sistema puede usarse para realizar varias tareas
simultneamente (vg: reproduccin de MP3s en background).
Paralelismo real
En un sistema multiprocesador, si la aplicacin se descompone en
varias hebras, el sistema operativo podr asignar cada una a una
de las CPUs del sistema.


Modularizacin: Paralelismo implcito
En muchas ocasiones, un programa puede disearse como varios procesos
paralelos que funcionen de forma independiente. La descomposicin de una
aplicacin en varias hebras puede resultar muy beneficiosa:
Se puede simplificar la implementacin de un sistema si una
secuencia compleja de operaciones se puede descomponer en una
serie de tareas independientes que se ejecuten concurrentemente.
Tareas independientes se implementan por separado (menor
acoplamiento entre las distintas partes del sistema)
Un diseo modular facilita la incorporacin de futuras ampliaciones
en nuestras aplicaciones.
IMPORTANTE
El objetivo principal del uso de paralelismo es mejorar el
rendimiento del sistema. El diseador/programador deber
decidir hasta qu punto debe utilizarse en cada momento.
En cualquier caso, el uso de paralelismo, y de hebras en particular, es ms comn
de lo que podra pensarse en un principio (y menos de lo que debera).
Procesos

Ejecucin de procesos
La clase System.Diagnostics.Process permite crear y monitorizar procesos
(accediendo a la informacin que se visualiza en el Administrador de Tareas de
Windows).
El mtodo Process.Start() equivale a la llamada ShellExecute del API de Windows
(Win32) y es el que deberemos utilizar para lanzar un proceso. Los parmetros del
proceso se especifican mediante un objeto de la clase ProcessStartInfo. Al
encapsular una llamada al shell de Windows, el mtodo Start tambin podemos
usarlo para abrir un fichero de cualquier tipo de los que tengan acciones asociadas
en el registro de Windows.
Ejecucin de procesos

Probar las distintas formas de lanzar un proceso escribiendo la
respuesta al evento Click de cada botn de la ventana que se muestra
en la imagen de encima:
Lanzar proceso:
System.Diagnostics.Process.Start("iexplore.exe");
Lanzar proceso con parmetros:
string proceso = "iexplore.exe";
string args = "http://elvex.ugr.es/decsai/Csharp/";

System.Diagnostics.Process.Start("iexplore.exe", args);
Lanzar un proceso utilizando ProcessStartInfo:
using System.Diagnostics;
...
ProcessStartInfo info = new ProcessStartInfo();

info.FileName = "iexplore.exe";
info.Arguments = "http://elvex.ugr.es/decsai/CSharp/";
info.WindowStyle =
System.Diagnostics.ProcessWindowStyle.Maximized;

Process.Start(info);
Lanzar el proceso adecuado para un fichero cualquiera:
OpenFileDialog openFileDialog = new OpenFileDialog();

openFileDialog.InitialDirectory = "c:\\" ;
openFileDialog.Filter = "txt files (*.txt)|*.txt|All files (*.*)|*.*";
openFileDialog.FilterIndex = 2 ;
openFileDialog.RestoreDirectory = true ; // Vuelve al directorio actual

if (openFileDialog.ShowDialog() == DialogResult.OK) {
System.Diagnostics.Process.Start(openFileDialog.FileName);
}
Abrir una URL:
System.Diagnostics.Process.Start("http://elvex.ugr.es/decsai/Csharp/");
Uso de los verbos del shell:
using System.Diagnostics;
...
ProcessStartInfo info = new ProcessStartInfo();

info.FileName = "Ade.jpg";
info.WorkingDirectory = "f://";
info.Verb = "edit"; // vs. "open" || "print"

Process.Start(info);

// Verbos comunes
// --------------
// open Abre un ejecutable, documento o carpeta
// edit Edita un documento
// print Imprime un documento
// explore Explora una carpeta
// find Inicia una bsqueda en el directorio especificado
Descargar cdigo fuente (Visual Studio 2003)
Descargar cdigo fuente (Visual Studio 2005)
RECOMENDACIN: Cuando se utiliza el mtodo Process.Start, tambin se
debera llamar siempre al mtodo Process.Close para liberar la memoria asociada
al objeto de tipo Process.
Finalizacin de procesos
En los ejemplos anteriores, si quisiramos que nuestra aplicacin detuviese su
ejecucin hasta que el proceso lanzado finalizase su ejecucin, slo tendramos
que llamar al mtodo WaitForExit:
Process proceso = Process.Start(...);

proceso.WaitForExit();
proceso.Close();
La espera podra hacerse por un perodo de tiempo limitado si utilizamos un
parmetro en la llamada al mtodo WaitForExit:
Ejecucin invisible de comandos MS-DOS
Process proceso = new Process()

string salida = Application.StartupPath + "/output.txt";
string path = System.Environment.GetFolderPath
(Environment.SpecialFolder.System);

proceso.StartInfo.FileName = "cmd.exe";
proceso.StartInfo.Arguments =
"/C dir \""+path+"\" >> \"" + salida +"\" && exit";

proceso.StartInfo.WindowStyle =
ProcessWindowStyle.Hidden;
proceso.StartInfo.CreateNoWindow = true;
proceso.Start()

proceso.WaitForExit(1000); // 1 segundo de timeout

if (!proceso.HasExited()) {
proceso.Kill(); // Finalizamos el proceso
} else {
...
}
Adems, tambin podemos detectar cundo finaliza un proceso que hayamos
lanzado si empleamos el manejador de eventos Exited de la clase Process. De
esta forma, no tenemos por qu bloquear la ejecucin de nuestra aplicacin
mientras esperamos la terminacin de un proceso:
Evento asociado a la terminacin de un proceso
Process proceso = new Process()
...
proceso.Exited += new EventHandler(ExitHandler);
proceso.Start()
...
El mtodo ExitHandler se encarga de realizar las tareas
necesarias al finalizar la ejecucin del proceso, como por
ejemplo:


public void ExitHandler(object sender, EventArgs e)
{
Process proceso = (Process) sender;

MessageBox.Show ( "PID: " + proceso.Id
+ System.Environment.NewLine
+ "Hora: " + proceso.ExitTime
+ System.Environment.NewLine
+ "Cdigo: " + proceso.ExitCode );

proces.Close();
}
La clase Process tambin nos permite provocar la terminacin de un proceso. sta
se puede realizar explcitamente utilizando los mtodos Kill() y
CloseMainWindow(), siendo este ltimo el mtodo recomendado, pues equivale a
que el usuario de la aplicacin cierre sta de la forma usual. Es decir,
CloseMainWindow solicita cerrar la aplicacin como si el propio usuario cerrase la
ventana principal de la aplicacin (lo cual puede provocar la aparicin de mensajes
de la aplicacin), mientras que Kill finaliza el proceso "por las bravas", pudiendo
ocasionar la prdida de datos que no hayan sido previamente guardados.
En la siguiente seccin veremos cmo se puede forzar la finalizacin de un
proceso y tambin aprenderemos a acceder a la informacin relativa a los
procesos que se estn ejecutando en una mquina Windows (la misma
informacin que figura en el Administrador de Tareas de Windows).








Monitorizacin de procesos
Monitor de procesos

Creamos una aplicacin Windows a la que denominamos
WindowMonitor, cuyo formulario principal (Text="Procesos
del sistema") contiene los siguientes componentes:
ListBox listProcesses (ScrollAlwaysVisible=true).
TextBox textInfo (BackColor=Info; Multiline=true).
Button buttonClose (Text="WindowClose";
ForeColor=ForestGreen).
Button buttonKill (Text="Kill"; ForeColor=DarkRed).
Label labelWarning (TextAlign=MiddleCenter;
Text="OJO! Kill puede ocasionar la prdida de datos
al forzar la terminacin de un proceso...").
Al cargar el formulario, rellenamos la lista de procesos del
sistema (evento FormLoad):
Process[] procesos = Process.GetProcesses();

listProcesses.Items.AddRange(procesos);
Cuando el usuario selecciona un proceso concreto,
mostramos informacin detallada acerca del proceso
seleccionado (evento SelectedIndexChanged del ListBox):
Process selected = (Process) listProcesses.SelectedItem;

textInfo.Clear();

if (selected!=null)
textInfo.Lines = new string[] {
"Proceso: " + selected.ProcessName,
"PID: " + selected.Id,
// "Mquina: " + selected.MachineName,
"Prioridad: " + selected.PriorityClass,
"Uso de memoria: " + selected.WorkingSet + " bytes",
// selected.PagedMemorySize
// selected.VirtualMemorySize
"Tiempo de CPU: " + selected.TotalProcessorTime,
"Hora de inicio: " + selected.StartTime,
"Mdulo principal: " + selected.MainModule.FileName
// selected.MainWindowTitle
};
Finalmente, programamos la respuesta de la aplicacin
correspondiente a los dos botones de nuestra interfaz:
// Fuerza la finalizacin del proceso
// (puede provocar la prdida de datos)

private void buttonKill_Click(object sender, System.EventArgs
e)
{
Process selected = (Process) listProcesses.SelectedItem;

if (selected!=null)
selected.Kill();
}

// Solicita la terminacin de un proceso
// (como si el usuario solicitase cerrar la aplicacin)

private void buttonClose_Click(object sender,
System.EventArgs e)
{
Process selected = (Process) listProcesses.SelectedItem;

if (selected!=null) {

if (selected.Id==Process.GetCurrentProcess().Id)
MessageBox.Show("Ha decidido finalizar la aplicacin
actual");

selected.CloseMainWindow();
}
}
Descargar cdigo fuente (Visual Studio 2003)
Descargar cdigo fuente (Visual Studio 2005)
NOTA: En la barra de herramientas [toolbox], dentro del apartado "Components",
podemos encontrar un componente denominado Process que nos permite trabajar
con procesos de forma visual.
Operaciones de E/S
En ocasiones nos interesa que la entrada de un proceso provenga directamente
de la salida de otro proceso. Enviar la salida a un fichero y despus leer el fichero
no siempre es la mejor opcin, ya que el uso de "pipes" resulta mucho ms
eficiente para conectar distintos procesos. La clase Process nos permite
redireccionar los canales de E/S estndar (StdIn, StdOut y StdErr). Slo tenemos
que fijar a "true" las propiedades RedirectStandardInput, RedirectStandardOutput
y RedirectStandardError, tras lo cual podemos acceder a las propiedades
StandardInput, StandardOutput y StandardError del proceso (objetos de tipo
StreamReader y StreamWriter).
Una observacin: Process.Start utiliza por defecto la funcin ShellExecute del API
Win32. Cuando utilicemos redireccionamientos, la propiedad
ProcessStartInfo.UseShellExecute debe estar puesta a "false" antes de invocar al
mtodo Process.Start.
Process proceso = new Process();

proceso.StartInfo.FileName = "cmd.exe";
proceso.StartInfo.UseShellExecute = false;
proceso.StartInfo.CreateNoWindow = true;
proceso.StartInfo.RedirectStandardInput = true;
proceso.StartInfo.RedirectStandardOutput = true;
proceso.StartInfo.RedirectStandardError = true;

proceso.Start()

StreamWriter sIn = proceso.StandardInput;
sIn.AutoFlush = true;

StreamReader sOut = proceso.StandardOutput;
StreamReader sErr = proceso.StandardError;

sIn.Write("dir c:\" + System.Environment.NewLine);
sIn.Write("exit" + System.Environment.NewLine);

string output = sOut.ReadToEnd();

sIn.Close();
sOut.Close();
sErr.Close();
proceso.Close();

MessageBox.Show(output);
Para los programas que no utilizan StdIn se puede utilizar el mtodo SendKeys
para simular la pulsacin de teclas:
Acceso al Bloc de Notas con SendKeys
Process proceso = new Process();

proceso.StartInfo.FileName = "notepad";
proceso.StartInfo.WindowStyle =
ProcessWindowStyle.Normal;
proceso.EnableRaisingEvents = true;
proceso.Start();

proceso.WaitForInputIdle(1000); // 1 segundo de timeout

if (proceso.Responding)
System.Windows.Forms.SendKeys.SendWait
("Texto enviado con System.Windows.Forms.SendKeys");
Descargar cdigo fuente (Visual Studio 2003)
Descargar cdigo fuente (Visual Studio 2005)
Cualquier combinacin de teclas se puede enviar con SendKeys, lo que permite
hacer prcticamente cualquier cosa con una aplicacin Windows (siempre que nos
aseguremos de que es la aplicacin activa, la que tiene el foco).
No se pueden utilizar SendKeys hasta que no se haya creado y se visualice la
ventana principal de la aplicacin, por lo que en el ejemplo de arriba se emple el
mtodo Process.WaitForInputIdle, que espera hasta que la aplicacin pueda
aceptar entradas por parte del usuario y cuyo funcionamiento es anlogo a
Process.WaitForExit.
Acceso a recursos nativos de Windows
Con SendKeys se puede simular la pulsacin de cualquier
combinacin de teclas. No obstante, SendKeys se limita a
enviar eventos de pulsacin de teclas a la aplicacin que est
activa en cada momento. Y no existe ninguna funcin
estndar en .NET que nos permita establecer la aplicacin
activa, pero s en el API nativo de Windows.
El API de Windows incluye una gran cantidad de funciones
(no mtodos), del orden de miles. En el caso que nos ocupa,
podemos recurrir a las funciones FindWindow y
SetForegroundWindow del API Win32 para establecer la
aplicacin activa. Como estas funciones no forman parte de
la biblioteca de clases .NET, tenemos que acceder a ellas
usando los servicios de interoperabilidad con COM. Estos
servicios son los que nos permiten acceder a recursos
nativos del sistema operativo y se pueden encontrar en el
espacio de nombres System.Runtime.InteropServices.
Para poder usar la funcin SetForegroundWindow, por
ejemplo, tendremos que escribir algo parecido a lo siguiente:
using System.Runtime.InteropServices;
...
[DllImport("User32",EntryPoint="SetForegroundWindow")]
private static extern bool
SetForegroundWindow(System.IntPtr hWnd);
Una vez hecho esto, ya podemos acceder a la funcin
SetForegroundWindow como si de un mtodo ms se tratase:
Process proceso = ...

if (proceso.Responding) {
SetForegroundWindow(proceso.MainWindowHandle);
SendKeys.SendWait("Sorpresa!!!");
SetForegroundWindow(this.Handle);
}
Y ya podemos hacer prcticamente cualquier cosa con una
aplicacin Windows, controlndola desde nuestros propios
programas.
Descargar cdigo fuente (Visual Studio 2003)
Descargar cdigo fuente (Visual Studio 2005)
Hebras

Todas las aplicaciones .NET son en realidad aplicaciones multihebra.
La clase Thread
La clase System.Thread representa una hebra del sistema. Las hebras se lanzan
cuando se invoca un delegado sin argumentos que sirve de punto de entrada a la
hebra. Al no tener argumentos este delegado, el estado inicial de la hebra hay que
establecerlo previamente en el objeto que aloje el delegado.
// Creacin del objeto que aloja nuestra hebra

Tarea tarea = new Tarea();

// Inicializacin de parmetros

tarea.parameter= 1234;

// Establecimiento del punto de entrada de la hebra (delegado)

ThreadStart threadStart = new ThreadStart(tarea.Run);

// Creacin de la hebra

Thread thread = new Thread(threadStart);

// Ejecucin de la hebra

thread.Start();

// ...

// Espera a la finalizacin de la hebra

thread.Join();
Clculo de PI
Aunque no sea algo especialmente til en el trabajo cotidiano de
un programador, supongamos que, por algn extrao motivo,
hemos de calcular con precisin el valor del nmero PI y no nos
vale la constante definida en System.Math.PI, que slo incluye
veinte dgitos de precisin. No obstante, el proceso que
seguiremos para desarrollar una aplicacin multihebra ser el
mismo que el que seguiramos en cualquier otro caso real (p.ej.
realizacin de copias de seguridad mientras nuestra aplicacin
sigue funcionando, implementacin de servidores de aplicaciones,
acceso a recursos a travs de la red sin detener la ejecucin de
nuestra aplicacin, etc.). Recordemos que, en general, la
utilizacin de hebras es siempre til cuando hemos de realizar
cualquier tarea que requiera su tiempo...
Comenzamos creando una aplicacin Windows con un formulario
principal como el siguiente:

Formulario FormPI (Text="Clculo de PI").
NumericUpDown editDigits (Align=Right; Maximum=10000;
Value=1000).
Button buttonCalc (Text="Calcular").
TextBox textPI (Text=""; Multiline=true).
ProgressBar progress.
Para calcular PI hasta la precisin deseada slo tenemos que
implementar un bucle y utilizar una funcin que nos va devolviendo
los dgitos de PI de 9 en 9 (Pi.cs):

private void CalcularPi (int precision)
{
int digitos, calculados, nuevos;
string ds;
StringBuilder pi = new StringBuilder("3.", precision + 2);

ShowProgress(pi.ToString(), 0, precision);
calculados = 0;

while (calculados<precision) {
digitos = NineDigitsOfPi.StartingAt(calculados+1);
nuevos = Math.Min(precision - calculados, 9);
ds = string.Format("{0:D9}", digitos);
pi.Append(ds.Substring(0, nuevos));

ShowProgress(pi.ToString(), calculados+nuevos,
precision);
calculados += 9;
}
}
Los avances en el clculo de PI los mostramos peridicamente en
el TextBox y, para que el usuario tenga una idea de cunto queda,
tambin en la barra de progreso de nuestro formulario:

void ShowProgress (string pi, int actual, int total)
{
textPI.Text = pi;
progress.Maximum = total;
progress.Value = actual;
}
La forma ingenua de implementar nuestro aplicacin sera hacer
que, al pulsar el botn, se calculase el valor de PI con la precisin
solicitada. No obstante, aparte de que nuestra aplicacin se queda
bloqueada mientras se realiza el clculo, al cambiar a otra
aplicacin y luego volver a la nuestra, nos podramos encontrar
una desagradable sorpresa (muy comn, por otro lado, en
demasiadas aplicaciones comerciales):

La imagen anterior se debe a que nuestra aplicacin Windows,
ocupada en realizar el clculo de PI como respuesta a la pulsacin
del botn, no atiende ningn otro evento de los que se producen
(como, por ejemplo, el que le pide refrescar su imagen en pantalla:
el evento Paint del formulario). La solucin a nuestro problema
pasa, pues, por crear una hebra independiente que se ejecute en
paralelo y no interfiera en el comportamiento habitual del interfaz
de nuestra aplicacin:



private void buttonCalc_Click(object sender, System.EventArgs e)
{
ThreadStart piThreadStart = new ThreadStart(PiStart);
Thread piThread = new Thread(piThreadStart);

piThread.Start();
}

private void PiStart ()
{
CalcularPi ( (int) editDigits.Value );
}
Con esta pequea modificacin conseguimos que el interfaz
grfico de nuestra aplicacin funcione correctamente:

Ejecucin asncrona de delegados
Para no tener que crear una funcin sin parmetros especialmente escrita para
poder lanzar la hebra (y tener que implementar en ella la inicializacin de los
parmetros reales de la hebra), podemos utilizar delegados:
// Declaracin

delegate void PiDelegate (int precision);

// Creacin

PiDelegate delegado = new PiDelegate(CalcularPi);

// Uso

delegado((int)editDigits.Value);
El delegado declarado deriva de la clase MultiCastDelegate, que implementa tres
funciones:
class PiDelegate : MulticastDelegate
{
public void Invoke(int precision);
public void BeginInvoke(int precision, AsyncCallback
callback, object state);
public void EndInvoke(IAsyncResult result);
}
Cuando un delegado se usa como si fuese una funcin, en realidad se est
llamando al mtodo sncrono Invoke, que es el que se encarga de llamar a la
funcin concreta con la que se instancia el delegado (CalcularPi en este caso).
Los otros dos mtodos del delegado, BeginInvoke y EndInvoke, son los que
permiten invocar al delegado de forma asncrona. De forma que podemos calcular
PI en una hebra independiente de la siguiente forma:
Clculo de PI con delegados
delegate void PiDelegate (int precision);

private void buttonCalc_Click(object sender,
System.EventArgs e)
{
PiDelegate delegado = new PiDelegate(CalcularPi);

delegado.BeginInvoke((int)editDigits.Value, null, null);
}
En principio, todo parece ir bien, aunque an nos quedan algunos detalles por
pulir...
Hebras en aplicaciones Windows
Aunque tuvimos suerte en el ejemplo anterior (posiblemente por la implementacin
de nuestro sistema operativo), en realidad violamos una regla bsica de Windows:
manipular una ventana nicamente desde la hebra que la crea. En general, como
veremos en la siguiente seccin del curso, no es correcto acceder un recurso
compartido desde distintas hebras si no utilizamos los mecanismos de proteccin
adecuados.
La documentacin de la plataforma .NET lo deja claro. Slo hay cuatro mtodos
de un control que se pueden llamar de forma segura desde cualquier hebra:
Invoke, BeginInvoke, EndInvoke y CreateGraphics). Cualquier otro mtodo ha de
llamarse a travs de uno de los anteriores, como, por ejemplo, los que modifican
las distintas propiedades de los controles de nuestra ventana. En realidad, slo
tenemos que crear un nuevo delegado que se ejecute en la hebra principal
correspondiente a la interfaz grfica.
Si nos preocupase que la hebra que calcula el valor de PI
quedase bloqueada (como antes la hebra de la interfaz
grfica), tendramos que utilizar los mtodos asncronos
BeginInvoke y EndInvoke. En el ejemplo que nos ocupa nos
basta con el mtodo sncrono Invoke:

public object Invoke(Delegate method);
public object Invoke(Delegate method, object[] args);
La segunda variante del mtodo es la que utilizaremos en
nuestra hebra para llamar a ShowProgress especificando sus
parmetros.
Creamos un delegado para la funcin que deseamos
llamar:

delegate void ShowProgressDelegate
(string pi, int actual, int total);
Sustituimos las llamadas a ShowProgress por:

ShowProgressDelegate showProgress =
new ShowProgressDelegate(ShowProgress);
...
this.Invoke(showProgress, new object[] {
pi.ToString(), calculados, precision});
El uso de Invoke nos garantiza que accedemos de forma segura a los controles de
nuestra ventana en una aplicacin multihebra. La hebra principal crea una hebra
encargada de realizar una tarea computacionalmente costosa [la hebra
trabajadora] y sta le pasa el control a la hebra principal cada vez que necesita
actuar sobre la interfaz. La siguiente figura ilustra cmo funciona nuestra
aplicacin en tiempo de ejecucin:

Tener que llamar a Invoke cada vez que queremos garantizar el correcto
funcionamiento de nuestra aplicacin multihebra es realmente incmodo y,
adems, resulta bastante fcil que se nos olvide hacerlo en alguna ocasin. Por
tanto, no es mala idea que sea la propia funcin a la que llamamos la que se
encargue de asegurar su correcta ejecucin en una aplicacin multihebra:
En el ejemplo anterior, si implementamos la funcin
ShowProgress de la siguiente forma, podemos llamar a la
funcin de la forma tradicional sin preocuparnos de la hebra
desde la que utilizamos nuestra funcin:

void ShowProgress(string pi, int actual, int total)
{
if ( textPI.InvokeRequired == false ) {
// Hebra correcta
textPI.Text = pi;
progress.Maximum = total;
progress.Value = actual;
} else {
// Llamar a la funcin de forma asncrona
ShowProgressDelegate showProgress = new
ShowProgressDelegate(ShowProgress);
this.Invoke(showProgress, new object[] { pi, actual, total});
}
}
Si tenemos en cuenta que la funcin ShowProgress no
devuelve ningn valor (esto es, en realidad se trata de un
procedimiento), podemos sustituir la llamada sncrona Invoke
por una llamada asncrona con BeginInvoke:

BeginInvoke(showProgress, new object[] { pi, actual, total});
BeginInvoke es siempre preferible cuando la funcin no
devuelve ningn valor ya que evita que se puedan producir
bloqueos.
Hebras en aplicaciones web
Las peculiaridades de las interfaces web ocasionan la aparicin de problemas de
los cuales no tendramos que preocuparnos en otros contextos. ste es el caso
cuando nuestra aplicacin web debe realizar una tarea relativamente larga. Lo que
est claro es que no queda demasiado bien de cara al usuario dejar su ventana en
blanco de forma indefinida mientras nuestra aplicacin realiza los clculos que
sean necesarios.
Una solucin a este problema involucra la utilizacin de hebras. El problema de
realizar un clculo largo lo volvemos a descomponer en dos hebras:
La hebra principal se encargar de mostrarle al usuario el estado
actual de la aplicacin, estado que se refrescar en su navegador
automticamente gracias al uso de la cabecera Refresh, definida en
el estndar para las respuestas HTTP.
Una hebra auxiliar ser la encargada de ejecutar el cdigo
correspondiente a efectuar todos los clculos que sean necesarios
para satisfacer la solicitud del usuario.
Hebras en aplicaciones web
Una pgina ASP.NET lanza la tarea:
...
using System.Threading;

public class Payment : System.Web.UI.Page
{
protected Guid ID; // Identificador de la solicitud

private void Page_Load(object sender, System.EventArgs e)
{
if (Page.IsPostBack) {

// 1. Crear un ID para la solicitud

ID = Guid.NewGuid();

// 2. Lanzar la hebra

ThreadStart ts = new ThreadStart(RealizarTarea);
Thread thread = new Thread(ts);
thread.Start();

// 3. Redirigir a la pgina de resultados

Response.Redirect("Result.aspx?ID=" + ID.ToString());
}
}

private void RealizarTarea ()
{
// Tarea larga !!!
System.Threading.Thread.Sleep(new TimeSpan(0, 0, 0, 7,
0));
object resultado = new Random(0).Next(100);

Results.Add(ID, resultado);
}

...
}
Una clase auxiliar Results mantiene los resultados de las
distintas hebras para que la pgina de resultados pueda
acceder a ellos:
using System;
using System.Collections;

public sealed class Results
{
private static Hashtable results = new Hashtable();

public static object Get(Guid ID)
{
return results[ID];
}

public static void Add (Guid ID, object result)
{
results[ID] = result;
}

public static void Remove(Guid ID)
{
results.Remove(ID);
}

public static bool Contains(Guid ID)
{
return results.Contains(ID);
}
}
Finalmente, la pgina encargada de mostrar los resultados se
refresca automticamente hasta que la ejecucin de la hebra
auxiliar haya terminado y sus resultados estn disponibles:


public class Result : System.Web.UI.Page
{
protected System.Web.UI.WebControls.Label lblMessage;

private void Page_Load(object sender, System.EventArgs e)
{
Guid ID = new Guid(Page.Request.QueryString["ID"]);

if (Results.Contains(ID)) {
// La tarea ha terminado: Mostrar el resultado
lblMessage.Text = Results.Get(ID).ToString();
Results.Remove(ID);
} else {
// An no tenemos el resultado: Esperar otros 2 segundos
Response.AddHeader("Refresh", "2");
}
}
...
}
La solucin aqu propuesta es extremadamente til en la prctica. Imagine, por
ejemplo, una aplicacin de comercio electrnico que ha de contactar con un banco
para comprobar la validez de una tarjeta de crdito. No resulta demasiado difcil
imaginar la impresin del usuario final cuando la implementacin utiliza hebras y
cuando no lo hace.
Control de la ejecucin de una hebra
An nos faltaba por hacer una cosa ms con nuestra aplicacin Windows. Una vez
que el usuario ordena la ejecucin de una tarea computacionalmente costosa,
sera deseable que siempre mantuviese el control sobre lo que hace el ordenador
(un principio bsico en el diseo de interfaces de usuario).
Para que el usuario mantenga el control absoluto sobre la
ejecucin de nuestra aplicacin, podemos hacer que el botn
mediante el cual se inici el clculo de PI sirva tambin para
detenerlo (o crear una ventana de dilogo independiente en
la cual se incluya algn botn con la misma funcionalidad). Si
el usuario decide cancelar la operacin en curso, la hebra
que controla la interfaz de usuario debe comunicarle a la otra
hebra que detenga su ejecucin y, mientras sta no se
detenga, tenemos que deshabilitar el botn (con el fin de
evitar que, por error, se cree una nueva hebra en el intervalo
de tiempo que abarca desde que el usuario pulsa el botn de
cancelar hasta que la hebra realmente se detiene).
Comenzamos definiendo una variable de estado:

enum Estado { Inactivo, Calculando, Cancelando };

Estado estado = Estado.Inactivo;

Implementamos la respuesta de nuestro botn en
funcin del estado actual de la operacin:

private void buttonCalc_Click(object sender,
System.EventArgs e)
{
switch (estado) {

case Estado.Inactivo: // Comenzar el
clculo
estado = Estado.Calculando;
buttonCalc.Text = "Cancelar";
PiDelegate delegado = new
PiDelegate(CalcularPi);
delegado.BeginInvoke((int)editDigits.Value, null,
null);
break;

case Estado.Calculando: // Cancelar
operacin
estado = Estado.Cancelando;
buttonCalc.Enabled = false;
break;

case Estado.Cancelando: // No debera
suceder nunca...
Debug.Assert(false);
break;
}
}

Para que la hebra detenga su ejecucin, podemos
hacer que las hebras se comuniquen a travs de paso
de parmetros (por ejemplo, haciendo que la funcin
ShowProgress devuelva un valor a travs de un
parmetro adicional, que ser comprobado en cada
iteracin de la hebra para determinar si el usuario ha
pedido cancelar la operacin en curso). Tambin
podemos emplear datos compartidos por las hebras,
aunque, en ese caso, deberemos tener especial
cuidado con los problemas de sincronizacin y
posibles bloqueos que puedan ocurrir. Si utilizamos
esta ltima opcin, podemos escribir lo siguiente:

delegate void EndProgressDelegate ();


private void CalcularPi (int precision)
{
EndProgressDelegate endProgress = new
EndProgressDelegate(EndProgress);
...

while ( (estado!=Estado.Cancelando) && ...) {
...
}

this.Invoke(endProgress);
}

private void EndProgress ()
{
estado = Estado.Inactivo;
buttonCalc.Text = "Calcular";
buttonCalc.Enabled = true;
}

OJO! Cuando varias hebras acceden
simultneamente a algn recurso compartido es
necesario implementar algn tipo de mecanismo de
exclusin mutua: en el desarrollo de aplicaciones
multihebra siempre debemos garantizar la exclusin
mutua en el acceso a recursos compartidos. En el
caso anterior, la aplicacin funciona porque slo una
de las hebras modifica el valor de la variable
compartida y, adems, lo restablece slo despus de
que la otra hebra haya comprobado el valor de la
variable compartida y haya decidido finalizar su
ejecucin (EndProgress). En general, no obstante,
deberamos estudiar las posibles condiciones de
carrera y la posibilidad de bloqueos si empleamos
tcnicas de exclusin mutua...
Cdigo fuente del ejemplo (Visual Studio .NET 2003)
Cdigo fuente del ejemplo (Visual Studio .NET 2005)
Paralelismo real
Podemos aprovechar el paralelismo real de nuestra mquina para reducir el
tiempo de reloj necesario para realizar un clculo costoso si disponemos de un
multiprocesador, de un microprocesador de doble (o cudruple) ncleo o
simplemente tenemos un procesador SMT (Simultaneous Multithreading, lo que
Intel hace comercializa bajo el nombre HyperThreading).
Igual que antes, para que el usuario mantenga el control
absoluto sobre la ejecucin de nuestra aplicacin, hacemos
que el botn mediante el cual se inici el clculo de PI sirva
tambin para detenerlo:

// Variable de estado

enum Estado { Inactivo, Calculando, Cancelando };

Estado estado = Estado.Inactivo;


// Evento asociado al ratn

private void buttonCalc_Click(object sender,
System.EventArgs e)
{
switch (estado) {

case Estado.Inactivo: // Comenzar el
clculo
estado = Estado.Calculando; // con varias
hebras
buttonCalc.Text = "Cancelar";
textPI.Text = "";
setPrecision((int)editDigits.Value);
startThread();
startThread();
break;

case Estado.Calculando: // Cancelar
operacin
estado = Estado.Cancelando;
buttonCalc.Enabled = false;
break;
}
}
La actualizacin de la interfaz de usuario la hacemos de
forma similar a como la hacamos antes, si bien hemos
simplificado algo la signatura de los mtodos utilizados ya
que, ahora, la precisin deseada de pi ser una variable de
instancia de nuestra clase:
delegate void ShowProgressDelegate(int actual);
delegate void EndProgressDelegate();

private void ShowProgress (int completed)
{
Debug.Assert(textPI.InvokeRequired == false);

progress.Maximum = precision;
progress.Value = Math.Min(precision, 9*completed);
}


private void EndProgress ()
{
if (estado != Estado.Cancelando)
textPI.Text = Pi();
else
textPI.Text = "Operacin cancelada";

estado = Estado.Inactivo;
buttonCalc.Text = "Calcular";
buttonCalc.Enabled = true;
}

Una vez ms, utilizaremos un delegado para nuestras hebras:

delegate void PiDelegate();

A continuacin, implementamos las operaciones que nos
permitirn lanzar la ejecucin de las hebras (startThread) y
terminar su ejecucin de manera ordenada (endThread).
Usaremos, de forma segura, un contador auxiliar (hebras)
para que slo se actualice la interfaz de usuario cuando
termine la ejecucin de la ltima hebra:

int hebras = 0;

private void startThread()
{
PiDelegate delegado = new PiDelegate(CalcularPi);

delegado.BeginInvoke(null, null);

lock (this) {
hebras++;
}
}

private void endThread ()
{
lock (this) {
hebras--;
}

if (hebras==0) {
EndProgressDelegate endProgress = new
EndProgressDelegate(EndProgress);
this.Invoke(endProgress);
}
}
El clculo del valor exacto de pi lo iremos haciendo por
segmentos independientes y slo reconstruiremos su valor al
terminar (para evitar posibles cuellos de botella que se
produciran si continuamente tuvisemos que estar
sincronizando la ejecucin de las distintas hebras):
private int precision;
private int[] segments;
private int current;
private void setPrecision (int precision)
{
this.precision = precision;
this.current = 0;
this.segments = new int[precision/9+1];
}
A partir de esos "segmentos" (conjuntos de dgitos que
estarn incluidos en la parte decimal del nmero pi), la
reconstruccin de pi ser inmediata:
private string Pi ()
{
StringBuilder pi = new StringBuilder("3.", precision + 2);

for (int i=0; i<segments.Length; i++) {
string ds = string.Format("{0:D9}", segments[i]);
int nuevos = Math.Min(precision - 9*i, 9);
pi.Append(ds.Substring(0, nuevos));
}

return pi.ToString();
}

Para ir repartiendo el trabajo entre las distintas hebras,
definimos un mtodo auxiliar que le indica a cada hebra qu
segmento del nmero pi ha de calcular a continuacin:
private int nextSegment()
{
int segment = current;

lock (this) {
current++;
}

return segment;
}
Por ltimo, ya slo nos queda implementar el cuerpo de las
hebras, que se encargar de ir calculando el valor exacto de
pi por segmentos:
private void CalcularPi ()
{
int segment;

ShowProgressDelegate showProgress = new
ShowProgressDelegate(ShowProgress);

this.Invoke(showProgress, new object[] { 0 });
segment = nextSegment();

while ((estado!=Estado.Cancelando) &&
(segment<segments.Length)) {

segments[segment] =
NineDigitsOfPi.StartingAt(9*segment+1);

this.Invoke(showProgress, new object[] { segment
});
segment = nextSegment();
}

endThread();
}
Con estas modificaciones, podemos reducir prcticamente un
50% el tiempo de ejecucin necesario para obtener el valor
de PI con la precisin deseada si disponemos de un
procesador de doble ncleo o de un multiprocesador con dos
procesadores (el ahorro sera mayor incluso si disponemos
de mayor paralelismo en el hardware y lanzamos ms hebras
en paralelo).
Cdigo fuente de la aplicacin multihebra para procesadores SMT y
multiprocesadores
Referencias
Shawn Cicoria: Proper Threading in Winforms .NET. CodeProject,
May 2003.
Chris Sells: Safe, Simple Multithreading in Windows Forms, Part 1.
MSDN, June 28, 2002.
Chris Sells: Safe, Simple Multithreading in Windows Forms, Part 2.
MSDN, September 2, 2002.
Chris Sells: Safe, Simple Multithreading in Windows Forms, Part 3.
MSDN, January 23, 2003.
David Carmona: Programming the Thread Pool in the .NET
Framework. MSDN, June 2002.


Sincronizacin

El cdigo ejecutado por una hebra debe tener en cuenta la posible existencia de
otras hebras que se ejecuten concurrentemente. Hay que tener cuidado para
evitar que dos hebras accedan a un recurso compartido al mismo tiempo (vg:
objeto o variable global). Adems, la ejecucin de una hebra puede depender del
resultado de las tareas que realicen otras hebras. En cualquier caso, las distintas
hebras de una aplicacin han de coordinar su ejecucin.
Nota
El modelo de sincronizacin utilizado en la plataforma .NET
es completamente anlogo al utilizado por el lenguaje de
programacin Java.
Uso de recursos compartidos
Cuando varias hebras comparten el uso de un recurso, pueden ocurrir situaciones
no deseadas si dos o ms hebras acceden (o intentan acceder) al mismo recurso
simultneamente.
Para evitar conflictos con otras hebras, puede que se necesite bloquear la
ejecucin de otras hebras al acceder a objetos o variables compartidas. En tal
caso, hay que tener cuidado de no bloquear innecesariamente la ejecucin de
otras hebras para no disminuir el rendimiento de la aplicacin.
En definitiva, las aplicaciones multihebra deben evitar el acceso concurrente a
recursos compartidos (p.ej. datos comunes), para lo cual pueden utilizar distintos
mecanismos de exclusin mutua:
Monitores
La clase System.Threading.Monitor proporciona un modelo de coordinacin similar
a las secciones crticas en Win32 (Enter/TryEnter/Exit), que garantizan la
exclusin mutua en el acceso a recursos compartidos:
Seccin crtica
// Entrada en la seccin crtica

Monitor.Enter(this);

try {

// Acciones que requiran un acceso coordinado

...

} finally {

// Salida de la seccin crtica

Monitor.Exit(this);
}
La misma clase, System.Threading.Monitor, tambin proporciona un mecanismo
de coordinacin similar a los semforos (Wait/Pulse/PulseAll). En este caso, una
hebra llama al mtodo Wait(obj) y queda a la espera de que otra hebra invoque al
mtodo Pulse(obj) (o PulseAll(obj)) para proseguir su ejecucin. Las
sincronizacin se puede realizar utilizando cualquier objeto gestionado por la
plataforma .NET.
Cerrojos (locks)
La sentencia lock de C# nos permite implementar una seccin crtica equivalente
al uso de Monitor.Enter y Monitor.Exit. Slo una de las sentencias lock puede
tener acceso a un objeto concreto:
lock (this) {

// Acciones que requiran un acceso coordinado
// ...
}
El acceso a objetos compartidos cuyo estado puede variar ha de hacerse siempre
de forma protegida:
public class Account
{
decimal balance;

public void Deposit(decimal amount)
{
lock (this) {
balance += amount;
}
}

public void Withdraw(decimal amount)
{
lock (this) {
balance -= amount;
}
}
}
Cajero automtico
Crear un simulador de cajero automtico introduciendo
llamadas a Thread.Sleep para provocar el entrelazado de
distintas operaciones sobre una cuenta. Comprobar las
diferencias que existen cuando usamos y cuando no usamos
lock.
Cajero automtico defectuoso: Cdigo fuente para Visual
Studio 2003
Cajero automtico defectuoso: Cdigo fuente para Visual
Studio 2005
Cuando queremos proteger el acceso a un miembro esttico de una clase, se
utiliza el objeto de tipo System.Type asociado a la clase que contiene el miembro
esttico al que queremos acceder:

public class Cache
{
public static void Add(object x) {
lock (typeof(Cache)) {
...
}
}
public static void Remove(object x) {
lock (typeof(Cache)) {
...
}
}
}
El uso de mecanismos de sincronizacin como cerrojos o monitores puede causar
problemas de inanicin (cuando un proceso queda bloqueado indefinidamente) e
interbloqueos (p.ej. cuando el orden de adquisicin de los cerrojos no siempre es
el mismo).
Lectores y escritores...
class RWLock
{
int lectores = 0;

public void AcquireExclusive()
{
lock (this) {
while (lectores!=0) Monitor.Wait(this);
lectores--;
}
}

public void AcquireShared()
{
lock (this) {
while (lectores<0) Monitor.Wait(this);
lectores++;
}
}

public void ReleaseExclusive()
{
lock (this) {
lectores = 0;
Monitor.PulseAll(this);
}
}

public void ReleaseShared()
{
lock (this) {
lectores--;
if (lectores==0) Monitor.Pulse(this);
}
}
}
Los mecanismos de sincronizacin anteriores utilizan memoria compartida y son
tiles en sistemas centralizados. En un sistema distribuido, la comunicacin y
sincronizacin entre procesos se habr de realizar mediante paso de mensajes, ya
sea mediante denominacin directa (haciendo referencia al proceso con el que
deseamos comunicarnos o canal a travs del cual realizaremos la comunicacin) o
a travs de nombres globales (buzones y puertos).
Otros mecanismos de sincronizacin
En el espacio de nombres System.Threading:
Eventos
Mutex
Timer
Interlocked
Operaciones asncronas
Generalmente, al llamar a un mtodo, la ejecucin de nuestra hebra queda
bloqueada hasta que el mtodo finaliza su ejecucin. Esto puede resultar
particularmente inoportuno cuando realizamos costosas operaciones de
entrada/salida. De forma anloga a la ejecucin asncrona de delegados, la
plataforma .NET nos permite realizar determinadas operaciones de forma
asncrona. Por ejemplo, nuestra aplicacin no tiene por qu detenerse cuando
realizamos operaciones de E/S.
E/S asncrona
1. Estructura de datos auxiliar:

public class StateObject
{
public byte[] bytes;
public int size;
public FileStream fs;
}
2. Inicio de la operacin de lectura asncrona:

...
StateObject state;
AsyncCallback callback;
FileStream fs;

callback = new AsyncCallback(CallbackFunction);
state = new StateObject();

fs = new FileStream ( "fichero.txt",
FileMode.Open,
FileAccess.Read,
FileShare.Read, 1, true);

state.fs = fs;
state.size = fs.Length;
state.bytes = new byte[state.size];

fs.BeginRead ( state.bytes, 0, state.size,
callback, state);
...
3. Procesamiento de los datos ledos (de forma
independiente):

public static void CallbackFunction
(IAsyncResult asyncResult)
{
StateObject state;
Stream stream;

state = (StateObject) asyncResult.AsyncState;
stream = state.fs;

int bytesRead = stream.EndRead(asyncResult);

if (bytesRead != state.size)
throw new Exception
("Slo se han ledo {0} bytes", bytesRead);

stream.Close();

// Operaciones con los datos (state.bytes)
...

// Notificacin de la finalizacin de la operacin
Monitor.Enter(WaitObject);
Monitor.Pulse(WaitObject);
Monitor.Exit(WaitObject);
}
4. Espera a la finalizacin de la operacin de lectura (desde
la hebra principal):

Monitor.Enter(WaitObject);
Monitor.Wait(WaitObject);
Monitor.Exit(WaitObject);
Informacin adicional
Vanse los mtodos BeginRead/EndRead y BeginWrite/EndWrite de la clase
System.IO.Stream.
Distribucin
Comunicacin entre procesos

Cuando en un sistema tenemos distintos procesos independientes ejecutndose,
necesitamos disponer de mecanismos que hagan posible la comunicacin entre
ellos. Se pueden utilizar distintos mecanismos de comunicacin entre procesos
[IPC: InterProcess Communication]:
El portapapeles de Windows [Windows Clipboard], que, como
almacn central de datos, nos permite que distintas aplicaciones
intercambien datos "copiando y pegando".
DDE [Dynamic Data Exchange], un protocolo que permite a las
aplicaciones intercambiar datos de una forma ms general que el
portapapeles.
OLE [Object Linking and Embedding], basado en COM, nos permite
manipular documentos compuestos de datos que pueden provenir de
distintas aplicaciones.
ActiveX es otra tecnologa basada en COM que permite la
comunicacin entre componentes independientemente del lenguaje
en el que estn implementados.
COM [Component Object Model] establece un estndar binario
mediante el cual se puede acceder a los servicios de un componente
utilizando uno o varios conjuntos de funciones relacionadas
(interfaces).
DCOM [Distributed COM] extiende el modelo de programacin COM
para que distintos componentes puedan comunicarse a travs de
una red.
.NET Remoting, el mecanismo que sustituye a DCOM en la
plataforma .NET.
NetBIOS [Network Basic Input/Output System], un protocolo para
redes de rea local de PCs creado por IBM en la poca del MS-DOS
que es similar a Novell Netware (IPX/SPX).
Sockets, para los cuales Windows incluye un API ms sofisticado
que el API tradicional que proviene del BSD UNIX [Berkeley Software
Distribution], el API que se ha utilizado habitualmente para transmitir
datos utilizando la familia de protocolos TCP/IP.
Pipes annimos [Anonymous pipes]: Permiten redireccionar la
entrada o salida estndar de un proceso (utilizando | en la lnea de
comandos, por ejemplo).
Pipes con nombre [Named pipes], similares a las colas FIFO de
POSIX [Portable Operating System Interface for Computer
Environments. IEEE 1003.1, 1988] pero no compatibles con ellas,
permiten a dos procesos intercambiar mensajes utilizando una
seccin de memoria compartida [pipe].
Mailslots, en Win32 y OS/2: Proporcionan un mecanismo de
comunicacin entre procesos unidireccional y no fiable que puede
ser til para difundir mensajes cortos a mltiples procesos en un
sistema distribuido.
WM_COPYDATA, un mensaje de Windows que se puede utilizar
para transmitir datos de un proceso a otro utilizando la infraestructura
del propio sistema operativo.
Ficheros mapeados en memoria: Permiten que dos procesos de una
misma mquina compartan un fichero que pueden manipular como si
fuese un bloque de memoria en su espacio de direcciones, si bien
para acceder a l correctamente debern utilizar algn mecanismo
de exclusin mutua (vg: semforos).
Semforos, eventos, Mutex y otras primitivas de sincronizacin que
permiten comunicar la ocurrencia de alguna accin o de algn
cambio de estado.
RPC [Remote Procedure Call]: Llamadas a procedimientos remotos.
Permiten realizar la comunicacin entre procesos como si se tratase
de llamadas a funciones. El RPC de Windows cumple con el
estndar OSF DCE [Open Software Foundation Distributed
Computing Environment], lo que permite la comunicacin entre
procesos que se ejecuten en sistemas operativos diferentes a
Windows.
CORBA [Common Object Request Broker Architecture], estndar del
OMG [Object Management Group] para el desarrollo de sistemas
distribuidos.
MPI [Message Passing Interface]: Estndar de paso de mensajes
muy utilizado en clusters y supercomputadores.
PVM [Parallel Virtual Machine]: Otro estndar de paso de mensajes
utilizado en multiprocesadores y multicomputadores.
Servicios web [Web services]: Conjunto de estndares que facilitan
el paso de mensajes en entornos heterogneos.
Como se puede ver, disponemos de una amplia variedad de mecanismos de
comunicacin entre procesos. Algunos de ellos facilitan la divisin del trabajo entre
distintos procesos de una mquina, otros permiten dividir el trabajo entre procesos
que se ejecutan en distintos ordenadores de un sistema distribuido.
Independientemente del mecanismo de comunicacin entre procesos que
decidamos emplear, nuestra aplicacin debera acceder a los recursos externos
de la misma forma que accede a recursos locales. Para encapsular el acceso a
recursos externos se suelen emplear proxies o gateways:

Adems, lo ideal es que intentemos que en nuestra aplicacin pueda modificar el
mecanismo de comunicacin entre procesos utilizado con el menor esfuerzo
posible, algo que no siempre facilitan los estndares existentes.
En cualquier caso, lo que siempre debemos tener en cuenta a la hora de
desarrollar sistemas distribuidos es que los mecanismos de comunicacin no
siempre son fiables (algunos paquetes se pierden), la comunicacin entre
procesos consume tiempo (la latencia no es cero), la capacidad del canal de
comunicacin no es infinita (el ancho de banda es un recurso muy valioso) y las
comunicaciones no siempre se realizan a travs de medios seguros.
NOTA: Usualmente, la aplicaciones que utilizan mecanismos de comunicacin
entre procesos suelen clasificarse como clientes o servidores, si bien pueden
desempear ambos roles en distintos momentos. El cliente es el que solicita
acceder a algn servicio proporcionado por otro proceso. El servidor es el que
atiende las peticiones de los clientes.
Sockets

La familia de protocolos TCP/IP
La familia de protocolos TCP/IP [Transmission Control Protocol / Internet Protocol]
define una serie de estndares que proporcionan la base necesaria para el
funcionamiento de Internet:

Funcionamiento
La familia TCP/IP define protocolos para las capas de red, transporte y aplicacin,
pero no para las capas de ms bajo nivel en el modelo OSI, de forma que los
protocolos TCP/IP pueden funcionar sobre distintos tipos de redes de
ordenadores:

El protocolo IP: La capa de red en Internet
La capa de red en el modelo OSI es la encargada de proporcionar servicios a la
capa de transporte y es responsable del enrutamiento de los paquetes desde su
origen hasta su destino. En el caso del protocolo IPv4, se utilizan direcciones de
32 bits para hacer referencia a los extremos de la comunicacin:

Algunas direcciones IP estn reservadas para propsitos especficos:

Los protocolos TCP y UDP: La capa de transporte en Internet
La capa de transporte se encarga de proporcionar servicios a las distintas
aplicaciones que hacen uso de la red. En el caso de la familia TCP/IP, el protocolo
TCP [Transmission Control Protocol, RFC 793] proporciona servicios orientados a
conexin, mientras que el protocolo UDP [User Datagram Protocol, RFC 768]
proporciona servicios no orientados a conexin.
Tanto TCP como UDP permiten multiplexar conexiones mediante "puertos", los
cuales sirven de punto de acceso a las distintas aplicaciones que se ejecutan en
una mquina con una direccin IP concreta. Algunos de estos puertos estn
asignados a los protocolos de las aplicaciones ms comunes de Internet:




Puerto TCP Protocolo Uso
21 FTP Transferencia de ficheros
23 Telnet Acceso remoto
25 SMTP Envo de correo electrnico
79 Finger Informacin acerca de usuarios
80 HTTP World Wide Web
110 POP3 Recepcin de correo electrnico
119 NNTP Grupos de noticial USENET
143 IMAP Lectura de correo electrnico
... ... ...
TCP se encarga de realizar el control de flujo de extremo a extremo (esto es,
garantizar la entrega ordenada de los paquetes y proceder a su retransmisin
cuando haga falta), mientras que UDP no garantiza ni la entrega ni la no existencia
de duplicados (a cambio de un overhead reducido).
El estndar que define las primitivas de servicio ofrecidas por el protocolo TCP
denomina "sockets" [enchufes] a los extremos de las conexiones TCP, de ah el
nombre que se le da al mecanismo de comunicacin entre procesos que utiliza la
familia de protocolos TCP/IP.
Uso de TCP
En la plataforma .NET, las clases que facilitan el uso de sockets se encuentran en
los espacios de nombres System.Net y System.Net.Sockets, por lo cual es
recomendable incluir las correspondientes sentencias using ...; al comienzo de los
ficheros de cdigo C# en los que utilicemos dichas clases.
Creacin de un servidor TCP
La creacin de un servidor TCP no resulta demasiado complicada. En primer
lugar, debemos establecer el socket a travs del cual el servidor aceptar
peticiones, para lo cual nos hace falta una direccin IP (la correspondiente a la
mquina en la que se ejecute el servidor) y un nmero de puerto TCP:
// DNS: nombre del host -> direccin IP
IPHostEntry ipHostInfo = Dns.Resolve(Dns.GetHostName());
IPAddress ipAddress = ipHostInfo.AddressList[0];

// Puerto
IPEndPoint localEndPoint = new IPEndPoint(ipAddress,
11000);
Una vez que tenemos el puerto a travs del cual aceptaremos conexiones,
creamos fsicamente el socket y lo configuramos para que pueda aceptar
conexiones:
Socket listener = new Socket ( AddressFamily.InterNetwork,
SocketType.Stream,
ProtocolType.Tcp );
listener.Bind(localEndPoint);
listener.Listen(100);
El mtodo Listen pone el socket en estado de escucha y su parmetro establece la
longitud mxima de la cola de conexiones pendientes que puede tener el servidor
antes de empezar a rechazar conexiones [backlog, en ingls].
A continuacin, nos quedamos esperando a que un cliente establezca una
conexin con nuestro servidor. El mtodo Accept extrae la primera peticin de la
cola asociada al servidor y devuelve un nuevo socket que podemos utilizar para
comunicarnos con el cliente:
Socket handler = listener.Accept();
En este caso hemos utilizado los sockets de la forma tradicional (la misma que
originalmente se ide en la distribucin BSD de UNIX): el servidor se queda
esperando al llamar a Accept hasta que llegue alguna peticin. En Windows, no
obstante, tambin podramos haber utilizado sockets de forma asncrona, para
evitar que el servidor quede bloqueado indefinidamente.
Cuando la llamada a Accept devuelve un nuevo socket, entonces podemos
realizar la tarea para la cual hayamos diseado nuestro servidor. Por ejemplo,
podemos construir un servidor que haga de eco, devolvindole al cliente lo mismo
que ste le enve:
byte[] bytes = new byte[1024];
int count;
String data = "";

do
{
count = handler.Receive(bytes);
data +=
System.Text.Encoding.ASCII.GetString(bytes,0,count);

} while ( data.IndexOf("\n") == -1 );

// Eco

Console.WriteLine( "Texto recibido: {0}", data);

byte[] msg = System.Text.Encoding.ASCII.GetBytes(data);

handler.Send(msg);
handler.Shutdown(SocketShutdown.Both);
handler.Close();
Cdigo fuente del servidor TCP utilizando sockets (Visual Studio 2003)
Cdigo fuente del servidor TCP utilizando sockets (Visual Studio 2005)
Para comprobar que nuestro servidor TCP funciona correctamente, podemos
utilizar la utilidad telnet para establecer una conexin con el puerto asociado a
nuestro servidor:
telnet localhost 11000
La plataforma .NET incluye una clase auxiliar denominada TcpListener que
simplifica algo la creacin de servidores TCP, si bien internamente se sigue
haciendo lo mismo:
TcpListener listener = new TcpListener(11000);

listener.Start();

TcpClient client = listener.AcceptTcpClient();

...
La nica diferencia reseable es que ahora, en vez de utilizar Send y Receive, el
objeto de tipo TcpClient dispone de un stream a travs del cual leemos y
escribimos datos.
Cdigo fuente del servidor TCP utilizando TcpListener (Visual Studio 2003)
Cdigo fuente del servidor TCP utilizando TcpListener (Visual Studio 2005)
Implementacin de un cliente TCP
Escribir un cliente TCP es an ms sencillo. A continuacin se muestra un ejemplo
de cliente TCP que se conecta al servidor implementado en la seccin anterior:
string mensaje = "Hola...\n";
string respuesta;
Byte[] SendBytes = Encoding.ASCII.GetBytes(mensaje);
Byte[] RecvBytes = new Byte[256];
int bytes;

// DNS
IPAddress address =
Dns.Resolve("localhost").AddressList[0];

// EndPoint
IPEndPoint EPhost = new IPEndPoint(address, 11000);

// Socket
Socket socket = new Socket ( AddressFamily.InterNetwork,
SocketType.Stream,
ProtocolType.Tcp );
// Conexin

try {

socket.Connect (EPhost);

socket.Send( SendBytes,
SendBytes.Length,
SocketFlags.None);

bytes = socket.Receive ( RecvBytes,
RecvBytes.Length,
SocketFlags.None);

respuesta = Encoding.ASCII.GetString
(RecvBytes, 0, bytes);

while (bytes > 0) {

bytes = socket.Receive ( RecvBytes,
RecvBytes.Length,
SocketFlags.None);

respuesta += Encoding.ASCII.GetString
(RecvBytes, 0, bytes);
}

Console.WriteLine(respuesta);

} catch (Exception error) {
Console.WriteLine("ERROR - "+error);
}
Cdigo fuente del cliente TCP utilizando sockets (Visual Studio 2003)
Cdigo fuente del cliente TCP utilizando sockets (Visual Studio 2005)
Igual que antes, la plataforma .NET incluye otra clase auxiliar que nos puede servir
de utilidad para crear clientes TCP: la clase TcpClient.
string mensaje = "Hola... \n";
string respuesta;
Byte[] SendBytes = Encoding.ASCII.GetBytes(mensaje);
Byte[] RecvBytes = new Byte[256];
int bytes;

// Cliente TCP

TcpClient client = new TcpClient();
NetworkStream stream;

// Conexin

try {

client.Connect("localhost",11000);

stream = client.GetStream();

stream.Write ( SendBytes,
0,SendBytes.Length );

bytes = stream.Read ( RecvBytes,
0, RecvBytes.Length);

respuesta = Encoding.ASCII.GetString
(RecvBytes, 0, bytes);

while (bytes > 0) {

bytes = stream.Read ( RecvBytes,
0, RecvBytes.Length );

respuesta += Encoding.ASCII.GetString
(RecvBytes, 0, bytes);
}

Console.WriteLine(respuesta);

} catch (Exception error) {
Console.WriteLine("ERROR - "+error);
}
Cdigo fuente del cliente TCP utilizando TcpClient (Visual Studio 2003)
Cdigo fuente del cliente TCP utilizando TcpClient (Visual Studio 2005)
Desarrollo de un servidor TCP real
Al implementar un servidor TCP real, tendremos que ser capaces de aceptar
conexiones de varios clientes simultneamente, por lo que tendremos que crear
una aplicacin multihebra. La forma ms sencilla de hacerlo en .NET es mediante
la ejecucin asncrona de delegados.
As es como quedara nuestro servidor TCP de eco en una implementacin ms
realista:
public class EchoServer
{
[STAThread]
static void Main(string[] args)
{
// Direccin IP
IPHostEntry ipHostInfo =
Dns.Resolve(Dns.GetHostName());
IPAddress ipAddress = ipHostInfo.AddressList[0];

// Puerto
IPEndPoint localEndPoint = new IPEndPoint(ipAddress,
11000);

// Listener
Socket listener = new Socket (
AddressFamily.InterNetwork,
SocketType.Stream,
ProtocolType.Tcp );

listener.Bind(localEndPoint);
listener.Listen(100); // backlog

Console.WriteLine("SERVIDOR DE ECO");
Console.WriteLine("Esperando una conexin...");

// Socket asncrono
listener.BeginAccept( new AsyncCallback(AcceptCallback),
listener );

// Finalizacin de la ejecucin del servidor
Console.Read();
}
...
Con los mtodos Begin... realizaremos las operaciones de
E/S de nuestro servidor de forma asncrona. Para ello,
tendremos que definir una clase auxiliar que se encargue de
mantener el estado de cada operacin asncrona de E/S:
public class StateObject
{
// Tamao del buffer
public const int BufferSize = 1024;

// Socket.
public Socket workSocket = null;

// Buffer de recepcin de datos
public byte[] buffer = new byte[BufferSize];

// Cadena recibida
public StringBuilder sb = new StringBuilder();
}

Cada vez que aceptemos una conexin, crearemos un nuevo
socket asncrono para atender nuevas peticiones y
recibiremos datos de la conexin aceptada (tambin de forma
asncrona):
public static void AcceptCallback (IAsyncResult ar)
{
Socket listener = (Socket)ar.AsyncState;

// 1. Nuevo socket para aceptar otras conexiones
concurrentemente

listener.BeginAccept(new AsyncCallback(AcceptCallback),
listener);

// 2. Aceptar conexin recibida

Socket handler = listener.EndAccept(ar);

// 3. Recibir datos de forma asncrona

StateObject state = new StateObject();
state.workSocket = handler;

handler.BeginReceive ( state.buffer, 0,
StateObject.BufferSize, 0,
new
AsyncCallback(ReadCallback), state);
}

La recepcin de datos se realiza de forma similar a los
ejemplos anteriores, si bien cada operacin de E/S se
realizar de forma asncrona (de ah lo de volver a invocar a
BeginReceive si an no han llegado todos los datos del
cliente):
public static void ReadCallback(IAsyncResult ar)
{
String content = String.Empty;
StateObject state = (StateObject)ar.AsyncState;
Socket handler = state.workSocket;

int bytesRead = handler.EndReceive(ar);

if (bytesRead > 0) {

// Almacenar los datos recibidos

state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0,
bytesRead));

content = state.sb.ToString();

if (content.IndexOf("\n") > -1) {
// Enviar eco
Send(handler, content);
} else {
// Recibir ms datos...
handler.BeginReceive(state.buffer, 0,
StateObject.BufferSize, 0,
new
AsyncCallback(ReadCallback), state);
}
}
}
Ya slo nos queda implementar la parte del servidor que le
devuelve al cliente lo que ste le haya enviado. Una vez ms,
lo hacemos de forma asncrona:
private static void Send(Socket handler, String data)
{
byte[] byteData = Encoding.ASCII.GetBytes(data);

handler.BeginSend( byteData, 0, byteData.Length, 0,
new AsyncCallback(SendCallback),
handler);
}

private static void SendCallback(IAsyncResult ar)
{
try {

Socket handler = (Socket)ar.AsyncState;

handler.EndSend(ar);
handler.Shutdown(SocketShutdown.Both);
handler.Close();

} catch (Exception e) {
Console.WriteLine(e.ToString());
}
}
Cdigo fuente del servidor TCP multihebra (Visual Studio 2005)
Acceso a recursos en la web
Aparte de proporcionar los mecanismos bsicos de comunicacin entre procesos
mediante sockets, la biblioteca de clases de la plataforma .NET incluye clases
auxiliares que nos facilitan el uso de los protocolos asociados a distintas
aplicaciones comunes. En el caso de la World Wide Web, la aplicacin ms
popular de Internet, podemos utilizar las clases WebClient, HttpWebRequest y
HttpWebResponse. Estas clases nos permiten acceder a cualquier recurso
disponible a travs de HTTP o de HTTPS sin necesidad de implementar ni
conocer los detalles de estos protocolos.
string url = "http://...";
HttpWebRequest request;
HttpWebResponse response;

request = (HttpWebRequest) WebRequest.Create(url);

request.KeepAlive = false;

response = (HttpWebResponse) request.GetResponse();

// Informacin proporcionada por el protocolo HTTP

Console.WriteLine ("Protocol = "
+ response.ProtocolVersion.ToString());

Console.WriteLine ("Status = "
+ response.StatusCode);

Console.WriteLine ("Description = "
+ response.StatusDescription.ToString());

Console.WriteLine ("Character Set = "
+ response.CharacterSet);

Console.WriteLine ("Encoding = "
+ response.ContentEncoding);

Console.WriteLine ("Content = "
+ response.ContentType);

Console.WriteLine ("Method = "
+ response.Method);

Console.WriteLine ("URI = "
+ response.ResponseUri);

Console.WriteLine ("Server = "
+ response.Server);

Console.WriteLine ("Cookies = "
+ response.Cookies);

Console.WriteLine ("Last modified = "
+ response.LastModified);


// Cabecera de la respuesta HTTP

Console.WriteLine ("HEADERS");
Console.WriteLine (response.Headers.ToString());

// Contenido del recurso al que se accede

Stream stream;
StreamReader reader;
Encoding encoder = Encoding.GetEncoding("utf-8");

Console.WriteLine ("CONTENT");

stream = response.GetResponseStream();

reader = new StreamReader( stream, encoder );

Char[] read = new Char[256];

int count = reader.Read( read, 0, 256 );

while (count > 0) {
String str = new String(read, 0, count);
Console.Write(str);
count = reader.Read(read, 0, 256);
}

reader.Close();
stream.Close();

response.Close();
Cdigo fuente del cliente HTTP/HTTPS (Visual Studio 2003)
Cdigo fuente del cliente HTTP/HTTPS (Visual Studio 2005)
NOTA: La plataforma .NET nos permite utilizar los mismos interfaces
(WebRequest y WebResponse) para acceder al sistema de archivos local
mediante las clases FileWebRequest y FileWebResponse. En este caso, lo que
hacemos es acceder a recursos utilizando URLs de la forma file://....
Correo electrnico
Como no poda ser menos, la plataforma .NET tambin incluye un componente
que nos permite enviar correo electrnico utilizando el protocolo estndar de
Internet para el envo de e-mails: el protocolo SMTP [Simple Mail Transfer
Protocol].
Podemos crear fcilmente un programa que nos permita enviar correos
electrnicos utilizando las clases del espacio de nombres System.Net.Mail si
utilizamos Visual Studio 2005 (.NET Framework 2.0) o bien las clases incluidas en
System.Web.Mail si utilizamos Visual Studio 2003 (.NET Framework 1.1).

Envo de correo electrnico en Visual Studio 2005 (.NET Framework 2.0)
Hemos de utilizar las clases definidas en el espacio de nombres System.Net.Mail.
Dentro de este espacio de nombres, la clase System.Net.Mail.SmtpClient es la
que nos permite acceder a un servidor SMTP para enviar mensajes de correo
electrnico:
SmtpClient smtp = new SmtpClient("smtp.gmail.com");

smtp.Send(from,to,subject,body);
Esta clase nos permite acceder a un servidor SMTP cualquiera (smtp.gmail.com
en este caso) para enviar correos electrnicos a travs de l. Es probable que
nuestro servidor requiera autentificacin, por lo que tambin tendremos que indicar
nuestro nombre de usuario y contrasea de la siguiente forma:
SmtpClient smtp = new SmtpClient("smtp.gmail.com");

smtp.Credentials = new
System.Net.NetworkCredential("usuario","********");
smtp.EnableSsl = true;

smtp.Send(from,to,subject,body);
La clase System.Net.Mail.MailMessage nos permite sacarle mayor partido al envo
de mensajes de correo electrnico. Por ejemplo, podemos enviar e-mails en
formato HTML:


MailMessage message = new
MailMessage("usuario@gmail.com", destinatario);

message.Subject = "...";
message.IsBodyHtml = true;
message.Body = "<html><body> ... </body></html>";

SmtpClient smtp = new SmtpClient("smtp.gmail.com");

smtp.Credentials = new
System.Net.NetworkCredential("usuario", "********");
smtp.EnableSsl = true;

smtp.Send(message);
Incluso podemos adjuntar ficheros a nuestros mensajes de correo electrnico
empleando la clase System.Net.Mail.Attachment:
OpenFileDialog dialog = new OpenFileDialog();

dialog.InitialDirectory = "c:\\" ;
dialog.Filter = "Ficheros de texto (*.txt)|*.txt" +
+ "|Cualquier fichero (*.*)|*.*" ;
dialog.FilterIndex = 2 ;
dialog.RestoreDirectory = true;

if (dialog.ShowDialog() == DialogResult.OK) {

MailMessage message = new
MailMessage("usuario@gmail.com", destinatario);
Attachment attachment = new
Attachment(dialog.FileName);

message.Subject = "...";
message.IsBodyHtml = true;
message.Body = "<html><body>"+...+"</body></html>";
message.Attachments.Add(attachment);

SmtpClient smtp = new SmtpClient("smtp.gmail.com");

smtp.Credentials = new
System.Net.NetworkCredential("usuario", "********");
smtp.EnableSsl = true;

smtp.Send(message);
}
Cdigo fuente de una aplicacin Windows para enviar correo electrnico (.NET
Framework 2.0, Visual Studio 2005)
Envo de correo electrnico en Visual Studio 2003 (.NET Framework 1.1)
Hemos de utilizar las clases definidas en el espacio de nombres System.Web.Mail.
Dentro de este espacio de nombres, la clase SmtpMail es la que sirve para enviar
mensajes de correo electrnico:
System.Web.Mail.SmtpMail.Send(from, to, subject, body);
Por defecto, esta clase intenta utilizar un servidor SMTP que se encuentre
instalado en la mquina local (si bien podemos utilizar otro servidor SMTP
estableciendo la propiedad esttica SmtpServer de la clase SmtpMail). Si nos
decantamos por utilizar un servidor SMTP local, podemos lanzar el servidor virtual
SMTP que viene con el Internet Information Server. Para activarlo, basta con
seleccionar la opcin "Iniciar" del men contextual asociado al servidor virtual
SMTP en la utilidad de administracin de equipos de Windows XP:

Si consultamos la documentacin de este servidor simple SMTP, podremos ver
que se limita a almacenar los mensajes de correo dirigidos a dominios locales en
un directorio local (C:\inetpub\mailroot\...\*.eml) e intenta redireccionar los
mensajes externos a algn servidor SMTP adecuado, sin garantizar su entrega (el
subdirectorio Drop almacena los mensajes que nunca llegaron a su destino).
Nota
Para utilizar las clases del espacio de nombres
System.Web.Mail debemos asegurarnos de que nuestro
proyecto en Visual Studio .NET incluya una referencia a la
DLL System.Web.dll.
La clase System.Web.Mail.MailMessage nos permite sacarle mayor partido al
envo de mensajes de correo electrnico. Por ejemplo, podemos enviar e-mails en
formato HTML:
System.Web.Mail.MailMessage message;

message = new System.Web.Mail.MailMessage();
message.From = ...;
message.To = ...;
message.Subject = ...;
message.BodyFormat = System.Web.Mail.MailFormat.Html;
message.Body = "<html><body> ... </body></html>";

System.Web.Mail.SmtpMail.Send(message);
Incluso podemos adjuntar ficheros a nuestros mensajes de correo electrnico
empleando la clase System.Web.Mail.MailAttachment:
OpenFileDialog dialog = new OpenFileDialog();

dialog.InitialDirectory = "c:\\" ;
dialog.Filter = "Ficheros de texto (*.txt)|*.txt"
+"|Cualquier fichero (*.*)|*.*" ;
dialog.FilterIndex = 2 ;
dialog.RestoreDirectory = true ;

if (dialog.ShowDialog() == DialogResult.OK) {

System.Web.Mail.MailAttachment attachment =
new
System.Web.Mail.MailAttachment(dialog.FileName);

System.Web.Mail.MailMessage message;

message = new System.Web.Mail.MailMessage();
message.From = ...;
message.To = ...;
message.Subject = ...;
message.BodyFormat =
System.Web.Mail.MailFormat.Html;
message.Body = "<html><body>"+...+"</body></html>";

message.Attachments.Add(attachment);

System.Web.Mail.SmtpMail.Send(message);
}
Cdigo fuente de una aplicacin Windows para enviar correo electrnico (.NET
Framework 1.1, Visual Studio 2003)
Si queremos utilizar un servidor de correo que requiera autentificacin, debemos
escribir lo siguiente para poder enviar mensajes de correo electrnico:
using System.Web.Mail
...

MailMessage message;

message.From = "fberzal@decsai.ugr.es";

message.Bcc = "jc.cubero@decsai.ugr.es";

message.Headers.Add ( "Reply-To",
"berzal@acm.org");
message.Headers.Add ( "From",
"Fernando Berzal <berzal@acm.org>");

message.Fields.Add (

"http://schemas.microsoft.com/cdo/configuration/smtpauthenticate",
"1" );
message.Fields.Add(
"http://schemas.microsoft.com/cdo/configuration/sendusername",
"fberzal" );
message.Fields.Add(
"http://schemas.microsoft.com/cdo/configuration/sendpassword",
"*********" );

SmtpMail.SmtpServer = "correo.ugr.es";

SmtpMail.Send( message );
Uso de UDP
UDP [User Datagram Protocol] es el protocolo que proporciona los servicios no
orientados a conexin en la familia TCP/IP. Esto implica que, cuando el cliente
enva un mensaje, no existe modo alguno en que pueda comprobar si el servidor
lleg a recibirlo (algo as como el correo convencional).
Por ejemplo, podemos utilizar UDP para construir una aplicacin Windows que nos
permita chatear:

Aunque un sistema real sera ms sofisticado, vamos a utilizar difusin de
mensajes [broadcasting] para hacer que el chat funcione en todos los ordenadores
conectados a una misma red puedan comunicarse. Para ello necesitamos la
direccin de broadcast de nuestra red. Esta direccin est formada por la direccin
de la red y una secuencia binaria de unos. La direccin de red es el resultado de
hacer la operacin lgica AND entre la direccin IP de nuestra mquina y la
mscara de red. Por ejemplo, si mi ordenador tiene la direccin IP
150.214.191.234 y la mscara de red es 255.255.255.0, la direccin IP de mi red
es 150.214.191.0, que al completarla con unos queda como 150.214.191.255.
IPAddress broadcastAddress =
IPAddress.Parse("192.168.1.255");
IPAddress localAddress =
Dns.Resolve(Dns.GetHostName()).AddressList[0];
int Port = 11000;
Una vez que sabemos las direcciones IP utilizadas para enviar y recibir
datagramas UDP y el puerto a travs del cual funcionar nuestra aplicacin,
tenemos que utilizar una hebra que se encargue de ir recibiendo mensajes (que
declararemos como miembro de nuestra clase formulario):
Thread listener; // Hebra empleada
// para recibir mensajes
La ejecucin de esta hebra auxiliar para recibir mensajes la controlaremos desde
los eventos Form_Load y Form_Closed del formulario (el flag done lo utilizaremos
ms adelante para asegurarnos de que la hebra auxiliar termina su ejecucin):
bool done = false; // Flag empleado para terminar
// la ejecucin de la hebra

// Arranque

private void ChatForm_Load
(object sender, System.EventArgs e)
{
ThreadStart start = new ThreadStart(Listener);
listener = new Thread(start);
listener.Start();
}

// Detencin

private void ChatForm_Closed
(object sender, System.EventArgs e)
{
done = true;
Send("FIN");
listener.Join();
}
La estructura general de una que recibe mensajes siempre es la misma:
private void Listener()
{
while (!done) {
...
}
}
Ahora bien, la hebra que recibe mensajes no es la misma hebra que controla la
interfaz de usuario de nuestra aplicacin, por lo que debemos asegurarnos de que
nunca se accede a la interfaz de usuario desde una hebra distinta a la hebra
responsable de su gestin. Para ello, podemos definir delegados, comprobar el
valor de la propiedad InvokeRequired y recurrir al mtodo Invoke del formulario:
// Actualizacin del texto (thread-safe)

delegate void AppendTextDelegate (string text);

void AppendText (string text)
{
if (textBoxDialog.InvokeRequired == false) {

// Hebra correcta
textBoxDialog.AppendText(text);

} else {

// Llamar al mtodo de forma asncrona
AppendTextDelegate appendText = new
AppendTextDelegate(AppendText);
this.Invoke(appendText, new object[] { text });
}
}
Ya tenemos listo todo lo necesario para poder centrarnos en el envo y recepcin
de mensajes del chat. Del envo de mensajes se encargar la funcin Send, cuya
implementacin es bastante simple:
private void Send (string message)
{
IPEndPoint ep;
Socket s;
byte[] msg;

ep = new IPEndPoint broadcastAddress, Port);

s = new Socket ( broadcastAddress.AddressFamily,
SocketType.Dgram,
ProtocolType.Udp);

msg = Encoding.Default.GetBytes( message );

try {

s.SendTo ( msg,
0, msg.Length,
SocketFlags.None,
ep );

} catch (Exception e) {

MessageBox.Show ( e.ToString(),
"Error enviando datos");
}
}
Esta funcin ser invocada cada vez que el usuario introduzca un retorno de carro
en el TextBox dedicado a la introduccin de mensajes:
private void textBoxUser_KeyPress
( object sender,
System.Windows.Forms.KeyPressEventArgs e)
{
if (e.KeyChar == (char)13) {
Send ( textBoxUser.Text );
textBoxUser.ResetText();
}
}
Finalmente, slo nos queda por implementar el cuerpo de la hebra que se encarga
de ir recibiendo datagramas UDP y nos permite ver lo que los dems usuarios del
chat escriben:
private void Listener()
{
IPEndPoint localEP;
Socket socket;

localEP = new IPEndPoint(localAddress, Port);

socket = new Socket ( localAddress.AddressFamily,
SocketType.Dgram,
ProtocolType.Udp );

try {

socket.Bind(localEP);

while (!done) {

// Buffer para recibir el mensaje
byte[] buffer = new byte[1024];

// IPEndPoint para identificar al emisor
IPEndPoint sender = new IPEndPoint
(IPAddress.Any, 0);
EndPoint remoteEP = (EndPoint) sender;

// Recepcin del mensaje (ojo: bloquea al
receptor)

socket.ReceiveFrom (buffer, ref remoteEP);

// Actualizacin segura de la interfaz de usuario

string mensaje =
Encoding.Default.GetString(buffer);

if (!done) {
AppendText(mensaje);
}
}

} catch (Exception e) {

MessageBox.Show ( e.ToString(),
"Error recibiendo datos" );
}
}
Cdigo fuente del chat UDP (Visual Studio 2003)
Cdigo fuente del chat UDP (Visual Studio 2005)
NOTA: Igual que suceda con TCP, podramos haber utilizado una clase especfica
proporcionada por la plataforma .NET para ahorrarnos un algunas lneas de
cdigo. En este caso, la clase se llama UdpClient. Esta clase sirve igualmente de
servidor y de cliente, ya que en UDP no hay servidores y clientes: todo el mundo
enva y todo el mundo escucha.
.NET Remoting

Un poco de historia
En el desarrollo de sistemas complejos usando tcnicas de orientacin a objetos,
una interfaz simple a nivel de bytes como la de los sockets no resulta del todo
apropiada. Por eso existen distintas tecnologas mediante las cuales un objeto
puede exponer sus interfaces al pblico para facilitar su utilizacin a un nivel de
abstraccin mayor.
En el modelo COM [Component Object Model], todos los objetos han de
implementar la interfaz IUnknown mediante la cual se controla su ciclo de vida
(contando las referencias existentes a un objeto se puede saber cundo puede ser
ste eliminado) y se pueden explorar sus caractersticas (consultando los
interfaces que implementa o, ms bien, preguntando si el objeto soporta un
interfaz dado):
[uuid(00000000-0000-0000-C000-000000000046)]
interface IUnknown {

HRESULT QueryInterface (
[in] const IID iid,
[out, iid_is(iid)] IUnknown iid );

unsigned long AddRef();

unsigned long Release();
}
La forma de hacer referencia a un interfaz COM es a travs de su identificador IID
[Interface IDentifier], un nmero de 128 bits nico a nivel global [GUID: Globally
Unique IDentifier].
Hay que mencionar que COM es un estndar binario que ni requiere ni impide el
uso de orientacin a objetos en el diseo de objetos COM. De hecho, COM se
limita a la especificacin de interfaces y no permite herencia de implementacin (lo
que puede considerarse algo positivo en el desarrollo de componentes software,
donde es preferible usar composicin en vez de herencia), si bien s permite
herencia simple de interfaces (aunque tambin es cierto que esta caracterstica
tampoco se usa mucho, ya que se prefieren definir categoras que no son ms que
conjuntos de interfaces).
Una vez publicado una intrefaz con su IID, su especificacin no puede modificarse
bajo ninguna circunstancia (un componente puede implementar distintas versiones
de un interfaz pero stas se tratan en realidad como interfaces diferentes
implementados por el componente, lo que, por otro lado, evita los conflictos de
nombres que se produciran en un modelo orientado a objetos convencional).
DCOM [Distributed COM] se limita a ampliar el modelo COM de forma
transparente mediante la utilizacin interna de un mecanismo de comunicacin
entre procesos basado en el uso de llamadas a procedimientos remotos usando
un estndar binario definido por DCOM.
COM/DCOM se utiliza mediante la generacin automtica de proxies en el cliente
y stubs en el servidor que se encargan de encapsular la comunicacin entre
procesos. De cara al programador, sta se realiza de forma transparente. Los
proxies y los stubs se generan automticamente a partir de la especificacin de los
interfaces en MIDL [Microsoft COM Interface Definition Language], un ejemplo de
la cual aparece arriba en la definicin de IUnknown.
COM+ es otra extensin de COM que apareci en el ao 2000 con Windows 2000
Server. Su primera versin, COM+ 1.0, integra COM con distintas tecnologas,
tales como el procesamiento de transacciones (con MTS [Microsoft Transaction
Server]) o el envo de mensajes asncronos (mediante MSMQ [Microsoft Message
Queue server]), entre otras. Su segunda versin, COM+ 2.0, es la plataforma
.NET.
La principal innovacin que supone COM+ (y, por ende, la plataforma .NET) en la
evolucin de los productos de Microsoft es la introduccin de atributos
declarativos. Estos atributos permiten separar distintos aspectos en el mismo
sentido en que el desarrollo de software orientado a aspectos permite identificar y
aislar asuntos compartidos por distintos componentes. Para entender esto de
forma intuitiva, digamos que los aspectos sirven para centralizar cdigo que de
otra forma aparecera duplicado y esparcido por distintas partes de una aplicacin
(p.ej. control de acceso, serializacin, sincronizacin, transacciones...).
Dominios de aplicacin
El CLR [Common Language Runtime] de la plataforma .NET divide cada proceso
en uno o varios dominios de aplicacin. Dichos dominios aislan los objetos que
contienen de todos los dems que queden fuera del dominio, de forma que para
que dos objetos de distintos dominios se puedan comunicar es necesario utilizar
"marshalling" (el mecanismo mediante el cual se empaquetan datos para su
transmisin, tambin conocido como serializacin):

El CLR se encarga de permitir la realizacin de llamadas que atraviesen los lmites
de un dominio, de un proceso y de una mquina. Cuando los objetos estn en el
mismo dominio, la llamada es local. En cualquier otro caso, la llamada es remota
(de ah lo de .NET Remoting), incluso cuando los objetos estn en el mismo
proceso pero en diferente dominio. Las llamadas locales son inmediatas (como en
cualquier invocacin a una funcin en un lenguaje de programacin tradicional),
mientras que las llamadas remotas involucran el uso de "marshalling" a travs de
proxies.
El siguiente fragmento de cdigo muestra el nombre del dominio de aplicacin
actual y de los assemblies que se hallan en l:
using System.Reflection;
using System.Runtime.Remoting;
...

AppDomain domain = AppDomain.CurrentDomain;

Console.WriteLine("Dominio actual: " +
domain.FriendlyName);

Assembly[] loadedAssemblies = domain.GetAssemblies();

Console.WriteLine("Assemblies en el dominio actual:");

foreach (Assembly assembly in loadedAssemblies)
Console.WriteLine (assembly.FullName);
Cdigo fuente de ejemplo
Los dominios de aplicacin constituyen unidades aisladas en el CLR. En ellos, una
aplicacin puede ejecutarse o detenerse sin afectar a las aplicaciones que se
ejecutan en otros dominios de aplicacin. De hecho, una aplicacin no puede
acceder directamente a los recursos que se encuentren en un dominio de
aplicacin distinto del suyo. Gracias a ello, un fallo en una aplicacin se puede
mantener confinado en los lmites de un dominio de aplicacin de forma que,
aunque distintos dominios de aplicacin estn en un mismo proceso, los dems
dominios de aplicacin no se vern afectados por el fallo.
Contextos
Los contextos definen una particin de los dominios de aplicacin, de tal forma
que los objetos pertenecientes a un contexto comparten las propiedades de su
contexto. Los contextos en COM+ derivan de los apartamentos COM (el
mecanismo mediante el cual se controla la sincronizacin entre hebras en COM) y
de los contextos MTS (que separan objetos en funcin de su "dominio
transaccional").
Un objeto puede ser gil [context-agile] o estar ligado a un contexto [context-
bound], lo cual viene predeterminado si el objeto deriva de la clase
System.ContextBoundObject. Un objeto gil A puede interactuar con un objeto B
ligado a un contexto como si A estuviese en el mismo contexto que B. Esto es,
puede llamarse a A desde cualquier contexto o dominio de aplicacin libremente.
Cualquier llamada que requiera pasar los lmites de un contexto es interceptada y,
dependiendo de las propiedades del contexto, es preprocesada, postprocesada o,
simplemente, rechazada.

Arquitectura de .NET Remoting
Sobre la infraestructura definida por los contextos y empleando la capacidad de
reflexin del CLI [Common Language Infraestructure], .NET Remoting (=CLR
Object Remoting) proporciona las bases para construir una amplia variedad de
estilos de comunicacin: desde llamadas sncronas (como DCOM) hasta llamadas
completamente asncronas (con la posibilidad de sondeo y notificacin de la
terminacin de la llamada), ya sea utilizando una codificacin binaria sobre un
canal TCP o empleando SOAP (en XML y, usualmente, sobre HTTP).
Canales
Los canales proporcionan el medio mediante el cual transmitir mensajes de
extremo a extremo. Se encargan de transmitir mensajes entre dominios de
aplicacin, puede que en distintas mquinas conectadas a travs de una red de
ordenadores, entre distintos procesos concurrentes que se ejecutan en una
mquina o, simplemente, entre dominios de aplicacin dentro de un proceso. La
plataforma .NET lleva incorporadas implementaciones de los canales para realizar
la comunicacin mediante sockets sobre TCP o HTTP.

Un canal normal, HTTP o TCP, no incorpora ninguna medida de seguridad a la
hora de transmitir datos. Si queremos que la transmisin de datos se realice de
forma segura, tendremos que crear nuestro propio canal (creando una clase que
implemente los interfaces IChannelReceiver, IChannel e IChannelSender) o alojar
nuestros objetos en el IIS, al cual se puede acceder usando HTTPS si lo
configuramos adecuadamente.
Formateadores
Los formateadores se encargan de serializar los objetos .NET para que puedan
transmitirse a travs de los canales de comunicacin. La plataforma .NET puede
serializar los objetos en binario y en SOAP/XML. El formateador binario es ms
eficiente, el que emplea XML resulta ms cmodo a la hora de integrar sistemas
heterogneos. Adems, podemos crear formateadores especficos que se adapten
a nuestras necesidades.

La diferencia clave entre .NET Remoting y los Servicios Web se encuentra en la
forma en que serializan los datos. Los Servicios Web emplean siempre XML, .NET
Remoting puede emplear cualquier formateador que implemente la interfaz
System.Runtime.Remoting.Messaging.IRemotingFormatter, como
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter o
System.Runtime.Serialization.Formatters.Soap.SoapFormatter.
Por defecto, los canales HTTP utilizan el formateador SOAP, mientras que los
canales TCP usan el formateador binario para acceder a objetos remotos, si bien
todo es configurable en .NET Remoting. Cuando las llamadas remotas slo cruzan
los lmites de un contexto pero se realizan dentro de un dominio de aplicacin, se
utiliza un canal especial CrossContextChannel que est optimizado para trabajar
dentro del espacio de memoria del proceso y no requiere formateador alguno.
Marshalling
"Marshalling" es el mecanismo mediante el cual se empaquetan las llamadas entre
dominios de aplicacin para su transmisin (tanto el paso de parmetros como la
devolucin de resultados). Se puede acceder de forma remota a un objeto de dos
formas diferentes:
Por valor (MarshalByValue), haciendo una copia del objeto completo, que
se transmite a travs del canal perdiendo el enlace entre el original y la
copia. El cliente dispondr localmente de una copia completa del objeto
remoto. Esta copia, obviamente, trabajar de forma independiente con
respecto a la copia remota del objeto. En otras palabras, en el momento en
el que se accede a un objeto remoto por valor, el objeto deja de ser remoto.
Por referencia (MarshalByRef), pasando nicamente una referencia al
objeto [ObjRef] y creando un "proxy" que sirve de enlace entre el cliente y el
objeto remoto. Los objetos remotos siempre residen y se ejecutan en el
servidor. El cliente se comunica con el objeto remoto a travs del proxy, que
slo tiene una referencia al objeto remoto.
Los objetos giles (independientes del contexto) se transmiten por valor si no
estn ligados a un dominio de aplicacin, mientras que se transmiten por
referencia si estn asociados a un dominio de aplicacin y se accede a ellos
desde fuera de ese dominio. Los objetos ligados a un contexto siempre se
transmiten por referencia fuera de ese contexto.
El acceso a un objeto por valor siempre ser ms rpido una vez que se dispone
de una copia local del objeto, si bien el tiempo que se tarda en obtener
inicialmente esa copia puede ser considerable si el objeto es grande. En realidad,
lo nico que se hace al acceder por valor a un objeto remoto es "descargar" el
objeto del servidor y trabajar con l localmente. Por el contrario, al acceder por
referencia a un objeto remoto, nuestra aplicacin es realmente una aplicacin
distribuida. Esto puede ser til cuando los objetos remotos son demasiado
grandes (lo que hace prohibitiva su transmisin hasta el cliente) o cuando los
objetos remotos residen en un servidor desde el cual se puede acceder a recursos
no disponibles directamente desde el cliente.
Proxy
Un "proxy" [apoderado] es un objeto que acta localmente en nombre de un objeto
remoto. Desde el punto de vista del programador, el proxy acepta llamadas como
si fuese el objeto real, si bien internamente lo nico que hace es delegar en el
objeto remoto para ejecutar las llamadas que recibe.
Cuando un cliente quiere acceder remotamente a un objeto del servidor, .NET
Remoting crea automticamente un proxy transparente que hace de servidor en el
lado del cliente, de forma que el cliente trabaja con l como si del propio objeto
remoto se tratase. El proxy implementa todos los mtodos que aparecen en la
interfaz del objeto remoto. Las llamadas que recibe las enva al objeto remoto, que
es el que verdaderamente se encarga de hacer el trabajo.
Dispatcher
El "dispatcher" se sita al otro extremo del canal, recibe los mensajes del proxy,
invoca al mtodo real en el objeto remoto, recoge la el resultado y devuelve un
mensaje de respuesta.
Uso de .NET Remoting
El modelo de activacin de objetos de la plataforma .NET se parece ms al de
CORBA que al de COM:
Si al otro extremo no se est escuchando, no se puede realizar ninguna
conexin (en COM, la activacin se produce bajo demanda).
No existe ningn registro a modo de pginas amarillas (como sucede en
RMI, por ejemplo).
Los servidores no se activan remotamente (en este sentido, .NET Remoting
se parece ms a los sockets TCP que a los componentes COM).
Nota
En los proyectos que utilizan clases del espacio de nombres
System.Runtime.Remoting es necesario agregar una
referencia a la DLL System.Runtime.Remoting.dll.
Uso de dominios de aplicacin dentro de un proceso
Comencemos creando un servidor ultra-simple (servidor.exe):
using System;

namespace RemotingExample
{
public class Servidor
{
[STAThread]
static void Main(string[] args)
{
Console.WriteLine ("Servidor cargado y ejecutado");
}

// Dominio en el que se ejecuta el servidor

public string Domain
{
get { return AppDomain.CurrentDomain.ToString(); }
}
}
}
Ahora intentamos usarlo desde un cliente que creamos como aplicacin
independiente (cliente.exe). Para que esta aplicacin funcione correctamente,
aadiremos una referencia al proyecto servidor.exe (algo que podemos hacer
directamente desde el explorador de soluciones del Visual Studio).
Ya en el cliente, creamos un nuevo dominio de aplicacin (dentro del mismo
proceso) y cargamos en l el assembly servidor.exe:
using System;
using System.Reflection;
using System.Runtime.Remoting;

namespace RemotingExample
{
public class Cliente
{
[STAThread]
static void Main(string[] args)
{
AppDomain newDomain;
ObjectHandle o;
Servidor s;

// Nuevo dominio

AnewDomain =
AppDomain.CreateDomain("MiNuevoDominio");
newDomain.ExecuteAssembly("Servidor.exe", null, args);

// Acceso al servidor...

o = newDomain.CreateInstance ( "Servidor",
"RemotingExample.Servidor");

s = o.Unwrap(); // ... para forzar la instanciacin del objeto

Console.WriteLine(s + " @ " + s.Domain);

// Fin

Console.WriteLine("Pulse ENTER...");
Console.ReadLine();
}
}
}
CreateInstance recibe como parmetros el assembly en el que se encuentra
nuestro "servidor" y el nombre completo de la clase a la que pertenece nuestro
servidor.
Cdigo fuente del ejemplo: Servidor no serializable (Visual
Studio 2003)
Cdigo fuente del ejemplo: Servidor no serializable (Visual
Studio 2005)
Al ejecutar el programa anterior, salta una excepcin porque el servidor no es un
objeto al que se pueda acceder desde otro dominio de aplicacin.
Para que el servidor sea accesible desde otro dominio de aplicacin por valor,
basta con marcarlo como serializable. Como para acceder al objeto por valor hay
que construir localmente una copia exacta del objeto remoto, el objeto completo ha
de transmitirse a travs del canal existente entre dominios de aplicacin
diferentes, para lo cual es imprescindible que el objeto sea serializable.
using System;

namespace RemotingExample
{
[Serializable]
public class Servidor
{
[STAThread]
static void Main(string[] args)
{
Console.WriteLine ("Servidor cargado y ejecutado");
}

// Dominio en el que se ejecuta el servidor

public string Domain
{
get { return AppDomain.CurrentDomain.ToString(); }
}
}
}
Cdigo fuente del ejemplo: Paso por valor (Visual Studio
2003)
Cdigo fuente del ejemplo: Paso por valor (Visual Studio
2005)
Tambin podramos controlar explcitamente cmo se serializa un objeto si, en vez
de limitarnos a utilizar el atributo [Serializable], implementsemos explcitamente la
interfaz ISerializable.
Hacer que al objeto remoto se pueda acceder por referencia es casi tan simple.
Basta con hacer que la clase Servidor derive de System.MarshalByRefObject.
using System;

namespace RemotingExample
{
public class Servidor: System.MarshalByRefObject
{
[STAThread]
static void Main(string[] args)
{
Console.WriteLine ("Servidor cargado y ejecutado");
}

// Dominio en el que se ejecuta el servidor

public string Domain
{
get { return AppDomain.CurrentDomain.ToString(); }
}
}
}
Cdigo fuente del ejemplo: Paso por referencia (Visual
Studio 2003)
Cdigo fuente del ejemplo: Paso por referencia (Visual
Studio 2005)
Acceso a objetos remotos con .NET Remoting
Una vez que ya tenemos los conocimientos bsicos necesarios para crear un
servidor real al que se acceda remotamente, creamos una biblioteca de clases
(Servidor.dll) en la que incluimos la funcionalidad que nuestro servidor
proporcionar a sus clientes:
public class Servidor: System.MarshalByRefObject
{
public Servidor()
{
Console.WriteLine("Constructor");
}

~Servidor()
{
Console.WriteLine("Destructor");
}

public string GetInfo ()
{
return AppDomain.CurrentDomain.FriendlyName;
}
}
El servidor lo tenemos que alojar en un dominio de aplicacin, para lo cual
creamos una aplicacin en modo de consola:
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
using System.Runtime.Remoting.Channels.Tcp;
...

class Host
{
static void Main(string[] args)
{
ChannelServices.RegisterChannel(
new HttpChannel(8888));

RemotingConfiguration.RegisterWellKnownServiceType(
typeof(RemotingExample.Servidor),
"Servidor",
WellKnownObjectMode.SingleCall);

Console.WriteLine(
"Servidor listo para aceptar mensajes...");
Console.WriteLine(
"Pulse INTRO para salir");
Console.ReadLine();
}
}
Esta aplicacin se encarga de crear y registrar un canal, ya que .NET Remoting
exige que, para que los clientes puedan acceder a los objetos remotos, stos
estn ligados a un canal cuyo nombre sea conocido por el cliente. Por tanto, lo
primero que hacemos es registrar un canal a travs del cual se pueda acceder al
objeto de forma remota.
Acto seguido, se registran los tipos de objetos a los que se podr acceder desde
fuera del dominio de aplicacin del servidor, as como su URL de acceso. Los
tipos registrados sern accesibles desde el exterior mientras la aplicacin que los
aloja est ejecutndose.
Tambin hay que indicar el tipo de activacin de los objetos a los que se accede
de forma remota. Desde el servidor, slo se permiten dos tipos de activacin en
.NET Remoting:
SingleCall: Se crea una instancia de la clase para cada llamada realizada a
travs del canal (comunicacin sin estado, como en el protocolo HTTP).
Singleton: Existe una nica instancia de la clase comn para todos los
clientes. Este singleton sirve de gateway a la lgica de la aplicacin.
Para poder hacer referencia al servidor desde el cliente, obtenemos una referencia
al proxy del objeto remoto mediante una llamada al mtodo GetObject de la clase
System.Runtime.Remoting.Activator. Una vez que tenemos el proxy, podemos
usar el servidor como cualquier otro objeto:

// Acceso remoto

ChannelServices.RegisterChannel(new HttpChannel());

Servidor remoto = (Servidor)Activator.GetObject (
typeof(RemotingExample.Servidor),
"http://localhost:8888/Servidor" );

Console.WriteLine( remoto.GetInfo() );

// Acceso local

Servidor local = new Servidor();

Console.WriteLine( local.GetInfo() );
El cliente, como es lgico, debe especificar el canal a travs del cual se
comunicar con el objeto remoto. Este objeto podra incluso ofrecer sus servicios a
travs de distintos canales, presumiblemente de distintos tipos. El cliente usar el
canal que resulte ms adecuado para comunicarse con el objeto remoto.
Cdigo fuente del ejemplo (Visual Studio 2003)
Cdigo fuente del ejemplo (Visual Studio 2005)
Los metadatos de la clase Servidor necesarios para poder compilar el cliente los
podemos obtener del propio assembly (Servidor.dll) o de la referencia web
http://localhost:8888/Servidor?wsdl. A partir de esa referencia se puede crear el
ensamblado requerido por el cliente sin tener que distribuir el cdigo completo del
servidor. Para lograrlo, podemos usar la utilidad soapsuds:
soapsuds -url:http://localhost:8888/Servidor?wsdl
-oa:InterfazServidor.dll
Cuando un objeto se registra en un canal para que se pueda acceder a l de
forma remota, los clientes podrn acceder a todas las propiedades, mtodos y
variables pblicas no estticas de la clase a la que corresponda el objeto.
En el ejemplo anterior, el ciclo de vida del objeto remoto se controla en el servidor.
Cuando un cliente quiere acceder al objeto remoto, el cliente obtiene un proxy y en
el servidor se crea una instancia de la clase del objeto remoto para atender nica y
exclusivamente a una peticin del cliente. El objeto remoto slo se instancia
("activa", si usamos la terminologa de .NET Remoting) cuando el cliente llama a
un mtodo del proxy. Esto nos permite ahorrar una llamada inicial a travs de la
red para, simplemente, crear el objeto.
Si usamos el modo de activacin SingleCall, el objeto remoto se instancia para
atender una nica peticin, tras la cual el objeto se elimina. Por tanto, esos objetos
no mantenienen su estado entre distintas peticiones provenientes de un mismo
cliente. Esto, que a primera vista es una seria limitacin, permite construir
aplicaciones distribuidas escalables, ya que el objeto slo consume recursos del
servidor temporalmente y, en el caso de usar un cluster en el servidor, la carga se
puede distribuir con mayor facilidad (al no importar qu servidor atiende cada
peticin porque stas son independientes unas de otras). En definitiva, este modo
de activacin es til en aplicaciones que slo requieren realizar una operacin
rpida e independiente del resto de la aplicacin como puede ser consultar un
dato concreto en una base de datos para mostrarlo en la interfaz de usuario.
Por el contrario, en el modo de activacin Singleton, slo tendremos una instancia
del objeto que atender todas las peticiones que se reciban de distintos clientes. A
diferencia de SingleCall, como el objeto existe de forma permanente, puede
mantener el estado entre distintas llamadas (estado que ser global para todos los
clientes que accedan a l). Por ejemplo, podramos usar este modo de activacin
para implementar correctamente un servidor de chat como un objeto remoto nico
(en vez de usar direcciones de broadcast).
Un objeto singleton es un objeto nico en su tipo que proporciona un punto de
acceso global a los servicios implementados por el objeto remoto. Cuando se hace
una llamada al objeto remoto, slo se crear una instancia del objeto si
previamente no existe, algo difcil de lograr con DCOM, en el que los objetos
remotos siempre se crean desde el cliente y hay que idear mecanismos artificiales
que nos permitan comprobar si existe ya un objeto de ese tipo para no crear otro.
En el caso de .NET Remoting, se simplifica la creacin de "singletons", si bien su
implementacin seguir siendo compleja. Los objetos remotos de este tipo
tendremos que implementarlos como aplicaciones multihebra para que puedan
atender concurrentemente las peticiones de mltiples clientes.
Este tipo de objetos remotos, SAO (objetos activados por el servidor),
proporcionan una funcionalidad limitada porque slo se pueden instanciar usando
constructores por defecto, sin parmetros. Pero no constituyen la nica opcin que
tenemos a nuestra disposicin en .NET Remoting, como veremos a continuacin
Ejercicio
Comprobar cundo se crean y se destruyen los objetos en el
servidor cuando utilizamos los modos de activacin Singleton
y SingleCall
Cuando el cliente retoma el control...
En situaciones como las vistas hasta ahora, el servidor controla cundo se crea un
objeto al que se pueda acceder de forma remota. Como no podra ser menos,
dada su flexibilidad, .NET Remoting tambin permite que el cliente sea el que
controle el ciclo de vida de los objetos a los que accede de forma remota, al ms
puro estilo de COM/DCOM.
Este tipo de objetos, denominados CAOs [Client activated objects], se instancian
en el servidor cuando el cliente lo solicita (no se espera a que se llame a un
mtodo, como suceda con los SAOs). Esto permite utilizar constructores con
parmetros si hace falta. La instanciacin/activacin de un objeto CAO se realiza
de la siguiente forma:
El cliente crea una instancia del objeto en el servidor mediante una solicitud
de activacin.
El servidor crea una instancia de la clase (previamente registrada) y
devuelve una referencia al objeto recin creado.
El cliente utiliza esa referencia para crear un proxy mediante el cual pueda
comunicarse con el objeto remoto.
Como consecuencia del proceso seguido, la instancia del objeto remoto atender
nicamente las peticiones provenientes del cliente que cre el objeto (a diferencia
de los SAOs Singleton, que son objetos compartidos entre todos los clientes). Por
ejemplo, se usara un objeto CAO cuando un cliente quiere realizar un pedido y la
realizacin del pedido involucra ir pasando por una serie de etapas, para las
cuales ha de mantenerse en todo momento el estado del pedido.
La implementacin en s de un objeto CAO no difiere de la de un objeto SAO.
Tendremos que crear una clase que herede de System.MarshalByRefObject. Lo
que s tendremos que cambiar es la aplicacin que aloja al objeto:
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
using System.Runtime.Remoting.Channels.Tcp;
...

class Host
{
static void Main(string[] args)
{
ChannelServices.RegisterChannel(new
HttpChannel(8888));

RemotingConfiguration.ApplicationName = "Servidor";
RemotingConfiguration.RegisterActivatedServiceType (
typeof(RemotingExample.Servidor) );

Console.WriteLine(
"Servidor listo para aceptar mensajes...");
Console.WriteLine(
"Pulse INTRO para salir");
Console.ReadLine();
}
}
El cliente, como antes, deber usar un canal HTTP para acceder al servidor. En
esta ocasin no obstante, se ha de utilizar la llamada al mtodo esttico
CreateInstance de la clase Activator. Este mtodo devuelve una referencia a partir
de la cual se puede obtener un proxy con el mtodo Unwrap y dicho proxy lo
usaremos para acceder al objeto de forma remota:
// Acceso remoto

ChannelServices.RegisterChannel(new HttpChannel());

object[] attrs =
{ new UrlAttribute("http://localhost:8888/Servidor")};

ObjectHandle handle = Activator.CreateInstance
( "Servidor", "RemotingExample.Servidor", attrs);

Servidor remoto = (Servidor) handle.Unwrap();

Console.WriteLine( remoto.GetInfo() );
En resumen, los CAOs ofrecen la mxima flexibilidad, mientras que los SAOs
proporcionan una mayor escalabilidad.
"Se alquila"
En .NET Remoting, el ciclo de vida de los objetos remotos se controla mediante
leasing, la realizacin de "contratos de alquiler", igual que en RMI o Jini en Java.
El lease determina el periodo de tiempo que el objeto estar activo en memoria
antes de que el CLR lo elimine. En el caso de los objetos activados por el servidor
de tipo SingleCall, estos slo existen mientras dure una llamada a un mtodo. En
cambio, los SAOs de tipo Singleton y los CAOs vivirn en funcin de sus
"contratos de alquiler".
Un objeto remoto puede definir su ciclo de vida redefiniendo el mtodo
InitializeLifeTimeService() de la clase base MarshalByRefObject. Cuando el objeto
se crea, la duracin de su contrato de alquiler se fija usando la propiedad
InitialLeaseTime del contrato representado por un objeto que implementa la
interfaz ILease. La duracin predeterminada del contrato es de 300 segundos por
defecto, aunque eso se puede modificar en la aplicacin que aloja el objeto remoto
(de forma global para todos los objetos de la aplicacin) o en el propio objeto
remoto (de forma local a cada objeto):
// En la aplicacin del servidor

public class Host
{
static void Main(string[] args)
{
LifetimeServices.LeaseTime = TimeSpan.FromMinutes(1);
LifetimeServices.RenewOnCallTime =
TimeSpan.FromSeconds(30);
...
}
...
}

// En el objeto remoto

public class ObjetoRemoto : MarshalByRefObject
{
...
public override Object InitializeLifetimeService()
{
ILease lease = (ILease)base.InitializeLifetimeService();

if (lease.CurrentState == LeaseState.Initial) {
lease.InitialLeaseTime = TimeSpan.FromMinutes(1);
lease.SponsorshipTimeout = TimeSpan.FromMinutes(2);
lease.RenewOnCallTime = TimeSpan.FromSeconds(30);
}

return lease;
}
...
}
Cada dominio de aplicacin contiene un "gestor de alquileres" [lease manager],
una hebra ms, que elimina los objetos cuando sus "contratos de alquiler" expiran.
El gestor de alquileres examina peridicamente los contratos para ver cules han
caducado y eliminar los objetos correspondientes (cada 10 segundos).
Cada vez que un "patrocinador" del objeto renueva su contrato de alquiler, dicho
contrato se ampla hasta el tiempo establecido por la propiedad RenewOnCallTime
(120 segundos por defecto), siempre y cuando fuese a caducar antes de ese
plazo. Cuando el contrato caduca, se espera el tiempo fijado por
SponsorshipTimeout antes de eliminar fsicamente el objeto, por si algn
patrocinador desea mantener el objeto (de nuevo, 120 segundos por defecto).
El patrocinador del objeto puede ser el cliente que accede a l o cualquier otro
objeto interesado en mantener al objeto remoto. Para renovar el contrato, el
patrocinador debe llamar al mtodo Renew() del contrato asociado al objeto
remoto, el cual se obtiene a travs de una llamada al mtodo GetLifetimeService().
El patrocinador, adems, recibir una notificacin cada vez que el lease vaya a
caducar, para lo cual ha de implementar la interfaz ISponsor y registrarse como
patrocinador del objeto:
remoto.GetLifeTimeService().Register ( patrocinador );
No obstante, el contrato se renueva automticamente cada vez que se accede al
objeto, por lo que usualmente no tendremos que preocuparnos por este asunto. Si
el contrato llegase a caducar y el objeto remoto fuese eliminado, la llamada al
mtodo remoto generara una excepcin (en el caso de los objetos CAO) o
volvera a instanciar otro objeto (en el caso de los objetos SAO Singleton, que son
nicos en cualquier momento pero no siempre son los mismos).
Ficheros de configuracin
En los ejemplos vistos hasta ahora, el cliente debe conocer el tipo concreto del
objeto remoto que activa. Eso implica que cualquier cambio en la configuracin del
servidor requiere recompilar todos los clientes.
Afortunadamente, esto no es necesario porque se pueden usar ficheros de
configuracin XML de forma que en el cdigo no tengamos que saber de qu tipo
concreto son los objetos a los que se accede (aunque siempre deberemos ser
conscientes de si el objeto remoto es un SAO SingleCall, un SAO Singleton o un
CAO).
En el servidor, una vez que tengamos el fichero de configuracin, podemos
registrar los canales oportunos y establecer los objetos a los que se puede
acceder de forma remota con una simple llamada al mtodo Configure de la clase
RemotingConfiguration:
RemotingConfiguration.Configure ("Servidor.config");
donde Servidor.config es el fichero XML con todos los datos necesarios para la
configuracin del servidor.
Como es lgico, en el cliente tambin podemos usar un fichero de configuracin
anlogo y no tener que especificar los datos relativos a la configuracin del
sistema (a costa de que un usuario malintencionado pueda acceder fcilmente a
esos datos, disponibles en XML). Adems, en el caso del cliente, ya no tenemos
que usar Activator para instanciar un objeto remoto. Bastar con crear un objeto
con new y la plataforma .NET se encargar automticamente de instanciar
correctamente el objeto, independientemente de si es un objeto local o un objeto
remoto:
RemotingConfiguration.Configure ("Cliente.config");

Servidor remoto = new Servidor();
Ms fcil, imposible !!!
Ejemplo
Como ejemplo del uso de ficheros de configuracin en .NET Remoting, crearemos
un sencillo servidor al que se acceder por referencia:
using System;
using System.Net;
using System.Runtime.Remoting;

namespace RemotingExample {

public class Servidor: System.MarshalByRefObject
{
public Servidor() {
Console.WriteLine("Constructor");
}

~Servidor() {
Console.WriteLine("Destructor");
}

public string GetHost () {
return Dns.GetHostName();
}

public string GetApplication () {
return RemotingConfiguration.ApplicationName;
}

public string GetAppDomain () {
return AppDomain.CurrentDomain.FriendlyName;
}
}
}
Este servidor lo alojaremos en un proceso al que se acceder remotamente
(Host.exe):
using System;
using System.Runtime.Remoting;

namespace RemotingExample
{
class Host {

static void Main(string[] args) {

RemotingConfiguration.Configure("Servidor.config");

Console.WriteLine(
"Servidor listo para aceptar mensajes...");
Console.WriteLine(
"Pulse INTRO para salir");
Console.ReadLine();
}
}
}
El fichero de configuracin Servidor.config especifica el canal que se utilizar para
acceder al servidor (un canal TCP en el puerto 7777) y el modo de activacin del
servidor (SingleCall):
Servidor.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.runtime.remoting>
<application name="RemotingExample">

<service>
<wellknown mode="SingleCall"
type="RemotingExample.Servidor, Servidor"
objectUri="RemotingExample.rem" />
</service>

<channels>
<channel port="7777" ref="tcp" />
</channels>

</application>
</system.runtime.remoting>
</configuration>
De forma anloga, para el cliente tambin necesitamos otro fichero de
configuracin en formato XML:
Cliente.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.runtime.remoting>
<application>

<client>
<wellknown type="RemotingExample.Servidor, Servidor"

url="tcp://localhost:7777/RemotingExample/RemotingExample.rem"
/>
</client>

<channels>
<channel ref="tcp" />
</channels>

</application>
</system.runtime.remoting>
</configuration>
A partir de este fichero, el acceso remoto al servidor desde el cliente resulta trivial:
using System;
using System.Runtime.Remoting;

namespace RemotingExample
{
class Cliente
{
[STAThread]
static void Main(string[] args)
{
// Acceso remoto

RemotingConfiguration.Configure( "Cliente.config" );

Servidor remoto = new Servidor();

Console.WriteLine( remoto.GetAppDomain() );
Console.WriteLine( remoto.GetApplication() );
Console.WriteLine( remoto.GetHost() );

Console.ReadLine();
}
}
}
Cdigo fuente del ejemplo (Visual Studio 2003)
Cdigo fuente del ejemplo (Visual Studio 2005)
NOTA: Para asegurarnos de que los ficheros de configuracin se distribuyen junto
con nuestros ejecutables, tenemos que comprobar que la propiedad "Copiar en el
directorio de resultados" de nuestros ficheros de configuracin .config est
activada.
.NET Remoting tambin en IIS
Usando ficheros de configuracin en XML, si nuestros objetos son de tipo SAO y
utilizamos canales HTTP, podemos utilizar el Internet Information Server para
alojar nuestros objetos remotos, sin necesidad de crear una aplicacin que haga
de servidor. Si alojamos la aplicacin en un directorio virtual llamado MiServicio
dentro del IIS, se podr acceder al objeto remoto a travs de la siguiente URL:
http://localhost/MiServicio/Servidor.soap



Servicios web

Arquitectura
Un servicio web es un componente software accesible a travs de protocolos
estndares de Internet. Los servicios web se hallan en el ncleo de la plataforma
.NET y de la visin del software como servicio (en contraposicin a la visin
tradicional del software como producto).
Del mismo modo que la proliferacin de PCs relativamente econmicos y la
disponibilidad de ancho de banda dio lugar a la expansin de Internet y a la
aparicin de las aplicaciones web, en las que los usuarios interactan con las
mquinas a travs de navegadores web, se pretende que el uso de XML y SOAP
facilite el desarrollo de sistemas distribuidos decentralizados en entornos
heterogneos. Los pilares sobre los que se construyen los servicios web (XML y
SOAP) estn diseados para facilitar la comunicacin entre distintos sistemas y
aplicaciones sin la intervencin de operadores humanos.
Bsicamente, los servicios web utilizan XML para facilitar que las aplicaciones
distribuidas puedan intercambiar datos en un formato sencillo fcilmente
interpretable. Esto resulta de particular inters en sistemas heterogneos. De
hecho, es en sistemas de este tipo donde otras alternativas a los servicios web
nunca han tenido demasiado xito (DCOM est limitado en la prctica a las
plataformas de Microsoft, RMI slo se usa en Java e incluso con CORBA se
producen problemas de interoperabilidad).
Los servicios web estn diseados como un mecanismo de paso de mensajes
adecuada para construir sistemas asncronos dbilmente acoplados. Debido al
formato que utilizan para transmitir mensajes (XML), los servicios web estn
pensados para utilizarse eficientemente en la transmisin de documentos y no se
deben interpretar como un simple mecanismo de llamadas a procedimientos
remotos (RPC), ya que para este fin resultan algo ineficientes.
Las aplicaciones basadas en servicios web se construyen empleando estndares
como HTTP, XML, SOAP, WSDL y UDDI. Estos tres ltimos se utilizan para
construir, describir y encontrar servicios web, respectivamente.

SOAP [Simple Object Access Protocol]
SOAP es el protocolo mediante el cual se envan mensajes en formato XML.
Como protocolo de transporte, SOAP suele emplear HTTP, si bien se puede
utilizar sobre cualquier otro protocolo (SMTP, TCP, UDP...). Cuando se utiliza
sobre HTTP, los mensajes SOAP se transmiten mediante solicitudes HTTP POST
en las que se ha de incluir una cabecera SOAPAction:
HTTP/1.1 POST /soap/myservice
Content-Type: text/xml
SOAPAction: MyInterface#MyComponentMethod

<SOAP:Envelope>
<SOAP:Header>
<MyHeader SOAP:mustUnderstand="0"> ... </MyHeader>
</SOAP:Header>
<SOAP:Body>
<MyRequest>
<argument>PI</argument>
</MyRequest>
</SOAP:Body>
</SOAP:Envelope>
La respuesta tiene el mismo formato que la solicitud:
HTTP/1.1 200 OK
...
Content-Type:text/xml
Content-Length: XXX

<?xml version="1.0"?>
<soap:Envelope ...>
<soap:Body>
<MyRequestResult>
<result>3.1416</result>
</MyRequestResult>
</soap:Body>
</soap:Envelope>
El formato de un mensaje SOAP es bastante simple. El mensaje en s es como un
sobre (<Envelope>) con una cabecera y un cuerpo. La cabecera del mensaje
SOAP (<Header>) puede incluir informacin acerca de cmo ha de procesarse el
mensaje en los sistemas intermedios por los que vaya pasando (actores, segn la
terminologa empleada en SOAP). El cuerpo del mensaje es el que incluye los
datos en s, representados como un documento XML.
SOAP incluye un tipo de mensaje especial, denominado SOAP fault, que sirve
para comunicar distintos tipos de error que se pueden producir en el paso de
mensajes.
HTTP/1.1 POST /soap/myservice
Content-Type: text/xml
SOAPAction: MyInterface#MyComponentMethod

<SOAP:Envelope>
<SOAP:Body>
<SOAP:Fault>
<faultcode>Server.InvalidArg</faultcode>
<faultstring>Type is wrong </faultstring>
<detail/>
</SOAP:Fault>
</SOAP:Body>
</SOAP:Envelope>
WSDL [Web Services Description Language]
WSDL se emplea para describir servicios web (de forma anloga a como los
esquemas XML describen documentos XML). Como no poda ser menos, las
especificaciones WSDL son documentos XML. Aunque su formato puede resultar
algo complejo a primera vista, las especificaciones WSDL resultan relativamente
fciles de interpretar en cuanto nos acostumbramos a ellas.
<definitions name="serviceName">
<import namespace="http://namespacePath"
location="http://path/fileName.wsdl">

<portType name="serviceNamePortType">
<operation name="opName">
<input message="msgNameInput" />
<output message="msgNameOutput" />
</operation>
</portType>

<binding name="serviceNameSoapBinding">
<soap:operation soapAction="http://..." />
</binding>

<service name="serviceName">
<port name="serviceNamePort" binding="bindingName">
<soap:address location="http://..." />
</port>
</service>

</definitions>
En primer lugar, una especificacin WSDL incluye algunas secciones con
informacin abstracta acerca de los mensajes y operaciones asociados al servicio
web que describe. Un mensaje es lo que se enva fsicamente de un proceso a
otro, mientras que una operacin es una combinacin de mensajes y representa
un grupo completo de mensajes relacionados. La especificacin WSDL tambin
incluye una seccin en la que se definen los tipos utilizados en los mensajes
(utilizando esquemas XML) y otra en la que se definen tipos de puertos
(definiciones abstractas de servicios web como grupos de operaciones).
Separadas de las secciones descritas en el prrafo anterior, las especificaciones
WSDL incluyen dos secciones en las que se recoge toda la informacin necesaria
para poder utilizar un servicio web. La seccin <binding> especifica los protocolos
utilizados en la transmisin de mensajes, mientras que la seccin <services>
recoge los servicios ofertados. Cada servicio es, en realidad, un conjunto de
puertos o puntos de acceso a los servicios web.
DISCO
DISCO permite inspeccionar los servicios web existentes en un servidor concreto.
Bsicamente, los documentos DISCO disponibles en un servidor web no son ms
que conjuntos de enlaces a especificaciones WSDL y a otros documentos DISCO.


UDDI [Universal Description, Discovery, and Integration]
UDDI viene a ser como las pginas amarillas de los servicios web. El estndar
UDDI, propuesto por Microsoft, IBM y Ariba, define el formato de las entradas del
directorio as como una serie de APIs para
poder realizar consultas (descubrir servicios, ya sea realizando bsquedas
al estilo de los sistemas de recuperacin de informacin o explorando
categoras),
publicar servicios web (esto es, enviar su especificacin para que sean
registrados), y
replicar el contenido del directorio UDDI para formar una base de datos
federada a nivel global.
Tutorial: Creacin de servicios web ASP.NET
Creacin manual de un servicio web en IIS
La creacin de un servicio web ASP.NET requiere realizar las siguientes tareas:
Tener instalada la plataforma .NET y el Internet Information Server. Si
tenemos el IIS funcionando pero no funcionan bien las pginas ASP.NET
debemos ejecutar la utilidad aspnet_regiis:
\Windows\Microsoft.NET\Framework\v...\aspnet_regiis -i
Crear un directorio virtual en IIS en donde alojar nuestra aplicacin
ASP.NET: Internet Services Manager > Default Web Site > Actions > Add a
new virtual directory.
Crear un fichero con extensin .asmx en el directorio virtual recin creado.
En dicho fichero se implementer el servicio web:
<%@ WebService Class="Eco" Language="C#" %>

using System;
using System.Web.Services;

public class Eco
{
[WebMethod]
public string Eco ( string mensaje)
{
return mensaje;
}
}

Obviamente, en un caso real no implementaremos nuestro servicio web
directamente en el fichero .asmx, sino que utilizaremos la directiva Codebehind
(como en las pginas ASP.NET).
Como se puede apreciar, lo nico que tenemos que hacer es utilizar el atributo
[WebMethod] del espacio de nombres System.Web.Services para que nuestro
mtodo se convierta en un servicio web.
Consideraciones de diseo
Aunque los entornos de programacin como el Visual Studio
.NET generen automticamente la interfaz del servicio web a
partir de las definiciones de clases, el diseo
acadmicamente correcto de servicios web requiere que
primero diseemos la interfaz del servicio web y despus
creemos la implementacin adecuada para el servicio
especificado. En otras palabras: los detalles de
implementacin no deberan determinar el diseo de los
servicios web. Los servicios web deben verse como puertos a
los que enviar mensajes y no como la interfaz de objetos a
los que se accede remotamente (al estilo de CORBA, DCOM
o RMI).
Creacin de un servicio web en Visual Studio 2005
La creacin de un servicio web ASP.NET desde Visual Studio .NET resulta
extremadamente fcil:
Creamos un nuevo "sitio web" de tipo Servicio Web ASP.NET:

El Visual Studio se encarga de crear adecuadamente un proyecto del tipo
seleccionado:

A continuacin, le ponemos el nombre que queramos a nuestro servicio
web, para lo que podemos usar la opcin "Cambiar nombre..." que aparece
en el men contextual "Refactorizar":

Tambin podemos aprovechar para cambiar los nombres de los ficheros de
nuestro proyecto: EchoService.asmx y EchoService.cs
Finalmente, implementamos la lgica de nuestro servicio web (trivial en este
ejemplo):

A la hora de implementar el servicio web de forma adecuada, lo nico que
debemos hacer siempre es asegurarnos de asociarle un espacio de nombres
adecuado (por defecto, el espacio de nombres ser http://tempuri.org/). As mismo,
tambin resulta aconsejable aadirle una descripcin al servicio utilizando el
parmetro Description del atributo [WebMethod].
Este es el servicio web que hemos creado:
El fichero EchoService.asmx simplemente incluye una
directiva que hace referencia al fichero de cdigo:
<%@ WebService Language="C#"
CodeBehind="~/App_Code/EchoService.cs"
Class="EchoService" %>
En el fichero App_Code/EchoService.cs es donde
encontramos el servicio web en s:
using System;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;

[WebService(Namespace = "http://csharp.ikor.org/")]
[WebServiceBinding(ConformsTo =
WsiProfiles.BasicProfile1_1)]
public class EchoService : System.Web.Services.WebService
{
public EchoService () {
}

[WebMethod]
public string Echo (string str)
{
return str;
}
}
Cdigo fuente del ejemplo
Prueba del servicio web en Visual Studio 2005
Una vez que tenemos creado el servicio web, para probarlo lo nico que tenemos
que hacer es acceder a l ejecutando nuestro proyecto. Al ejecutar el proyecto, se
lanza el servidor de desarrollo ASP.NET en alguno de los puertos libres que tenga
nuestra mquina:

Para acceder al servicio web, no tenemos ms que teclear la URL correspondiente
al fichero .asmx en la barra de direcciones de nuestro navegador web. Desde la
URL de nuestro servicio web podemos explorar las distintas operaciones
permitidas por el servicio web:

Incluso podemos probar el funcionamiento de nuestro servicio web utilizando los
formularios HTML que automticamente genera el Internet Information Server para
ver qu respuesta que nos da (en formato XML, como es natural):

La invocacin de un servicio web, en .NET, se puede realizar igual que si
utilizsemos un formulario HTML, pasndole los parmetros mediante una
solicitud HTTP POST. Obviamente, tambin podremos utilizar siempre el protocolo
SOAP.
El servidor de desarrollo ASP.NET nos ofrece toda la informacin que podemos
necesitar para utilizar nuestro servicio web, desde los distintos formatos de
mensajes que podemos emplear para utilizarlo hasta su especificacin WSDL.
Para ello, no tenemos ms que acceder a la URL de nuestro servicio web
aadiendo el parmetro ?WSDL:
http://localhost:1881/WebServiceExample/EchoService.asmx?WSDL

Creacin de un servicio web en Visual Studio 2003
La creacin de un servicio web ASP.NET desde Visual Studio .NET resulta
extremadamente fcil:
Creamos un proyecto de tipo ASP.NET Web Service:

El Visual Studio se encarga de configurar adecuadamente el servidor web
(IIS)::

A continuacin, le ponemos el nombre que queramos a nuestro servicio
web:

Finalmente, implementamos la lgica de nuestro servicio web (trivial en este
ejemplo):

A la hora de implementar el servicio web de forma adecuada, lo nico que
debemos hacer siempre es asegurarnos de asociarle un espacio de nombres
adecuado (por defecto, el espacio de nombres ser http://tempuri.org/). As mismo,
tambin resulta aconsejable aadirle una descripcin al servicio utilizando el
parmetro Description del atributo [WebMethod].
Cdigo fuente del ejemplo
Prueba del servicio web en Visual Studio 2003
Una vez que tenemos creado el servicio web, para probarlo lo nico que tenemos
que hacer es acceder a l a travs del Internet Information Server:

Cuando instalamos un servicio web en el IIS, para acceder a l no tenemos ms
que teclear la URL correspondiente al fichero .asmx en la barra de direcciones de
nuestro navegador web (p.ej. podramos escribir
http://elvex.ugr.es:800/WebService/Service.asmx si nuestra mquina fuese
elvex.ugr.es y el IIS estuviese funcionando en el puerto TCP 800 en vez del puerto
80). Desde la URL de nuestro servicio web podemos explorar las distintas
operaciones permitidas por el servicio web:

Incluso podemos probar el funcionamiento de nuestro servicio web utilizando los
formularios HTML que automticamente genera el Internet Information Server para
ver qu respuesta que nos da (en formato XML, como es natural):

La invocacin de un servicio web, por tanto, se puede realizar igual que si
utilizsemos un formulario HTML, pasndole los parmetros mediante una
solicitud HTTP POST o empleando incluso una solicitud de tipo HTTP GET. En
este ltimo caso, los parmetros se codifican en la URL de la solicitud, p.ej.
http://elvex.ugr.es:800/WebService/Service.asmx/Hola?

http://elvex.ugr.es:800/WebService/Service.asmx/Convertir?tempFahrenheit=100
El IIS nos ofrece toda la informacin que podemos necesitar para utilizar nuestro
servicio web, desde los distintos formatos de mensajes que podemos emplear
para utilizarlo hasta su especificacin WSDL, a la que podemos acceder mediante
la URL ...asmx?WSDL:
http://localhost:800/WebService/Service.asmx?WSDL
Tutorial: Uso de servicios web
Para poder utilizar un servicio web, del que usualmente slo tendremos su
especificacin WSDL, tenemos que implementar el cliente que acceder al
servicio web. En vez de implementar manualmente el cliente, podemos emplear la
utilidad wsdl.exe que genera los proxies necesarios a partir de la descripcin
WSDL.
Desde dentro del entorno, el acceso a un servicio web es an ms simple. Slo
tenemos que agregar una referencia web a nuestro proyecto y el Visual Studio
.NET se encargar de crear las clases necesarias para acceder al servicio web
como si se tratase de un acceso local a los servicios ofrecidos por cualquier otra
de las clases de nuestra aplicacin. Para ver cules son las clases generadas
automticamente por el Visual Studio .NET slo tenemos que pinchar en el botn
"Mostrar todos los archivos" del Explorador de Soluciones.
Acerca del uso de proxies...
En ocasiones, desde nuestra red local no podremos acceder
a un servicio web disponible en un servidor remoto si nuestro
acceso al exterior requiere el uso de proxies. Si para acceder
al exterior de nuestra red tenemos que utilizar
necesariamente un proxy, debemos recurrir a la clase
System.Net.Proxy para indicar en nuestra aplicacin que todo
el trfico dirigido al exterior de nuestra red pase por el proxy
que indiquemos.
Si queremos que cualquier trfico dirigido hacie el exterior
pase por el proxy, podemos utilizar la propiedad Select de la
clase auxiliar GlobalProxySelection incluida en el espacio de
nombres System.Net. Por ejemplo:
System.Net.GlobalProxySelection.Select =
new
System.Net.WebProxy("http://stargate.ugr.es:3128/",true);
Si slo tenemos que utilizar el proxy para acceder a un
servicio web concreto, podemos fijar la propiedad Proxy del
objeto que represente el servicio web al que queremos
acceder, tal como aparece en el siguiente fragmento de
cdigo:
using System.Net;
...

WebService service = ...

service.Proxy = new
WebProxy("http://stargate.ugr.es:3128/",true);
En este caso, slo se utiliza el proxy para acceder al servicio
web cuya propiedad Proxy hayamos establecido. El resto del
trfico generado por nuestra aplicacin no se pasar a travs
del proxy.
Uso desde Java
Una de las ventajas de utilizar servicios web es que stos facilitan la
interoperabilidad entre aplicaciones escritas de forma independiente utilizando
distintos lenguajes y entornos de programacin.
Por ejemplo, podemos acceder a un servicio web ASP.NET utilizando el lenguaje
de programacin Java. A continuacin mostramos cmo se puede acceder a un
servicio web ASP.NET utilizando Apache Axis [Apache Extensible Interaction
Engine], un conjunto de paquetes que implementan los interfaces estndar en
Java para usar servicios web:
import org.apache.axis.client.Call;
import javax.xml.rpc.Service;
import javax.xml.rpc.ServiceFactory;
import javax.xml.rpc.encoding.XMLType;
import javax.xml.namespace.QName;

public class Client
{
public static void main(String [] args)
{
try {


// Punto de acceso al servicio web

String endpointURL =
"http://elvex.ugr.es:800/WebService/Service.asmx";
String namespace = "http://elvex.ugr.es/";
String methodName;

ServiceFactory factory = ServiceFactory.newInstance();
Service service = factory.createService(new
QName(namespace,methodName));


// Servicio simple sin parmetros

methodName = "Hola";

Call call = (Call) service.createCall();
call.setTargetEndpointAddress(endpointURL);
call.setOperationName( new
QName(namespace,methodName) );
call.setProperty( Call.SOAPACTION_URI_PROPERTY,
namespace+methodName);

String response = (String) call.invoke( new Object[] {} );
System.out.println(response);

// Servicio de eco

methodName = "Eco";

Call call3 = (Call) service.createCall();
call3.setTargetEndpointAddress(endpointURL);
call3.setOperationName( new
QName(namespace,methodName) );
call3.setProperty( Call.SOAPACTION_URI_PROPERTY,
namespace+methodName );
call3.addParameter( new QName(namespace,"echo"),
XMLType.XSD_STRING,
javax.xml.rpc.ParameterMode.IN);
call3.setReturnType(XMLType.XSD_STRING);

System.out.println(
call3.invoke( new Object[] {"Prueba de eco... OK" } ));


// Servicio de conversin de temperaturas

methodName = "Convert";

Call call2 = (Call) service.createCall();
call2.setTargetEndpointAddress(endpointURL);
call2.setOperationName( new
QName(namespace,methodName) );
call2.setProperty( Call.SOAPACTION_URI_PROPERTY,
namespace+methodName);
call2.addParameter( new
QName(namespace,"tempFahrenheit"),
XMLType.XSD_INT,
javax.xml.rpc.ParameterMode.IN);
call2.setReturnType(XMLType.XSD_INT);

Integer iresponse;

iresponse = (Integer) call2.invoke( new Object[] { new
Long(100) } );
System.out.println(iresponse);

} catch(Exception error){
System.out.println(error);
}
}
}
Importante
Aunque el protocolo SOAP en s es el mismo en las distintas
implementaciones existentes, existen distintas formas de
codificar los mensajes SOAP, algo que deberemos tener en
cuenta para construir sistemas interoperables en la prctica.
Por defecto, en la plataforma .NET se utilizan mensajes
SOAP de tipo "document/literal" (que representan los datos
utilizando serializacin XML y no utiliza el estndar
SOAP/RPC para realizar las llamadas a los servicios web).
Por otra parte, Apache Axis emplea, por defecto, mensajes
SOAP "RPC/encoded" (que utiliza el estndar SOAP para
establecer una correspondencia entre los tipos de datos y su
representacin en XML, as como el estndar definido en la
especificacin SOAP para realizar llamadas a mtodos). Este
hecho deberemos tenerlo en cuenta a la hora de implementar
servicios web en distintas plataformas. En la prctica, basta
con utilizar el atributo [SoapRpcService] en la implementacin
.NET.
Ejercicio
Comprobar las diferencias existentes entre los formatos
SOAP "document/literal" y SOAP "RPC/encoded". Para ello,
pruebe con mtodos etiquetados con [WebMethod] que
reciban parmetros de distintos tipos (por ejemplo, fechas y
otros tipos de objetos).
Caso prctico: Servicios web de Google
Ahora veremos cmo utilizar el buscador ms famoso de Internet en nuestras
propias aplicaciones.
Comenzamos creando un nuevo "sitio web" de tipo "aplicacin web ASP.NET" con
Visual C#:

Para que el proyecto quede tal como aparece en la figura, nos hace falta agregar
la referencia web que nos permitir acceder a los servicios web de Google. Para
ello, usamos la descripcin WSDL que se encuentra en
http://api.google.com/GoogleSearch.wsdl

Una vez que hemos aadido la referencia web, para acceder a los servicios web
de Google slo tendreemos que incluir la sentencia using com.google.api en la
cabecera de nuestros ficheros de cdigo (en Visual Studio 2002/2003, el servicio
web se importaba dentro del espacio de nombres de nuestra aplicacin, por lo que
la sentencia deba ser using GoogleClient.com.google.api).
A continuacin, diseamos el formulario web para que quede como en la siguiente
imagen:

Para que el formulario quede tal como aparece en la figura, hemos aadido
algunos controles estndar a nuestro formulario: una imagen (Image), una caja de
texto (TextBox) y un botn (Button). A continuacin, establecemos las siguiente
propiedades para los controles de nuestro formulario:


Componente Propiedad Valor
DOCUMENT title Cliente para Google
Image (ID) Logo

ImageUrl logo.gif
TextBox (ID) TextBoxSearch
Button (ID) ButtonSearch

Text Buscar...
Al cargar la pgina comprobamos si tenemos que hacer una bsqueda:
private void Page_Load(object sender, System.EventArgs e)
{
string query;

if ( Page.IsPostBack )
query = TextBoxSearch.Text;
else
query = Request.Params["query"];

if ((query!=null) && (query.Length>0))
Search(query);
}
La rutina encargada de hacer la bsqueda accede al servicio web utilizando
nuestra clave de acceso, la cual nos permite hacer 1000 bsquedas diarias. Con
el resultado devuelto por Google creamos un documento XML, al cual le
aplicaremos una hoja de estilo XSLT para generar la salida en HTML. La
implementacin en C# resulta algo tediosa pero es fcil de entender:
private void Search (string query)
{
GoogleSearchService service = new
GoogleSearchService();
GoogleSearchResult result;

// GUI

TextBoxSearch.Text = query;

// Consulta

string key = "9hKEFf1QFHJ73rgLZe3Pe9BWYofMTkU/"; //
Clave de acceso
int start = 0;
int maxResults = 10;
bool filter = true; // Elimina resultados similares
string restrict = ""; // Restringe la bsqueda a un pas, a
un tema...
bool safeSearch = true; // Elimina contenido para adultos
string lr = "lang_es"; // Idioma

result = service.doGoogleSearch
(key, query, start, maxResults, filter, restrict, safeSearch,
lr, "", "");

// Resultado

MemoryStream stream = new MemoryStream();
XmlTextWriter writer = new XmlTextWriter(stream,null);

writer.Formatting = Formatting.Indented;
writer.Indentation = 2;

writer.WriteStartDocument();

writer.WriteStartElement("SearchResults");
writer.WriteAttributeString("searchComments",
result.searchComments);
writer.WriteAttributeString( "estimatedTotalResultsCount",

result.estimatedTotalResultsCount.ToString());
writer.WriteAttributeString( "searchQuery",
result.searchQuery);
writer.WriteAttributeString( "startIndex",
result.startIndex.ToString());
writer.WriteAttributeString( "endIndex",
result.endIndex.ToString());
writer.WriteAttributeString( "searchTips", result.searchTips);
writer.WriteAttributeString( "searchTime",
result.searchTime.ToString());

writer.WriteStartElement("ResultSet");

foreach (ResultElement element in result.resultElements) {

writer.WriteStartElement("Item");

writer.WriteStartElement("summary");
writer.WriteString(element.summary);
writer.WriteEndElement();

writer.WriteStartElement("URL");
writer.WriteString(element.URL);
writer.WriteEndElement();

writer.WriteStartElement("snippet");
writer.WriteString(element.snippet);
writer.WriteEndElement();

writer.WriteStartElement("title");
writer.WriteString(element.title);
writer.WriteEndElement();

writer.WriteStartElement("directoryTitle");
writer.WriteString(element.directoryTitle);
writer.WriteEndElement();

writer.WriteEndElement();
}

writer.WriteEndElement(); // ResultSet

writer.WriteEndElement(); // SearchResults

writer.WriteEndDocument();
writer.Flush();

// Transformacin XSLT: XML -> HTML

XmlDocument xml = new XmlDocument();
XslTransform xslt = new XslTransform();
MemoryStream html = new MemoryStream();

stream.Position = 0;

xml.Load(stream);
xslt.Load(Server.MapPath("search.xsl"));
xslt.Transform(xml,null,html);

// Salida final

html.Position=0;

StreamReader streamReader = new StreamReader(html);
char[] buffer = new char[html.Length];
streamReader.Read ( buffer, 0, (int) html.Length );

Response.Write( buffer, 0, (int) html.Length );
}
Lo nico que nos falta es crear la transformacin XSLT que nos permita visualizar
el resultado de la bsqueda en HTML. Tambin resulta algo larga, pero no debera
ser difcil de interpretar si se tienen unos conocimientos mnimos de XML y XSLT:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"/>
<xsl:template match="SearchResults">

<xsl:if test="@estimatedTotalResultsCount = 0" >
<center><b>
No se encontraron documentos con
"<xsl:value-of select="@searchQuery"/>".
</b></center>
</xsl:if>

<xsl:if test="@estimatedTotalResultsCount > 0" >
<xsl:if test="string(@searchQuery)">

<center>
<table width="90%" border="0">
<tr bgcolor="#3366cc">
<td>
<font color="#ffffff">Pginas con
<b>"<xsl:value-of select="@searchQuery"/>".</b></font>
</td>
<td width="20%" align="center">
<font color="#ffffff">
Resultados
<xsl:value-of select="@startIndex"/> - <xsl:value-of
select="@endIndex"/>
de <xsl:value-of
select="@estimatedTotalResultsCount"/>.
</font>
</td>
<td width="20%" align="center">
<font color="#ffffff">
Tiempo de bsqueda: <xsl:value-of
select="@searchTime"/> segundos.
</font>
</td>
</tr>

<xsl:for-each select="ResultSet/Item">

<tr>
<td colspan="3">
<a>
<xsl:attribute name="href">
<xsl:value-of select="URL"/>
</xsl:attribute>

<xsl:value-of disable-output-escaping="yes"
select="title"/>

</a>

</td>
</tr>

<tr>
<td colspan="3">
<xsl:value-of disable-output-escaping="yes"
select="snippet"/>
</td>
</tr>

<tr>
<td colspan="3">
<font color="green"> <xsl:value-of select="URL"/> </font>
<br></br>
</td>
</tr>

</xsl:for-each>

</table>
</center>
</xsl:if>
</xsl:if>
</xsl:template>

</xsl:stylesheet>
Voil! Hemos terminado una aplicacin web que utiliza el Google como motor de
bsqueda:

Cdigo fuente del cliente de Google (aplicacin web para Visual Studio 2003)
Cdigo fuente del cliente de Google (sitio web para Visual Studio 2005)
Cdigo fuente del cliente de Google (aplicacin web para Visual Studio 2005)
Cdigo fuente del cliente de Google (aplicacin web para Visual Studio 2008)
Como es lgico, podemos acceder a un servicio web desde una aplicacin de
cualquier tipo, no slo desde aplicaciones web ASP.NET. Por ejemplo, podemos
usar los servicios web ofrecidos por Google desde una aplicacin Windows. Para
ello, slo tenemos que agregar una referencia web a nuestro proyecto, p.ej.
Cdigo fuente del cliente de Google (como aplicacin Windows, para Visual
Studio 2008)
Caso prctico: Servicios web de Amazon
Ahora construiremos una sencilla aplicacin de e-business, para lo cual
accederemos a los servicios web de Amazon, la librera ms grande de Internet,
para lo cual comenzamos creando una aplicacin web ASP.NET.
En primer lugar, tenemos que conseguir la descripcin WSDL de los servicios web
de Amazon para poder utilizarlos en nuestro proyecto. De entre los muchos
servicios web ofrecidos por Amazon para realizar diferentes tareas, nosotros
usaremos A2S (Amazon Associates Service). La descripcin WSDL de este
servicio web ofrecido por Amazon se puede encontrar en la URL
http://ecs.amazonaws.com/AWSECommerceService/AWSECommerceService.wsd
l o, de formal alternativa, tambin la podemos encontrar en
http://webservices.amazon.com/AWSECommerceService/AWSECommerceServic
e.wsdl.
Como tenemos que acceder a los servicios web de Amazon desde nuestra
aplicacin, necesitamos crear las clases que harn de proxy en nuestra aplicacin
cliente. Esto lo podemos hacer utilizando la utilidad wsdl.exe:
wsdl /o:AmazonWebServices.cs
AWSECommerceService.wsdl
o bien aadiendo una referencia web a nuestro proyecto de aplicacin web
ASP.NET:

Hecho esto, si llamamos AmazonClient al formulario web que utilizaremos para
acceder al catlogo de libros de Amazon, nuestro proyecto debera aparecer en el
explorador de soluciones tal como muestra la siguiente captura de pantalla:

A continuacin, diseamos nuestro formulario web con una tabla (men "Tabla >
Insertar"). Al formulario le aadiremos un par de botones, una imagen en la que
mostraremos la portada del libro (ImagenLibro) y tres etiquetas en las que se
visualizarn el ttulo del libro, su autor y su precio (LabelTitulo, LabelAutor y
LabelPrecio, respectivamente).

Antes de que se nos olvide, al fichero de cdigo asociado al formulario hemos de
aadirle la sentencia using que nos permitir acceder a los servicios ofertados por
Amazon:
using Amazon.com.amazonaws.ecs;
si tenamos Amazon como espacio de nombres predeterminado para nuestro
proyecto en Visual Studio, o bien
using com.amazonaws.ecs;
si no estamos usando un espacio de nombres predeterminado para el proyecto
actual.
Aparte de los controles de nuestra interfaz, necesitaremos las siguientes variables
de instancia en nuestro formulario ASP.NET:
// ISBN del libro mostrado
private string isbn;

// Servicio web
AWSECommerceService service;

// Cesta de la compra
Cart cart;
Consulta del catlogo de productos
Para seleccionar un libro utilizaremos su ISBN [International Standard Book
Number], que pasaremos como parmetro a nuestro formulario web cuando
queramos consultar los datos de un libro concreto en Amazon:
El ISBN lo obtenemos de la solicitud HTTP. Si sta no incluye ningn ISBN,
utilizamos uno por defecto.
Para acceder a cualquier servicio web de Amazon, siempre tendremos que
identificarnos (usando AssociateTag y AWSAccessKeyId, dos valores que
nos dan al registrarnos como usuarios de los servicios web de Amazon, en
todos y cada uno de los accesos que hagamos a los servicios web de
Amazon).
Una vez realizada una consulta para obtener los datos del libro cuyo ISBN
hayamos especificado, actualizamos la interfaz de usuario de acuerdo con
los datos ledos (URL de imagen de la portada, ttulo del libro, autor/es y
precio).
Por ltimo, no podemos olvidarnos de mantener el estado actual del carrito
de la compra de cada usuario como una variable de sesin, para lo cual
utilizamos la coleccin Session.
protected void Page_Load(object sender, EventArgs e)
{
// ISBN del libro

isbn = (Request["isbn"] == null) ? String.Empty :
Request["isbn"];

if (isbn == String.Empty)
isbn = "0321113594";

// Servicio web de Amazon

service = new AWSECommerceService();

// Solicitud

ItemSearchRequest request = new
ItemSearchRequest();

// Parmetros de la bsqueda

request.SearchIndex = "Books";
request.Power = "ISBN: " + isbn;

request.MerchantId = "Amazon"; // ofertas slo de
Amazon.com

// Resultados deseados

request.ResponseGroup = new string[] { "ItemAttributes",
"Images", "Offers" };

// Bsqueda de artculos

ItemSearch search = new ItemSearch();

search.AssociateTag = "ikorbooks-20";
search.AWSAccessKeyId =
"0PDW1BZN4N11TAXXS6G2";
search.Request = new ItemSearchRequest[] { request };

// Ejecucin de la consulta

ItemSearchResponse response =
service.ItemSearch(search);

// Resultado de la consulta

Item book = response.Items[0].Item[0];


// Datos del libro

ImagenLibro.ImageUrl = book.MediumImage.URL;
LabelTitulo.Text = book.ItemAttributes.Title;

int i;
int autores = (book.ItemAttributes.Author != null) ?
book.ItemAttributes.Author.Length : 0;

LabelAutor.Text = "";

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

LabelAutor.Text += book.ItemAttributes.Author[i];

if (i < autores - 2) {
LabelAutor.Text += ", ";
} else if (i == autores - 2) {
LabelAutor.Text += " & ";
}
}

LabelPrecio.Text = "Precio: "
+
book.Offers.Offer[0].OfferListing[0].Price.FormattedPrice;
// + book.ItemAttributes.ListPrice.FormattedPrice;


// Carrito de la compra

cart = (Cart)Session["cart"];

if (cart != null)
LabelCompra.Text = "Tiene " +
cart.CartItems.CartItem.Length
+ " libros distintos en su cesta de la
compra...";
}
Los accesos a los servicios web de Amazon, en su versin actual, siempre
funcionan de la misma forma:
Para acceder al servicio X, en primer lugar, hemos de preparar la solicitud
adecuada, con nombre XRequest.
A continuacin, creamos un objeto de tipo X, que usamos para
identificarnos (con AssociateTag y AWSAccessKeyId) y acceder al servicio
X pasndole como parmetro los objetos de tipo XRequest que representan
nuestras solicitudes (aunque no lo hayamos hecho, podramos ejecutar
varias a la vez).
La ejecucin de la consulta nos da como resultado un objeto de tipo
XResponse, que contiene las colecciones de Items obtenidas como
respuesta a nuestras solicitudes de tipo XRequest.
En el caso de la bsqueda de productos en el catlogo de Amazon (la operacin
ItemSearch ofrecida como parte de la interfaz pblica del servicio A2S [Amazon
Associates Service]), podemos realizar bsquedas de distintos tipos: por ttulo, por
autor, por temas... En este caso, hemos optado por realizar la bsqueda de un
libro concreto usando su ISBN, usando "power search" (propiedad Power) en el
ndice "Books" (propiedad SearchIndex).
Tambin hemos de especificar qu informacin nos interesa acerca del libro que
estamos buscando. Para ello, existen diferentes grupos de respuestas
predefinidas que se han de seleccionar mediante el parmetro RequestGroup de
nuestra solicitud. Si slo quisiramos informacin bsica acerca del libro (ttulo,
autores, ISBN, etc.), nos bastara con indicar "ItemAttributes". Como queremos
mostrar una imagen de la portada del libro en nuestra aplicacin, usamos adems
el grupo "Images". Por ltimo, como tambin queremos mostrar informacin
relativa al precio real del libro, no simplemente su precio de catlogo, recurrimos al
grupo "Offers".
Amazon, adems de comercializar sus productos, permite que sus asociados
vendan sus productos a travs de Amazon Marketplace, motivo por el que
podemos encontrarnos diversas ofertas para un mismo producto. Esto, entre otras
muchas cosas, nos permite comprar libros usados a un precio muy reducido (y en
excelente estado), una costumbre muy habitual en pases como EE.UU..
En nuestras aplicacin, no obstante, hemos restringido la bsqueda a productos
proporcionados directamente por Amazon (de ah lo de request.MerchantId =
"Amazon") para poder averiguar fcilmente el precio real al que Amazon vende
cada producto:
book.ItemAttributes.ListPrice nos da el precio de catlogo del producto.
book.OfferSummary.LowestNewPrice nos devuelve el precio ms barato al
que podemos comprar el producto nuevo.
book.OfferSummary.LowestUsedPrice nos indica el precio ms barato al
que podemos comprar el producto (usado).
book.Offers.Offer[0].OfferListing[0].Price nos da el precio del producto
ofertado por Amazon al haber restringido la bsqueda con MerchantId =
"Amazon". Si no lo hubisemos hecho, Offers podra incluir ofertas de ms
proveedores diferentes.
Realizacin de pedidos
An nos falta por implementar la parte de nuesta aplicacin que le permitir al
usuario realizar sus compras a travs de nuestra aplicacin.
En primer lugar, hemos de implementar el cdigo necesario para que el usuario
aada un libro a su cesta de la compra cuando pulse sobre el botn
correspondiente, para lo cual necesitamos de nuevo acceder al servicio web de
Amazon, usando la operacin CartCreate cuando el usuario selecciona un
producto por primera vez y la operacin CartAdd cuando el usuario ya tiene un
carrito de la compra al que quiere aadirle nuevos productos.
Lo primero que hacemos es comprobar si el usuario ya tiene una cesta de
la compra. Si no la tiene, la creamos con CartCreate y, si ya la tiene, todas
nuestras solicitudes de tipo CartAdd deben incluir el identificador de su
cesta de la compra (CartId) y un cdigo de autentificacin complementario
(HMAC, keyed-Hash Message Authentication Code).
Como siempre, en cada solicitud que hagamos a un servicio web de
Amazon, debemos acordarnos de identificarnos (mediante las propiedades
AssociateTag y AWSAccessKeyId de la operacin correspondiente).
Por ltimo, cada vez que el usuario realice una operacin sobre su cesta de
la compra, tenemos que actualizar adecuadamente la interfaz de usuario de
nuestra aplicacin para que sta muestre el estado de la compra del
usuario (y guardar el carrito de la compra del usuario como una variable de
sesin).
protected void Button_Click(object sender, EventArgs e)
{
if (cart == null) {

CartCreateRequestItem cartCreateRequestItem = new
CartCreateRequestItem();

cartCreateRequestItem.ASIN = isbn;
cartCreateRequestItem.Quantity = "1";
cartCreateRequestItem.AssociateTag = "ikorbooks-
20";

CartCreateRequest request = new
CartCreateRequest();

request.Items = new CartCreateRequestItem[] {
cartCreateRequestItem };

CartCreate create = new CartCreate();

create.AssociateTag = "ikorbooks-20";
create.AWSAccessKeyId =
"0PDW1BZN4N11TAXXS6G2";
create.Request = new CartCreateRequest[] { request
};

CartCreateResponse response =
service.CartCreate(create);

cart = response.Cart[0];

} else {

CartAddRequestItem cartAddRequestItem = new
CartAddRequestItem();

cartAddRequestItem.ASIN = isbn;
cartAddRequestItem.Quantity = "1";
cartAddRequestItem.AssociateTag = "ikorbooks-20";

CartAddRequest request = new CartAddRequest();

request.Items = new CartAddRequestItem[] {
cartAddRequestItem };
request.CartId = cart.CartId;
request.HMAC = cart.HMAC;

CartAdd add = new CartAdd();

add.AssociateTag = "ikorbooks-20";
add.AWSAccessKeyId =
"0PDW1BZN4N11TAXXS6G2";
add.Request = new CartAddRequest[] { request };

CartAddResponse response = service.CartAdd(add);

cart = response.Cart[0];
}


if (cart != null) {
Session["cart"] = cart;
LabelCompra.Text = "Tiene " +
cart.CartItems.CartItem.Length
+ " libros en su cesta de la compra...";
}

}
Finalmente, para que nuestra aplicacin de comercio electrnico sea operativa,
slo nos falta dejar que el usuario pueda pasar por caja cuando lo desea, proceso
del cual se encargar Amazon en la URL a la que redireccionamos a nuestro
cliente:
protected void ButtonCheckOut_Click(object sender,
EventArgs e)
{
if (cart != null && cart.CartItems.CartItem.Length > 0) {
Response.Redirect(cart.PurchaseURL);
} else {
LabelCompra.Text = "No tiene ningn artculo en su
cesta de la compra";
LabelCompra.ForeColor =
System.Drawing.Color.Red;
}
}
Y con esto ya tenemos una atractiva pgina que le permite al usuario comprar sus
libros favoritos por Internet:

Cdigo fuente del cliente de Amazon (aplicacin web para Visual Studio 2005)
Cdigo fuente del cliente de Amazon (aplicacin web para Visual Studio 2008)
Servicios web de Amazon
En 2008, Amazon reemplaz su servicio web de comercio
electrnico ECS (Electronic Commerce Service) por A2S
(Amazon Associates Service), que corresponde a la versin 4
de ECS. Se puede obtener ms informacin acerca de este y
otros servicios web de Amazon en la siguiente URL:
http://aws.amazon.com/.
Diseo de arquitecturas software
Arquitecturas multicapa

La divisin de un sistema en distintas capas o niveles de abstraccin es una de las
tcnicas ms comunes empleadas por los ingenieros para construir sistemas
complejos.
Esta divisin se puede apreciar en el hardware, donde el diseo de un sistema en
un lenguaje de alto nivel como VHDL o Verilog se traduce en un diseo a nivel de
registros lgicos (RTL); ste se implementa mediante puertas lgicas, a partir de
las cuales se obtiene un diseo a nivel de transistores; los transistores, finalmente,
se crean en un circuito integrado con una serie de mscaras.
Los protocolos de red tambin se disean utilizando distintas capas: la capa de
aplicacin (HTTP) utiliza los servicios de la capa de transporte (TCP), la cual se
implementa sobre la capa de red (IP) y as sucesivamente hasta llegar a la
transmisin fsica de los datos a travs de algn medio de transmisin.
En realidad, el uso de capas es una forma ms de la tcnica de resolucin de
problemas conocida con el nombre de "divide y vencers", que se basa en
descomponer un problema complejo en una serie de problemas ms sencillos de
forma que se pueda obtener la solucin al problema complejo a partir de las
soluciones a los problemas ms sencillos. Al dividir un sistema en capas, cada
capa puede tratarse de forma independiente (sin tener que conocer los detalles de
las dems).
Desde el punto de vista de la Ingeniera del Software, la divisin de un sistema en
capas facilita el diseo modular (cada capa encapsula un aspecto concreto del
sistema) y permite la construccin de sistemas dbilmente acoplados (si
minimizamos las dependencias entre capas, resultar ms fcil sustituir la
implementacin de una capa sin afectar al resto del sistema). Adems, el uso de
capas tambin fomenta la reutilizacin (p.ej. TCP/IP se utiliza en una amplia
variedad de aplicaciones, desde HTTP y FTP hasta telnet y SSH).
Capas en el diseo de software
Como es lgico, la parte ms difcil en la construccin de un sistema multicapa es
decidir cuntas capas utilizar y qu responsabilidades asignarle a cada capa.
En las arquiecturas cliente/servidor se suelen utilizar dos capas. En el caso de
las aplicaciones informticas de gestin, esto se suele traducir en un servidor de
bases de datos en el que se almacenan los datos y una aplicacin cliente que
contiene la interfaz de usuario y la lgica de la aplicacin.
El problema con esta descomposicin es que la lgica de la aplicacin suele
acabar mezclada con los detalles de la interfaz de usuario, dificultando las tareas
de mantenimiento a que todo software se ve sometido y destruyendo casi por
completo la portabilidad del sistema, que queda ligado de por vida a la plataforma
para la que se dise su interfaz en un primer momento.
Mantener la misma arquitectura y pasar la lgica de la aplicacin al servidor
tampoco resulta una solucin demasiado acertada. Se puede implementar la
lgica de la aplicacin utilizando procedimientos almacenados, pero stos suelen
tener que implementarse en lenguajes estructurados no demasiado verstiles.
Adems, suelen ser lenguajes especficos para cada tipo de base de datos, por lo
que la portabilidad del sistema se ve gravemente afectada.
La solucin, por tanto, pasa por crear nueva capa en la que se separe la lgica de
la aplicacin de la interfaz de usuario y del mecanismo utilizado para el
almacenamiento de datos. El sistema resultante tiene tres capas:
La capa de presentacin, encargada de interactuar con el usuario
de la aplicacin mediante una interfaz de usuario (ya sea una interfaz
web, una interfaz Windows o una interfaz en lnea de comandos,
aunque esto tlimo suele ser menos habitual en la actualidad).
La lgica de la aplicacin [a la que se suele hacer referencia como
business logic o domain logic], usualmente implementada utilizando
un modelo orientado a objetos del dominio de la aplicacin, es la
responsable de realizar las tareas para las cuales se disea el
sistema.
La capa de acceso a los datos, encargada de gestionar el
almacenamiento de los datos, generalmente en un sistema gestor de
bases de datos relacionales, y de la comunicacin del sistema con
cualquier otro sistema que realice tareas auxiliares (p.ej.
middleware).
Cuando el usuario del sistema no es un usuario humano, se hace evidente la
similitud entre las capas de presentacin y de acceso a los datos. Teniendo esto
en cuenta, el sistema puede verse como un ncleo (lgica de la aplicacin) en
torno al cual se crean una serie de interfaces con entidades externas. Esta vista
simtrica del sistema es la base de la arquitectura hexagonal de Alistair Cockburn.
No obstante, aunque slo fuese por las peculiaridades del diseo de interfaces de
usuario, resulta til mantener la vista asimtrica del software como un sistema
formado por tres capas. Adems, suele ser recomendable diferenciar lo que se
suministra (presentacin) de lo que se consume (acceso a los servicios
suministrados por otros sistemas).
A pesar del atractivo de esta arquitectura con tres capas (basta con pensar lo que
facilitara la conversin de aplicaciones Windows en aplicaciones web), esta
arquitectura no se ha impuesto del todo porque las herramientas de desarrollo
suelen estar diseadas para construir aplicaciones cliente/servidor ligadas a algn
productor de software. De hecho, puede resultar difcil (e incluso imposible)
descomponer un sistema en tres capas con determinadas herramientas de
desarrollo.
En el caso de la plataforma .NET, el aspecto final de una arquitectura con tres
capas sera algo as como muestra la siguiente figura:

LAYER vs. TIER
Aunque ambos trminos se traduzcan igual en castellano, el
trmino tier suele hacer referencia a las distintas capas
utilizadas en el despliegue de una aplicacin a nivel fsico,
mientras que el trmino layer se emplea de un modo ms
abstracto para hacer referencia a la descomposicin de
cualquier sistema (aunque ste se implemente en un nico
tier).
Referencias
Martin Fowler et al.: Patterns of Enterprise Application Architecture.
Addison-Wesley, 2003. ISBN 0-321-12742-0.
Frank Buschmann, Regine Meunier, Hans Rohnert, Peter Sommerlad
& Michael Stal: Pattern-Oriented Software Architecture. Volume 1.
John Wiley & Sons, 1996. ISBN 0-471-95869-7.
Refactorizacin & Pruebas de unidad

Organizacin de la lgica de la aplicacin
Una vez que hemos separado el ncleo de la aplicacin de sus distintos
interfaces, an nos queda por decidir cmo vamos a organizar la implementacin
de la lgica asociada a la aplicacin. Como en cualquier otra tarea de diseo,
tenemos que llegar a un compromiso adecuado entre distintos intereses. Por un
lado, nos gustara que el diseo resultante fuese lo ms sencillo posible. Por otro
lado, sera deseable que nuestro diseo estuviese bien preparado para soportar
las modificaciones que hayan de realizarse en el futuro.
Por lo general, el diseo de la lgica una aplicacin se suele ajustar a uno de los
tres siguientes patrones de diseo:
Rutinas: La forma ms simple de implementar cualquier sistema se
basa en implementar procedimientos y funciones que acepten y
validen las entradas recibidas de la capa de presentacin, realicen
los clculos necesarios, utilicen los servicios de aquellos sistemas
que hagan falta para completar la operacin, almacenen los datos en
las bases de datos y enven una respuesta adecuada al usuario.
Bsicamente, cada accin que el usuario pueda realizar se traducir
en un procedimiento que realizar todo lo que sea necesario al ms
puro estilo del diseo estructurado tradicional. Aunque este modelo
sea simple y pueda resultar adecuado a pequea escala, la
evolucin de las aplicaciones diseadas de esta forma suele acabar
siendo una pesadilla para las personas encargadas de su
mantenimiento.
Mdulos de datos: Ante la situacin descrita en el prrafo anterior,
lo usual es dividir el sistema utilizando los distintos conjuntos de
datos con los que trabaja la aplicacin para crear mdulos ms o
menos independientes. De esta forma, se facilita la eliminacin de
lgica duplicada. De hecho, muchos de los entornos de desarrollo
visual de aplicaciones permiten definir mdulos de datos que
encapsulen los conjuntos de datos con los que se trabaja y la lgica
asociada a ellos. Las herramientas de Borland, Delphi y C++Builder,
son un claro ejemplo. Microsoft, en su arquitectura DNA [Distributed
interNet Application], fomenta este estilo al emplear conjuntos de
datos (resultado de ejecutar consultas SQL) sobre los cuales operan
directamente las distintas capas de una aplicacin multicapa. En el
caso de la plataforma .NET, la clase DataSet proporciona la base
sobre la que se montara todo el diseo de una aplicacin (algo que
obviamente facilita el Visual Studio .NET).
Modelo del dominio: Una tercera opcin, la ideal para cualquier
purista de la orientacin a objetos, es crear un modelo orientado a
objetos del dominio de la aplicacin. En vez de que una rutina se
encargue de todo lo que haya que hacer para completar una accin,
cada objeto es responsable de realizar las tareas que le ataen
directamente.
En la prctica, no todo es blanco o negro. Aunque empleemos un modelo
orientado a objetos del dominio de la aplicacin, es habitual crear una capa
intermedia entre la capa de presentacin y la lgica de la aplicacin a la que se
suele denominar capa de servicio. La interfaz de la capa de servicio incluir
mtodos asociados a las distintas acciones que pueda realizar el usuario, si bien,
en vez de incluir en ella la lgica de la aplicacin, la capa de servicio delega
inmediatamente en los objetos responsables de cada tarea. En cierto modo, la
capa de servicio se encarga de la lgica especfica de la aplicacin, dejando para
el modelo del dominio la lgica del dominio (comn a cualquier aplicacin que se
construya sobre el mismo dominio de aplicacin).
Cualquier aplicacin sufre modificaciones de mayor o menor importancia a lo largo
de su vida til. Dichas modificaciones alteran el diseo inicial de la aplicacin y
tienden a aumentar su entropa. Si utilizamos rutinas para implementar la lgica de
nuestra aplicacin en el sentido tradicional, las modificaciones pueden suponer
tener que revisar el cdigo completo de la aplicacin para descubrir lo que hay que
actualizar. Es como si tuvisemos una habitacin completamente desordenada en
la que hay que encontrar algo en particular (y ya sabemos lo que las madres
suelen decir acerca de eso). En el caso de los mdulos de datos, el impacto de las
modificaciones suele ser ms fcil de determinar pero, an as, podemos
encontrarnos con sorpresas desagradables si todos los mdulos de nuestra
aplicacin trabajan sobre un conjunto de datos cuya estructura debemos alterar
ligeramente. Por ltimo, si diseamos un buen modelo orientado a objetos, la
encapsulacin proporcionada por los objetos de nuestra aplicacin permitir que el
impacto de las modificaciones sea de carcter local en la mayora de las
ocasiones. En cierto modo, estamos limitando el aumento de la entropa al interior
de los cajones (algo que la mayora de nosotros tolera sin demasiados
problemas). Para llegar a este punto, no obstante, primero hemos de ser capaces
de crear un buen modelo orientado a objetos...
Buenas costumbres en el desarrollo de software
En Ingeniera del Software se han identificado distintas prcticas que suelen
considerarse muy tiles en el desarrollo de aplicaciones. A continuacin veremos
algunas que nos ayudarn a mejorar nuestros hbitos en el desarrollo de software:
Patrones de diseo
Los patrones de diseo describen soluciones elegantes a problemas especficos
que se repiten en muchas aplicaciones diferentes. Estas soluciones puede que
requieran algo ms de esfuerzo que una solucin ad hoc del problema que
tengamos entre manos, pero este esfuerzo se ver recompensado con creces si
nos permite construir aplicaciones ms flexibles y fciles de mantener.
Adems, la existencia de catlogos de patrones nos permite aprovechar la
experiencia de otros diseadores y mejorar nuestras habilidades en el diseo de
software. En estos catlogos, los patrones se representan en un formato estndar
(problema-solucin-consecuencias) que codifica de una forma digerible las
soluciones a problemas recurrentes que diseadores expertos han descubierto en
un contexto dado.
Los patrones de diseo nos permiten reutilizar buenos diseos, hacer explcito el
conocimiento del diseo de software (mediante abstracciones de las soluciones
encontradas en buenos diseos) y amplan nuestro vocabulario (usualmente, a
cada patrn de diseo se le asocia un nombre con el que hacerle referencia). En
otras palabras, los patrones de diseo nos permiten, no slo mejorar nuestros
diseos, sino ampliar nuestras expectativas de aprendizaje y nuestra capacidad de
comunicacin con otros diseadores.
Refactorizacin
Una refactorizacin [refactoring] es un cambio realizado a la estructura interna del
software sin modificar su comportamiento observable desde el exterior. Los
cambios de este tipo pueden ser tiles si hacen que el software sea ms fcil de
comprender y de modificar cuando hemos de adaptarlo a nuevas necesidades.
Refactorizar es reestructurar el software aplicando una serie de refactorizaciones
sin modificar su comportamiento. Cuando se realiza esta tarea de forma
adecuada, lo que estamos haciendo es mejorar la calidad de nuestros diseos
(para lo cual muchas veces echaremos mano de patrones de diseo). En otras
palabras, aunque no estemos aadindole funciones al programa (su valor actual),
estamos facilitando su desarrollo futuro.
La refactorizacin no es una actividad ms a la que se le reserva un espacio en la
planificacin de un proyecto, sino que es algo que se va haciendo sobre la marcha
conforme hace falta. Refactorizar es extremadamente til ya que nos permite
eliminar redundancias cuando se encuentra lgica duplicada en una aplicacin.
Tambin puede ser til cuando tenemos que aadir funcionalidad a una aplicacin,
pues nos sirve de paso inicial de preparacin para que el cdigo resulte ms fcil
de entender y sea ms fcil aadir la funcionalidad deseada a la aplicacin.
Incluso puede resultar til cuando estamos intentando depurar nuestra aplicacin:
si la hacemos ms fcil de entender, ser ms fcil encontrar ese molesto error
que se nos resiste.
Pruebas de unidad
Cuando se implementa software, resulta recomendable comprobar que el cdigo
que hemos escrito funciona correctamente. Para ello implementamos tests que
verifican que nuestro programa genera los resultados que de l esperamos.
Conforme vamos aadindole nueva funcionalidad a nuestras aplicaciones,
creamos nuevos tests con los que podemos medir nuestros progresos y
comprobar que lo que antes funcionaba sigue funcionando (test de regresin). Las
pruebas de unidad tambin son de vital importancia cuando refactorizamos:
aunque no aadimos nueva funcionalidad, estamos modificando la estructura
interna de nuestro programa y debemos comprobar que no introducimos errores.
Las pruebas de unidad son, por tanto, muy importantes en el desarrollo de
software.
Para agilizar las pruebas resulta recomendable que un test sea completamente
automtico y compruebe los resultados esperados. No es muy apropiado llamar a
una funcin, guardar el resultado en algn sitio y despus tener que comprobar
manualmente si el resultado era el deseado. En la prctica, mantener
automatizado un conjunto amplio de tests permite reducir el tiempo que se tarda
en depurar errores y en verificar la correccin del cdigo. De hecho, existen
herramientas especialmente diseadas para implementar y automatizar la
realizacin de pruebas de unidad (NUnit en la plataforma .NET y JUnit en Java,
por ejemplo).
Incluso hay quien defiende que, antes de comenzar a escribir cdigo se deben
implementar los tests que nos permitirn comprobar que el cdigo funciona
correctamente [TDD: Test-Driven Development]. Aunque a primera vista pueda
parecer extrao, de esta forma nos podemos centrar mas fcilmente en qu
necesitamos hacer para que nuestra aplicacin funcione, hacemos hincapi en la
interfaz de nuestro sistema antes que en su implementacin (algo siempre bueno)
y definimos claramente cundo termina nuestro trabajo (cuando se pasan con
xito todos los tests).
Gestin de la configuracin del software
La gestin de la configuracin del software [SCM: Software Configuration
Management], ms conocida vulgarmente por uno de sus aspectos, el control de
versiones, es una actividad clave en la gestin del proceso de desarrollo de
software. De hecho, aparece como rea clave para obtener el nivel 2 del CMM
[Capability Maturity Model], modelo en el cual el nivel 1 representa la anarqua en
el proceso de desarrollo de software.
Independientemente de su importancia en el control del proceso de desarrollo de
software (algo innegable) y de su utilidad a la hora de evitar prdidas irreparables
(siempre y cuando se hagan copias de seguridad de todos los componentes del
software), las herramientas de control de versiones son esenciales para que
podamos refactorizar sin piedad el cdigo sobre el que estemos trabajando en
cada momento. Si, por cualquier motivo, al terminar las modificaciones que
hagamos no obtenemos lo que esperbamos, siempre podemos volver
cmodamente a una versin anterior de nuestro cdigo y cambiar de estrategia sin
haber perdido nada, ya que el tiempo empleado no se habr perdido del todo si
nos ha servido para aprender algo.
Bibliografa de inters
Frank Buschmann, Regine Meunier, Hans Rohnert, Peter Sommerlad
& Michael Stal: Pattern-Oriented Software Architecture. Volume 1.
John Wiley & Sons, 1996. ISBN 0-471-95869-7.
Alistair Cockburn: Agile Software Development. Addison-Wesley,
2002. ISBN 0-201-69969-9.
Martin Fowler et al.: Refactoring: Improving the design of existing
code. Addison-Wesley, 2000. ISBN 0-201-48567-2.
Martin Fowler et al.: Patterns of Enterprise Application Architecture.
Addison-Wesley, 2003. ISBN 0-321-12742-0.
Erich Gamma, Richard Helm, Ralph Johnson & John Vlissides:
Design Patterns: Elements of reusable object-oriented software.
Addison-Wesley, 1994. ISBN 0-201-63361-2.
Robert L. Glass: Facts and Fallacies of Software Engineering.
Addison-Wesley, October 2002. ISBN 0-321-11742-5.
Caso prctico: Biblioteca digital
En esta seccin, intentaremos mostrar la importancia de las tcnicas descritas en
el desarrollo de software de calidad mediante un ejemplo concreto, aun a riesgo
de que la simplicidad del mismo haga parecer triviales y de poco valor los pasos
que iremos dando. Es de recibo resaltar que, en un proyecto real, la utilidad y la
importancia de las tcnicas empleadas crece de forma proporcional a la dimensin
del problema.
Diseo inicial
Tomaremos como ejemplo ilustrativo el diseo de una biblioteca digital. En dicha
biblioteca los documentos se clasifican por categoras. stas categoras se
organizan de forma jerrquica. Adems, permitimos que los usuarios de la
biblioteca escriban sus propios comentarios acerca de los documentos de la
biblioteca y evalen stos en funcin de sus gustos.
Dados los requerimientos del problema, un diseo inicial de las clases de nuestro
sistema podra ser como el siguiente:

La clase Document sirve para representar los documentos del
catlogo de la biblioteca, los cuales se identifican por un cdigo
alfanumrico (asin).
La clase Author es una clase auxiliar empleada para mantener
informacin acerca de los autores de los documentos.
La clase Category se emplea para clasificar los documentos por
temas, pudiendo establecer jerarquas de categoras gracias a la
asociacin involutiva que aparece en el diagrama.
La clase Review nos permite almacenar comentarios y crticas
relativos a los documentos de la bibliteca.
Dichos comentarios los podrn escribir los usuarios registrados de
nuestra biblioteca (representados por la clase Reviewer).
A partir del diagrama anterior, podemos escribir el esqueleto del cdigo en C# de
las cinco clases principales que inicialmente formarn parte de nuestro sistema.
Para ello, creamos una biblioteca de clases llamada DigitalLibrary.dll. Aunque
podramos directamente crear clases con atributos pblicos, esto no resulta
demasiado correcto para mantener la encapsulacin de los distintos mdulos de
nuestro sistema, por lo que crearemos variables de instancia privadas y
propiedades pblicas para acceder a ellas (sin olvidar los comentarios XML para
documentar nuestro cdigo, que aqu omitimos por brevedad).
Comenzamos por la clase ms simple:
public class Author
{
private string name;

public Author (string name)
{
this.name = name;
}

public string Name {
get { return name; }
}
}
La clase Category es algo ms compleja, pues debemos mantener la jerarqua de
categoras de una forma apropiada, para lo cual utilizamos la clase ArrayList del
espacio de nombres System.Collections:
public class Category
{
private string description;
private ArrayList super;
private ArrayList sub;

public Category (string description)
{
this.description = description;
this.super = new ArrayList();
this.sub = new ArrayList();
}

public string Description {
get { return description; }
}

public Category[] SuperCategories {
get { return (Category[]) super.ToArray (); }
}

public Category[] SubCategories {
get { return (Category[]) sub.ToArray(); }
}

public bool isSuperCategory (Category category) {
return super.Contains(category);
}

public bool isSubCategory (Category category) {
return sub.Contains(category);
}

public void AddSuperCategory (Category category) {
super.Add(category);
category.sub.Add(this);
}

public void RemoveSuperCategory (Category category) {
super.Remove(category);
category.sub.Remove(this);
}

public void AddSubCategory (Category category) {
category.AddSuperCategory(this);
}

public void RemoveSubCategory (Category category) {
category.RemoveSuperCategory(this);
}
}
A partir de las dos clases anteriores es casi inmediata la implementacin de la
clase Document, la clase central de nuestra biblioteca digital:
public class Document
{
private string asin;
private string title;
private string source;
private ArrayList authors;
private ArrayList categories;


public Document(string asin, string title)
{
this.asin = asin;
this.title = title;
this.authors = new ArrayList();
this.categories = new ArrayList();
}


public string ASIN {
get { return asin; }
}

public string Title {
get { return title; }
set { title = value; }
}

public string Source {
get { return source; }
set { source = value; }
}

// Autores

public Author[] Authors {
get { return (Author[]) authors.ToArray( typeof(Author) ); }
}

public bool isAuthor (Author author) {
return authors.Contains(author);
}

public void AddAuthor (Author author) {
authors.Add (author);
}

public void RemoveAuthor (Author author) {
authors.Remove (author);
}

// Categoras

public Category[] Categories {
get { return (Category[]) categories.ToArray(
typeof(Category) ); }
}

public bool isCategory (Category category) {
return categories.Contains(category);
}

public void AddCategory (Category category) {
categories.Add (category);
}

public void RemoveCategory (Category category) {
categories.Remove(category);
}
}
A continuacin, implementamos la clase Reviewer, que sirve para modelar a los
usuarios registrados del sistema, aqullos que pueden escribir comentarios acerca
de los documentos de nuestra biblioteca digital:
public class Reviewer
{
private string login;

public Reviewer (string login)
{
this.login = login;
}

public string Login {
get { return login; }
}

public void changePassword (string oldPassword, string
newPassword)
{
if ( checkPassword(oldPassword) ) {
Password = newPassword;
} else {
throw new SecurityException("Clave incorrecta");
}
}

// Gestin de la clave de acceso

public bool checkPassword (string password) {
return true; // Aqu se comprobara si la clave es correcta
}

private string Password {
set { } // Aqu se establecera la clave del usuario
}
}

public class SecurityException :
System.ApplicationException {
public SecurityException(string msg) :base(msg) { }
}
Los aspectos ms destacables de la clase anterior son, en primer lugar, la forma
de trabajar con las claves de acceso, que nunca se deben almacenar sin encriptar,
y, en segundo lugar, el uso de excepciones para indicar la ocurrencia de errores.
Finalmente, slo nos queda por implementar la clase Review, que inicialmente
ser una clase inmutable:
public class Review
{
private string text;
private Reviewer reviewer;
private Document document;
private DateTime date;
private Rating rating;

public Review
(Reviewer reviewer, Document document, string text,
Rating rating)
{
this.reviewer = reviewer;
this.document = document;
this.text = text;
this.date = DateTime.Now;
this.rating = rating;
}

public Reviewer Reviewer {
get { return reviewer; }
}

public Document Document {
get { return document; }
}

public string Text {
get { return text; }
}

public DateTime Date {
get { return date; }
}

public Rating Rating {
get { return rating; }
}
}

/// Posibles evaluaciones

public enum Rating {
Excellent,
Good,
Fair,
Bad
}
En este caso, hemos definido una enumeracin para especificar el conjunto de
valores permitido al evaluar un documento.
Implementacin inicial de las clases de la biblioteca digital (Visual Studio 2003)
Implementacin inicial de las clases de la biblioteca digital (Visual Studio 2005)
Importante
Obsrvense las distintas estrategias que se han utilizado para
implementar en C# las diferentes asociaciones que aparecen
en el diagrama de clases, especialmente la asociacin
bidireccional que nos sirve para definir jerarquas de
categoras.
Pruebas de unidad
Una vez que tenemos implementadas las clases que servirn de base a nuestra
biblioteca digital, resulta recomendable que escribamos algunas pruebas de
unidad. stas nos servirn para comprobar que nuestra aplicacin funciona
correctamente y, conforme vayamos aadindole nuevas funciones, las pruebas
de unidad nos servirn de comprobacin de que todo sigue funcionando
correctamente.
Para implementar las pruebas de unidad, resulta recomendable utilizar alguna
heramienta tipo xUnit. Dichas herramientas estn diseads para facilitarnos la
automatizacin de las pruebas. En el caso de la plataforma .NET, la utilidad se
denomina NUnit y la podemos descargar gratuitamente de http://www.nunit.org.
Para implemetar pruebas de unidad con NUnit, creamos una nueva biblioteca de
clases en la solucin correspondiente a nuestra aplicacin. Por convencin, su
nombre estar formado por el nombre de la biblioteca que estemos probando
seguido de Test. Es este caso, la biblioteca que albergar los tests ser, por tanto,
DigitalLibraryTest.dll. Dicha biblioteca incluir una referencia a la biblioteca de
clases donde tenemos implementadas las clases de nuestra aplicacin:

As mismo, tambin deberemos incluir una referencia a la DLL donde estn
definidos los atributos utilizados por NUnit, los cuales veremos cmo funcionan en
unos momentos. Seleccionamos la opcin "Agregar referencia..." y, a
continuacin, pinchamos sobre el botn "Examinar...". Hemos de localizar la DLL
denominada nunit.framework.dll que se halla en el subdirectorio bin del directorio
donde hayamos instalado NUnit (usualmente, en C:\Archivos de
Programa\NUnit...):

Una vez aadidas las dos referencias (al proyecto DigitalLibrary.dll y a la biblioteca
nunit.framework.dll), podemos comenzar a escribir nuestras primeras pruebas de
unidad:

using System;
using NUnit.Framework;

namespace DigitalLibrary
{
[TestFixture]
public class CategoryTest
{
[Test]
public void HierarchyTest ()
{
Category software = new Category("Software");
Category lenguajes = new Category("Lenguajes de
programacin");

software.AddSubCategory(lenguajes);

Assert.AreEqual (0, software.SuperCategories.Length );
Assert.AreEqual (1, software.SubCategories.Length );
}
}
}
Nota
En versiones anteriores de NUnit, en vez de utilizar el mtodo
Assert.AreEqual, habramos escrito Assertion.AssertEquals.
Del mismo modo, mientras que antes escribiramos
Assertion.Assert, ahora usaremos el mtodo Assert.IsTrue.
Las clases que implementan pruebas de unidad con NUnit deben tener el atributo
[TestFixture] y deben ser pblicas (para poder acceder a ellas desde fuera y poder
ejecutar las pruebas de unidad).
Cada prueba de unidad es un mtodo etiquetado con el atributo [Test]. Dicho
mtodo ha de ser necesariamente un procedimiento (sin parmetros ni valor de
retorno). Usualmente, dichos mtodos se encargarn de inicializar un conjunto de
objetos, invocar algunos de sus mtodos y, finalmente, comprobar el estado de los
objetos mediante aserciones utilizando la clase NUnit.Framework.Assertion.
Para ejecutar las pruebas de unidad, podemos utilizar la utilidad NUnit-Gui que
viene incluida en la distribucin de NUnit:

Su funcionamiento es muy sencillo. Slo tenemos que abrir el fichero que contiene
nuestras pruebas de unidad (men "File", opcin "Open...") y pulsar el botn
"Run". Sorprendentemente (o quiz no tanto), la simple prueba nos da un error:

NUnit nos muestra en rojo los nodos correspondientes a tests que no se han
ejecutado con xito. En los paneles de la derecha podemos buscar las posibles
causas de los errores. Analizando la situacin en la que se ha producido nuestro
error, nos podemos dar cuenta del origen del error: al implementar las propiedades
SuperCategories y SubCategories de la clase Category no tuvimos en cuenta que
deseamos obtener un vector de categoras (no de simples objetos), por lo que
debemos corregir la implementacin "errnea" de estas propiedades (y otras
anlogas). Aparte de corregir el error concreto, es deseable que corrijamos errores
similares all donde aparezcan, por lo que buscaremos los usos del mtodo
ToArray de la clase ArrayList y escribiremos algo similar a lo siguiente:
get
{
return (Category[]) XXX.ToArray ( typeof(Category) );
}
Una vez realizada la correccin, recompilamos y volvemos a ejecutar las pruebas
de unidad con NUnit. Ahora s obtenemos luz verde para seguir avanzando:

Es conveniente que completemos nuestros tests para asegurarnos de que nuestra
implementacin funciona correctamente. Por ejemplo, podemos comprobar mejor
el funcionamiento de nuestra clase Category construyendo una jerarqua simple.
Los siguientes tests nos dan una idea ms realista de los conjuntos de pruebas
tpicos en aplicaciones reales:
using System;
using NUnit.Framework;

namespace DigitalLibrary
{
///
/// Pruebas de unidad para la clase Category
///

[TestFixture]
public class CategoryTest
{
Category software;
Category lenguajes;
Category csharp;
Category delphi;
Category java;

[SetUp]
public void Init()
{
// Categoras
software = new Category("Software");
lenguajes = new Category("Lenguajes de programacin");
csharp = new Category("C#");
delphi = new Category("Delphi");
java = new Category("Java");

// Jerarqua
software.AddSubCategory(lenguajes);
lenguajes.AddSubCategory(csharp);
lenguajes.AddSubCategory(delphi);
lenguajes.AddSubCategory(java);
}

[Test]
public void TopHierarchyTest ()
{
Assert.AreEqual(0, software.SuperCategories.Length );
}

[Test]
public void SingleSubcategoryTest ()
{
Assert.AreEqual(1, software.SubCategories.Length );
Assert.AreEqual(lenguajes, software.SubCategories[0]);
}

[Test]
public void SingleSupercategoryTest ()
{
Assert.AreEqual(1, lenguajes.SuperCategories.Length);
Assert.AreEqual(software, lenguajes.SuperCategories[0]);
}

[Test]
public void MultipleSubcategoryTest ()
{
Assert.AreEqual(3, lenguajes.SubCategories.Length);
Assert.IsTrue (lenguajes.isSubCategory(csharp));
Assert.IsTrue (lenguajes.isSubCategory(delphi));
Assert.IsTrue (lenguajes.isSubCategory(java));
}

[Test]
public void BottomHierarchyTest()
{
Assert.AreEqual(0, csharp.SubCategories.Length);
Assert.AreEqual(0, delphi.SubCategories.Length );
Assert.AreEqual(0, java.SubCategories.Length );
}

[Test]
public void TransitivityTest ()
{
Assert.AreEqual( software,
csharp.SuperCategories[0].SuperCategories[0] );
}
}
}
La clase anterior muestra cmo podemos implementar un conjunto de tests a partir
de un conjunto de objetos cuya configuracin inicial establecemos en el mtodo
etiquetado con el atributo [SetUp].
Aunque no lo hayamos empleado, los distintos mtodos estticos de la clase
Assert incluyen la posibilidad de incluir un parmetro adicional de tipo cadena.
Dicho parmetro podemos utilizarlo para mostrar un mensaje informativo
adecuado si se produce un error al ejecutar el caso de prueba.

En determinados proyectos, puede sernos muy tiles la creacin de tests como los
anteriores para definir un criterio de aceptacin para nuestra implementacin,
adems de ofrecernos ciertas garantas de que nuestro cdigo hace lo que tiene
que hacer (recurdense los comentarios relativos a TDD [Test-Driven
Development]).
Obviamente, la ejecucin correcta de los casos de prueba no quiere decir que
nuestra aplicacin est libre de errores, slo que sta funciona adecuadamente
para los casos de prueba que hemos creado. Por tanto, cuanto ms exhaustivos
sean nuestros casos de prueba, ms confianza podremos tener en la correccin
de nuestra aplicacin.
Ejemplo de uso de NUnit (Visual Studio 2003, con Assertion)
Ejemplo de uso de NUnit (Visual Studio 2005, con Assert)
Control de versiones
Aparte de establecer un buen conjunto de pruebas de unidad que nos ayuden a
verificar la correccin de nuestro cdigo, resulta ms que aconsejable utilizar
alguna herramienta de control de versiones. De hecho, las herramientas de este
tipo son vitales en los proyectos que se realizan en equipo. Las herramientas de
control de versiones almacenan en una base de datos la evolucin de los distintos
archivos de nuestros proyectos para que podamos acceder, en cualquier
momento, a cualquier versin que haya existido de nuestros ficheros.
Al emplear una herramienta de control de versiones, podremos hacer tantos
cambios como deseemos en el cdigo sin temor a perder nada. Es ms, la
ausencia de una herramienta de control de versiones podra provocar que
actusemos de una forma excesivamente conservadora para no estropear nada
que ya funcionase y, de esa forma, perderamos gran parte de las ventajas que
ofrece tener un conjunto slido de tests a la hora de mejorar nuestro diseo (esto
es, refactorizar).
Herramientas de control de versiones existen muchsimas, algunas de las cuales
se integran perfectamente en el entorno de desarrollo. Por ejemplo, podemos
utilizar la herramienta Visual SourceSafe de Microsoft. Una vez que tengamos esta
herramienta instalada, lo nico que tenemos que hacer es seleccionar la opcin
"Agregar solucin al control de cdigo fuente...", la cual aparece en el men
contextual del "Explorador de soluciones" y en el submen "Control de cdigo
fuente" del men Archivo en la ventana principal del Visual Studio .NET:

Al activar el control de versiones para nuestra solucin, debemos acceder a una
de las bases de datos de nuestra herramienta de control de versiones, para lo cual
deberemos identificarnos correctamente:

A continuacin, debemos indicar dnde queremos almacenar los ficheros de
nuestro proyecto dentro de la estructura jerrquica de ficheros del repositorio de la
herramienta de control de versiones:

Una vez seleccionada la ubicacin de los archivos de nuestro proyecto dentro del
repositorio de la herramienta de control de versiones, las versiones actuales de
dichos archivos quedan almacenadas en la herramienta de control de versiones y
el Visual Studio .NET se encargar de interactuar con dicha herramienta cuando
haga falta. Los ficheros de nuestro proyecto estn protegidos a buen recaudo:

Si intentamos modificar alguno de los ficheros protegidos, el Visual Studio .NET
nos da la oportunidad de poder editarlos (operacin conocida como "check-out" en
la gestin de la configuracin del software):

En el Explorador de soluciones tambin podemos ver los ficheros sobre los que
estamos realizando modificaciones (por ejemplo, para aadir nuevos tests):

Cuando ya hemos efectuado las modificaciones pertinentes (y hemos comprobado
que las modificaciones efectuadas son correctas), guardamos las versiones
revisadas de nuestros ficheros en el repositorio de la herramienta de control de
versiones para que los dems integrantes de nuestro proyecto puedan utilizarlas.
Para realizar esta operacin, conocida como "check-in", basta con seleccionar la
opcin "Proteger..." del men contextual del Explorador de soluciones:

De esta forma nos aseguramos de que el resultado de nuestro trabajo no se
pierda:

Adems, seleccionando la opcin "Comparar..." (versiones) del men contextual
del Explorador de soluciones, podemos ver y analizar las diferencias existentes
entre distintas versiones de un fichero, a las cuales podemos hacer referencia en
Visual SourceSafe aadindole la terminacin ;v al nombre del fichero, donde v es
el nmero de la versin del fichero:

Refactorizacin...
Una vez que ya sabemos cmo crear un conjunto slido de pruebas de unidad y
tenemos nuestros ficheros almacenados de forma segura con una herramienta de
control de versiones, ya podemos atacar el cdigo sin piedad para ir aadindole
funcionalidad a nuestra aplicacin mejorando su diseo interno conforme nos vaya
haciendo falta.
Al aadir nuevas funciones, tambin crearemos nuevos tests que verfiquen el
funcionamiento correcto de lo que hayamos aadido. Al refactorizar (reestructurar
nuestro diseo para que ste resulte ms legible, modular, reutilizable, mantenible
o eficiente), utilizaremos los tests ya disponibles a modo de test de regresin para
comprobar que nuestras modificaciones no introducen errores donde no los haba.
O/R Mapping

Acceso a los datos
Existen distintas formas en las que una aplicacin puede acceder a los datos,
almacenados por lo general en una base de datos relacional:
La primera opcin que se nos puede ocurrir cuando diseamos un sistema
orientado a objetos es utilizar un registros activos, objetos que encapsulan
directamente las estructuras de datos externas (p.ej. las tuplas de las tablas
de la base de datos) e incorporan la lgica del dominio que les
corresponda, aparte de las operaciones necesarias para obtener y guardar
objetos en la base de datos.
Algo ms adecuado puede resultar el empleo de gateways, clases
auxiliares que se corresponden con las tablas de la base de datos e
implementan las operaciones necesarias para manipular la base de datos
[CRUD: Create, Retrieve, Update & Delete]. Estas clases auxiliares nos
permiten no mezclar la lgica de la aplicacin con el acceso a los datos
externos, tal como sucede si utilizamos registros activos.
La tercera opcin (y siempre hay una tercera opcin) es la ms compleja
pero la ms flexible: O/R Mapping. Se basa en establecer una
correspondencia entre el modelo orientado a objetos del dominio y la
representacin de los distintos objetos en una base de datos relacional. En
las dos alternativas anteriores, los objetos de la aplicacin han de ser
conscientes de cmo se representan en la base de datos. En el caso del
O/R Mapping, los objetos pueden ignorar la estructura de la base de datos y
cmo se realiza la comunicacin con la base de datos. La inversin de
control caracterstica de esta opcin independiza el modelo orientado a
objetos del dominio de la capa de acceso a los datos: se puede cambiar la
base de datos sin tener que tocar el modelo orientado a objetos del dominio
y viceversa. De esta forma, se facilita el desarrollo, la depuracin y la
evolucin de las aplicaciones.
Correspondencia entre las clases y la base de datos
En primer lugar, hemos de establecer la correspondencia existente entre las
clases de nuestro modelo orientado a objetos del dominio de la aplicacin y las
tablas que almacenarn los datos en un sistema gestor de bases de datos
relacionales. Para ello, definiremos una serie de atributos que nos permitirn
mantener la correspondencia entre el modelo orientado a objetos y la base de
datos. Dichos atributos los definiremos dentro del espacio de nombres
DB.ORmap.
Tablas y clases
En principio, cada clase de nuestra aplicacin se traducir en una tabla en la base
de datos, lo cual indicamos mediante el atributo [DBTable]:

[DBTable("DOCUMENT")]
public class Document {
...
}
Dicho atributo, aplicable a clases y structs, se implementa de la siguiente manera:
[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Struct)]
public class DBTableAttribute : Attribute
{
string tableName;

public DBTableAttribute(string tableName)
{
this.tableName = tableName;
}

public string TableName
{
get { return tableName; }
set { tableName = value; }
}
}
Columnas y variables de instancia
Cada variable de instancia de la clase ha de traducirse a una columna en la tabla
correspondiente de la base de datos (o conjunto de columnas, segn el caso).
Para establecer esta correspondencia podemos crear un atributo [DBColumn]:
[DBTable("DOCUMENT")]
public class Document {
...
[DBColumn(DbType.Int32)]
public int id;
[DBColumn(DbType.String, Size=256)]
public string title;
...
}
La implementacin del atributo [DBColumn] es algo ms larga pero sigue siendo
bastante simple:
[AttributeUsage(AttributeTargets.Property |
AttributeTargets.Field)]
public class DBColumnAttribute : Attribute
{
string columnName;
DbType type = DbType.String;
int size = 0;
bool nullable = true;


public DBColumnAttribute(DbType type)
{
this.type = type;
}

public DBColumnAttribute(string columnName, DbType
type)
{
this.columnName = columnName;
this.type = type;
}


public string ColumnName
{
get { return columnName; }
set { columnName = value; }
}

public DbType Type
{
get { return type; }
set { type = value; }
}

public int Size
{
get { return size; }
set { size = value; }
}

public bool Nullable {
get { return nullable; }
set { nullable = value; }
}
}
Claves
De las distintas columnas de una tabla, alguna(s) debe(n) formar parte de la clave
primaria de la relacin, lo que podemos indicar introduciendo un nuevo atributo
[DBKey]:
[DBTable("DOCUMENT")]
public class Document {
...
[DBColumn(DbType.Int32)]
[DBKey]
public int id;
...
}
La implementacin del atributo [DBKey] es trivial:
[AttributeUsage(AttributeTargets.Property |
AttributeTargets.Field)]
public class DBKeyAttribute : Attribute
{
public DBKeyAttribute ()
{
}
}
Claves externas
La existencia de una claves definidas es vital para poder establecer restricciones
de integridad referencial por medio de claves externas. Dichas claves externas las
podemos especificar utilizando un atributo ms: [DBForeignKey].
[DBTable("DOCUMENT")]
public class Document
{
...
[DBForeignKey]
private User owner;
}
La implementacin de este ltimo atributo es similar a la de los dems:
[AttributeUsage(AttributeTargets.Property |
AttributeTargets.Field)]
public class DBForeignKeyAttribute : Attribute
{
private string columnName;

public DBForeignKeyAttribute ()
{
}

public DBForeignKeyAttribute (string columnName)
{
this.columnName = columnName;
}

public string ColumnName
{
get { return columnName; }
set { columnName = value; }
}
}
Los cuatro atributos anteriores ([DBTable], [DBColumn], [DBKey] y
[DBForeignKey]) los definiremos bajo el espacio de nombres DB.ORmap en una
biblioteca de clases denominada ORmap.dll.
Reflexionando sobre las clases
A continuacin utilizaremos un mecanismo conocido con el nombre de reflexin
para ser capaces de explorar las clases de un assembly en tiempo de ejecucin.
En el caso de la plataforma .NET, las clases necesarias para reflexionar se
encuentran en el espacio de nombres System.Reflection.
Creamos una nueva aplicacin en lnea de comandos y comenzamos su
implementacin cargando un assembly que el usuario ha de especificar como
parmetro:
class TestReflection
{
[STAThread]
static void Main(string[] args)
{
if (args.Length != 1) {
Console.WriteLine("Uso: TestReflection
<assembly>");
return;
}

Assembly assembly = null;

try {
assembly = Assembly.LoadFrom(args[0]);
ParseAssembly(assembly);
} catch (Exception e) {
Console.WriteLine("No se pudo cargar el assembly " +
args[0]);
Console.WriteLine(e.Message);
}

Console.ReadLine();
}
...
Cuando tenemos el assembly, podemos recorrer los distintas clases que lo forman
para ver en cules de ellas disponemos de la informacin necesaria para
almacenar sus instancias en una base de datos de forma automtica (aqullas
para las cuales hemos utilizado el atributo DBTable):
public static void ParseAssembly(Assembly assembly)
{
Type[] types = assembly.GetTypes();

foreach (Type type in types) {

if (type.IsClass) {
DBTableAttribute table = getTable(type);

if (table!=null) {
Console.WriteLine();
Console.WriteLine("Clase '{0}' [Tabla {1}]",
type.ToString(), table.TableName);
ParseClass(type);
}
}
}
}
En una funcin auxiliar descubrimos si una clase tiene asociado un atributo de tipo
[DBTable]:
public static DBTableAttribute getTable (Type type)
{
DBTableAttribute[] dataTable = (DBTableAttribute[])
type.GetCustomAttributes(typeof(DBTableAttribute),
true);

if (dataTable.Length > 0) {
return dataTable[0];
} else {
return null;
}
}
Finalmente, tenemos que analizar cada una de las clases por separado:
public static void ParseClass (Type type)
{
DBTableAttribute table = getTable(type);
FieldInfo[] fields = type.GetFields( BindingFlags.Public
| BindingFlags.NonPublic |
BindingFlags.Instance );

// Columnas

foreach (FieldInfo field in fields)
ParseMember(field,field.FieldType,table);

// Clave primaria

Console.Write (" Key: ");

foreach (FieldInfo field in fields)
if ( isKey(field) )
ParseKey(field,field.FieldType,table);
}
Dentro de cada clase hemos de examinar sus campos por separado (para ver
cmo se traducirn en la base de datos) y tambin debemos ver cul es la clave
primaria de la tabla. Tenemos que recorrer los campos de la clase y ver cmo se
traducen a columnas de la base de datos, pero antes debemos definir un par de
funciones auxiliares de utilidad:
public static bool isKey (MemberInfo member)
{
DBKeyAttribute[] key = (DBKeyAttribute[])
member.GetCustomAttributes(typeof(DBKeyAttribute),
true);

return (key.Length > 0);
}

public static bool isForeignKey (MemberInfo member) {
DBForeignKeyAttribute[] key = (DBForeignKeyAttribute[])

member.GetCustomAttributes(typeof(DBForeignKeyAttribute),
true);

return (key.Length > 0);
}
Pasemos ahora al anlisis de los campos de la clase. La existencia de claves
externas hace que debamos introducir, en la tabla correspondiente a una clase,
las columnas pertenecientes a la clave primaria de la tabla a la que hace
referencia la clave externa. Este hecho nos obliga a implementar la siguiente
funcin de una forma un tanto extraa (un poco ms adelante ya veremos por
qu):
public static void ParseMember
(MemberInfo member, Type type, DBTableAttribute table)
{
ParseMember(member,type,table,"");
}

public static void ParseMember
(MemberInfo member, Type type, DBTableAttribute table,
string prefix)
{
if ( !isForeignKey(member) ) {

DBColumnAttribute[] column = (DBColumnAttribute[])

member.GetCustomAttributes(typeof(DBColumnAttribute),
true);

if (column.Length > 0) {

DBColumnAttribute col = column[0];

Console.WriteLine(
" " + member.ReflectedType+"."+member.Name + "
[" + type + "]"+
" -> Columna " + prefix + col.ColumnName + " [" +
col.Type + "]");
}

} else {
ParseForeignKey (member.Name,type,table,prefix, new
Visitor(ParseMember));
}
}
Dejemos por ahora a un lado el anlisis de las claves externas y pasemos a ver
cmo se identifica la clave primaria de la tabla asociada a una clase:
public static void ParseKey
(MemberInfo member, Type type, DBTableAttribute table)
{
ParseKey(member,type,table,"");
}

public static void ParseKey
(MemberInfo member, Type type, DBTableAttribute table,
string prefix)
{
if ( !isForeignKey(member) ) {
Console.Write(" " + prefix + member.Name );
} else {
Console.Write(" [" + member.Name +"]");
ParseForeignKey (member.Name, type, table, prefix, new
Visitor(ParseKey));
}
}
Tanto al analizar las columnas de una tabla como al identificar su clave primaria
debemos tener en cuenta la existencia de claves externas (llamadas a
ParseForeignKey en los fragmentos de cdigo anteriores). Esto se debe a que la
presencia de una clave externa entre los campos de una clase (sea o no dentro de
su clave) implica la introduccin en la tabla correspondiente de una serie de
columnas que harn referencia a la clave primaria de otra tabla, la cual estar, a
su vez, derivada de una de otra de las clases de nuestro assembly. Por tanto,
habr que analizar la clase a la que se hace referencia para completar el anlisis
de la clase inicial.
Al repetirse este hecho tanto en ParseMember como en ParseKey, hemos optado
por utilizar una variante del patrn de diseo "Visitante" que parametriza nuestra
implementacin del anlisis de claves externas:
public delegate void Visitor
(MemberInfo member, Type type, DBTableAttribute table,
string prefix);

public static void ParseForeignKey
(string name, Type type, DBTableAttribute table, string
prefix, Visitor visitor)
{
string foreignTable;

if (type.IsClass) {
DBTableAttribute dataTable = getTable(type);

if (dataTable!=null) {
foreignTable = dataTable[0].TableName;
prefix = prefix+name+"_";

FieldInfo[] fields = type.GetFields
( BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Instance );

foreach (FieldInfo field in fields) {
if (isKey(field))
visitor(field,field.FieldType,table,prefix);
}
}
}
}
Con esto conseguimos recorrer la informacin relativa a una clase que nos permite
saber cmo se representarn las instancias de esa clase en la base de datos:
Las instancias de cada clase etiquetada con el atributo [DBTable] se
almacenarn en una tabla de la base de datos.
Cada campo de la clase etiquetado con el atributo [DBColumn] dar lugar a
una columna en la tabla correspondiente de la base de datos.
Todos los campos etiquetados con el atibuto [DBKey] formarn parte de la
clave primaria de la tabla derivada de la clase.
La existencia de una clave externa, etiquetada con el atributo
[DBForeignKey], implicar la inclusin en la tabla de tantas columnas como
hagan falta para hacer referencia a la clave primaria de la tabla
referenciada (la tabla que se deriva de la clase a la que pertenece el
atributo etiquetado con [DBForeignKey].
Veamos un ejemplo para comprender mejor la traduccin del conjunto de clases
que forman modelo orientado a objetos al conjunto de tablas que constituyen la
base de datos en la que se almacenarn las instancias de nuestras clases cuando
existen referencias de una clase a otra:
[DBTable("ACCESS")]
public class User
{
[DBColumn("id", DbType.Int32)]
[DBKey]
public int id;

[DBColumn("login", DbType.String, Size=16)]
public string login;

[DBColumn("passwd", DbType.String, Size=64)]
public string password;

[DBColumn("access", DbType.String, Size=16)]
public string priviledges;
}

[DBTable("DOCUMENT")]
public class Document {

[DBColumn("id",DbType.Int32)]
[DBKey]
public int id;

[DBColumn("title", DbType.String, Size=256)]
public string title;
[DBColumn("author", DbType.String, Size=256)]
public string author;
[DBColumn("source", DbType.String, Size=256)]
public string source;

[DBForeignKey]
public DocumentUser owner;
}

Estas dos clases dan lugar al siguiente esquema en la base
de datos:
Clase 'User' [Tabla ACCESS]
User.id [System.Int32] -> Columna id [Int32]
User.login [System.String] -> Columna login [String]
User.password [System.String] -> Columna passwd
[String]
User.priviledges [System.String] -> Columna access
[String]
Key: id

Clase 'Document' [Tabla DOCUMENT]
Document.id [System.Int32] -> Columna id [Int32]
Document.title [System.String] -> Columna title [String]
Document.author [System.String] -> Columna author
[String]
Document.source [System.String] -> Columna source
[String]
User.id [System.Int32] -> Columna owner_id [Int32]
Key: id
Del modelo orientado a objetos a la base de datos
Obviamente, el programa de la seccin anterior que recorra las clases de un
assembly para descubrir cmo se deben almacenar los objetos en la base de
datos es slo un ejemplo ilustrativo y no resulta demasiado til en la realidad.
Es mucho ms interesante crear en memoria una estructura de datos que refleje la
estructura de la base de datos asociada a los objetos de nuestra aplicacin. De
esta forma, podremos reutilizar dicha estructura con diferentes fines sin necesidad
de volver a implementar todo lo relacionado con la reflexin. Esto es, en la funcin
ParseAssembly de la aplicacin descrita en la seccin anterior slo tendremos que
escribir:
model = new DatabaseObjectModel(type);
model.ToXMLFile(type.ToString()+".xml");
La clase DatabaseObjectModel nos servir para representar la estructura de los
objetos de un tipo en la base de datos y nos ser de gran utilidad para automatizar
el acceso a la base de datos en aplicaciones diseadas utilizando un modelo
orientado a objetos del dominio de la aplicacin.
El modelo de los objetos en la base de datos lo construiremos a partir de tres
clases, que colocaremos en el espacio de nombres DB.ORmap.Model:
La clase DatabaseColumn representa una columna de una tabla de la base
de datos.
La clase DatabaseColumns representa un conjunto de columnas
pertenecientes a una misma tabla de la base de datos.
Finalmente, la clase DatabaseObjectModel nos servir para modelar la
representacin de los objetos de una clase en la base de datos.
DatabaseColumn
La implementacin de esta clase es relativamente sencilla. Nos limitamos a
encapsular una serie de propiedades asociadas a una columna e implementamos
una funcin que nos sirve para obtener una representacin en XML de los datos
de los que disponemos:

public class DatabaseColumn
{
private MemberInfo member; // Miembro asociado de la
clase
private string id; // QName
private string name; // Nombre de la columna
private DbType type; // Tipo de la columna
private int size; // Tamao de la columna
private bool nullable; // Puede ser NULL?

// Constructores

public DatabaseColumn
(MemberInfo member, string name, DbType type)
{
this.member = member;
this.id = member.Name;

if (name!=null)
this.name = name;
else
this.name = member.Name;

this.type = type;
}

public DatabaseColumn
(MemberInfo member, DBColumnAttribute column,
string prefix)
{
this.member = member;

this.id = prefix+member.Name;
this.id = id.Replace("_",".");

if (column.ColumnName!=null)
this.name = prefix+column.ColumnName;
else
this.name = prefix+member.Name;

this.type = column.Type;
this.size = column.Size;
this.nullable = column.Nullable;
}

// Propiedades

public string MemberPath {
get { return id; }
}

public string MemberName {
get { return member.Name; }
}

public Type MemberReflection {
get { return member.ReflectedType; }
}

public string ColumnName {
get { return name; }
set { name = value; }
}

public DbType ColumnType {
get { return type; }
}

// Conversin en una cadena

public override string ToString () {
return member.ReflectedType+"."+member.Name
+ " -> " + name + " [" + type + "]";
}

// Volcado de los datos en XML

public void ToXML (ref XmlTextWriter writer)
{
writer.WriteStartElement("column");

if (id!=null)
writer.WriteAttributeString("id",id);

if (member!=null)
writer.WriteAttributeString

("member",member.ReflectedType+"."+member.Name);

writer.WriteAttributeString("name",name);
writer.WriteAttributeString("type",type.ToString());

if (size>0)
writer.WriteAttributeString("size",size.ToString());

if (!nullable)
writer.WriteAttributeString("nullable",nullable.ToString());

writer.WriteEndElement();
}
}
DatabaseColumns
Esta clase no es ms que una coleccin de columnas de una tabla y su
implementacin es algo tediosa pero simple:
public class DatabaseColumns
{
private string table; // Nombre de la tabla
private MemberInfo member; // Miembro asociado al
conjunto
private ArrayList fields; // Conjunto de columnas

// Constructores

public DatabaseColumns (string table)
: this(table,null)
{
}

public DatabaseColumns (string table, MemberInfo
member)
: this(table, member, new ArrayList() )
{
}

public DatabaseColumns (string table, MemberInfo
member, ArrayList list)
{
this.table = table;
this.member = member;
this.fields = list;
}

// Propiedades

public MemberInfo Member {
get { return member; }
set { member = value; }
}

public string Table {
get { return table; }
set { table = value; }
}

// Conjunto de columnas

internal ArrayList Columns {
get { return fields; }
}

public int Size {
get { return fields.Count; }
}

public DatabaseColumn this[int index]
{
get { return (DatabaseColumn) fields[index]; }
set { fields[index] = value; }
}

public void addColumn (DatabaseColumn column)
{
fields.Add (column);
}

internal void addColumns (ArrayList columns)
{
fields.AddRange(columns);
}

// Volcado de los datos en XML

public void ToXML (ref XmlTextWriter writer)
{
int i;

if (member!=null)
writer.WriteAttributeString("member",member.Name);

for (i=0; i<fields.Count; i++)
this[i].ToXML(ref writer);
}
}
DatabaseObjectModel
Finalmente, llegamos a la clase clave en la conversin del modelo orientado a
objetos en un modelo vlido para una base de datos relacional. La implementacin
de esta clase comienza con la declaracin de variables de instancia:
/// <summary>
/// Modelo del objeto tal como se almacena en la base de
datos
/// </summary>

public class DatabaseObjectModel
{
private Type type;
private MemberInfo member;
private string table;
private DatabaseColumns key;
private DatabaseColumns data;
private ArrayList foreignKeys;
...
Acto seguido, definimos sus constructores, los cuales no tienen mayor secreto:
/// <summary>
/// Constructor
/// </summary>
/// <param name="type">Tipo de objeto para el cual se
construye el modelo</param>

public DatabaseObjectModel (Type type)
{
this.type = type;
this.table = getTable(type).TableName;

key = new DatabaseColumns ( table );
data = new DatabaseColumns ( table );
foreignKeys = new ArrayList();

ParseClass(type);
}

/// <summary>
/// Constructor privado
/// </summary>
/// <param name="table">Nombre de la tabla
destino</param>
/// <param name="member">Miembro de la clase que da
origen a la tabla</param>

private DatabaseObjectModel (string table, MemberInfo
member)
{
this.member = member;
this.table = table;

key = new DatabaseColumns ( table );
data = new DatabaseColumns ( table );
foreignKeys = new ArrayList();
}
Como es habitual, nos hacen falta los mtodos y propiedades que dan acceso a
las variables de instancia de la clase: la tabla asociada, las columnas que forman
la clave primaria, las columnas que no forman parte de la clave primaria y el
conjunto de claves externas asociado a la clase que se est modelando.
public string Table {
get { return table; }
}

public DatabaseColumns Key {
get { return key; }
}

public DatabaseColumns Data {
get { return data; }
}

public DatabaseColumns getForeignKey (int index) {
return (DatabaseColumns) foreignKeys[index];
}

public int getForeignKeyCount () {
return foreignKeys.Count;
}

public void addForeignKey (DatabaseColumns key) {
foreignKeys.Add(key);
}
Anlisis de una clase
A continuacin nos encargamos de la parte central: la encargada de construir el
modelo a partir de los atributos con los que etiquetamos las clases de nuestra
aplicacin de una forma similar a como lo hicimos en la seccin anterior:
private const BindingFlags memberFlags =
BindingFlags.Public
| BindingFlags.NonPublic
| BindingFlags.Instance;

// Anlisis de la estructura de la clase

private void ParseClass (Type type)
{
// Miembros de la clase

FieldInfo[] fields = type.GetFields( memberFlags );

foreach (FieldInfo field in fields)
addColumns ( ParseMember(field,field.FieldType),
isKey(field) );

// Superclase

DBTableAttribute table = getTable(type);
DBTableAttribute super = getTable(type.BaseType);

if ((super!=null) && (super!=table))
addForeignKey( new DatabaseColumns(
super.TableName, null, Key.Columns ) );
}

private void addColumns (ArrayList list, bool key)
{
if (key)
Key.addColumns (list);
else
Data.addColumns (list);
}

// Anlisis de un miembro de una clase

private ArrayList ParseMember (MemberInfo member, Type
type)
{
return ParseMember(member,type, "");
}

private ArrayList ParseMember (MemberInfo member, Type
type, string prefix)
{
ArrayList list = new ArrayList();

if ( !isForeignKey(member) ) {

DBColumnAttribute column = getColumn(member);

if (column!=null)
list.Add( new DatabaseColumn (member, column,
prefix ) );

} else {
list = ParseForeignKey (member, type, prefix, new
Visitor(ParseMember));
}

return list;
}

// Claves externas

private delegate ArrayList Visitor
(MemberInfo member, Type type, string prefix);

private ArrayList ParseForeignKey
(MemberInfo member, Type type, string prefix, Visitor
visitor)
{
string foreignTable;
ArrayList list = new ArrayList();

if (type.IsClass) {

DBTableAttribute table = getTable(type);

if (table!=null)
foreignTable = table.TableName;
else
foreignTable = null;

prefix += member.Name+"_";

// Columnas

FieldInfo[] fields = type.GetFields( memberFlags );

foreach (FieldInfo field in fields) {
if (isKey(field) || (foreignTable==null))
list.AddRange( visitor(field,field.FieldType,prefix) );
}

// ID

DBForeignKeyAttribute fk = getFK(member);

if (fk.ColumnName!=null) {
for (int i=0; i<list.Count; i++)
((DatabaseColumn)list[i]).ColumnName =
fk.ColumnName;
}

// Clave externa

if (foreignTable!=null)
addForeignKey ( new
DatabaseColumns(foreignTable,member,list) );
}

return list;
}
En formato XML...
Tambin hemos implementado una rutina que nos permite volcar el modelo en
formato XML:
public void ToXML (ref XmlTextWriter writer)
{
int i;

writer.WriteStartElement("Table");

if (member!=null)
writer.WriteAttributeString("member", member.Name);

writer.WriteAttributeString("name", this.table);

writer.WriteStartElement("Key");
Key.ToXML(ref writer);
writer.WriteEndElement();

writer.WriteStartElement("Data");
Data.ToXML(ref writer);
writer.WriteEndElement();

for (i=0; i<getForeignKeyCount(); i++) {
writer.WriteStartElement("Foreign-Key");

writer.WriteAttributeString("table",getForeignKey(i).Table);
getForeignKey(i).ToXML(ref writer);
writer.WriteEndElement();
}

writer.WriteEndElement();
}

private void ToXMLDocument (ref XmlTextWriter writer)
{
writer.Formatting = Formatting.Indented;
writer.Indentation = 2;

writer.WriteStartDocument();
writer.WriteStartElement("DatabaseModel");
writer.WriteAttributeString("type", this.type.ToString());

ToXML(ref writer);

writer.WriteEndElement();
writer.WriteEndDocument();
}

public void ToXMLFile (string filename)
{
XmlTextWriter writer = new XmlTextWriter(filename,
System.Text.Encoding.GetEncoding("ISO-
8859-1"));
ToXMLDocument(ref writer);
writer.Close();
}

public override string ToString ()
{
StringWriter w = new StringWriter();
XmlTextWriter writer = new XmlTextWriter(w);

ToXMLDocument(ref writer);
writer.Flush();

return w.ToString();
}
Mtodos auxiliares
La implementacin de la clase se completa con una serie de rutinas auxiliares que
hacen ms legible el resto del cdigo. Estas rutinas son similares a las empleadas
en la seccin anterior:
public static DBTableAttribute getTable (Type type) {
DBTableAttribute[] table = (DBTableAttribute[])
type.GetCustomAttributes(typeof(DBTableAttribute),
true);

if (table.Length > 0) {
return table[0];
} else {
return null;
}
}

public static DBColumnAttribute getColumn (MemberInfo
member) {
DBColumnAttribute[] column = (DBColumnAttribute[])

member.GetCustomAttributes(typeof(DBColumnAttribute),
true);

if (column.Length > 0)
return column[0];
else
return null;
}

public static DBForeignKeyAttribute getFK (MemberInfo
member) {
DBForeignKeyAttribute[] fk = (DBForeignKeyAttribute[])

member.GetCustomAttributes(typeof(DBForeignKeyAttribute),
true);

if (fk.Length > 0)
return fk[0];
else
return null;
}

public static bool isKey (MemberInfo member) {
DBKeyAttribute[] key = (DBKeyAttribute[])
member.GetCustomAttributes(typeof(DBKeyAttribute),
true);

return (key.Length > 0);
}

public static bool isForeignKey (MemberInfo member) {
DBForeignKeyAttribute[] key = (DBForeignKeyAttribute[])

member.GetCustomAttributes(typeof(DBForeignKeyAttribute),
true);

return (key.Length > 0);
}
Creacin automtica de las tablas en la base de datos
Las clases descritas en los apartados anteriores nos pueden servir para
automatizar la creacin de la base de datos a partir de un modelo orientado a
objetos. De hecho, el modelo obtenido por la clase DatabaseObjectModel no es
ms que una representacin de la parte del esquema de la base de datos
relacionada con los objetos cuyo tipo se modela. Por tanto, podemos derivar los
CREATE TABLE necesarios para crear la tablas en la base de datos a partir de la
representacin en XML del modelo obtenido con DatabaseObjectModel. Para ello
utilizaremos una hoja de estilo XSLT como la siguiente:
<?xml version="1.0" encoding="ISO-8859-1"?>

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="/">
/* SQL DDL para <xsl:value-of
select="DatabaseModel/@type" /> */
<xsl:apply-templates/>
COMMIT;
</xsl:template>

<!-- CREATE TABLE -->

<xsl:template match="Table">

CREATE TABLE <xsl:value-of select="@name" />
(
<xsl:apply-templates/>
<xsl:call-template name="pk" />
);
<xsl:call-template name="fk" />
</xsl:template>


<xsl:template match="Key">
<xsl:apply-templates/>
</xsl:template>

<xsl:template match="Data">
<xsl:if test="count(column)>0">
<xsl:if test="count(../Key/column)>0">,</xsl:if>
<xsl:apply-templates/>
</xsl:if>
</xsl:template>

<xsl:template match="Key/column">
<xsl:value-of select="@name" />
<xsl:call-template name="type" />not null
<xsl:if test="position()!=last()">,</xsl:if>
</xsl:template>

<xsl:template match="Data/column">
<xsl:value-of select="@name" />
<xsl:call-template name="type" />
<xsl:if test="@nullable='False'">not null</xsl:if>
<xsl:if test="position()!=last()">,</xsl:if>
</xsl:template>

<xsl:template name="pk">
<xsl:if test="count(Key/column)>0">,
primary key(<xsl:for-each select="Key/column">
<xsl:call-template name="list" />
</xsl:for-each>)
</xsl:if>
</xsl:template>

<xsl:template name="fk">
<xsl:for-each select="Foreign-Key">
ALTER TABLE <xsl:value-of select="../@name" /> ADD
foreign key ( <xsl:for-each select="column">
<xsl:call-template name="list" />
</xsl:for-each>
) references <xsl:value-of select="@table" />;
</xsl:for-each>
</xsl:template>


<xsl:template name="list">
<xsl:value-of select="@name" />
<xsl:if test="position()!=last()">,</xsl:if>
</xsl:template>

<!-- Tipos de datos: SQL Server -->

<xsl:template name="type">
<xsl:if test="@type='Int32'"> INT </xsl:if>
<xsl:if test="@type='Date'"> DATETIME </xsl:if>
<xsl:if test="@type='String'"> VARCHAR
(<xsl:value-of select="@size" />) </xsl:if>
<xsl:if test="@type='Object'"> BLOB SUB_TYPE TEXT
</xsl:if>
</xsl:template>

</xsl:stylesheet>
Incluso podemos variar ligeramente la parte final de la hoja de estilo para portar
nuestro sistema a otra base de datos (p.ej. InterBase):
<!-- Tipos de datos: Interbase -->

<xsl:template name="type">
<xsl:if test="@type='Int32'"> INTEGER </xsl:if>
<xsl:if test="@type='Date'"> TIMESTAMP </xsl:if>
<xsl:if test="@type='String'"> VARCHAR
(<xsl:value-of select="@size" />) </xsl:if>
<xsl:if test="@type='Object'"> TEXT </xsl:if>
</xsl:template>

NOTA: Si queremos que el script resultante con las sentencias CREATE TABLE
en SQL sea ejecutable directamente, deberemos asegurarnos de que no se
intenta crear una clave externa a una tabla antes de haber creado dicha tabla.
Esto se puede conseguir fcilmente realizando lo que tcnicamente se denomina
una ordenacin topolgica del grafo de dependencias. En cristiano, esto quiere
decir que primero se crean las tablas que no tienen referencias externas a otras
tablas y, a continuacin, se van creando las tablas cuyas claves externas hacen
referencia a tablas ya creadas.
Anexo: Aplicacin de hojas de estilo XSLT
Para aplicar una hoja de estilo XSLT a un fichero XML podemos utilizar el
siguiente programa escrito en C#:
using System;
using System.IO;
using System.Xml;
using System.Xml.XPath;
using System.Xml.Xsl;

namespace XSLT
{
public class XSLT
{
public static void Main (string[] args)
{
if (args.Length != 2) {
Console.Error.WriteLine("Uso: XSLT <fichero XML>
<fichero XSLT>");
return;
}

string filename = args[0];
string stylesheet = args[1];

XslTransform xslt = new XslTransform();

xslt.Load(stylesheet);

XPathDocument xpathdocument = new
XPathDocument(filename);
XmlTextWriter writer = new XmlTextWriter(Console.Out);
writer.Formatting=Formatting.Indented;

xslt.Transform(xpathdocument, null, writer, null);
}
}
}
De la base de datos al modelo orientado a objetos
Hasta ahora hemos automatizado la conversin del modelo orientado a objetos al
modelo relacional tpico de muchos gestores de bases de datos. Sin embargo, an
no hemos visto cmo podemos aprovehar lo que ya tenemos hecho para
simplificar el desarrollo en s de las aplicaciones desde el punto de vista del
programador.
En esta seccin vamos a ver cmo podemos implementar un conjunto de rutinas
que nos permitirn leer objetos directamente de la base de datos con slo
especificar su clave primaria (y sin tener que escribir nada de cdigo!). Por
ejemplo, podramos leer un documento de la base de datos si sabemos cul es su
identificador (666):
PersistenceService service = new
PersistenceService(database);
Document doc = (Document)
service.Find(typeof(Document), 666);
Difcilmente se nos puede ocurrir una forma ms sencilla de realizar la
comunicacin entre nuestra aplicacin y la base de datos. Pasemos ahora a ver
cmo podemos lograr que nuestro "servicio de persistencia" funcione
correctamente.
Una interfaz genrica para el acceso a bases de datos a travs de ADO.NET
En primer lugar, vamos a independizar la implementacin de nuestro servicio de
persistencia de la base de datos concreta que podamos utilizar en un momento
dado. Para lograrlo basta con definir un interfaz genrico que implementaremos
adecuadamente para nuestra base de datos. Slo tenemos que incluir cuatro
mtodos "factory" en este interfaz:
namespace DB
{
public interface Database
{
IDbConnection newConnection ();

IDbCommand newCommand (string sql, IDbConnection
connection);

IDbDataAdapter newDataAdapter ();

IDbDataParameter newParameter (string name, DbType
type);
}
}
A continuacin se muestra cmo podemos definir una clase que implementa el
interfaz anterior y nos permite acceder a una base de datos utilizando OLE DB.
Slo tenemos que tener en cuenta la conversin de tipos entre
System.Data.DbType y los tipos de datos soportados por nuestra base de datos:
public class OleDbDatabase: Database
{
private string connectionString;

public OleDbDatabase(string connectionString)
{
this.connectionString = connectionString;
}

public IDbConnection newConnection ()
{
return new
System.Data.OleDb.OleDbConnection(connectionString);
}

public IDbCommand newCommand (string sql,
IDbConnection connection)
{
return new System.Data.OleDb.OleDbCommand
(sql, (System.Data.OleDb.OleDbConnection)
connection);
}

public IDbDataAdapter newDataAdapter ()
{
return new System.Data.OleDb.OleDbDataAdapter();
}

public IDbDataParameter newParameter (string
name,DbType type)
{
return new
System.Data.OleDb.OleDbParameter(name,map(type) );
}

private System.Data.OleDb.OleDbType map (DbType type)
{
switch (type) {
case DbType.AnsiString: return
System.Data.OleDb.OleDbType.VarChar;
case DbType.String: return
System.Data.OleDb.OleDbType.VarChar;
case DbType.Date: return
System.Data.OleDb.OleDbType.Date;
case DbType.Int32: return
System.Data.OleDb.OleDbType.Integer;
case DbType.Object: return
System.Data.OleDb.OleDbType.VarChar;
}
return System.Data.OleDb.OleDbType.Empty;
}
}
Construccin dinmica de consultas SQL
La siguiente clase se encarga de construir dinmica las sentencias SQL
adecuadas para acceder a la base de datos a partir del modelo de la base de
datos de DatabaseObjectModel:
public class SQLCommandBuilder
{
private static string ColumnList (DatabaseObjectModel
model)
{
int i;
string sql = "";

if (model.Key.Size>0) {
sql += model.Key[0].ColumnName;

for (i=1; i<model.Key.Size; i++)
sql += ", "+model.Key[i].ColumnName;
}

if (model.Data.Size>0) {

if (model.Key.Size>0)
sql += ", ";

sql += model.Data[0].ColumnName;

for (i=1; i<model.Data.Size; i++)
sql += ", "+model.Data[i].ColumnName;
}

return sql;
}

private static string InsertList (DatabaseObjectModel
model)
{
int i;
string sql = "?";

for (i=1; i<model.Key.Size+model.Data.Size; i++)
sql += ",?";

return sql;
}

private static string KeyWhereClause
(DatabaseObjectModel model)
{
int i;
string where ="";

if (model.Key.Size>0) {
where = " WHERE "+model.Key[0].ColumnName+"=?";

for (i=1; i<model.Key.Size; i++)
where += " AND "+model.Key[i].ColumnName+"=?";
}

return where;
}


public static string SelectAll (DatabaseObjectModel model)
{
return "SELECT " + ColumnList(model) + " FROM " +
model.Table;
}

public static string SelectKey (DatabaseObjectModel
model)
{
return SelectAll(model) + KeyWhereClause(model);
}


public static string DeleteAll (DatabaseObjectModel model)
{
return "DELETE FROM " + model.Table;
}

public static string DeleteKey (DatabaseObjectModel
model)
{
return DeleteAll(model) + KeyWhereClause(model);
}

public static string Insert (DatabaseObjectModel model)
{
return "INSERT INTO " +model.Table +"
("+ColumnList(model) + ")"
+ " VALUES ("+InsertList(model)+")";
}
}
La interpretacin del cdigo anterior es directa si conocemos la sintaxis del
lenguaje de consulta SQL [Structured Query Language].
El servicio de persistencia
Una vez independizada nuestra implementacin de la base de datos utilizada,
podemos pasar a implementar nuestro servicio de persistencia: el mdulo de
nuestro sistema que nos permitir leer, guardar, cambiar y borrar objetos de la
base de datos.
public class PersistenceService
{
private DB.Database database;
private ObjectFactory factory;

/// Constructor

public PersistenceService(DB.Database database)
{
this.database = database;
factory = new ObjectFactory(this);
}

/// Bsqueda de un objeto dada su clave

public object Find (Type type, object key)
{
return Find (type, new object[]{key} );
}

public object Find (Type type, object[] key)
{
DatabaseObjectModel model = new
DatabaseObjectModel(type);
string sqlSelect =
SQLCommandBuilder.SelectKey(model);
IDbConnection connection = database.newConnection();
IDbCommand selectCommand =
database.newCommand(sqlSelect,connection);
object[] data = null;
object obj = null;

if (model.Data.Size > 0) {

ParameterList(selectCommand,model.Key, key);

connection.Open();

try {

IDataReader reader =
selectCommand.ExecuteReader();

if (reader.Read())
data = DataObjects(reader);

reader.Close();
reader = null;

} catch (Exception error) {
Console.Error.WriteLine(error);
}

selectCommand.Dispose();
selectCommand = null;
connection.Close();

if (data!=null)
obj = factory.Create(type,model,data);
}

return obj;
}


/// Borrado de un objeto

public void Delete (object obj)
{
DatabaseObjectModel model = new
DatabaseObjectModel(obj.GetType());
string sqlDelete =
SQLCommandBuilder.DeleteKey(model);
IDbConnection connection = database.newConnection();
IDbCommand deleteCommand =
database.newCommand(sqlDelete,connection);

ParameterList(deleteCommand,model.Key, obj);

connection.Open();

try {
deleteCommand.ExecuteNonQuery();
} catch (Exception error) {
Console.Error.WriteLine(error);
}

connection.Close();
}

/// Insercin de un objeto

public void Insert (object obj)
{
DatabaseObjectModel model = new
DatabaseObjectModel(obj.GetType());
string sqlInsert =
SQLCommandBuilder.Insert(model);
IDbConnection connection = database.newConnection();
IDbCommand insertCommand =
database.newCommand(sqlInsert,connection);

ParameterList(insertCommand,model.Key, obj);
ParameterList(insertCommand,model.Data,obj);

connection.Open();

try {
insertCommand.ExecuteNonQuery();
} catch (Exception error) {
Console.Error.WriteLine(error);
}

connection.Close();
}


// Lectura de datos

private object[] DataObjects (IDataReader reader)
{
int i;
object[] values = new object[reader.FieldCount];

for (i=0; i<reader.FieldCount; i++) {

if (reader.IsDBNull(i))
values[i] = null;
else
values[i] = reader.GetValue(i);
}

return values;
}


// Listas de parmetros

private void ParameterList
(IDbCommand command, DatabaseColumns
columns, object obj)
{
int i;
IDbDataParameter param;
PropertyInfo property;
FieldInfo field;

for (i=0; i<columns.Size; i++) {
param = database.newParameter
("@"+columns[i].ColumnName,
columns[i].ColumnType);
command.Parameters.Add (param);

property =
obj.GetType().GetProperty(columns[i].MemberName);

if (property!=null) {
param.Value = property.GetValue(obj,null);
} else {
field =
obj.GetType().GetField(columns[i].MemberName);
param.Value = field.GetValue(obj);
}
}
}

private void ParameterList
(IDbCommand command, DatabaseColumns
columns, object[] key)
{
int i;
IDbDataParameter param;

for (i=0; i<columns.Size; i++) {
param = database.newParameter
("@"+columns[i].ColumnName,
columns[i].ColumnType);
command.Parameters.Add (param);
param.Value = key[i];
}
}
}
Con esto ya tenemos implementado el cdigo que dinmicamente se encarga de
ejecutar las sentencias SQL adecuadas sobre la base de datos con la que
estemos trabajando. Slo nos falta ver cmo podemos instanciar objetos a partir
de los datos que leemos de la base de datos:
Instanciacin dinmica de objetos
La clase ObjectFactory nos permite crear objetos a partir de los datos que leemos
de la base de datos:
public class ObjectFactory
{
PersistenceService service;

public ObjectFactory (PersistenceService service)
{
this.service = service;
}

/// Creacin dinmica de un objeto

public object Create (Type type, DatabaseObjectModel
model, object[] data)
{
Type[] types = new Type[0]; // Argumentos del
constructor
object[] param = new object[0]; // Parmetros del
constructor
ConstructorInfo constructor = type.GetConstructor
( BindingFlags.Instance | BindingFlags.Public,
null,
CallingConventions.HasThis, types, null );
object obj = null;

if (constructor!= null) {
obj = constructor.Invoke(param);
FillColumns(obj, type, model.Key, data, 0);
FillColumns(obj, type, model.Data, data,
model.Key.Size);
FillFK(obj, type, model, data);
}

return obj;
}

/// Campos del objeto a partir de las columnas de la tabla

private void FillColumns ( object obj, Type type,
DatabaseColumns columns,
object[] data, int offset)
{
int i;
object value;

for (i=0; i<columns.Size; i++) {

value = data[offset+i];

if (value!=null
&& columns[i].MemberPath.Equals (
columns[i].MemberName) )
SetValue(obj, type, columns[i].MemberName, value);
}
}

private static void SetValue (object obj, Type type, string
name, object value)
{
FieldInfo field = type.GetField(name);

if (field!=null)
field.SetValue(obj,value);
}

/// Claves externas

private void FillFK ( object obj, Type type,
DatabaseObjectModel model,
object[] data )
{
for (int i=0; i<model.getForeignKeyCount(); i++)
FillFK (obj,type,model,model.getForeignKey(i), data);
}

private void FillFK ( object obj, Type type,
DatabaseObjectModel model,
DatabaseColumns columns, object[] data)
{
int i;
object[] key = new object[columns.Size];
bool valid = true;

for (i=0; i<columns.Size; i++) {
key[i] = GetValue(model,columns[i],data);

if (key[i]==null)
valid = false;
}

if (valid) {
object fk = service.Find(
GetType(type,columns.Member.Name), key);
SetValue(obj,type,columns.Member.Name,fk);
}
}

private static Type GetType (Type type, string name)
{
FieldInfo field = type.GetField(name);

if (field!=null){
return field.FieldType;
else
return null;}
}

private object GetValue
(DatabaseObjectModel model, DatabaseColumn
column, object[] data)
{
int pos = GetPosition(model,column);

if ( pos>=0 )
return data[pos];
else
return null;
}

private int GetPosition (DatabaseObjectModel model,
DatabaseColumn column)
{
int i;
int pos = -1;

for (i=0; (i<model.Key.Size) && (pos<0); i++)
if (model.Key.Columns[i] == column)
pos = i;

for (i=0; (i<model.Data.Size) && (pos<0); i++)
if (model.Data.Columns[i] == column)
pos = model.Key.Size + i;

return pos;
}
}
El aspecto ms destacable del cdigo anterior es que, al instanciar un objeto,
puede que tengamos que traer otros objetos de la base de datos: si la
representacin del objeto en la base de datos incluye una clave externa, esto se
traduce en una referencia a otro objeto que habr de construirse a partir de su
clave primaria (de ah la llamada a service.Find en el mtodo FillFK). Ojo! Tal
como est, esta implementacin no funcionar correctamente ante la
presencia de ciclos.
Implementacin bsica de O/R Mapping en la plataforma .NET

Tendencias

El principal objetivo de las empresas de desarrollo de software es maximizar su
ROI [Return On Investment], para lo cual no cabe duda que la mejor opcin es
maximizar la reutilizacin siempre que utilizar algo hecho resulte ms econmico
que rehacerlo. Es aqu donde se enmarcan tres de las lneas de accin ms
prometedoras en la actualidad, las cuales fomentan la reutilizacin desde distintos
puntos de vista:
Desarrollo de componentes
En programacin existen diferentes paradigmas. De la programacin no
estructurada utilizada inicialmente se pas a la programacin estructurada, en la
cual todo se expresa utilizando nicamente estructuras de control secuenciales,
condicionales e iterativas (vase el artculo "Go to statement considered harmful"
en http://www.acm.org/classics/oct95/).
Posteriormente, surgi la programacin orientada a objetos, en la cual los objetos
encapsulan tanto a los datos (miembros, campos, variables de instancia o
propiedades, segn el lenguaje) como a las operaciones que se pueden efectuar
sobre ellos (mtodos, funciones y procedimientos). Utilizar un objeto es muy fcil,
slo hay que crear una instancia de la clase deseada e invocar los mtodos
definidos en su interfaz. Sin embargo, el diseo de nuevos tipos de objetos es
bastante difcil porque, a veces, entran en conflicto distintos objetivos (simplicidad,
funcionalidad, rendimiento, capacidad de reutilizacin, etc.).
Hoy en da, el nfasis recae en el desarrollo de componentes, mdulos
independientes que se pueden reutilizar, los cuales suelen ser de una granularidad
mayor que los objetos. Mientras que los objetos son construcciones del lenguaje,
los componentes, en principio, son independientes del lenguaje. De hecho, el
programador que utiliza un componente puede que ni siquiera conozca al que lo
desarroll, ni trabaje para la misma empresa, ni utilice el mismo lenguaje de
programacin.
Actualmente, existen distintas plataformas para las cuales se pueden desarrollar
componentes. Cada plataforma define un modelo de componente y suele
especificar protocolos para instanciar y utilizar componentes dentro de un mismo
proceso o en procesos diferentes, los cuales pueden estar en distintas mquinas.
A continuacin se mencionan las plataformas ms importantes en la actualidad:
Microsoft comercializa su plataforma .NET, cuyos orgenes se
remontan a COM [Component Object Model], estndar con el cual
est preparada para funcionar. De hecho, COM es la base de
tecnologas como ActiveX u OLE.
Sun Microsystems promueve EJB [Enterprise Java Beans], un
estndar ligado a los servidores de aplicaciones J2EE [Java 2
Enterprise Edition] que emplea el lenguaje de programacin Java.
El OMG [Object Management Group] propone su estndar CORBA
[Common Object-Request Broker], el cual proviene de su
experiencia en el desarrollo de aplicaciones empresariales en
entornos distribuidos.
Finalmente, los servicios web se pueden ver como una opcin ms
en el desarrollo de componentes.
Como se puede ver, existen distintas plataformas que coexisten e interactan. De
hecho, la evolucin de unas influye en la evolucin de otras. Esta constante
evolucin hace que sea imposible predecir el futuro de los componentes como
mercado independiente de las aplicaciones. Lo que s parecen claros son los
beneficios que se obtienen al desarrollar componentes reutilizables en distintos
proyectos (aunque esto se limite a proyectos dentro de una misma empresa).
Para profundizar...
Clemens Szyperski: Component Software: Beyond object-oriented
programming. Addison-Wesley, 2002 [2nd edition]. ISBN 0-201-
74572-0
Creacin de lneas de productos
Aparte del desarrollo de componentes, la creacin de lneas de productos es otra
lnea prometedora de accin para fomentar la reutilizacin. El desarrollo de lneas
de productos consiste en construir un sistema genrico [framework] a partir del
cual se pueden derivar con facilidad distintos productos (instanciaciones del
framework). Mientras que el desarrollo de componentes hace hincapi en el
fomento de la reutilizacin de mdulos, el desarrollo de lneas de productos centra
su atencin en la reutilizacin de la infraestructura comn a distintos productos.
El desarrollo de frameworks requiere identificar aspectos comunes y puntos de
variacin en familias de aplicaciones relacionadas. La instanciacin de un
framework, proceso clave que posibilita la existencia de lneas de productos,
consiste en rellenar los huecos dejados por el armazn del framework. En otras
palabras, el framework proporciona la infraestructura que la aplicacin ha de
personalizar para responder a las necesidades de los usuarios.
Los frameworks suelen hacer un uso extensivo de patrones de diseo y su
creacin requiere una gran habilidad, destreza y visin de futuro por nuestra parte,
adems de un amplio conocimiento del diseo de software mantenible, flexible y
extensible.
Para profundizar...
Frank Buschmann, Regine Meunier, Hans Rohnert, Peter Sommerlad
& Michael Stal: Pattern-Oriented Software Architecture - Volume 1: A
System of Patterns. John Wiley & Sons, 1996. ISBN 0-471-95869-7.
Martin Fowler et al.: Patterns of Enterprise Application Architecture.
Addison-Wesley, 2003. ISBN 0-321-12742-0.
Erich Gamma, Richard Helm, Ralph Johnson & John Vlissides:
Design Patterns: Elements of reusable object-oriented software.
Addison-Wesley, 1994. ISBN 0-201-63361-2.
Douglas Schmidt, Michael Stal, Hans Rohnert & Frank Buschmann:
Pattern-Oriented Software Architecture - Volume 2: Patterns for
concurrent and networked objects. John Wiley & Sons, 2000. ISBN 0-
471-60695-2.
MDSD [Model-Driven Software Development]
Existen mltiples plataformas sobre las cuales podemos implementar nuestras
aplicaciones. Adems, las tecnologas que forman parte de cada una de esas
plataformas est en continua evolucin, lo que supone un gran gasto y muchsimo
esfuerzo para las empresas de desarrollo de software, que intentan sobrevivir de
las aplicaciones que construyen y no de lo ltimo en tecnologa, aunque a menudo
tengan que adaptar sus sistemas para seguir la ltima moda.
MDA [Model-Driven Architecture], por ejemplo, es un estndar propuesto por el
OMG [Object Management Group] que promete acelerar el desarrollo de
aplicaciones, simplificar la integracin entre distintas tecnologas y reducir el coste
de la migracin de las aplicaciones a nuevas plataformas. Ah es nada.
La idea consiste en desarrollar modelos de alto nivel (como los que siempre
deberamos construir al disear una aplicacin). Estos modelos, no obstante, no
slo se utilizan para generar papel (documentar nuestro diseo), sino que se
utilizan para, automticamente, transformarlos en artefactos que formarn parte
del producto final (esto es, cdigo). Para ello, los modelos han de ser modelos
formales que puedan ser interpretados por un ordenador y, cuanto ms precisos
sean, menos trabajo quedar por hacer en la fase de codificacin.
En realidad, lo que propone MDA es elevar el nivel de abstraccin al que
trabajamos y dejar que sean herramientas las que realicen todas las tareas
rutinarias que usualmente se hacen a mano (por ejemplo, derivar el diseo de la
base de datos a partir de un diagrama de clases que representa un modelo
orientado a objetos del dominio de la aplicacin).
MDA, en concreto, propone crear un modelo inicial independiente de la tecnologa
[PIM: Platform Independent Model] en el que se represente la funcionalidad de la
aplicacin. A partir del PIM se generan diferentes modelos especficos para las
tecnologas utilizadas [PSMs: Platform Specific Models], as como los puentes que
puedan necesitarse para que interacten entre ellos. Estos modelos son ya
cercanos a la tecnologa empleada (interfaces web, bases de datos relacionales,
clases C#...). Finalmente, a partir de los PSMs se genera el cdigo de la aplicacin
que implementa la funcionalidad especificada en el modelo abstracto de alto nivel.
La clave en el uso con xito de MDA radica en la utilizacin de modelos cuya
construccin sea ms fcil que la implementacin del cdigo correspondiente, de
forma que la conversin automtica PIM->PSMs->Cdigo sirva realmente para
ahorrarnos trabajo.
Para profundizar...
Anneke Kleppe, Jos Warmer & Wim Bast: MDA Explained - The
Model Driven Architecture: Practice and Promise. Addison-Wesley,
2003. ISBN 0-321-19442-X

You might also like