Sei sulla pagina 1di 221

Saul Mamani Mamani

Desarrollo Agil de Software

DAS Desarrollo Agil de Software


das_irpiri.org

SAUL MAMANI MAMANI

Saul Mamani Mamani

Desarrollo Agil de Software

MODULO I
INTRODUCCION A LA PLATAFORMA .NET Contenido Este mdulo presenta con carcter general la plataforma .NET y cmo sta se diferencia de otros sistemas de desarrollo tradicionales, como ASP.

Leccin 1: Introduccin a la plataforma .NET o Qu es la plataforma .NET? o El entorno de ejecucin CLR Leccin 2: El lenguaje intermedio y el CLS o El lenguaje intermedio o La especificacin comn de los lenguajes .NET o El sistema de tipos comunes Leccin 3: La biblioteca de clases de .NET o La BCL o Los espacios de nombres Leccin 4: Acceso a datos con ADO.NET o ADO.NET o La arquitectura de ADO.NET o Capa conectada de datos o Capa desconectada Leccin 5: Aplicaciones Windows Forms o Introduccin Leccin 6: Aplicaciones Web Forms o Introduccin

1. Introduccin a la plataforma .NET


Simplificando mucho las cosas para poder dar una definicin corta y comprensible, se podra decir que la plataforma .NET es un amplio conjunto de bibliotecas de desarrollo que pueden ser utilizadas por otras aplicaciones para acelerar enormemente el desarrollo y obtener de manera automtica caractersticas avanzadas de seguridad, rendimiento, etc... En realidad .NET es mucho ms que eso ya que ofrece un entorno gestionado de ejecucin de aplicaciones, nuevos lenguajes de programacin y compiladores, y permite el desarrollo de todo tipo de funcionalidades: desde programas de consola o servicios Windows hasta aplicaciones para dispositivos mviles, pasando por desarrollos de escritorio o para Internet. Son estos ltimos de los que nos ocuparemos en este curso. Pero antes conviene conocer los fundamentos en los que se basa cualquier aplicacin creada con .NET, incluyendo las que nos interesan. El entorno de ejecucin CLR .NET ofrece un entorno de ejecucin para sus aplicaciones conocido como Common Language Runtime o CLR. La CLR es la implementacin de Microsoft de un estndar llamado Common Language Infrastructure o CLI. ste fue creado y promovido por la propia Microsoft pero desde hace aos es un estndar reconocido mundialmente por el ECMA. El CLR/CLI esencialmente define un entorno de ejecucin virtual independiente en el que trabajan las aplicaciones escritas con cualquier lenguaje .NET. Este entorno virtual se ocupa de multitud de cosas importantes para una aplicacin: desde la gestin de la memoria y la vida de los objetos hasta la seguridad y la gestin de subprocesos.

Saul Mamani Mamani

Desarrollo Agil de Software

Todos estos servicios unidos a su independencia respecto a arquitecturas computacionales convierten la CLR en una herramienta extraordinariamente til puesto que, en teora, cualquier aplicacin escrita para funcionar segn la CLI puede ejecutarse en cualquier tipo de arquitectura de hardware. Por ejemplo Microsoft dispone de implementacin de .NET para Windows de 32 bits, Windows de 64 bits e incluso para Windows Mobile, cuyo hardware no tiene nada que ver con la arquitectura de un ordenador comn.

2. El Lenguaje Intermedio y el CLS


Al contrario que otros entornos, la plataforma .NET no est atada a un determinado lenguaje de programacin ni favorece a uno determinado frente a otros. En la actualidad existen implementaciones para varias decenas de lenguajes que permiten escribir aplicaciones para la plataforma .NET. Los ms conocidos son Visual Basic .NET, C# o J#, pero existen implementaciones de todo tipo, incluso de COBOL!. Lo mejor de todo es que cualquier componente creado con uno de estos lenguajes puede ser utilizado de forma transparente desde cualquier otro lenguaje .NET. Adems, como ya se ha comentado, es posible ejecutar el cdigo .NET en diferentes plataformas y sistemas operativos. Cmo se consigue esta potente capacidad? Dentro de la CLI, existe un lenguaje llamado IL (Intermediate Language o Lenguaje Intermedio) que est pensado de forma independiente al procesador en el que se vaya a ejecutar. Es algo parecido al cdigo ensamblador pero de ms alto nivel y creado para un hipottico procesador virtual que no est atado a una arquitectura determinada. Cuando se compila una aplicacin escrita en un lenguaje .NET cualquiera (da igual que sea VB, C# u otro de los soportados), el compilador lo que genera en realidad es un nuevo cdigo escrito en este lenguaje intermedio. As, todos los lenguajes .NET se usan como capa de ms alto nivel para producir cdigo IL. Un elemento fundamental de la CLR es el compilador JIT (just-in-time). Su cometido es el de compilar bajo demanda y de manera transparente el cdigo escrito en lenguaje intermedio a lenguaje nativo del procesador fsico que va a ejecutar el cdigo. Al final, lo que se ejecuta es cdigo nativo que ofrece un elevado rendimiento. Esto es cierto tambin para las aplicaciones Web escritas con ASP.NET y contrasta con las aplicaciones basadas en ASP clsico que eran interpretadas, no compiladas, y que jams podran llegar al nivel de desempeo que ofrece ASP.NET. La siguiente figura muestra el aspecto que tiene el cdigo intermedio de una aplicacin sencilla y se puede obtener usando el desemsamblador que viene con la plataforma .NET.

Saul Mamani Mamani

Desarrollo Agil de Software

Figura 1.1. Cdigo en lenguaje intermedio obtenido con ILDASM.exe La especificacin comn de los lenguajes y el sistema de tipos comunes Para conseguir la interoperabilidad entre lenguajes no slo llega con el lenguaje intermedio, sino que es necesario disponer de unas "reglas del juego" que definan un conjunto de caractersticas que todos los lenguajes deben incorporar. A este conjunto regulador se le denomina Common Language Specification (CLS) o, en castellano, especificacin comn de los lenguajes. Entre las cuestiones que regula la CLS se encuentran la nomenclatura, la forma de definir los miembros de los objetos, los metadatos de las aplicaciones, etc... Una de las partes ms importantes de la CLS es la que se refiere a los tipos de datos. Si alguna vez ha programado la API de Windows o ha tratado de llamar a una DLL escrita en C++ desde Visual Basic 6 habr comprobado lo diferentes que son los tipos de datos de VB6 y de C++. Para evitar este tipo de problemas y poder gestionar de forma eficiente y segura el acceso a la memoria, la CLS define un conjunto de tipos de datos comunes ( Common Type System o CTS) que indica qu tipos de datos se pueden manejar, cmo se declaran y se utilizan stos y de qu manera se deben gestionar durante la ejecucin. Si nuestras bibliotecas de cdigo utilizan en sus interfaces hacia el exterior datos definidos dentro de la CTS no existirn problemas a la hora de utilizarlos desde cualquier otro cdigo escrito en la plataforma .NET. Cada lenguaje .NET utiliza una sintaxis diferente para cada tipo de datos. As, por ejemplo, el tipo comn correspondiente a un nmero entero de 32 bits (System.Int32) se denomina Integer en Visual Basic .NET, pero se llama int en C#. En ambos casos representan el mismo tipo de datos que es lo que cuenta. Nota: En ASP 3.0 se suele usar VBScript como lenguaje de programacin. En este lenguaje interpretado, al igual que en VB6, un Integer representaba un entero de 16 bits. Los enteros de 32 bits eran de tipo Long. Es un fallo muy comn usar desde Visual Basic .NET el tipo Integer pensando que es de 16 bits cuando en realidad es capaz de albergar nmeros mucho mayores. Tngalo en cuenta cuando empiece a programar. Existen tipos por valor (como los enteros que hemos mencionado o las enumeraciones) y tipos por referencia (como las clases). En el siguiente mdulo se profundiza en todas estas cuestiones.

Saul Mamani Mamani

Desarrollo Agil de Software

3. La biblioteca de clases de .NET


Todo lo que se ha estado comentando hasta ahora en el curso constituye la base de la plataforma .NET. Si bien es muy interesante y fundamental, por s mismo no nos servira de mucho para crear programas si debisemos crear toda la funcionalidad desde cero. Obviamente esto no es as, y la plataforma .NET nos ofrece infinidad de funcionalidades "de fbrica" que se utilizan como punto de partida para crear las aplicaciones. Existen funcionalidades bsicas (por ejemplo todo lo relacionado con la E/S de datos o la seguridad) y funcionalidades avanzadas en las que se fundamentan categoras enteras de aplicaciones (acceso a datos, creacin de aplicaciones Web...). Toda esta funcionalidad est implementada en forma de bibliotecas de funciones que fsicamente se encuentran en diversas DLL (bibliotecas de enlazado dinmico). A su conjunto se le denomina Base Classes Library (Biblioteca de clases base o BCL) y forman parte integral de la plataforma .NET, es decir, no se trata de aadidos que se deban obtener o adquirir aparte. La siguiente figura ilustra a vista de pjaro la arquitectura conceptual de la plataforma .NET. En ella se pueden observar los elementos que se han mencionado en apartados anteriores (lenguajes, CLR, CLS...) y en qu lugar de se ubican las bibliotecas de clases base:

Figura 1.2. Distintos elementos de la plataforma .NET y cmo se relacionan entre s. Resulta muy til para comprender lo explicado hasta ahora. No se preocupe si hay elementos que no conoce, ms adelante los estudiaremos todos. Todo lo que se encuentra en la BCL forma parte de la plataforma .NET. De hecho existe tal cantidad de funcionalidad integrada dentro de estas bibliotecas (hay decenas de miles de clases) que el mayor esfuerzo que todo programador que se inicia en .NET debe hacer es el aprendizaje de las ms importantes. De todos modos Visual Studio ofrece mucha ayuda contextual (documentacin, Intellisense...) y una vez que se aprenden los rudimentos resulta fcil ir avanzando en el conocimiento de la BCL a medida que lo vamos necesitando.

Saul Mamani Mamani

Desarrollo Agil de Software

Los espacios de nombres Dada la ingente cantidad de clases que existen debe existir algn modo de organizarlas de un modo coherente. Adems hay que tener en cuenta que podemos adquirir ms funcionalidades (que se traducen en clases) a otros fabricantes, por no mencionar que crearemos continuamente nuevas clases propias. Para solucionar este problema existen en todos los lenguajes .NET los espacios de nombres o namespaces. Un espacio de nombres no es ms que un identificador que permite organizar de modo estanco las clases que estn contenidas en l as como otros espacios de nombres. As, por ejemplo, todo lo que tiene que ver con el manejo de estructuras de datos XML en la plataforma .NET se encuentra bajo el espacio de nombres System.Xml. La funcionalidad fundamental para crear aplicaciones Web est en el espacio de nombres System.Web. ste a su vez contiene otros espacios de nombres ms especializados como System.Web.Caching para la persistencia temporal de datos, System.Web.UI.WebControls, que contiene toda la funcionalidad de controles Web para interfaz de usuario, etc...

4. Acceso a datos con ADO.NET


El acceso a fuentes de datos es algo indispensable en cualquier lenguaje o plataforma de desarrollo. La parte de la BCL que se especializa en el acceso a datos se denomina de forma genrica como ADO.NET. Si usted ha programado con Visual Basic 6.0 o con ASP, ha empleado en su cdigo con total seguridad la interfaz de acceso a datos conocida como ADO (ActiveX Data Objects), puede que combinado con ODBC (Open Database Connectivity). Si adems es usted de los programadores con solera y lleva unos cuantos aos en esto es probable que haya usado RDO o incluso DAO, todos ellos mtodos mucho ms antiguos. ADO.NET ofrece una funcionalidad completamente nueva, que tiene poco que ver con lo existente hasta la fecha en el mercado. Sin embargo, con el nimo de retirar barreras a su aprendizaje, Microsoft denomin a su nuevo modelo de acceso a datos con un nombre similar y algunas de sus clases recuerdan a objetos de propsito anlogo en el vetusto ADO. ADO.NET es un modelo de acceso mucho ms orientado al trabajo desconectado de las fuentes de datos de lo que nunca fue ADO. Si bien este ltimo ofreca la posibilidad de desconectar los Recordsets y ofreca una forma de serializacin de estos a travs de las diferentes capas de una aplicacin, el mecanismo no es ni de lejos tan potente como el que nos ofrece ADO.NET. El objeto ms importante a la hora de trabajar con el nuevo modelo de acceso a datos es el DataSet. Sin exagerar demasiado podramos calificarlo casi como un motor de datos relacionales en memoria. Aunque hay quien lo asimila a los clsicos Recordsets su funcionalidad va mucho ms all como se ver en el correspondiente mdulo. Arquitectura de ADO.NET El concepto ms importante que hay que tener claro sobre ADO.NET es su modo de funcionar, que se revela claramente al analizar su arquitectura:

Saul Mamani Mamani

Desarrollo Agil de Software

Figura 1.3.- Arquitectura de ADO.NET Existen dos capas fundamentales dentro de su arquitectura: la capa conectada y la desconectada. Capa conectada La primera de ellas contiene objetos especializados en la conexin con los orgenes de datos. As, la clase genrica Connection se utiliza para establecer conexiones a los orgenes de datos. La clase Command se encarga de enviar comandos de toda ndole al origen de datos. Por fin la clase DataReader est especializada en leer los resultados de los comandos mientras se permanece conectado al origen de datos. La clase DataAdapter hace uso de las tres anteriores para actuar de puente entre la capa conectada y la desconectada. Estas clases son abstractas, es decir, no tienen una implementacin real de la que se pueda hacer uso directamente. Es en este punto en donde entran en juego los proveedores de datos. Cada origen de datos tiene un modo especial de comunicarse con los programas que los utilizan, adems de otras particularidades que se deben contemplar. Un proveedor de datos de ADO.NET es una implementacin concreta de las clases conectadas abstractas que hemos visto, que hereda de stas y que tiene en cuenta ya todas las particularidades del origen de datos en cuestin. As, por ejemplo, las clases especficas para acceder a SQL Server se llaman SqlConnection, SqlCommand, SqlDataReader y SqlDataAdapter y se encuentran bajo el espacio de nombres System.Data.SqlClient. Es decir, al contrario que en ADO clsico no hay una nica clase Connection o Command que se use en cada caso, si no que existen clases especializadas para conectarse y recuperar informacin de cada tipo de origen de datos. Nota: El hecho de utilizar clases concretas para acceso a las fuentes de datos no significa que no sea posible escribir cdigo independiente del origen de datos. Todo lo contrario. La plataforma .NET ofrece grandes facilidades de escritura de cdigo genrico basadas en el uso de herencia e implementacin de interfaces. De hecho la versin 2.0 de .NET ofrece grandes novedades especficamente en este mbito.

Saul Mamani Mamani

Desarrollo Agil de Software

Existen proveedores nativos, que son los que se comunican directamente con el origen de datos (por ejemplo el de SQL Server o el de Oracle), y proveedores "puente", que se utilizan para acceder a travs de ODBC u OLEDB cuando no existe un proveedor nativo para un determinado origen de datos. Nota: Estos proveedores puente, si bien muy tiles en determinadas circunstancias, ofrecen un rendimiento menor debido a la capa intermedia que estn utilizando (ODBC u OLEDB). Un programador novel puede sentir la tentacin de utilizar siempre el proveedor puente para OLEDB y as escribir cdigo compatible con diversos gestores de datos de forma muy sencilla. Se trata de un error y siempre que sea posible es mejor utilizar un proveedor nativo. Capa desconectada Una vez que ya se han recuperado los datos desde cualquier origen de datos que requiera una conexin sta ya no es necesaria. Sin embargo sigue siendo necesario trabajar con los datos obtenidos de una manera flexible. Es aqu cuando la capa de datos desconectada entra en juego. Adems, en muchas ocasiones es necesario tratar con datos que no han sido obtenidos desde un origen de datos relacional con el que se requiera una conexin. A veces nicamente necesitamos un almacn de datos temporal pero que ofrezca caractersticas avanzadas de gestin y acceso a la informacin. Por otra parte las conexiones con las bases de datos son uno de los recursos ms escasos con los que contamos al desarrollar. Su mala utilizacin es la causa ms frecuente de cuellos de botella en las aplicaciones y de que stas no escalen como es debido. Esta afirmacin es especialmente importante en las aplicaciones Web en las que se pueden recibir muchas solicitudes simultneas de cualquier parte del mundo. Finalmente otro motivo por el que es importante el uso de los datos desconectado de su origen es la transferencia de informacin entre capas de una aplicacin. stas pueden encontrarse distribuidas por diferentes equipos, e incluso en diferentes lugares del mundo gracias a Internet. Por ello es necesario disponer de algn modo genrico y eficiente de poder transportar los datos entre diferentes lugares, utilizarlos en cualquiera de ellos y posteriormente tener la capacidad de conciliar los cambios realizados sobre ellos con el origen de datos del que proceden. Todo esto y mucho ms es lo que nos otorga el uso de los objetos DataSet. Es obvio que no se trata de tareas triviales, pero los objetos DataSet estn pensados y diseados con estos objetivos en mente. Como podremos comprobar ms adelante en este curso es bastante sencillo conseguir estas funcionalidades tan avanzadas y algunas otras simplemente usando de manera adecuada este tipo de objetos. Nota: Otra interesante caracterstica de los DataSet es que permiten gestionar simultneamente diversas tablas (relaciones) de datos, cada una de un origen diferente si es necesario, teniendo en cuenta las restricciones y las relaciones existentes entre ellas. Los DataSet, como cualquier otra clase no sellada de .NET, se pueden extender mediante herencia. Ello facilita una tcnica avanzada que consiste en crear tipos nuevos de DataSet especializados en la gestin de una informacin concreta (por ejemplo un conjunto de tablas relacionadas). Estas nuevas tipos clases se denominan genricamente DataSet Tipados, y permiten el acceso mucho ms cmodo a los datos que representan, verificando reglas de negocio, y validaciones de tipos de datos ms estrictas.

5. Aplicaciones Windows Forms


Las aplicaciones de escritorio son aquellas basadas en ventanas y controles comunes de Windows que se ejecutan en local. Son el mismo tipo de aplicaciones que antes construiramos con Visual Basic 6 u otros entornos similares.

Saul Mamani Mamani

Desarrollo Agil de Software

En la plataforma .NET el espacio de nombres que ofrece las clases necesarias para construir aplicaciones de escritorio bajo Windows se denomina Windows Forms. Este es tambin el nombre genrico que se le otorga ahora a este tipo de programas basados en ventanas. Windows Forms est constituido por multitud de clases especializadas que ofrecen funcionalidades para el trabajo con ventanas, botones, rejillas, campos de texto y todo este tipo de controles habituales en las aplicaciones de escritorio. Visual Studio ofrece todo lo necesario para crear visualmente este tipo de programas, de un modo similar aunque ms rico al que ofreca el entorno de desarrollo integrado de Visual Basic.

Figura 1.4.- Diseador de interfaces de aplicaciones de escritorio con Windows Forms en Visual Studio 2005. Al contrario que en en VB6, .NET proporciona control sobre todos los aspectos de las ventanas y controles, no dejando nada fuera del alcance del programador y otorgando por lo tanto la mxima flexibilidad. Los formularios (ventanas) son clases que heredan de la clase base Form, y cuyos controles son miembros de sta. De hecho se trata nicamente de cdigo y no es necesario (aunque s muy recomendable) emplear el diseador grfico de Visual Studio para crearlas. Este es el aspecto que presenta parte del cdigo que genera la interfaz mostrada en la anterior figura:

Saul Mamani Mamani

Desarrollo Agil de Software

10

Figura 1.5.- Cdigo autogenerado por Visual Studio para crear la interfaz de la figura anterior. Al contrario que en Visual Basic tradicional, en donde siempre existan instancias por defecto de los formularios que podamos usar directamente, en .NET es necesario crear un objeto antes de poder hacer uso de los formularios:
Dim frm As New MiFormulario frm.Show()

Todos los controles heredan de una clase Control por lo que conservan una serie de funcionalidades comunes muy interesantes, como la capacidad de gestionarlos en el diseador (movindolos, alinendolos...), de definir mrgenes entre ellos o hacer que se adapten al tamao de su contenedor.

6. Aplicaciones Web Forms


Tradicionalmente las aplicaciones Web se han desarrollado siguiendo un modelo mixto que intercalaba cdigo HTML y JavaScript propio de pginas Web (parte cliente), junto con cdigo que se ejecutara en el servidor (parte servidora). Este modelo contrastaba por completo con el modelo orientado a eventos seguido por las principales herramientas de desarrollo de aplicaciones de escritorio. En el modelo orientado a eventos se define la interfaz de usuario colocando controles en un contenedor y se escribe el cdigo que actuar como respuesta a las interacciones de los usuarios sobre estos controles. Si conoce el diseador de VB6 o de Windows Forms mencionado en el apartado anterior sabe exactamente a qu nos referimos. Hacer esto en una aplicacin de escritorio no tiene mayor dificultad ya que todo el cdigo se ejecuta en el mismo lugar. La principal caracterstica de las aplicaciones Web sin embargo es que se la interfaz de usuario (lo que los usuarios de la aplicacin ven) se ejecuta en un lugar diferente al cdigo de la aplicacin que reside en un servidor. Para mayor desgracia estas aplicaciones se basan en el uso del protocolo HTTP que es un protocolo sin estado y que no conserva la conexin entre dos llamadas consecutivas.

Saul Mamani Mamani

Desarrollo Agil de Software

11

Por ejemplo, el siguiente cdigo ilustra el cdigo que es necesario escribir en ASP para disponer de una pgina que rellena una lista de seleccin con unos cuantos nombres (podran salir de una base de datos y an sera ms complicado), y que dispone de un botn que escribe un saludo para el nombre que se haya elegido de la lista.

Figura 1.6.- Cdigo ASP sencillo que genera una lista de seleccin y saluda al presionar un botn. Obviamente se podra haber simplificado sin enviar el formulario al servidor usando JavaScript en el cliente para mostrar el saludo, pero la intencin es ilustrar la mezcla de cdigo de cliente y de servidor que existe en este tipo de aplicaciones. Las principales desventajas de este tipo de codificacin son las siguientes: 1. No existe separacin entre el diseo y la lgica de las aplicaciones. Si queremos cambiar sustancialmente la apariencia de la aplicacin Web lo tendremos bastante complicado puesto que el cdigo del servidor est mezclado entre el HTML. 2. En ASP clsico no existe el concepto de control para la interfaz de usuario. Lo nico que hay es HTML y JavaScript que se deben generar desde el servidor. En el ejemplo de la figura para generar un control de lista con unos elementos no podemos asignar una propiedad de la lista (porque no existe tal lista), sino que tenemos que crear un bucle que genere los elementos HTML necesarios para generarla. Tampoco disponemos de un diseador visual que nos permita gestionar los controles y elementos HTML existentes, y menos cuando stos se encuentran mezclados con el cdigo del servidor. 3. No disponemos de forma de detectar en el servidor que se ha realizado algo en el cliente . El cliente se encuentra desconectado desde el momento en que se termina de devolver la pgina. Slo se recibe informacin en el servidor cuando se solicita una nueva pgina o cuando se enva un formulario tal y como se hace en el ejemplo, debindonos

Saul Mamani Mamani

Desarrollo Agil de Software

12

encargar nosotros de averiguar si la peticin es la primera vez que se hace o no, y de dar la respuesta adecuada. En cualquier caso es mucho menos intuitivo que el modelo de respuesta a eventos de una aplicacin de escritorio. 4. No existe constancia del estado de los controles de cada pgina entre las llamadas. En cada ejecucin de la pgina tendremos que recrear completamente la salida. Por ejemplo si presionamos el botn Di Hola tenemos que escribir adems de la etiqueta Hola, nombre el resto de la pantalla, incluyendo la lista con todos los nombres dejando seleccionado el mismo que hubiese antes. Si estos nombres viniesen de una base de datos esto puede ser todava ms ineficiente y tendremos que buscar mtodos alternativos para generarlos ya que en ASP tampoco se deben almacenar en los objetos de sesin y/o aplicacin Recordsets resultado de consultas. 5. No existe el concepto de Propiedad de los controles. En una aplicacin Windows asignamos el texto de un campo usando una propiedad (por ejemplo Text1.Text = "Hola") y sta se asigna y permanece en la interfaz sin que tengamos que hacer nada. En una aplicacin Web clsica tenemos que almacenarlas en algn sitio (una variable de sesin o un campo oculto) para conservarlas entre diferentes peticiones de una misma pgina. 6. Los controles complejos no tienen forma de enviar sus valores al servidor. Si intentamos crear una interfaz avanzada que utilice tablas y otros elementos que no son controles de entrada de datos de formularios de HTML tendremos que inventarnos mecanismos propios para recoger esos datos y enviarlos al servidor. La principal aportacin de ASP.NET al mundo de la programacin es que ha llevado a la Web el paradigma de la programacin orientada a eventos propia de aplicaciones de escritorio, ofreciendo:

Separacin entre diseo y lgica. Componentes de interfaz de usuario, tanto estndar como de terceras empresas o propios. Diseadores grficos. Eventos. Estado. Enlazado a datos desde la interfaz.

Saul Mamani Mamani

Desarrollo Agil de Software

13

MODULO II
CARACTERISTICAS DEL LENGUAJE C#

Contenido

Leccin 1: El sistema de tipos o Tipos primitivos o Variables y constantes o Enumeraciones o Arrays (matrices) Leccin 2: Clases y estructuras o Clases o Definir una clase o Instanciar una clase o Estructuras o Accesibilidad o Propiedades o Interfaces Leccin 3: Manejo de excepciones o Manejo de excepciones Leccin 4: Eventos y delegados o Eventos o Delegados o Definir y producir eventos en una clase Leccin 5: Atributos o Atributos

LECCION 1: Introduccin

SISTEMA DE TIPOS

En esta primera leccin veremos los tipos de datos que .NET Framework pone a nuestra disposicin y cmo tendremos que usarlos desde Visual C# 2005. A continuacin daremos un repaso a conceptos bsicos o elementales sobre los tipos de datos, que aunque puede que nos sean familiares, es importante que lo veamos para poder comprender mejor cmo estn definidos y organizados los tipos de datos en .NET Framework. Tipos de datos de .NET Visual C# 2005 est totalmente integrado con .NET Framework, por tanto los tipos de datos que podremos usar con este lenguaje sern los definidos en este "marco de trabajo", por este motivo vamos a empezar usando algunas de las definiciones que nos encontraremos al recorrer la documentacin que acompaa a este lenguaje de programacin. Los tipos de datos que podemos usar en Visual C# 2005 son los mismo tipos de datos definidos en .NET Framework y por tanto estn soportados por todos los lenguajes que usan esta tecnologa. Estos tipos comunes se conocen como el

Saul Mamani Mamani

Desarrollo Agil de Software

14

Common Type System, (CTS), que traducido viene a significar el sistema de tipos comunes de .NET. El hecho de que los tipos de datos usados en todos los lenguajes .NET estn definidos por el propio Framework nos asegura que independientemente del lenguaje que estemos usando, siempre utilizaremos el mismo tipo interno de .NET, si bien cada lenguaje puede usar un nombre (o alias) para referirse a ellos, aunque lo importante es que siempre sern los mismos datos, independientemente de cmo se llame en cada lenguaje. Esto es una gran ventaja, ya que nos permite usarlos sin ningn tipo de problemas para acceder a ensamblados creados con otros lenguajes, siempre que esos lenguajes sean compatibles con los tipos de datos de .NET. En los siguientes enlaces tenemos los temas a tratar en esta primera leccin del mdulo sobre las caractersticas del lenguaje Visual C# 2005.

Tipos primitivos o Sufijos o caracteres y smbolos identificadores para los tipos o Promociones numricas implcitas (automticas) o Conversiones numricas explcitas Cundo debemos usar las conversiones explcitas? Siempre funcionan las conversiones explcitas? Funciones de conversin o Tipos por valor y tipos por referencia Variables y constantes o Declarar constantes o Consejo para usar las constantes o Declarar variables Declarar variables y asignar el valor inicial o El tipo de datos char o Cadenas de caracteres Enumeraciones: Constantes agrupadas o El nombre de los miembros de las enumeraciones o Los valores de una enumeracin no son simples nmeros Arrays (matrices) o Declarar arrays o Declarar e inicializar un array o Cambiar el tamao de un array o Eliminar el contenido de un array o Los arrays son tipos por referencia

1. Tipos primitivos
Veamos en la siguiente tabla los tipos de datos definidos en .NET Framework y los alias utilizados en Visual C# 2005.
.NET Framework System.Boolean System.Byte System.Int16 System.Int32 System.Int64 System.Single bool byte short int long float C# 2005

Saul Mamani Mamani

Desarrollo Agil de Software

15

System.Double System.Decimal System.Char System.String System.Object System.DateTime System.SByte System.UInt16 System.UInt32 System.UInt64

double decimal char string object N.A. (DateTime) sbyte ushort uint ulong

Tabla 2.1. Tipos de datos de .NET y su equivalencia en C# El nico tipo de datos que no tiene un "alias" en C# es tipo DateTime, por tanto siempre usaremos el definido en el propio .NET. Debemos tener en cuenta, al menos si el rendimiento es una de nuestra prioridades, que las cadenas en .NET son inmutables, es decir, una vez que se han creado no se pueden modificar y en caso de que queramos cambiar el contenido, .NET se encarga de desechar la anterior y crear una nueva cadena, por tanto si usamos las cadenas para realizar concatenaciones (unin de cadenas para crear una nueva), el rendimiento ser muy bajo, si bien existe una clase en .NET que es ideal para estos casos y cuyo rendimiento es superior al tipo string: la clase StringBuilder. Las ltimas filas mostradas en la tabla son tipos especiales que si bien son parte del sistema de tipos comunes (CTS) no forman parte de la Common Language Specification (CLS), es decir la especificacin comn para los lenguajes "compatibles" con .NET, por tanto, si queremos crear aplicaciones que puedan interoperar con todos los lenguajes de .NET, esos tipos no debemos usarlos como valores de devolucin de funciones ni como tipo de datos usado en los parmetros de las funciones. Los tipos mostrados en la tabla 2.1 son los tipos primitivos de .NET y por extensin de Visual C# 2005, es decir son tipos "elementales" para los cuales cada lenguaje define su propia palabra clave equivalente con el tipo definido en el CTS de .NET Framework. Todos estos tipos primitivos podemos usarlos tanto por medio de los tipos propios de Visual C#, los tipos definidos en .NET o bien como literales. Por ejemplo, podemos definir un nmero entero largo ( long) literal indicndolo con el sufijo L: 12345L o bien asignndolo a un valor de tipo long o a un tipo Sytem.Int64 de .NET. La nica excepcin de los tipos mostrados en la tabla 2.1 es el tipo de datos object, este es un caso especial del que nos ocuparemos en la prxima leccin. Sufijos o caracteres y smbolos identificadores para los tipos Cuando usamos valores literales numricos en Visual C# 2005, el tipo de datos que le asigna el compilador es el que ms se acerque al tipo en el que lo estamos asignando, es decir, el propio compilador de C# hace lo que se conoce como "promocin de tipos", esto significa que si estamos asignando un valor a una variable de tipo entero, el compilador comprobar que tipo es el ms adecuado, usando este orden: int, uint, long, ulong. Si el tipo al que queremos asignar el valor es un tipo diferente a los cuatro que hemos mostrado, el compilador nos avisar que el tipo de datos que estamos usando no es el correcto. Por otro lado, si la constante contiene el separador de decimales, el compilador de C# asumir que estamos trabajando con valores de coma flotante, pero siempre asumir que el valor es de tipo double. Por tanto si el tipo que hay a la izquierda de esa constante no es double, el compilador mostrar un error. Estos errores de compilacin los podemos evitar de dos formas: La primera es usando un sufijo en la constante, con ese sufijo le indicamos que el tipo de datos que queremos que tenga esa constante. En la tabla 2.2 tenemos los sufijos que podemos usar en C#. La segunda forma de evitar estos errores de compilacin, es haciendo una conversin explcita de tipos. En la seccin siguiente veremos algunos ejemplos de conversiones de tipos.

Saul Mamani Mamani

Desarrollo Agil de Software

16

Tipo de datos short int long float double decimal ushort uint ulong string

Sufijo de C# N.A. N.A. L F D M N.A. UI UL N.A.

Tabla 2.2. Sufijos para identificar los tipos de datos Los indicados con N.A. es que no existe un sufijo para ese tipo. Nota: Los sufijos pueden indicarse en minsculas, maysculas o cualquier combinacin de mayscula y minscula. Por ejemplo, el sufijo de un tipo ulong puede ser: UL, Ul, ul, uL, LU, Lu, lU o lu. Para evitar confusiones, se recomienda siempre indicarlos en maysculas. El uso de estos caracteres nos puede resultar de utilidad particularmente para los tipos de datos que no se pueden convertir de forma automtica. Por ejemplo, si queremos asignar este valor literal 12345678901234567890.50 a un tipo decimal, el compilador de Visual C# nos avisar de que no se puede asignar un valor doble a uno decimal, y nos recomienda que usemos el sufijo M en el literal, tal como vemos en la figura 2.1.

Figura 2.1. Error de compilacin al asignar un valor double a una variable decimal Nota: Este error nos lo mostrar despus de compilar el cdigo, ya que en C# no se hace una pre-compilacin del cdigo mientras escribimos. Tal como indicamos en la nota, el entorno de desarrollo integrado (IDE) de Visual C# 2005 nos avisar de los errores de compilacin una vez que hayamos compilado el cdigo, aunque los errores sintcticos los mostrar justo al producirse. Estos errores sintcticos los mostrar con los dientes de sierra de color rojo, tal como podemos comprobar en la figura 2.2 al intentar asignar un valor entero que es demasiado grande.

Saul Mamani Mamani

Desarrollo Agil de Software

17

Figura 2.2. Error sintctico y resalte en el IDE de Visual C# Promociones numricas implcitas (automticas) Como hemos comentado anteriormente, el compilador de C# siempre intentar usar el valor ms adecuado cuando estamos utilizando distintos tipos de datos. Esas promociones, (convertir un valor de un tipo pequeo en otro de mayor capacidad), las realizar solamente en los tipos de datos que no produzcan prdida de informacin o que estn contempladas en el compilador. Por ejemplo, podemos asignar un valor de tipo short a un tipo long, ya que no se producir ningn tipo de prdida de informacin, porque el valor de un short es menor que el de un long. En la tabla 2.3 tenemos las conversiones implcitas (o automticas) que puede hacer el compilador de Visual C#.
Del tipo sbyte byte short ushort int uint long char float ulong Al tipo (o tipos) short, int, long, float, double, o decimal short, ushort, int, uint, long, ulong, float, double, o decimal int, long, float, double, o decimal int, uint, long, ulong, float, double, o decimal long, float, double, o decimal long, ulong, float, double, o decimal float, double, o decimal ushort, int, uint, long, ulong, float, double, o decimal double float, double, o decimal

Tabla 2.3. Conversiones implcitas En esta tabla se incluye el tipo char, ya que aunque un char representa un carcter, realmente es un tipo entero. A pesar de que podamos convertir un char en un tipo numrico, no podemos hacer lo contrario, al menos de forma implcita. Conversiones numricas explcitas Hay ocasiones en las que el compilador considera que no se pueden hacer ciertas conversiones o asignaciones entre variables o valores literales de distintos tipos de datos. Pero si nosotros sabemos que es posible hacerlas, podemos obligar al compilador a que las acepte. De esa forma no recibiremos ningn error y la aplicacin se crear correctamente. Otra cosa es que cuando el programa est en ejecucin esa conversin que hemos hecho de forma explcita, es decir, obligando al compilador a hacerla, (ahora veremos cmo podemos obligar al compilador a que cumpla nuestras rdenes), puede fallar, por la sencilla razn de que no se pueda hacer. Para hacer una conversin explcita, lo haremos de esta forma:
int entero = (int)valor_long;

Saul Mamani Mamani

Desarrollo Agil de Software

18

Es decir, incluimos entre parntesis el tipo de datos al que queremos convertir y a continuacin indicamos el valor a convertir. Cundo debemos usar las conversiones explcitas? Las conversiones explcitas las tenemos que usar en todos los casos que no se adecuen a los casos mostrados en la tabla 2.3. Siempre funcionan las conversiones explcitas? Habr situaciones en que una conversin explcita produzca un error de desbordamiento o bien se asigne el valor mximo permitido por el tipo de destino desestimando el resto del valor asignado. Esto depender de ciertas circunstancias, dependiendo del tipo de datos que intervengan en la conversin y de que usemos ciertas instrucciones de C# o de la utilizacin de opciones del compilador. Cuando convertimos un literal de tipo entero de mayor capacidad en uno que tiene menos, el compilador producir un error, porque "sabe" que el valor grande no "cabe" en el pequeo. Este comportamiento lo podemos cambiar de forma que si hacemos ese tipo de conversin, slo se almacene en el tipo de menor capacidad el valor que pueda soportar, desechando el resto, pero debido a que esto puede producir prdida de informacin, hay que hacerlo de forma explcita, indicndolo con la instruccin unchecked. En el siguiente ejemplo, se producir un error de compilacin indicndonos que el valor que queremos almacenar es muy grande para el tipo de destino:
short sh = (short)1234567890;

Para solucionarlo debemos usar la instruccin unchecked:


short sh = unchecked((short)1234567890);

Si en lugar de trabajar con un literal lo hacemos con una variable, el compilador no sabe que valor contiene la variable, por tanto compilar sin problemas, aunque ese error de desbordamiento se puede producir en tiempo de ejecucin. En este caso concreto o en otros en los que en la conversin intervengan variables de tipos enteros (int, short, byte, long, etc.), no se producir ningn error de compilacin ni en tiempo de ejecucin, el valor que se almacenar en el entero de menor capacidad ser el valor mximo que dicho tipo pueda contener, todo esto sin necesidad de usar la instruccin unchecked, ya que ese es el comportamiento predeterminado del compilador, para cambiarlo, debemos usar la instruccin checked o bien usar la opcin /checked+ del compilador. De forma predeterminada, (usando unchecked), si en esas conversiones explcitas intervienen tipos decimales o de punto flotante el comportamiento depender del tipo de datos que reciba el valor. Asignar un valor de tipo flotante a uno entero no producir nunca un error de desbordamiento (overflow), pero si el valor a convertir es un tipo decimal, se producir un error si el contenido de ese valor decimal es mayor que el soportado por el tipo entero al que queremos asignarlo. Por ejemplo, si utilizamos este cdigo, todo funcionar bien, porque el valor de m es adecuado para un valor int:
decimal m = 123456.55M; int i = (int)m;

Pero si tenemos este otro cdigo, se producir un error en tiempo de ejecucin, ya que el valor de m es mayor que el soportado por un valor de tipo int:
decimal m = 12345678901234567890.55M;

Saul Mamani Mamani

Desarrollo Agil de Software

19

int i = (int)m;

Funciones de conversin En C# no existen funciones de conversin como instrucciones independientes, siempre se utiliza el formato mostrado; en el argot se utiliza la expresin inglesa utilizada para indicar que hacemos una conversin: cast. Aunque C# no dispone de funciones de conversin, .NET Famework si que las tiene, adems de que los propios tipos de datos soportan algunas, por ejemplo, para convertir un valor numrico en uno de tipo cadena, podemos usar el mtodo ToString() que todos los tipos de .NET tienen. Pero si queremos realizar otras conversiones entre distintos tipos numricos, usaremos la clase Convert, la cual tiene funciones para realizar conversiones entre los tipos numricos soportados por .NET:
i = Convert.ToInt32(m);

Tipos por valor y tipos por referencia Los tipos de datos de .NET los podemos definir en dos grupos:

Tipos por valor Tipos por referencia

Los tipos por valor son tipos de datos cuyo valor se almacena en la pila o en la memoria "cercana", como los numricos que hemos visto. Podemos decir que el acceso al valor contenido en uno de estos tipos es directo, es decir se almacena directamente en la memoria reservada para ese tipo y cualquier cambio que hagamos lo haremos directamente sobre dicho valor, de igual forma cuando copiamos valores de un tipo por valor a otro, estaremos haciendo copias independientes. Por otro lado, los tipos por referencia se almacenan en el "monto" (heap) o memoria "lejana", a diferencia de los tipos por valor, los tipos por referencia lo nico que almacenan es una referencia (o puntero) al valor asignado. Si hacemos copias de tipos por referencia, realmente lo que copiamos es la referencia propiamente dicha, pero no el contenido. Estos dos casos los veremos en breve con ms detalle.

2. Variables y constantes
Disponer de todos estos tipos de datos no tendra ningn sentido si no los pudiramos usar de alguna otra forma que de forma literal. Y aqu es donde entran en juego las variables y constantes. Definamos brevemente estos conceptos. Las constantes son valores que nunca cambian y pueden ser de dos tipos: Constantes literales, por ejemplo, cuando usamos 12345, estamos usando un valor constante, ya que ese nmero siempre tendr el mismo valor. Constantes con nombre, son constantes a las que le damos un nombre y tampoco pueden cambiar. Por otro lado, las variables son como las constantes con nombres, pero su valor es variable, por tanto, puede que no siempre tengan el mismo valor, de ah que se llamen variables. Declarar constantes: Las constantes literales las usamos directamente, tal como hemos visto anteriormente, pero para usar las constantes con nombre debemos declararlas previamente, para ello utilizaremos la instruccin const, tal como vemos en este ejemplo:
const int maximo = 12345678;

Saul Mamani Mamani

Desarrollo Agil de Software

20

Como podemos comprobar, tenemos que utilizar una instruccin para indicar que es una constante, ( const), seguida del tipo de datos y el valor que le asignaremos a esa constante. Consejo para usar las constantes Siempre que tengamos que indicar un valor constante, ya sea para indicar el mximo o mnimo permitido en un rango de valores o para comprobar el trmino de un bucle, deberamos usar una constante en lugar de un valor literal, de esta forma si ese valor lo usamos en varias partes de nuestro cdigo, si en un futuro decidimos que dicho valor debe ser diferente, nos resultar ms fcil realizar un solo cambio que cambiarlo en todos los sitios en los que lo hemos usado, adems de que de esta forma nos aseguramos de que el cambio se realiza adecuadamente y no tendremos que preocuparnos de las consecuencias derivadas de no haber hecho el cambio en todos los sitios que deberamos. Declarar variables En la declaracin de las variables en Visual C# 2005, siempre hay que indicar el tipo de datos que tendr la variable, por la sencilla razn de que las variables definidas en C# siempre se harn usando el tipo de datos sin necesidad de utilizar ninguna instruccin especial que indique que es una declaracin. Por ejemplo, en este ejemplo, estamos declarando una variable de tipo int y otra de tipo decimal:
int i; decimal m;

Tambin podemos declarar ms de una variable en la misma sentencia. Lo nico que tendremos que hacer es separar las variables con comas. Por ejemplo, el siguiente cdigo definimos tres variables del tipo int:
int a, b, c;

Declarar variables y asignar el valor inicial Cuando declaramos variables en C#, estas estarn en un estado "no iniciado", es decir, no tendrn ningn valor inicial asignado de forma automtica, salvo cuando estas variables estn definidas como campos de una clase. Por tanto, si queremos que en C# tengan un valor inicial, tendremos que asignarlos de forma expresa. Por ejemplo:
int a = 10;

En esa misma lnea podemos declarar y asignar ms variables, simplemente separndolas con comas:
int b = 12, c = 15;

Si queremos declarar ms de una variable pero con tipos diferentes tendremos que separarlas con un punto y coma, que es la forma de decirle al compilador que es una nueva instruccin, por tanto esta declaracin:
decimal m = 22.5M; double d = 55.556;

Ser lo mismo que esta otra:


decimal m = 22.5M;

Saul Mamani Mamani

Desarrollo Agil de Software

21

double d = 55.556;

Como es natural, a una variable podemos asignarle el contenido de una constante "con nombre" que previamente hayamos declarado, esa asignacin tambin podemos hacerla al declarar la variable:
const int maximo = 12345678; int i = maximo;

El tipo de datos char Tal como comentamos anteriormente, el tipo char de C# es un caso especial, realmente es un carcter Unicode, pero internamente es un valor int. Veamos primero cmo declarar variables (o constantes) de tipo char y despus veremos que realmente son enteros (o al menos que en algunas circunstancias se comportan como tales):
char c1 = 'a'; char c2 = (char)49;

En la primera declaracin utilizamos una constante literal para asignarla a una variable de tipo char. Esa constante la indicaremos entre comillas simples. En la segunda usamos una conversin explcita de un tipo numrico entero a un valor char. Al ser un valor entero, podremos hacer operaciones aritmticas con variables de ese tipo, aunque el resultado siempre ser un valor entero, no un carcter. En este cdigo sumamos el valor de las dos constantes y lo asignamos en una variable de tipo int:
int i2 = c1 + c2;

Nota: En la tabla 2.3 tenemos una lista de los tipos de datos que podemos convertir de forma implcita a char. Si queremos volver a convertir el contenido de la variable i en un valor de tipo char tendremos que hacer una conversin (cast) o bien usar la clase Convert.
char c3 = (char)i2; char c4 = Convert.ToChar(i2);

Cadenas de caracteres Las cadenas de caracteres en .NET, y por tanto en C#, se definen indicando la cadena entre comillas dobles. Tal como vimos en la tabla 2.1, la instruccin para declarar cadenas es string:
string s1 = "Hola, ";

Tal como indicamos en el comentario de esa tabla, las cadenas de C# son inmutables, es decir una vez que hemos creado una cadena y la modificamos, se crear una nueva cadena y se desechar la cadena anterior. Por ejemplo, si hacemos esto:
s1 = s1 + "mundo";

Saul Mamani Mamani

Desarrollo Agil de Software

22

Se crear una nueva cadena en la que asignar el contenido de las dos usadas en la expresin de la derecha de la asignacin, el contenido anterior de la variable s1 se desechar y se usar el resultado de unir lo que hubiera antes en esa variable ms la cadena que indicamos despus del signo igual. Para paliar este pobre rendimiento de las cadenas en .NET, podemos usar la clase StringBuilder, la cual deberamos usar preferentemente cuando realicemos concatenaciones o uniones de cadenas, ya que esa clase no crea nuevas instancias y para ello se utilizan mtodos que nos permiten realizar esa unin de cadenas; el ejemplo anterior lo podemos convertir de esta forma:
StringBuilder sb = new StringBuilder(); sb.Append("Hola, "); sb.Append("mundo");

Nota: Si vamos a hacer ms de dos o tres concatenaciones, es recomendable utilizar un objeto del tipo StringBuilder para hacer esas uniones de cadenas y despus usar el mtodo ToString() del objeto StringBuilder para recuperar la cadena completa.

3. Enumeraciones: Constantes agrupadas


Una enumeracin es una serie de constantes que estn relacionadas entre s. La utilidad de las enumeraciones es ms manifiesta cuando queremos manejar una serie de valores constantes con nombre, es decir, podemos indicar un valor, pero en lugar de usar un literal numrico, usamos un nombre, ese nombre es, al fin y al cabo, una constante que tiene un valor numrico. En Visual C# 2005 las enumeraciones pueden ser de cualquier tipo numrico integral, incluso enteros sin signo, aunque el valor predefinido es el tipo int. Podemos declarar una enumeracin de varias formas: 1- Sin indicar el tipo de datos, por tanto sern de tipo int:
enum Colores { Rojo = 1, Verde = 2, Azul = 4

};

2- Indicando el tipo de datos que realmente tendr:


enum ColoresByte : byte { Rojo = 1, Verde = 2, Azul = 4 };

En este segundo caso, el valor mximo que podemos asignar a los miembros de una enumeracin ser el que pueda contener un tipo de datos de tipo byte. 3- Indicando el atributo FlagsAttibute, (realmente no hace falta indicar el sufijo Attribute cuando usamos los atributos) de esta forma podremos usar los valores de la enumeracin para indicar valores que se pueden "sumar" o complementar entre s, pero sin perder el nombre, en breve veremos qu significa esto de "no perder el nombre".
[Flags()] enum ColoresFlags : byte { Rojo = 1, Verde = 2, Azul = 4 };

Saul Mamani Mamani

Desarrollo Agil de Software

23

Nota: Los atributos los veremos con ms detalle en otra leccin de este mismo mdulo. El nombre de los miembros de las enumeraciones Tanto si indicamos o no el atributo Flags a una enumeracin, la podemos usar de esta forma:
Colores c = Colores.Azul | Colores.Rojo;

Es decir, podemos "sumar" los valores definidos en la enumeracin. Como hemos comentado, las enumeraciones son constantes con nombres, pero en C# esta definicin llega ms lejos, de hecho, podemos saber "el nombre" de un valor de una enumeracin, para ello tendremos que usar el mtodo ToString(), (que como ya comentamos, se usa para convertir en una cadena cualquier valor numrico). Por ejemplo, si tenemos la siguiente asignacin:
string s1 = Colores.Azul.ToString();

La variable s1 contendr la palabra "Azul" no el valor "4". Esto es aplicable a cualquier tipo de enumeracin, se haya o no usado el atributo FlagsAttribute. Una vez aclarado este comportamiento de las enumeraciones en C#, veamos que es lo que ocurre cuando sumamos valores de enumeraciones a las que hemos aplicado el atributo Flags y a las que no se lo hemos aplicado. Empecemos por este ltimo caso. Si tenemos este cdigo:
enum Colores : byte { Rojo = 1, Verde = 2, Azul = 4 }; Colores c = Colores.Azul | Colores.Rojo; string s = c.ToString();

El contenido de la variable s ser "5", es decir, la representacin numrica del valor contenido: 4 + 1, ya que el valor de la constante Azul es 4 y el de la constante Rojo es 1. Pero si ese mismo cdigo lo usamos de esta forma (aplicando el atributo Flags a la enumeracin):
[Flags()] enum Colores : byte { Rojo = 1, Verde = 2, Azul = 4 }; Colores c = Colores.Azul | Colores.Rojo; string s = c.ToString();

Saul Mamani Mamani

Desarrollo Agil de Software

24

El contenido de la variable s ser: "Rojo, Azul", es decir, se asignan los nombres de los miembros de la enumeracin que intervienen en ese valor, no el valor "interno". Los valores de una enumeracin no son simples nmeros Como hemos comentado, los miembros de las enumeraciones realmente son valores de un tipo de datos entero (en cualquiera de sus variedades). Por tanto, podemos pensar que podemos usar cualquier valor para asignar a una variable declarada como una enumeracin, al menos si ese valor est dentro del rango adecuado. Pero en C# esto no es posible, al menos si lo hacemos de forma "directa", ya que recibiremos un error indicndonos que no podemos convertir, por ejemplo, un valor entero en un valor del tipo de la enumeracin. En la figura 2.3 podemos ver ese error al intentar asignar el valor 3 a una variable del tipo Colores (definida con el tipo predeterminado int).

Figura 2.3. Error al asignar un valor "normal" a una variable del tipo Colores El error nos indica que no podemos realizar esa asignacin, adems de darnos una pista de que es lo que debemos hacer: convertirlo explcitamente por medio de un "cast". En este caso, la conversin que tenemos que hacer es del tipo Colores al tipo int, por tanto, nuestro cdigo quedara de esta forma:
Colores c1 = ((Colores)3);

Si compilamos y ejecutamos la aplicacin, sta funcionar correctamente. Aunque es posible que usando ese cast no asignemos un valor dentro del rango permitido. En este caso, el valor 3 podramos darlo por bueno, ya que es la suma de 1 y 2 (Rojo y Verde), pero que pasara si el valor asignado es, por ejemplo, 15? En teora no deberamos permitirlo. Estas validaciones podemos hacerlas de dos formas: 1- Con la clsica solucin de comprobar el valor indicado con todos los valores posibles. 2- Usando la funcin IsDefined de la clase Enum de .NET Framework. Esa funcin devolver un valor verdadero o falso dependiendo de que el valor que contiene una variable est o no definido en la enumeracin. En el siguiente ejemplo vemos cmo podemos hacer esa comprobacin:
if( Enum.IsDefined(typeof(Colores), c) == false ) { c = Colores.Azul; } // seguimos con el resto del cdigo

Este cdigo lo que hace es comprobar si el tipo de datos Colores tiene definido el valor contenido en la variable c, en caso de que no sea as, usamos un valor predeterminado.

Saul Mamani Mamani

Desarrollo Agil de Software

25

Nota: La funcin IsDefined slo comprueba los valores que se han definido en la enumeracin, no las posibles combinaciones que podemos conseguir sumando cada uno de sus miembros, incluso aunque hayamos usado el atributo FlagsAttribute.

4. Arrays (matrices)
Los arrays (o matrices) nos permitirn agrupar valores que de alguna forma queremos que estn relacionados entre s. Nota: sta es la definicin usada en la documentacin de Visual Studio sobre qu es una matriz: "Una matriz es una estructura de datos que contiene una serie de variables denominadas elementos de la matriz." Aclaramos este punto, porque la traduccin en castellano de Array puede variar dependiendo del pas, pero aqu utilizaremos la usada a lo largo de la documentacin de Visual Studio. Declarar arrays En C# los arrays se definen indicando un par de corchetes en el tipo de datos. Por ejemplo, para indicar que queremos tener un array llamado numeros para almacenar valores de tipo int, lo haremos de esta forma:
int[] numeros;

Esta declaracin simplemente indica que la variable numeros "ser" un array, pero an no est inicializada. Para iniciarla, al menos con valores cero, podemos hacerlo de estas dos formas, dependiendo de que ya hayamos declarado previamente la variable, (primera lnea), o lo queramos hacer al declararla, (segunda lnea):
numeros = new int[4]; int[] num2 = new int[3];

En ambos casos debemos usar new seguido del tipo de datos y entre corchetes el nmero de elementos que tendr el array. Ese nmero indicar el total de elementos que tendr el array, pero debido a como .NET trata los arrays, el ndice inferior ser cero y el ndice superior ser uno menos del nmero de elementos que hemos indicado al crear el array. Para asignar valores a los elementos de un array, lo haremos como con cualquier variable, pero usando los corchetes y la posicin en la que queremos guardar ese valor:
num2[0] = 3; num2[1] = 22;

Para recorrer todos los elementos que tenga un array podemos hacerlo de la forma tradicional, es decir, usando un bucle for con una variable que recorra todos los ndices, en cuyo caso necesitamos averiguar el valor del ndice superior del array, cosa que haremos por medio de la propiedad Length, tal como podemos ver en este cdigo:
for (int i = 0; i < num2.Length; i++) Console.WriteLine(num2[i]);

Como vemos, para acceder a cada uno de los valores, el elemento al que queremos acceder lo indicamos dentro de corchetes. La otra forma de hacerlo es mediante un bucle foreach, pero debido a que foreach obtiene cada un de los valores que le indiquemos, no necesitamos acceder directamente al array, sino que usaremos el valor obtenido en cada ciclo del bucle, tal como podemos comprobar en este trozo de cdigo:

Saul Mamani Mamani

Desarrollo Agil de Software

26

foreach(int i in num2) Console.WriteLine(i);

Declarar e inicializar un array Al igual que podemos declarar variables e inicializarlas al mismo tiempo, con los arrays tambin podemos hacerlo, aunque la sintaxis es un poco distinta, ya que en esta ocasin debemos indicar varios valores. Esos valores los indicaremos justo despus de definir el array:
string[] nombres = {"Pepe", "Juan", "Luisa"};

En este caso, cuando iniciamos el array al declararlo, no debemos indicar el nmero de elementos que tendr ese array, ya que ese valor lo averiguar el compilador cuando haga la asignacin. Tampoco es vlido indicar el nmero de elementos que queremos que tenga y solo asignarle unos cuantos menos (o ms), ya que se producir un error en tiempo de compilacin. Si el array es bidimensional (o con ms dimensiones), tambin podemos inicializarlos al declararlo, pero en este caso debemos usar doble juego de llaves:
string[,] nombres = { { "Juan", "Pepe" }, { "Ana", "Eva" } };

En este cdigo tendramos un array bidimensional con los siguientes valores: nombres[0,0]= Juan nombres[0,1]= Pepe nombres[1,0]= Ana nombres[1,1]= Eva Como podemos ver en la declaracin anterior, si definimos arrays con ms de una dimensin, debemos indicarlas, en la declaracin del tipo, usando una coma para separar cada dimensin, o lo que es ms fcil de recordar: usando una coma menos del nmero de dimensiones que tendr el array. En los valores a asignar, usaremos las llaves encerradas en otras llaves, segn el nmero de dimensiones. Aunque, la verdad, es que hay algunas veces hay que hacer un gran esfuerzo mental para asociar los elementos con los ndices que tendrn en el array, por tanto, algunas veces puede que resulte ms legible si indentamos o agrupamos esas asignaciones, tal como vemos en el siguiente cdigo:
string[, ,] nomTri = { { { "Juan", "Pepe" }, {"Luisa", "Eva" } }, { { "A", "B" }, { "C", "D" } } }; Console.WriteLine(nomTri[0, 0, 0]); // Juan Console.WriteLine(nomTri[0, 0, 1]); // Pepe Console.WriteLine(nomTri[0, 1, 0]); // Luisa Console.WriteLine(nomTri[0, 1, 1]); // Eva Console.WriteLine(nomTri[1, 0, 0]); // A Console.WriteLine(nomTri[1, 0, 1]); // B Console.WriteLine(nomTri[1, 1, 0]); // C Console.WriteLine(nomTri[1, 1, 1]); // D

Cambiar el tamao de un array Visual C# 2005 no nos ofrece una forma fcil de cambiar el tamao de un array, de hecho no tiene ninguna instruccin que sirva para eso.

Saul Mamani Mamani

Desarrollo Agil de Software

27

Aunque redimensionar un array realmente no tiene ningn problema, ya que simplemente lo haremos de la misma forma que vimos anteriormente:
num2 = new int[4];

E incluso podemos asignarle nuevos valores al "redimensionarlo", usando la misma sintaxis que vimos en la seccin anterior:
num2 = new int[] { 6, 7, 8, 9 };

Lo que no podemos hacer en C# es cambiar el tamao de un array conservando el contenido anterior, algo que en otros lenguajes si es posible. La nica forma de conseguirlo es hacer una copia previa de ese array, redimensionarlo al nuevo tamao y volver a copiar el contenido anterior. En el siguiente cdigo podemos ver estos pasos:
int[] original = { 1, 2, 3, 4 }; int[] copia = new int[original.Length]; // Copiamos el original en la copia original.CopyTo(copia, 0); // Aadimos 5 elementos ms original = new int[original.Length + 5]; // Asignamos nuevamente lo copiado a partir del primer elemento copia.CopyTo(original, 0);

Para hacer la copia hemos usado el mtodo CopyTo, al que le indicamos el array de destino, (que previamente debe estar dimensionado con el nmero de elementos que recibir), y el ndice a partir del cual queremos copiar ese array, en nuestro caso le indicamos la primera posicin, que como sabemos es la posicin cero. Si el array contiene valores por valor, podemos usar otro mtodo: Clone, ya que este mtodo devuelve una copia "superficial" del objeto en cuestin, en nuestro caso el array; esa copia superficial significa que solo copiar el contenido del array, si este tuviera referencia a objetos, no copiara los objetos, solo las referencias, (de todo esto de los objetos y referencias a objetos nos ocuparemos en la prxima leccin), pero el valor que devuelve ese mtodo es del tipo object, es decir el tipo genrico de .NET, por tanto tendramos que hacer una conversin al tipo array, tal como vemos en este cdigo:
int[] copia = (int[])original.Clone();

Eliminar el contenido de un array Una vez que hemos declarado un array y le hemos asignado valores, es posible que nos interese eliminar esos valores de la memoria, para lograrlo, en C# podemos hacerlo de dos formas: 1. Redimensionando el array indicando que tiene cero elementos, aunque en el mejor de los casos, si no estamos trabajando con arrays de ms de una dimensin, tendramos un array de un elemento, ya que, como hemos comentado anteriormente, los arrays de .NET el ndice inferior es cero. 2. Asignar un valor null al array. Esto funciona en C# porque los arrays son tipos por referencia. Los arrays son tipos por referencia Como acabamos de ver, en Visual C# 2005 los arrays son tipos por referencia, y tal como comentamos anteriormente, los tipos por referencia realmente lo que contienen son una referencia a los datos reales no los datos propiamente dichos. Cul es el problema? Vemoslo con un ejemplo y as lo tendremos ms claro.

Saul Mamani Mamani

Desarrollo Agil de Software

28

string[] nombres = { "Juan", "Pepe", "Ana", "Eva" }; string[] otros; otros = nombres; nombres[0] = "Antonio"; Console.WriteLine(otros[0]);

En este ejemplo definimos el array nombres y le asignamos cuatro valores. A continuacin definimos otro array llamado otros y le asignamos lo que tiene nombres. Por ltimo asignamos un nuevo valor al elemento cero del array nombres. Si mostramos el contenido de ambos arrays nos daremos cuenta de que realmente solo existe una copia de los datos en la memoria, y tanto nombres[0] como otros[0] contienen el nombre "Antonio". Qu ha ocurrido? Debido a que los arrays son tipos por referencia, solamente existe una copia de los datos en la memoria y tanto la variable nombres como la variable otros lo que contienen es una referencia (o puntero) a los datos. Si realmente queremos tener copias independientes, debemos hacer una copia del array utilizando el mtodo CopyTo que ya vimos anteriormente. Adems de los mtodos que hemos visto y de la propiedad Length que nos sirve para averiguar cuantos elementos tiene un array, tenemos ms mtodos y propiedades que podemos usar, por ejemplo para saber cuantas dimensiones tiene un array (Rank) o saber cual es el ndice superior de una dimensin determinada (GetUpperBound). Para finalizar este tema, slo nos queda por decir, que los arrays de .NET, y por tanto los de C#, realmente son tipos de datos derivados de la clase Array y por tanto disponen de todos los miembros definidos en esa clase, aunque de esto hablaremos en la prxima leccin, en la que tambin tendremos la oportunidad de profundizar un poco ms en los tipos por referencia y en cmo podemos definir nuestros propios tipos de datos, tanto por referencia como por valor.

LECCION 2: CLASES Y ESTRUCTURAS


Introduccin En la leccin anterior vimos los tipos de datos predefinidos en .NET Framework, en esta leccin veremos cmo podemos crear nuestros propios tipos de datos, tanto por valor como por referencia. Tambin tendremos ocasin de ver los distintos niveles de accesibilidad que podemos aplicar a los tipos, as como a los miembros definidos en esos tipos de datos. De los distintos miembros que podemos definir en nuestros tipos, nos centraremos en las propiedades para ver en detalle los cambios que han sufrido con respecto a la versin anterior de C#. Tambin veremos temas relacionados con la programacin orientada a objetos (POO) en general y de forma particular los que ataen a las interfaces. Clases y estructuras

Clases: Tipos por referencia definidos por el usuario o Las clases: El corazn de .NET Framework La herencia: Caracterstica principal de la Programacin Orientada a Objetos Encapsulacin y Polimorfismo Object: La clase base de todas las clases de .NET
o

Definir una clase Los miembros de una clase Caractersticas de los mtodos y propiedades Accesibilidad, mbito y miembros compartidos (estticos) Parmetros y parmetros opcionales Array de parmetros opcionales (params)

Saul Mamani Mamani

Desarrollo Agil de Software

29

Sobrecarga de mtodos Parmetros por valor y parmetros por referencia

Instanciar: Crear un objeto en la memoria Declarar primero la variable y despus instanciarla Declarar e instanciar en un solo paso El constructor: El punto de inicio de una clase Constructores parametrizados Cuando C# no crea un constructor automticamente El destructor: El punto final de la vida de una clase

Estructuras: Tipos por valor definidos por el usuario o Definir una estructura o Constructores de las estructuras o Destructores de las estructuras o Los miembros de una estructura Accesibilidad y mbito o mbito mbito de bloque mbito de procedimiento mbito de mdulo mbito de espacio de nombres o La palabra clave global:: o Accesibilidad Accesibilidad de las variables en los procedimientos o Las accesibilidades predeterminadas o Anidacin de tipos Los tipos anidables El nombre completo de un tipo Importacin de espacios de nombres Alias de espacios de nombres Propiedades o Definir una propiedad o Propiedades de solo lectura o Propiedades de solo escritura o Diferente accesibilidad para los bloques get y set o Propiedades predeterminadas (indizadores) Sobrecarga en los indizadores Interfaces o Qu es una interfaz? o Qu puede contener una interfaz? o Una interfaz es un contrato o Las interfaces y el polimorfismo o Usar una interfaz en una clase o Acceder a los miembros implementados o Saber si un objeto implementa una interfaz (is y as) o Implementacin de mltiples interfaces o Mltiple implementacin de un mismo miembro o Implementacin privada o Dnde podemos implementar las interfaces? o Un ejemplo prctico usando una interfaz de .NET

Saul Mamani Mamani

Desarrollo Agil de Software

30

1. Clases: Tipos por referencia definidos por el usuario


Tal como vimos en la leccin anterior, los tipos de datos se dividen en dos grupos: tipos por valor y tipos por referencia. Los tipos por referencia realmente son clases, de la cuales debemos crear una instancia para poder usarlas, esa instancia o copia, se crea siempre en la memoria lejana (heap) y las variables lo nico que contienen es una referencia a la direccin de memoria en la que el CLR (Common Language Runtime, motor en tiempo de ejecucin de .NET), ha almacenado el objeto recin creado. En .NET Framework todo es de una forma u otra una clase, por tanto Visual C# 2005 tambin depende de la creacin de clases para su funcionamiento, ya que todo el cdigo que escribamos debemos hacerlo dentro de una clase. Antes de entrar en detalles sintcticos, veamos la importancia que tienen las clases en .NET Framework y como repercuten en las que podamos definir nosotros usando Visual C# 2005. Las clases: el corazn de .NET Framework Todo lo que podemos hacer en .NET Framework lo hacemos mediante clases. La librera de clases de .NET Framework es precisamente el corazn del propio .NET, en esa librera de clases est todo lo que podemos hacer dentro de este marco de programacin; para prcticamente cualquier tarea que queramos realizar existen clases, y si no existen, las podemos definir nosotros mismos, bien ampliando la funcionalidad de alguna clase existente mediante la herencia, bien implementando algn tipo de funcionalidad previamente definida o simplemente crendolas desde cero. La herencia: Caracterstica principal de la Programacin Orientada a Objetos El concepto de Programacin Orientada a Objetos (POO) es algo intrnsico al propio .NET Framework, por tanto es una caracterstica que todos los lenguajes basados en este "marco de trabajo" tienen de forma predeterminada, entre ellos el Visual C# 2005. De las caractersticas principales de la POO tenemos que destacar la herencia, que en breve podemos definir como una caracterstica que nos permite ampliar la funcionalidad de una clase existente sin perder la que ya tuviera previamente. Gracias a la herencia, podemos crear una nueva clase que se derive de otra, esta nueva clase puede cambiar el comportamiento de la clase base y/o ampliarlo, de esta forma podemos adaptar la clase, llammosla, original para adaptarla a nuestras necesidades. El tipo de herencia que .NET Framework soporta es la herencia simple, es decir, solo podemos usar una clase como base de la nueva, si bien, como veremos ms adelante, podemos agregar mltiple funcionalidad a nuestra nueva clase. Esta funcionalidad nos servir para aprovechar la que ya tienen muchas de las clases existentes en .NET Framework, funcionalidad que solamente podremos aplicar si previamente hemos firmado un contrato que asegure a la clase de .NET que la nuestra est preparada para soportar esa funcionalidad, esto lo veremos dentro de poco con ms detalle.} Encapsulacin y Polimorfismo La encapsulacin y el polimorfismo son otras dos caractersticas de la programacin orientada a objetos. La encapsulacin nos permite abstraer la forma que tiene de actuar una clase sobre los datos que contiene o manipula, para poder lograrlo se exponen como parte de la clase los mtodos y propiedades necesarios para que podamos manejar esos datos sin tener que preocuparnos cmo se realiza dicha manipulacin. El polimorfismo es una caracterstica que nos permite realizar ciertas acciones o acceder a la informacin de los datos contenidos en una clase de forma semi-annima, al menos en el sentido de que no tenemos porqu saber sobre que tipo de objeto realizamos la accin, ya que lo nico que nos debe preocupar es que podemos hacerlo, por la sencilla razn de que estamos usando ciertos mecanismos que siguen unas normas que estn adoptadas por la clase. El ejemplo clsico del polimorfismo es que si tengo un objeto que "sabe" cmo morder, da igual que lo aplique a un ratn o

Saul Mamani Mamani

Desarrollo Agil de Software

31

a un dinosaurio, siempre y cuando esas dos "clases" expongan un mtodo que pueda realizar esa accin... y como deca la documentacin de Visual Basic 5.0, siempre ser preferible que nos muerda un ratn antes que un dinosaurio. Object: La clase base de todas las clases de .NET Todas las clases de .NET se derivan de la clase Object, es decir, lo indiquemos o no, cualquier clase que definamos tendr el comportamiento heredado de esa clase. El uso de la clase Object como base del resto de las clases de .NET es la nica excepcin a la herencia simple soportada por .NET, ya que de forma implcita, todas las clases de .NET se derivan de la clase Object independientemente de que estn derivadas de cualquier otra. Esta caracterstica nos asegura que siempre podremos usar un objeto del tipo Object para acceder a cualquier clase de .NET, aunque no debemos abrumarnos todava, ya que en el texto que sigue veremos con ms detalle que significado tiene esta afirmacin. De los miembros que tiene la clase Object debemos resaltar el mtodo ToString, el cual ya lo vimos en la leccin anterior cuando queramos convertir un tipo primitivo en una cadena. Este mtodo est pensado para devolver una representacin en formato cadena de un objeto. El valor que obtengamos al usar este mtodo depender de cmo est definido en cada clase y por defecto lo que devuelve es el nombre completo de la clase, si bien en la mayora de los casos el valor que obtendremos al usar este mtodo debera ser ms intuitivo, por ejemplo los tipos de datos primitivos tienen definido este mtodo para que devuelva el valor que contienen, de igual forma, nuestras clases tambin deberan devolver un valor adecuado al contenido almacenado. De cmo hacerlo, nos ocuparemos en breve. Nota: Todos los tipos de datos de .NET, ya sean por valor o por referencia siempre estn derivados de la clase Object, por tanto podremos llamar a cualquiera de los mtodos que estn definidos en esa clase. Aunque en el caso de los tipos de datos por valor, cuando queremos acceder a la clase Object que contienen, .NET Framework primero debe convertirla en un objeto por referencia (boxing) y cuando hemos dejado de usarla y queremos volver a asignar el dato a la variable por valor, tiene que volver a hacer la conversin inversa (unboxing).

2. Definir una clase


En Visual C# 2005, todo el cdigo que queramos escribir, lo tendremos que hacer en un fichero con la extensin .cs, dentro de ese fichero es donde escribiremos nuestro cdigo, el cual, tal como dijimos anteriormente siempre estar incluido dentro de una clase, aunque un fichero de cdigo de C# puede contener una o ms clases, es decir, no est limitado a una clase por fichero. En C# las clases se definen usando la palabra clave class seguida del nombre de la clase, el cuerpo de la clase estar encerrada entre un par de llaves, que por otra parte es la forma habitual de definir bloques de cdigo en C#. En el siguiente ejemplo definimos una clase llamada Cliente que tiene dos campos pblicos.
class Cliente { public string Nombre; public string Apellidos; }

Una vez definida la clase podemos agregar los elementos (o miembros) que creamos conveniente. En el ejemplo anterior, para simplificar, hemos agregado dos campos pblicos, aunque tambin podramos haber definido cualquiera de los miembros permitidos en las clases. Los miembros de una clase Una clase puede contener cualquiera de estos elementos (miembros):

Enumeraciones

Saul Mamani Mamani

Desarrollo Agil de Software

32

Campos Mtodos (funciones o procedimientos) Propiedades Eventos

Las enumeraciones, como vimos en la leccin anterior, podemos usarlas para definir valores constantes relacionados, por ejemplo para indicar los valores posibles de cualquier "caracterstica" de la clase. Los campos son variables usadas para mantener los datos que la clase manipular. Los mtodos son las acciones que la clase puede realizar, normalmente esas acciones sern sobre los datos que contiene. Los mtodos pueden devolver valores como resultado de la "accin" realizada o tambin pueden devolver un valor void, que significa que realmente no devuelve nada. Las propiedades son las "caractersticas" de las clases y la forma de acceder "pblicamente" a los datos que contiene. Por ejemplo, podemos considerar que el nombre y los apellidos de un cliente son dos caractersticas del cliente. Los eventos son mensajes que la clase puede enviar para informar que algo est ocurriendo en la clase. Caractersticas de los mtodos y propiedades Accesibilidad, mbito y miembros compartidos Aunque estos temas los veremos en breve con ms detalle, para poder comprender mejor las caractersticas de los miembros de una clase (o cualquier tipo que definamos), daremos un pequeo adelanto sobre estas caractersticas que podemos aplicar a los elementos que definamos. Accesibilidad y mbito son dos conceptos que estn estrechamente relacionados. Aunque en la prctica tienen el mismo significado, ya que lo que representan es la "cobertura" o alcance que tienen los miembros de las clases e incluso de las mismas clases que definamos. Si bien cada uno de ellos tienen su propia "semntica", tal como podemos ver a continuacin: mbito Es el alcance que la definicin de un miembro o tipo puede tener. Es decir, cmo podemos acceder a ese elemento y desde dnde podemos accederlo. El mbito de un elemento de cdigo est restringido por el "sitio" en el que lo hemos declarado. Estos sitios pueden ser:

mbito de bloque: Disponible nicamente en el bloque de cdigo en el que se ha declarado. mbito de procedimiento: Disponible nicamente dentro del procedimiento, (funcin o propiedad), en el que se ha declarado. mbito de mdulo: Disponible en todo el cdigo de la clase o la estructura donde se ha declarado. mbito de espacio de nombres: Disponible en todo el cdigo del espacio de nombres.

Accesibilidad A los distintos elementos de nuestro cdigo (ya sean clases o miembros de las clases) podemos darle diferentes tipos de accesibilidad. Estos tipos de "acceso" dependern del mbito que queramos que tengan, es decir, desde dnde podremos accederlos. Los modificadores de accesibilidad son:

Saul Mamani Mamani

Desarrollo Agil de Software

33

public: Acceso no restringido. protected: Acceso limitado a la clase contenedora o a los tipos derivados de esta clase. internal: Acceso limitado al proyecto actual. protected internal: Acceso limitado al proyecto actual o a los tipos derivados de la clase contenedora. private: Acceso limitado al tipo contenedor.

Por ejemplo, podemos declarar miembros privados a una clase, en ese caso, dichos miembros solamente los podremos acceder desde la propia clase, pero no desde fuera de ella. Nota: En C# cuando declaramos una variable sin indicar el modificador de accesibilidad, el mbito ser privado. Miembros compartidos (estticos) Por otro lado, los miembros compartidos , (o miembros estticos), de una clase o tipo, son elementos que no pertenecen a una instancia o copia en memoria particular, sino que pertenecen al propio tipo y por tanto siempre estn accesibles o disponibles, dentro del nivel del mbito y accesibilidad que les hayamos aplicado, y su tiempo de vida es el mismo que el de la aplicacin. Del mbito, la accesibilidad y los miembros compartidos nos ocuparemos con ms detalle en una leccin posterior, donde veremos ciertas peculiaridades, como puede ser la limitacin del mbito de un miembro que aparentemente tiene una accesibilidad no restringida. Parmetros y parmetros opcionales En Visual C# 2005, los mtodos de una clase, (funciones), pueden recibir parmetros. Esos parmetros pueden estar explcitamente declarados, de forma que podamos indicar cuantos argumentos tendremos que pasar al mtodo, o bien podemos usar lo que se conoce como un array de parmetros opcionales, en los que se puede indicar un nmero variable de argumentos al llamar a la funcin que los define, esto lo veremos en la siguiente seccin. En C#, solamente podemos indicar parmetros en los mtodos no en las propiedades. Nota: Sobre parmetros y argumentos Para clarificar las cosas, queremos decir que los parmetros son los definidos en la funcin, mientras que los argumentos son los "parmetros" que pasamos a esa funcin cuando la utilizamos desde nuestro cdigo. En C# no existen los parmetros opcionales como los de Visual Basic en los que podemos indicar solo los que necesitemos, en lugar de eso, tendremos que declarar varias funciones con el mismo nombre en las que indiquemos diferentes nmeros de parmetros, pero de esto nos dedicaremos un poco ms adelante. Veamos un ejemplo de una funcin que recibe dos parmetros y devuelve la suma de esos dos parmetros:
int suma(int uno, int dos) { return uno + dos; }

Los dos parmetros definidos son de tipo int y el valor devuelto tambin es de ese tipo, (aunque podemos definirlo con cualquier tipo de datos, ya sean propios de .NET o bien definidos por nosotros). El valor devuelto, es la suma de los dos parmetros, y ese resultado se devuelve mediante la instruccin return. Para usar esta funcin podemos hacer algo como esto:
int total = suma(10, 25);

Saul Mamani Mamani

Desarrollo Agil de Software

34

En este caso, los argumentos que hemos pasado a la funcin son constantes literales, pero podemos usar cualquier tipo de argumento que genere un valor del tipo adecuado, en este caso int o cualquiera que produzca un valor implcitamente convertible en int, (ver tabla 2.3). Por tanto podemos usar expresiones, variables o constantes. El valor resultante ser el que se pase a la funcin y lo ver como un valor simple. Por ejemplo, si en el primer argumento indicamos n * 3 + x, se evaluar esa expresin y el resultado ser el que se asigne al parmetro uno. Array de parmetros opcionales (params) En cuanto al uso de los parmetros declarados con params, este tipo de parmetro "opcional" nos permite indicar un nmero variable de argumentos. En realidad, la declaracin de params siempre es un array de un tipo determinado, y al ser un array, se admite cualquier nmero de argumentos al llamar a la funcin, pero siempre debern ser del tipo definido en la funcin. Por ejemplo, si tenemos esta definicin:
int sumaParams(params int[] parametros) { int total = 0; for (int i = 0; i < parametros.Length; i++) total += parametros[i]; return total; }

Podemos llamar a esa funcin indicando el nmero de argumentos que creamos conveniente:
int total2 = sumaParams(1, 3, 5, 7, 9);

Si nos fijamos bien en el cdigo de esta funcin, utilizamos un bucle para recorrer todos los elementos que tiene el array parametros, ya que cada uno de los valores que especifiquemos a la hora de llamar a la funcin se convertir en un elemento del array, que en este caso es de tipo int, pero puede ser de cualquier tipo. Sobrecarga de mtodos La sobrecarga de funciones, es una caracterstica que nos permite tener una misma funcin con diferentes tipos de parmetros, ya sea en nmero o en tipo. Supongamos que queremos tener dos funciones (o ms) que nos permitan hacer operaciones con diferentes tipos de datos, y que, segn el tipo de datos usado, el valor que devuelva sea de ese mismo tipo. En este ejemplo, tenemos dos funciones que se llaman igual pero una recibe valores de tipo entero y la otra de tipo doble:
int suma(int uno, int dos) { return uno + dos; } double suma(double uno, double dos) { return uno + dos; }

Como podemos comprobar las dos funciones tienen el mismo nombre, pero tanto una como otra reciben parmetros de tipos diferentes. Siempre podremos crear sobrecarga de funciones si los parmetros son de distinto tipo o tienen un nmero diferente de parmetros, ya que si solo se diferencian por el tipo del valor devuelto, no podemos crear sobrecarga.

Saul Mamani Mamani

Desarrollo Agil de Software

35

La siguiente declaracin no crear una sobrecarga de la funcin suma, ya que el tipo de parmetros es el mismo que el mostrado anteriormente y solo se diferencia en el tipo del valor devuelto, por tanto obtendremos un error del compilador indicndonos que ya existe una funcin suma con ese mismo tipo de parmetros.
float suma(int uno, int dos) { return uno + dos; }

Debemos tener en cuenta que C# distingue entre maysculas y minsculas, por tanto, una funcin llamada suma ser diferente de otra llamada Suma (con la ese en maysculas). Tambin podemos tener sobrecargas usando una cantidad diferente de parmetros, aunque stos sean del mismo tipo que otra funcin previamente definida, pero al tener un nmero diferente de parmetros, no existir ningn conflicto. Por ejemplo, podemos aadir esta declaracin al cdigo anterior sin que exista ningn tipo de error, ya que esta nueva funcin recibe tres parmetros en lugar de dos:
int suma(int uno, int dos, int tres) { return uno + dos + tres; }

Cuando, desde el IDE de Visual C# 2005, queremos usar los mtodos sobrecargados, nos mostrar una lista de opciones indicndonos las posibilidades de sobrecarga que existen y entre las que podemos elegir, tal como vemos en la figura 2.4:

Figura 2.4. Lista de parmetros soportados por un mtodo Parmetros por valor y parmetros por referencia No debemos confundir los parmetros por valor y por referencia con los tipos por valor o por referencia, ya que en este caso el concepto es diferente. Por defecto los parmetros son por valor, es decir, se hace una copia del argumento indicado al llamar a la funcin y es esa copia lo que se pasa a la funcin. Este concepto se comprende mejor si tomamos el ejemplo que pusimos anteriormente sobre el paso de expresiones a la funcin, el runtime de .NET primero realizar el clculo de esa expresin y despus pasa el resultado como argumento de la funcin, por tanto, cualquier cambio que hagamos dentro de la funcin no afectar al argumento externo, ya que lo que la funcin utiliza es una copia. Cuando usamos parmetros por referencia, se pasa la direccin de memoria del argumento indicado, con lo cual se consigue que cualquier cambio realizado dentro de la funcin afectar a la variable usada como argumento. En C# los parmetros por referencia pueden ser de dos tipos: out o ref, y siempre debemos indicar el modificador al llamar a la funcin. La diferencia entre estos dos tipos de parmetros por referencia, es que para usar los parmetros ref la variable usada como argumento debe estar inicializada previamente, mientras que los parmetros indicados con out no es necesario que estn previamente inicializadas, pero de ser as, se deben inicializar dentro de la funcin. Veamos un par de ejemplos de cmo declarar los parmetro ref y out y cmo usarlos. Este primer ejemplo, la funcin suma recibe dos parmetros por referencia por medio de ref:

Saul Mamani Mamani

Desarrollo Agil de Software

36

int suma(ref int uno, ref int dos) { uno += 1; dos *= 2; return uno + dos; }

En este caso, las dos variable se pasan por referencia y se modifican los valores dentro de la funcin, por tanto ese cambio afectar a las variables usadas como argumento:
int i1 = 15, i2 = 10; Console.WriteLine("Antes de llamar a la funcin:"); Console.WriteLine("i1 = {0}, i2= {1}", i1, i2); int t1 = suma(ref i1, ref i2); Console.WriteLine("Despus de llamar a la funcin:"); Console.WriteLine("i1 = {0}, i2= {1}", i1, i2); Console.WriteLine("total = {0}", t1);

Al ejecutar ese cdigo obtendremos la siguiente salida, en la que podemos comprobar que el cambio hecho internamente afecta a las variables externas:
Antes de llamar a la funcin: i1 = 15, i2= 10 Despus de llamar a la funcin: i1 = 16, i2= 20 total = 36

En este ejemplo, el segundo parmetro es por referencia de tipo out:


int suma(int uno, out int dos) { uno += 1; dos = uno * 2; return uno + dos; }

Internamente cambiamos el valor del primer parmetro, pero este cambio no afectar a la variable usada como primer argumento, en el caso del segundo parmetro, al ser de tipo out, siempre tendremos que inicializarlo dentro de la funcin, independientemente de que antes de llamar a esta funcin estuviera inicializado, si no lo hacemos as, el compilador nos mostrar un error y no compilar el cdigo. Veamos ahora cmo usar esa funcin y el resultado obtenido:
int i3; i1 = 10; int t2 = suma(i1, out i3); Console.WriteLine("Despus de llamar a la funcin:"); Console.WriteLine("i1 = {0}, i3= {1}", i1, i3); Console.WriteLine("total = {0}", t2);

Y este es resultado que obtendremos:


Despus de llamar a la funcin: i1 = 10, i3= 22 total = 33

Tal como podemos afectar, el cambio realizado al primer parmetro no afecta al valor de la variable i1, aunque el valor asignado internamente en la funcin si afecta al resultado devuelto. Nota: En C# se consideran sobrecargas distintas si los parmetros estn definidos como out o ref, ya que al indicar esos modificadores en la llamada, el compilador no tendr problemas de saber que funcin debe utilizar en cada caso.

Saul Mamani Mamani

Desarrollo Agil de Software

37

3. Instanciar Crear un objeto en la memoria


Una vez que tenemos una clase definida, lo nico de lo que disponemos es de una especie de plantilla o molde a partir del cual podemos crear objetos en memoria. Para crear esos objetos en Visual C# 2005 lo podemos hacer de dos formas, pero como veremos siempre ser mediante la instruccin new que es la encargada de crear el objeto en la memoria y asignar la direccin del mismo a la variable usada en la parte izquierda de la asignacin. Declarar primero la variable y despus instanciarla Lo primero que tenemos que hacer es declarar una variable del tipo que queremos instanciar, esto lo hacemos de la misma forma que con cualquier otro tipo de datos:
Cliente cli1;

Con esta lnea de cdigo lo que estamos indicando a C# es que tenemos intencin de usar una variable llamada cli1 para acceder a una clase de tipo Cliente. Esa variable, cuando llegue el momento de usarla, sabr todo lo que hay que saber sobre una clase Cliente, pero hasta que no tenga una "referencia" a un objeto de ese tipo no podremos usarla. La asignacin de una referencia a un objeto Cliente la haremos usando la instruccin new seguida del nombre de la clase:
cli1 = new Cliente();

A partir de este momento, la variable cli1 tiene acceso a un nuevo objeto del tipo Cliente, por tanto podremos usarla para asignarle valores y usar cualquiera de los miembros que ese tipo de datos contenga:
cli1.Nombre = "Antonio"; cli1.Apellidos = "Ruiz Rodrguez"; cli1.Saludar();

Declarar e instanciar en un solo paso La otra forma de instanciar una clase es hacindolo al mismo tiempo que la declaramos. En C# esto se hace como si uniramos la declaracin y la instanciacin en una sola instruccin:
Cliente cli2 = new Cliente();

De esta forma se asignar a la variable cli2 una referencia a un nuevo objeto creado en la memoria, el cual ser totalmente independiente del resto de objetos creados con esa misma clase. El constructor: El punto de inicio de una clase Cada vez que creamos un nuevo objeto en memoria estamos llamando al constructor de la clase. En Visual C# 2005 el constructor es una especie de mtodo que se llama de la misma forma que la clase. En el constructor de una clase podemos incluir el cdigo que creamos conveniente, pero realmente solamente deberamos incluir el que realice algn tipo de inicializacin, en caso de que no necesitemos realizar ningn tipo de inicializacin, no es necesario definir el constructor, ya que el propio compilador lo har por nosotros. Esto es as porque todas las clases deben implementar un constructor, por tanto si nosotros no lo definimos, lo har el compilador de C#.

Saul Mamani Mamani

Desarrollo Agil de Software

38

Si nuestra clase Cliente tiene un campo para almacenar la fecha de creacin del objeto podemos hacer algo como esto:

class Cliente { public string Nombre; public string Apellidos; public DateTime FechaCreacion; public Cliente() { FechaCreacion = DateTime.Now; } }

De esta forma podemos crear un nuevo Cliente y acto seguido comprobar el valor del campo FechaCreacion para saber la fecha de creacin del objeto. En los constructores tambin podemos hacer las inicializaciones que, por ejemplo permitan a la clase a conectarse con una base de datos, abrir un fichero o cargar una imagen grfica, etc. Constructores parametrizados De la misma forma que podemos tener mtodos sobrecargados, tambin podemos tener constructores sobrecargados, ya que en C# el constructor es como una funcin con un tratamiento especial que devuelve un nuevo objeto. La ventaja de tener constructores que admitan parmetros es que podemos crear nuevos objetos indicando algn parmetro, por ejemplo un fichero a abrir o, en el caso de la clase Cliente, podemos indicar el nombre y apellidos del cliente o cualquier otro dato que creamos conveniente. Para comprobarlo, podemos ampliar la clase definida anteriormente para que tambin acepte la creacin de nuevos objetos indicando el nombre y los apellidos del cliente.
class Cliente; { public string Nombre; public string Apellidos; public DateTime FechaCreacion; public Cliente() { FechaCreacion = DateTime.Now; } public Cliente(string elNombre, string losApellidos) { Nombre = elNombre; Apellidos = losApellidos; FechaCreacion = DateTime.Now; } }

Teniendo esta declaracin de la clase Cliente, podemos crear nuevos clientes de dos formas:
Cliente c1 = new Cliente(); Cliente c2 = new Cliente("Jose", "Snchez");

Como podemos comprobar, en ciertos casos es ms intuitiva la segunda forma de crear objetos del tipo Cliente, adems de que as nos ahorramos de tener que asignar individualmente los campos Nombre y Apellidos.

Saul Mamani Mamani

Desarrollo Agil de Software

39

Esta declaracin de la clase Cliente la podramos haber hecho de una forma diferente. Por un lado tenemos un constructor "normal" (no recibe parmetros) en el que asignamos la fecha de creacin y por otro el constructor que recibe los datos del nombre y apellidos. En ese segundo constructor tambin asignamos la fecha de creacin, ya que, se instancie como se instancie la clase, nos interesa saber siempre la fecha de creacin. En este ejemplo, por su simpleza no es realmente un problema repetir la asignacin de la fecha, pero si en lugar de una inicializacin necesitramos hacer varias, la verdad es que nos encontraramos con mucha "duplicidad" de cdigo. Por tanto, en lugar de asignar los datos en dos lugares diferentes, podemos hacer esto otro:
class Cliente { public string Nombre; public string Apellidos; public DateTime FechaCreacion; public Cliente() { FechaCreacion = DateTime.Now; } public Cliente(string elNombre, string losApellidos) : this() { Nombre = elNombre; Apellidos = losApellidos; } }

Es decir, desde el constructor con parmetros llamamos al constructor que no los tiene, consiguiendo que tambin se asigne la fecha. La instruccin this se emplea para acceder a la instancia actual, es decir al objeto que est en la memoria, y cuando lo usamos de la forma mostrada en el cdigo, realmente llama al constructor, en este caso sin parmetros, aunque de haberlo necesitado podemos indicarle los argumentos que creamos conveniente, siempre que exista un constructor con ese nmero de parmetros. La llamada a otros constructores de la misma instancia, solo podemos hacerla desde un constructor y esa llamada debe estar antes del inicio del cdigo de la clase, es decir, antes de la llave de apertura. Cuando C# no crea un constructor automticamente Tal como hemos comentado, si nosotros no definimos un constructor, lo har el propio compilador de Visual C# 2005, y cuando lo hace automticamente, siempre es un constructor sin parmetros. Pero hay ocasiones en las que nos puede interesar que no exista un constructor sin parmetros, por ejemplo, podemos crear una clase Cliente que solo se pueda instanciar si le pasamos, por ejemplo el nmero de identificacin fiscal, (NIF), en caso de que no se indique ese dato, no podremos crear un nuevo objeto Cliente; de esta forma nos aseguramos de que el NIF siempre est especificado. Seguramente por ese motivo, si nosotros definimos un constructor con parmetros, C# no lo crea automticamente sin parmetros. Por tanto, si definimos un constructor con parmetros en una clase y queremos que tambin tenga uno sin parmetros, lo tenemos que definir nosotros mismos, aunque no es necesario que el constructor sin parmetros ejecute cdigo, puede estar declarado sin ms. El destructor: El punto final de la vida de una clase De la misma forma que una clase tiene su punto de entrada o momento de nacimiento en el constructor, tambin tienen un sitio que se ejecutar cuando el objeto creado en la memoria ya no sea necesario, es decir, cuando acabe la vida del objeto creado. El destructor de C# se llama igual que la clase pero precedida del carcter ~ y no puede tener ningn modificador de visibilidad. Y solo es necesario definirlo si nuestra clase utiliza algn tipo de recurso, por tanto no es necesario definirlo si no tenemos que liberar expresamente algn recurso asignado en el constructor.

Saul Mamani Mamani

Desarrollo Agil de Software

40

La definicin del destructor de nuestra clase Cliente poda ser algo como esto:
~Cliente() { }

El que un destructor no sea accesible externamente es por la sencilla razn de que es el propio CLR el que se encarga de gestionar la destruccin de los objetos que ya no son utilizados. Debido a que no es posible invocar al destructor desde el objeto creado en la memoria, debemos implementar explcitamente un mtodo que sea el que se encargue de hacer esa limpieza de los recursos utilizados por nuestra clase, ese mtodo suele tener el nombre Close o Dispose, en este ltimo caso, lo que se recomienda es que nuestra clase implemente la interfaz IDisposable con idea de que si no se llama expresamente al mtodo que libera la memoria, sea el propio runtime del .NET Framework el que lo llame cuando el objeto ya no sea necesario mantenerlo en memoria. Es importante saber que los destructores no se llaman de forma inmediata, es decir, es posible que un objeto ya no se est usando, pero el CLR no llamar al destructor hasta que no lo crea conveniente, por tanto si nuestra clase utiliza recursos externos es imperativo que el usuario que utilice nuestra clase llame de forma explcita al mtodo que hayamos definido para liberar esos recursos.

4. Estructuras: Tipos por valor definidos por el usuario


De la misma forma que podemos definir nuestros propios tipos de datos por referencia, Visual C# 2005 nos permite crear nuestros propios tipos por valor. Para crear nuestros tipos de datos por referencia, usamos la "instruccin" class, por tanto es de esperar que tambin exista una instruccin para crear nuestros tipos por valor, y esa instruccin es: struct, de ah que los tipos por valor definidos por el usuario se llamen estructuras. Las estructuras pueden contener los mismos miembros que las clases, aunque algunos de ellos se comporten de forma diferente o al menos tengan algunas restricciones, como que los campos definidos en las estructuras no se pueden inicializar al mismo tiempo que se declaran o no pueden contener constructores "simples", ya que el propio compilador siempre se encarga de crearlo, para as poder inicializar todos los campos definidos. Otra de las caractersticas de las estructuras es que no es necesario crear una instancia para poder usarlas, ya que es un tipo por valor y los tipos por valor no necesitan ser instanciados para que existan. Definir una estructura Las estructuras se definen usando la palabra struct seguida del nombre, en cdigo de las estructuras, al igual que el de las clases o cualquier otro bloque de cdigo de C# lo incluiremos entre un par de llaves. El siguiente cdigo define una estructura llamada Punto en la que tenemos dos campos pblicos.
struct Punto { public int X; public int Y; }

Para usarla podemos hacer algo como esto:


Punto p; p.X = 100; p.Y = 75;

Tambin podemos usar new al declarar el objeto:


Punto p = new Punto();

Saul Mamani Mamani

Desarrollo Agil de Software

41

Aunque en las estructuras, usar new, sera algo redundante y por tanto no necesario. Nota: Las estructuras siempre se almacenan en la pila, por tanto deberamos tener la precaucin de no crear estructuras con muchos campos o con muchos miembros, ya que esto implicara un mayor consumo del "preciado" espacio de la pila.

Constructores de las estructuras Tal y como hemos comentado, las estructuras siempre definen un constructor sin parmetros, este constructor no lo podemos definir nosotros, es decir, siempre existe y es el que el propio compilador de Visual C# 2005 define. Por tanto, si queremos agregar algn constructor a una estructura, este debe tener parmetros y, tal como ocurre con cualquier mtodo o como ocurre en las clases, podemos tener varias sobrecargas de constructores parametrizados en las estructuras. La forma de definir esos constructores es como vimos en las clases: usando distintas sobrecargas de un "mtodo" llamado de la misma forma que la estructura. En el caso de las estructuras tambin podemos usar la palabra clave this para referirnos a la instancia actual. Esto es particularmente prctico cuando queremos dejar claro de que estamos llamando a un mtodo de la instancia actual. Nota: Tanto en las estructuras como en las clases podemos tener constructores compartidos, (static), en el caso de las estructuras, este tipo de constructor es el nico que podemos declarar sin parmetros. Destructores de las estructuras Debido a que las estructuras son tipos por valor y por tanto una variable declarada con un tipo por valor "contiene el valor en si misma", no podemos destruir este tipo de datos, lo ms que conseguiramos al asignarle un valor nulo ( null) sera eliminar el contenido de la variable, pero nunca podemos destruir ese valor. Por tanto, en las estructuras no podemos definir destructores. Los miembros de una estructura Como hemos comentado, los miembros o elementos que podemos definir en una estructura son los mismos que ya vimos en las clases. Por tanto aqu veremos las diferencias que existen al usarlos en las estructuras. Campos Como vimos, las variables declaradas a nivel del tipo, son los campos, la principal diferencia con respecto a las clases, es que los campos de una estructura no pueden inicializarse en la declaracin y el valor que tendrn inicialmente es un valor "nulo", que en el caso de los campos de tipo numricos es un cero. Por tanto, si necesitamos que los campos tengan algn valor inicial antes de usarlos, deberamos indicrselo a los usuarios de nuestra estructura y proveer un constructor que realice las inicializaciones correspondientes, pero debemos recordar que ese constructor debe tener algn parmetro, ya que el predeterminado sin parmetros no podemos "reescribirlo". Los nicos campos que podemos inicializar al declararlos son los campos compartidos, pero como tendremos oportunidad de ver, estos campos sern accesibles por cualquier variable declarada y cualquier cambio que realicemos en ellos se ver reflejado en el resto de "instancias" de nuestro tipo.

Saul Mamani Mamani

Desarrollo Agil de Software

42

Mtodos y otros elementos El resto de los miembros de una estructura se declaran y usan de la misma forma que en las clases. De los mtodos podemos crear sobrecargas, definirlos compartidos (estticos) o de instancia, no existiendo ningn tipo de restriccin en este aspecto, ya que podemos definir una estructura sin mtodos o con todos los mtodos compartidos, lo mismo es aplicable a las propiedades, salvo que stas no pueden tener sobrecarga. Cmo usar las estructuras Tal como hemos comentado las estructuras son tipos por valor, para usar los tipos por valor no es necesario instanciarlos explcitamente, ya que el mero hecho de declararlos indica que estamos creando un nuevo objeto en memoria. Por tanto, a diferencia de las clases o tipos por referencia, cada variable definida como un tipo de estructura ser independiente de otras variables declaradas, aunque no las hayamos instanciado. Esta caracterstica de las estructuras nos permite hacer copias "reales", no una copia de la referencia (o puntero) al objeto en memoria, como ocurre con los tipos por referencia. Vemoslos con un ejemplo:
Punto p = new Punto(100, 75); Punto p1; p1 = p; p1.X = 200; // p.X vale 100 y p1.X vale 200

En este trozo de cdigo definimos e "instanciamos" una variable del tipo Punto, a continuacin declaramos otra variable del mismo tipo y le asignamos la primera; si estos tipos fuesen por referencia, tanto una como la otra estaran haciendo referencia al mismo objeto en memoria, y cualquier cambio realizado a cualquiera de las dos variables afectaran al mismo objeto, pero en el caso de las estructuras (y de los tipos por valor), cada cambio que realicemos se har sobre un "objeto" diferente, por tanto la asignacin del valor 200 al campo X de la variable p1 solo afecta a esa variable, dejando intacto el valor original de la variable p.

5. Accesibilidad y mbito
Tal y como comentamos anteriormente, dependiendo de dnde y cmo estn declarados los tipos de datos y los miembros definidos en ellos, tendremos o no acceso a esos elementos. Recordemos que el mbito es el alcance con el que podemos acceder a un elemento y depende de dnde est declarado; por otro lado, la accesibilidad depende de cmo declaremos cada uno de esos elementos. mbito Dependiendo de donde declaremos un miembro o un tipo, ste tendr mayor alcance o cobertura, o lo que es lo mismo, dependiendo del mbito en el que usemos un elemento, podremos acceder a l desde otros puntos de nuestro cdigo. A continuacin veremos con detalle los mbitos en los que podemos declarar los distintos elementos de Visual C# 2005.

mbito de bloque: Disponible nicamente en el bloque de cdigo en el que se ha declarado. Por ejemplo, si declaramos una variable dentro de un bucle for o un if, esa variable solo estar accesible dentro de ese bloque de cdigo. mbito de procedimiento: Disponible nicamente dentro del procedimiento en el que se ha declarado. Cualquier variable declarada dentro de un procedimiento (mtodo o propiedad) solo estar accesible en ese procedimiento y en cualquiera de los bloques internos a ese procedimiento.

Saul Mamani Mamani

Desarrollo Agil de Software

43

mbito de mdulo: Disponible en todo el cdigo de la clase o la estructura donde se ha declarado. Las variables con mbito a nivel de mdulo, tambin estarn disponibles en los procedimientos declarados en el mdulo (clase o estructura) y por extensin a cualquier bloque dentro de cada procedimiento. mbito de espacio de nombres: Disponible en todo el cdigo del espacio de nombres. Este es el nivel mayor de cobertura o alcance, aunque en este nivel solo podemos declarar tipos como clases, estructuras y enumeraciones, ya que los procedimientos solamente se pueden declarar dentro de un tipo.

Nota: Por regla general, cuando declaramos una variable en un mbito, dicha variable "ocultar" a otra que tenga el mismo nombre y est definida en un bloque con mayor alcance, aunque veremos que en C# existen ciertas restricciones dependiendo de dnde declaremos esas variables.

En VB6 los mbitos disponibles solamente eran los de mdulo y procedimiento, en estos casos, cualquier variable declarada en un procedimiento oculta a una definida en un mdulo. En Visual C# 2005 podemos definir una variable dentro de un bloque de cdigo, en ese caso dicha variable solo ser accesible dentro de ese bloque. Aunque, como veremos a continuacin, en un procedimiento solamente podremos definir variables que no se oculten entre s, estn o no dentro de un bloque de cdigo. mbito de bloque En los siguientes ejemplos veremos cmo podemos definir variables para usar solamente en el bloque en el que estn definidas. Los bloques de cdigo en los que podemos declarar variables son los bucles, (for,foreach, do, while), y los bloques condicionales, (if, switch), o en cualquier otro bloque formado por un par de llaves. Por ejemplo, dentro de un procedimiento podemos tener varios de estos bloques y por tanto podemos definir variables "internas" a esos bloques:
int n = 3; // for(int i = 1; i <= 10; i++ ) { int j = 0; j += 1; if( j < n ) { //... } } // if( n < 5 ) { int j = n * 3; } // do { int j = 0; for(int i = 1; i <= n; i++) { j += i; } if( j > 10 ) break; }while(true);

La variable n estar disponible en todo el procedimiento, por tanto podemos acceder a ella desde cualquiera de los bloques. En el primer bucle for, definimos la variable i como la variable a usar de contador, esta variable solamente estar accesible

Saul Mamani Mamani

Desarrollo Agil de Software

44

dentro de este bucle for. Lo mismo ocurre con la variable j. En el primer if definimos otra variable j, pero esa solo ser accesible dentro de este bloque y por tanto no tiene ninguna relacin con la definida en el bucle for anterior. En el bucle do volvemos a definir nuevamente una variable j, a esa variable la podemos acceder solo desde el propio bucle do y cualquier otro bloque de cdigo interno, como es el caso del bucle for, en el que nuevamente declaramos una variable llamada i, que nada tiene que ver con el resto de variables declaradas con el mismo nombre en los otros bloques. Nota: El cdigo del bucle do mostrado anteriormente se convertir en un bucle infinito, ya que la variable j siempre se pone a cero en cada repeticin del bucle do/while, por tanto solo hay que tomarlo como un ejemplo para probar lo del mbito de las variables. Lo nico que no podemos hacer en cualquiera de esos bloques, es declarar una variable llamada n, ya que al estar declarada en el procedimiento, el compilador de C# nos indicar que no podemos ocultar una variable previamente definida fuera del bloque, tal como podemos ver en la figura 2.5.

Figura 2.5. Error al ocultar una variable definida en un procedimiento Esta restriccin solo es aplicable a las variables declaradas en el procedimiento, ya que si declaramos una variable a nivel de mdulo, no habr ningn problema para usarla dentro de un bloque, esto es as porque en un procedimiento podemos declarar variables que se llamen de la misma forma que las declaradas a nivel de mdulo, aunque stas ocultarn a las del "nivel" superior. mbito de procedimiento Las variables declaradas en un procedimiento tendrn un mbito o cobertura que ser el procedimiento en el que est declarada, y como hemos visto, ese mbito incluye tambin cualquier bloque de cdigo declarado dentro del procedimiento. Estas variables ocultarn a las que se hayan declarado fuera del procedimiento, si bien, dependiendo del tipo de mdulo, podremos acceder a esas variables "externas" indicando el nombre completo del mdulo o bien usando la instruccin this seguida del nombre de la variable. Pero mejor vemoslo con un ejemplo. En el siguiente cdigo, definimos una clase en la que tenemos un campo llamado Nombre, tambin definimos un mtodo en el que internamente se utiliza una variable llamada Nombre, para acceder a la variable declarada en la clase, tendremos que usar la instruccin o palabra clave this.
public class Cliente { public string Nombre = "Juan";

Saul Mamani Mamani

Desarrollo Agil de Software

45

public string Mostrar() { string Nombre = "Pepita"; return "Externo= " + this.Nombre + ", interno= " + Nombre; } }

mbito de mdulo Cuando hablamos de mdulos, nos estamos refiriendo a una clase, a una estructura o a cualquier otro tipo de datos que nos permita .NET. En estos casos, las variables declaradas dentro de un tipo de datos sern visibles desde cualquier parte de ese tipo, siempre teniendo en cuenta las restricciones mencionadas en los casos anteriores. mbito de espacio de nombres Los espacios de nombres son los contenedores de tipos de datos de mayor nivel, y sirven para contener definiciones de clases, estructuras, enumeraciones y delegados. Cualquier tipo definido a nivel de espacio de nombres estar disponible para cualquier otro elemento definido en el mismo espacio de nombres. Al igual que ocurre en el resto de mbitos "inferiores", si definimos un tipo en un espacio de nombres, podemos usar ese mismo nombre para nombrar a un procedimiento o a una variable, en cada caso se aplicar el mbito correspondiente y, tal como vimos anteriormente, tendremos que usar nombres nicos para poder acceder a los nombres definidos en niveles diferentes. La palabra clave global:: En Visual C# 2005 podemos definir espacios de nombres cuyos nombres sean los mismos que los definidos en el propio .NET Framework, para evitar conflictos de mbitos, podemos usar la palabra clave global:: para acceder a los que se han definido de forma "global" en .NET. Por ejemplo, si tenemos el siguiente cdigo, declarado dentro del espacio de nombres que C# define en cada nuevo proyecto, en el que definimos una clase dentro de un espacio de nombres llamado System y queremos acceder a uno de los tipos definidos en el espacio de nombres System de .NET, tendremos un problema:
namespace System { class Cliente { public string Nombre; public System.Int32 Edad; } }

El problema es que el compilador de C# nos indicar que el tipo Int32 no est definido, ya que intentar buscarlo dentro del mbito que actualmente tiene, es decir, la declaracin que nosotros hemos hecho de System, por tanto para poder acceder al tipo Int32 definido en el espacio de nombres "global" System de .NET, tal como podemos ver en la figura 2.6, para solventar el problema, tendremos que usar la instruccin global:: delante del espacio de nombre que produce el error.

Saul Mamani Mamani

Desarrollo Agil de Software

46

Figura 2.6. Error por conflicto de espacios de nombres globales El cdigo, una vez solventado el error, quedara de la siguiente forma:
namespace System { class Cliente { public string Nombre; public global::System.Int32 Edad; public override string ToString() { return "Nombre= " + Nombre + ", edad= " + Edad; } } }

Por supuesto nuestro espacio de nombres System nos dar ms quebraderos de cabeza, ya que al usarlo desde el mtodo Main de la clase principal de nuestro proyecto, nos obligar a usar el "nombre real" o completo de la clase Cliente, y si nuestra intencin es usar la clase Console para mostrar el nombre y la edad, tambin tendremos que aplicarle el modificador global:: a esa clase, para que el compilador sepa dnde encontrarla.
class Program { static void Main(string[] args) { System.Cliente cli = new System.Cliente(); cli.Nombre = "Pepe"; cli.Edad = 22; global::System.Console.WriteLine(cli); } }

Como podemos comprobar, el uso del nombre System como un espacio de nombres interno de nuestro proyecto no es una buena eleccin, aunque aqu lo hemos usado para resaltar los conflictos que se pueden producir al usar identificadores de espacios de nombres que ya estn definidos en la biblioteca de clases de .NET Framework. Nota: Afortunadamente este conflicto con los espacios de nombres no ser muy habitual para los desarrolladores que usemos el idioma de Cervantes, por la sencilla razn de que los espacios de nombres de .NET Framework suelen estar definidos usando palabras en ingls. Accesibilidad La accesibilidad es la caracterstica que podemos aplicar a cualquiera de los elementos que definamos en nuestro cdigo. Dependiendo de la accesibilidad declarada tendremos distintos tipos de accesos a esos elementos.

Saul Mamani Mamani

Desarrollo Agil de Software

47

Los modificadores de accesibilidad que podemos aplicar a los tipos y elementos definidos en nuestro cdigo pueden ser cualquiera de los mostrados en la siguiente lista:

public: Acceso no restringido. Este es modificador de accesibilidad con mayor "cobertura", podemos acceder a cualquier miembro pblico desde cualquier parte de nuestro cdigo. Aunque, como veremos, este acceso no restringido puede verse reducido dependiendo de dnde lo usemos. protected: Acceso limitado a la clase contenedora o a los tipos derivados de esta clase. Este modificador solamente se usa con clases que se deriven de otras. internal: Acceso limitado al proyecto actual (o ensamblado). protected internal: Acceso limitado al proyecto actual o a los tipos derivados de la clase contenedora. Una mezcla de los dos modificadores anteriores. private: Acceso limitado al tipo contenedor. Es el ms restrictivos de todos los modificadores de accesibilidad.

Estos modificadores de accesibilidad los podemos usar tanto en clases, estructuras, interfaces, enumeraciones, delegados, eventos, mtodos, propiedades y campos. Aunque no sern aplicables en espacios de nombres ( Namespace) ni en los miembros de las interfaces y enumeraciones. Accesibilidad de las variables en los procedimientos Las variables declaradas dentro de un procedimiento solo son accesibles dentro de ese procedimiento, en este caso solo se puede aplicar el mbito privado, aunque no podremos usar la instruccin private, sino declararlas de la forma habitual. Las accesibilidades predeterminadas La accesibilidad de un tipo, variable o procedimiento en la que no hemos indicado el modificador de accesibilidad depender del sitio en el que lo hemos declarado. Por ejemplo, las clases y estructuras definidas a nivel de un espacio de nombres solo pueden ser declaradas como public o internal, y si no llevan un modificador de accesibilidad, por defecto sern internal, es decir sern visibles en todo el proyecto actual. Por otro lado, las interfaces y enumeraciones por defecto sern pblicas. Cuando la clase, estructura, interfaz o enumeracin est definida dentro de otro tipo, la accesibilidad predeterminada ser private, pero admitirn cualquiera de los modificadores, salvo las interfaces y enumeraciones que no pueden ser protected. Por otro lado, los miembros de esos tipos tambin permiten diferentes niveles de accesibilidad, en el caso de las interfaces y enumeraciones, siempre sern pblicos, aunque no se permite el uso de esa instruccin. Los miembros de as clases y estructuras sern privados de forma predeterminada, en el caso de las clases, en esos miembros podemos indicar cualquiera de los cinco niveles de accesibilidad, mientras que en las estructuras no podremos declarar ningn miembro como protected o protected internal, ya que una estructura no puede usarse como base de otro tipo de datos. Anidacin de tipos Tal como hemos comentado en la seccin, podemos declarar tipos dentro de otros tipos, por tanto el mbito y accesibilidad de esos tipos dependen del mbito y accesibilidad del tipo que los contiene. Por ejemplo, si declaramos una clase con acceso internal, cualquier tipo que esta clase contenga siempre estar supeditado al mbito de esa clase, por tanto si declaramos otro tipo interno, aunque lo declaremos como public, nunca estar ms accesible que la clase contenedora, aunque en estos casos no habr ningn tipo de confusin, ya que para acceder a los tipos declarados dentro de otros tipos siempre tendremos que indicar la clase que los contiene. En el siguiente cdigo podemos ver cmo declarar dos clases "anidadas". Tal como podemos comprobar, para acceder a la clase Salario debemos indicar la clase Cliente, ya que la nica forma de acceder a una clase anidada es mediante la clase contenedora.

Saul Mamani Mamani

Desarrollo Agil de Software

48

internal class Cliente { public string Nombre; public class Salario { public decimal Importe; } } // Para usar la clase Salario debemos declararla de esta forma: Cliente.Salario s = new Cliente.Salario(); s.Importe = 2200;

Los tipos anidables Las clases y estructuras pueden contener otros tipos, mientras que las interfaces y enumeraciones no pueden contener otros tipos anidados. Los espacios de nombres pueden anidarse dentro de otros espacios de nombres y estos pueden contener definiciones de cualquiera de los tipos que C# nos permite crear. El nombre completo de un tipo Tal como hemos visto, al poder declarar tipos dentro de otros tipos y estos a su vez pueden estar definidos en espacios de nombres, podemos decir que el nombre "completo" de un tipo cualquiera estar formado por el/los espacios de nombres y el/los tipos que los contiene, por ejemplo si la clase Cliente definida anteriormente est a su vez dentro del espacio de nombres Ambitos, el nombre completo ser: Ambitos.Cliente y el nombre completo de la clase Salario ser: Ambitos.Cliente.Salario. Aunque para acceder a la clase Cliente no es necesario indicar el espacio de nombres, al menos si la queremos usar desde cualquier otro tipo declarado dentro de ese espacio de nombres, pero si nuestra intencin es usarla desde otro espacio de nombre externo a Ambitos, en ese caso si que tendremos que usar el nombre completo. Por ejemplo, en el siguiente cdigo tenemos dos espacios de nombres que no estn anidados, cada uno de ellos declara una clase y desde una de ellas queremos acceder a la otra clase, para poder hacerlo debemos indicar el nombre completo, ya que en caso contrario, el compilador de Visual C# 2005 sera incapaz de saber a que clase queremos acceder.
namespace Uno { public class Clase1 { public string Nombre; } } namespace Dos { public class Clase2 { public string Nombre; public void Prueba() { Uno.Clase1 c1 = new Uno.Clase1(); c1.Nombre = "Pepe"; } } }

Esto mismo lo podemos aplicar en el caso de que tengamos dos clases con el mismo nombre en espacios de nombres distintos.

Saul Mamani Mamani

Desarrollo Agil de Software

49

Nota: En el mismo proyecto podemos tener ms de una declaracin de un espacio de nombres con el mismo nombre, en estos casos el compilador lo tomar como si todas las clases definidas estuvieran dentro del mismo espacio de nombres, aunque estos estn definidos en ficheros diferentes. Importacin de espacios de nombres Tal como hemos comentado, los espacios de nombres pueden contener otros espacios de nombres y estos a su vez tambin pueden contener otros espacios de nombres o clases, y como hemos visto, para poder acceder a una clase que no est dentro del mismo espacio de nombres debemos indicar el "nombre completo". Para evitar estar escribiendo todos los espacios de nombres en los que est la clase que nos interesa declarar, podemos usar una especie de acceso directo o para que lo entendamos mejor, podemos crear una especie de "Path", de forma que al declarar una variable, si esta no est definida en el espacio de nombres actual, el compilador busque en todos los espacios de nombres incluidos en esas rutas (paths). Esto lo conseguimos usando la instruccin using seguida del espacio de nombres que queremos importar o incluir en el path de los espacios de nombres. Podemos usar tantas importaciones de espacios de nombres como necesitemos y estas siempre deben aparecer al principio del fichero. Por ejemplo, si tenemos el cdigo anterior y hacemos la importacin del espacio de nombres en el que est definida la clase Clase1:
using Uno;

podremos acceder a esa clase de cualquiera de estas dos formas:


Uno.Clase1 c1 = new Uno.Clase1(); Clase1 c1 = new Clase1();

Alias de espacios de nombres Si hacemos demasiadas importaciones de nombres, el problema con el que nos podemos encontrar es que el IntelliSense de Visual C# 2005 no sea de gran ayuda, ya que mostrar una gran cantidad de clases, y seguramente nos resultar ms difcil encontrar la clase a la que queremos acceder, o tambin podemos encontrarnos en ocasiones en las que nos interese usar un nombre corto para acceder a las clases contenidas en un espacio de nombres, por ejemplo, si queremos indicar de forma explcita las clases de un espacio de nombres como el de System.IO, podemos hacerlo de esta forma:
using io = System.IO;

De esta forma podemos usar el "alias" io para acceder a las clases y dems tipos definidos en ese espacio de nombres. En la figura 2.7 podemos ver que al escribir esa palabra, el IntelliSense nos muestra un mensaje de que realmente esa palabra es el espacio de nombres System.IO. En la figura 2.8 al escribir el punto, podemos ver los miembros del espacio de nombres al que hace referencia el alias io.

Saul Mamani Mamani

Desarrollo Agil de Software

50

Figura 2.7. IntelliSense y los alias a los espacios de nombres

Figura 2.8. Acceder a los miembros de un espacio de nombres usando un alias

6. Propiedades
Las propiedades son los miembros de los tipos que nos permiten acceder a los datos que dicho tipo manipula. Normalmente una propiedad est relacionada con un campo, de forma que el campo sea el que realmente contenga el valor y la propiedad simplemente sea una especie de mtodo a travs del cual podemos acceder a ese valor. Debido a que el uso de las propiedades realmente nos permite acceder a los valores de una clase (o tipo), se suelen confundir los campos con las propiedades, pero realmente un campo (o variable) pblico no es una propiedad, al menos en el sentido de que el propio .NET Framework no lo interpreta como tal, aunque en la prctica nos puede parecer que es as, ya que se utilizan de la misma forma. Pero no debemos dejarnos llevar por la comodidad y si no queremos perder funcionalidad, debemos diferenciar en nuestro cdigo las propiedades de los campos. Lo primero que debemos tener presente es que gracias a esta diferenciacin que hace .NET Framework, (realmente VB6 tambin la hace), podemos poner en prctica una de las caractersticas de la programacin orientada a objetos: la encapsulacin, de forma, que la manipulacin de los datos que una clase contiene siempre se deben hacer de forma "interna" o privada a la clase, dejando a las propiedades la posibilidad de que externamente se manipulen de forma controlada, esos datos. De esta forma tendremos mayor control sobre cmo se acceden o se asignan los valores a esos datos, ya que al definir una propiedad, tal como hemos comentado, realmente estamos definiendo un procedimiento con el cual podemos controlar cmo se acceden a esos datos. Definir una propiedad

Saul Mamani Mamani

Desarrollo Agil de Software

51

Debido a que una propiedad realmente nos permite acceder a un dato que la clase (o estructura) manipula, siempre tendremos un campo relacionado con una propiedad. El campo ser el que contenga el valor y la propiedad ser la que nos permita manipular ese valor. En C#, las propiedades las declaramos como cualquier otra funcin, con la diferencia de que no podemos usar parntesis despus del nombre y que en el cuerpo de la propiedad, que estar limitado por las tpicas llaves, tendremos que indicar dos bloques de cdigo, los cuales servirn para diferenciar el cdigo que el compilador utilizar para recuperar el valor de la propiedad o el usado para hacer la asignacin. Esos dos bloques estarn indicados por la instruccin get, para recuperar el valor, y la instruccin set, para asignar el valor. El bloque set tendr un parmetro implcito que representa al valor indicado a la derecha del signo igual de la asignacin y se llama value. La mejor forma de aclarar cmo se define una propiedad es viendo un ejemplo.
public class Cliente { private string nombre; public string Nombre { get { return nombre; } set { nombre = value; } } }

Como podemos comprobar tenemos dos bloques de cdigo, el bloque get que es el que se usa cuando queremos acceder al valor de la propiedad, por tanto devolvemos el valor del campo privado usado para almacenar ese dato. El bloque set es el usado cuando asignamos un valor a la propiedad, este bloque tiene predefinido un parmetro (value) que representa al valor que queremos asignar a la propiedad. Nota: En este ejemplo, estamos usando nombre (con ene minscula) para el campo privado, y Nombre con la ene en maysculas para el nombre de la propiedad, esta es la recomendacin que se hace para los desarrolladores de .NET, y en C# se puede seguir "al pie de la letra" porque, como sabemos, C# considera diferentes a las variables que solo se diferencian por las maysculas y minsculas. Propiedades de slo lectura En ciertas ocasiones nos puede resultar interesante que una propiedad sea de slo lectura, de forma que el valor que representa no pueda ser cambiado. Para definir una propiedad de solo lectura en C# basta con definir solo el bloque get de la propiedad. En el siguiente cdigo definimos una propiedad de solo lectura llamada Hoy:
public DateTime Hoy { get { return DateTime.Now; } }

Propiedades de slo escritura

Saul Mamani Mamani

Desarrollo Agil de Software

52

De igual forma, si queremos definir una propiedad que sea de slo escritura, solo definiremos el bloque set, tal como podemos ver en este cdigo:
public string Password { set { if( value == "blablabla" ) { // ok } } }

Diferente accesibilidad para los bloques get y set Una de las novedades de Visual C# 2005 con respecto a las versiones anteriores de este lenguaje es que en las propiedades normales (de lectura y escritura), podemos definir diferentes niveles de accesibilidad a cada uno de los dos bloques que forman una propiedad. Por ejemplo, podramos definir el bloque get como pblico, (siempre accesible), y el bloque set como private, de forma que solo se puedan realizar asignaciones desde dentro de la propia clase. Por ejemplo, el salario de un empleado podramos declararlo para que desde cualquier punto se pueda saber el importe, pero la asignacin de dicho importe solo estar accesible para los procedimientos definidos en la propia clase:
public class Empleado { private decimal salario; public decimal Salario { get { return salario; } private set { salario = value; } } }

Para hacer que el bloque set sea privado, lo indicamos con el modificador de accesibilidad private, al no indicar ningn modificador en el bloque get, ste ser el mismo que el de la propiedad. Nota: El nivel de accesibilidad de los bloques get o set debe ser igual o inferior que el de la propiedad, por tanto si la propiedad la declaramos como private, no podemos definir como pblico ninguno de los bloques get o set. Propiedades predeterminadas (indizadores) Las propiedades predeterminadas son aquellas en las que no hay que indicar el nombre de la propiedad para poder acceder a ellas, en C# no existen las propiedades predeterminadas, ya que el nico tipo de propiedad predeterminada que se permite en .NET y por tanto en C#, son las conocidas como indizadores, que son propiedades que reciben un parmetro y hacen que la clase en las que est definida acte como un array. Para definir un indizador en C# debemos usar la instruccin this como nombre de la propiedad, por tanto, debemos suponer que la propiedad predeterminada realmente no tiene un nombre definido, aunque en otros lenguajes de .NET, e incluso en el propio .NET Framework el nombre que se utiliza, o al menos el que se recomienda, es Item. La definicin de un indizador quedara como mostramos a continuacin:

Saul Mamani Mamani

Desarrollo Agil de Software

53

public Empleado this[string index] { get { return new Empleado(); } }

En este caso, el indizador devuelve un nuevo objeto del tipo Empleado, pero aqu lo hacemos as para simplificar. Lo importante es la forma de construir el indizador: se utiliza this como nombre, recibe un parmetro y devuelve cualquier tipo de datos, ya sean definidos por nosotros o los que existen en .NET. Para usar este tipo de propiedades lo haremos como si estuvisemos accediendo a un array, pero usando la variable que contiene el objeto de la clase que define el indizador.
Empleado e = new Empleado(); Empleado e1 = e["Uno"];

Sobrecarga en los indizadores Como cualquier otro procedimiento que define algn parmetro, podemos crear sobrecargas de los indizadores para que reciba diferentes tipos de datos como argumento, y si el tipo del parmetro es diferente, el tipo de valor devuelto tambin lo puede ser, por ejemplo, en el siguiente ejemplo, definimos otra sobrecarga que recibe como parmetro un valor de tipo int y devuelve una cadena.
public string this[int index] { get { return "El empleado"; } }

Las propiedades predeterminadas de C# son las nicas que permiten definir parmetros, adems de que puede definir uno o ms, pero como mnimo uno. Y como cualquier propiedad puede definir un solo bloque get o set o los dos, dependiendo de la funcionalidad que queramos darle, y como es de suponer, la accesibilidad de uno de los dos bloques puede ser diferente del definido en la propiedad, es decir, actan de la misma forma que cualquier propiedad, pero con las "obligaciones" de que debe definir al menos un parmetro y que el nombre es la palabra clave this.

7. Interfaces
Las interfaces son un elemento bastante importante en .NET Framework, ya que de hecho se utiliza con bastante frecuencia, en esta leccin veremos que son las interfaces y como utilizarlas en nuestros proyectos, tambin veremos que papel juegan en .NET y cmo aplicar algunas de las definidas en la biblioteca base. Qu es una interfaz? Las interfaces son una forma especial de una clase, aunque la diferencia principal con las clases es que las interfaces no contienen cdigo ejecutable, solo definen los miembros y, en el caso de los mtodos, si reciben parmetros y de que tipo son. Las interfaces se utilizan para indicar el "comportamiento" que tendr una clase, o al menos qu miembros debe definir esa clase.

Saul Mamani Mamani

Desarrollo Agil de Software

54

Para definir una interfaz en C# tenemos que usar la instruccin interface seguida del nombre y encerrado entre un par de llaves los miembros que define:
public interface IAnimal { //... }

Tal como comentamos en el apartado de la accesibilidad, los miembros de una interfaz siempre son pblicos, pero no debemos indicarlo, ya que se sobreentiende que es as y el compilador producira un error indicndonos que el modificador de accesibilidad no se puede aplicar a ese tipo de elementos. Como los miembros de las interfaces no contienen cdigo ejecutable, solo debemos definir la "firma" del mtodo, propiedad o evento que queremos definir. En el caso de las propiedades, tendremos que indicar los bloques get y/o set que la clase o tipo que la implemente debe definir.

public interface IAnimal { void Desplazar(); void Alimentarse(); string Especie { get; set;} int Edad { get;} }

Nota: Segn las indicaciones de nomenclatura de .NET Framework, se recomienda que todas las interfaces empiecen con una I mayscula seguida del nombre al que hacer referencia la interfaz. Qu puede contener una interfaz? Tal como hemos visto anteriormente, una interfaz pude contener cualquiera de los miembros que una clase puede contener, salvo los campos; por tanto, tambin puede contener eventos, pero debido a cmo son tratados los eventos en C#, los cuales veremos con ms detalle en la leccin 4 de este primer mdulo, y la dependencia con los delegados, para poder definir un evento en una interfaz, antes debemos definir el delegado que se usar con ese evento. En el siguiente ejemplo tenemos una interfaz que define todos los tipos de miembros posibles, incluso dos indizadores, en uno de los cuales debemos implementar tanto el bloque get como el set y en el otro, que devuelve un valor del mismo tipo que la interfaz, solo define el bloque get:
// Declaracin del delegado para el evento de la interfaz public delegate void DatosCambiadosEventHandler(); public interface IPrueba { void Mostrar(); string Saludo(string nombre); string Nombre { get; set;} int Edad { get;} string this[int index] { get; set;} IPrueba this[string index] { get;} event DatosCambiadosEventHandler DatosCambiados; }

Una interfaz es un contrato Siempre que leemos sobre las interfaces, lo primero con lo que nos solemos encontrar es que una interfaz es un contrato. Veamos que nos quieren decir con esa frase.

Saul Mamani Mamani

Desarrollo Agil de Software

55

Tal como acabamos de ver, las interfaces solo definen los miembros, pero no el cdigo a usar en cada uno de ellos, esto es as precisamente porque el papel que juegan las interfaces es el de solo indicar que es lo que una clase o estructura puede, o mejor dicho, debe implementar. Si en una clase indicamos que queremos "implementar" una interfaz, esa clase debe definir cada uno de los miembros que la interfaz expone. De esta forma nos aseguramos de que si una clase implementa una interfaz, tambin implementa todos los miembros definidos en dicha interfaz. Cuando una clase implementa una interfaz est firmando un contrato con el que se compromete a definir todos los miembros que la clase define, de hecho el propio compilador nos obliga a hacerlo. Las interfaces y el polimorfismo Como comentamos anteriormente, el polimorfismo es una caracterstica que nos permite acceder a los miembros de un objeto sin necesidad de tener un conocimiento exacto de ese objeto (o de la clase a partir del que se ha instanciado), lo nico que tenemos que saber es que ese objeto tiene ciertos mtodos (u otros miembros) a los que podemos acceder. Tambin hemos comentado que las interfaces representan un contrato entre las clases que las implementan, por tanto las interfaces pueden ser, (de hecho lo son), un medio para poner en prctica esta caracterstica de la programacin orientada a objetos. Si una clase implementa una interfaz, esa clase tiene todos los miembros de la interfaz, por tanto podemos acceder a esa clase, que en principio pude sernos desconocida, desde un objeto del mismo tipo que la interfaz. Usar una interfaz en una clase Para poder utilizar una interfaz en una clase, o dicho de otra forma: para "implementar" los miembros expuestos por una interfaz en una clase debemos hacerlo a continuacin del nombre de la clase, separndola con dos puntos y a continuacin el nombre de la interfaz:
public class Prueba : IPrueba { }

Si estamos usando el IDE de Visual C# 2005, al escribir el nombre de la interfaz, tal como podemos ver en la figura 2.9, nos indicar s queremos implementar los miembros de esa interfaz.

Figura 2.9. Ayuda del IDE de Visual C# 2005 para implementar una interfaz en una clase Las opciones ofrecidas por el IDE de C# son dos, aunque la primera ser la que utilicemos con ms frecuencia. En la figura 2.10 podemos ver las dos opciones que nos ofrece:

Figura 2.10. Las dos opciones de implementacin ofrecidas por el IDE de Visual C# 2005

Saul Mamani Mamani

Desarrollo Agil de Software

56

Al pulsar en la primera opcin tendremos el siguiente cdigo:


public class Prueba : IPrueba { #region IPrueba Members public void Mostrar() { throw new Exception("The method or operation is not implemented."); } public string Saludo(string nombre) { throw new Exception("The method or operation is not implemented."); } public string Nombre { get { throw new Exception("The method or operation is not implemented."); } set { throw new Exception("The method or operation is not implemented."); } } public int Edad { get { throw new Exception("The method or operation is not implemented."); } } public string this[int index] { get { throw new Exception("The method or operation is not implemented."); } set { throw new Exception("The method or operation is not implemented."); } } public IPrueba this[string index] { get { throw new Exception("The method or operation is not implemented."); } } public event DatosCambiadosEventHandler DatosCambiados; #endregion }

Como podemos apreciar, no solo ha aadido las definiciones de cada miembro de la interfaz, sino que tambin aade cdigo extra a cada uno de esos miembros con idea de que nos demos cuenta de que debemos escribir el nuestro propio. Tambin podemos apreciar que lo nico que ha hecho el IDE de C# es declarar cada uno de los miembros de la interfaz, pero no ha relacionado esos miembros con los definidos en la interfaz, para C# no es necesario hacer esa liga, ya que lo nico que le basta para crear esa relacin es que el miembro de la clase se llame igual que el de la interfaz y que tenga el mismo nmero de parmetros, tipos, etc., es decir, que tenga la misma "firma". Adems, comprobamos que todos los miembros los ha definido como pblicos, esto realmente no es obligatorio, y podemos cambiarlos a privado, interno o como queramos, pero debemos saber que en cualquier caso, aunque un mtodo

Saul Mamani Mamani

Desarrollo Agil de Software

57

lo hayamos declarado como privado en la clase, siempre ser accesible por medio de un objeto del tipo de la interfaz, pero de esto nos ocuparemos en la siguiente seccin. Acceder a los miembros implementados Una vez que tenemos implementada una interfaz en nuestra clase, podemos acceder a esos miembros de forma directa, es decir, usando un objeto creado a partir de la clase:
Prueba p = new Prueba(); p.Mostrar();

O bien de forma indirecta, por medio de una variable del mismo tipo que la interfaz:
Prueba prueba1 = new Prueba(); IPrueba interfaz1; interfaz1 = prueba1; interfaz1.Mostrar();

Qu ocurre aqu? Como ya comentamos anteriormente, cuando asignamos variables por referencia, realmente lo que asignamos son referencias a los objetos creados en la memoria, por tanto la variable interfaz1 est haciendo referencia al mismo objeto que prueba1, aunque esa variable solo tendr acceso a los miembros de la clase Prueba que conoce, es decir, los miembros definidos en IPrueba. Si la clase define otros miembros que no estn definidos en la interfaz, la variable interfaz1 no podr acceder a ellos. Saber si un objeto implementa una interfaz (is y as) Si las interfaces sirven para acceder de forma annima a los mtodos de un objeto, es normal que en C# tengamos algn mecanismo para descubrir si un objeto implementa una interfaz, as podemos tomar ciertas acciones dependiendo de que un objeto implemente o no una interfaz determinada. Para realizar esta comprobacin podemos usar en una expresin if la instruccin is, de forma que si la variable indicada contiene el tipo especificado despus de is, la condicin se cumple:
if (prueba1 is IPrueba) Console.WriteLine("prueba1 es del tipo IPrueba"); else Console.WriteLine("prueba1 no es del tipo IPrueba");

De esta forma nos aseguramos de que el cdigo se ejecutar solamente si la variable prueba1 contiene una definicin de la interfaz IPrueba. Si lo que queremos hacer es trabajar con objetos que implementen una interfaz en concreto, por ejemplo, podemos tener el siguiente cdigo en el que si el objeto comprobado implementa la interfaz IPrueba llamaremos al mtodo Mostrar:
static void comprobarIPrueba(object o) { if (o is IPrueba) ((IPrueba)o).Mostrar(); }

Saul Mamani Mamani

Desarrollo Agil de Software

58

En este ejemplo, el parmetro es de tipo object, que es el que sirve como base de todas las clases de .NET, hacemos la comprobacin de que implementa la interfaz IPrueba, si es as, haciendo una conversin (cast) llamamos al mtodo Mostrar, hay que notar los parntesis que encierran el cast de la variable o para poder acceder al mtodo, ya que si no lo hacemos el compilador entender que queremos convertir o.Mostrar() al tipo IPrueba, aunque antes de llegar ah, nos dar un error de compilacin indicndonos que object no implementa Mostrar. Pero si en lugar de hacer una simple llamada a un mtodo del objeto necesitamos hacer ms cosas, el cdigo que usaramos sera algo as:
static void comprobarIPrueba(object o) { if (o is IPrueba) { IPrueba unaPrueba = (IPrueba)o; unaPrueba.Mostrar(); Console.WriteLine(unaPrueba.Saludo(unaPrueba.Nombre)); } }

Es decir, que no solo llamamos a un mtodo, sino que hacemos una conversin que asignamos a un objeto y despus usamos ese objeto para llamar a los mtodos y propiedades que queramos. En este ejemplo solo usamos dos de los miembros de ese objeto, pero podramos usar ms. En estos casos, es preferible usar otra instruccin de C#: as. Con as hacemos una conversin al tipo indicado y si no es de ese tipo devuelve un valor nulo, y si lo es, devuelve una referencia al objeto de ese tipo:
static void comprobarIPrueba2(object o){ IPrueba unaPrueba = o as IPrueba; if (unaPrueba != null) { unaPrueba.Mostrar(); Console.WriteLine(unaPrueba.Saludo(unaPrueba.Nombre));} }

En este caso, asignamos a la variable unaPrueba el contenido de la variable o, pero tomando la implementacin de IPrueba, (si es que la tiene), en caso de que el valor devuelto no sea nulo, usamos ese objeto. Aunque pueda parecer que es lo mismo usar is o as, al menos por el nmero de lneas de cdigo, debemos saber que el cdigo generado por el compilador al usar la instruccin as es ms compacto que el de la instruccin is. Implementacin de mltiples interfaces En .NET las clases solo pueden derivarse de una sola clase (herencia simple), pero lo que si pueden hacer las clases de .NET es implementar ms de una interfaz. Para indicar que implementamos ms de una interfaz lo haremos indicando las interfaces separadas por comas:
public class Prueba2 : IPrueba, IComparable

Ni que decir tiene que debemos implementar todos los miembros de todas las interfaces indicadas. Mltiple implementacin de un mismo miembro Como acabamos de comprobar, una misma clase puede implementar ms de una interfaz, y esto nos puede causar una duda: Qu ocurre si dos interfaces definen un mtodo que es idntico en ambas? En principio, no habra problemas, si la creacin de los mtodos implementados la hacemos ayudndonos por el IDE de Visual C# 2005, (ver figuras 2.9 y 2.10), ste creara solo un mtodo el cual servir para las dos interfaces, ya que, como comentamos anteriormente, el compilador de C# lo que espera es que exista un mtodo que se llame de la misma forma que el definido en la interfaz y que tenga la misma "firma".

Saul Mamani Mamani

Desarrollo Agil de Software

59

Por ejemplo, si tenemos las siguientes interfaces y las implementamos en la clase Prueba3, el cdigo quedara de la siguiente forma:
public interface IPrueba2 { void Mostrar(); string Nombre { get; set;} } public interface IMostrar { void Mostrar(); } public class Prueba3 : IPrueba2, IMostrar { public void Mostrar() { Console.WriteLine("Mostrar en Prueba3"); } private string nombre; public string Nombre { get{ return nombre; } set{ nombre = value; } } }

Implementacin privada Tal como hemos visto en la seccin anterior, podemos implementar ms de una interfaz en una misma clase, adems si existen mtodos con la misma "firma" en las interfaces, C# utiliza solo un mtodo para todos los mtodos que tengan esa firma. Aunque es posible que queramos que no todos los miembros que tengan la misma firma utilicen el mismo mtodo pblico de la clase, o bien puede que queramos implementar una interfaz sin que se expongan los miembros como parte pblica de la clase, en esos casos, tambin podremos acceder a esos miembros privados, aunque siempre por medio de un objeto del mismo tipo que la interfaz. En estos casos, podemos usar lo que el IDE de C# llama "implementacin explcita de una interfaz", (ver figura 2.10). Si nos decidimos por esa opcin, el cdigo generado por el IDE para la clase Prueba3 del ejemplo de la seccin anterior, quedara de esta forma:
public class Prueba3 : IPrueba2, IMostrar { public void Mostrar() { Console.WriteLine("Mostrar en Prueba3"); } private string nombre; public string Nombre { get{ return nombre; } set{ nombre = value; } } void IMostrar.Mostrar() { Console.WriteLine("Mostrar de IMostrar en Prueba3"); } }

IMostrar.Mostrar() no formara parte de la clase Prueba3, salvo que usemos un objeto del tipo IMostrar, tal como vemos en la figura 2.11:

Saul Mamani Mamani

Desarrollo Agil de Software

60

Figura 2.11. Los miembros de la interfaz IMostrar Para probar que esto funciona, podemos usar este cdigo:
Prueba3 p3 = new Prueba3(); p3.Nombre = "Juan"; p3.Mostrar(); IMostrar mostrar = p3; mostrar.Mostrar();

Cuya salida sera la siguiente:


Mostrar en Prueba3 Mostrar de IMostrar en Prueba3

Resumiendo: podemos implementar varias interfaces y tambin podemos decidir si queremos que esas implementaciones sean visibles a travs de la propia clase o por medio de objetos del tipo de la interfaz implementada. Dnde podemos implementar las interfaces? Para ir acabando este tema nos queda por saber, entre otras cosas, dnde podemos implementar las interfaces, es decir, en que tipos de datos podemos usarlas. La implementacin de interfaces la podemos hacer en las clases (class), estructuras (struct) y en otras interfaces (interface). Debido a que una interfaz puede implementar otras interfaces, si en una clase implementamos una interfaz que a su vez implementa otras, esa clase tendr definidas cada una de las interfaces, lo mismo ocurre con una clase que "se derive" de otra clase que implementa alguna interfaz, la nueva clase tambin incorporar esa interfaz. Si en una clase implementamos una interfaz que a su vez implementa otras interfaces, esa clase tendr definiciones de todos los miembros de todas las interfaces, por ejemplo, si tenemos la siguiente definicin de la interfaz IPrueba3 que "implementa" la interfaz IMostrar:
public interface IPrueba3 : IMostrar { string Saludo(string nombre); string Nombre{get; set;} }

Y la clase Prueba4 implementa IPrueba3, la definicin de los miembros quedara de la siguiente forma:
public class Prueba4 : IPrueba3 { #region IPrueba3 Members public string Saludo(string nombre) { // El mtodo Saludo de IPrueba3 } // La propiedad Nombre de IPrueba3

Saul Mamani Mamani

Desarrollo Agil de Software

61

public string Nombre { get{ ... } set{ ... } } #endregion #region IMostrar Members public void Mostrar() { // El mtodo Mostrar de IMostrar } #endregion }

En este cdigo, el mtodo Mostrar se indica mediante la interfaz IMostrar, pero tambin se puede hacer por medio de IPrueba3.Mostrar, ya que IPrueba3 tambin lo implementa (o hereda). Un ejemplo prctico usando una interfaz de .NET Tal como comentamos al principio, el propio .NET est "plagado" de interfaces, cada una de ellas tiene un fin concreto, por ejemplo, si queremos definir una clase que pueda ser clasificada por el propio .NET, esa clase debe implementar la interfaz IComparable, ya que el mtodo Sort, (de la clase que contiene los elementos del tipo definido por nosotros), que es el encargado de clasificar los elementos, har una llamada al mtodo IComparable.CompareTo de cada uno de los objetos que queremos clasificar, por tanto, si la clase no ha definido esa interfaz, no podremos clasificar los elementos que contenga. En el siguiente cdigo tenemos la definicin de una clase llamada Empleado que implementa la interfaz IComparable y en el mtodo CompareTo hace la comprobacin de que objeto es mayor o menor, si el de la propia clase o el indicado en el parmetro de esa funcin:
public class Empleado : IComparable { public string Nombre; public Empleado() {} public Empleado(string nombre) { this.Nombre = nombre; } public int CompareTo(object obj) { if (obj is Empleado) { Empleado e1 = ((Empleado)obj); return String.Compare(this.Nombre, e1.Nombre); } else return 0; } }

En el mtodo CompareTo hacemos una comprobacin de que el objeto con el que debemos realizar la comparacin es del tipo Empleado, en ese caso convertimos el objeto pasado en uno del tipo Empleado y comparamos los nombres. Si el objeto que recibe el mtodo no es del tipo Empleado, devolvemos un cero, para indicar que no haga ninguna clasificacin, ya que ese valor indica que los dos objetos son iguales. Esta clase la podemos usar de esta forma:
// Una coleccin de datos del tipo Empleado

Saul Mamani Mamani

Desarrollo Agil de Software

62

List<Empleado> empleados = new List<Empleado>(); // Aadimos varios empleados a la coleccin empleados.Add(new Empleado("Pepe")); empleados.Add(new Empleado("Bernardo")); empleados.Add(new Empleado("Juan")); empleados.Add(new Empleado("Ana")); // La clasificamos empleados.Sort(); // Mostramos los nombres clasificados foreach (Empleado e1 in empleados) Console.WriteLine(e1.Nombre);

Al llamar al mtodo Sort de la coleccin empleados, se utilizar la implementacin del mtodo CompareTo que es el encargado de indicar en que orden deben ir los datos. Si nuestra clase Empleado tuviese que hacer la clasificacin basndose en otras propiedades, sera en el mtodo CompareTo donde habra que hacer la decisin de que valor devolver, teniendo en cuenta de que un valor cero indica que los dos objetos son iguales, un valor menor que cero que nuestro objeto debe ir antes que el indicado en el parmetro y un valor mayor de cero para indicar que nuestro objeto debe ir despus del indicado. El resultado de este cdigo sera el siguiente:
Ana Bernardo Juan Pepe

LECCION 3: MANEJO DE EXCEPCIONES.


Introduccin Es indiscutible que por mucho que nos lo propongamos, nuestras aplicaciones no estarn libres de errores, y no nos referimos a errores sintcticos, ya que, afortunadamente, el IDE (Integrated Development Envirnment, entorno de desarrollo integrado) de Visual Studio 2005 nos avisar de cualquier error sintctico e incluso de cualquier asignacin no vlida, pero de lo que no nos avisar, como es lgico, ser de los errores que se produzcan en tiempo de ejecucin. Para estos casos, C# pone a nuestra disposicin el manejo de excepciones, veamos pues cmo utilizarlo. Manejo de excepciones

Manejo de excepciones o Manejo de excepciones estructuradas Bloque try Bloque catch Varias capturas de errores en un mismo bloque try/catch Bloque finally o Captura de errores no controlados

1.

Manejo de excepciones

En Visual C# 2005 se utiliza un tratamiento estructurado de excepciones, de esta forma podemos detectar los errores que se produzcan en nuestras aplicaciones de una forma ms "ordenada".

Saul Mamani Mamani

Desarrollo Agil de Software

63

Manejo de excepciones estructuradas Las excepciones en C# las podemos controlar usando las instrucciones try / catch / finally. Estas instrucciones realmente son bloques de instrucciones, y por tanto estarn delimitadas con un par de llaves. Cuando queramos controlar una parte del cdigo que puede producir un error lo incluimos dentro del bloque try, si se produce un error, ste lo podemos detectar en el bloque catch, por ltimo, independientemente de que se produzca o no una excepcin, podemos ejecutar el cdigo que incluyamos en el bloque finally. Cuando creamos una estructura de control de excepciones no estamos obligados a usar los tres bloques, aunque el primero: try si es necesario, ya que es el que le indica al compilador que tenemos intencin de controlar los errores que se produzcan. Por tanto podemos crear un "manejador" de excepciones usando los tres bloques, usando try y catch o usando try y finally. Veamos ahora con ms detalle cada uno de estos bloques y que es lo que podemos hacer en cada uno de ellos. Bloque try En este bloque incluiremos el cdigo en el que queremos comprobar los errores. El cdigo a usar ser un cdigo normal, es decir, no tenemos que hacer nada en especial, ya que en el momento que se produzca el error se usar (si hay) el cdigo del bloque catch. Bloque catch Si se produce una excepcin, sta la capturamos en un bloque catch. En el bloque catch podemos indicar que tipo de excepcin queremos capturar, para ello usaremos una variable de tipo Exception, la cual puede ser del tipo de error especfico que queremos controlar o de un tipo genrico. Por ejemplo, si sabemos que nuestro cdigo puede producir un error al trabajar con ficheros, podemos usar un cdigo como ste:
try { // cdigo para trabajar con ficheros, etc. } catch(System.IO.IOException ex) { // el cdigo a ejecutar cuando se produzca ese error }

Si nuestra intencin es capturar todos los errores que se produzcan, es decir, no queremos hacer un filtro con errores especficos, podemos usar la clase Exception como tipo de excepcin a capturar. La clase Exception es la ms genrica de todas las clases para manejo de excepciones, por tanto capturar todas las excepciones que se produzcan.
try { // cdigo que queremos controlar } catch(System.Exception ex) { // el cdigo a ejecutar cuando se produzca cualquier error }

Aunque si no vamos usar la variable indicada en el bloque Catch, pero queremos que no se detenga la aplicacin cuando se produzca un error, podemos hacerlo de esta forma:

Saul Mamani Mamani

Desarrollo Agil de Software

64

try { // cdigo que queremos controlar } catch { // el cdigo a ejecutar cuando se produzca cualquier error }

La variable indicada en el bloque catch la podemos usar para mostrar un mensaje al usuario o para obtener informacin extra sobre el error, pero no siempre vamos a hacer uso de esa variable, en ese caso podemos utilizar el cdigo anterior, en el que no se usa una variable y tampoco se indica el tipo de error que queremos interceptar. Pero es posible que nuestra intencin sea capturar errores de un tipo concreto sin necesidad de utilizar una variable, en ese caso podemos crear un bloque catch como el siguiente, en el que solo se indica el tipo de excepcin:
try { // cdigo que queremos controlar } catch(FormatException) { // interceptar los errores del tipo FormatException }

Varias capturas de errores en un mismo bloque try/catch En un mismo try/catch podemos capturar diferentes tipos de errores, para ello podemos incluir varios bloques catch, cada uno de ellos con un tipo de excepcin diferente. Es importante tener en cuenta que cuando se produce un error y usamos varios bloques catch, el CLR de .NET buscar la captura que mejor se adapte al error que se ha producido, pero siempre lo har examinando los diferentes bloques catch que hayamos indicado empezando por el indicado despus del bloque try, por tanto deberamos poner las ms genricas al final, de forma que siempre nos aseguremos de que las capturas de errores ms especficas se intercepten antes que las genricas. Aunque el propio compilador de C# detectar si hay capturas de errores genricas antes que las ms especficas, avisndonos de ese hecho. En el siguiente cdigo capturamos un error especfico y tambin uno genrico, con idea de que tengamos siempre controlado cualquier error que se produzca:
try { // cdigo que queremos controlar } catch(FormatException) { // captura de error de formato } catch(Exception ex) { // captura del resto de errores }

Bloque finally En este bloque podemos indicar las instrucciones que queremos que se ejecuten, se produzca o no una excepcin. De esta forma nos aseguramos de que siempre se ejecutar un cdigo, por ejemplo para liberar recursos, se haya producido un error o no.

Saul Mamani Mamani

Desarrollo Agil de Software

65

En este cdigo tenemos tres capturas de errores diferentes y un bloque finally que siempre se ejecutar, se produzca o no un error:
int i, j; // try { Console.Write("Un numero "); i = Convert.ToInt32(Console.ReadLine()); Console.Write("Otro numero "); j = Convert.ToInt32(Console.ReadLine()); int r = i / j; Console.WriteLine("El resultado es: {0}", r); } catch (FormatException) { Console.WriteLine("No es un nmero vlido"); // Salimos de la funcin, pero se ejecutar el finally return; } catch (DivideByZeroException) { Console.WriteLine("La divisin por cero no est permitida."); } catch (Exception ex) { // Captura del resto de excepciones Console.WriteLine(ex.Message); } finally { // Este cdigo siempre se ejecutar Console.WriteLine("Se acab"); }

Nota: Hay que tener en cuenta de que incluso si usamos return dentro de un bloque de control de errores, se ejecutar el cdigo indicado en el bloque finally. Captura de errores no controlados Como es lgico, si no controlamos las excepciones que se puedan producir en nuestras aplicaciones, stas sern inicialmente controladas por el propio runtime de .NET, en estos casos la aplicacin se detiene y se muestra el error al usuario. Pero esto es algo que no deberamos consentir, por tanto siempre deberamos detectar todos los errores que se produzcan en nuestras aplicaciones, pero a pesar de que lo intentemos, es muy probable que no siempre podamos conseguirlo. Una forma de hacerlo es iniciando nuestra aplicacin dentro de un bloque try/catch, de esta forma, cuando se produzca el error, se capturar en ese bloque catch, porque cuando el runtime de .NET se encuentra con una excepcin, lo que hace es revisar "la pila" de llamadas y buscar algn try/catch, si lo encuentra, lo utiliza, y si no lo encuentra, se encarga de lanzar la excepcin deteniendo el programa. Esto es importante saberlo, no ya por detectar esos errores que no hemos tenido la previsin de controlar, sino porque es posible que si un error se produce dentro de un mtodo en el que no hay captura de errores, pero antes de llamar a ese mtodo hemos usado un try/catch, el error ser interceptado por ese catch, aunque posiblemente ni siquiera lo pusimos pensando que poda capturar errores producidos en otros niveles ms profundos de nuestra aplicacin.

Saul Mamani Mamani

Desarrollo Agil de Software

66

LECCION4: EVENTOS Y DELEGADOS.


Introduccin La forma que tienen nuestras clases y estructuras de comunicar que algo est ocurriendo, es por medio de eventos. Los eventos son mensajes que se lanzan desde una clase para informar al "cliente" que los utiliza de que est pasando algo. Seguramente estaremos acostumbrados a usarlos, incluso sin tener una nocin consciente de que se tratan de eventos, o bien porque es algo tan habitual que no le prestamos mayor atencin, es el caso de las aplicaciones de escritorio, cada vez que presionamos un botn, escribimos algo o movemos el mouse se estn produciendo eventos. En Visual C# 2005 podemos definir eventos en nuestras clases, pero como comprobaremos, estos estn totalmente relacionados con los delegados. En esta leccin veremos que son los delegados y que relacin tienen con los eventos, tambin veremos que podemos tener mayor control sobre cmo se interceptan los eventos e incluso cmo y cuando se asocian los eventos en la aplicacin cliente, aunque primero empezaremos viendo cmo declarar y utilizar eventos en nuestros tipos de datos. Eventos y delegados

Eventos o Interceptar los eventos de los controles de un formulario o Asociar un evento con un control o Asociar varios eventos a un mismo procedimiento Asociarlos usando el diseador de Windows Forms Asociarlos usando cdigo Delegados o Definicin "formal" de delegado o Utilizar un delegado para acceder a un mtodo o Cmo producir eventos o Los delegados y los mtodos annimos o Qu ocurre cuando se asigna y se produce un evento? Definir y producir eventos en una clase o Definir eventos en una clase o Producir un evento en nuestra clase o Asociar los eventos de una clase con un mtodo o Desasociar eventos o Detectar cuando se aade o se elimina un delegado de un evento

1.

Eventos

En esta leccin veremos cmo trabajar con los eventos producidos en una aplicacin de Windows, con idea de que nos familiaricemos con ellos, y veamos la relacin que tienen con los delegados; en la siguiente leccin trataremos los delegados con ms profundidad, para seguir con la forma de declararlos en nuestras clases. Interceptar los eventos de los controles de un formulario Debido a que an no hemos visto el mdulo dedicado a las aplicaciones de Windows, en las que se utilizan los "clsicos" formularios, no vamos a entrar en detalles sobre cmo crear un formulario ni como aadir controles, etc., todo eso lo veremos en el siguiente mdulo. Para simplificar las cosas, veremos simplemente cmo definir un evento y asociarlo a un

Saul Mamani Mamani

Desarrollo Agil de Software

67

control e incluso al propio formulario, esto lo haremos de dos formas: usando el diseador de Windows Forms, ya que esta ser la manera ms habitual de hacerlo, y la otra escribiendo el cdigo directamente, ya que as empezaremos a adentrarnos en este mundo de los eventos y delegados, con idea de que lo que expliquemos a continuacin sea ms fcil de comprender. Asociar un evento con un control Cuando trabajamos con el diseador de formularios, tenemos dos formas de asociar un evento con el cdigo. La primera es haciendo doble pulsacin sobre el control en cuestin, de esta forma se crear un mtodo que asociar el evento que est predeterminado para ese control. Por ejemplo, si hacemos doble-click en un botn, el evento predeterminado es el evento Click. Si esa misma accin la realizamos en una caja de textos, el evento predeterminado es TextChanged. Si queremos asociar otros eventos con esos controles, tenemos que recurrir a la ventana de propiedades del diseador de Windows Forms, que tal como podemos comprobar en la figura 2.12, nos muestra, entre otras cosas, un rayo el cual nos servir para poder indicar los diferentes eventos que queramos interceptar en el control que actualmente tengamos seleccionado en el formulario.

Figura 2.12. Ventana de propiedades En la figura 2.13 podemos ver algunos de los eventos que tenemos disponibles para un control TextBox.

Figura 2.13. Los eventos disponibles de un TextBox

Saul Mamani Mamani

Desarrollo Agil de Software

68

Para crear un evento, simplemente haremos doble-click en el que nos interesa y el diseador de formularios nos mostrar el mtodo en el que podemos escribir nuestro cdigo para cuando se produzca ese evento:
private void textBox1_TextChanged(object sender, EventArgs e) { }

Pero adems de eso, el diseador de formularios de Visual Studio 2005 tambin ha aadido una lnea de cdigo que es la que realmente hace que se asocie un mtodo con un evento, en el caso del evento TextChanged del control textBox1 que tenemos en el formulario, el cdigo sera el siguiente:
this.textBox1.TextChanged += new System.EventHandler(this.textBox1_TextChanged);

Como vemos en los dos trozos de cdigo, hay una relacin entre el mtodo que utilizaremos cuando se produzca el evento, el cual utiliza dos parmetros, que es la forma habitual de hacerlo en .NET. El primer parmetro es de tipo object y representa al control que ha producido el evento, el segundo parmetro es de una clase derivada de EventHandler que en algunos casos nos puede proporcionar informacin extra sobre el evento que se ha producido. Para asociar el evento con ese mtodo, en C# se utiliza el operador += seguido de la creacin de un objeto al que se le pasa como argumento el nombre del procedimiento a usar para interceptar el evento. La definicin de la "clase" EventHandler que podemos encontrar en la ayuda de Visual C# 2005, es la siguiente:
public delegate void EventHandler(object sender, EventArgs e);

Como podemos apreciar realmente no es una clase normal, sino que es un "delegado". Y como tambin podemos ver, en ningn sitio se indica que reciba un solo parmetro. Aunque de esto nos ocuparemos ms adelante, por ahora solo debemos saber que en C# los eventos siempre estn ligados a los delegados. Si seguimos buscando en la ayuda de Visual C# 2005, nos encontramos con esta definicin del evento TextChanged:
public event EventHandler TextChanged;

Es decir, el evento TextChanged es del tipo del delegado EventHandler. Para mantener un poco el suspense, dejemos aqu las explicaciones. Por ahora, simplemente debemos saber que para asociar un evento con un mtodo, debemos usar el operador += para asignar el "manejador" de eventos al evento en cuestin, y lo que asignamos es un nuevo objeto basado en un tipo declarado como delegado al que le pasamos el nombre del procedimiento que se usar cuando se produzca el evento. Asociar varios eventos a un mismo procedimiento Algunas veces nos encontraremos en la situacin de que estamos utilizando el mismo cdigo para interceptar el mismo tipo de evento en controles diferentes, en esos casos nos puede interesar utilizar un solo mtodo, en el que haremos el trabajo. Asociarlos usando el diseador de Windows Forms La forma ms fcil de asociar un mismo mtodo con varios controles es hacerlo usando la ventana de propiedades, ver figura 2.13, en esa ventana de propiedades, adems de crear nuevos mtodos donde interceptar los eventos, podemos asociar un evento con un mtodo ya existente. Por ejemplo, si tenemos varios controles del tipo TextBox en los que nos interesa utilizar un solo mtodo para seleccionar

Saul Mamani Mamani

Desarrollo Agil de Software

69

todo el texto cuando el control recibe el foco, podramos escribir este cdigo que inicialmente estara asociado con el evento Enter del control textBox1:
private void textBox1_Enter(object sender, EventArgs e) { ((TextBox)sender).SelectAll(); }

Lo que tenemos que hacer es lo siguiente: -Seleccionamos el control al que queremos asociar ese mismo evento. -En la ventana de propiedades, (con la opcin de eventos seleccionada), buscamos el evento Enter, y en lugar de hacer doble pulsacin, (porque si lo hacemos creara un nuevo procedimiento), lo que tenemos que hacer es presionar el combo que es lo que se usa para indicar el mtodo usado, de la lista desplegable que se muestra, (ver figura 2.14), seleccionamos el mtodo que nos interese. En nuestro ejemplo, seleccionaramos textBox1_Enter.

Figura 2.14. Asociar un evento a un mtodo existente Como podemos apreciar en la figura 2.14, en la lista desplegable se muestran tres mtodos, esto es as porque todos ellos tienen los parmetros adecuados al tipo de evento que hemos seleccionado. Asociarlos usando cdigo Si en lugar de asociar un mismo mtodo usando el diseador de Windows Forms, queremos hacerlo por cdigo, lo podemos aadir en el constructor del formulario despus de la llamada al mtodo InitializeComponent(). El IDE de Visual C# 2005 nos ayuda a la hora de crear el manejador de eventos, tal como podemos ver en la figura 2.15:

Figura 2.15. Ayuda del IDE de C# para crear manualmente manejadores de eventos Si presionamos la tecla TAB se aadir automticamente el cdigo necesario para "ligar" ese evento con el mtodo que el IDE de C# "cree" que debe ser, en este caso, sera textBox2_Enter, pero una vez que el IDE ha aadido el cdigo podemos cambiarlo para que apunte al que realmente nos interesa.

Saul Mamani Mamani

Desarrollo Agil de Software

70

En caso de que ese evento ya est asociado con otro mtodo, el IDE nos avisar de ese hecho.

2.

Delegados

Tal como hemos comentado, los eventos y los delegados estn muy unidos en C#. De hecho no se pueden definir eventos si no definimos previamente un delegado; ya que por medio de ese delegado podremos crear o asignar el mtodo que se encargar de interceptar el evento. Por esa razn veremos primero que son los delegados para de esta forma comprender mejor todo lo que tenemos que hacer para definir eventos en nuestras clases y desde los objetos creados a partir de ellas asociarlos a un mtodo. Definicin "formal" de delegado Veamos algunas de las definiciones de la documentacin de Visual Studio sobre los delegados: "Los delegados habilitan escenarios que en otros lenguajes se han resuelto con punteros a funcin. No obstante, a diferencia de los punteros a funcin, los delegados estn orientados a objetos y proporcionan seguridad de tipos." "Un delegado es una clase que puede contener una referencia a un mtodo. A diferencia de otras clases, los delegados tienen un prototipo (firma) y pueden guardar referencias nicamente a los mtodos que coinciden con su prototipo." De estas dos definiciones podemos sacar en claro que los delegados son clases especiales que pueden tener referencias a un mtodo, y que ese mtodo debe cumplir con el "prototipo" definido por el delegado. Y que esa referencia que contienen es como los punteros de otros lenguajes, pero que estn enfocados a ser utilizados desde el punto de vista de .NET, es decir, a ser usados desde una perspectiva orientada a objetos. Por tanto, cuando definimos un delegado, estamos definiendo la firma que debe tener una funcin (o mtodo), con idea de que podamos crear un "puntero" a dicha funcin, pero de una forma "controlada" por el CLR de .NET. Por tanto, solo se admitirn "punteros" a funciones que concuerden con la definicin que ha hecho el delegado. Si retomamos la definicin del delegado System.EventHandler definido por .NET para los eventos, veremos que ese delegado realmente est definiendo la "forma" que debemos declarar el mtodo que intercepte un evento basado en ese delegado. Analicemos con algo de detalle la definicin tanto del delegado EventHandler como del evento TextChanged. El delegado est definido de la siguiente forma:
public delegate void EventHandler(object sender, EventArgs e);

Si desechamos public delegate, nos quedamos con una definicin de un mtodo que bien podramos incluir en una interfaz, ya que lo que hace es indicar que ese mtodo debe ser de tipo void (no devuelve ningn valor), y que tiene dos parmetros, el primero de tipo object y el segundo de tipo EventArgs. Ahora veamos la definicin del evento TextChanged:
public event EventHandler TextChanged;

Si en esta ocasin tambin obviamos las dos primeras instrucciones, tendremos una declaracin "tpica" de C#, en la que el tipo de datos es EventHandler (el delegado, que al fin y al cabo es una clase de tipo especial), seguida de "la variable" que

Saul Mamani Mamani

Desarrollo Agil de Software

71

define el evento. Lo que notamos aqu es que no aparece por ningn lado los parmetros que hay que usar, y esa es una de las caractersticas de los delegados, y posiblemente lo que complica ms su entendimiento. Veamos ahora cmo asignamos el mtodo al evento del control:
this.textBox1.TextChanged += new System.EventHandler(this.textBox1_TextChanged);

El evento lo conectamos por medio del constructor del delegado, el cual espera como parmetro un mtodo, y lo que le pasamos es "un puntero al mtodo", es decir, le indicamos dnde est ese mtodo. Como hemos comentado antes, ese mtodo debe cumplir con las especificaciones indicadas por el delegado, cosa que podemos comprobar si vemos el cdigo del mtodo que se utilizar cuando el evento se produzca:
private void textBox1_TextChanged(object sender, EventArgs e) {...}

Y tal como podemos ver, el mtodo cumple a la perfeccin con la definicin del delegado. Utilizar un delegado para acceder a un mtodo Ahora veamos brevemente cmo usar los delegados, en este caso sin necesidad de que defina un evento. Como hemos comentado, un delegado realmente es una clase que puede contener una referencia a un mtodo, adems define el prototipo del mtodo que podemos usar como referencia. Sabiendo esto, podemos declarar una variable del tipo del delegado y por medio de esa variable acceder al mtodo que indiquemos, siempre que ese mtodo tenga la misma "firma" que el delegado. Parece complicado verdad? Y no slo lo parece, es que realmente lo es. Comprobemos esta "complicacin" por medio de un ejemplo. En este cdigo, que iremos mostrando poco a poco, vamos a definir un delegado, un mtodo con la misma firma para que podamos usarlo desde una variable definida con el mismo tipo del delegado. Definimos un delegado de tipo void que recibe un valor de tipo cadena:
delegate void Saludo(string nombre);

Definimos un mtodo con la misma firma del delegado, el cual devolver Hola, seguido del contenido del parmetro:
static private void mostrarSaludo(string elNombre) { Console.WriteLine("Hola, " + elNombre); }

Ahora vamos a declarar una variable para que acceda a ese mtodo. Para ello debemos declararla con el mismo tipo del delegado:
Saludo saludando;

La variable saludando es del mismo tipo que el delegado Saludo. La cuestin es cmo o que asignamos a esta variable? Primer intento: Como hemos comentado, los delegados realmente son clases, por tanto podemos usar new Saludo y, segn parece, deberamos pasarle un nombre como argumento. Algo as:

Saul Mamani Mamani

Desarrollo Agil de Software

72

saludando = new Saludo("Pepe");

Pero esto no funciona, entre otras cosas, porque hemos comentado que un delegado contiene (o puede contener) una referencia a un mtodo, y "Pepe" no es un mtodo ni una referencia a un mtodo. Segundo intento: Por lgica y, sobre todo, por sentido comn, mxime cuando hemos declarado un mtodo con la misma "firma" que el delegado, deberamos pensar que lo que debemos pasar a esa variable es el mtodo, ya que un delegado puede contener una referencia a un mtodo, por tanto podemos pensar que si creamos un nuevo objeto del tipo Saludo pasndole el nombre del mtodo y el argumento...
saludando = new Saludo(mostrarSaludo("Pepe"));

Tampoco funciona, porque nos Probemos entonces a indicarle slo el nombre del mtodo:
saludando = new Saludo(mostrarSaludo);

dice

que

espera

un

mtodo.

Ahora s, al menos no da error de compilacin, aunque, no muestra nada, cosa que es lgica porque en ningn sitio le hemos indicado la cadena que tiene que usar para el saludo. Cmo podemos hacer que se muestre el saludo? A ver, recapacitemos, tenemos una variable (saludando), que es del tipo del delegado (Saludo), y que "apunta" a un mtodo (mostrarSaludo), que espera que le pasemos como argumento una cadena. Podremos hacer esto?
saludando("Pepe");

Pues s! Ahora muestra el saludo a "Pepe". En realidad, cuando estamos usando la lnea anterior, es como si estuvisemos llamando al mtodo de esta forma:
mostrarSaludo("Pepe");

Qu forma de complicarnos la vida, verdad? Pero debemos pensar, que en este contexto funciona la llamada directa al mtodo porque est definido en la misma clase que la variable declarada como el delegado. Qu pasa si esa variable del tipo del delegado lo creamos fuera de nuestro proyecto? Que podr llamar al mtodo sin ningn tipo de problemas, ya que el delegado le asignar un puntero a ese mtodo indicndole en que direccin de la memoria est, y como es de suponer, ese mtodo ser pblico, no habr problemas de que pueda acceder a l, por tanto, podemos acceder a mtodos que no estn en nuestro propio proyecto. Si lo comparamos con los eventos de los controles, esos mtodos que se ejecutarn cuando se produzca un evento estn en nuestro proyecto, pero el evento que se produce, lo hace en un ensamblado externo a nuestra aplicacin. Podramos seguir hablando de los delegados unas cuantas secciones ms, pero estaramos desvindonos del camino que nos ha llevado hasta ellos: los eventos; ya que en C# no se puede comprender cmo funcionan los eventos si no sabemos, aunque sea, lo bsico de los delegados.

Saul Mamani Mamani

Desarrollo Agil de Software

73

Los delegados y los mtodos annimos Una nueva caracterstica de Visual C# 2005 es la posibilidad de usar lo que se conoce como mtodos annimos en los sitios en los que se espera un delegado. De esta forma, podemos usar mtodos annimos en lugar de mtodos especficos para usar con los eventos. Aunque el uso de los mtodos annimos no solo estn limitados a los eventos, ya que, como acabamos de comentar, se pueden usar donde se espera un delegado. Usando el mismo delegado del ejemplo anterior, podramos crear un mtodo annimo que estuviese asociado con la variable declarada con el tipo del delegado, pero en lugar de indicar el nombre de un mtodo existente, podemos escribir el cdigo que se usar cuando se utilice el delegado. Vemoslo con un ejemplo:
// Usando mtodos annimos Saludo saludoAnonimo = delegate (string nombre) { Console.WriteLine("Que tal {0}?", nombre); }; saludoAnonimo("Antonio");

Para definir el mtodo annimo, asignamos a la variable que utilizaremos para invocar al delegado, un delegado, pero usando la propia instruccin delegate, en caso de que la declaracin del delegado haya definido parmetros, tambin tendremos que declararlos, ya que al fin y al cabo lo que aqu hacemos es crear una especie de mtodo "virtual" que el CLR de .NET guardar en la memoria y que tendr la misma firma propuesta por el delegado. Por ltimo dentro de un par de llaves escribimos el cdigo que se usar en ese mtodo annimo. Al igual que el resto de mtodos "normales", cualquier variable declarada dentro del par de llaves ser solo visible dentro del mtodo virtual, y lo mismo ocurre con los parmetros que tenga. De la misma forma, podemos usar variables externas al mtodo annimo, ya que, como hemos comentado, el comportamiento de estos mtodos es el mismo que los declarados de la forma habitual. Para llamar o usar el delegado, lo haremos de la misma forma que con el resto de delegados, lo nico que cambia es la forma de definir el mtodo que se usar al invocarlo. Seguramente donde puede tener ms utilidad este tipo de mtodos es en los eventos de los controles de los formularios de Windows, como en este ejemplo:
this.button1.Click += delegate { MessageBox.Show("Has presionado el botn!"); };

Cmo producir eventos As es como funcionan los eventos, un evento solo tiene la direccin de memoria de un mtodo, ese mtodo recibe los mismos parmetros que los definidos por el evento, (realmente por el delegado), cuando se produce el evento, es como si llamramos a cada uno de los mtodos que se han ido agregando al delegado, si es que se ha agregado alguno, ya que en caso de que no haya ningn mtodo asociado a ese evento, ste no se producir, por la sencilla razn de que no habr ningn cdigo al que llamar. Cuando queremos "lanzar" un evento lo haremos de la forma descrita anteriormente, como si estuvisemos llamando a un mtodo. Pero debido a que es posible que nadie est "escuchando" o esperando a que se produzca el evento, siempre debemos tener la precaucin de comprobar si el evento que hemos definido tiene algn mtodo asociado. Nota: Algunas veces es complicado explicar lo que, en principio, es sencillo; el problema viene cuando hay que explicar cosas

Saul Mamani Mamani

Desarrollo Agil de Software

74

distintas pero que estn totalmente relacionadas y, tanto la una como la otra es importante conocerla para un buen entendimiento, por esta razn, pido al lector que tenga paciencia y se deje llevar, al final todo estar tan claro como el agua clara. Clarifiquemos un poco todo esto. Cuando queremos interceptar un evento, lo que hacemos es "incrementar" ese evento con un nuevo objeto devuelto por un delegado, tal como vimos en el cdigo del evento TextChanged:
this.textBox1.TextChanged += new System.EventHandler(this.textBox1_TextChanged);

El operador += aade el mtodo indicado como argumento del constructor del delegado al evento del objeto textBox1. Cuando el cdigo de la clase TextBox decide que debe "lanzar" el evento utilizar un cdigo como este:
TextChanged(this, EventArgs.Empty);

Es decir, utilizar la variable declarada con event para producir el evento. Pero como hemos comentado antes, es posible que ese evento no se est interceptando, si ese es el caso, esa lnea producira una excepcin, ya que no est inicializado el evento, (ahora veremos cmo funciona todo esto), as que lo ms adecuando es comprobar si no es un valor nulo y en ese caso, hacer la llamada a los mtodos que haya esperando el evento:
if( TextChanged != null ) TextChanged(this, EventArgs.Empty);

Qu ocurre cuando se asigna y se produce un evento? Intentemos ver de forma sencilla lo que ocurre "por dentro" cada vez que definimos un mtodo que intercepta un evento y cmo hace Visual C# 2005 para comunicarse con el receptor de dicho evento. 1. Cuando C# se encuentra con el cdigo que le indica que un mtodo debe interceptar un evento, (mediante el operador +=), lo que hace es aadir la direccin de memoria de ese mtodo a una especie de array. En la figura 2.16 podemos ver un diagrama en el que un mismo evento lo interceptan tres clientes, cuando decimos que un cliente intercepta un evento, nos referimos a que hay un mtodo que lo intercepta y el evento realmente guarda la direccin de memoria de ese mtodo.

Figura 2.16. El evento guarda la direccin de memoria de cada mtodo que lo intercepta

Saul Mamani Mamani

Desarrollo Agil de Software

75

2. Cuando desde la clase producimos el evento, se examina esa lista de direcciones y se manda el mensaje a cada uno de los mtodos que tenemos en el "array". En este caso, lo que realmente ocurre es que se hace una llamada a cada uno de los mtodos, de forma que se ejecute el cdigo al que tenemos acceso mediante la direccin de memoria almacenada en la lista. Como podemos comprobar en la figura 2.16, un mismo evento puede estar interceptado ms de una vez, incluso por el mismo mtodo, no es que tenga mucha utilidad recibir ms de una vez el aviso de que ha ocurrido algo, pero se puede hacer, incluso por error!

3.

Definir y producir eventos en una clase

Una vez que tenemos unas nociones de que son los eventos y qu relacin tienen con los delegados, es hora de que veamos de forma prctica cmo definirlos en nuestras clases y qu es lo que tenemos que hacer para que una aplicacin lo intercepte, sin olvidarnos de cmo y cundo lo podemos producir. Tambin nos ocuparemos de ver cmo eliminar un mtodo de la lista de mtodos que deben ser notificados por el evento. Definir eventos en una clase Para definir un evento en una clase, lo primero que debemos hacer es definir un delegado con la "firma" que tendr el mtodo utilizado para interceptar el evento. Ese delegado debe estar definido con un mbito adecuado para que puede ser utilizado por los clientes que los vayan a utilizar. Teniendo en cuenta que en el fondo un delegado es una clase, el delegado (o los delegados) deben estar declarados en el mismo nivel que nuestra clase, de esa forma nos aseguramos que est accesible. Una vez definido el delegado, tenemos que definir el evento en nuestra clase. La forma definirlo es usando la instruccin event seguida del nombre del delegado y a continuacin el nombre que queramos darle. Para este ejemplo, vamos a usar una clase llamada Empleado, que tendr dos propiedades de lectura/escritura: Nombre y Apellidos, una propiedad de solo lectura: Salario, (el valor de esta propiedad lo indicaremos en el constructor de la clase), un mtodo que reemplaza al mtodo ToString de la clase object, el cual utilizaremos para mostrar los datos de la clase; adems, tendremos un evento llamado DatosModificados, el cual se producir cada vez que se modifique, (realmente cuando se asigne), las dos propiedades que tienen acceso de escritura. Este evento no tendr ningn parmetro. Empecemos declarando el delegado, que como hemos dicho no tendr ningn parmetro, y al utilizarse para recibir eventos, ser de tipo void.
public delegate void DatosCambiadosEventHandler();

Nota: Segn las recomendaciones de nomenclatura de .NET Framework, los nombres de los delegados deben terminar con EventHandler. El evento lo declararemos de la siguiente forma:
public event DatosCambiadosEventHandler DatosCambiados;

Producir un evento en nuestra clase Para producir un evento en nuestra clase, y de esta forma notificar a quin quiera interceptarlo, utilizaremos el nombre con el que hemos declarado el evento, pero debemos recordar lo que comentamos anteriormente: tenemos que comprobar si algn mtodo ser notificado, es decir, si se ha aadido a ese evento una direccin de memoria gestionada por el delegado.

Saul Mamani Mamani

Desarrollo Agil de Software

76

En caso de que no lo hagamos, (y no haya ningn mtodo esperando ser notificado), recibiremos una excepcin del tipo NullReferenceException, y como podemos comprobar en la figura 2.17, el IDE de Visual Studio 2005 nos indica lo que debemos hacer:

Figura 2.17. Error al producir un evento que no est interceptado Nota: De forma interna y totalmente transparente para nosotros, el compilador de C# crea un objeto, (el cual inicialmente tiene un valor nulo), que ser el encargado de gestionar los clientes que interceptan los eventos. Para lograrlo, define dos mtodos pblicos llamados add_NombreDelEvento y remove_NombreDelEvento que son los que se utilizan cuando aadimos nuevos mtodos a la lista de mtodos a notificar y cuando los quitamos de dicha lista. Para que no se produzca ese error, lo que debemos hacer es comprobar si el "evento" contiene un valor nulo, y en caso de que no lo tenga lanzamos el evento:
if (DatosCambiados != null) DatosCambiados();

Nota: Segn las recomendaciones de .NET Framework sobre los eventos, para lanzar un evento en una clase deberamos declarar un mtodo protegido llamado OnNombreDelEvento, al que llamaremos cuando tengamos que lanzarlo. Y si alguna clase se deriva de la nuestra y sobrescribe dicho mtodo para adaptarlo a sus necesidades, debera llamar a ese mtodo de la clase base para que los delegados que existan sigan notificando dicho evento. Asociar los eventos de una clase con un mtodo Tal como hemos estado comentando, la forma que tiene C# de asociar eventos con los mtodos que lo interceptarn es usando el operador += para asignar al evento el delegado, el cual, como ya sabemos recibe un parmetro que es el mtodo que recibir la notificacin de que dicho evento se ha producido. En la aplicacin de ejemplo, declaramos una variable del tipo Empleado y llamamos al constructor en el que le indicamos los datos que debe asignar a cada una de las propiedades:
Empleado emp; emp = new Empleado("pepe", "Ruiz", 100);

Antes de asociar el evento con un mtodo, debemos declararlo con la misma firma que ha definido el delegado:
static void emp_DatosCambiados() { Console.WriteLine("Datos cambiados en el Empleado"); }

Saul Mamani Mamani

Desarrollo Agil de Software

77

En este caso, es un mtodo de tipo void que no recibe ningn argumento. Por ltimo tendremos que "ligar" el evento con el mtodo, esto lo hacemos de la siguiente forma:
emp.DatosCambiados += new DatosCambiadosEventHandler(emp_DatosCambiados);

Si ejecutamos el siguiente cdigo:


Console.WriteLine(emp.ToString()); emp.Nombre = "Jose"; Console.WriteLine(emp.ToString());

Obtendremos la siguiente salida en la consola:


Ruiz, pepe Datos cambiados en el Empleado Ruiz, Jose

Ya que al asignarle un valor a la propiedad es cuando se lanza el evento. Veamos el cdigo completo de la clase Empleado, para ver cundo lanzamos los eventos:
public class Empleado { private string nombre; private string apellidos; private decimal salario; public event DatosCambiadosEventHandler DatosCambiados; public Empleado(string nombre, string apellidos, decimal salario) { this.nombre = nombre; this.apellidos = apellidos; this.salario = salario; } public string Nombre { get{ return nombre; } set { nombre = value; if (DatosCambiados != null) DatosCambiados(); } } public string Apellidos { get{ return apellidos; } set { apellidos = value; if (DatosCambiados != null) DatosCambiados(); } } public decimal Salario { get{ return salario; } } public override string ToString() { return Apellidos + ", " + Nombre; }

Saul Mamani Mamani

Desarrollo Agil de Software

78

Desasociar eventos Cuando no queramos que un evento sea interceptado, podemos eliminar el delegado que previamente hemos asociado. Esto lo haremos de la misma forma que cuando asociamos un mtodo a un evento, pero en lugar de utilizar el operador +=, tendremos que usar el operador -=. En el siguiente cdigo "desligamos" el mtodo del evento, de forma que a partir de esa asignacin, dejar de recibir notificaciones:
emp.DatosCambiados -= new DatosCambiadosEventHandler(emp_DatosCambiados);

Por tanto, si despus de esa lnea aadimos este cdigo:


Console.WriteLine(emp.ToString()); emp.Nombre = "Antonio"; Console.WriteLine(emp.ToString());

No se mostrar la lnea que indica que los datos del empleado han cambiado. Detectar cundo se aade o se elimina un delegado de un evento Si queremos detectar cundo se aade un delegado a un evento o cundo se elimina, podemos utilizar una de las nuevas caractersticas de Visual C# 2005: propiedades de eventos o eventos de propiedades. La forma de hacerlo es algo ms complicado que lo que hemos visto, pero realmente no es tan distinto. Esta caracterstica la podemos usar cuando de alguna forma nos interese tener una relacin de los mtodos que interceptan nuestros eventos o bien para solventar ciertos conflictos que se pueden presentar cuando una clase implementa ms de una interfaz en las que se ha definido un evento que tiene el mismo nombre en las interfaces, y el delegado usado en cada uno de ellos sea diferente. Si aplicamos esta nueva forma de definir un evento en nuestra clase Empleado, debemos eliminar la declaracin que tenamos del evento y utilizar esta otra:
// La variable privada ser la que se usar // en esta clase para lanzar los eventos. private DatosCambiadosEventHandler datosCambiados; // El evento declarado al estilo de una propiedad. public event DatosCambiadosEventHandler DatosCambiados { add { datosCambiados += value; Console.WriteLine("El mtodo a notificar es: {0}", value.Method.Name); Console.WriteLine("Se aade un delegado al evento DatosCambiados"); } remove { datosCambiados -= value; Console.WriteLine("Se elimina un delegado al evento DatosCambiados"); } }

Lo primero que hacemos es declarar una variable privada del mismo tipo que el delegado, esa variable ser la que se use, como solemos hacer con cualquier propiedad, para almacenar "el valor interno de la propiedad", aunque en este caso sea una propiedad "especial". Despus declaramos el evento-propiedad, que como vemos se hace de la misma forma que cuando declaramos un evento, con la diferencia de que en lugar de terminarlo con un punto y coma, utilizamos un bloque de cdigo, en el que definimos otros dos bloques, pero en lugar de utilizar los tpicos get y set de las propiedades normales, aadiremos un bloque para cuando se aada el delegado (add) y otro para cuando se elimine (remove). Esos dos bloque

Saul Mamani Mamani

Desarrollo Agil de Software

79

reciben un parmetro implcito (value), el cual representa al delegado que se quiere aadir o quitar, por tanto debemos realizar esas mismas operaciones dentro de cada bloque, utilizando los operadores += y -=. Como podemos comprobar en el listado anterior, tambin podemos aadir ms cdigo a cada bloque, incluso podemos obtener informacin del delegado usado para asociar el evento con un mtodo. Para producir este evento, en lugar de usar el declarado como propiedad, tendremos que usar la variable privada, el uso es exactamente el mismo que vimos anteriormente:
nombre = value; if (datosCambiados != null) datosCambiados();

Aunque parezca que es el mismo cdigo de antes, debemos recordar que C# hace distincin entre maysculas y minsculas. En el programa que utiliza la clase Empleado no tenemos que hacer ningn cambio, ya que la forma que implementemos nuestro evento no afecta fuera de la clase.

LECCION 5: ATRIBUTOS.
Introduccin sta es la definicin que nos da la ayuda de C# sobre lo que es un atributo. "Los atributos son etiquetas descriptivas que proporcionan informacin adicional sobre elementos de programacin como tipos, campos, mtodos y propiedades. Otras aplicaciones, como el compilador de Visual Basic, pueden hacer referencia a la informacin adicional en atributos para determinar cmo pueden utilizarse estos elementos." En esta leccin veremos algunos ejemplos de cmo usarlos en nuestras propias aplicaciones y, aunque sea de forma general, cmo usar y aplicar algunos de los atributos definidos en el propio .NET Framework, al menos los que ms directamente nos pueden interesar a los desarrolladores de C#. Atributos

Atributos o Atributos para representar informacin de nuestra aplicacin Mostrar los ficheros ocultos del proyecto o Tipos de atributos que podemos usar en una aplicacin Atributos globales a la aplicacin Atributos particulares a las clases o miembros de las clases o Atributos personalizados Acceder a los atributos personalizados en tiempo de ejecucin o Marcar ciertos miembros de una clase como obsoletos

1.

Atributos

Como hemos comentado en la introduccin, los atributos son etiquetas que podemos aplicar a nuestro cdigo para que el compilador y, por extensin, el propio .NET Framework los pueda usar para realizar ciertas tareas o para obtener informacin extra sobre nuestro cdigo.

Saul Mamani Mamani

Desarrollo Agil de Software

80

De hecho en cualquier aplicacin que creemos con Visual C# 2005 estaremos trabajando con atributos, aunque nosotros ni nos enteremos, ya que el propio compilador los utiliza para generar los metadatos del ensamblado, es decir, la informacin sobre todo lo que contiene el ejecutable o librera que hemos creado con Visual C# 2005. Por otra parte, el uso de los atributos nos sirve para ofrecer cierta funcionalidad extra a nuestro cdigo, por ejemplo, cuando creamos nuestros propios controles, mediante atributos podemos indicarle al diseador de formularios si debe mostrar ciertos miembros del control en la ventana de propiedades, etc. Atributos para representar informacin de nuestra aplicacin De forma ms genrica podemos usar los atributos para indicar ciertas caractersticas de nuestra aplicacin, por ejemplo, el ttulo, la versin, etc. Todos estos atributos los indicaremos como "caractersticas" de nuestra aplicacin, y lo haremos sin ser demasiados conscientes de que realmente estamos usando atributos, ya que el propio C# los controla mediante propiedades de la aplicacin. Por ejemplo, si mostramos la ventana de propiedades de nuestro proyecto, ver figura 2.18:

Figura 2.18. Propiedades de la aplicacin Tendremos acceso a las propiedades de la aplicacin, como el nombre del ensamblado, el espacio de nombres, etc. Si queremos agregar informacin extra, como la versin, el copyright, etc. podemos presionar el botn Assembly Information, al hacerlo, se mostrar una nueva ventana en la que podemos escribir esa informacin, tal como mostramos en la figura 2.19:

Saul Mamani Mamani

Desarrollo Agil de Software

81

Figura 2.19. Informacin del ensamblado Esa informacin realmente est definida en un fichero del proyecto llamado AssemblyInfo.cs, el cual de forma predeterminada est oculto, si lo mostramos, veremos que esa informacin la contiene en formato de atributos. Parte del cdigo de ese fichero lo podemos ver en la figura 2.20:

Figura 2.20. Contenido del fichero AssemblyInfo

Saul Mamani Mamani

Desarrollo Agil de Software

82

En este cdigo podemos resaltar tres cosas: La primera es que tenemos una importacin al espacio de nombres System.Reflection, este espacio de nombres contiene la definicin de las clases/atributos utilizados para indicar los atributos de la aplicacin, como el ttulo, etc. La segunda es la forma de usar los atributos, estos deben ir encerrados entre signos de corchetes: [assembly: ComVisible(false)]. La tercera es que, en este caso, los atributos estn definidos a nivel de ensamblado, para ellos se aade la instruccin assembly: al atributo. Como veremos a continuacin, los atributos tambin pueden definirse a nivel local, es decir, solo aplicable al elemento en el que se utiliza, por ejemplo, una clase o un mtodo, etc. Mostrar los ficheros ocultos del proyecto Como acabamos de comentar, el fichero AssemblyInfo.cs que es el que contiene la informacin sobre la aplicacin (o ensamblado), est dentro de la carpeta Properties. Conforme vayamos trabajando con los proyectos de C# nos daremos cuenta que el IDE oculta algunos de esos ficheros con idea de poner a nuestro alcance, al menos de una forma ms o menos rpida, los que realmente debemos utilizar. En la figura 2.21 podemos ver la ventana del explorador de soluciones, desde esa misma ventana podremos acceder a todos los ficheros que contenga nuestra solucin. Adems de los ficheros "ocultados" dentro de carpetas, podemos hacer que se muestren todos los ficheros que tenemos en el directorio en el que hemos creado el proyecto, para mostrarlos tendremos que presionar el segundo botn de la barra de herramientas de esa ventana, al hacerlo tendremos a la vista todos los ficheros, tal como podemos comprobar en la figura 2.21:

Figura 2.21. Mostrar todos los ficheros de la solucin Tipos de atributos que podemos usar en una aplicacin Como hemos comentado, existen atributos que son globales a toda la aplicacin y otros que podremos aplicar a elementos particulares, como una clase o un mtodo. Atributos globales a la aplicacin Estos se indican usando assembly: en el atributo y los podremos usar en cualquier parte de nuestro cdigo, aunque lo habitual es usarlos en el fichero AssemblyInfo.cs. Nota: La palabra o instruccin assembly: lo que indica es que el atributo tiene un mbito de ensamblado.

Saul Mamani Mamani

Desarrollo Agil de Software

83

Atributos particulares a las clases o miembros de las clases Estos atributos slo se aplican a la clase o al miembro de la clase que creamos conveniente, el formato es parecido a los atributos globales, ya que se utilizan los corchetes para encerrarlo, con la diferencia de que en este tipo de atributos no debemos usar assembly:, ya que esta instruccin indica que el atributo es a nivel del ensamblado. Cuando aplicamos un atributo "particular", este debe estar junto al elemento al que se aplica, aunque si queremos darle mayor legibilidad al cdigo podemos ponerlo encima de dicho elemento:
[STAThread()] static void Main(string[] args)

Atributos personalizados Adems de los atributos que ya estn predefinidos en el propio .NET, podemos crear nuestros propios atributos, de forma que en tiempo de ejecucin podamos acceder a ellos mediante las clases del espacio de nombres Reflection, aunque debido a que este tema se sale un poco de la intencin de este curso, simplemente indicar que los atributos personalizados son clases que se derivan de la clase System.Attribute y que podemos definir las propiedades que creamos conveniente utilizar en ese atributo para indicar cierta informacin a la que podemos acceder en tiempo de ejecucin. En el siguiente cdigo tenemos la declaracin de una clase que se utilizar como atributo personalizado, notamos que, por definicin las clases para usarlas como atributos deben terminar con la palabra Attribute despus del nombre "real" de la clase, que como veremos en el cdigo que utiliza ese atributo, esa "extensin" al nombre de la clase no se utiliza. Veamos primero el cdigo del atributo personalizado:
[AttributeUsage(AttributeTargets.All)] public class AutorAttribute : System.Attribute { private string modificadoPor; private string version; private string fecha; public string ModificadoPor { get{ return modificadoPor; } set{ modificadoPor = value; } } public string Version { get{ return version; } set{ version = value; } } public string Fecha { get{ return fecha; } set{ fecha = value; } } }

Para usar este atributo lo podemos hacer de la siguiente forma:


[Autor(ModificadoPor="Guillermo 'guille'", Version="1.0.0.0", Fecha="13/Abr/2005")] public class PruebaAtributos : IPruebaObsoleto {

Nota: Cuando utilizamos el atributo, en el constructor que de forma predeterminada crea C#, los parmetros se indican por el orden alfabtico de las propiedades, (ver figura 2.22), aunque esto es solo para facilitar la lectura, ya que siempre debemos

Saul Mamani Mamani

Desarrollo Agil de Software

84

indicarlos por medio de los nombres de los mismos, sin importar el orden, tal como podemos ver en el cdigo de ejemplo anterior.

Figura 2.22. El constructor de los atributos los muestra por orden alfabtico Acceder a los atributos personalizados en tiempo de ejecucin Para acceder a los atributos personalizados podemos hacer algo como esto, (suponiendo que tenemos la clase AutorAttribute y una clase llamada PruebaAtributos a la que hemos aplicado ese atributo personalizado):
static void Main(string[] args) { Type tipo; tipo = typeof(PruebaAtributos); Attribute[] atributos; atributos = Attribute.GetCustomAttributes(tipo); foreach (Attribute atr in atributos) { if (atr is AutorAttribute) { AutorAttribute aut; aut = (AutorAttribute)atr; Console.WriteLine("Modificado por: " + aut.ModificadoPor); Console.WriteLine("Fecha: " + aut.Fecha); Console.WriteLine("Versin: " + aut.Version); } } }

Marcar ciertos miembros de una clase como obsoletos En ocasiones nos encontraremos que escribimos cierto cdigo que posteriormente no queremos que se utilice, por ejemplo porque hemos creado una versin optimizada. Si ese cdigo lo hemos declarado en una interfaz, no deberamos eliminarlo de ella, ya que as romperamos el contrato contrado por las clases que implementan esa interfaz. En estos casos nos puede venir muy bien el uso del atributo [Obsolete], ya que as podemos informar al usuario de que ese mtodo no debera usarlo. En el constructor de este atributo podemos indicar la cadena que se mostrar al usuario. En el siguiente cdigo se declara un mtodo con el atributo Obsolete:
public interface IPruebaObsoleto { [Obsolete("Este mtodo ya est obsoleto, utiliza el mtodo Mostrar")] void MostrarNombre(); void Mostrar(); }

El nico problema que tiene este atributo al usarlo desde C# es que solo nos avisar cuando usemos un objeto del tipo de la interfaz o clase en el que lo hemos indicado, pero si esa interfaz se implementa en una clase, al usar el mtodo desde un objeto creado a partir de la clase, no nos avisar. Pero esto realmente no es importante, ya que lo ms habitual es que lo usemos en los mtodos de las clases que distribuyamos, aunque si el usuario utiliza un objeto del tipo de la interfaz, el compilador de C# generar una advertencia, y si estamos trabajando con el IDE de Visual Studio 2005, esa advertencia ser visible, tal como podemos ver en la figura 2.23:

Saul Mamani Mamani

Desarrollo Agil de Software

85

Figura 2.23. Mensaje de que un mtodo est obsoleto

MODULO III
DESARROLLO DE APLICACIONES WINDOWS Mdulo 3 - Desarrollo de aplicaciones Windows En este mdulo, conoceremos las partes generales y ms importantes del entorno de desarrollo rpido Visual Studio 2005 para la programacin de aplicaciones con este lenguaje de la familia .NET. Veremos las partes principales del entorno, tambin veremos como desarrollar nuestros propios controles Windows, aprenderemos a trabajar con imgenes y grficos con Visual C# y finalmente, conoceremos como desplegar nuestras aplicaciones desarrolladas en Visual Studio 2005. FAQ: Qu tipo de aplicaciones puedo desarrollar si estoy utilizando Visual C# 2005 Express? Podr desarrollar principalmente, Aplicaciones para Windows, Bibliotecas de Clases y Aplicaciones de Consola. Adems, podr aadir sus propias plantillas para obtener un mayor rendimiento en el desarrollo de aplicaciones. Si su inters es el desarrollo de aplicaciones Web, puede utilizar Visual Web Developer 2005 Express. Las partes que forman parte de este mdulo son las siguientes: Captulo 1 Uso del diseador de Visual Studio 2005 Captulo 2 Controles de Windows Forms Captulo 3 Desarrollo de controles Captulo 4 Trabajo con imgenes y grficos Captulo 5 Despliegue de aplicaciones

LECCION1: USO DEL DISEADOR DE VISUAL ESTUDIO 2005


Introduccin Cuando nos encontramos con Visual Studio 2005 por primera vez, saltan a la vista, algunos de los cambios ms importantes de este novedoso entorno de desarrollo de aplicaciones Windows. Para un desarrollador, familiarizarse con el entorno de Visual Studio 2005 es una tarea que no debe entraar una complejidad excesivamente grande. Como nos ocurre a todos los que nos encontramos delante de un nuevo entorno de trabajo, lo nico que se requiere es constancia y prctica, mucha prctica. Sin embargo, si usted es ya un desarrollador habitual de otros entornos de desarrollo, notar que sus avances van a ser significativos en muy poco tiempo. Nota: Si est utilizando Visual C# 2005 Express para seguir este curso debe saber que este entorno est especializado en desarrollar aplicaciones Windows con Visual C# 2005, aunque podr usar controles y libreras escritas en otros lenguajes de la plataforma .NET .
1. Cuadro de herramientas 2. Explorador de base de datos

Saul Mamani Mamani 3. Explorador de soluciones 4. Propiedades 5. Mens y barra de botones 6. Otras consideraciones

Desarrollo Agil de Software

86

1. Cuadro de herramientas
El cuadro o barra de herramientas de Visual Studio 2005, nos permite utilizar los distintos componentes que .NET Framework pone a nuestra disposicin, en Visual Studio 2005 tenemos una gran cantidad de controles dispuestos en diferentes categoras. En la figura 1 podemos ver la barra de herramientas de Visual Studio 2005.

Visual Studio 2005

Figura 1 El Cuadro de herramientas, lo localizar en la parte izquierda del entorno Visual Studio 2005.

Cuando iniciamos un nuevo proyecto con Visual Studio 2005, el cuadro de herramientas queda rellenado con los controles que podemos utilizar en el proyecto. Si abrimos un formulario Windows, los controles quedan habilitados para que los podamos insertar en el formulario Windows. En la figura 2 se muestra la barra de herramientas con los controles preparados para ser insertados en el formulario Windows.

2. Explorador de base de datos


Si ha sido lo suficientemente observador cuando se explicaban los detalles del cuadro o barra de herramientas, y ha prestado especial atencin a las figuras o a las ventanas del entorno de desarrollo de Visual Studio 2005 Express, quizs haya notado que en la parte izquierda adems de la solapa cuadro de herramientas, aparece otra solapa de nombre explorador de base de datos. Desde esta solapa, un programador puede acceder a diferentes recursos del sistema. El principal y ms importante recurso, es el que tiene que ver con las conexiones con bases de datos, ya sean Microsoft Access, Microsoft SQL Server o cualquier otra fuente de datos. En la figura 1 puede observar la solapa Explorador de base de datos extendida con parte de sus opciones.

Saul Mamani Mamani

Desarrollo Agil de Software

87

La solapa del Explorador de base de datos desplegada de Visual Studio 2005

Figura 1

Conectando con una base de datos Microsoft Access a travs de OLE DB Para muestra un botn, y dado el carcter prctico de este tutorial, aprender a crear una conexin con cualquier base de datos, en nuestro caso de ejemplo una base de datos Microsoft Access, para poder utilizarla fcilmente en nuestra aplicacin Windows. Haga clic sobre el botn representado por la siguiente imagen que se muestra en la figura 2. . En este instante, se abrir una nueva ventana como la

Ventana Agregar conexin, para establecer las propiedades de conexin con una fuente de datos

Figura 2

Saul Mamani Mamani

Desarrollo Agil de Software

88

Por defecto, la ventana Agregar conexin queda preparada para establecer una conexin con una fuente de datos de origen de datos OLE DB, por lo que si nuestra intencin es establecer una conexin con otra fuente de datos, entonces deberemos hacer clic sobre el botn Cambiar... que se indica en la figura 3.

Botn Cambiar... para seleccionar otro proveedor de origen de datos

Figura 3

De esta manera, podemos indicar el origen de acceso a datos que necesitamos para establecer la conexin con nuestra fuente de datos, y que en nuestro ejemplo, no ser un proveedor de SQL Server, por lo que el origen de datos OLE DB es vlido para nosotros. Una vez que hemos hecho clic sobre el botn Cambiar..., nos aseguramos por lo tanto, que nuestro origen de datos es base de datos de Microsoft Access (OLE DB), como se indica en la figura 4.

Ventana de seleccin del proveedor u origen de datos

Figura 4

FAQ: Puedo utilizar el proveedor OLE DB en lugar del proveedor de SQL Server para conectar con una base de datos SQL Server? Con OLE DB, puede acceder a fuentes de datos SQL Server u otras fuentes de datos como Microsoft Access, sin embargo, si utiliza SQL Server 7.0, SQL Server 2000 SQL Server 2005, se recomienda el uso del proveedor de SQL Server, que es un proveedor de acceso a datos nativo que aumenta el rendimiento de nuestras aplicaciones con SQL Server. Slo si utiliza una versin de SQL Server anterior a SQL Server 7.0, deber utilizar necesariamente el proveedor de acceso a datos OLE DB. Una vez que hemos seleccionado el proveedor de acceso a datos, nos centraremos en la opcin Nombre de archivo base de datos como se muestra en la figura 5.

Saul Mamani Mamani

Desarrollo Agil de Software

89

En este lugar indicaremos el fichero de base de datos con el que estableceremos la conexin

Figura 5 Para agregar el

fichero de base de datos a la conexin, presionaremos el botn Examinar... y seleccionaremos el fichero de base de datos de nuestro disco duro. De esta manera, la base de datos quedar indicada en la conexin y tan slo deberemos probar nuestra conexin pulsando el botn Probar conexin como se indica en la figura 6.

Es recomendable probar la conexin antes de agregarla al entorno

Figura 6

Si la prueba de conexin se ha realizado satisfactoriamente, recibiremos un mensaje en pantalla afirmativo como el que se indica en la figura 7.

Saul Mamani Mamani

Desarrollo Agil de Software

90

Prueba de la conexin realizada con xito

Figura 7

A tener en cuenta: En este ejemplo, la conexin con la base de datos Microsoft Access, no tiene ningn tipo de usuario y contrasea. Tenga en cuenta que en la parte identificada como Conexin con la base de datos, podramos indicar el usuario y contrasea si fuera necesario. En este punto, tan slo deberemos presionar el botn Aceptar para que la base de datos con la que hemos establecido la conexin, quede ahora insertada en la ventana del Explorador de base de datos como se muestra en la figura 8.

Base de datos Microsoft Access insertada en la ventana del Explorador de base de datos

Figura 8

Advertencia!: Tener demasiadas conexiones activas en el entorno o configuradas en l, puede incidir negativamente en el rendimiento de Visual Studio 2005 cuando se trabaja con l. Tenga configuradas solamente, aquellas conexiones que va a utilizar, o aquellas conexiones de uso ms habitual y frecuente.

Saul Mamani Mamani

Desarrollo Agil de Software

91

Toolbox de Visual Studio 2005 con controles Windows preparados para ser insertados en el formulario Windows

Figura 2

Nota: Para insertar un control en un formulario Windows, se requiere que el formulario Windows sobre el que deseamos insertar un control, est abierto. Una vez que est abierto, bastar con realizar una de las tres siguientes acciones para insertar un control al formulario: Hacer doble clic sobre un control del cuadro de herramientas Hacer clic sobre un control del cuadro de herramientas, y sin soltar el botn del mouse, arrastrarlo sobre el formulario Hacer clic sobre un control del cuadro de herramientas, y luego hacer clic sobre el formulario y arrastrar para marcar una zona que cubrir nuestro control y soltar el ratn El control quedar entonces insertado dentro del formulario.

3. Explorador de soluciones
El Explorador de soluciones lo podemos encontrar en la parte derecha de nuestro entorno de desarrollo. Una solucin se compone de proyectos y stos, de recursos y objetos. Por lo general, una solucin contendr un proyecto, pero podemos encontrarnos con ms de un proyecto dentro de una misma solucin. Sin embargo, estos conceptos son muy sencillos de comprender y controlar, y para nada debe hacernos pensar que esto es algo complejo que nos costar mucho tiempo dominar.

Saul Mamani Mamani

Desarrollo Agil de Software

92

En la figura 1, podemos observar el explorador de soluciones de Visual Studio 2005.

La opcin Explorador de soluciones desplegada en Visual Studio 2005

Figura 1

Si queremos aadir un nuevo formulario al proyecto, lo haremos presionando con el botn secundario en cualquier parte de la ventana del explorador de soluciones, pero si esa pulsacin la hacemos en alguno de los objetos que contiene el proyecto, no podremos hacerlo, ya que el IDE de Visual Studio 2005 muestra un men diferente segn el objeto presionado, por ejemplo si queremos aadir un nuevo proyecto, podemos hacerlo presionando con el botn secundario del mouse sobre la "solucin". Nota: Para abrir un recurso de la solucin, basta con situarnos en el recurso determinado, por ejemplo un formulario Windows de nombre Form1.cs y hacer doble clic sobre l. El recurso se abrir automticamente en Visual Studio 2005. Adems, en Visual Studio 2005 sabremos en todo momento sobre qu recurso estamos trabajando en un momento dado.

4. Propiedades
La ventana de propiedades la encontraremos en la parte derecha y ms abajo de la ventana Explorador de soluciones en nuestro entorno de desarrollo. Esta ventana nos permitir acceder a las propiedades de los objetos insertados en nuestros formularios Windows, como se muestra en la figura 1.

Saul Mamani Mamani

Desarrollo Agil de Software

93

Ventana de Propiedades de Visual Studio 2005

Figura 1

Para acceder a las propiedades de un determinado control, deberemos seleccionar el control en el formulario Windows y acudir a la ventana Propiedades, o bien, seleccionar el control en el formulario Windows y presionar la tecla F4.

5. Mens y barra de botones


Respecto a los mens y barra de botones, son muchas las opciones que tenemos disponibles, tal como podemos comprobar en la figura 1. Las barras de botones son configurables, adems de que podemos elegir las que queremos que se muestren de forma permanente en el entorno de desarrollo de Visual Studio 2005. Algunas de las barras de botones se mostrarn automticamente segn las tareas que estemos realizando, por ejemplo, cuando estamos en modo depuracin o diseando las tablas de una base de datos. Con el contenido de los mens ocurre lo mismo, segn el elemento que tengamos seleccionado se mostrarn ciertas opciones que sea relevantes para ese elemento del IDE de Visual Studio 2005.

Los mens y barras de botones de Visual Studio 2005

Figura 1

Saul Mamani Mamani

Desarrollo Agil de Software

94

Algunas de las opciones que tenemos en los mens tambin las podemos conseguir usando los mens contextuales (el mostrado al presionar con el botn secundario del mouse), y como es de esperar, tambin sern diferentes segn el elemento que hemos presionado. Por ejemplo, para configurar el proyecto actual, podemos elegir la opcin Propiedades del men Proyecto o bien presionar con el botn secundario del mouse sobre el proyecto mostrado en el Explorador de soluciones. Al seleccionar las propiedades del proyecto, tendremos una nueva ventana desde la que podemos configurar algunas de las caractersticas de nuestro proyecto. En la figura 2, tenemos esa ventana de propiedades del proyecto, en la que podemos apreciar que est dividida segn el tipo de configuracin que queremos realizar, en este caso concreto las opciones de generacin o compilacin del proyecto.

Propiedades del proyecto sobre la que se trabaja en Visual Studio 2005

Figura 2

Como vemos en la figura 2, existen sin embargo multitud de opciones y apartados diferentes relacionados todos ellos con nuestra solucin. Otro de los apartados destacables, es el apartado denominado Publicar. An as, ste es el corazn o parte fundamental que debemos controlar a la hora de desarrollar una aplicacin o a la hora de gestionar una solucin, porque dentro de esta ventana, se resume buena parte de los mens y barra de botones del entorno de Visual Studio 2005. De todos los modos, tendremos la oportunidad de ver ms adelante, algunos usos de algunas de las opciones de la barra de botones del entorno.

Saul Mamani Mamani

Desarrollo Agil de Software

95

6. Otras consideraciones
El desarrollador que haya utilizado previamente otros entornos de desarrollo distinto a los de la familia de Visual Studio .NET, encontrar muy interesantes algunos de los cambios incorporados en Visual Studio 2005. Al principio, quizs se encuentre un poco desorientado, pero rpidamente y gracias a su experiencia en otros entornos de desarrollo, se acostumbrar al cambio. Entre algunos de estos cambios, destacara los siguientes: En Visual Studio 2005, acceder a los objetos de nuestra aplicacin es mucho ms fcil. Dentro del entorno, observaremos que se van creando diferentes solapas que nos permite acceder y localizar los recursos con los que estamos trabajando de forma rpida. En la figura 1 podemos observar justamente esto que comento.

Solapas de los objetos abiertos en Visual Studio 2005

Figura 1

Visual C# 2005 permite, hacer un Stop & Go (editar y continuar), de nuestras aplicaciones, es decir, pausar la ejecucin de una aplicacin en modo depuracin y modificar los valores o propiedades que deseemos y continuar ejecutndola. Esta opcin que los programadores de Visual Basic 6 utilizan con mucha frecuencia en el desarrollo de sus aplicaciones, se ha mantenido en Visual C# 2005, pero no en Visual Studio .NET 2002 y Visual Studio .NET 2003. Si por alguna razn, debe trabajar con alguno de estos entornos, debe saber que esta opcin no est disponible para las versiones comentadas. Otra caracterstica que debemos conocer de nuestro entorno de desarrollo, es la capacidad de anclar o fijar una ventana de las comentadas anteriormente o de permitir que se haga visible cuando acercamos el puntero del mouse sobre ella. Esta opcin es la que puede verse en la figura 2.

Opcin de ocultar o mostrar la ventana seleccionada en Visual Studio 2005

Figura 2

Ntese que al presionar sobre el icono indicado en la figura 2, haremos que esta ventana quede fija en el entorno de desarrollo. Cuando pulsamos este icono, la ventana queda fija y queda representado por un icono como el que se muestra en la figura 3.

Icono para ocultar o mostrar la ventana seleccionada cuando se encuentra en modo anclado

Figura 3

Saul Mamani Mamani

Desarrollo Agil de Software

96

Algo que oculta el entorno de Visual Studio 2005 por defecto, son las denominadas clases parciales. Se trata de una nueva caracterstica aadida a .NET 2.0 y por lo tanto a Visual C# 2005, que permite separar o partir una clase en varias porciones de cdigo.

La explicacin ruda de esto, es que el programador puede tener dos ficheros de cdigo fuente independientes, que posean el mismo nombre de clase. Para indicar que pertenece a la misma clase, sta debe tener la palabra clave partial como parte de su definicin para indicar que es una clase parcial. Un ejemplo que aclare esto es el siguiente:

partial class Class1 { public int Accion1() { return 1; } }

partial class Class1 { public int Accion2() { return 2; } }

El comportamiento de la clase es el de una nica clase, por lo que su declaracin y uso es como el de cualquier clase normal, tal y como se indica en el siguiente cdigo:

public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click( object sender, EventArgs e ) { Class1 miClase = new Class1(); MessageBox.Show(miClase.Accion1() + "\n" + miClase.Accion2()); } }

De todas las maneras, el entorno nos oculta muchas veces las clases parciales de un aplicacin. Para ello, seleccionaremos la opcin Mostrar todos los archivos de la ventana Explorador de soluciones como se indica en la figura 4.

Icono u opcin para mostrar todos los archivos del proyecto

Figura 4

De esta manera, podremos acceder a los archivos y recursos del proyecto, incluidas las clases parciales como se indica en la figura 5. En el archivo Form1.Designer.cs estar el cdigo utilizado por el diseador de formularios de Windows Forms, en el que se incluye la declaracin de todos los controles y controladores de eventos que hemos definido en nuestro proyecto.

Saul Mamani Mamani

Desarrollo Agil de Software

97

Clase parcial mostrada en los archivos del proyecto

Figura 5

A tener en cuenta: Cuando se genera un proyecto con Visual Studio 2005, el entorno genera diferentes clases parciales, como por ejemplo la que se genera para un formulario. Nota: En Visual C# 2005 todas las partes de una clase parcial deben incluir la instruccin partial.

LECCION 2: CONTROLES DE WINDOWS FORMS


Introduccin Dentro del entorno de desarrollo de Visual Studio 2005, nos encontramos un enorme conjunto de libreras y controles que podemos utilizar en nuestras aplicaciones Windows. Dependiendo del tipo de aplicacin que llevemos a cabo, el entorno habilitar los controles correspondientes para cada tipo de aplicacin. En nuestro caso, nos centraremos en los controles ms habituales de Windows, e indicaremos como utilizarlos en nuestros desarrollos. En nuestro entorno de desarrollo, encontraremos diferentes grupos de controles o componentes dispuestos de ser utilizados. En la figura 1 encontraremos los grupos de controles y componentes ms habituales.

Grupos de controles en Visual Studio 2005

Saul Mamani Mamani Figura 1

Desarrollo Agil de Software

98

Estos controles se dividen en los grupos representados en la figura anterior. A continuacin veremos los ms representativos. Mdulo 3 - Captulo 2 1. Datos 2. Componentes 3. Controles comunes 4. General 5. Otras consideraciones

1. Datos
El grupo Datos corresponde con el grupo que tiene relacin directa con los componentes de acceso a datos, como se muestra en la figura 1.

Controles Datos en Visual Studio 2005

Figura 1

Para muchos desarrolladores, los controles, componentes y mtodos de acceso a datos, contiene dentro de s un especial misterio, es como el Santo Grial de la programacin. Casi siempre nos atascamos ah, siempre en el mismo sitio. Pero no se preocupe ni lo ms mnimo por ello, aprenderemos a utilizarlos a base de prctica, y lo que es ms importante, los dominaremos rpidamente. Solo como curiosidad y por ahora, le presentar uno de los componentes ms destacables en Visual Studio 2005, por su semejanza con otro muy utilizado en "otros" entornos de desarrollo, estoy hablando del control y componente BindingNavigator que usaremos frecuentemente en nuestras aplicaciones con acceso a fuentes de datos. Este control insertado en un formulario Windows, es el que se puede ver en la figura 2.

Control BindingNavigator insertado en un formulario Windows en Visual Studio 2005

Saul Mamani Mamani Figura 2

Desarrollo Agil de Software

99

Como puede observar, este control, tiene un aspecto muy similar al del famoso Recordset de Visual Basic 6 o al DataNavigator de Borland. Lgicamente, este control tiene un aspecto mucho ms vistoso y moderno, pero es uno de los controles estrella de Visual Studio 2005, ya que en Visual Studio .NET 2002 y Visual Studio .NET 2003 no exista este control en el entorno. Visual Studio 2005 s que nos trae sin embargo, la novedad del control BindingNavigator. Comunidad dotNet: Visual Studio 2005 le proporciona un amplio conjunto de controles y componentes as como un no menos completo conjunto de clases que le facilita al desarrollador las tareas de programacin requeridas. Sin embargo, existen contribuciones gratuitas y otras de pago, que el programador puede utilizar segn lo requiera. A continuacin le indico el que a mi modo de ver es el lugar ms representativo de este tipo de contribuciones a la Comunidad de desarrolladores .NET.

2. Componentes
Windows Forms incluye un conjunto de componentes muy nutrido y variado. Algunos de estos componentes, han sido mejorados y otros ampliados. En la figura 1 podemos observar estos componentes.

Componentes de Windows Forms

Figura 1

Los componentes son como controles no visibles, o dicho de otra forma, son controles que realizan ciertas tareas, pero no tienen un interfaz que mostrar, como puede ser el caso de un botn o una caja de textos.

Saul Mamani Mamani

Desarrollo Agil de Software

100

Por ejemplo, el componente Timer nos permite recibir una notificacin cada x tiempo, pero no muestra nada al usuario de nuestra aplicacin. Si hacemos doble clic sobre el componente Timer para insertarlo en el formulario, ste quedar dispuesto en la parte inferior del formulario como se indica en la figura 2.

Control Timer insertado en un formulario de Visual C# 2005

Figura 2

Este tipo de componentes no son visibles en tiempo de ejecucin.

3. Controles comunes
Con este nombre, se aglutinan los controles ms generales y variados que podemos utilizar en nuestras aplicaciones Windows. Sera algo as, como el resto de controles y componentes no contenidos en ninguna de las secciones que hemos visto anteriormente, aunque esto no es siempre as. Por esa razn, si encuentra dos controles o componentes iguales en dos o ms secciones, no lo tenga en consideracin. Digamos que en esta solapa se aglutinan por lo tanto, los controles que utilizaremos con ms frecuencia. En la figura 1 podemos observar los controles y componentes citados.

Saul Mamani Mamani

Desarrollo Agil de Software

101

Controles Windows Forms en Visual Studio 2005

Figura 1

Debido a la cantidad de controles y componentes de los distintos grupos del Cuadro de herramientas, podemos usar el siguiente truco para que nos resulte ms fcil su localizacin. Truco: Como puede observar, a veces cuesta localizar un control debido a la enorme cantidad de controles que hay. Para ordenarlos, puede arrastrar y soltar los controles y componentes en la barra de herramientas o bien, si quiere hacer una ordenacin por orden alfabtico, puede hacer clic con el botn secundario del mouse sobre una determinada seccin de controles y seleccionar la opcin Ordenar elementos alfabticamente como se indica en la siguiente figura siguiente:

Saul Mamani Mamani

Desarrollo Agil de Software

102

Los controles y componentes de esa seccin quedarn ordenados alfabticamente. Lo ms destacable para el desarrollador habituado a otros entornos, es que aqu veremos una gran cantidad de controles que nos resultarn muy familiares. Controles como: Label, PictureBox, TextBox, Frame que ahora pasa a llamarse GroupBox, CommandButton que ahora pasa a llamarse Button, CheckBox, OptionButton que ahora pasa a llamarse RadioButton, ComboBox, ListBox, HScrollBar, VScrollBar, Timer, etc. Pero adems tenemos muchos otros que no son tan habituales en todos los entornos de desarrollo diferentes de Visual Studio .NET. Controles que proporcionan nuevas y ventajosas caractersticas a la hora de desarrollar aplicaciones con Visual C# 2005. Entre estos controles, podemos encontrar el control PrintDocument y PrintPreviewControl, para imprimir y realizar vistas preliminares, ErrorProvider, WebBrowser, FolderBrowserDialog, ToolTip para aportar tooltips a nuestros controles, TrackBar, NumericUpDown, SplitContainer, MonthCalendar, DateTimePicker, etc. Cada uno de los controles, tiene unas caractersticas y cualidades determinadas. Slo a base de prctica, aprenderemos a utilizarlos y lo nico que debemos saber, es cul de ellos utilizar en un momento dado. El abanico de controles y componentes es lo suficientemente amplio como para poder abordar con ellos, cualquier tipo de proyecto y aplicacin Windows que nos sea demandada.

4. General
Esta seccin es como el cajn desastre, un lugar dnde podemos insertar otros controles o componentes desarrollados por terceros, por ejemplo.

Saul Mamani Mamani

Desarrollo Agil de Software

103

Seccin General en Visual Studio 2005

Figura 1

Esta seccin de todos los modos, la puede utilizar un desarrollador en muchos casos. Por ejemplo, los desarrolladores que desean arrastrar y soltar aqu los controles y componentes que ms utiliza o los que utiliza en un determinado proyecto. Otro caso de ejemplo es cuando se trabaja con controles o componentes similares desarrollados por dos empresas diferentes que queremos tener localizados o separados para no mezclarlos. En otras circunstancias, tampoco es raro encontrarse con controles o componentes con iconos similares, por lo que aclararse cul es el que nos interesa puede ser una tarea obligada. An as, otra de las posibilidades con la que nos podemos encontrar para utilizar esta seccin es la de tener que utilizar un control o componente circunstancialmente en un momento dado, y por eso, que no deseemos aadir este control o componente a otra seccin como la de Controles comunes por ejemplo. Utilice por lo tanto esta seccin como lo considere oportuno.

5. Otras consideraciones
La seccin General nos indica un repositorio de mbito y carcter general, sin embargo, el desarrollador puede querer ordenar su propio repositorio o seccin de controles y componentes. Manipulando el Cuadro de herramientas Para ello, nos posicionaremos en la barra de herramientas y presionaremos el botn secundario del mouse sobre la parte gris de la barra de herramientas desplegada y seleccionaremos la opcin Agregar ficha del men emergente, como se muestra en la figura 1.

Saul Mamani Mamani

Desarrollo Agil de Software

104

Opcin de personalizacin de nuestros propios grupos de controles y componentes

Figura 1

Cuando seleccionamos esta opcin, aparecer una caja de texto en la barra de herramientas dnde podremos escribir el nombre que consideremos oportuno, como se muestra en la figura 2.

Personalizacin de un grupo de controles y componentes en Visual Studio 2005

Figura 2

Si se hace la siguiente pregunta, cmo cambiar el nombre de una seccin ya creada o una existente?, sepa que deber realizar los siguiente pasos.

Saul Mamani Mamani

Desarrollo Agil de Software

105

Haga clic con el botn secundario del mouse sobre la seccin sobre la que desea cambiar el nombre y seleccione la opcin Cambiar nombre de ficha como se muestra en la figura 3.

Figura 3

De igual forma, puede cambiar tambin el nombre de los controles o componentes insertados. Para hacer eso, haga clic con el botn secundario del mouse sobre un control o componente y seleccione la opcin Cambiar nombre de elemento como se muestra en la figura 4.

Figura 4

Visual C# 2005, nos proporciona un amplio conjunto de opciones de personalizacin del entorno de trabajo, para que se ajuste a las exigencias de los desarrolladores.

Saul Mamani Mamani

Desarrollo Agil de Software

106

FAQ: Qu ocurre si me equivoco personalizando mi barra de herramientas? Visual Studio 2005 nos proporciona la posibilidad de resetear o restaurar el estado inicial de la barra de herramientas en el entorno de desarrollo. Para hacer esto, haremos clic con el botn secundario del mouse la barra de herramientas y seleccionaremos la opcin Restablecer cuadro de herramientas del men emergente, como se muestra en la siguiente figura.

Ojo!, al seleccionar esta opcin, perderemos todas las modificaciones que hayamos realizado sobre la barra de herramientas. Otros controles a tener en cuenta Dentro del entorno de Visual Studio 2005 y en .NET en general, se han aadido una serie de controles nuevos que conviene comentar. Uno de estos controles, se llama WebBrowser, tal y como se indica en la figura 5.

Control WebBrowser en el Cuadro de herramientas

Figura 5

Saul Mamani Mamani

Desarrollo Agil de Software

107

Este control es la representacin de un control especfico para mostrar contenido XML o contenido HTML, como si de una pgina Web se tratara. Sirva el siguiente ejemplo de cdigo fuente para demostrar cmo usar el control y como se muestra dicho control en una aplicacin Windows. El cdigo de la aplicacin quedara como se detalla a continuacin:

public partial class Form1 : Form { private void Form1_Load( object sender, EventArgs e ) { this.webBrowser1.Navigate("http://localhost:8080/Bienvenido.aspx"); }

Nuestro ejemplo en ejecucin es el que se muestra en la figura 6.

Control WebBrowser en ejecucin

Figura 6

Hay ms controles que representan una novedad para el desarrollador de .NET, como puede ser por ejemplo, el control MaskedTextBox, como se muestra en la figura 7.

Control MaskedTextBox en Visual C# 2005

Figura 7

Sin embargo, hay otros controles clsicamente demandados por los desarrolladores, como los controles de accesos a puertos COM y puertos serie, como es el caso del control SerialPort que se muestra en la figura 8.

Control SerialPort en Visual C# 2005

Figura 8

Saul Mamani Mamani

Desarrollo Agil de Software

108

No es cuestin de repasar cada uno de los controles que el programador puede encontrar en Visual Studio 2005, sin embargo, no me gustara dejar de comentar, uno de los controles ms usados y tiles para las aplicaciones Windows, que tiene a su vez su equivalente para el desarrollo de aplicaciones Web en ASP.NET. Me refiero al control MonthCalendar que se muestra en la figura 9.

Control MonthCalendar en Visual C# 2005

Figura 9

Este control, que se muestra en la figura 10 cuando lo insertamos en un formulario, es un control que nos facilita la entrada de fechas en el sistema y permite asegurarnos, que la fecha seleccionada es una fecha vlida.

Control MonthCalendar insertado en un formulario Windows

Figura 10

LECCION 3: TRABAJANDO CON CONTROLES


Introduccin Hasta ahora, hemos aprendido a identificar las partes ms importantes del entorno de desarrollo de Visual Studio 2005, hemos visto igualmente como se separan los controles y componentes, y hemos visto tambin que existe un grupo de solapas donde podemos aadir nuestros propios controles y componentes, incluso hemos aprendido a crear nuestro propio grupo o seccin de controles y componentes, en el que podemos tambin incluir los controles y componentes que por ejemplo hayamos desarrollado nosotros mismos, sin embargo, para poder insertar ah un control o componente desarrollado por nosotros, deberamos aprender a crearlos. Eso es justamente lo que veremos a continuacin adems de ver otras tcnicas que conviene repasar antes de aprender a desarrollar nuestros propios controles y componentes.

Saul Mamani Mamani

Desarrollo Agil de Software

109

Por eso, lo primero que haremos ser aprender a desenvolvernos adecuadamente en el entorno de desarrollo con los controles que ya conocemos. Captulo 3 1. Dominando los controles en el entorno de trabajo 2. Creacin de controles en tiempo de ejecucin 3. Creacin de una matriz de controles 4. Creacin de controles nuevos 5. Otras consideraciones

1. Dominando los controles en el entorno de trabajo


Si queremos aprender a crear nuestros propios controles para poder distribuirlos y utilizarlos en nuestro entorno, lo mejor es dominar el uso de los controles en nuestro entorno de desarrollo. Ya hemos visto anteriormente como insertar un control a nuestro formulario, pero quizs no sepamos como manejarlos de forma eficiente en ese formulario. Inserte en un formulario Windows por ejemplo, tres controles Button como se muestra en la figura 1.

Controles Button insertados en un formulario Windows

Figura 1 Como podemos observar, los controles estn dispuestos de una forma desordenada, ya que al insertarlos por ejemplo haciendo doble clic tres veces sobre un control Button, stos quedan dispuestos anrquicamente en el formulario. Separe los controles Button de forma que queden ahora esparcidos en el formulario de alguna manera tal y como se muestra en la figura 2.

Saul Mamani Mamani

Desarrollo Agil de Software

110

Controles Button separados en el formulario

Figura 2 Seleccione todos los controles Button como se mostraba en la figura 2 y seleccione del men las opciones Formato > Alinear > Lados izquierdos como se indica en la figura 3.

Los controles Button quedarn ordenados correctamente dentro del formulario

Figura 3 Sin embargo, podemos extender el uso de las propiedades especiales para alinear los controles en un formulario. Por ejemplo, ahora que tenemos los controles Button alienados correctamente, podemos hacer uso de la opcin de men Formato > Espaciado vertical > Quitar como se indica en la figura 4.

Saul Mamani Mamani

Desarrollo Agil de Software

111

Otras opciones especiales, nos permiten alinear o trabajar con los controles de forma rpida y segura

Figura 4 En este caso, los controles quedarn dispuestos en el formulario como se indica en la figura 5.

Controles alineados y espaciados segn la eleccin de opciones del entorno

Figura 5 Como podemos apreciar, alinear los controles en el entorno es realmente Visual Studio 2005 nos proporciona una gran cantidad de opciones para llevar a cabo este tipo de tareas. sencillo.

Incluso si nos encontramos con un controles de diferente tamao entre s como se muestra en la figura 6, podemos hacer uso de la opcin del men Formato > Igualar tamao permitindonos cambiar el tamao de los controles seleccionados dentro del formulario Windows.

Saul Mamani Mamani

Desarrollo Agil de Software

112

Controles de diferentes tamaos dispuestos en el formulario Windows

Figura 6 El men que nos permite cambiar los tamaos de los controles insertados en un formulario posee diferentes posibilidades. En nuestro caso, seleccionaremos del men, la opcin Formato > Igualar tamao > Ambos tal y como se muestra en la figura 7.

Cambiando los tamaos ancho y alto de los controles seleccionados de un formulario

Figura 7 Una vez seleccionada esta opcin, los controles se modificarn con el mismo tamao tal y como se muestra en la figura 8.

Saul Mamani Mamani

Desarrollo Agil de Software

113

Controles del formulario con su tamao modificado

Figura 8 Una vez seleccionada esta opcin, los controles se modificarn con el mismo tamao tal y como se muestra en la figura 8.

Truco: Suponiendo que tengamos tres controles Button de diferentes tamaos y que queramos que todos tengan el mismo tamao que el segundo de sus controles, seleccionaremos siempre como primer control, el control que queremos como base de tamao para el resto de controles, y posteriormente con la tecla Ctrl seleccionaremos uno a uno el resto de controles.
Sin embargo, para alinear los controles en un formulario tenemos ms opciones. Hemos visto algunas de ellas, quizs las ms habituales, pero como podemos deducir del men Formato, podremos alinear los controles, espaciarlos entre s horizontal o verticalmente, modificar su tamao, centrarlos en el formulario horizontal o verticalmente, etc. Por otro lado, Visual Studio 2005, nos proporciona una utilidad en tiempo de diseo muy til. Se trata de las guas de representacin que veremos en tono azul claro, y que aparecen cuando movemos un control en el formulario. Estas guas indican la situacin y posicin de un control respecto a otro prximo. La representacin de estas guas que se muestran en la figura 9, nos facilita enormemente la disposicin de los controles en un formulario.

Saul Mamani Mamani

Desarrollo Agil de Software

114

Guas o reglas de direccin o separacin entre controles

Figura 9

2. Creacin de controles en tiempo de ejecucin


Prcticamente todo en .NET son objetos. Los controles tambin, as que para aadir un control a un formulario en tiempo de ejecucin, deberemos hacerlo tratndolo como un objeto. La declaracin principal de un objeto, se realiza con la instruccin new. El siguiente ejemplo de cdigo, crea un control Button en tiempo de ejecucin.

private void Form1_Load( object sender, EventArgs e ) { // Declaramos el objeto Button Button MiControl = new Button(); // Declaramos un nombre al control (si queremos) MiControl.Name = "btn1"; // Cambiamos algunas de sus propiedades MiControl.Text = "Ejemplo de Botn"; MiControl.Top = 50; MiControl.Left = 50; MiControl.Width = 200; MiControl.Height = 50; // Aadimos el control al Formulario this.Controls.Add(MiControl); } En la figura 1 podemos ver el resultado en ejecucin del cdigo escrito anteriormente. Para ejecutar nuestra aplicacin, presionaremos el botn F5, que es la forma habitual de ejecutar una aplicacin.

Saul Mamani Mamani

Desarrollo Agil de Software

115

Creacin de un control en tiempo de ejecucin en Visual Studio 2005

Figura 1 Otra de las caractersticas de los controles, es la posibilidad de manipular los controles en tiempo de ejecucin. Sin embargo, en nuestro caso, vamos a modificar la propiedad Text del control Button que hemos insertado en tiempo de ejecucin. Para hacer esto, lo ms habitual es poner el nombre del control, su propiedad y el valor correspondiente. En nuestro caso, el cdigo quedara como el que se indica a continuacin:

private void Form1_Load( object sender, EventArgs e ) { // Declaramos el objeto Button Button MiControl = new Button(); // Declaramos un nombre al control (si queremos) MiControl.Name = "btn1"; // Cambiamos algunas de sus propiedades MiControl.Text = "Ejemplo de Botn"; MiControl.Top = 50; MiControl.Left = 50; MiControl.Width = 200; MiControl.Height = 50; // Aadimos el control al Formulario this.Controls.Add(MiControl); // Modificamos la propiedad Text del control insertado btn1.Text = "Otro texto"; } Analizando este cdigo, parece estar bien escrito, pero al presionar F5 para ejecutar nuestro proyecto, nos encontramos con que Visual C# 2005 nos muestra un error. Nos indica que btn1 no est declarado. Qu ocurre?. Al buscar la clase de ensamblados de la aplicacin, el control Button de nombre btn1 no existe, por lo que Visual C# 2005 detecta que debemos declarar el control, sin embargo y en nuestro caso, esta declaracin la hacemos en tiempo de ejecucin. Cmo acceder a la propiedad Text del control Button creado en tiempo de ejecucin?. El siguiente cdigo resuelve esta duda:

private void Form1_Load( object sender, EventArgs e ) { // Declaramos el objeto Button Button MiControl = new Button(); // Declaramos un nombre al control (si queremos) MiControl.Name = "btn1";

Saul Mamani Mamani

Desarrollo Agil de Software

116

// Cambiamos algunas de sus propiedades MiControl.Text = "Ejemplo de Botn"; MiControl.Top = 50; MiControl.Left = 50; MiControl.Width = 200; MiControl.Height = 50; // Aadimos el control al Formulario this.Controls.Add(MiControl); // Modificamos la propiedad Text del control insertado ((Button)this.FindForm().Controls["btn1"]).Text = "Otro texto"; } Bsicamente, utilizamos una conversin explcita del objeto devuelto por la bsqueda realizada en los controles del formulario principal, que ser un control Button de nombre btn1, para poder as, cambiar la propiedad Text de este control. An as, tambin podramos haber accedido a la propiedad Text del control mediante otra accin complementaria, como se muestra en el siguiente cdigo:

public partial class Form1 : Form { // Declaramos el objeto Button a nivel de clase Button MiControl = new Button(); private void Form1_Load( object sender, EventArgs e ) { // Declaramos un nombre al control MiControl.Name = "btn1"; // Cambiamos algunas de sus propiedades MiControl.Text = "Ejemplo de Botn"; MiControl.Location = new Point(50, 50); MiControl.Size = new Size(200, 50); // Aadimos el control al Formulario this.Controls.Add(MiControl); } private void button1_Click( object sender, EventArgs e ) { MiControl.Text = "Otro texto"; }

Ojo!: Tenga en cuenta que esta accin se puede realizar si la declaracin del objeto Button est dentro del mismo mbito de llamada de la propiedad Text. Por esa razn, hemos sacado la declaracin MiControl del objeto Button fuera del procedimiento de creacin dinmica del control, pero tenga en cuenta tambin, que en este caso, tendremos declarada siempre en memoria la variable MiControl. El uso de la conversin (Button) es siempre ms seguro.
El caso anterior utilizando nicamente la conversin explcita, (Button), quedara como se detalla a continuacin (para estos dos ejemplos, he aadido adems un control Button al formulario Windows, desde el cul cambiaremos la propiedad Text del control Button creado en tiempo de ejecucin):

public partial class Form1 : Form { private void Form1_Load( object sender, EventArgs e ) { // Declaramos el objeto Button Button MiControl = new Button(); // Declaramos un nombre al control MiControl.Name = "btn1"; // Cambiamos algunas de sus propiedades MiControl.Text = "Ejemplo de Botn"; MiControl.Location = new Point(50, 50); MiControl.Size = new Size(200, 50); // Aadimos el control al Formulario this.Controls.Add(MiControl);

Saul Mamani Mamani

Desarrollo Agil de Software

117

} private void button1_Click( object sender, EventArgs e ) { ((Button)this.FindForm().Controls["btn1"]).Text = "Otro texto"; } Este ejemplo en ejecucin es el que se muestra en la figura 2.

Ejecucin del ejemplo anterior en Visual C# 2005

Figura 2 Antes de continuar con el siguiente apartado en el que aprenderemos a crear un array de controles, comentar que a la hora de posicionar un determinado control en un formulario Windows, lo podemos hacer con las propiedades Top y Left, o bien, utilizando la propiedad Location. Lo mismo ocurre con las propiedades Width y Height que pueden ser sustituidas por la propiedad Size. El mismo ejemplo de creacin de controles en tiempo de ejecucin utilizando el mtodo Location y Size, quedara como se detalla a continuacin:

private void Form1_Load( object sender, EventArgs e ) { // Declaramos el objeto Button Button MiControl = new Button(); // Declaramos un nombre al control (si queremos) MiControl.Name = "btn1"; // Cambiamos algunas de sus propiedades MiControl.Text = "Ejemplo de Botn"; // Propiedad Location MiControl.Location = new Point(50, 50); // Propiedad Size MiControl.Size = new Size(200, 50); // Aadimos el control al Formulario this.Controls.Add(MiControl); } Hasta ahora hemos visto como crear controles en tiempo de ejecucin, pero en muchas ocasiones, nos es til no slo crear un control en tiempo de ejecucin, sino relacionarlo con un evento. En nuestro ejemplo del control Button, cuando hacemos clic sobre el control, no ocurre nada, y sera interesante mostrar un mensaje o realizar una accin determinada. Para hacer esto, deberemos asociar el evento Click del botn que hemos creado con el delegado EventHandler. El cdigo de nuestro ejemplo mejorado es el que se detalla a continuacin:

Saul Mamani Mamani

Desarrollo Agil de Software

118

private void Form1_Load( object sender, EventArgs e ) { // Declaramos el objeto Button Button MiControl = new Button(); // Declaramos un nombre al control (si queremos) MiControl.Name = "btn1"; // Cambiamos algunas de sus propiedades MiControl.Text = "Ejemplo de Botn"; // Propiedad Location MiControl.Location = new Point(50, 50); // Propiedad Size MiControl.Size = new Size(200, 50); // Aadimos el control al Formulario this.Controls.Add(MiControl); // Aadimos el evento Click al control creado dinmicamente MiControl.Click += new EventHandler(btn1Click); } private void btn1Click( object Sender, EventArgs e ) { // Mostramos un Mensaje MessageBox.Show("Soy el Control Button con texto: '" + ((Button)Sender).Text + "'"); } Nuestro ejemplo en ejecucin es el que se muestra en la figura 3.

Ejecucin del ejemplo con asociacin de evento desarrollado en Visual Studio 2005

Figura 3

3. Creacin de una matriz de controles


En el captulo anterior, hemos visto como crear controles en tiempo de ejecucin e incluso como asociar un evento a un control creado dinmicamente en Visual Studio 2005, pero, qu ocurre si queremos crear un array o matriz de controles?. Podemos simular una matriz de controles de muchas formas. Yo en este ejemplo, aplicar la siguiente forma de llevar a cabo esta tarea:

private void Form1_Load( object sender, EventArgs e )

Saul Mamani Mamani

Desarrollo Agil de Software

119

{ // Declaramos la variable contador del bucle Para byte i; // Declaramos la variable contador del nmero de controles a crear byte intNumControles = 5; // Iniciamos el bucle Para for( i = 0; i < intNumControles; i++ ) { // Declaramos el objeto TextBox TextBox MiControl = new TextBox(); // Le asignamos un nombre al control MiControl.Name = "txt1"; // Utilizamos la propiedad Tag para almacenar ah // el valor del control de la matriz virtual MiControl.Tag = i; // Le asignamos un tamao en el Formulario Windows MiControl.Size = new Size(100, 20); // Le asignamos una posicin en el formulario Windows MiControl.Location = new Point(50, 22 * (i + 1)); // Le cambiamos la propiedad Text MiControl.Text = MiControl.Name + "(" + i.ToString() + ")"; // Aadimos el control al Formulario this.Controls.Add(MiControl); // Aadimos el evento Click al control creado dinmicamente MiControl.Click += new EventHandler(txt1Click); } } public void txt1Click( object Sender, EventArgs e ) { // Mostramos un Mensaje MessageBox.Show( "Control " + ((TextBox)Sender).Tag.ToString() ); } Nuestro ejemplo de demostracin en ejecucin es el que se puede ver en la figura 1.

Ejecucin de la simulacin de una matriz de controles

Figura 1 Obviamente, existen diferentes formas y tcnicas de simular un array o matriz de controles. Sirva esta que hemos visto, como ejemplo de lo que se puede hacer, pero para nada se trata de una norma o regla fija. Debemos destacar que en .NET no existe el concepto de array o matriz de controles ya que sta es una caracterstica no propia del lenguaje, pero que para ciertas ocasiones conviene conocer.

4. Creacin de controles nuevos


Ya hemos visto la diferencia ms genrica entre un componente y un control, pero an no sabemos como desarrollar nuestros propios controles en Visual Studio 2005.

Saul Mamani Mamani

Desarrollo Agil de Software

120

En primer lugar y antes de adentrarnos en la creacin de nuestros propios controles con Visual Studio 2005, debo indicarle que debemos obviar todo lo relacionado con los ActiveX OCX y ActiveX en general. En Visual Studio 2005, la palabra ActiveX ya no existe. El modelo de programacin ha cambiado y por eso, los componentes y controles se generan ahora siguiendo otras normas que aprenderemos a utilizar de forma inmediata. Iniciaremos Visual Studio 2005 y seleccionaremos un proyecto de tipo Biblioteca de clases. En el nombre de proyecto, podemos indicarle el nombre que deseemos tal y como se muestra en la figura 1, y a continuacin presionaremos el botn OK.

Seleccin de nuevo proyecto Biblioteca de clases en Visual C# 2005

Figura 1 La diferencia mayor que reside entre el desarrollo de componentes y controles en .NET, es que en lugar de heredar de la clase Component como en el caso de la creacin de los componentes, se ha de heredar de la clase Control o System.Windows.Forms.UserControl. El tipo de proyecto seleccionado no posee por defecto como ocurre con los controles ActiveX, de la superficie contenedora sobre la cul podremos insertar otros controles o realizar las acciones que consideremos pertinentes para crear as nuestro control personalizado. En este caso, la superficie contenedora la deberemos crear aadiendo las referencias necesarias a nuestro programa. Haga clic con el botn secundario del mouse sobre el proyecto o solucin de la ventana Explorador de soluciones y a continuacin, seleccione la opcin Propiedades del men emergente. A continuacin, agregue las referencias a las libreras de clases System.Drawing y System.Windows.Forms. Por ltimo, escriba las siguientes instrucciones bsicas.

using System; using System.ComponentModel; using System.Windows.Forms;

Saul Mamani Mamani

Desarrollo Agil de Software

121

using System.Drawing; namespace ClassLibrary1 { public class Class1 : UserControl { } } En este punto, nuestra clase habr sido transformada en la clase contenedora de un control que es la que puede verse en la figura 2.

Superficie contenedora por defecto de un control

Figura 2 An as, sino quiere realizar esta accin, otra forma de tener lista la superficie contenedora del control, es la de eliminar la clase del proyecto y presionar el botn secundario del mouse sobre la ventana del Explorador de soluciones y seleccionar la opcin de Agregar > Nuevo elemento... del men emergente. De las opciones que salen, (ver figura 2B), deberamos seleccionar entonces la plantilla Control de usuario.

Seleccionar un control de usuario

Figura 2B Sin ms dilacin, aadiremos sobre la superficie del control contenedor, (al que habremos cambiado el nombre a MiControl), dos controles Label y dos controles TextBox. Debido a que al aadirle los controles tendremos mucho cdigo que simplemente sirve para el diseo del interfaz de usuario, podemos hacer lo que el propio diseador hace: dividir la clase en dos partes o clases parciales, de esta forma conseguiremos el propsito de las clases parciales, (al menos en cuanto al diseo de formularios y controles de usuario

Saul Mamani Mamani

Desarrollo Agil de Software

122

se refiere), que es, como ya comentamos anteriormente, separar el cdigo de diseo del cdigo que nosotros vamos a escribir. Para ello, aadiremos un nuevo elemento del tipo Class, al que le daremos el nombre MiControl.Designer.cs, y en esa clase pegaremos el cdigo que actualmente hay en la clase, pero aadiendo la partcula partial antes de class, con idea de que el compilador de Visual C# 2005 sepa que nuestra intencin es crear una clase parcial. En el nuevo archivo tendremos el siguiente cdigo:

using using using using

System; System.ComponentModel; System.Windows.Forms; System.Drawing;

namespace ClassLibrary1 { partial class MiControl { public MiControl() { InitializeComponent(); } private private private private TextBox textBox1; TextBox textBox2; Label label2; Label label1;

private void InitializeComponent() { this.label1 = new System.Windows.Forms.Label(); this.textBox1 = new System.Windows.Forms.TextBox(); this.textBox2 = new System.Windows.Forms.TextBox(); this.label2 = new System.Windows.Forms.Label(); this.SuspendLayout(); // // label1 // this.label1.Location = new System.Drawing.Point(10, 18); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(74, 19); this.label1.TabIndex = 0; this.label1.Text = "Usuario:"; this.label1.TextAlign = System.Drawing.ContentAlignment.TopRight; // // textBox1 // this.textBox1.Location = new System.Drawing.Point(101, 15); this.textBox1.Name = "textBox1"; this.textBox1.Size = new System.Drawing.Size(100, 20); this.textBox1.TabIndex = 1; // // label2 // this.label2.Location = new System.Drawing.Point(10, 44); this.label2.Name = "label2"; this.label2.Size = new System.Drawing.Size(74, 19); this.label2.TabIndex = 2; this.label2.Text = "Contrasea:"; this.label2.TextAlign = System.Drawing.ContentAlignment.TopRight; // // textBox2 // this.textBox2.Location = new System.Drawing.Point(101, 41); this.textBox2.Name = "textBox2"; this.textBox2.PasswordChar = '*'; this.textBox2.Size = new System.Drawing.Size(100, 20); this.textBox2.TabIndex = 3;

Saul Mamani Mamani

Desarrollo Agil de Software

123

// // MiControl // this.Controls.Add(this.textBox2); this.Controls.Add(this.label2); this.Controls.Add(this.textBox1); this.Controls.Add(this.label1); this.Name = "MiControl"; this.Size = new System.Drawing.Size(228, 85); this.ResumeLayout(false); this.PerformLayout(); } } } Realmente es casi lo mismo que tenemos actualmente, tan solo tendremos que aadir la instruccin partial, (no hace falta que est declarada como public, ya que en el otro "trozo" tendremos declarada esta clase como pblica), adems del constructor de la clase, desde el que llamaremos al mtodo que se encarga de toda la inicializacin: InitializeComponent().

Nota: Al crear un archivo con el mismo nombre que uno existente, pero con "una extensin extra", (por llamarlo de alguna forma), se mostrar "agrupado" con el archivo principal, tal como podemos ver en esta figura:

A continuacin, escribiremos el siguiente cdigo, en el archivo que tenamos originalmente (MiControl.cs): using System; using System.ComponentModel; using System.Windows.Forms; namespace ClassLibrary1 { public partial class MiControl : UserControl { private bool _Acceso; [Category("Acceso"), Description("Indica si se permite o no el acceso"), DefaultValue(false), ReadOnly(true)] public bool Acceso { get{ return _Acceso; } set{ _Acceso = value; } } public void Validar() { if( textBox1.Text == "ejemplo" & this.textBox2.Text == "ejemplo" )

Saul Mamani Mamani

Desarrollo Agil de Software

124

_Acceso = true; else _Acceso = false; } private void MiControl_Load( object sender, EventArgs e ) { _Acceso = false; } } } Observando el cdigo, vemos que hemos creado una propiedad Acceso que nos permitir saber si un usuario y contrasea han sido validadas o no. Ahora nuestro control, est listo ya para ser compilado y probado en una aplicacin Windows. Para compilar nuestro control, haga clic en el men Generar > Generar Solucin como se muestra en la figura 3.

Opcin para compilar nuestro control

Figura 3 A continuacin aada un nuevo proyecto Aplicacin para Windows a la solucin. Para aadir un proyecto a la solucin actual, tendremos que ir al men Archivo y seleccionar Nuevo proyecto, cuando nos muestre el cuadro de dilogo, indicaremos que lo aada a la misma solucin, tal como podemos ver en la figura 3B:

Saul Mamani Mamani

Desarrollo Agil de Software

125

Aadir un nuevo proyecto a la solucin existente

Figura 3B Establzcalo como proyecto inicial de la solucin. Acuda a la barra de herramientas y busque el control que hemos creado y compilado para insertarlo en el formulario Windows. Sino aparece en la barra de herramientas, (que debera aparecer en el grupo ClassLibrary1), deber aadirlo de la siguiente manera. Haga clic sobre la barra de herramientas y seleccione la opcin Elegir Elementos... del men emergente que aparece en la figura 4.

Saul Mamani Mamani

Desarrollo Agil de Software

126

Opcin para aadir un control o componente a la barra de herramientas

Figura 4 Aparecer una ventana para buscar el control ya compilado en el disco duro. Presionaremos el botn Examinar... y buscaremos nuestro control para seleccionarlo. Una vez hecho esto, tal y como se indica en la figura 5, haremos clic sobre el botn OK.

Seleccin del control compilado anteriormente

Figura 5 Nuestro control quedar insertado en en la barra de herramientas como se muestra en la figura 6.

Saul Mamani Mamani

Desarrollo Agil de Software

127

Control insertado en la barra de herramientas

Figura 6 Para insertar el control en el formulario, haremos doble clic sobre l. Este quedar dispuesto en el formulario Windows como se indica en la figura 7.

Control insertado en el formulario Windows de prueba

Figura 7 Nuestro formulario Windows de prueba en ejecucin con el control insertado en l, es el que puede verse en la figura 8.

Formulario Windows de prueba en ejecucin con el control insertado

Figura 8 Como ha podido comprobar, la creacin de controles en Visual C# 2005, tampoco requiere de una gran habilidad o destreza, y su similitud con la creacin de componentes es enorme. De hecho, todo se reduce en el uso y programacin de una clase con la salvedad de que dentro de esa clase, indicamos si se trata de una clase como tal, la clase de un componente o la clase de un control.

Saul Mamani Mamani

Desarrollo Agil de Software

128

5. Otras consideraciones
Controles contenedores
Cuando trabajamos con controles, surgen muchas veces muchas dudas de carcter habitual. Definamos que hay dos tipos de controles, los controles contenedores y los controles no contenedores. La diferencia entre ambos es que los controles contenedores, pueden como su propia palabra dice, contener otros controles dentro de ste. Los controles no contenedores no pueden. Un claro ejemplo de control contenedor, es el control TabControl, el cul permite incluir dentro de l otros controles, incluso controles contenedores a su vez. Un ejemplo de control no contenedor es el control Button por ejemplo. Si intenta poner un control TextBox encima de un control Button, observar que no puede. Cuando un control se inserta dentro de un contenedor (no olvidemos que el formulario Windows es otro contenedor), ste control queda alojado dentro de su contenedor, de tal manera que si movemos el contendor, estaremos moviendo tambin el contenido de ste. El problema sin embargo cuando trabajamos con controles, viene cuando deseamos recorrer los controles de un formulario. Esto es muy habitual cuando deseamos borrar el contenido de todos los controles TextBox de un formulario Windows por ejemplo. Dentro de un formulario y supuesto que tengamos 4 controles TextBox insertados dentro de l, y luego un botn que nos permita eliminar el contenido de cada caja de texto, tal y como se indica en la figura 1, deberamos escribir un cdigo similar al que veremos a continuacin:

Formulario Windows de ejemplo con sus controles insertados

Figura 1 public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click( object sender, EventArgs e ) { foreach( object obj in Controls ) if( obj is TextBox ) ((TextBox)obj).Text = ""; } } Observando este cdigo, vemos que lo que hacemos no es otra cosa que recorrer los objetos como controles dentro del formulario y miramos si se trata de un control de tipo TextBox. En este caso, modificamos la propiedad Text para

Saul Mamani Mamani

Desarrollo Agil de Software

129

dejarla en blanco. Ejecute este ejemplo con la tecla de funcin F5, escriba algo en las cajas de texto y pulse el botn. Observar que logramos conseguir nuestro objetivo de borrar el contenido de todos los controles TextBox. A continuacin, lo que haremos ser aadir un control TabControl a nuestro formulario Windows, y dentro de l, aadiremos los cuatro controles TextBox y el control Button anteriormente comentados, tal y como se muestra en la figura 2.

Controles insertados dentro de un control contenedor como el control TabControl

Figura 2 El cdigo anterior no har falta modificarlo, por lo que ejecutaremos la aplicacin nuevamente presionando el botn F5, escribiremos algo de texto en las cajas de texto y pulsaremos el botn como hicimos antes. Observaremos que en este caso, los controles TextBox no se han quedado en blanco como antes. El contenedor no es el formulario Windows, sino el control TabControl. Dicho control es tratado en el bucle anterior del control como control dependiente del formulario Windows, pero los controles TextBox y el propio control Button, quedan encerrados dentro de un mbito contenedor diferente. Para solventar este problema, deberemos recorrer los controles del contenedor. Una forma de solventar esto es de la siguiente manera: public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click( object sender, EventArgs e ) { for( byte i = 0; i < tabControl1.TabPages.Count; i++ ) foreach( object obj in tabControl1.TabPages[i].Controls ) if( obj is TextBox ) ((TextBox)obj).Text = ""; } } De esta manera, recorreremos todos los controles que residen en las pginas de un control TabControl y si se trata de un control TextBox, modificaremos la propiedad Text correspondiente. Por esta razn, cuando trabajamos con controles dentro de un formulario y queremos actuar sobre un conjunto de controles determinado, debemos tener en cuenta entre otras cosas, si se trata de un conjunto de controles o un control simplemente, se encuentra dentro de un control contenedor o fuera de l. Sin embargo, para resolver el problema de recorrer todos los controles de un formulario, estn o no dentro de un control contenedor, lo mejor es ampliar la funcin anterior y hacerla recursiva, de modo que permita recorrer todos los controles del formulario, estn o no dentro de un contenedor.

Saul Mamani Mamani

Desarrollo Agil de Software

130

El siguiente cdigo, refleja un ejemplo de como hacer esto posible. En el mismo proyecto, podemos aadir nuevos TextBox a la otra ficha del TabControl, adems uno en el propio formulario, (para que no est dentro del TabControl). Rellene algo de contenido en los controles TextBox y ejecute el cdigo: public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click( object sender, EventArgs e ) { VaciarTextBox(this); } private void VaciarTextBox( Control Parent ) { foreach( Control obj in Parent.Controls ) { if( obj.Controls.Count > 0 ) VaciarTextBox(obj); if( obj is TextBox ) ((TextBox)obj).Text = ""; } } }

Smart Tags
Otras de las novedades del diseador de formularios de Visual Studio 2005 es el hecho de poder utilizar las denominadas Smart Tags. Si usted es usuario de Office en sus ltimas versiones, sabr a que me refiero. Las Smart Tags nos indican trucos y consejos directa a la cul se puede acceder desde Visual Studio 2005 de forma rpida, ahorrndonos tiempo y aportndonos productividad en nuestros desarrollos. Las Smart Tags aparecen en Visual Studio 2005 tanto en el lado del diseo del formulario Windows como en la parte correspondiente del cdigo fuente escrito. En el caso de los controles insertados en un formulario Windows, estas Smart Tags aparecern por lo general en el borde superior derecho del control y se representar con un icono parecido a este .

Si hacemos clic sobre ese icono, aparecer una ayuda grfica con varias opciones que permite realizar las acciones ms comunes sobre el control seleccionado, tal y como se indica en la figura 3 para el caso del control TabControl.

Controles insertados dentro de un control contenedor como el control TabControl

Figura 3 En el caso del cdigo, las Smart Tags poseen un comportamiento ligeramente diferente dependiendo del caso. Un ejemplo habitual es el hecho de declarar una variable incorrecta tratando de trabajar con ella. Cuando ejecutamos nuestra aplicacin, el depurador se detiene en la declaracin incorrecta y nos muestra una ventana de depuracin, como se indica en la figura 4.

Saul Mamani Mamani

Desarrollo Agil de Software

131

Ventana de depuracin detallada

Figura 4 La ventana emergente de depuracin nos indica no slo el tipo de error encontrado, sino que adems, nos indica las posibles soluciones para solventar el problema. Estos consejos, sugerencias o Tips, nos ofrecen una ventaja sobre nuestros desarrollos, proporcionndonos una rpida solucin a un problema y permitindonos ahorrar mucho tiempo resolviendo el porqu del problema. Si en la ventana de depuracin emergente presionamos sobre la opcin Ver detalle..., obtendremos una nueva ventana como la que se muestra en la figura 5.

Ventana de detalles del error

Figura 5 De esta manera, conoceremos los detalles del problema detectado por Visual Studio 2005 permitindonos corregirlo cmodamente.

Saul Mamani Mamani

Desarrollo Agil de Software

132

Generacin de cdigo rpido


Otra consideracin a tener en cuenta a la hora de trabajar con cdigo y a la hora por lo tanto, de desarrollar nuestros propios controles, componentes, clases, etc., es la ayuda que nos proporciona el entorno cuando deseamos escribir determinadas funciones rpidas de cdigo que son bastante habituales. Posicinese sobre el cdigo de Visual C# 2005 y haga clic con el botn secundario del mouse y seleccione la opcin Insertar fragmento de cdigo... como se indica en la figura 9.

Opcin de recortes de cdigo de Visual Studio 2005

Figura 9 Aparecer una ventana emergente como la que se muestra en la figura 10 dentro de la cul, podremos seleccionar la carpeta que contiene un amplio conjunto de funciones, para insertarla rpidamente a nuestro cdigo.

Ventana emergente de carpetas de recortes de cdigo

Figura 10 Como ejemplo, sitese con los cursores sobre Math, pulse Enter, seleccione a continuacin Random Number como se indica en la figura 11 y vuelva a presionar Enter. El cdigo quedar insertado a nuestro proyecto de Visual C# 2005 como se muestra en la figura 12:

Saul Mamani Mamani

Desarrollo Agil de Software

133

Seleccin de un recorte de cdigo

Figura 11

El cdigo generado por el snippet

Figura 12 Estos atajos, aumentan la productividad en el desarrollo de aplicaciones de forma abismal. Tengamos en cuenta, que muchas de las operaciones que realizamos los desarrolladores, son acciones repetitivas, funciones generales o funciones habituales que reducen nuestro rendimiento y productividad y nos permite cometer errores. Con estas acciones, evitamos todo esto.

FAQ: Cmo aadir nuestros propios recortes o Snippets? Indudablemente, una cosa es usar los recortes que nos proporciona Visual C# 2005 y otra muy diferente aadir los nuestros al entorno. Para hacer esto ltimo, primero debemos escribir nuestros recortes de cdigo, la mejor forma es fijarse cmo estn hechos los que se incluyen con Visual Studio 2005, para localizarlos, abra una ventana de Windows y sitese sobre la carpeta de instalacin del entorno de Visual Studio 2005 y concretamente en la carpeta de Snippets. En mi caso, la ruta es C:\Archivos de programa\Microsoft Visual Studio 8\VC#\Snippets\1033\Visual C#. Para aadir nuevos recortes, lo podemos hacer desde el men Herramientas > Administrador de fragmentos de cdigo (Tools > Code Snippets Manager).
Refactorizar (Refactor)
Otra de las caractersticas de Visual C# 2005 es lo que se conoce como Refactoring (refactorizar), al igual que los Code Snippets, esta nueva funcionalidad, nos ayudar a acelerar la escritura de nuestro cdigo, pero en lugar de aadir nuevas funciones o mtodos o todo lo que los Snippets nos puede aportar, con las opciones del men Refactorizar podemos "reorganizar" nuestro cdigo con un solo click de botn. Por ejemplo, en el segundo ejemplo del principio de esta leccin, tenamos el formulario, al principio lo tenamos dentro del mtodo que interceptaba "extraerlo" y ponerlo en un nuevo mtodo llamado VaciarTextBox, pues mtodo e insertar el cdigo cortado, y poner una llamada al nuevo mtodo hacer con un par de clicks de mens. cdigo que buscaba los TextBox en nuestro el evento Click del botn, para despus esa tarea de cortar el cdigo, escribir un donde antes estaba el cdigo, lo podemos

Veamos paso a paso lo que tenemos que hacer para utilizar las opciones del men Refactor:

Saul Mamani Mamani 1. Seleccionamos el cdigo que queremos "extraer" 2. Presionamos con el botn secundario y del men desplegable seleccionamos Refactor

Desarrollo Agil de Software

134

3. De las opciones que nos ofrece seleccionamos Extract Method, (tal como se muestra en la figura 13) 4. Nos mostrar un cuadro de dilogo para que indiquemos cmo se llamar el mtodo, (figura 14) 5. Al presionar Aceptar, tendremos creado el mtodo y desde el mtodo button1_Click se har una llamada 6. Ahora solo queda indicar el argumento y hacer ajustes mnimos

Extraer un mtodo con Refactor

Figura 13

El nombre del mtodo a crear para insertar el cdigo extrado

Figura 14

LECCION 5: DESPLIEGUE DE APLICACIONES.


Introduccin En este captulo, aprenderemos lo que son los ensamblados o assemblies, un trmino completamente nuevo para los desarrolladores de otros entornos distintos de .NET, y aprenderemos a desplegar o instalar nuestras aplicaciones.

Saul Mamani Mamani

Desarrollo Agil de Software

135

Siempre que terminamos de desarrollar un proyecto, nos aborda la duda y pregunta casi obligada de y ahora qu?. Eso es lo que veremos en este captulo... el qu. Captulo 5 1. Desmitificando los ensamblados 2. Desplegando con XCOPY 3. GAC y Strong Names 4. Creando un paquete de instalacin 5. Otras consideraciones

1. Desmitificando los ensamblados


Un concepto completamente nuevo para los que nunca han desarrollado con .NET, es la palabra Assembly, denominada ensamblado. Los ensamblados, son para entenderlo muy rpidamente, como los ejecutables de una aplicacin. La nica diferencia notable, es que en .NET, un proyecto o aplicacin, se compila en cdigo intermedio, el conocido como Intermediate Language o lenguaje intermedio, que luego interpretar el CLR o Common Language Runtime para ejecutarla en el sistema operativo correspondiente. Ese cdigo intermedio, es el ensamblado de nuestra aplicacin que a su vez puede contener uno o ms ficheros, y a su vez, un proyecto, puede estar contenido por uno o ms ensamblados. Por lo tanto, dentro de un ensamblado, se encierran algunas partes importantes que debemos conocer. Lo mejor para entender bien lo que hay en un ensamblado y que contiene es que abramos Visual Studio 2005 y seleccionemos una plantilla de proyecto de tipo Console Application como se muestra en la figura 1.

Seleccionando un proyecto de tipo Console Application

Figura 1

Saul Mamani Mamani

Desarrollo Agil de Software

136

A continuacin, escribiremos un ejemplo simple de consola, para que estudiemos el resultado de ste. El cdigo fuente de nuestra pequea aplicacin de ejemplo, es el que se detalla a continuacin: class Program { static void Main( string[] args ) { Console.WriteLine("Ejemplo de consola"); Console.WriteLine(""); Console.Write("<Pulse Enter para terminar>"); Console.ReadLine(); } } Nuestro ejemplo de prueba en ejecucin, es el que puede verse en la figura 2.

Ejecucin del ejemplo de Consola

Figura 2 Ahora bien, lo que tenemos una vez compilamos nuestra aplicacin de consola, no es un ejecutable como tal o como lo entenderamos en otros compiladores, por ejemplo en Visual C++. En Visual C++, generbamos un ejecutable nativo al sistema en el cul compilbamos el proyecto y si ese ejecutable lo llevbamos a otra mquina con otro sistema operativo diferente a Windows, ese programa no iba a funcionar. Con .NET y en nuestro caso con Visual C# 2005, este concepto ha cambiado. El proyecto que hemos desarrollado no se compila a un ejecutable nativo, sino que el sistema .NET lo compila a un lenguaje intermedio, que luego el CLR de la mquina en la cul lanzamos nuestra aplicacin, ser capaz de interpretar adecuadamente. Puede que estos conceptos le puedan desorientar un poco, pero es muy fcil de comprender. Adicionalmente a esto, lo suyo es destripar un poco lo que hay dentro del ensamblado que hemos convertido a cdigo intermedio. Para llevar a cabo nuestra tarea, haremos uso de una herramienta externa de Microsoft y del entorno Visual Studio 2005, que nos permite analizar el ensamblado de un proyecto convertido a cdigo intermedio.

Aclaracin: Un ensamblado o el cdigo intermedio, (IL a partir de ahora), no es ni el cdigo fuente de nuestra aplicacin ni el programa ejecutable. El IL se puede analizar con una herramienta de Microsoft denominada ildasm. Si dispone de una versin Express de Visual Studio, debe saber que esta herramienta no se incluye en ellas, slo forma parte del SDK de .NET Framework que se incluye con las versiones de Visual Studio 2005.
Cuando abrimos el fichero ejecutable de nuestra aplicacin con la herramienta ildasm.exe, observamos que esta tiene una gran cantidad de informacin, como se indica en la figura 3.

Saul Mamani Mamani

Desarrollo Agil de Software

137

ildasm con el fichero ejecutable de nuestra aplicacin de ejemplo abierto

Figura 3 Antes de adentrarnos ms en los entresijos de un ensamblado, piense en l como si fuera una coleccin de elementos. Esos elementos, pueden ser recursos, tipos, funcionalidades, que todas juntas, forman nuestra aplicacin. As, lo primero que vemos en la figura 3, es Referencias a las libreras utilizadas que el CLR deber interpretar posteriormente. la palabra MANIFIEST.

Luego encontramos otras el mbito de la aplicacin y las clases de sta, con sus mtodo tanto estticos como no estticos. No es cuestin de entrar mucho ms en detalle del cdigo IL que contiene un proyecto compilado en .NET como ste, pero es conveniente a verlo para entender los conceptos que estamos tratando. En la figura 4, tenemos el cdigo IL del mtodo Main.

El cdigo IL del mtodo Main de la aplicacin de ejemplo

Figura 4

Saul Mamani Mamani

Desarrollo Agil de Software

138

Los ensamblados como podemos ver, contiene ms informacin de la que propiamente tendra un ejecutable "normal". Un ensamblado en .NET, contiene tambin como hemos podido ver, datos e informacin que encontraramos en cualquier tipo de librera, lo cul representa adems, toda la informacin necesaria del CLR en cualquier momento. Con todo esto, podemos resumir por lo tanto, que un ensamblado contiene cdigo, recursos y metadatos. El cdigo en IL es el cdigo que ser ejecutado por el CLR. Adems, cualquier recurso (imgenes, metadatos, etc.) son accesibles en el ensamblado. Los metadatos por su parte, contiene informacin sobre las clases, interfases, mtodos y propiedades, que posibilitan toda la informacin necesaria por el CLR para poder ejecutar nuestra aplicacin correctamente

2. Desplegando con XCOPY


Notas previas
Todas las aplicaciones desarrolladas con .NET estn aisladas, no como ocurre con los compilados pre-.NET, de manera tal que los conflictos con las DLL se han visto reducidos enormemente, por no decir que han desaparecido. El famoso infierno de las DLLs que tanto hemos sufrido los desarrolladores, ha pasado ya a la historia. Tambin podemos usar componentes Basta con copiarlos al mismo directorio en el que se encuentra nuestra aplicacin ejecutable. privados.

Adems, .NET nos permite tener ms de un componente versionado (diferentes versiones de un mismo componentes) dentro de un mismo ordenador, por lo que los problemas de compatibilidad estaran resueltos. Con estos detalles, repasamos algunas de las mejoras a la hora de desplegar o instalar una aplicacin desarrollada con Visual Studio 2005 en un sistema. A continuacin, veremos algunas anotaciones adicionales que nos ayudarn a realizar estas y otras acciones.

XCOPY
Lo que nos ofrece XCOPY a los desarrolladores e ingenieros, es la posibilidad de instalar e implementar nuestra solucin y proyecto a un sistema de una manera rpida, fiable y sin apenas impacto. El mtodo XCOPY para desplegar aplicaciones, pasa por alto la posibilidad de implementar los ensamblados en la GAC, algo que segn determinadas circunstancias, resulta ms que provechoso. Hay que tener en cuenta, que si hacemos un mal uso de la GAC, sta puede convertirse en un desvn difcil de gestionar. Utilice la GAC con criterio y si no quiere complicaciones, despliegue sus aplicaciones con XCOPY. Como habr podido ya observar, dentro de un proyecto de Visual C# 2005, nos podemos encontrar con una extensa estructura de directorios. Esta estructura, lejos de ser una molestia, constituye las caractersticas ms importantes a la hora de desplegar nuestras aplicaciones a travs de XCOPY. Como vemos, todo en .NET tiene su sentido y tiene un porqu. Para instalar nuestra aplicacin desarrollada en Visual C# 2005 en un sistema cliente, bastar por lo tanto, con realizar una accin similar al XCOPY de DOS, es decir, copiaremos en el ordenador cliente, los ficheros o ensamblados necesarios (ejecutables, recursos, dlls, etc.) de la estructura de nuestro proyecto. Debemos tener en cuenta, que en otros lenguajes basados en COM, como Visual Basic 6, podamos hacer esto igualmente, pero debamos adems registrar las DLL con aquel famossimo comando regsvr32 para que no hubiera problemas, an as, algunas veces nos encontrbamos con algunos contratiempos, sin embargo, con Visual Studio 2005, esta forma de trabajar ha desaparecido y ahora el despliegue de una aplicacin es mucho ms sencilla.

Ojo! Cuando desarrollemos una aplicacin, tenga en cuenta otros recursos que utiliza en la misma. Crystal Reports, bases de datos SQL Server, que la mquina cliente disponga de .NET Framework o de la versin mnima de MDAC necesaria, etc. En caso contrario, la ejecucin de nuestra aplicacin, podra no funcionar o provocar algn tipo de excepcin o error. Por ltimo, y aunque parezca de perogrullo, no olvide que debe tener los permisos necesarios para instalar y ejecutar los ensamblados y otras partes de Software, necesarios para ejecutar su aplicacin.

Saul Mamani Mamani

Desarrollo Agil de Software

139

3. GAC y Strong Names


GAC
El GAC o Global Assembly Cache no es otra cosa que un repositorio de ensamblados globales. Imaginemos que usted todos los das cuando llega a casa de trabajar, lo primero que hace es quitarse los zapatos y ponerse una zapatillas cmodas para estar por casa. Lo lgico en este caso, ser situar un zapatero o un pequeo armarito para que ponga ah las zapatillas, ya que todos los das, hace repetitivamente esta operacin. Obviamente, las zapatillas deben de estar ah y no en la otra punta de la casa, pero para estar en ese zapatero, deber cumplir una serie de requisitos que usted mismo exige, por lo que no todos los zapatos o zapatillas deberan estar ah. El GAC funciona de una manera realmente semejante. En el GAC se incluyen aquellas libreras o ensamblados que son utilizados frecuentemente. Si usted va a desarrollar y distribuir su propio ensamblado, convendra ponerlo en el GAC. Una aplicacin .NET, lo primero que hace cuando se ejecuta, es revisar los ensamblados que va a necesitar, y el primer sitio dnde va a ir a buscarlo es en el GAC. Sino lo encuentra, se pondr a buscarlo en el directorio en el que se encuentra el fichero ejecutable, pero esto repercute en el rendimiento. Si nuestras aplicaciones utilizan frecuentemente unos ensamblados, sera lgico y conveniente ponerlos en el GAC. Y cmo se aade un ensamblado al GAC?. La tarea no es sencilla, ya que para aadirlo debemos realizar algunos pasos, entre los que est el crear un nombre fuerte o Strong Name.

Strong Names
Con los Strong Names, aseguramos un uso seguro de los componentes contenidos en el ensamblado. Hay que tener en cuenta que un ensamblado declarado en la GAC que es llamado por varias aplicaciones, crea una nica instancia. De ah, que crear un Strong Name para el ensamblado, est ms que justificado. Un Strong Name, nos asegura por otro lado, que el nombre de un ensamblado es nico y que por lo tanto, al ser nico, no puede ser utilizado por otros ensamblados. Los Strong Names se generan con un par de claves, una de ellas de carcter pblico y otra de carcter privado, claves que se introducen en fichero de ensamblado de la aplicacin, y que luego al compilar nuestra aplicacin, queda registrada con ese Strong Name. Para indicar un Strong Names a un ensamblado, debe crear primero el par de claves (clave pblica y clave privada), que se generar en un fichero con extensin snk, y modificar posteriormente el fichero AssemblyInfo.cs de su proyecto. En ese archivo, debe aadir una instruccin similar a la siguiente:

[assembly: AssemblyKeyFile("KeyFile.snk")]

4. Creando un paquete de instalacin


Setup Project
Otra accin habitual, es que cuando desarrollemos nuestras aplicaciones, generemos diferentes dependencias con otros ensamblados o componentes, para lo cul, la forma ms sencilla de desplegar nuestra aplicacin, es generando un paquete de distribucin que contenga esas dependencias y relaciones.

Nota: Si est utilizando Visual C# 2005 Express no tendr disponible la opcin de proyecto Setup.
Con la plantilla Setup Project crearemos un nuevo proyecto para generar el paquete de distribucin e instalacin de nuestra aplicacin y proyecto.

Saul Mamani Mamani

Desarrollo Agil de Software

140

Cuando generamos el paquete de instalacin, debemos tener en cuenta que se generan dos archivos que por lo general tendrn los nombres de Setup.exe y Setup.msi. La diferencia entre ambos ficheros es que el fichero con extensin exe instalar Windows Installer si es necesario, mientras que el fichero con extensin msi, no instalar Windows Installer, por lo que si Windows Installer no est presente en la mquina en la que se ejecuta el programa de instalacin, dar un error. Otra consideracin a tener en cuenta cuando generamos el paquete de instalacin, es en el momento de la distribucin, el asegurar que el sistema destino tenga el .NET Framework correspondiente.

Sugerencia: Cmo usar Visual Studio 2005 para distribuir Microsoft .NET Framework? Ms que interesante artculo dnde obtendr informacin adicional sobre cmo generar un paquete de instalacin para distribuir .NET Framework.
Respecto al comportamiento de Windows Installer sobre aplicaciones ya instaladas que deseamos desinstalar, el entorno se comporta de manera tal, que si detecta componentes compartidos, estos no son desinstalados del sistema. Otro comportamiento de Windows Installer es el que nos permite echar marcha atrs si el proceso de instalacin se cancela o falla por algn motivo. Windows Installer dejar el sistema en el mismo estado que tena justo antes de realizar la instalacin.

Tipos de despliegues de proyectos


Cuando generamos un paquete de instalacin tenemos dentro del entorno de desarrollo Visual Studio varias opciones, como se indica en la imagen 1. As, nos podemos encontrar por lo general con diferentes tipos de despliegues de proyectos.

Tipos de despliegue de proyectos en Visual Studio

Figura 1 La plantilla Cab Project El fichero con extensin CAB es un fichero comprimido que contiene todos los ficheros y recursos necesarios para instalar nuestra aplicacin. Por lo general, este tipo de ficheros son usados para descargarlos de Servidores Web. Por lo general y en nuestro caso, crearemos aplicaciones Windows, y deberamos entonces utilizar la plantilla Setup Project.

Saul Mamani Mamani

Desarrollo Agil de Software

141

Si utilizamos la plantilla, Merge Module Project, crearemos un paquete instalacin para componentes compartidos. El uso de la plantilla, Web Setup Project por su parte, nos permitir generar un paquete de instalacin para aplicaciones basadas en Internet o aplicaciones Web. La plantilla, SetJup Wizard genera un asistente para generar uno de los cuatro anteriores tipos de proyectos de despliegue. Es una forma rJpida de generar el proyecto de despliegue.

5. Otras consideraciones
Setup Project
Ya lo hemos comentado por encima en los captulos anteriores sobre el Despliegue de aplicaciones, pero cuando se procede a instalar y distribuir una aplicacin en otros sistemas, se deben tener en cuenta diferentes aspectos que no podemos pasar por alto. Debemos conocer en primer lugar, la naturaleza del sistema o sistemas destino dnde vamos a implantar nuestro programa. Entendiendo y conociendo bien esto, podremos saber qu aplicaciones Software adicionales necesitaremos para abordar nuestro trabajo adecuadamente. Entre otros, debemos tener en cuenta que el sistema destino dnde se va a ejecutar nuestra aplicacin dispone de los drivers de acceso a datos adecuados, MDAC (versin 2.6 superior), y por supuesto de Microsoft .NET Framework.

FAQ: Cuando instalar con Windows Installer y cuando a travs de XCOPY? Interesante artculo que debate sobre cuando instalar con Windows Installer y cuando con XCOPY.
El anterior artculo, nos da un enfoque sobre qu tipo de instalacin o despliegue establecer de nuestra aplicacin desarrollada con Visual Studio 2005.

El concepto ClickOnce
En Visual Studio 2005 se introduce un concepto nuevo denominado ClickOnce. Este concepto representa la tecnologa que permite instalar y ejecutar aplicaciones Windows desde un servidor Web con una escasa accin por parte del usuario. Inicie un nuevo proyecto Windows. Dentro del formulario Windows inserte un control Button dentro del cul, inserte un texto identificativo como el que se muestra en la figura 1.

Aplicacin Windows de ejemplo para explicar el concepto de ClickOnce

Figura 1

Saul Mamani Mamani A continuacin, escriba el siguiente cdigo fuente: public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click( object sender, EventArgs e ) { MessageBox.Show("Ejemplo ClickOnce ejecutado a las:\n" + DateTime.Now.ToLongTimeString()); } }

Desarrollo Agil de Software

142

Una vez que ha escrito el cdigo fuente de la aplicacin, ejectela y compruebe que funciona como se espera. Si lo desea, abra el fichero AssemblyInfo.cs e indique la versin de la aplicacin que desee. En mi caso he dejado la versin 1.0.0.0 que es la versin que aparece por defecto. Despus de esto, genere la aplicacin como se indica en la figura 2.

Generamos la aplicacin para comprobar entre otras cosas que todo est preparado

Figura 2 Por ltimo, pulse el botn secundario del mouse sobre el proyecto y seleccione la opcin Publicar... como se indica en la figura 3.

Opcin de Publicar la aplicacin para ejecutar la tecnologa de distribucin ClickOnce

Figura 3

Saul Mamani Mamani

Desarrollo Agil de Software

143

Al ejecutar la opcin Publicar..., el entorno nos muestra una ventana similar a la que se muestra en la figura 4.

Asistente de la publicacin de la aplicacin

Figura 4 Seleccione una ubicacin para publicar la aplicacin y haga clic en el botn Siguiente. Aparecer en el asistente entonces, una ventana similar a la que se presenta en la figura 5.

Ventana del asistente para indicar dnde estar disponible la aplicacin

Figura 5

Saul Mamani Mamani Pulse el botn Siguiente.

Desarrollo Agil de Software

144

Nuevamente, el asistente mostrar una ltima ventana similar a la que se muestra en la figura 6.

Ventana final del asistente con un resumen de las opciones seleccionadas

Figura 6 El asistente muestra en este caso, una informacin de resumen. Lo que haremos a continuacin, ser presionar el botn Finalizar. Con esta accin, nuestra aplicacin est ya publicada en el servidor Web, por lo que si abrimos una ventana de nuestro explorador Web y escribimos la direccin en la cul hemos publicado nuestra aplicacin, sta se ejecutar de forma correcta como se indica en la figura 7.

Saul Mamani Mamani

Desarrollo Agil de Software

145

Aplicacin Web del lanzamiento de la aplicacin Windows a travs del Servidor Web

Figura 7 Si presionamos sobre el botn Instalar del navegador Web, observaremos que el Servidor Web realiza diferentes acciones de verificacin. La primera accin es una accin de conexin como la que se muestra en la figura 8.

La primera accin que se realiza es una accin de conexin con el Servidor Web

Figura 8 Posteriormente, puede aparecer una ventana de seguridad como la que se indica en la figura 9, siempre y cuando no hayamos realizado un proceso de generacin segura o confiable de la aplicacin, como ha sido el caso.

Saul Mamani Mamani

Desarrollo Agil de Software

146

La aplicacin Windows ejecutada a travs del Servidor Web, debe ser confiable y segura

Figura 9 En nuestro caso, como sabemos que no hay problema en ejecutar la aplicacin, presionaremos el botn Instalar. Nuestra aplicacin en ejecucin es la que se muestra en la figura 10.

Aplicacin Windows en ejecucin

Figura 10 Cuando hacemos esto, siempre que ejecutemos la aplicacin, el sistema detectar que aceptamos una vez su seguridad, por lo que siempre se ejecutar sin indicarnos ningn mensaje de seguridad. Ahora bien, supongamos que decidimos modificar parte del cdigo de nuestra aplicacin y que por supuesto, cambiamos la versin de la misma. Acuda antes a la ventana del Explorador de soluciones y observe que se ha aadido en la ventana un fichero de nombre WindowsApplication10_TemporaryKey.pfx que corresponde a una llave o clave temporal relacionada con el proyecto publicado. Esto es lo que se muestra en la figura 11.

Saul Mamani Mamani

Desarrollo Agil de Software

147

Ventana del Explorador de soluciones con el fichero WindowsApplication10_TemporaryKey.pfx aadido a l

Figura 11 Vamos a actualizar nuestra aplicacin y la vamos a cambiar la versin, por lo que ahora escribiremos el siguiente cdigo: public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click( object sender, EventArgs e ) { MessageBox.Show("Ejemplo ClickOnce ejecutado a las:\n" + DateTime.Now.ToShortDateString() + "\n" + DateTime.Now.ToLongTimeString()); } } La versin de la aplicacin, no tiene relacin con la versin de publicacin, por ejemplo, la versin de la aplicacin la he modificado a 1.1.0.0, pero el instalador autogenera su propio nmero de versin, el cual podemos cambiar en las propiedades del proyecto, ficha Publish. En la figura 12 podemos ver la ficha Publish de las propiedades del proyecto.

Saul Mamani Mamani

Desarrollo Agil de Software

148

Ventana Web para ejecutar la aplicacin

Figura 12 El siguiente paso que he hecho es compilar la aplicacin y publicarla nuevamente. Una vez hecho esto, acudimos a la pgina Web de la aplicacin y presionamos nuevamente el botn Instalar como se indica en la figura 13.

Saul Mamani Mamani

Desarrollo Agil de Software

149

Ventana Web para ejecutar la aplicacin

Figura 13 La ejecucin de la aplicacin se realizar sin problemas de manera sencilla y controlada. Como vemos, la publicacin de aplicaciones Windows a travs de un Servidor Web, lo que se denomina tecnologa de publicacin ClickOnce, es un proceso de publicacin rpido y sencillo, que aporta grandes ventajas y que en otras versiones de .NET trae consigo algunos inconvenientes superados en esta versin de .NET.

Comunidad dotNet: Si quiere saber ms sobre ClickOnce, le recomiendo la lectura del siguiente artculo, escrito en espaol que complementar y ampliar la informacin de este tutorial.

Saul Mamani Mamani

Desarrollo Agil de Software

150

MODULO 5 ACCESO A DATOS CON ADO.NET Mdulo 5 - Acceso a datos


En este mdulo, aprenderemos a trabajar con datos y fuentes de datos en Visual Studio 2005. ADO.NET es la tecnologa principal para conectarse a una base de datos , nos ofrece un alto nivel de abstraccin, ocultando los detalles de bajo nivel de la implementacin de la base de datos de un fabricante.

En este tutorial, encontrar las cosas ms importantes que debe saber, para trabajar con fuentes de datos con Visual Studio 2005. De esta manera, aprender en poco tiempo, a encontrarse cmodo en el entorno de clases de ADO.NET y podr as, sacar el mximo provecho a sus desarrollos. Las partes que forman parte de este mdulo son las siguientes:

DESCRIPCION DE ADO.NET
Introduccin
A continuacin veremos todo lo que debe saber sobre ADO.NET para crear aplicaciones que accedan a fuentes de datos desde Visual Studio 2005. Comprobar que ahora, es incluso mucho ms fcil y rpido, si bien, es necesario conocer el modelo con el que se trabaja para poder saber lo que deberemos hacer en un momento dado.

1. Acercndonos a ADO.NET
ADO.NET ha sufrido a lo largo de los ltimos aos diferentes mejoras y actualizaciones, desde que .NET apareci. El resumen de las diferentes versiones de ADO.NET podra quedar de la siguiente forma. ADO.NET 1.0 apareci con Microsoft .NET Framework 1.0. Posteriormente, ADO.NET 1.1 sufri una pequeas y casi inapreciables actualizaciones con la aparicin de Microsoft .NET Framework 1.1. En el caso del entorno Visual Studio 2005, ste trabaja con Microsoft .NET Framework 2.0 y por lo tanto, utiliza ADO.NET 2.0, el cul aade algunas caractersticas nuevas adicionales. En nuestro caso, nos centraremos nica y exclusivamente en ADO.NET como modelo de objetos de acceso a datos para la plataforma .NET de Microsoft, ya que es el mismo para cualquier tipo de versin de ADO.NET.

Qu es ADO.NET?
ADO.NET es la tecnologa principal para conectarse aun gestor de bases de datos, con un alto nivel de abstraccin, lo que nos permite olvidearnos de los detalles de bajo nievel de las bases de datos. Adems ADO.NET es una tecnologa interoperativa. Aperte del almacenamiento y recuperacin de dats, ADO.NET introduce la posibilidad de integrarse con el estndar XML, los datos pueden 'Serializarse' directamente a y desde XML lo que favorece el intercambio de informacion. ADO.NET proporciona diferentes clases del nombre de espacio System.Data dentro de las cules, destacaremos por encima de todas, la clase DataView, la clase DataSet y la clase DataTable. Este conjunto de clases de carcter armnico, funcionan de igual forma con la capa inferior que es la que corresponde a los proveedores de acceso a datos con los que podemos trabajar. Esto facilita el trabajo en n-capas y la posible migracin de aplicaciones que utilicen una determinada fuente de datos y deseemos en un momento dado, hacer uso de otra fuente de datos.

Qu capas o qu partes hay dentro de ADO.NET?


Dentro de ADO.NET tenemos dos partes importantes. La primera de ellas es la que corresponde con el nombre de espacio System.Data y que constituye los objetos y clases globales de ADO.NET. La otra parte es la que corresponde con los objetos que permiten el acceso a datos a una determinada fuente de datos desde ADO.NET y que utilizan as mismo, las clases del nombre de espacio System.Data. Esta ltima parte, queda constituida por las clases y objetos de los diferentes proveedores de acceso a datos como se muestra en la figura 1.

Saul Mamani Mamani

Desarrollo Agil de Software

151

Visin general de las clases de ADO.NET

Figura 1 Para resumir de alguna forma lo que estamos comentando, diremos que el trabajo de conexin con la base de datos, la ejecucin de una instruccin SQL determinada, una vista, etc., la realiza el proveedor de acceso a datos. Recuperar esos datos para tratarlos, manipularlos o volcarlos a un determinado control o dispositivo, es accin de la capa superior que corresponde con el nombre de espacio System.Data. A continuacin veremos todo esto con ms detalle y comprenderemos de una forma ms clara cada una de las partes que componen el modelo de trabajo con ADO.NET.

Qu nos permite realmente ADO.NET cuando trabajamos con XML?


El entorno de Microsoft .NET Framework nos proporciona el trabajo con estndares y con ello, la posibilidad de trabajar con diferentes tipos de aplicaciones, entornos, sistemas operativos y lenguajes sin necesidad de conocer lo que hay al otro lado de nuestra aplicacin. XML es sin lugar a dudas, el lenguaje de etiquetas por excelencia, vlido para llevar a cabo esta tarea sin tener un impacto relevante cuando trabajamos con diferentes soluciones en entornos dispares. Tanto la posibilidad de trabajar con Servicios Web XML como con documentos e informacin en XML, sobre todo al trabajar con fuentes de datos en ADO.NET, nos proporciona a los desarrolladores las posibilidades necesarias que nos permite hacer que la informacin con la que trabajamos, pueda ser tratada entre diferentes sistemas o entornos, sin que por ello nos preocupemos de lo que hay al otro lado.

2. System.Data
Las clases del nombre de espacio System.Data son bastantes extensas y variadas. Quizs las clases ms importantes son la clase DataView, la clase DataSet y la clase DataTable.

La clase DataSet
El DataSet es una representacin de datos residente en memoria que proporciona una modelo de programacin relacional coherente independientemente del origen de datos que contiene. El DataSet contiene en s, un conjunto de datos que han sido volcados desde el proveedor de datos. Debemos tener en cuenta que cuando trabajamos con DataSets, el origen de datos no es lo ms importante, ya que ste, puede ser cualquier tipo de origen de datos. No tiene porqu ser una base de datos. Un DataSet contiene colecciones de DataTables y DataRelations. El DataTable contiene una tabla o tablas, mientras que la DataRelation contiene las relaciones entre las DataTables. Sin embargo, no es necesario especificar todo esto hasta el ltimo detalle como veremos ms adelante.

La clase DataView

Saul Mamani Mamani

Desarrollo Agil de Software

152

Este objeto nos permite crear mltiples vistas de nuestros datos, adems de permitirnos presentar los datos. Es la clase que nos permite representar los datos de la clase DataTable, permitindonos editar, ordenar y filtrar, buscar y navegar por un conjunto de datos determinado.

La clase DataTable
Este objeto nos permite representar una determinada tabla en memoria, de modo que podamos interactuar con ella. A la hora de trabajar con este objeto, debemos tener en cuenta el nombre con el cul definamos una determinada tabla, ya que los objetos declarados en en el DataTable es sensitivo a maysculas y minsculas.

Un pequeo ejemplo prctico


El siguiente ejemplo prctico, nos ensea a utilizar un DataSet y nos muestra como podemos acceder a los objetos que dependen de un DataSet para recuperar por ejemplo, los campos y propiedades de una determinada tabla o tablas. using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Data.SqlClient; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Xml; namespace WindowsApplication { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { string Conexion = "server=.;uid=sa;password=VisualBasic;database=MSDNVideo"; DataSet MiDataSet = new DataSet(); SqlDataAdapter MiAdaptador = new SqlDataAdapter("SELECT * FROM alquileres",Conexion); MiAdaptador.Fill(MiDataSet); foreach (DataTable tabla in MiDataSet.Tables) { TextBox1.Text = "Tabla:" + tabla.TableName + "\n"; foreach (DataColumn columna in tabla.Columns) { TextBox1.Text = "Columna:" + columna.ColumnName + "\t" + "Tipo:" + columna.DataType.Name; } } } } } Nuestro ejemplo en ejecucin es el que se muestra en la figura 1.

Saul Mamani Mamani

Desarrollo Agil de Software

153

Ejemplo en ejecucin del uso de DataSet, DataTable y DataColumn

Figura 1

3. Los proveedores de acceso a datos


Los proveedores de acceso a datos es la capa inferior de la parte correspondiente al acceso de datos y es la responsable de establecer la comunicacin con las fuentes de datos. En este conjunto de nombres de espacio, encontraremos casi siempre las clases Connection, Command, DataAdapter y DataReader como las clases ms generales, las cuales nos permiten establecer la conexin con la fuente de datos.

Proveedores de acceso a datos de .NET Framework


Dentro del entorno .NET Framework, encontramos un nutrido conjunto de proveedores de acceso a datos. Estos son los siguientes: ODBC .NET Data Provider OLE DB .NET Data Provider Oracle Client .NET Data Provider SQL Server .NET Data Provider

Estos proveedores de acceso a datos incluidos en Microsoft .NET Framework, los podemos encontrar en los nombres de espacio: System.Data.Odbc System.Data.OleDb System.Data.OracleClient System.Data.SqlClient

El proveedor ODBC .NET permite conectar nuestras aplicaciones a fuentes de datos a travs de ODBC. El proveedor OLE DB .NET permite conectar nuestras aplicaciones a fuentes de datos a travs de OLE DB. El proveedor Oracle Client .NET es un proveedor de acceso a datos especialmente diseado para bases de datos Oracle. Por ltimo, el proveedor SQL Server .NET es un proveedor de acceso a datos nativo, que nos permite conectar nuestras aplicaciones a fuentes de datos Microsoft SQL Server 7 o posterior. Se trata de un proveedor especfico para bases de datos Microsoft SQL Server 7.0, Microsoft SQL Server 2000 y Microsoft SQL Server 2005.

Consejo: Siempre que pueda, utilice para acceder a fuentes de datos, un proveedor de acceso a datos nativo. Esto le permitir aumentar considerablemente el rendimiento a la hora de establecer la conexin con una determinada fuente de datos

Saul Mamani Mamani

Desarrollo Agil de Software

154

Los proveedores de acceso a datos que distribuye Microsoft en ADO.NET y algunos desarrollados por otras empresas o terceros, contienen los mismos objetos, aunque los nombres de stos, sus propiedades y mtodos, pueden ser diferentes. Ms adelante veremos algn ejemplo, y observar en la prctica cules son estas diferencias ms destacables.

Otros proveedores de acceso a datos


Si bien el proveedor de acceso a datos es el mecanismo a travs del cul podemos establecer una comunicacin nativa con una determinada fuente de datos, y dado que Microsoft proporciona los proveedores de acceso a datos ms corrientes, es cierto que no los proporciona todos, si bien, con OLE DB y ODBC, podemos acceder a la inmensa totalidad de ellos. Sin embargo, hay muchos motores de bases de datos de igual importancia como Oracle, MySql, AS/400, etc. En estos casos, si queremos utilizar un proveedor de acceso a datos nativo, deberemos acudir al fabricante o a empresas o iniciativas particulares para que nos proporcionen el conjunto de clases necesarias que nos permitan abordar esta accin. El objeto Connection Este objeto es el encargado de establecer una conexin fsica con una base de datos determinada. Para establecer la conexin con una determinada fuente de datos, no slo debemos establecer la cadena de conexin correctamente, sino que adems deberemos usar los parmetros de conexin y el proveedor de acceso a datos adecuado. Con este objeto, podremos adems abrir y cerrar una conexin.

El objeto Command
Este objeto es el que representa una determinada sentencia SQL o un Stored Procedure. Aunque no es obligatorio su uso, en caso de necesitarlo, lo utilizaremos conjuntamente con el objeto DataAdapter que es el encargado de ejecutar la instruccin indicada.

El objeto DataAdapter
Este objeto es quizs el objeto ms complejo y a la vez complicado de todos los que forman parte de un proveedor de acceso a datos en .NET. Cuando deseamos establecer una comunicacin entre una fuente de datos y un DataSet, utilizamos como intermediario a un objeto DataAdapter. A su vez, un DataAdapter contiene 4 objetos que debemos conocer: SelectCommand es el objeto encargado de realizar los trabajos de seleccin de datos con una fuente de datos dada. En s, es el que se encarga de devolver y rellenar los datos de una fuente de datos a un DataSet. DeleteCommand es el objeto encargado de realizar las acciones de borrado de datos. InsertCommand es el objeto encargado de realizar las acciones de insercin de datos. UpdateCommand es el objeto encargado de realizar las acciones de actualizacin de datos.

Los objetos DeleteCommand, InsertCommand y UpdateCommand son los objetos que se utilizan para manipular y transmitir datos de una fuente de datos determinada, al contrario del objeto SelectCommand que tan slo interacta con la fuente de datos para recuperar una porcin o todos los datos indicados en el objeto Command anteriormente comentado.

El objeto DataReader
Este objeto es el utilizado en una sola direccin de datos. Se trata de un objeto de acceso a datos muy rpido. Este objeto puede usar a su vez el objeto Command o el mtodo ExecuteReader.

4. El concepto DataBinding
DataBinding es una expresin de enlace a datos. Como veremos a continuacin es una forma rpida y sencilla de manejar la fuentes de datos mediante su enlace con controles o clases..

Saul Mamani Mamani

Desarrollo Agil de Software

155

El uso de DataBind
El mtodo DataBind se utiliza para rellenar de datos un determinado control o clase. Muchos controles y clases, posee este mtodo al que le asignaremos un conjunto de datos para que se rellene con ellos, pudiendo despus interactuar con los datos de forma directa. En s, un DataBinding es un enlace a datos que se encarga de rellenar de datos a un determinado control o clase. Como ejemplo de esto, veremos como rellenar un control TextBox con un dato utilizando este mtodo. Iniciaremos una nueva aplicacin Windows y escribiremos el siguiente cdigo fuente: using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Data.SqlClient; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Xml; namespace WindowsApplication { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { string CadenaConexion = "server=.;uid=sa;password=VisualBasic;database=MSDNVideo"; SqlConnection MiConexion = new SqlConnection(CadenaConexion); DataSet MiDataSet = new DataSet(); SqlDataAdapter MiAdaptador = new SqlDataAdapter("SELECT TITULO FROM ALQUILERES, PELICULAS " + "WHERE PELICULACODBARRAS = CODBARRAS AND SOCIONIF = '111111'", MiConexion); MiAdaptador.Fill(MiDataSet,"Pelis"); textBox1.DataBindings.Add("Text", MiDataSet, "Pelis.Titulo"); } } } Nuestro ejemplo en ejecucin es el que se muestra en la figura 1.

Ejemplo en ejecucin del uso de DataBinding

Figura 1

5. Otras consideraciones
Dentro de las conexiones a fuentes de datos, hay algunas partes de stas que permanecen a veces en el olvido y su importancia sin embargo, es bastante grande.

Saul Mamani Mamani

Desarrollo Agil de Software

156

La accin ms pesada cuando realizamos un acceso a una fuente de datos, se encuentra en la conexin con la fuente de datos. Esa tarea, simple tarea, es la que ms recursos del sistema consume cuando accedemos a fuentes de datos. Esto lo debemos tener en cuenta, y por lo tanto, variante de esto que comentamos son las siguientes premisas: La conexin debe realizarse siempre que se pueda, con los proveedores de acceso a datos nativos, que por lo general salvo raras excepciones, sern ms rpidos que los accesos a fuentes de datos a travs de proveedores del tipo OLE DB y ODBC. La conexin con la fuente de datos (apertura de la conexin), debe realizarse lo ms tarde posible. Es recomendable definir todas las variables que podamos, antes de realizar la conexin. La conexin debe cerrarse lo antes posible, siempre y cuando no tengamos la necesidad de utilizar la conexin previamente abierta. Hay ms particularidades a tener en cuenta cuando trabajamos con fuentes de datos. El hecho de que con un DataSet podamos trabajar con datos desconectados, no significa que dentro de l, podamos abrir una tabla con una cantidad de registros enormes, y trabajemos sobre ella creyendo que esto nos beneficiar. Todo lo contrario.

ACCESO CONECTADO A BASE DE DATOS


Introduccin
Ahora que ya tenemos un poco ms clara la estructura del modelo de acceso a datos ADO.NET, podemos adentrarnos en todo lo que rodea al acceso conectado de base de datos y la manipulacin y trabajo de datos conectados. A continuacin, encontrar el ndice detallado de este captulo.

1. El paradigma de la conexin
Cuando abordamos un proyecto de acceso a fuentes de datos, siempre nos encontramos con una duda existencial. Debemos crear una conexin con la base de datos al principio de nuestra aplicacin y cerrarla cuando la aplicacin se cierre?, o debemos crear una conexin con la base de datos slo cuando vayamos a trabajar con la fuente de datos?. Y si estamos trabajando continuamente con una fuente de datos?, cmo penalizaran todas estas acciones?. Es difcil de asumir que accin tomar en cada caso, y es que dependiendo de lo que vayamos a realizar, a veces es ms efectiva una accin que otra, y en otras ocasiones, no est del todo claro, ya que no existe en s una regla clara que especifique qu accin tomar en un momento dado. Lo que s est claro es que el modelo de datos de ADO.NET que hemos visto, quedara resumido en cuanto a la conectividad de la manera en la que se representa en la figura 1.

Visin general de ADO.NET respecto a la conectividad con bases de datos

Figura 1 El objeto DataSet nos ofrece la posibilidad de almacenar datos, tablas y bases de datos de una determinada fuente de datos. De esta manera, podemos trabajar con las aplicaciones estando desconectados de la fuente de datos. Sin embargo, a veces necesitamos trabajar con la fuente de datos estando conectados a ella. El objeto DataReader nos ofrece precisamente la posibilidad de trabajar con fuentes de datos conectadas.

Saul Mamani Mamani

Desarrollo Agil de Software

157

Por otro lado, el objeto DataReader tiene algunas particularidades que conviene conocer y que veremos a continuacin.

2. Conociendo el objeto DataReader


El objeto DataReader nos permite como hemos indicado anteriormente, establecer una conexin con una fuente de datos y trabajar con esta fuente de datos sin desconectarnos de ella, sin embargo, hay diferentes cualidades y particularidades que conviene conocer.

DataReader es de solo lectura


Lo que hemos dicho anteriormente, requiere sin embargo, que esta conexin se establezca en un modo de slo lectura, al contrario de lo que se puede hacer con el objeto DataSet, con el que podemos interactuar con la fuente de datos en modo lectura y modo escritura.

DataReader se maneja en una sola direccin


El objeto DataReader slo permite que nos desplacemos por los datos en una sola direccin, sin vuelta atrs. Por el contrario, el objeto DataSet nos permite movernos por los registros para adelante y para atrs. Adems, slo podemos utilizar el objeto DataReader con conexiones establecidas en una sentencia SQL por ejemplo, pero no podemos variar esta. Para hacerlo, debemos entonces modificar la conexin con el comando establecido.

DataReader es rpido
Debido a su naturaleza y caractersticas, este objeto es bastante rpido a la hora de trabajar con datos. Como es lgico, consume adems menos memoria y recursos que un objeto DataSet por ejemplo. Sin embargo, dependiendo de las necesidades con las que nos encontremos, puede que este mtodo de acceso y trabajo no sea el ms idneo.

Analizando el flujo de trabajo de DataReader


Cuando trabajamos con fuentes de datos conectadas, trabajaremos con el objeto DataReader. Para trabajar con este objeto, utilizaremos los objetos siguientes del proveedor de acceso a datos: Connection Command DataReader Un resumen grfico de esto es lo que podemos ver en la figura 1.

El flujo de conectividad de DataReader

Figura 1

3. Un primer contacto con el objeto DataReader


A continuacin veremos un ejemplo sencillo sobre la forma de trabajar con DataReader

Un ejemplo simple para entenderlo mejor

Saul Mamani Mamani

Desarrollo Agil de Software

158

Tenga en cuenta que en el siguiente ejemplo nos conectaremos a Microsoft SQL Server y recorreremos los registros uno a uno en un ambiente conectado y volcaremos estos registros en un control TextBox. using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Data.SqlClient; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Xml; namespace WindowsApplicationTemplate { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { string CadenaConexion = "server=.;uid=sa;password=VisualBasic;database=MSDNVideo"; SqlConnection MiConexion = new SqlConnection(CadenaConexion); SqlCommand MiComando = new SqlCommand("SELECT TITULO FROM ALQUILERES, PELICULAS " + "WHERE PELICULACODBARRAS = CODBARRAS AND SOCIONIF = '111111'", MiConexion); MiConexion.Open(); SqlDataReader MiDataReader = MiComando.ExecuteReader(); while (MiDataReader.Read()) { textBox1.Text += MiDataReader["titulo"].ToString(); } MiConexion.Close(); } } } El cdigo de ejemplo en ejecucin es el que se muestra en la figura 1.

Ejemplo en ejecucin del uso simple de DataReader

Figura 1 Este es un ejemplo simple del uso de DataReader. Sin embargo, el objeto DataReader contiene un conjunto de propiedades y mtodos que nos proporcionan acciones determinadas. Por ejemplo, en el ejemplo anterior, hemos dado por hecho que la ejecucin de la instruccin Select nos devolver uno o ms valores, pero podramos tambin saber antes de manipular y trabajar con los posibles datos, si hay o no

Saul Mamani Mamani informacin. Esto lo conseguimos con el mtodo HasRows.

Desarrollo Agil de Software

159

4. Trabaja DataReader en un ambiente conectado realmente?


Pese a todo esto, que ocurre si trabajando en un ambiente conectado se desconecta el servidor de acceso a datos?. Imaginemos por un instante, que la conexin con SQL Server se establece correctamente y que en un momento dado se detiene el servicio del servidor de base de datos. Esto es lo que veremos en el siguiente ejemplo.

Desenchufando la fuente de datos usando DataReader


Inicie un nuevo proyecto, inserte en el formulario de la aplicacin un control TextBox y un control Button, y escriba el cdigo que se detalla a continuacin: using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Data.SqlClient; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Xml; namespace WindowsApplication { public partial class Form1 : Form { SqlConnection MiConexion = new SqlConnection("server=.;uid=sa;password=VisualBasic;database=MSDNVideo"); SqlDataReader MiDataReader; string strSql = "SELECT TITULO FROM ALQUILERES, PELICULAS WHERE PELICULACODBARRAS = CODBARRAS AND SOCIONIF = '111111'"; long contador = 0; long posicion = 0; public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { Establecer_Conexion(true); if (!MiDataReader.HasRows) button1.Enabled = false; else button1_Click(sender, e); } private void Establecer_Conexion(bool Accion) { SqlCommand MiComando; if (Accion) { //Creamos el comando MiComando = new SqlCommand(strSql, MiConexion); //Abrimos la conexion MiConexion.Open(); //Ejecutamos la sentencia SQL MiDataReader = MiComando.ExecuteReader(); //Obtenermos la cantidad de registros obtenidos contador = MiDataReader.VisibleFieldCount + 1; } else { button1.Enabled = false; //Cerramos la conexion MiConexion.Close();

Saul Mamani Mamani

Desarrollo Agil de Software

160

} } private void button1_Click(object sender, EventArgs e) { //Recorremos los registros y los mostramos posicion += 1; MiDataReader.Read(); textBox1.Text = MiDataReader["titulo"].ToString(); //Si hemos recorrido todos los registros finalizamos la conexion if (posicion == contador) Establecer_Conexion(false); } } } Suponiendo que tenemos en nuestra base de datos varios registros, ejecute la aplicacin. Si todo ha ido como se esperaba, observaremos que nuestra aplicacin tiene un aspecto como el que se muestra en la figura 1.

Ejemplo en ejecucin del uso de DataReader en un ambiente conectado, forzando la desconexin de la fuente de datos

Figura 1 En este punto, detenga el servicio de SQL Server y pulse el botn Siguiente >>. Observar que la aplicacin sigue funcionando. En este punto se har la pregunta que todos nos hemos hecho, no es el objeto DataReader un objeto conectado?, cmo es posible que funcione si hemos detenido el servicio de SQL Server?. La respuesta es sencilla. El objeto DataReader recupera un nutrido conjunto de valores llenando un pequeo buffer de datos e informacin. Si el nmero de registros que hay en el buffer se acaban, el objeto DataReader regresar a la fuente de datos para recuperar ms registros. Si el servicio de SQL Server est detenido en ese momento o en su caso, la fuente de datos est parada, la aplicacin generar un error a la hora de leer el siguiente registro. En s, DataReader es un objeto conectado, pero trabaja en background con un conjunto de datos, por lo que a veces nos puede resultar chocante su comportamiento como el ejemplo que comento.

5. Usando DataSource con DataReader


Podemos usar el mtodo DataSource con el objeto DataReader?.

Demostracin del uso de DataSource con DataReader


La respuesta es s, en ADO.NET 2.0, se ha incorporado un nuevo mtodo al objeto DataTable que le permite tener mayor independencia respecto al modo en el que nos conectemos y recuperemos datos de una fuente de datos.

Saul Mamani Mamani

Desarrollo Agil de Software

161

Recuerde que podemos recuperar datos en modo conectado DataReader o en modo desconectado DataSet. Este mtodo que se ha incorporado a ADO.NET y que tiene por nombre Load, nos permite cargar un DataReader para volcarlo a continuacin dentro de un control como por ejemplo el control DataGridView. Lo mejor es que veamos como funciona esto con un ejemplo que nos ayude a comprender mejor la teora. Inserte en un formulario un control DataGridView y escriba el siguiente cdigo: using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Data.SqlClient; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Xml; namespace WindowsApplication { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { SqlConnection MiConexion = new SqlConnection("server=.;uid=sa;password=VisualBasic;database=MSDNVideo"); string strSql = "SELECT TITULO FROM ALQUILERES, PELICULAS WHERE PELICULACODBARRAS = CODBARRAS AND SOCIONIF = '111111'"; SqlCommand MiComando = new SqlCommand(strSql, MiConexion); SqlDataReader MiDataReader; DataTable MiTabla = new DataTable(); //Abrimos la conexion MiConexion.Open(); //Ejecutamos la instruccion SQL MiDataReader = MiComando.ExecuteReader(); //Cargamos en la tabla la lectura del DataReader MiTabla.Load(MiDataReader, LoadOption.OverwriteChanges); //Volcamos los datos en el dataGridView dataGridView1.DataSource = MiTabla; //Cerramos la conexion MiConexion.Close(); } } } Nuestro ejemplo en ejecucin es el que podemos ver en la figura 1.

Ejemplo en ejecucin del uso de DataReader y DataSource en un control DataGridView

Saul Mamani Mamani Figura 1

Desarrollo Agil de Software

162

Con todo y con esto, lo que realmente es curioso, es que hemos olvidado por un instante que el objeto DataReader es un objeto de slo lectura que funciona en una nica direccin, hacia delante. Qu significa esto o como puede influir o como podemos aprovechar esta circunstancia en nuestros desarrollos?.

Carga segmentada de datos con DataSource y DataReader


Si recuperamos los datos de una fuente de datos con DataReader y leemos algunos de sus datos y posteriormente, ejecutamos el mtodo DataSource, el resto de datos, aquellos datos que quedan en el DataReader, sern los que se vuelquen en el control que definamos como destino de los datos. Imaginemos el ejemplo anterior, y el siguiente cdigo fuente. using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Data.SqlClient; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Xml; namespace WindowsApplication { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { SqlConnection MiConexion = new SqlConnection("server=.;uid=sa;password=VisualBasic;database=MSDNVideo"); string strSql = "SELECT TITULO FROM ALQUILERES, PELICULAS WHERE PELICULACODBARRAS = CODBARRAS AND SOCIONIF = '111111'"; SqlCommand MiComando = new SqlCommand(strSql, MiConexion); SqlDataReader MiDataReader; DataTable MiTabla = new DataTable(); //Abrimos la conexion MiConexion.Open(); //Ejecutamos la instruccion SQL MiDataReader = MiComando.ExecuteReader(); //Leemos el primer registro por lo que nos posicionamos en el segundo MiDataReader.Read() //Cargamos en la tabla la lectura del DataReader MiTabla.Load(MiDataReader, LoadOption.OverwriteChanges); //Volcamos los datos en el dataGridView dataGridView1.DataSource = MiTabla; //Cerramos la conexion MiConexion.Close(); } } }

En este caso, lo que ocurre como ya hemos comentado, es que los datos que se cargan son los que an no han sido ledos en el objeto DataReader, por lo que se mostrarn todos los datos desde el ltimo ledo hasta llegar al final del objeto.

Saul Mamani Mamani

Desarrollo Agil de Software

163

6. Usando los componentes de acceso a datos de .NET


Los componentes del entorno .NET nos proporcionan las caractersticas necesarias para poder acceder a fuentes de datos de forma rpida y sencilla. El mejor ejemplo de esto que comento es el que veremos a continuacin.

Demostracin del uso de BindingSource y BindingNavigator


Para ello, crearemos un proyecto nuevo e insertaremos un control BindingSource y un control BindingNavigator dentro del formulario. Tambin insertaremos un control TextBox al formulario, dnde presentaremos la informacin sobre la que navegaremos. Nuestro formulario con los controles insertados en l, tendr un aspecto similar al que se presenta en la figura 1.

Controles de navegacin y acceso a datos dispuestos en el formulario

Figura 1 Una vez llegado a este punto, lo que tendremos que hacer a continuacin ser escribir el cdigo fuente necesario para poder representar los datos de la sentencia SQL en el control BingindNavigator, para que a su vez los presente en el control TextBox. A continuacin se indica el cdigo fuente de esta parte de demostracin de la aplicacin. using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Data.SqlClient; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Xml;

Saul Mamani Mamani

Desarrollo Agil de Software

164

namespace WindowsApplication { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { SqlConnection MiConexion = new SqlConnection("server=.;uid=sa;password=VisualBasic;database=MSDNVideo"); string strSql = "SELECT TITULO FROM ALQUILERES, PELICULAS WHERE PELICULACODBARRAS = CODBARRAS AND SOCIONIF = '111111'"; SqlCommand MiComando = new SqlCommand(strSql, MiConexion); SqlDataReader MiDataReader; DataTable MiTabla = new DataTable(); //Abrimos la conexion MiConexion.Open(); //Ejecutamos la instruccion SQL MiDataReader = MiComando.ExecuteReader(); //Cargamos en la tabla la lectura del DataReader MiTabla.Load(MiDataReader, LoadOption.OverwriteChanges); //Volcamos los datos en el control TextoBox bindingSource1.DataSource = MiTabla; bindingNavigator1.BindingSource = bindingSource1; textBox1.DataBindings.Add("Text", bindingSource1, "Titulo", true); //Cerramos la conexion MiConexion.Close(); } } }

ACCESO DESCONECTADO: DATA SET Y DATA ADAPTER


Introduccin
Ya tenemos claro el funcionamiento con fuentes de datos conectadas, sin embargo, trabajar con datos conectados slo es necesario en algunos mbitos, lo ms habitual, ser que nos encontremos trabajando con ambientes y accesos a datos desconectados, como ocurrir en la inmensa mayora de la veces. A continuacin, aprender a utilizar el DataSet y DataAdapter para sacar el mximo provecho a un ambiente desconectado de datos. El ndice detallado de este captulo es el que se indica a continuacin.

1. Esquema general de la estructura desconectada de acceso a datos


En los captulos anteriores de este mdulo, hemos visto ya el uso de la clase DataSet. Incluso lo hemos visto con algn ejemplo. La clase DataSet est pensada y diseada para trabajar con fuentes de datos desconectadas. Indudablemente, en este punto, debemos tener clara la estructura general de cmo funciona el acceso desconectado con fuentes de datos. En la figura 1, podemos observar el diagrama general de esta parte

Saul Mamani Mamani

Desarrollo Agil de Software

165

Estructura general del uso de DataSet en el acceso desconectado a datos

Figura 1

Connection, DataAdapter y DataSet


Como podemos observar en la figura 1, para comunicarnos con una fuente de datos, siempre deberemos establecer una conexin, independientemente de si la conexin con la fuente de datos va a permanecer a lo largo del tiempo o no. El objeto Connection nos permite por lo tanto, establecer la conexin con la fuente de datos. El objeto DataSet nos permite por otro lado, recoger los datos de la fuente de datos y mandrselos a la aplicacin. Entre medias de estos dos objetos, encontramos el objeto DataAdapter que hace las funciones de puente o nexo de unin entre la conexin y el objeto DataSet. Esto es lo que veremos a continuacin, como funciona el objeto DataAdapter, y como encaja todo esto en el acceso a fuentes de datos desconectadas.

2. Conociendo el objeto DataAdapter


El objeto DataAdapter forma parte del proveedor de acceso a datos, tal y como se muestra en la figura 1.

Visin general de las clases de ADO.NET

Figura 1 Cada proveedor de acceso a datos posee su propio objeto DataAdapter. Cuando realizamos alguna modificacin o accin sobre la fuente de datos, utilizaremos siempre el objeto DataAdapter a caballo entre el objeto DataSet y la fuente de datos establecida a travs de la conexin con el objeto Connection. Con el objeto DataAdapter, podremos adems realizar diferentes acciones sobre nuestras bases de datos, acciones como la ejecucin general de sentencias de SQL no slo para seleccionar un conjunto de datos, sino para alterar el contenido de una base de datos o de sus tablas.

Connection, DataAdapter y DataSet

Saul Mamani Mamani

Desarrollo Agil de Software

166

Antes de entrar en materia ms profundamente, diremos que en lo que respecta a los proveedores de acceso a datos que vienen integrados con .NET, encontramos dos formas de usar un DataAdapter. La primera de ellas es utilizando los componentes del proveedor de acceso a datos. La segunda de ellas es utilizando las clases del nombre de espacio del proveedor de acceso a datos. La mejor forma de entender todo esto que comentamos, es trabajando con un ejemplo prctico que nos ensee a usar el objeto DataAdapter en Visual Studio 2005.

Utilizando las clases de .NET


En este primer ejemplo de demostracin del uso de DataAdapter a travs de cdigo usando para ello las clases de .NET, estableceremos una conexin con SQL Server y mostraremos los datos recogidos en un control TextBox. Iniciaremos Visual Studio 2005 y seleccionaremos un proyecto de formulario de Windows. Dentro del formulario, insertaremos un control TextBox y aadiremos dos referencias al proyecto. Las referencias aadidas sern a las libreras System.Data y System.XML, como se muestra en la figura 2.

Referencias a las clases de acceso a datos de .NET

Figura 2 Una vez que hemos aadido las referencias necesarias para utilizar las clases que queremos en nuestro proyecto, iremos al cdigo y escribiremos las siguientes instrucciones: using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Data.SqlClient; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Xml; namespace WindowsApplication { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) {

Saul Mamani Mamani

Desarrollo Agil de Software

167

SqlConnection MiConexion = new SqlConnection("server=.;uid=sa;password=VisualBasic;database=MSDNVideo"); string strSql = "SELECT SocioNIF, FechaAlquiler FROM ALQUILERES"; SqlDataAdapter MiAdaptador = new SqlDataAdapter(strSql, MiConexion); DataSet MiDataSet = new DataSet(); MiAdaptador.Fill(MiDataSet,"Alquileres"); //Recorremos todas las filas de la tabla alquileres del DataSet foreach (DataRow Fila in MiDataSet.Tables[0].Rows) textBox1.Text += Fila["SocioNif"].ToString() + "\t" + Fila["FechaAlquiler"].ToString() + "\n"; MiDataSet = null; } } }

El ejemplo en ejecucin del uso de DataAdapter junto con las clases de .NET es el que se muestra en la figura 3.

Ejemplo del acceso a datos con DataAdapter a travs de las clases de .NET

Figura 3

Utilizando los componentes de .NET


Sin embargo y como ya hemos comentado, existe otro mtodo de acceso a fuentes de datos diferente a las clases de .NET, el acceso a travs de componentes que nos faciliten esa tarea. Sin embargo, los componentes de acceso a datos, utilizan por detrs las clases de .NET que hemos visto, lo que ocurre, es que simplifica enormemente el trabajo y ahorra tiempo a la hora de desarrollar aplicaciones. De todos los modos, todo depende de la utilidad o necesidades con las que nos encontremos en un momento dado. Iniciaremos un proyecto Windows nuevamente, e insertaremos en l un control TextBox como hicimos en el caso anterior. A continuacin, aadiremos los componentes de acceso a fuentes de datos SQL Server que es la fuente de datos origen. Como hemos visto, para conectar a fuentes de datos SQL Server, hemos utilizado el nombre de espacio System.Data y hemos importado en el proyecto los nombres de espacio System.Data y System.Data.SqlClient. Los componentes .NET de acceso a fuentes de datos de SQL Server, se identifican por el nombre Sqlxxx, siendo xxx el tipo de componente a utilizar. Para poder utilizarlos, deberemos aadirlo a la barra de herramientas. Para aadir los componentes a nuestro proyecto, haremos doble clic sobre el formulario y posteriormente haremos clic con el botn secundario del mouse sobre la barra de herramientas y seleccionaremos la opcin Elegir elementos... (Choose Items...), como se muestra en la figura 4.

Saul Mamani Mamani

Desarrollo Agil de Software

168

Opcin de la barra de herramientas para aadir componentes al entorno

Figura 4 Una vez que hemos hecho esto, seleccionaremos los componentes SqlCommand, SqlCommandBuilder, SqlConnection, SqlDataAdapter y SqlDataSource, tal y como se muestra en la figura 5.

ponentes a aadir al entorno

Figura 5 Una vez que hemos aadido los componentes al entorno, estos quedarn dispuestos dentro de la barra de herramientas como se indica en la figura 6.

Saul Mamani Mamani

Desarrollo Agil de Software

169

Componentes aadidos en la barra de herramientas

Figura 6 Lo primero que haremos ser insertar un componente SqlConnection dentro del formulario. Acudiremos a la ventana de propiedades del componente y modificaremos la propiedad ConnectionString dentro de la cul escribiremos la instruccin: server=.;uid=sa;password=VisualBasic;database=MSDNVideo entendiendo que sta, es la cadena de conexin vlida con nuestra base de datos. A continuacin aadiremos el componente SqlDataAdapter a nuestro formulario. Si se abre alguna ventana, cirrela. Vamos a configurar el control con la ventana Propiedades. Podramos haberlo hecho desde el asistente que se nos ha abierto, pero lo vamos a hacer de otra forma menos sencilla. Sitese sobre la propiedad SelectCommand, y dentro de sta, en la propiedad Connection. Lo que vamos a hacer, es asignar al componente SqlDataAdapter el componente de conexin que vamos a usar para establecer la comunicacin entre la fuente de datos y nuestra aplicacin. Despliegue la propiedad Connection indicada, y seleccione el componente de conexin SqlConnection1 anteriormente configurado, tal y como se muestra en la figura 7.

Saul Mamani Mamani

Desarrollo Agil de Software

170

Componente SqlDataAdapter con la conexin establecida para ser usada en la ejecucin de nuestra aplicacin

Figura 7 Por ltimo, inserte un componente DataSet al formulario. Todos los componentes quedarn por lo tanto insertados, tal y como se indica en la figura 8.

Componentes aadidos en el formulario de nuestra aplicacin

Figura 8

Saul Mamani Mamani

Desarrollo Agil de Software

171

Una vez que tenemos todo preparado, tan slo nos queda escribir la parte de cdigo fuente necesario para poder realizar todas las operaciones y acciones que necesitamos. A continuacin, se expone el cdigo fuente de nuestra aplicacin de demostracin: using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Data.SqlClient; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Xml; namespace WindowsApplication { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { //Establecemos la cadena SQL a utilizar sqlDataAdapter1.SelectCommand = "SELECT SocioNIF, FechaAlquiler FROM ALQUILERES"; //Poblamos el dataSet con los resultados obtenidos de la instruccion SQL sqlDataAdapter1.Fill(dataSet1); //Recorremos todas las filas de la tabla alquileres del DataSet foreach (DataRow Fila in MiDataSet.Tables[0].Rows) textBox1.Text += Fila["SocioNif"].ToString() + "\t" + Fila["FechaAlquiler"].ToString() + "\n"; } } } Ahora nos queda nicamente ejecutar nuestra aplicacin para estudiar el resultado final. Este es el que se puede ver en la figura 9.

Ejemplo en ejecucin del uso de componentes

Figura 9 Pese a todo lo que hemos visto, quizs se pregunte como es que en el caso del primer ejemplo que hemos visto y en el que hemos declarado el uso de un DataAdapter sin usar componentes, hemos tenido la obligatoriedad de aadir las

Saul Mamani Mamani

Desarrollo Agil de Software

172

referencias a los nombres de espacio System.Data y System.Xml, mientras que en este segundo ejemplo, no hemos hecho referencia a ellos. En realidad nosotros no hemos hecho referencia a ellos, pero al insertar los componentes dentro del formulario, el entorno Visual Studio 2005 se ha encargado por nosotros de aadir esas referencias al proyecto, tal y como puede verse en la figura 10.

Referencias aadidas automticamente al trabajar con componentes de acceso a datos

Figura 10

3. Insertando datos a travs del objeto DataAdapter


Hasta ahora, todos los ejemplos que hemos visto del objeto DataAdapter, han sido ejemplos del uso de seleccin de datos, pero an no hemos visto como debemos trabajar cuando realicemos otras acciones sobre la fuente de datos. A continuacin, veremos como realizar acciones de actualizacin de datos, utilizando para ello el objeto DataAdapter.

Recuerde: El DataSet permanece desconectado de la fuente de datos y si realizamos una modificacin o alteracin de los datos de un DataSet, estos no son propagados a la fuente de datos. Para ello, el DataAdapter debe recibir la orden que queramos ejecutar. Cmo se insertan datos con el objeto DataAdapter
Suponiendo que hemos recogido un conjunto de datos y que trabajando con el objeto DataSet hemos realizado una insercin de datos y que a continuacin, queremos propagar dicha insercin a la base de datos, deberemos hacer uso del mtodo Insert del objeto DataAdapter. El objeto DataAdapter se encargar de llamar al comando apropiado para cada una de las filas que han sido modificadas en un determinado DataSet. Esto lo realizar siempre a travs del mtodo Update.

Trabajando con un ejemplo


La mejor manera de ver esto es con un ejemplo que nos ayude a entender mejor como funciona la insercin de datos a travs del objeto DataAdapter. Tenga en cuenta adems, que la actualizacin y el borrado de datos funciona de la misma manera. Iniciaremos un nuevo proyecto de formulario Windows y en l insertamos los componentes SqlConnection, SqlDataAdapter, DataSet y SqlCommand. Para el componente SqlConnection, estableceremos la propiedad ConnectionString con el valor: server=.;uid=sa;password=VisualCSharp;database=MSDNVideo

Saul Mamani Mamani

Desarrollo Agil de Software

173

A continuacin seleccionaremos el componente SqlDataAdapter y modificaremos la propiedad SelectCommand > Connection como vimos en el captulo anterior. De la lista de posibles conexiones que le aparezca, seleccione la conexin SqlConnection1. Finalmente, inserte un control Button y un control DataGridView en el formulario. ste quedar como se indica en la figura 1.

Formulario con los componentes y controles insertados en l

Figura 1 Finalmente, escribiremos el cdigo necesario para ejecutar nuestra aplicacin tal y como queremos. Este es el que se detalla a continuacin: using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Data.SqlClient; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Xml; namespace WindowsApplication { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { //Establecemos la cadena SQL a utilizar

Saul Mamani Mamani

Desarrollo Agil de Software

174

sqlDataAdapter1.SelectCommand = "SELECT NIF, Nombre, Apellido1, Apellido2, Telefono, Email, Direccion, Ciudad, Provincia, CP FROM SOCIOS"; //Poblamos el dataSet con los resultados obtenidos de la instruccion SQL sqlDataAdapter1.Fill(dataSet1,"ejemplo"); //Asociamos al control dataGridView1 el DataSet dataGridView1.DataSource = dataSet1.Tables["ejemplo"]; } private void button1_Click(object sender, EventArgs e) { DataRow MiFila; //Creamos una nueva fila MiFila = dataSet1.Tables["ejemplo"].NewRow(); MiFila["NIF"] = "222222"; MiFila["Nombre"] = "Maria"; MiFila["Apellido1"] = "Juarez"; MiFila["Apellido2"] = "Fernandez"; MiFila["Telefono"] = "56852324"; MiFila["Email"] = "mariajuarez@cuentademail.com"; MiFila["Direccion"] = "Fernandez de los Rios 6"; MiFila["Ciudad"] = "Valladolid"; MiFila["Provincia"] = "Valladolid"; MiFila["CP"] = "11111"; //Aadimos la fila nueva a la tabla dataSet1.Tables["ejemplo"].Rows.Add(MiFila); //Miramos si el dataSet ha cambiado if (dataSet1.HasChanges()) { //Creamos el comando de insercion sqlCommand1.CommandText = "INSERT INTO SOCIOS(NIF, Nombre, Apellido1, Apellido2, Telefono, Email, Direccion, Ciudad, Provincia, CP) VALUES(@NIF, @Nombre, @Apellido1, @Apellido2, @Telefono, @Email, @Direccion, @Ciudad, @Provincia, @CP)"; sqlCommand1.Connection = sqlConnection1; sqlDataAdapter1.InsertCommand = sqlCommand1; //Aadimos los parametros sqlCommand1.Parameters.Add("@NIF", Data.SqlDbType.NChar, 10, "NIF"); sqlCommand1.Parameters.Add("@Nombre", Data.SqlDbType.NVarChar, 50, "Nombre"); sqlCommand1.Parameters.Add("@Apellido1", Data.SqlDbType.NVarChar, 50, "Apellido1"); sqlCommand1.Parameters.Add("@Apellido2", Data.SqlDbType.NVarChar, 50, "Apellido2"); sqlCommand1.Parameters.Add("@Telefono", Data.SqlDbType.NVarChar, 13, "Telefono"); sqlCommand1.Parameters.Add("@Email", Data.SqlDbType.NVarChar, 50, "Email"); sqlCommand1.Parameters.Add("@Direccion", Data.SqlDbType.NVarChar, 100, "Direccion"); sqlCommand1.Parameters.Add("@Ciudad", Data.SqlDbType.NVarChar, 50, "Ciudad"); sqlCommand1.Parameters.Add("@Provincia", Data.SqlDbType.NVarChar, 50, "Provincia"); sqlCommand1.Parameters.Add("@CP", Data.SqlDbType.NChar, 5, "CP"); sqlConnection1.Open(); //Actualizamos el dataSet contra la base de datos sqlDataAdapter1.Update(dataSet1, "ejemplo"); sqlConnection1.Close(); MessageBox.Show(" Cambios realizados correctamente !"); } } } }

Saul Mamani Mamani

Desarrollo Agil de Software

175

Por ltimo, ejecute la aplicacin. Si todo ha ido correctamente, los datos habrn quedado correctamente insertados en la base de datos. Un ejemplo de nuestra aplicacin en ejecucin es la que puede verse en la figura 2.

Aplicacin de ejemplo de insercin de datos con DataAdapter y DataSet en ejecucin

Figura 2 Como vemos, el uso de DataAdapter en el caso de manipular datos, vara ligeramente. Sin embargo, no es mucho ms complicado con la actualizacin y borrado de datos. De hecho, la forma de actuar es la misma como veremos a continuacin.

4. Actualizando datos a travs del objeto DataAdapter


De la misma manera que hemos insertado datos en nuestra base de datos, debemos hacer a la hora de actualizar los mismos. Sobre la base del ejemplo anterior (componentes y controles), escriba o modifique el siguiente cdigo: using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Data.SqlClient; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Xml; namespace WindowsApplication { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { //Establecemos la cadena SQL a utilizar sqlDataAdapter1.SelectCommand = "SELECT NIF, Nombre, Apellido1, Apellido2, Telefono, Email, Direccion, Ciudad, Provincia, CP FROM SOCIOS"; //Poblamos el dataSet con los resultados obtenidos de la instruccion SQL sqlDataAdapter1.Fill(dataSet1,"ejemplo"); //Asociamos al control dataGridView1 el DataSet dataGridView1.DataSource = dataSet1.Tables["ejemplo"]; }

Saul Mamani Mamani

Desarrollo Agil de Software

176

private void button1_Click(object sender, EventArgs e) { //Modificamos la fila 1 columna 4 ( indices comenzando en 0 ) dataSet1.Tables["ejemplo"].Rows[1][4] = "1122323"; //Miramos si el dataSet ha cambiado if (dataSet1.HasChanges()) { //Creamos el comando de actualizacion sqlCommand1.CommandText = "UPDATE SOCIOS SET Telefono=@Telefono WHERE NIF=@NIF"; sqlCommand1.Connection = sqlConnection1; sqlDataAdapter1.UpdateCommand = sqlCommand1; //Aadimos los parametros sqlCommand1.Parameters.Add("@NIF", Data.SqlDbType.NChar, 10, "NIF"); sqlCommand1.Parameters.Add("@Nombre", Data.SqlDbType.NVarChar, 50, "Nombre"); sqlCommand1.Parameters.Add("@Apellido1", Data.SqlDbType.NVarChar, 50, "Apellido1"); sqlCommand1.Parameters.Add("@Apellido2", Data.SqlDbType.NVarChar, 50, "Apellido2"); sqlCommand1.Parameters.Add("@Telefono", Data.SqlDbType.NVarChar, 13, "Telefono"); sqlCommand1.Parameters.Add("@Email", Data.SqlDbType.NVarChar, 50, "Email"); sqlCommand1.Parameters.Add("@Direccion", Data.SqlDbType.NVarChar, 100, "Direccion"); sqlCommand1.Parameters.Add("@Ciudad", Data.SqlDbType.NVarChar, 50, "Ciudad"); sqlCommand1.Parameters.Add("@Provincia", Data.SqlDbType.NVarChar, 50, "Provincia"); sqlCommand1.Parameters.Add("@CP", Data.SqlDbType.NChar, 5, "CP"); sqlConnection1.Open(); //Actualizamos el dataSet contra la base de datos sqlDataAdapter1.Update(dataSet1, "ejemplo"); sqlConnection1.Close(); MessageBox.Show(" Cambios realizados correctamente !"); } } } } Nuestro ejemplo en ejecucin es el que se puede ver en la figura 1.

Aplicacin de ejemplo de actualizacin de datos con DataAdapter y DataSet

Saul Mamani Mamani Figura 1

Desarrollo Agil de Software

177

5. Eliminando datos a travs del objeto DataAdapter


De igual forma sucede con la eliminacin de datos utilizando para ello el objeto DataAdapter junto al objeto DataSet. Utilizaremos nuevamente en este caso, la base del ejemplo anterior (componentes y controles), y escribiremos el siguiente cdigo: using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Data.SqlClient; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Xml; namespace WindowsApplication { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { //Establecemos la cadena SQL a utilizar sqlDataAdapter1.SelectCommand = "SELECT NIF, Nombre, Apellido1, Apellido2, Telefono, Email, Direccion, Ciudad, Provincia, CP FROM SOCIOS"; //Poblamos el dataSet con los resultados obtenidos de la instruccion SQL sqlDataAdapter1.Fill(dataSet1,"ejemplo"); //Asociamos al control dataGridView1 el DataSet dataGridView1.DataSource = dataSet1.Tables["ejemplo"]; } private void button1_Click(object sender, EventArgs e) { //Modificamos la fila 1 columna 4 ( indices comenzando en 0 ) dataSet1.Tables["ejemplo"].Rows[1].Delete(); //Miramos si el dataSet ha cambiado if (dataSet1.HasChanges()) { //Creamos el comando de insercion sqlCommand1.CommandText = "DELETE SOCIOS WHERE NIF=@NIF"; sqlCommand1.Connection = sqlConnection1; sqlDataAdapter1.DeleteCommand = sqlCommand1; //Aadimos los parametros sqlCommand1.Parameters.Add("@NIF", Data.SqlDbType.NChar, 10, "NIF"); sqlCommand1.Parameters.Add("@Nombre", Data.SqlDbType.NVarChar, 50, "Nombre"); sqlCommand1.Parameters.Add("@Apellido1", Data.SqlDbType.NVarChar, 50, "Apellido1"); sqlCommand1.Parameters.Add("@Apellido2", Data.SqlDbType.NVarChar, 50, "Apellido2"); sqlCommand1.Parameters.Add("@Telefono", Data.SqlDbType.NVarChar, 13, "Telefono"); sqlCommand1.Parameters.Add("@Email", Data.SqlDbType.NVarChar, 50, "Email"); sqlCommand1.Parameters.Add("@Direccion", Data.SqlDbType.NVarChar, 100, "Direccion"); sqlCommand1.Parameters.Add("@Ciudad", Data.SqlDbType.NVarChar, 50, "Ciudad"); sqlCommand1.Parameters.Add("@Provincia", Data.SqlDbType.NVarChar, 50, "Provincia"); sqlCommand1.Parameters.Add("@CP", Data.SqlDbType.NChar, 5, "CP");

Saul Mamani Mamani

Desarrollo Agil de Software

178

sqlConnection1.Open(); //Actualizamos el dataSet contra la base de datos sqlDataAdapter1.Update(dataSet1, "ejemplo"); sqlConnection1.Close(); MessageBox.Show(" Cambios realizados correctamente !"); } } } } Nuestro ejemplo en ejecucin es el que se puede ver en la figura 1.

Aplicacin de ejemplo de eliminacin de datos con DataAdapter y DataSet

Figura 1

DATA SET TIPADOS Introduccin


Otra particularidad de .NET a la hora de trabajar con fuentes de datos y con los componentes y controles que nos permiten acceder a ellas, es el trabajo con dos tipos de DataSets. Sin quererlo ya hemos visto como trabajar con uno de ellos, me refiero a los DataSets no tipados, sin embargo, hay otro tipo de DataSet diferente denominado as, DataSet tipado. En qu consiste un DataSet tipado y como utilizarlos, es lo que vamos a ver a continuacin.

1. Qu son los DataSets tipados?


De forma genrica, podemos definir como DataSet tipado, a aquel DataSet que posee un esquema de datos, a diferencia del DataSet no tipado, que no necesita de ese esquema. Respecto a los DataSet tipados, diremos que en el entorno de desarrollo, encontramos muchas utilidades y herramientas que nos facilitan el trabajo de este tipo de DataSets. Inclusive, el propio SDK posee herramientas de comandos que nos permite y nos facilita la preparacin para trabajar con DataSets tipados. El esquema al que un DataSet tipado hace referencia, es un documento XML. Se trata de un documento XML con extensin .xsd. Trabajar con un esquema en lugar de trabajar con una tabla directamente, es mucho ms gil, fcil, rpido y seguro, como veremos ms adelante.

Saul Mamani Mamani

Desarrollo Agil de Software

179

Cmo trabajar con un DataSet tipado


Evidentemente, lo que tenemos que tener claro y tomarlo as, como punto de partida, es que si queremos trabajar con un DataSet tipado, tenemos que crear su correspondiente esquema XML. Esto lo podemos hacer con una herramienta externa, manualmente, o con las herramientas que el entorno de desarrollo de Visual Studio 2005 o el propio SDK nos ofrece. En nuestro caso, ser estas ltimas acciones lo que usaremos. Obviamente, hacerlo manualmente es en mi opinin para frikis, mxime cuando sabemos que tenemos herramientas que nos facilitan su creacin y nos ahorran mucho tiempo y recursos. Cuando tengamos ya creado el esquema XML, deberamos generar la clase del DataSet, algo que a estas alturas, ya lo tenemos dominado. En uno de los mtodos de creacin del esquema, el entorno hace ese trabajo por nosotros, en el otro, que es un proceso ms manual como veremos, deberamos crear esa clase con posterioridad, pero an as, esto ltimo no lo veremos con excesiva profundidad.

Qu ventajas nos aportan los DataSets tipados?


Ya hemos enumerado algunas de ellas, rapidez, seguridad, agilidad, facilidad de trabajo, todo ello aplicable a los datos. An as, existen muchas ms razones para interesarnos por los DataSets tipados. A travs de los DataSets tipados, podemos realizar acciones que comnmente son costosas, de manera rpida y sencilla. Acciones como actualizacin de datos, modificacin, insercin o eliminacin de datos, o bsquedas de datos, son algunas de las acciones que en apenas un par de lneas de cdigo, estarn resueltas gracias al uso de DataSet tipados. Esto lo veremos ms adelante con los ejemplos de este captulo. Otras acciones adicionales vinculadas con los DataSets tipados son la posibilidad de filtrar datos, ordenar los datos, y trabajar con datos jerrquicos y relaciones de datos. Una diferencia notable entre los DataSets no tipados y los DataSets tipados, es que los primeros no saben ni tienen conocimiento alguno de lo que almacenan. Los segundos tienen cierta dosis de inteligencia y conocen el tipo de datos que almacena dentro. Adems de todo esto, el acceso a los datos que guarda un DataSet tipado, as como la manipulacin de los mismos, es mucho ms simple y requiere menos cdigo, haciendo que el acceso a los datos y el mantenimiento de la aplicacin sea ms cmoda y sencilla.

2. Generando nuestros DataSets tipados


Dentro de Visual Studio 2005 y de .NET en general, tenemos varias formas de generar nuestros propios DataSets tipados. Una de las formas de generar DataSets tipados es utilizando el entorno de desarrollo rpido Visual Studio 2005. La otra es utilizando la herramienta XSD.exe que encontraremos en el directorio SDK del entorno. Ambas formas, las veremos a continuacin.

Diagrama de datos
Antes de continuar, repasaremos el diagrama de datos con el cul vamos a trabajar. Este es el que se muestra en la figura 1.

Saul Mamani Mamani

Desarrollo Agil de Software

180

Diagrama de datos con el que vamos a trabajar

Figura 1 Este diagrama nos ayudar a interpretar los datos y a trabajar con ellos, en los ejemplos que veremos a continuacin. A continuacin, veremos como generar DataSets tipados desde el entorno de trabajo rpido, es decir Visual Studio 2005, y desde la lnea de comandos.

3. Generando un DataSet tipado con Visual Studio 2005


La forma ms rpida para generar DataSets tipados es utilizando las herramientas automticas del entorno Visual Studio 2005. A continuacin veremos como hacer esto de manera rpida y sencilla.

Usando el entorno de desarrollo rpido Visual Studio 2005


Cree un nuevo proyecto de Aplicacin para Windows y haga clic con el botn secundario del mouse sobre la ventana Explorador de soluciones, y del men emergente, seleccione la opcin Agregar > Nuevo elemento..., tal y como se muestra en la figura 1.

Saul Mamani Mamani

Desarrollo Agil de Software

181

Men para agregar un elemento al proyecto

Figura 1 Aparecer entonces una ventana de Agregar nuevo elemento, dentro de la cul seleccionaremos la plantilla de Conjunto de datos, tal y como se muestra en la figura 2.

Ventana para agregar un nuevo elemento al proyecto

Figura 2

Saul Mamani Mamani Haga clic en el botn Agregar. La plantilla del esquema del DataSet, tendr un aspecto similar al que se muestra en la figura 3.

Desarrollo Agil de Software

182

Esquema por defecto del DataSet en Visual Studio 2005

Figura 3 El siguiente paso que deberemos abordar, ser la de aadir al esquema, las tablas que queremos que formen parte del DataSet tipado. Para hacer esto, abra la ventana Explorador de base de datos y seleccione la base de datos de prueba. Pulse sobre la tabla Socios y arrstrela sobre el esquema del DataSet como se indica en la figura 4.

Arrastramos la tabla Socios sobre el esquema del DataSet

Figura 4 El esquema de la tabla quedar entonces aadido a la aplicacin, tal y como se indica en la figura 5.

Saul Mamani Mamani

Desarrollo Agil de Software

183

Esquema de la tabla Socios aadido al entorno de trabajo

Figura 5 En este punto, tendremos listo nuestro DataSet tipado, pero an no tendremos una clase o cdigo asociado al DataSet tipado. An no lo hemos dicho, pero en este punto, Visual Studio 2005 ha generado para nosotros, el cdigo relacionado con el DataSet tipado creado. Este cdigo, est dentro del directorio de la aplicacin, y puede ser accedido a l presionando el botn de la ventana Explorador de soluciones y representado por el siguiente icono Esto es lo que se representa en la figura 6.

Opcin de mostrar todos los archivos

Figura 6 En este punto, veremos que los archivos relacionados con nuestro proyecto y en el caso del elemento DataSet1.xsd, son los que se detallan en la figura 7.

Saul Mamani Mamani

Desarrollo Agil de Software

184

Archivos del proyecto

Figura 7 Observamos que el archivo DataSet1.Designer.cs depende del archivo DataSet1.xsd, y es el que contiene el cdigo necesario para utilizar el DataSet tipado. De hecho, si vemos el cdigo generado por Visual Studio 2005, observaremos que coincide con el que se detalla a continuacin: //-----------------------------------------------------------------------------// <auto-generated> // This code was generated by a tool. // Runtime Version:2.0.50215.44 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // </auto-generated> //-----------------------------------------------------------------------------Ms adelante veremos como usarlo, pero antes veremos otra forma de crear nuestro DataSet tipado. Esta que hemos visto, es la forma ms sencilla y habitual de crearlo, pero existe otra manera que es un procedimiento manual que a modo general, conviene que la conozcamos.

4. Generando un DataSet tipado con la lnea de comandos


Otra manera de generar nuestros propios DataSets tipados es mediante la lnea de comandos de MS-DOS con el uso de la herramienta xsd.exe. A continuacin veremos como realizar esta tarea con este comando.

Usando la herramienta XSD.exe


XSD no es otra cosa que un fichero ejecutable, que encontraremos en el SDK de .NET y que nos permitir crear nuestros DataSet tipados de manera rpida y sencilla. Para llevar a cabo nuestra tarea, abra una ventana MS-DOS y sitese en el directorio en el cul tenemos instalado nuestro entorno Visual Studio 2005, como se indica en la figura 1.

Saul Mamani Mamani

Desarrollo Agil de Software

185

Ventana de MS-DOS situada en el directorio de Visual Studio 2005

Figura 1 Una vez situados sobre este directorio, nos moveremos al subdirectorio SDK\v2.0\Bin tal y como se indica en la figura 2.

Ventana de MS-DOS situada en el subdirectorio Bin de Visual Studio 2005

Figura 2 A continuacin, lo que deberemos hacer es el fichero xsd correspondiente. Para eso, deberemos hacerlo mediante un editor de textos o bien, desde una pequea aplicacin Windows que generaremos para tal propsito. En nuestro caso lo haremos con una aplicacin Windows, para lo cul, iniciaremos un nuevo proyecto de Aplicacin para Windows. En el formulario Windows, insertaremos un control Button tal y como se muestra en la figura 3.

Saul Mamani Mamani

Desarrollo Agil de Software

186

Control Button insertado en el formulario de generacin del esquema

Figura 3 A continuacin, escribiremos el siguiente cdigo que lo que nos permitir, ser crear el fichero o documento XML de extensin xsd correspondiente, el cul contiene la informacin para generar el DataSet tipado. El cdigo de la aplicacin de generacin del esquema es el que se detalla a continuacin: using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Data.SqlClient; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Xml; namespace WindowsApplication { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { } private void button1_Click(object sender, EventArgs e) { SqlConnection MiConexion = new SqlConnection("server=.;uid=sa;password=VisualBasic;database=MSDNVideo"); SqlDataAdapter MiAdaptador = new SqlDataAdapter("SELECT TOP 1 * FROM SOCIOS", MiConexion); DataSet MiDataSet = new DataSet(); //Poblamos el DataSet MiAdaptador.Fill(MiDataSet, "SOCIOS"); //Creamos un objeto datatable DataTable MiTabla = ds.Tables["SOCIOS"]; // Completamos algunos parmetros de los campos de la tabla MiTabla.Columns["NIF"].Unique = true;

Saul Mamani Mamani

Desarrollo Agil de Software

187

MiTabla.Columns["Nombre"].MaxLength = 50; MiTabla.Columns["Apellido1"].MaxLength = 50; MiTabla.Columns["Apellido2"].MaxLength = 50; MiTabla.Columns["Telefono"].MaxLength = 13; MiTabla.Columns["Email"].MaxLength = 50; MiTabla.Columns["Direccion"].MaxLength = 100; MiTabla.Columns["Ciudad"].MaxLength = 50; MiTabla.Columns["Provincia"].MaxLength = 50; MiTabla.Columns["CP"].MaxLength = 50; MiDataSet.WriteXmlSchema(@"c:\MiEsquema.xsd"); MessageBox.Show("Esquema creado correctamente"); } } } Una vez que hemos escrito el cdigo fuente necesario para generar el esquema, ejecutaremos nuestra aplicacin, obteniendo una ejecucin afirmativa como la que se indica en la figura 4.

Ejecucin del programa de generacin del esquema

Figura 4 El cdigo de nuestro esquema, ser una vez que se ha creado, como se indica en el siguiente cdigo: <?xml version="1.0" standalone="yes"?> <xs:schema id="NewDataSet" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> Lo que haremos a continuacin, ser utilizar la herramienta xsd.exe que est dentro del directorio SDK\v2.0\Bin. Con la herramienta xsd.exe, generaremos la clase que podremos usar para el DataSet tipados. Una vez por lo tanto, que estamos situados en la lnea de comandos de MS-DOS, escribiremos la instruccin: xsd c:\MiEsquema.xsd /d /l:CS /n:MiDataSet.DSTypedSocios /o:c:\

Nota: El lenguaje predeterminado de la utilidad xsd es C#, por tanto no es necesario indicar el lenguaje en el que queremos la clase, por tanto, conseguiremos lo mismo si la escribimos tal como se muestra en la figura 5.
Esto lo haremos tal y como se indica en la figura 5.

Saul Mamani Mamani

Desarrollo Agil de Software

188

Comandos de creacin de la clase del esquema creado

Figura 5 Si todo ha ido correctamente, la clase para usar el DataSet tipado, se habr creado como se indica en la figura 6.

Clase de DataSet tipado creada con la aplicacin xsd.exe

Figura 6 El cdigo de nuestra clase tendr un aspecto similar al que se indica en el siguiente cdigo fuente: //-----------------------------------------------------------------------------// <auto-generated> // Una herramienta gener este cdigo. // Versin del motor en tiempo de ejecucin:2.0.50215.44 // // Los cambios en este archivo podran causar un comportamiento incorrecto y se perdern si // el cdigo se vuelve a generar. // </auto-generated> //-----------------------------------------------------------------------------En este punto, podemos incluir el archivo MiEsquema.cs a nuestro proyecto, o bien compilarlo desde la lnea de comandos o desde el entorno de desarrollo. Sin embargo, no realizaremos esa accin. Tan solo comentar que si quisiramos compilar desde la lnea de comandos el fichero con el compilador de Visual C#, utilizaramos el fichero ejecutable csc.exe, que corresponde con las iniciales de CSharp Compiler, y que por defecto estar en la carpeta C:\WINDOWS\Microsoft.NET\Framework\v2.0.50215. Ahora que ya sabemos como generar nuestros DataSet tipados, aprenderemos a usarlos en nuestras aplicaciones.

5. Usando los DataSets tipados


A continuacin veremos un ejemplo del uso de DataSets tipados utilizando Visual Studio 2005. Para esto nos servir lo que ya hemos visto en la generacin de un DataSet tipado con Visual Studio 2005.

Saul Mamani Mamani

Desarrollo Agil de Software

189

Recordemos que ya habamos aadido un elemento DataSet1.xsd a nuestro proyecto, tal y como se muestra en la figura 1.

DataSet1.xsd de ejemplo, aadido al formulario, para utilizarlo como DataSet tipado

Figura 1

Uso rpido de nuestro DataSet tipado


El uso de un DataSet tipado, no tiene muchas diferencias respecto a un DataSet no tipado. Este ltimo ya lo hemos visto, y el DataSet tipado lo veremos a continuacin de forma prctica. Este primer ejemplo, muestra de forma sencilla, como trabajar con el esquema que hemos creado y como hacerlo rpidamente a travs de nuestra aplicacin de prueba. El cdigo de ejemplo que nos sirve de toma de contacto, es el que se indica a continuacin: using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Data.SqlClient; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Xml; namespace WindowsApplication { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { } private void button1_Click(object sender, EventArgs e) { //Creamos el objeto conexion SqlConnection MiConexion = new SqlConnection("server=.;uid=sa;password=VisualCSharp;database=MSDNVideo"); //Creamos el adaptador para la conexion anterior SqlDataAdapter MiAdaptador = new SqlDataAdapter("SELECT * FROM SOCIOS", MiConexion); //Declaramos un objeto dataSet con el esquema del dataSet tipado DataSet MiStrongTypedDataSet = new dataSet1();

Saul Mamani Mamani

Desarrollo Agil de Software

190

MiAdaptador.Fill(MiStrongTypedDataSet, "SOCIOS"); foreach (dataSet1.SociosRow MisDatos in MiStrongTypedDataSet.Socios.Rows) { MessageBox.Show(MisDatos.Nombre + " " + MisDatos.Apellido1) + " " + MisDatos.Apellido2; } } } }

Nuestro ejemplo en ejecucin, es el que se puede ver en la figura 2.

Ejemplo en ejecucin del uso sencillo de DataSets tipados

Figura 2 A continuacin veremos otros tipos de ejecucin de DataSets tipados mucho ms complejos.

Atencin especial al Cuadro de herramientas


Cuando trabajamos con DataSets tipados como lo hemos hecho hasta ahora, habremos notado que entre otras cosas, tenemos las capacidades o posibilidades de trabajar con el DataSet como objetos. Obviamente, estos objetos estn incluidos en el Cuadro de herramientas, tal y como puede verse en la figura 3.

Componentes creados por el entorno para trabajar con DataSets tipados

Figura 3 Para trabajar con ellos, podemos arrastrarlos sobre el formulario como hacemos con cualquier control o componente.

Usando las herramientas automticas para trabajar con DataSets tipados


An as, vamos a arrastrar sobre el formulario, un componente DataSet como se indica en la figura 4.

Saul Mamani Mamani

Desarrollo Agil de Software

191

Seleccin de un control DataSet del Cuadro de herramientas

Figura 4 En este punto, recordemos que tenemos nuestro DataSet tipado o esquema ya creado y que para usar este esquema desde nuestro objeto DataSet, podemos utilizar las herramientas del entorno .NET. Cuando arrastramos el componente DataSet sobre el formulario, aparecer una ventana como que se muestra en la figura 5, y que nos permitir indicar si se trata de un DataSet tipado o un DataSet no tipado.

Ventana para agregar un conjunto de datos

Figura 5 Por defecto, aparecer seleccionada la opcin de Conjunto de datos con tipo y el DataSet o esquema que hemos creado. Presionaremos el botn Aceptar y de esta manera, nuestro objeto DataSet habr quedado insertado y preparado en el formulario, para utilizar el esquema del DataSet indicado. Para no complicarlo, he decidido renombrar el control DataSet como dtSet. El DataSet quedar insertado en nuestro entorno como se indica en la figura 6.

Saul Mamani Mamani

Desarrollo Agil de Software

192

Control DataSet (dtSet) insertado en el formulario de ejemplo

Figura 6 Para usar el objeto DataSet insertado, deberemos acceder a l a travs de cdigo, de forma muy parecida a lo que lo hacamos anteriormente. using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Data.SqlClient; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Xml; namespace WindowsApplication { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { } private void button1_Click(object sender, EventArgs e) { //Creamos el objeto conexion SqlConnection MiConexion = new SqlConnection("server=.;uid=sa;password=VisualCsharp;database=MSDNVideo"); //Creamos el adaptador para la conexion anterior SqlDataAdapter MiAdaptador = new SqlDataAdapter("SELECT * FROM SOCIOS", MiConexion); MiAdaptador.Fill(dtSet, "SOCIOS");

Saul Mamani Mamani

Desarrollo Agil de Software

193

foreach (dataSet1.SociosRow MisDatos in dtSet.Socios.Rows) { MessageBox.Show(MisDatos.Nombre + " " + MisDatos.Apellido1) + " " + MisDatos.Apellido2; } } } } El funcionamiento de nuestra aplicacin de ejemplo, es igual al que hemos visto en la figura 2.

Usando DataAdapter con DataSets tipados


Escribiremos a continuacin una pequea aplicacin Windows como la hemos hecho anteriormente. Crearemos nuestro esquema como lo hemos hecho hasta ahora y aadiremos dos botones como se indica en la figura 7.

Botones del ejemplo aadidos a la ventana Windows

Figura 7 A continuacin aadiremos un DataSet al cul le asignaremos el nombre del DataSet tipado correspondiente al esquema creado, tal y como se indica en la figura 8.

DataSet tipado del esquema asignado al objeto DataSet

Figura 8 A este objeto DataSet, le he llamado dtSet. A continuacin, aadiremos un componente SqlDataAdapter al formulario Windows.

Saul Mamani Mamani En este punto, aparecer una ventana como la que se muestra en la figura 9.

Desarrollo Agil de Software

194

Asistente del SqlDataAdapter

Figura 9 Elegiremos la conexin adecuada y presionaremos el botn Siguiente. A continuacin, el asistente nos mostrar una informacin como la que se indica en la figura 10.

Opcin para elegir el tipo de comando a utilizar

Figura 10

Saul Mamani Mamani

Desarrollo Agil de Software

195

Elegiremos la primera de las opciones que es la que se indica en la figura 10, y presionaremos nuevamente el botn Siguiente. El asistente continuar presentndonos ventanas de informacin para configurar nuestro componente SqlDataAdapter. Dentro de esta ventana y dentro de los datos que deseamos cargar, escribiremos la instruccin SQL SELECT * FROM SOCIOS como se indica en la figura 11.

Opcin de generacin de las instrucciones SQL

Figura 11 A continuacin, presionaremos el botn Siguiente. De esta manera, el asistente terminar de crear las instrucciones necesarias para trabajar con el componente SqlDataAdapter. Si todo ha ido correctamente, aparecer una ventana como la que se indica en la figura 12.

Resultados de la configuracin del componente SqlDataAdapter

Figura 12

Saul Mamani Mamani Para concluir, haremos clic sobre el botn Finalizar.

Desarrollo Agil de Software

196

Automticamente, en Visual Studio 2005, aparecer aadido al formulario Windows el componente SqlConnection, como vemos en la figura 13.

Componentes aadidos al formulario Windows

Figura 13 A continuacin, escribiremos el cdigo fuente de la aplicacin, que se encarga de recoger los datos de la base de datos, insertarlos en un DataSet, para modificarlos y actualizar las modificaciones en la base de datos. El cdigo fuente de la aplicacin, quedar como se detalla a continuacin: using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Data.SqlClient; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Xml; namespace WindowsApplication { public partial class Form1 : Form { SqlConnection MiSqlConnection; SqlDataAdapter MiSqlDataAdapter; public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) {

Saul Mamani Mamani

Desarrollo Agil de Software

197

//Poblamos el dataSet tipado con la informacion obtenida mediante la instruccion SELECT MiSqlDataAdapter.Fill(dtSet, "SOCIOS"); } private void button1_Click(object sender, EventArgs e) { dataSet1.SociosRow MisDatos = dtSet.FindByNif("11111"); MisDatos.CP = "22343"; //Deshabilitamos como medida de seguridad el botn button1.Enabled = false; } private void button2_Click(object sender, EventArgs e) { if (dtSet.HasChanges()) { //Declaramos un DataSet que nicamente contiene los cambios DataSet dtModificado = dtSet.GetChanges(); //Actualizamos los cambios del data set mediante el adaptador MiSqlDataAdapter.Update(dtModificado); //Aceptamos los cambios en el dataset para seguir trabajando con el dtSet.AcceptChanges(); MessageBox.Show("Los cambios del DataSet han sido actualizados en la base de datos"); } else MessageBox.Show("NO hay cambios en el DataSet"); } } } Nuestro ejemplo en ejecucin, es el que puede verse en la figura 14.

Ejemplo del DataAdapter y DataSet tipado en ejecucin

Figura 14

Nota: Cuando trabajamos con un componente DataAdapter, aparece un asistente. En el caso de elegir la opcin de generacin de sentencias SELECT, UPDATE, DELETE e INSERT, el asistente crear a partir de la SELECT, esas instrucciones por nosotros.
Dentro de este mismo ejemplo, aada otros dos botones como se muestra en la figura 15.

Saul Mamani Mamani

Desarrollo Agil de Software

198

Ejemplo del DataAdapter y DataSet tipado en ejecucin

Figura 15 Estos botones nos servirn para aadir una fila nueva a nuestra base de datos y para eliminar una fila existente (en nuestro caso la nueva fila aadida). El cdigo fuente de nuestra aplicacin de ejemplo, es el que se detalla a continuacin: using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Data.SqlClient; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Xml; namespace WindowsApplication { public partial class Form1 : Form { SqlConnection MiSqlConnection; SqlDataAdapter MiSqlDataAdapter; public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { //Poblamos el dataSet tipado con la informacion obtenida mediante la instruccion SELECT MiSqlDataAdapter.Fill(dtSet, "SOCIOS"); } private void button1_Click(object sender, EventArgs e)

Saul Mamani Mamani

Desarrollo Agil de Software

199

{ dataSet1.SociosRow MisDatos = dtSet.FindByNif("11111"); MisDatos.CP = "22343"; //Deshabilitamos como medida de seguridad el botn button1.Enabled = false; } private void button2_Click(object sender, EventArgs e) { if (dtSet.HasChanges()) { //Declaramos un DataSet que nicamente contiene los cambios DataSet dtModificado = dtSet.GetChanges(); //Actualizamos los cambios del data set mediante el adaptador MiSqlDataAdapter.Update(dtModificado); //Aceptamos los cambios en el dataset para seguir trabajando con el dtSet.AcceptChanges(); MessageBox.Show("Los cambios del DataSet han sido actualizados en la base de datos"); } else MessageBox.Show("NO hay cambios en el DataSet"); } private void button3_Click(object sender, EventArgs e) { //Aadimos una nueva fila al dataSet tipado dtSet.Socios.AddSociosRow("111112", "Mara", "Snchez", "Rodrguez", "1231234", "maria@cuentadecorreo.com", "C\ San Fernando, 13", "Barcelona", "Barcelona", "08001"); //Actualizamos el dataSet MiSqlDataAdapter.Update(dtSet); //Deshabilitamos el boton aadir y habilitamos el de eliminar button3.Enabled = false; button4.Enabled = true; MessageBox.Show(" Fila aadida correctamente "); } private void button4_Click(object sender, EventArgs e) { //Eliminamos una de las filas dtSet.Socios.FindByNIF("111112").Delete(); //Actualizamos el dataSet MiSqlDataAdapter.Update(dtSet); //Deshabilitamos el boton de eliminar button4.Enabled = true; MessageBox.Show("Fila eliminada correctamente"); } } }

Nuestro ejemplo en ejecucin es el que se muestra en la figura 16.

Saul Mamani Mamani

Desarrollo Agil de Software

200

Ejemplo del uso de DataAdapter y DataSet tipado con todas las acciones de insercin y eliminacin de datos incluidas

Figura 16 Como vemos, el uso del DataSet tipado, posee grandes ventajas sobre los DataSets no tipados, permitindonos trabajar con datos de forma muy rpida y sencilla. La parte ms compleja quizs, es la preparacin del esquema de datos, pero una vez realizada esta tarea, el trabajo con los datos es asombrosamente rpida y segura.

ENLACE A FROMULARIOS Introduccin


Visual Studio 2005 proporciona a los desarrolladores un conjunto de herramientas y asistentes que facilitan enormemente el trabajo y ahorran grandes cantidades de tiempo en el desarrollo de diferentes aplicaciones. De los asistentes o herramientas que nos proporcionan mayor rendimiento en el entorno de desarrollo rpido de Microsoft, est la que nos permite trabajar con fuentes de datos como los datos de una aplicacin tpica maestro detalle. Entre otras cosas, nos centraremos en esta accin, que ser la que veremos en detalle a continuacin.

1. Qu son los datos Maestro Detalle?


El desarrollador de aplicaciones que debe trabajar con datos y fuentes de datos relacionadas entre s, encuentra con frecuencia problemas de desarrollo en aplicaciones con datos interrelacionados. Adems, este tipo de aplicaciones, consumen gran parte del tiempo de desarrollo y son por lo general, acciones repetitivas. Supongamos como ejemplo general, la tabla Socios de un videoclub. Adems, relacionemos los socios del videoclub, con una tabla Alquileres, para saber si un socio determinado tiene pelculas alquiladas, y en ese caso, saber cules. Este sencillo ejemplo, es un claro exponente de una aplicacin que relaciona datos maestro detalle. Ambas tablas deben estar relacionadas para recopilar la informacin que se necesite en un momento dado. Los datos maestros seran expuestos por los socios del videoclub, mientras que los datos detalle estaran relacionados con los datos de los alquileres de los socios. En nuestro caso, vamos a cambiar las palabras maestro y detalle por padre e hijo, y a partir de ahora, nos referiremos a padre como la tabla Socios, e hijo como la tabla Alquileres. De esta forma, ubicaremos sin problemas ambos conceptos dentro del entorno de Visual Studio 2005, ya que ste tiene alguna ligera connotacin que podra infundirnos a error como observar ms adelante. Por suerte, Visual Studio 2005 nos proporciona un conjunto de herramientas, que hace que realizar una aplicacin Windows con todas las caractersticas de una aplicacin maestro detalle, sea un autntico juego de nios, que nos llevar aproximadamente un minuto de nuestro tiempo como mucho.

Saul Mamani Mamani

Desarrollo Agil de Software

201

No me cree?. Contine el captulo, y se sorprender de lo que Visual Studio 2005 puede hacer por usted.

2. Configurando la fuente de datos


Trabajar con fuentes de datos requiere como tarea inicial, que tengamos listo y preparado un origen de fuentes de datos vlido. Para esta tarea, deberemos configurar la fuente de datos que vamos a utilizar, algo que vamos a aprender a hacer a continuacin.

Configurando el origen de la fuente de datos


Iniciaremos una nueva aplicacin Windows con Visual Studio 2005 y seleccionaremos el men Data > Show Data Sources (Datos > Mostrar orgenes de datos) como se indica en la figura 1.

Men para mostrar los orgenes de datos

Figura 1 En este punto, aparecer una ventana como la que se muestra en la figura 2.

Ventana de orgenes de datos

Figura 2 Como podemos apreciar, la ventana no tiene por defecto ningn origen de datos asignado, adems, esta ventana inicialmente, no est anclada a ningn sitio del entorno. Para situarla en un lugar especfico del entorno, haga clic sobre la ventana y arrstrela sobre la parte en la que desee situarla, como se indica por ejemplo, en la figura 3.

Saul Mamani Mamani

Desarrollo Agil de Software

202

La ventana orgenes de datos podemos situarla dnde deseemos dentro de Visual Studio 2005

Figura 3 En este punto, la ventana de orgenes de datos, quedar anclada en el entorno de desarrollo, como se muestra en la figura 4.

Saul Mamani Mamani

Desarrollo Agil de Software

203

La ventana orgenes de datos anclada en Visual Studio 2005

Figura 4 Cada uno, puede situar esta ventana dnde considere oportuno. En mi caso la he situado entre las ventanas del Explorador de soluciones y de Propiedades, pero podemos situarla dnde consideremos opotuno. Sobre la configuracin del orgen de datos, haga clic sobre la opcin Agregar nuevo origen de datos... como se indica en la figura 5.

Opcin para agregar un nuevo origen de datos

Figura 5

Saul Mamani Mamani

Desarrollo Agil de Software

204

Aparecer una ventana como la que se muestra en la figura 6 en la cul indicaremos el lugar de dnde la aplicacin obtendr los datos, y que en nuestro caso ser de una Base de datos.

Como tipo de origen de datos elegiremos una Base de datos

Figura 6 Una vez seleccionado la opcin de Base de datos como tipo de origen de datos, presionaremos el botn Siguiente. En la siguiente ventana, elegiremos la conexin de la base de datos que vamos a utilizar, o presionaremos sobre el botn Nueva conexin... sino tenemos seleccionada la conexin que queremos utilizar. En la figura 7, podemos ver como hemos establecido la conexin con nuestra fuente de datos, que utilizaremos en nuestro ejemplo de creacin de la aplicacin de acceso a datos maestro detalle.

Saul Mamani Mamani

Desarrollo Agil de Software

205

Ventana dnde elegimos la conexin de datos

Figura 7 A continuacin, haremos clic en el botn Siguiente. En la nueva ventana que aparece ahora dentro del asistente para la creacin del origen de datos, indicaremos el nombre de la cadena de conexin que crearemos. En nuestro caso, no modificaremos el nombre de la cadena de conexin, dejndola por defecto como se muestra en la figura 8.

Saul Mamani Mamani

Desarrollo Agil de Software

206

Asignamos un nombre para el nombre de la cadena de conexin

Figura 8 A continuacin, haremos clic sobre el botn Siguiente. En este punto, el asistente se conecta a la base de datos para recuperar la informacin de la base de datos y mostrarla en la ventana del asistente como se muestra en la figura 9.

Saul Mamani Mamani

Desarrollo Agil de Software

207

Ventana con los objetos de la base de datos seleccionada

Figura 9 A continuacin, despliegue el objeto Tablas y seleccione las tablas Alquileres y Socios como se indica en la figura 10.

Saul Mamani Mamani

Desarrollo Agil de Software

208

Seleccin de los objetos de la base de datos seleccionada

Figura 10 Una vez que hemos seleccionado los objetos de la base de datos, haremos clic sobre el botn Finalizar para que el asistente concluya las opciones aadidas al asistente. La ventana del origen de datos, quedar ahora configurado de una forma similar a la que se presenta en la figura 11.

Ventana de origen de datos con la configuracin aadida

Figura 11 A partir de aqu, deberemos preparar las tablas del origen de datos para que se comporten como autnticos datos e informaciones maestro detalle. Esto es lo que veremos en el siguiente mdulo.

3. Preparando el origen de datos


Ya hemos aprendido a aadir nuestro origen de datos, y ahora aprenderemos a prepararlo para poder utilizarlo posteriormente en la aplicacin Windows. La preparacin del origen de datos, nos permitir seleccionar que tabla o datos queremos que acten como maestro y cuales como detalle, o dicho de otra forma, que datos queremos que sean padre y cuales hijo.

Saul Mamani Mamani

Desarrollo Agil de Software

209

Nuestro objetivo principal es mostrar la tabla Alquileres como tabla hijo y la tabla Socios como padre de la tabla anterior. Prepararemos e incrustaremos primero la tabla Socios dentro del formulario Windows como Detalle de la informacin, y posteriormente insertaremos la tabla Alquileres dentro del formulario. Por ltimo, asignaremos alguna relacin que permita trabajar con las dos tablas en nuestro formulario Windows sin perder la conexin entre ambas tablas y permitindonos acceder a la informacin que nos proporciona dicha relacin.

Preparando la tabla padre


Lo primero que haremos ser preparar la tabla padre para poderla aadir al formulario Windows. Por esa razn, haremos clic sobre la tabla Socios de la ventana de Orgenes de datos como se indica en la figura 1.

Tabla Socios de la ventana de orgenes de datos

Figura 1 En la ventana de Orgenes de datos y en concreto con la tabla Socios desplegada, centraremos nuestra atencin en el campo NIF como se indica en la figura 2.

Campo NIF de la tabla Socios

Figura 2 Pulse sobre la lista desplegable que aparece a la derecha del campo NIF y seleccione la opcin Label como se indica en la figura 3.

Lista desplegable con la opcin Label seleccionada como campo de la tabla desplegada

Saul Mamani Mamani Figura 3

Desarrollo Agil de Software

210

En este caso, la ventana de Orgenes de datos quedar informada tal y como se indica en la figura 4.

Campo NIF modificado en la ventana de Orgenes de datos

Figura 4 A continuacin, haremos clic sobre la tabla Socios como se indica en la figura 5, y posteriormente presionaremos sobre la lista desplegable que aparece a la derecha de la tabla.

Tabla Socios seleccionada en la ventana de Orgenes de datos

Figura 5 Si hacemos clic sobre la lista desplegable, aparecer una lista de opciones o posibilidades, para indicar cmo queremos que sean los campos de la tabla seleccionada con respecto a que tipo de controles queremos que sean. Esto es lo que se indica en la figura 6.

Lista desplegable de la tabla Socios de la ventana de Orgenes de datos

Figura 6 Por defecto, una tabla queda determinada con un icono que representa el control DataGridView, aunque se puede modificar la representacin que deseamos tengan los datos dentro de un formulario seleccionando cualquiera de las opciones que tenemos de la lista desplegable. Estas opciones pueden ser cualquiera de las siguientes: Representa los datos volcados dentro de un control DataGridView Representa los datos volcados dentro de controles estndar como TextBox u otros controles para reflejarla como Detalle de la informacin No representa ningn control como tipo de control de volcado de datos

Saul Mamani Mamani

Desarrollo Agil de Software

211

En el caso de la tabla Socios que usaremos como tabla padre, cambiaremos la representacin por defecto de DataGridView para indicarle que nos represente la informacin en controles, tal y como se indica en la figura 7.

Tabla Socios seleccionada en la ventana de Orgenes de datos como Detalle

Figura 7 Ahora que tenemos la tabla maestra ya preparada, pasaremos a preparar la tabla hija.

Preparando la tabla hija


Ahora que ya tenemos preparada la tabla tabla padre, prepararemos la tabla hija de los alquileres de las pelculas de video, para poder usar su informacin dentro del formulario Windows. Por esa razn, haga clic sobre la tabla Alquileres de la ventana de Orgenes de datos como se indica en la figura 8.

Tabla Alquileres de la ventana de orgenes de datos

Figura 8 Dentro de la ventana de Orgenes de datos y en concreto de la tabla Alquileres desplegada, centraremos nuestra atencin en el campo AlquilerID y SocioNIF como se indica en la figura 9.

Campos AlquilerID y SocioNIF de la tabla Alquileres

Figura 9 Sobre el campo AlquilerID y SocioNIF, haremos una pequea modificacin. Pulse sobre la lista desplegable que aparece a la derecha del campo AlquilerID y SocioNIF y seleccione la opcin Label como se indica en la figura 10.

Saul Mamani Mamani

Desarrollo Agil de Software

212

Lista desplegable de opciones de un campo de la tabla desplegada

Figura 10 De esta manera, el campo AlquilerID y SocioNIF quedarn modificados en la ventana Orgenes de datos como se indica en la figura 11.

Campo AlquilerID y SocioNIF modificados en la ventana de Orgenes de datos

Figura 11 A continuacin, haremos clic sobre la tabla Alquileres como se indica en la figura 12, y posteriormente presionaremos sobre la lista desplegable que aparece a la derecha de la tabla.

Tabla Alquileres seleccionada en la ventana de Orgenes de datos

Figura 12 Nos aseguraremos que est seleccionada la opcin DataGridView que es la que aparece por defecto. Esto es lo que se indica en la figura 13.

Saul Mamani Mamani

Desarrollo Agil de Software

213

En la tabla Alquileres, nos aseguraremos de seleccionar la opcin DataGridView

Figura 13 Una vez que tenemos las tabla maestra y detalle preparadas para utilizarlas, las aadiremos al formulario Windows para que tengan el funcionamiento esperado.

4. Incrustando los datos maestro detalle


Ya sabemos como crear un origen de datos, tambin sabemos como preparar los datos maestro y detalle, y por ltimo, lo que nos queda es insertar esos datos en el formulario, y relacionar todos sus datos para que funcionen de la forma esperada. A continuacin, veremos como llevar a cabo esta tarea y aprenderemos a hacerlo posible de forma muy rpida y sencilla.

Incrustando la tabla padre en el formulario


Nuestra primera accin, ser incrustar en el formulario los datos o tabla padre, que en nuestro caso es la formada por la tabla Socios. Para situar los datos de la tabla Socios dentro de un formulario y en concreto como una informacin de detalle, bastar con arrastrar y soltar la tabla Socios sobre el formulario como se indica en la figura 1.

Para presentar la informacin como detalle, arrastraremos la tabla Socios de la ventana Orgenes de datos sobre el formulario Windows

Figura 1

Saul Mamani Mamani

Desarrollo Agil de Software

214

Observaremos que Visual Studio 2005, generar por nosotros un conjunto de componentes y controles, que por defecto tendr una apariencia similar a la que se presenta en la figura 2.

Controles y Componentes de la tabla maestra aadidos al formulario Windows

Figura 2 Observe por un momento, que el campo NIF que declaramos como Label en la ventana de Orgenes de datos, aparece como tal en el formulario, tal y como se indica en la figura 3.

Campo NIF de la tabla, representado como un control Label en el formulario Windows

Figura 3 Si ejecutamos nuestra aplicacin, observaremos que esta acta como una tpica aplicacin de acceso a datos que nos permite navegar a travs de los campos de la tabla Socios, tal y como se indica en la figura 4.

Saul Mamani Mamani

Desarrollo Agil de Software

215

Aplicacin en ejecucin de la tabla de detalle incrustada en el formulario Windows

Figura 4 A continuacin, insertaremos en el formulario la tabla Alquileres y relacionaremos ambas tablas para que se muestren los datos relacionados, dentro del formulario Windows.

Incrustando la tabla hija en el formulario


Ya tenemos la tabla padre insertada en nuestro formulario Windows. Nuestra segunda accin, ser la de incrustar en el formulario los datos o tabla hoja, que en nuestro caso es la formada por la tabla Alquileres, la cul adems, posee una relacin entre campos con la tabla Socios insertada anteriormente. Para llevar a cabo esta accin arrastraremos y soltaremos la tabla Alquileres sobre el formulario como se indica en la figura 5.

Para presentar la informacin de la tabla Alquileres, arrastraremos la tabla de la ventana Orgenes de datos sobre el

Saul Mamani Mamani


formulario Windows

Desarrollo Agil de Software

216

Figura 5 Observe que Visual Studio 2005, genera por nosotros ms componentes y controles, que por defecto tendr una apariencia similar a la que se presenta en la figura 6.

Controles y Componentes de la tabla maestra y detalle aadidos al formulario Windows

Figura 6 Como podemos observar, el entorno de trabajo ha hecho por nosotros el trabajo ms complejo para representar los datos de forma rpida y sencilla. El esquema de datos tipados, apareca ya en nuestro proyecto cuando asignamos el correspondiente origen de datos. Ahora lo que ha ocurrido, es que al arrastrar y soltar la tabla padre Socios de la ventana de Orgenes de datos, en el entorno se ha aadido un componente de nombre MSDNVideoDataSet que es el que permitir relacionar el DataSet tipado con nuestros datos. Este componente ser usado por la relacin maestro detalle de las dos tablas aadidas al formulario. En la figura 7, podemos ver el esquema aadido a nuestro proyecto, y el componente del que estamos hablando.

Esquema del DataSet tipado aadido al proyecto y su componente de relacin

Figura 7

Saul Mamani Mamani Ejecute la aplicacin y observe el comportamiento de la misma.

Desarrollo Agil de Software

217

Observar por lo tanto, que los datos entre detalle y maestra, no estn relacionados. Si navegamos a travs de los datos de detalle a travs del objeto SociosBindingNavigator, el control DataGridView no representa la relacin de los datos seleccionados. Esto es lo que se muestra en la figura 8.

Ejecucin de la aplicacin confirmando que los datos mostrados no estn relacionados

Figura 8 A continuacin, la tarea que nos queda para completar el correcto funcionamiento de nuestra aplicacin, es la de relacionar la tabla detalle y la tabla maestra entre s, para que los datos que se muestran en la aplicacin, estn relacionados entre s.

Relacionando la tabla padre con la tabla hija


La tarea ms sencilla es la de relacionar la tabla detalle con la tabla maestra. Es una tarea sencilla, porque Visual Studio 2005 nos proporciona las herramientas necesarias para simplificar al mximo esta tarea. Para llevar a cabo esta tarea, haga clic sobre el control DataGridView que corresponde a los datos de la tabla maestra, y acceda a la ventana de Propiedades. Dentro de la ventana de Propiedades, acceda a la propiedad DataSource como se indica en la figura 9.

Saul Mamani Mamani

Desarrollo Agil de Software

218

Propiedad DataSource del control DataGridView de la informacin maestra

Figura 9 Despliegue esta propiedad, y de la lista desplegable que aparece, seleccione la opcin FK_Alquileres_Socios como se indica en la figura 10.

Asignacin de la clave de relacin entre las tablas

Figura 10 Cuando se asigna el campo de relacin de las tablas, dentro de la aplicacin se aade esta relacin para que cuando naveguemos entre los datos de la tabla Socios aparezca toda la informacin de la tabla Alquileres relacionada con la tabla Socios. Esto de lo que hablamos, est supeditado por el componente FK_Alquileres_SociosBindingSource que es lo que se indica en la figura 11.

Saul Mamani Mamani

Desarrollo Agil de Software

219

Controles y componentes incluido el de relacin entre tablas, aadidos al formulario Windows

Figura 11 Para finalizar, ejecutaremos nuestra aplicacin y comprobaremos que el funcionamiento de esta, incluida la relacin entre tablas, funciona como esperbamos. En la figura 12, podemos observar el comportamiento de nuestra aplicacin en ejecucin.

Saul Mamani Mamani


Aplicacin en ejecucin, mostrando la correcta relacin entre las tablas

Desarrollo Agil de Software

220

Figura 12

5. Manipulando los datos maestro detalle


Obviamente, los datos maestro detalle no nos sirve nicamente para insertar las tablas de datos en un formulario, mostrarlos y navegar por ellos. Adems de esto, podemos tambin manipular los datos maestro detalle, modificarlos, actualizarlos, borrarlos, sin hacer ninguna accin adicional. El control BindingNavigator ya proporciona todas las caractersticas necesarias para realizar estas acciones. Podemos personalizar el control para permitir o denegar estas acciones. Adems, dentro de la ventana de Orgenes de datos, podemos seleccionar diferentes campos de las tablas y cambiar el tipo de control en el que queremos representar sus datos. A continuacin veremos un breve ejemplo de como manipular datos para que nos sirva de aprendizaje de cmo hacer esto posible.

Modificando datos
Ejecute la aplicacin de ejemplo que hemos diseado hasta ahora y sitese en alguno de sus campos. Centrndonos en la informacin de la tabla Socios, cambiaremos un campo determinado, como el que se muestra en la figura 1.

Modificaremos el valor de un campo para que nos sirva de ejemplo

Figura 1 Acto seguido, cuando hayamos realizado la modificacin, haremos clic sobre la opcin de Guardar datos, tal y como se muestra en la figura 1.

Opcin del control BindingNavigator para guardar los datos modificados

Saul Mamani Mamani Figura 2

Desarrollo Agil de Software

221

Como vemos, la manipulacin de datos es realmente sencilla y en la relacin de datos mostrada, no tiene porqu presentarnos ninguna dificultad.

Insertando y eliminando datos


Si queremos agregar datos, deberemos hacer clic sobre la opcin Agregar nuevo del control BindingNavigator como se muestra en la figura 3.

Aadir un registro nuevo es realmente sencillo

Figura 3 De la misma manera funciona el mecanismo para eliminar un registro, tal y como se muestra en la figura 4.

Eliminar un registro de forma rpida

Figura 4

Recuerde presionar el icono si quiere que los cambios y modificaciones realizadas se mantengan. Pulsando sobre ese icono, la accin de manipulacin de datos se lanza contra la base de datos.

Potrebbero piacerti anche