Professional Documents
Culture Documents
C#
Edición española:
( EDICIONES ANAYA MULTIMEDIA (GRUPO ANAYA. S.A.). 2003
Juan Ignacio Lúea de Tena. 15. 2X027 Madrid
Depósito legal: M. 3.033 - 2003
ISBN: 84-415-1484-4
Printed in Spain
Imprime: Imprime Artes Gráficas Guemo. S.L.
Febrero. 32. 28022 Madrid.
Para mi familia y amigos.
J e f f Eerguson
Agradecimientos ......................................................................................... 6
Sobre los autores ........................................................................................ 7
Sobre el editor de la serie ...................................................................... 8
Introducción............................................................................................................... 29
Quién debería leer este libro .................................................................. 30
Cómo está organizado este libro............................................................. 30
Parte I: Fundamentos del lenguaje C# ........................................... 30
Parte II: Programación orientada a objetos con C# ........................ 31
Parte III: C# avanzado ..................................................................... 31
Parte IV: Desarrollando soluciones NET usando C# .......................... 31
Parte V: C# y NET Framcwork ........................................................ 31
Parte VI: Apéndices ........................................................................ 31
Cómo usar este libro .................................................................................. 32
Normas usadas en este libro ...................................................................... 32
Durante los últimos 20 años. C y C++ han sido los lenguajes elegidos para
desarrollar aplicaciones comerciales y de negocios. Estos lenguajes proporcio -
nan un altísimo grado de control al programador permitiéndole el uso de punteros
y muchas funciones de bajo nivel. Sin embargo, cuando se comparan lenguajes,
como Microsoft Visual Basic con C/C++. uno se da cuenta de que aunque C y
C++ son lenguajes mucho más potentes, se necesita mucho más tiempo para
desarrollar una aplicación con ellos. Muchos programadores de C/C++ han temi -
do la idea de cambiar a lenguajes como Visual Basic porq ue podrian perder gran
parte del control de bajo nivel al que estaban acostumbrados.
Lo que la comunidad de programadores necesitaba era un lenguaje que estu -
viera entre los dos. Un lenguaje que ayudara a desarrollar aplicaciones rápidas
pero que también permitiese un gran control y un lenguaje que se integrase bien
con el desarrollo de aplicaciones Web. XML y muchas de las tecnologías emer -
gentes.
Facilitar la transición para los programadores de C/C++ existentes y propor -
cionar a la vez un lenguaje sencillo de aprender para los programadores inexper -
tos son sólo dos de las ventajas del nuevo lenguaje del barrio. C#. Microsoft
presentó C# al público en la Profesional Developer’s Conference en Orlando.
Florida, en el verano del 2000. C# combina las mejor es ideas de lenguajes como
C. C++ y Java con las mejoras de productividad de NET Framework de Microsoft
Yb r ind a u na e xp er ie nc ia d e co d i fi cac ió n mu y p ro d uc ti v a ta nto p ara lo s n u e vo s
p r o gr a mad o re s co mo p a r a lo s ve ter a no s . E st e c ap ít u lo p ro fu nd iza e n lo s c u atro
co mp o n e nt es q ue co n s ti t u ye n la p la ta fo r ma NET ad e ma s d e a na liz ar la c o mp a ti -
b ilid ad p ar a la s tec no lo g ía s W eb e me r ge n te s. A c o nt i n ua ció n, se a na liz a n mu c h a s
d e la s fu n cio ne s d el le n g uaj e C# y s e co mp ar a n co n o tro s le n g uaj e s p o p ul are s.
.NET Framework
Micro so ft d i se ñó C# d e sd e s u b as e p ar a ap ro ve ch ar el n ue vo e n to r no NET
Fr a me wo r k. Co mo C# f o r ma p ar t e d e e ste n ue v o mu nd o N ET . d eb erá c o mp r e n -
d er p er fec ta me n te lo q u e p r o p o r cio na N ET Fra me wo r k y d e q u e ma ner a a u me n ta
s u p r o d uc ti v id ad .
NET Fr a me wo r k se co m p o ne d e c ua tr o p art e s, c o mo s e mu e str a e n l a fi g ura
1.1 : e l e nto r no co mú n d e ej ecu ció n, u n co nj u n to d e b ib lio te ca s d e c la se s, u n gr upo
d e l e n g uaj es d e p ro gr a m ació n y e l e n to r no ASP . NET . NET Fra me wo r k fue d i se -
ñad o co n tre s o b j et i vo s en me n te . P r i mer o , d eb í a lo grar ap li cac io ne s W ind o ws
mu c ho ma s e s tab l es , a u nq ue t a mb i é n d eb ía p ro p o rcio nar u na ap lic ac ió n co n un
ma yo r g rad o d e se g ur id a d . En se g u nd o l u gar , d e b ía s i mp li ficar el d e sarr o llo d e
ap li cac io ne s y s er v icio s W eb q ue no só lo f u nc io n en e n p la ta fo r ma s trad i ci o na le s,
si no t a mb i é n e n d i sp o si t ivo s mó vi le s. P o r ul ti m o , el e n to r no fu e d i se ña d o p ara
p r o p o rcio nar u n so lo gr up o d e b ib lio tec as q ue p ud i era n tr ab aj ar co n var io s l e n -
g uaj e s. Las si g u ie n te s s eccio n e s a na liz a n cad a u no d e lo s co mp o ne n te s d e NET
Fr a me wo r k.
Desarrollo Web
NET Fra me wo r k f u e d i s eñ ad o co n u na id e a e n me n te : p o t e nci ar e l d e sa rro llo
d e I nt er ne t. E s te n u e vo i nc e nt i vo p ar a e l d es arro llo d e I n ter ne t se l la ma se rv icio s
Web . P ued e p e n sar e n lo s ser v icio s W eb co mo e n u na p á gi n a W eb q u e i nt era ct ua
co n p ro gra ma s e n l u gar d e co n ge n te. E n l u ga r d e e n v iar p a g i na s W eb . u n s er vi cio
W eb re cib e u n a so l ic it ud e n fo r ma to XM L . re ali za u na fu nc ió n e n co ncr eto y
lu e go d e v u el ve u na r esp ue s ta a l so l ic ita n te e n fo r ma d e me n s aj e X M L.
NOTA: XML o el Lenguaje de marcado extensible es un lenguaje
autodescriptivo muy parecido a HTML. Por otra parte, XML no consta de
etiquetas predefinidas, lo que le concede una gran flexibilidad para repre-
sentar una amplia variedad de objetos.
Una típica aplicación para un servicio Web podría ser corno capa situada en lo
alto de un sistema de facturación de una empresa. Cuando un usuario que navega
por la red compra los productos de una página Web. la información de la compra
es enviada al servicio Web. que calcula el precio de todos los productos, añade
una línea a la base de datos de existencias y devuelve una respuesta con una
confirmación de pedido. Este servicio Web no sólo puede interactuar con páginas
Web, puede interactuar con otros servicios Web. como un sistema de cuentas de
pago de una empresa.
Para que el modelo de servicio Web sobreviva a la evolución natural de los
lenguajes de programación, debe incluir muchas más cosas que un simple interfaz
para la Web. El modelo de servicio Web también incluye protocolos que permiten
que las aplicaciones encuentren servicios Web disponibles en una red interna o en
Internet. Este protocolo también permite a la aplicación explorar el servicio Web
y decidir cómo comunicarse con él y cómo intercambiar información. Para permi -
tir el descubrimiento de servicios Web se estableció la Descripción, descubri -
miento e integración universal (UDDI). Esta permite que los servicios Web sean
registrados y consultados, basándose en datos clave como el nombre de la com -
pañía. el tipo de servicio y su localización geográfica.
Desarrollo de aplicaciones
Aparte del desarrollo Web. con NET Framework también puede construir l as
tradicionales aplicaciones Windows. Estas aplicaciones creadas con NET
Framevvork se basan en Windows Forms. Windows Forms es una especie de cruce
entre los formularios de Visual Basic 6 y los formularios de Visual C++. Aunque
los formularios parecen iguales a sus predecesores, están completamente orienta -
dos a objetos y basados en clases, de forma muy parecida a los formularios obje -
to de Microsoft Foundation Class.
Estos nuevos Windows Forms ahora admiten muchos de los controles clásicos
que aparecían en Visual Studio. como Button. TextBox y Labe 1. junto a los
controles ActiveX. Aparte de los controles tradicionales, también admite nuevos
componentes como PrintPreview. LinkLabel. ColorDialog y
OpenFileDialog.
La creación de aplicaciones con NET también brinda muchas mejoras no
disponibles en otros lenguajes, como la seguridad. Estas medidas de seguridad
pueden determinar si una aplicación puede escribir o leer un archivo de disco.
También permiten insertar firmas digitales en la aplicación para asegur arse de
que la aplicación fue escrita por una fuente de confianza. NET Framework tam -
bién permite incluir información de componentes, y de versión, dentro del código
real. Esto hace posible que el software se instale cuando se lo pidan,
automáticamente o sin la intervención del usuario. Juntas, todas estas funciones
reducen los costes asistencia para la empresa.
Entorno ASP.NET
Internet fue concebida en un principio para enviar contenido estático a los
clientes Web Estas páginas Web nunca cambiaban y eran las mismas para todos
los usuarios que navegaban hasta esa localización. Microsoft lanzo servidores
activos para permitir la creación de páginas dinámicas basadas en la aportación e
interacción del usuario con una página Web. Esto se consigu ió mediante el uso de
secuencias de comandos que funcionaban por detras de la pagina Web. general -
mente escritas en VB Script. Cuando los usuarios visitaban una pagina Web. se
les podía pedir que verificasen la información (manualmente o con una cookie). y
luego la secuencia de comandos podía generar una página Web que le era devuel -
ta al usuario.
ASP.NET mejora al original ASP proporcionando "codigo detrás". En ASP.
HTML y las secuencias de comando se mezclaban en un documento. Con
ASP.NET
y su "código detras", se puede separar el codigo y HTML. Ahora, cuando la
lógica de una pagina Web necesite cambiar, no hace falta buscar por cientos o
miles de líneas de HTML para localizar la secuencia de comandos que necesita
modificarse.
De forma parecida a Windows Forms. ASP NET admite Web Forms Los Web
Forms permiten arrastrar y colocar controles en sus formularios y codificarlos
como baria en cualquier típica aplicación Windows.
Como ASP.NET usa NET Framevvork. también usa el compilador Justo a
tiempo (J1T) Las paginas ASP tradicionales se ejecutaban muy lentamente por -
que el codigo era interpretado. ASP.NET compila el codigo cuando es instalado
en el servidor o la primera vez que es necesario, lo que aumenta enormemente la
velocidad.
Historia de C, C++ y C#
El lenguaje de programación C# fue creado con el mismo espíritu que los
lenguajes C y C++. Esto explica sus poderosas prestaciones y su fácil curva de
aprendizaje. No se puede decir lo mismo de C y C++. pero como C# fue creado
desde cero. Microsoft se tomó la libertad de eliminar algunas de las prestaciones
mas pesadas (cómo los punteros). Esta sección echa un vistazo a los lenguajes C
y C++. siguiendo su evolución hasta C#.
El lenguaje de programación C fue diseñado en un principio para ser usado en
el sistema operativo UNIX. C se usó para crear muchas aplicaciones UNIX,
incluyendo un compilador de C. y a la larga se usó para escribir el mismo UNIX.
Su amplia aceptación en el mundo académico se amplió al mundo comercial v los
proveedores de software como Microsoft y Borland publicaron compiladores C
para los ordenadores personales. El API original para Windows fue diseñado
para trabajar con código Windows escrito en C y el último conjunto de API
básicos del sistema operativo Windows sigue siendo compatible con C hoy en día.
Desde el punto de vista del diseño. C carecía de un detalle que ya ofrecían
otros lenguajes como Smalltalk: el concepto de objeto. Piense en un objeto como
en una colección de datos y un conjunto de operaciones que pueden ser realizadas
sobre esos datos La codificación con objetos se puede lograr usando C. pero la
noción de objeto no era obligatoria para el lenguaje. Si quería estructurar su
codigo para que simulara un objeto, perfecto. Si no. perfecto también. En realidad
a C no le importaba. Los objetos no eran una parte fundamental del lenguaje, por
lo que mucha gente no prestó mucha atención a este estándar de programación.
Una vez que el concepto de orientación a objetos empezó a ganar aceptación,
se hizo evidente que C necesitaba ser depurado para adoptar este nuevo modo de
considerar al codigo C++ fue creado para encarnar esta depuración. Fue diseña -
do para ser compatible con el anterior C (de manera que todos los programas
escritos en C pudieran ser también programas C++ y pudieran ser c ompilados con
un compilador de C++). La principal aportación a C++ fue la compatibilidad
para el nuevo concepto de objeto. C++ incorporó compatibilidad para clases (que
son "plantillas" de objetos) y permitió que toda una generación de programadores
de C pensaran en términos de objetos y su comportamiento.
El lenguaje C++ es una mejora de C. pero aún así presenta algunas desventa -
las C y C++ pueden ser difíciles de manejar. A diferencia de lenguajes fáciles de
usar como Visual Basic. C y C++ son lenguajes de muy "bajo nivel" y exigen que
mucho código para funcionar correctamente. Tiene que escribir su propio codigo
para manejar aspectos como la gestión de memoria y el control de errores. C y
C++ pueden dar como resultado aplicaciones muy potentes, pero debe asegurarse
de que el código funciona bien. Un error en la escritura del programa puede hacer
que toda la aplicación falle o se comporte de forma inesperada. Como el objetivo
al diseñar C++ era retener la compatibilidad con el anterior C. C++ fue incapaz
de escapar de la naturaleza de bajo nivel de C.
Microsoft diseño C # de modo que retuviera casi toda la sintaxis de C y C++.
Los programadores que esten familiarizados con esos lenguajes pueden escoger el
código C # y empezar a programar de forma relativamente rápida. Sin embargo, la
gran v entaja de C # consiste en que sus diseñadores decidieron no hacerlo compa -
tible con los anteriores C y C++. Aunque esto puede parecer un mal asunto, en
realidad es una buena noticia. C # elimina las cosas que hacían que fuese difícil
trabajar con C y C++. Como todo el código C es también código C++. C++ tenía
que mantener todas las rarezas y deficiencias de C. C # parte de cero y sin ningún
requisito de compatibilidad, así que puede mantener los puntos fuertes de sus
predecesores y descartar las debilidades que complicaban las cosas a los progra -
madores de C y C+T
Introducción a C#
C#. el nuevo lenguaje presentado en NET Framework. procede de C++. Sin
embargo. C# es un lenguaje orientado a objetos (desde el principio), moder no y
seguro.
Tipos de datos
C# permite trabajar con dos tipos de datos: de calor y de referencia. Los de
valor contienen valores reales. Los de refere ncia contienen referencias a valores
almacenados en algún lugar de la memoria. Los tipos primitivos como char.
int y float. junto con los calores y estructuras comentados, son tipos de
calor. Los tipos de referencia tienen variables que tratan con objetos y matrices.
C# viene con tipos de referencia predefinidos (object y string). junto con
tipos de valor predefinidos (sbyte. short.. int. long. byte. ushort.
uint.
ulonq. float. double. bool. char y decimal). También puede definir
en el código sus propios tipos de v alor y referencia. Todos los tipos de valor y de
referencia derivan en ultima instancia de un tipo base llamado object.
C# le permite convertir un valor de un tipo en un valor de otro tipo. Puede
trabajar con conversiones implícitas y explícitas. Las conversiones implícitas
siempre funcionan y nunca pierden información (por ejemplo, puede convertir un
int en un long sin perder ningún dato porque un long es mayor que un int).
Las conversiones explícitas pueden producir perdidas de datos (por ejemplo , con-
vertir un long en un int puede producir perdida de datos porque un long
puede contener valores mayores que un int). Debe escribir un operador ca.st
en el código para que se produzca una conversión explicita.
En C# puede trabajar con matrices unidimensionales y multidimensionales.
Las matrices multidimensionalcs pueden ser rectangulares, en las que cada una de
las matrices tiene las mismas dimensiones, o escalonadas, en las que cada una de
las matrices puede tener diferentes dimensiones.
Las clases y las estructuras pueden tener miembros de datos llamados propie-
dades y campos. Los campos son variables que están asociadas a la clase o
estructura a la que pertenecen. Por ejemplo, puede definir una estructura llamada
Empleado que tenga un campo llamado Nombre. Si define una variable de tipo
Empleado llamada Emplead o Actual, puede recuperar el nombre del em-
pleado escribiendo EmpleadoActual . Nombre. Las propiedades son como
los campos, pero permiten escribir codigo que especifique lo que debería ocurri r
cuando el codigo acceda al valor. Si el nombre del empicado debe leerse de una
base de datos, por ejemplo, puede escribir código que diga "cuando alguien pre -
gunte el valor de la propiedad Nombre, lee el nombre de la base de datos y
devuelve el nombre como una cadena".
Funciones
Lina función es un fragmento de código que puede ser invocado y que puede
o no devolver un valor al código que lo invocó en un principio. Un ejemplo de una
función podría ser la función FullName mostrada anteriormente en este capitu-
lo. en la clase Family. Una función suele asociarse a fragmentes de código que
devuelven información, mientras que un método no suele devolver información.
Sin embargo, para nuestros propósitos, generalizamos y nos referimos a las dos
como funciones.
Las funciones pueden tener cuatro tipos de parámetros.
• Parametros de entrada, tienen valores que son enviados a la función, pero
la función no puede cambiar esos v alores.
• Parámetros de salida: no tienen valor cuando son enviados a la función,
pero la función puede darles un valor y enviar el valor de vuelta al invoca -
dor.
• Parámetros de referencia: introducen una referencia en otro valor. Tienen
un valor de entrada para la función y ese valor puede ser cambiado dentro
de la función.
• Parámetros Params: definen un numero variable de argumentos en una
lista.
C # y el CLR trabajan juntos para brindar gestión de memoria automática. No
necesita escribir código que diga "asigna suficiente memoria para un numero
entero" o "libera la memoria que está usando este obj eto". El CLR monitoriza el
uso de memoria y recupera automáticamente más cuando la necesita. También
libera memoria automáticamente cuando detecta que ya no está siendo usada (esto
también se conoce como recolección de objetos no utilizados).
C # proporciona varios operadores que le permiten escribir expresiones mate -
máticas y de bits. Muchos (pero no todos) de estos operadores pueden ser
redefinidos, permitiéndole cambiar la forma en que trabajan estos operadores.
C # admite una larga lista de expresiones que le permiten definir varias rutas de
ejecución dentro del código. Las instrucciones de flujo de control que usan pala -
bras clave como if. switch. while. for. break y continué permiten al
código bifurcarse por caminos diferentes, dependiendo de los v alo res de sus va-
riables. Las clases pueden contener código y datos. Cada miembro de una clase
tiene algo llamado ámbito de accesibilidad, que define la visibilidad del miembro
con respecto a otros objetos. C # admite los ámbitos de accesibilidad public.
protected. internal. protected internal y private.
Variables
Las variables pueden ser definidas como constantes. Las constantes tienen
v alores que no pueden cambiar durante la ejecución del código. Por ejemplo, el
valor de pi es una buena muestra de una cons tante porque el valor no cambia a
medida que el codigo se ejecuta. Las declaraciones de tipo de enumeración espe-
cifican un nombre de tipo para un grupo de constantes relacionadas. Por ejemplo,
puede definir una enumeración de planetas con valores de Merc urio. Venus. Tie-
rra. Marte. Júpiter. Saturno. Urano. Neptuno y Plutón. y usar estos nombres en el
código. Usando los nombres de enumeraciones en el código hace que sea más fácil
leerlo que si usara un número para representar a cada planeta.
C# incorpora un mecanismo para definir y procesar eventos. Si escribe una
clase que realiza una operación muy larga, quizás quiera invocar un evento cuan -
do la operación se complete. Los clientes pueden suscribirse a ese evento e in -
cluirlo en el codigo. lo que permite que se les pueda avisar cuando haya acabado
su operación. El mecanismo de control de eventos en C# usa delegados, que son
variables que se refieren a una función.
Atributos
Los atributos aportan información adicional sobre su clase al CLR Antes, si
quería que la clase fuera autodescriptiva. tenia que seguir un enfoque sin co -
nexión. en el que la documentación fuera almacenada en archivos externos co mo
un archivo IDL o incluso archivos HTML. Los atributos solucionan este proble -
ma permitiendo al programador vincular información a las clases (cualquier tipo
de información). Por ejemplo, puede usar un atributo para insertar información
de documentación en una clase, explicando cómo debe actuar al ser usada. Las
posibilidades son infinitas y ésa es la razón por la que Microsoft incluye tantos
atributos predefinidos en NET Framework.
Cómo compilar C#
Ejecutar el código C# con el compilador de C# produce d os importantes con-
juntos de información: el código y los metadatos. Las siguientes secciones descri -
ben estos dos elementos y luego terminan examinando el componente esencial del
código NET: el ensamblado.
Resumen
En este capítulo se han explicado las bases de NET Framework. Tras seguir
la evolución desde C a C++ y hasta C#. se han examinado los puntos fuertes de
la lista de prestaciones de C#. También se ha investigado el produc to del
compilador de C#. el código MSIL y los metadatos. y se ha revisado el uso de
ensamblados como los bloques de construcción esenciales del código NET com -
pilado.
2 Escribir
su primer
programa en C#
Este capítulo le guía a través del desarrollo de una sencilla aplicación de C#.
También aprenderá cómo están estructuradas las aplicaciones C# más sencillas
y cómo invocar al compilador de C# para convertir el código fuente en código
que puede ser ejecutado por NET Framework. Finalmente, aprendera a doc u-
mentar el código empleando comentarios de código fuente y cómo convertir
automáticamente sus comentarios en un documento XML.
c1ass Hel1oWorld
{
public static void Main()
{
System.Console.WriteLine ("Helio World!");
}
}
class @virtual
{
static void Mam ()
{
System.Consolé.WriteLine("Helio World!");
}
}
Sin el precedente símbolo a,, obtendría un error del compilador, como el que
se muestra en la figura 2.2.
Class
H a l l 0W 0r 1 d
{
s ta ti c v o i d M a i n ( )
{
System.Console.WriteLine("Helio World!");
}
}
Si co mp il a y ej ec u ta e l l is tad o 2 .3 . ver á q u e s e co mp o rta e x act a me n te i g ua l
q ue el co d i go d el l is tad o 2 .1 : p r o d uc e l a c ad e na " Helio Wo r ld 1 " . La n ue va d i sp o -
si ció n d e esp acio s e n b la nco no t ie n e n i n g ú n efec to e n el co mp o rt a m ie nto d el
co d i go q ue s e ej ec u ta e n el tie mp o d e ej ec uc ió n.
En este caso, la matriz de cadenas que se pasa a la función Main() tiene los
siguientes contenidos:
Arguments [0] :Paraml
Arguments [1] :Paraml
Arguments [2] :Param3
*/
class HelloWorld
{
/// Ésta es la función Main () para la clase del listado 2.4.
/// No devuelve un valor y no toma ningún
/// argumento. Escribe el texto "Helio World!" en la
/// consola y luego sale.
static void Main()
{
System.Console.WriteLine ("Hello World!");
}
}
Puede compilar esta aplicación con la opción /doc para generar documenta -
ción XML para el códi go fuente:
ese /doc:HelloWorId.xml HelloWorld.es
Listado 2.5. Documento XML generado para el código del listado 2.4
<?xml version="1.O"?>
<doc>
< assembly>
< name>HelloWorld</name >
< /assembly >
< members>
<member name = "T:HelloWorld">
La clase HelloWorld es la única clase de la
clase "HelloWorld". La clase implementa la función
Main() de la aplicación. La clase no contiene otras
funciones.
</member>
''member name= "M:HelloWorld.Main">
Esta es la función Main () para la clase del listado 2.4.
No devuelve un valor y no toma ningún
argumento. Escribe el texto "HelloWorld!" en la
consola y luego sale.
< /member>
</members>
</doc>
Ahora puede escribir una hoj a de estilo para este documento en XML y mos -
trarlo en una página Web. proporcionando a l os demás usuarios documentación
actualizada de su códi go.
La principal porción del documento XML est á en el elemento <members>.
Este elemento contiene una etiqueta <member> para cada obj eto documentado
en el codi go fuente. La etiqueta <member> contiene un atributo, ñame, que
designa al miembro documentado. El valor del atributo name empieza con un
prefij o de una letra que describe el tipo de infor mación en cuestión. La tabla 2.1
describe los posibles valores del atributo del nombre y su si gnificado.
Tabla 2.1. Prefijos de atributo <member> "name="
<c>
Puede usar la etiqueta <c> para indicar que una pequeña parte del comentario
debe ser tratada como código. Las hojas de estilo pueden usar este elemento para
mostrar la porción de código del comentario con una fuente de tamaño fijo, como
Courier:
/// Ésta es la función <c>Main()</c> para la
/// clase HelloWorld.
<code>
Puede usar la etiqueta <code> para indicar que varias líneas de texto de sus
comentarios deben ser tratadas como código:
/// Llamar a esta aplicación con tres argumentos
/// hara que la matriz de cadenas suministrada a Main()
/// contenga tres elementos:
/// <code>
/// Argument[0]: command line argument 1
/// Argument[1]: command line argument 2
/// Argument[2]: command line argument 3
/// </code>
<example>
Puede usar la etiqueta < example> para indicar un ejemplo de cómo usar las
clases que desarrolle a otros programadores. Los ejemplos suelen incluir una
muestra de código y quizás quiera usar las etiquetas <example> y <code>
juntas:
/// <example>Aqui tiene un ejemplo de un cliente llamando
/// a este codigo:
/// <code>
/// ponga aquí su codigo de ejemplo
/// </code>
/// < / example>
<exception>
Puede usar la etiqueta <exception> para documentar cualquier excepción
que pueda surgir en el código del miembro. La etiqueta <exception> debe
contener un atributo llamado cref cuyo valor especifica el tipo de excepción
que
se documenta. El valor del atributo cref debe estar entre comillas. El texto del
elemento describe las condiciones en las que aparece la excepción:
<list>
Puede usar la etiqueta <list> para describir una lista de elementos en la
documentación. Puede describir una lista no numerada, una lista numerada o una
tabla. La etiqueta <list> usa un atributo llamado type para describir el tipo
de la lista. La tabla 2.2 enumera los posibles valores para el atributo type y
describe su significado.
Tabla 2.2. Valores para el atributo "type" de la etiqueta <list>
Los estilos bullet y number deberían también incluir una o más etiquetas
<item> dentro de la etiqueta <list>.
Cada etiqueta <item> se corresponde a un elemento de la lista. Cada
etiqueta
<item> debería contener una etiqueta <description>. cuyo texto define el
texto de la lista de elementos:
/// <list type="bullet">
/// <item>
/// <description>Éste es el elemento 1.</description>
/// </item>
/// <item>
/// <description>Éste es el elemento 2.</description>
/// </item>
/// </list>
El primer aviso dice que se encontró una etiqueta <param> con un atributo
name cuyo valor no concuerda con ninguno de los parámetros de l a función. El
segundo aviso dice que a uno de los parámetros l e falta una etiqueta <param>.
La etiqueta <param> se coloca en el archivo XML de documentación, inclu -
so si el atributo name es incorrecto:
<member name="M:Classl.Main(System.String[])">
<param name="junk">Esto es junk.</param>
</member>
<paramref>
Puede usar la etiqueta <paramref> para hacer referencia a un parámetro
desde una descripción. La etiqueta puede no tener ningún texto; sin embargo,
lleva un atributo llamado name.
El valor del atributo name debe indicar el nombre del parámetro al que se
hace referencia:
/// La matriz <paramref name="Arguments"/> contiene
/// los parametros especificados es la linea de comandos.
<permission>
Use la etiqueta <permission> para documentar los permisos disponibles
en una función o variable dadas. El acceso al código y los datos de una clase
puede significar el acceso a todo el código o puede ser restringido a cierto
subconjunto de código. Puede usar la etiqueta <permission> para documen-
tar la disponibilidad del código y sus datos.
La etiqueta <permission> hace uso de un atributo, cref. El valor del
elemento cref debe designar la función o la variable cuyos permisos se están
documentando:
/// <permission name= "Mam () ">
/// Todo el mundo puede acceder a Main().
/// </permission>
<remarks>
Use la etiqueta <remarks> para añadir información. El elemento <remarks>
es estupendo para mostrar una vista general de un método o una variable y su uso.
La etiqueta <remarks> no tiene atributos y su texto contiene las observaciones:
/// <remarks>
/// La función Main() es el punto de entrada a la
/// aplicación. El CLR llamará a Main() para iniciar
/// la aplicación una vez que ésta se haya abierto.
/// </remarks>
<returns>
Use la etiqueta <returns> para describir un valor devuelto por una fun-
ción. La etiqueta <returns> no tiene atributos y su texto contiene la informa -
ción del valor devuelto:
/// <returns>
/// La función Main() devolverá 0 si la aplicación
/// procesó los datos correctamente y devolverá 1
/// en caso contrario.
/// </returns>
<see>
Use la etiqueta <see> para añadir una referencia a una función o variable que
se encuentre en otra parte del archivo. El elemento <see> usa un atributo llama-
do cref cuyo valor especifica el nombre del método o variable al que se hace
referencia. La etiqueta <see> no debe contener texto:
/// <see cref="C1ass1.Main"/>
<seealso>
Como < s e e > . puede usar la etiqueta < s e e a l s o > para añadir una referencia
a una f unción o variabl e que este' en otra part e del archi vo. Puede que necesite
generar documentación que contenga una sección de referencias < s e e > ademas
de una sección de refer encias S e e A l s o y el compilador de C# le per mit e hacer
esa distinción al admitir las etiquetas < s e e > y < s e e a l s o > . La etiqueta
< s e e a i s o > usa un atri buto llamado c r e f cuyo valor especifica el nombre del
método o variable al que se hace referencia. La etiqueta < s e e a l s o > no debe
contener texto:
/// <seealso cref="Class1.Main"/>
Resumen
Este capítulo muestra cómo crear aplicaciones C# con un simple editor de
texto, como el Bloc de notas. También examina varias alternativas a Visual Studio
para escribir código.
Ha construido su primera aplicación de C #. Las aplicaciones C #. indepen-
dientemente de su tamaño, deben contener una clase con una función llamada
Main (). La función Main () es el punto de partida de su aplicación de C #.
También aprendió a añadir comentarios al código fuente en C #. Puede añadir
comentarios al código para ayudar a otros programadores a comprender cómo
está estructurado el código fuente. También puede dar formato a las comentarios
de tal modo que el compilador pueda convertir los comentarios en un documento
XML; y añadiendo palabras clave especiales, puede hacer el documento XML
muy rico e informativo.
3 Trabajar
con variables
También puede usar una palabra clave de C# como nombre de variable, pero
sólo si va precedida del carácter a . No obstante, no se trata de una práctica
recomendable, ya que puede hacer que su código resulte difícil de leer, pero es
factible y el compilador de C # lo permite.
int Las variables con tipo int pueden contener números en-
teros de 32 bits con signo. El menor valor posible para una
variable int es -2.147.483.648; el valor más alto es
2.147.483.647.
uint
Las variables con tipo uint pueden contener números
enteros de 32 bits sin signo. La u’ en uint significa sin
signo. El menor valor posible para una variable uint
variable es 0; el valor más alto posible es 4.294.967.295.
long Las variables con tipo long pueden contener números
enteros de 64 bits con signo. El menor valor posible para
una variable long es 9.223.372.036.854.775.808; el va-
lor más alto es 9.223.372.036.854.775.807.
ulong
Las variables con tipo ulong pueden contener números
enteros de 64 bits sin signo. La ‘u’ en ulong significa sin
signo. El menor valor posible para una variable ulong
es 0; el valor más alto es 18.446.744.073.709.551.615.
char Las variables con tipo char pueden contener caracteres
Unicode de 16 bits. El menor valor posible para una va-
riable char es el carácter Unicode cuyo valor es 0; el
valor más alto posible es el carácter Unicode cuyo valor
es 65.535.
float
Las variables con tipo float pueden contener un valor
de coma flotante de 32 bits con signo. El menor valor
posible para una variable float es aproximadamente
1,5 por 10 elevado a 45; el valor más alto es aproxima-
damente 3,4 por 10 elevado a 38.
doub1e
Las variables con tipo double pueden contener un valor
de coma flotante de 64 bits con signo. El menor valor
posible para una variable double es aproximadamente
5 por 10 elevado a 324; el valor más alto es aproximada-
mente 1,7 por 10 elevado a 308.
decimal
Las variables con tipo decimal pueden contener un va-
lor de coma flotante de 128 bits con signo. Las variables
de tipo decimal son buenas para cálculos financieros.
El menor valor posible para una variable decimal es
aproximadamente 1 por 10 elevado a 28; el valor más
alto es aproximadamente 7,9 por 10 elevado a 28.
bool
Las variables con tipo bool pueden tener uno de los dos
posibles valores: true o false. El uso del tipo bool es
una de las partes en las que C# se aparta de su legado C
y C++. En ellos, el valor entero 0 era sinónimo de f a l s e
y cualquier valor que no fuese cero era sinónimo de true.
Sin embargo, en C# los tipos no son sinónimos. No pue-
de convertir una variable entera en su valor equivalente
bool. Si quiere trabajar con una variable que necesita
tener una condición verdadera o falsa, use una variable
bool y no una variable i n t .
Debe declarar sus variables dentro de una clase o de una función. El siguiente
código es válido:
class MyClass
{
int MylntegerVariable ;
System.Console.WriteLme ("Hello!");
}
}
NOTA: Puede declarar una variable donde desee, pero tenga en cuenta
que si la declara en una función, como se muestra en la variable
AnotherVariable del ejemplo anterior, sólo el código incluido en esa
función podrá trabajar con la variable. Si la declara dentro de la clase,
como en la variable Mylnt egerVariable (también en el ejemplo ante-
rior), todo el código de esa clase podrá trabajar con esa variable. Si toma el
código del ejemplo y le añade otra función a la clase, el código de esa nueva
función podrá trabajar con la variable MylntegerVariable pero no
podrá trabajar con la variable AnotherVariable. Si esta nueva fun-
ción intenta acceder a la variable AnotherVariable declarada en la
función Main(), obtendrá el siguiente mensaje de error del Compilador de
C#:
error CS0103: El nombre 'AnotherVariable' no existe en la clase
o espacio de nombre 'MyClass'
int MyVariable;
FirstTestScore = TestScoresForStudents[0];
Est e co d i go a cced e al p r i me r el e me n to d e la ma triz T e s t s c o -
res ForStudents y a s ig n a s u valo r a l a p ri mera var iab le F i r s t T e s t S c o r e
variable.
P ar a p o n er u n v alo r e n la ma tr iz , s i mp l e me n te acced a al e le me n to u s and o la
mi s ma si n ta x i s, p e ro co l o q ue el no mb r e d e la ma triz y e l n ú me ro d e l e le me n to a l a
izq ui er d a d el si g no i g ua l:
TestScoresForStudents[9] = 100;
Est e có d i go se co mp i la s in er r o r e s, p er o al ej ec u t ar la ap li cac ió n s e p ro d u ce u n
err o r p o rq u e no ha y u n ele me n to 1 0 0 0 e n s u m atri z d e 2 5 e le me n to s. C ua nd o se
lle g a a e s ta i n s tr ucc ió n, e l E n to r no d e ej ec u ció n co mú n ( C LR) d e tie n e el p ro gra -
ma e i nic ia u n me n saj e d e e xcep ció n :
Exception occurred: System.IndexOutOfRangeException : An exception
of type System.IndexOutOfRangeException was thrown.
Pero esto podría resultar confuso. ¿Cómo se usa esta matriz? ¿Aparecen pri -
mero las puntuaciones de un solo estudiante o colo camos en primer lugar las
calificaciones del primer estudiante?
Un modo mejor de declarar una matriz consiste en especificar cada dimensión
por separado. Declarar una matriz multidimcnsional es tan sencillo como colocar
comas entre los corchetes. Coloque una coma menos que el número de dimensio -
nes necesarias para su matriz multidimcnsional. como en la siguiente declaración:
byte[,] TestScoresForStudents;
Este código indica al compilador de C# que quiere crear una matriz con una
dimensión de 10 y otra dimensión de 25. Puede imaginar una matriz de dos di -
me n s io ne s co mo u na ho j a d e ca lc ulo d e Mi cr o so ft E xce l co n 1 0 fi la s y 2 5 co l u m -
na s. La tab la 3 .3 r eco g e el a sp ec to q u e p o d ría te ner e st a matr iz si s u s d ato s
es t u vier a n e n u n a t ab l a .
Examen 1 90 80 85 75
Examen 2 95 85 90 80
FifthScoreForStudenti16 = Tes t Sc o re sF o r S tu d e n t s [ 4 , 1 5 ] ;
En o tra s p alab r a s, c ua nd o tr ab aj e co n u na ma tr iz d e d o s d i me n s io ne s y c o n si -
d er e la ma tr iz co mo u na tab l a, co n sid e r e la p r i me ra d i me n sió n co mo el n u me ro d e
f ila d e la tab l a y e l se g u nd o n u me r o co mo el n u me ro d e co l u mn a .
P ued e i n ic ial iza r lo s el e me n to s d e u na mat r iz mu ltid i me n s io na l c u a nd o d e clar e
la v ar iab le d e ma triz . P a r a e llo , co lo q ue cad a co nj u n to d e v alo re s p ara u na so la
d i me n sió n e n u na li s ta d eli mi t ad a p o r co ma s ro d ead a p o r ll a ve s:
int [,] MyArray = {{0, 1, 2}, {3, 4, 5}};
Figura 3.1. Las matrices escalonadas permiten definir una matriz que contiene otras
matrices, cada una con un número diferente de elementos.
Estas matrices escalonadas tienen dos dimensiones, como las matrices rec -
tangulares. pero cada fila puede tener un número de elementos diferente (lo que
da a las matrices su aspecto escalonado).
Las matrices escalonadas se definen utilizando dos grupos de corchetes inme -
diatamente después del nombre del tipo de matriz. Cuando hace una llamada a
new. se especifica un tamaño para la primera dimensión (la matriz student en
nuestro ejemplo), pero no la segunda. Tras definir la primera matriz, haga una
nueva llamada a new para definir las otras matrices (las matrices score en
nuestro ejemplo):
byte [] [] ArraysOfTestScores ;
Una vez que ha construido la matriz escalonada, puede acceder a sus elemen-
tos de la misma forma que en una matriz rectangular.
IntegerVariable = 12345;
¿Por qué son tan diferentes las matrices? ¿Por qué se necesita utilizar new al
crear una matriz? La respuesta está en la dif erencia entre tipos de valor y tipos de
referencia.
Con un tipo de valor, la variable retiene el valor de la variable. Con un tipo de
referencia, la variable retiene una referencia al valor almacenado en algún otro
sitio de la memoria. Puede imaginar una referencia como una variable que apunta
hacia otra parte de memoria. La figura 3.2 muestra la diferencia.
Figura 3.2. Los tipos de valor contienen datos. Los tipos de referencia contienen
referencias a datos situados en algún otro lugar de la memoria.
Cada uno de los tipos comentados hasta este punto es un tipo de valor. Las
variables proporcionan suficiente capacidad de almacenamiento para los valores
que pueden contener y no necesita new para crear un espacio para sus varia-
bles. Las matrices son tipos de valor y los objetos son tipos de referencia. Sus
valores están en algún otro lugar de la memoria y no necesita usar la palabra
clave new para crear suficiente espacio para sus datos.
Aunque necesita usar la palabra clave new para crear espacio de memoria
para un tipo de referencia, no necesita escribir ningún código que borre la memo -
ria cuando haya acabado de usar la variable. El CLR contiene un mecanismo
llamado recolector de elementos no utilizados que realiza la tarea de liberar la
memoria que no se usa. El CLR ejecuta el recolector de elementos no utilizados
mientras se ejecuta su aplicación de C#. El recolector de elementos no utilizados
registra su programa buscando memoria no usada por ninguna de sus variables.
Liberar la memoria que ya no se usa es tarea del recolector de elementos no
utilizados.
Conversiones implícitas
El compilador de C# realiza automáticamente las conversiones implícitas.
Examine el siguiente código:
int IntegerVaríable;
long LongVariable;
IntegerVariable = 123;
LongVariable = IntegerVanable ;
En este código, a una variable de tipo entero se le asigna el valor 123 y a una
variable long se le asigna el valor de la variable de tipo entero. Cuando se
ejecute este código, el valor de LongVariable es 123.
El compilador de C# convierte el valor de la variable de tipo entero a un valor
long porque la conversión de un valor int a un valor long es una de las
conversiones implícitas permitidas por C#. La tabla 3.4 recoge las conversiones
i mp l íci ta s p er mi tid a s p o r C# . La p r i me r a co l u m na e n u mer a el tip o o r i gi na l d e la
var iab le y la fila s up er i o r en u me r a lo s t ip o s d e d ato s a lo s q u e p u ed e co n ver tir lo .
U na X e n l a ce ld i lla si g ni f ic a q u e p ued e co n v ertir i mp l íc ita me nt e el t ip o d e la
izq ui er d a al tip o e n l a p ar te s up e r io r .
— sbyte byte short ushort int uint long char float ulong decimal double
X - X - X - X - X - X -
- X X X X X X - X X X -
- - X - X - X - X - X X
- - - X X X X - X X X X
- - - - X - X - X - X X
- - - - - X X - X X X X
- - - - - - X - X - X X
- - - X X X X X X X X X
- - - - - - - - X - - X
- - - - - - - -
X X X X
Conversiones explícitas
Si e sc rib e có d i go q ue i nt en te co n v er t ir u n valo r q ue u se tip o s no ad mi t id o s p o r
u na co n ver s ió n i mp l íci t a, e l co mp il ad o r d e C# ge n era u n erro r, co mo m ue s tra el
si g u ie n te có d i go :
char GharacterVariable;
int IntegerVanable;
IntegerVariable = 5;
CharacterVariable = IntegerVariable;
El co mp il ad o r d e C# p r o d uc e e l si g u ie nt e er r o r :
error CS0029: No se puede convertir implícitamente el tipo
'int' a 'char'
Este error se produce porque ninguna convers ión implícita admite la conver-
sión de una variable i n t a una variable c h a r variable.
Si realmente necesita hacer esta conversión, tiene que realizar una conversión
explícita. Las conversiones explícitas se escriben en su código fuente y le dicen al
compilador "haz que se produzca esta conversión, aunque no puede ser realizada
implícitamente". Escribir una conversión explícita en el codigo C # requiere colo-
car el tipo al que está convirtiendo entre paréntesis. Los paréntesis se colocan
justo antes de la variable que está usando como fuente de la conversión. A conti -
nuación se incluye el código anterior pero usando una conversión explícita:
char CharacterVanable;
int IntegerVariable;
IntegerVariable = 9;
CharacterVariable = (char)IntegerVariable;
Esta técnica es conocida como conv ersión de la variable de tipo entero a una
variable de tipo carácter. Algunos tipos no pueden ser convertidos, ni siquiera
mediante una operación de conversión explícita. La tabla 3.5 enumera las conver -
siones explícitas que admite C # . La primera columna enumera el tipo original de
la variable y la fila superior enumera los tipos de datos a los que puede convertir -
lo. Una X en la celdilla significa que puede convertir explícitamente el tipo de la
izquierda al tipo de la parte superior usando la operación de conversión explícita.
.... sbyte byte short ushort int uint long char float ulong decimal double
X X - X X - X X - - - -
X X - - - - - X - - - -
X X X X - X X X - - - -
X X X X - - - X - - - -
X X X X X X - X - X - -
X X X X X X - X - - - -
X X X X X X X X - X - -
X X X - - - - X - - - -
X X X X X X X X X X X -
X X X X X X X X - X - -
X X X X X X X X X X X X
X X X X X X X X X X X X
IntegerVariable = 123;
LongVanable = (long) IntegerVariable;
Al igual que con el resto de las variables, puede inicializar una cadena en la
misma linea que su declaración:
string MyString = "Hello from C#!";
1 Caracteres
Función
\t
Los caracteres especiales \ t incrustan una tabulación
en la cadena. Una cadena definida como h e l l o \ t t h e r e
Caracteres Función
Sin embargo, con el prefijo puede conseguirlo con una sola barra invertida.
string MyFilename = @"C:\Folder1\Folder2\Folder3\file.txt";
MyCharacter = MyString[9];
Declaración de enumeraciones
A diferencia de las variables tratadas hasta el momento, una enumeración no
es un tipo en sí misma, sino una forma especial de tipo de valor. Una enumeración
se deriva de S y s t e m . E n u m y proporciona nombres para valores. El tipo subya-
cente que representa una enumeración debe ser b y t e . s h o r t . i n t o l o n g .
Cada campo de una enumeración es estático y representa una constante.
Para declarar una enumeración, debe usar la palabra clave e n u m seguida del
nombre de la enumeración. A continuación debe escribir una llave de apertura
seguida por una lista de las cadenas de la enumeración y finalizar con una llave
de cierre, como se muestra en el siguiente ejemplo:
public enum Pizza
{
Supreme,
MeatLovers,
CheeseLovers,
Vegetable,
}
Resumen
Este capitulo examina las variables y sus tipos. Hay muchas clases de tipos de
valor y cada uno tiene sus propias características y r equisitos de memoria. Algu-
nos tipos pueden ser convertidos implícitamente a otros tipos, pero otros deben
ser convertidos explícitamente usando la sintaxis apropiada.
Las matrices contienen colecciones de variables del mismo tipo. Son útiles
cuando se necesita mantener un conjunto de variables del mismo tipo. C# admite
matrices unidimensionales y multidimensionales. Las matrices de C# son de base
cero: es decir, el número del primer elemento de una matriz es el 0. Las cadenas
le
ayudan a trabajar con partes de texto en su código. Son conjuntos de caracteres
Unicode. C# permite incrustar caracteres especiales en sus cadenas, pero pro -
porciona el prefijo @ para especificar los casos en los que no necesite que se
procesen los caracteres especiales. Se puede acceder a los caracteres de una
cadena como si fueran matrices de caracteres.
4 Expresiones
Como puede ver, éste es un enfoque mucho más claro para usar un valor
literal.
Literales booleanos
C# define dos valores literales booleanos, las palabras clave True y False:
bool MyTrueVariable = true;
bool MyFalseVariable = false;
Se pueden escribir literales enteros usando una notación decimal o una nota-
ción hcxadccimal. De forma parecida a los literales vistos anteriormente, estos
literales permiten ordenar el código. Los valores literales pueden ser colocados en
la parte superior del listado del código. Si resultara necesario modificar estos en
alguna ocasión, resultaria muy sencillo cambiar la ocurrencia del valor.
Los literales decimales integrales se escriben como series de uno o más núme-
ros usando los caracteres 0, 1, 2, 3, 4, 5, 6, 7, 8 y 9:
int MyVariable = 125;
NOTA: Si quiere utilizar una comilla simple como literal de carácter, de-
berá anteponerle una barra invertida. Escribir confunde al compilador
de C#. Escriba en su lugar '\'.
Cuando se compila este código, el ejecutable contiene una copia del literal de
la cadena Helio. Las dos variables de cadena leen el valor de la única copia
almacenada en el ejecutable. Esta optimización permite al compilador de C# con -
servar el uso de memoria del código, ya que almacenar sólo una copia del literal
requiere menos memoria que almacenar dos copias del mismo literal.
Cómo usar los literales nuil
El literal nuil es una palabra clave de C# que permite poner un objeto en un
estado nulo o sin uso:
object MyObject = null;
Uso de identificadores
Los identificadores C# son ejemplos de expresiones simples. Los identificadores
tienen un tipo y el tipo se especifica cuando se declara el ídentificador:
int MyVariable = 123;
class MyClass
{
public static void Main()
{
int GetArraylndex()
{
return 0 ;
}
}
ciass MyClass
{
public static void Main ()
{
//llame DoWork() en este objeto
MyClass myclass = new MyClass();
myclass.DoWork();
}
void DoWork()
{
MyClass myclass = new MyClass();
this.DoWork2();
// haga aquí su trabajo
)
void DoWork2()
{
}
}
class MyClass
{
public static void Main()
{
int Mylnteger;
Mylnteger = 125;
Mylnteger++; // el valor ahora es 126
Mylnteger--; // el valor ahora vuelve a ser 125
}
}
class Listing4_5
{
publíc static void Mam ()
{
int Intl;
int Int2;
int
Int1PlusIn
t2;
Intl = 2000000000;
Int2 = 2000000000;
IntlPlusInt2 = Intl + Int2;
System.Consolé.WriteLine(IntlPlusInt2);
}
}
A cada variable entera Intl y Int2 se le asigna el valor de dos mil millones.
Esta operación no supone ningún problema porque las variables enteras pueden
almacenar valores por encima de dos mil cien millones. Sin embargo, sumar estos
dos integrales y almacenar el resultado en otro integral va a suponer un problema.
La suma sería cuatro mil millones, lo que supera el valor límite de un entero, poco
más de dos mil cien millones.
Compile el código anterior con la línea de comando habitual.
esc Listing4-l.cs
class Listing4_6
{
public static void Main()
{
int Intl;
int Int2;
int IntlPlusInt2;
Intl = 2000000000;
Int2 = 2 0 0 0 0 0 0 0 0 0 ;
IntlPlusInt2 = checked(Intl + Int2);
System.Console.WriteLme(IntlPlusInt2);
}
}
class MyClass
i
public static void Main()
{
bool MyBoolean;
MyBoolean = true;
MyBoolean = IMyBoolean; // "MyBoolean" ahora es false
}
)
class MyClass
{
public static void Main()
{
int Int1;
Int1 = 123;
Intl = Int1;
}
}
class MyClass
{
public static void Main ()
{
int Mylnteger;
Mylnteger = 125;
++Mylnteger; // el valor ahora es 126
--Mylnteger; // el valor ahora vuelve a ser 125
}
}
class Listing4_10
{
public static void Main()
{
int Int1;
Int1 = 123;
System.Console.WriteLine(Int1++);
System.Console.WriteLine (++Int1);
}
}
115
Figura 4.2. Uso de operadores prefijos y postfijos
class MyClass
{
public static v o i d M a i n ( )
{
int Mylnteger;
class MyClass
(
public static void Main ()
{
int Mylnteger;
class MyClass
{
public static void Main()
{
int Mylnteger;
Mylnteger = 7 / 3 ;
}
}
Cuando se ejecuta este código, la variable Mylnteger tiene un valor de 2.
porque si se divide 7 entre 3 queda un cociente de 2 y un resto de 1.
Si se divide un valor entre una variable y se coloca el resultado en la misma
variable, se puede escribir una instrucción abreviada para realizar la división.
Escribiendo una barra inclinada seguida por un signo igual se divide un valor
entre una variable y se actualiza el valor de la variable con el resultado:
Mylnteger /= 3 ;
class MyClass
{
public static void Main()
{
int Mylnteger;
Mylnteger = 7 3 ;
}
}
class MyClass
{
public static void Main()
{
int Mylnteger;
class MyClass
{
public static void Main()
{
int Mylnteger;
class MyClass
{
public static void Main()
{
int Mylnteger;
Mylnteger = 6 << 3;
}
}
Cuando se ejecuta este código, la variable M y l n t e g e r tiene un valor de 48.
porque el valor original. 6. es considerado un número binario con un valor
binario
de 00000110. Cada bit en el valor original se desplaza tres lugares, que es el
valor
que aparece después del operador de desplazamiento a la izquierda y se colocan
ceros en los bits de orden inferior. Al cambiar cada bit tres lugares da como
resultado un valor binario de 00110000 o 48 en el sistema decimal.
Se pueden aplicar desplazamientos a la izquierda a los valores de las
expresio-
nes de tipo i n t , u i n t , l o n g y u l o n g . También pueden desplazarse a la iz-
quierda otras expresiones que pueden ser convertidas a uno de estos tipos. Las
expresiones de tipo int y uint pueden desplazarse hasta 32 bits de una vez.
Las expresiones de tipo long y ulong pueden ser desplazadas hasta 64 bits de
una vez.
Si se está calculando una operación de desplazamiento a la izquierda de un valor
y una variable, y se coloca el resultado en la misma variable, se puede escribir una
instrucción abreviada que realice esta operación. Al escribir dos signos menor que
(«) seguidos por un signo igual se calcula la operación de desplazamiento a la
izquierda y se actualiza el valor de la variable con el resultado.
Mylnteger <<= 3;
class MyClass
(
public static void Main()
{
int Mylnteger;
Mylnteger = 48 >> 3;
}
}
Instrucciones de C#
Una instrucción es una expresión válida de C# que define una acción realiza -
da por el código. Las instrucciones pueden examinar valore s de variables, asig-
nar nuevos valores a una variable, llamar a métodos, realizar una operación, crear
objetos o realizar alguna otra acción.
La instrucción más corta posible en C# es la instrucción vacia. Ésta consiste
en sólo el punto y coma:
Se puede usar la instrucción vacía para decir. "No hagas nada aquí". Esto
podría no parecer muy útil, pero tiene su función.
MyVariable = 123;
MyVariable += 234;
Al usar una constante se logra que el código sea un poco más sencillo de
entender:
const double Pi = 3.14159;
}
También se pueden usar bloques de instrucciones en las cláusulas else:
if (MyVariable == 123)
{
System.Console.WriteLine("MyVariable's value is 123.");
System.Consolé.WriteLine("This prints if MyVariable ==
123.");
}
else
{
System.Consolé.WriteLine("MyVariable's valué is not 123.");
System.Consolé.WriteLine("This prints if MyVariable !=
123.");
}
La instrucción switch
La instrucción switch evalúa una expresión y compara el valor de esa ex-
presión con varios casos. Cada caso se asocia con una lista de instrucciones, que
recibe el nombre de sección de switch. C# ejecuta la lista de instrucción asociada
con la sección de switch que concuerde con el valor de la expresión.
La expresión usada como controlador de la instrucción switch se encierra
entre los paréntesis que siguen a la palabra clave switch. La expresión va
seguida por llaves y las secciones de switch están entre las llaves.
switchÍMyVariable)
{
// aquí se colocan las secciones de switch
)
La instrucción do
La instrucción while ejecuta sus instrucciones incrustadas cero o más veces.
Si la expresión booleana usada en la expresión while devuelve false. ninguna
de las instrucciones incrustadas se ejecuta:
int MyVariable = 100;
Las sentencias incrustadas siempre se ejecutan al menos una vez debido a que
la expresión booleana se evalúa después de que se ejecuten las instrucciones in-
crustadas, como se puede ver en el siguiente código:
int MyVariable = 100;
do
{
System.Console.WriteLine(MyVariable);
MyVariable++;
}
while(MyVariable < 10);
La instrucción for
La instrucción for es la instrucción de iteración más potente. El código de
control de una instrucción for se divide en tres partes.
• Un iniciador. que fija las condiciones iniciales de la instrucción de bucle
for.
• Una condición, que especifica la expresión booleana que mantiene ejecu -
tándose la instrucción f o r .
• Un iterador, que especifica las instrucciones que se ejecutan al final d e
cada paso por las instrucciones incrustadas.
La instrucción f o r empieza con la palabra clave f o r . seguida por paréntesis,
que contienen las instrucciones iniciadora, de condición y de iteración, todas
sepa-
radas por puntos y coma. Las instrucciones incr ustadas siguen a los paréntesis.
Observe el siguiente bucle simple f o r :
int MyVariable;
}
Este código también es equivalente al código original:
int MyVariable;
for(MyVariable = 0; ; MyVariable++)
{
System.Console.WriteLine(MyVariable);
}
for(MyFirstVariable = 0, MySecondVariable = 0;
MyFirstVaríable < 10;
MyFirstVariable++, MySecondVariable++)
{
System.Console.WriteLine(MyFirstVariable);
System.Console.WnteLine(MySecondVariable);
}
La instrucción foreach
Se puede usar la instrucción foreach para repetir varias veces los elemen -
tos de una colección. Las matrices de C# admiten la instrucción foreach y
pueden usarse para trabajar fácilmente con cada elemento de la matriz.
La instrucción foreach se usa escribiendo la palabra clave foreach se-
guida de paréntesis. Estos paréntesis deben contener la siguiente información:
• El tipo del elemento de la colección.
• Un nombre identificador para un elemento de la colección.
• La palabra clave in.
• El identificador de la colección.
Tras los paréntesis se colocan las instrucciones incrustadas.
El listado 5.1 muestra la instrucción foreach en acción. Crea una matriz
entera de cinco elementos y luego usa la instrucción foreach para acudir a cada
elemento de la matriz y escribir su valor en la consola.
Listado 5.1. Usando la instrucción foreach
class Listmg5^1
{
public static void Main()
{
int[] MyArray;
La instrucción continué
La instrucción continué devuelve el control a la expresión booleana que
controla una instrucción de iteración, como se puede ver en el siguiente código:
int MyVariable;
Resumen
C# dispone de varios medios de controlar la ejecución del código, dándole
opciones para ejecutar un bloque de código más de una vez o. a veces, ninguna
vez. basándose en el resultado de una expresión booleana.
La instrucción if ejecuta el código una vez. pero sólo si la expresión booleana
que la acompaña devuelve true. La instrucción if puede incluir una cláusula
else. que ejecuta un bloque de código si la expresión booleana devuelve false.
La instrucción switch ejecuta uno de los muchos bloque s de código posi-
bles. Cada bloque de código viene precedido de una lista de instrucciones de
caso.
C# evalúa la expresión de la instrucción switch y a continuación busca una
lista de instrucciones de caso cuyo valor coincida con la expresión evaluada en la
instrucción switch.
Las instrucciones while. do y for continúan ejecutando el código mientras
la expresión booleana indicada sea true. Cuando la expresión booleana devuel -
ve false. las instrucciones incrustadas dejan de ejecutarse. Las instrucciones
while y for se pueden definir para que sus expresiones booleanas devuelvan
inmediatamente false. lo que quiere decir que sus instrucciones incrustadas
nunca llegan a ejecutarse realmente. La instrucción do, sin embargo, siempre
ejecuta sus instrucciones incrustadas al menos una vez.
La instrucción foreach proporciona un buen modo de recorrer repetida y
rápidamente los elementos de una matriz. Puede ordenar a un bucle foreach
que recorra repetidamente los elementos de una matriz sin conocer el tamaño de
la matriz o los elementos que la forman. La instrucción foreach prepara un
identificador especial formado por el valor del elemento de una matriz durante
cada iteración del bucle foreach.
Las instrucciones break, continue y goto afectan al flujo normal de una
instrucción de iteración, como while o foreach. La instrucción break sale
del bucle iterativo, incluso si la expresión booleana que controla la ejecución del
bucle sigue devolviendo true. La instrucción continue devuelve el control a
la parte superior del bucle iterativo sin ejecutar ninguna de las instrucciones
incrustadas que la siguen. La instrucción goto siempre transfiere el control a la
instrucción etiquetada.
Puede acompañar sus operaciones matemáticas con instrucciones checked o
unchecked para especificar cómo quiere tratar los errores matemáticos del
código C#.
6 Cómo
trabajar
con métodos
Los métodos son bloques de instrucciones que devuelven algún tipo de valor
cuando se ejecutan. Pueden ser llamados mediante el nombre y llamar a un méto -
do hace que las instrucciones del método se ejecuten. Ya hemos visto un método:
el método Mai n (). Aunque C# permite poner todo el código en el método Mai n () .
probablemente quiera diseñar sus clases para definir más de un método. El uso de
métodos mantiene el código legible porque las instrucciones se colocan en
bloques
más pequeños, en lugar de en un gran bloque de código. Los métodos también
permiten tomar instrucciones que pueden ser ejecutadas varias veces y colocarlas
en un bloque de código que puede ser llama do todas las veces que haga falta.
En este capítulo, aprenderá a crear funciones que devuelven datos y que no los
devuelven. Aprenderá a pasar parámetros a los métodos y la mejor manera de
estructurar un método para hacer sus aplicaciones modulares.
La estructura de un método
Como mínimo, un método está compuesto de las siguientes partes:
• Tipo devuelto
• Nombre del método
• Lista de parámetros
• Cuerpo del método
Los métodos tienen otras partes opcionales, como las listas de atributos y los
modificadores de ámbito. Las siguientes secciones analizan los fundamentos de
un método.
Tipo devuelto
Un método comienza definiendo el tipo de datos que devolverá cuando se le
llame. Por ejemplo, suponga que quiere escribir un método que suma dos números
enteros y devuelve el resultado. En esc caso, escribirá el tipo devuelto como i n t .
C# permite escribir un método que no devuelve nada. Por ejemplo, puede es-
cribir un método que simplemente escriba algún texto en la consola, pero que no
calcule ningún dato que deba devolver al código que llamó al método. En ese
caso, se puede usar la palabra clave v o i d para indicar al compilador de C# que
el método no devuelve ningún dato.
Si se quiere devolver un valor de un método se usa la palabra clave r e t u r n
para especificar el valor que debe devolverse. La palabra clave va seguida de una
expresión que evalúa el tipo de valor que debe devolverse. Esta expresión puede
ser un valor literal, una variable o una expresión más compleja.
Lista de parámetros
Se puede llamar a métodos con los parámetros que se usan para pasar los datos
al método. En el ejemplo anterior, en el que un método suma dos números enteros,
se necesitaría enviar al método los valores de los dos números enteros que se van
a sumar. La lista de variables recibe el nombre de lista de parámetros del método.
La lista de parámetros del método aparece entre paréntesis y sigue al nombre del
método. Cada parámetro de la lista de parámetros está separado por una coma e
incluye el tipo del parámetro seguido por su nombre.
También se puede prefijar los parámetros de la lista de parámetros con modi-
ficadores que especifican cómo se usan sus valores dentro del método. Veremos
estos modificadores más adelante en este mismo capítulo.
Se pueden definir métodos que no reciben ningún parámetro. Si se quiere utili-
zar uno de estos métodos, basta con dejar vacíos los paréntesis. Ya hemos visto
esto en los métodos Main() escritos. También se puede colocar la palabra clave
void entre los paréntesis para especificar que el método no acepta parámetros.
class Listing6_l
{
public static void Main()
{
Listing6_1 MyObject;
void CallMethod()
{
System.Consolé.WriteLine("Hello from CallMethod()!");
}
}
NOTA: Necesita las instrucciones de Main () que crean uií nuevo obje-
to Listihq6_ l antes de poder llamar a ios métodos del objeto.
Si el método se define con una lista de parámetros, sus valores deben ser
especificados en el momento de llamar al método. Debe especificar los
parametros
en el mismo orden en que son especificados en la lista de parámetros del
método,
como muestra el listado 6.2.
Listado 6.2. Llamada a un método con un parámetro
class Listing6_2
I
public static void Main()
{
int Mylnteger;
Listingt6_2 MyObject;
parámetros:
class Listing6_3
{
public static void Main()
{
Listing6_3 MyObject;
Figura 6.2. La llamada a un método con un tipo de datos no válido produce errores
de compilación.
class Listing6_4
f
public static void Main()
(
Listing6_4 MyObject;
int ReturnValue;
Tipos de parámetros
C# permite cuatro tipos de parámetros en una lista de parámetros:
• Parámetros de entrada
• Parámetros de salida
• Parámetros de referencia
• Matrices de parámetros
Parámetros de entrada
Los parámetros de entrada son parámetros cuyo valor es enviado al método.
Todos los parámetros que se han usado hasta ahora han sido parámetros de entra-
da. Los valores de estos parámetros de entrada se envían a la función, pero el
cuerpo del método no puede cambiar permanentemente sus valores.
El listado 6.4 del anterior ejemplo define un método con dos parámetros de
entrada: Integer1 y Integer2. Los valores de estos parámetros se introdu-
cen en el método, que lee sus valores y hace su trabajo. Los parámetros de entrada
se pasan a los métodos por valor. Básicamente, el método ve una copia del valor
del parámetro, pero no se le permite cambia r el valor proporcionado por la parte
que realiza la llamada. En el listado 6.5 se puede ver un ejemplo.
Listado 6.5. Cómo modificar copias de parámetros de entrada
c1ass Listing6_5
{
public static void Main()
{
int Mylnteger;
Listing6_5 MyObject;
Parámetros de salida
Los parámetros de salida son parámetros cuyos v alores no se establecen cuando
se llama al método. En su lugar, el método establece los v alores y los devuelve al
elemento que hace la llamada mediante el parámetro de salida. Suponga, por
ejemplo, que quiere escribir un método que cuente el número de registros de una
tabla de una base de datos. Suponga que también quiere especificar si la opera -
ción se realizó satisfactoriamente. (La operación puede no realizarse si, por ejem-
plo. la tabla de bases de datos no está disponible.) Por tanto, queremos que el
método devuelva dos instancias de información:
• Un contador de registros.
• Un indicador de éxito de operación.
C# sólo permite a los métodos devolver un valor. ¿,Qué hacer si queremos que
devuelva dos instancias de información?
La respuesta está en el concepto de parámetro de salida. Puede hacer que su
método devuelva el indicador de éxito de la operación como un valor booleano y
especificar el recuento de registros como un parámetro de salida. El método alma -
cena el recuento de registros en una variable de salida, cuyo valor es recogido por
el elemento que hizo la llamada.
Los parámetros de salida se especifican en listas de parámetros con la palabra
clave out. La palabra clave out debe preceder al tipo de par ámetro en la lista de
parámetros. Cuando se llama a un método con un parámetro de salida, se debe
declarar una variable que contenga ese valor, como se ve en el listado 6.6.
Listado 6.6. Cómo trabajar con parámetros de salida
class Listing6_6
{
public static void Main()
{
int Mylnteger;
Listing6_6 M y O b j e c t ;
Debe usarse la palabra clave o u t dos veces por cada parámetro: una vez
cuando se declara el parámetro y otra vez cuando se especifica el parámetro de
salida al llamar al método. Si se olvida la palabra clave o u t al llamar a un
método con un parámetro de salida, se obtienen los siguientes errores del
compilador:
Listing6-6.cs (9, 6): error CS 1502: La mejor coincidencia de
método sobrecargado para ' Listing6_6.CallMethod(out int)' tiene
algunos argumentos no validos
Listing6-6.cs (9,26) : error CS1503: Argumento '1': no se puede
convertir de 'int' a 'out int'
Cualquier valor asignado a variables usadas como parámetros de salida antes
de que se llame al método se pierde. Los valores originales se sobrescriben con los
valores que les asignó el método.
Parámetros de referencia
Los parámetros de referencia proporcionan valores por referencia. En otras
palabras, el método recibe una referencia a la variable especificada cuando se
llama al método. Piense en un parámetro de referencia como en una variable de
entrada y de salida: El método puede leer el valor original de la variable y también
modificar el valor original como si fuera un parámetro de salida.
Los parámetros de referencia se especifican en listas de parámetros con la
palabra clave ref. La palabra clave ref debe preceder al tipo del parámetro en
la lista de parámetros. Cuando se llama a un método con un parámetro de referen-
cia. se debe declarar una variable para que contenga el valor del parámetro de
referencia, como se ve en el listado 6.7.
Listado 6.7. Cómo trabajar con parámetros de referencia
class Listing6_7
{
public static void Main()
{
int Mylnteger;
Listing6_7 MyObject;
Matrices de parámetros
Los métodos suelen escribirse para recibir un número especifico de parámetros.
Un método con una lista de tres parámetros siempre espera ser llamada con tres
parametros, ni más. ni menos. Sin embargo, a veces puede ocurrir que un método
no conozca cuántos parámetros debe aceptar al ser diseñado. Puede escribir un
método que acepte una lista de cadenas que especifiquen los nombres de los regis-
tros que deberán borrarse del disco. ¿.Cuántas cadenas debe permitir el método'.’
Para ser flexible, el método debe diseñarse de manera que el invocador pueda
especificar las cadenas que necesita. Esto hace que llamar al método sea un poco
más flexible porque el elemento que realiza la llamada ahora puede decidir cuan-
tas cadenas deben pasarse al método. Sin embargo ¿cómo escribiremos la lista
de parametros de la lista cuando el método no conoce cuántos parámetros le
serán pasados ?
Las matrices de parámetros resuelven este problema de diseño ya que permiten
especificar que el método acepte un número variable de argumentos. La matriz de
parámetros de la lista de parámetros se especifica usando la palabra clave de C#
p a r a m s . seguida del tipo de variable que deberá proporcionar el llamador La
especificación de tipos va seguida de corchetes, que a su vez van seguidos del
identificador de la matriz de parametros, como se ve en el listado 6.8.
Listado 6.8. Cómo trabajar con matrices de parámetros
class Listing6_8
{
public static void Main()
{
Listing6_8 MyObject;
Sobrecarga de métodos
C# permite definir varios métodos con el mismo nombre en la misma clase,
siempre que esos métodos tengan listas de parámetros diferentes. Esta operación
se conoce como sobrecargar el nombre del método. Observe el ejemplo en el
listado 6.9.
Listado 6.9. Cómo trabajar con métodos sobrecargados
class Listing6_9
{
public static void Main()
{
Listing6_9 MyObject;
Métodos virtuales
Para proseguir con el tema de los métodos virtuales, hay que comprender el
concepto de herencia. La herencia basa una clase en otra ya existente, añadiendo
o quitando funcionalidad según se necesite. En las siguientes secciones examina-
remos cómo se crean y se usan los métodos virtuales.
Métodos sobrecargados
Para empezar esta sección, construirá un ejemplo de clase llamado B o o k s .
Esta clase contiene dos métodos llamados T i t l e y R a t i n g . El método T i t l e
devuelve el nombre de un libro y el método R a t i n g devuelve una cadena indi-
cando el número de estrellas con que ha sido calificado el libro en cuestión. En el
listado 6.10 se recoge el código completo de su aplicación. Escríbalo en su editor
de texto preferido y compílelo como hizo antes.
using System;
namespace BookOverride
{
class Book
{
public string Title()
{
return "Programming Book";
}
public string Rating()
{
return "5 Stars";
}
}
class Class1
{
static void Main(string[] args)
{
Book bc = new Book();
Console.WriteLine(be.Title());
Console.WriteLine(be.Rating());
}
}
}
Este código crea una clase Wiley que hereda la clase Book. Ahora puede
para crear un nuevo método público llamado Title. Como ya se ha asignado a
este método el mismo nombre que al definido en la clase Book, se sobrecarga el
método Title aunque sigue disponiendo de acceso a los otros miembros dentro
de la clase Book.
Resumen
C# permite escribir métodos en sus clases de C#. Los mé todos pueden ayudar
a dividir el código en partes fáciles de entender y pueden brindar un lugar único en
el que almacenar codigo que puede ser llamado varias veces.
Las funciones pueden recibir parametros. Los parámetros de entrada contie -
nen valores que han sido pasados a los métodos, pero sus valores no pueden
cambiar. Los parámetros de salida tienen valores que les son asignados por un
método y el valor asignado es visible para el elemento que hace la llamada. Los
parámetros de referencia contienen v alores que pueden ser proporcionados dentro
de la función y ademas, su valor puede ser modificado por el método. Las matri -
ces de parámetros permiten escribir métodos que toman un número variable de
argumentos.
C# también permite sobrecargar métodos. Los mé todos sobrecargados tienen
el mismo nombre pero diferentes listas de parámetros. C# usa los parametros
proporcionados en una llamada para determinar qué método debe invocar cuando
se ejecute el código.
7 Agrupación
de datos
usando
estructuras
class Listing7_1
{
struct Point
{
public int X;
public int Y;
}
public static void Main()
{
Point MyPoint;
MyPomt.X = 100;
MyPoint.Y = 200;
System.Console.WriteLine(MyPoint.X);
System.Console.WriteLine(MyPoint.Y);
}
}
El resultado de este ejemplo debería ser el siguiente:
100
200
Se puede asignar una variable de estructura a otra, siempre que las estructuras
sean del mismo tipo. Cuando se asigna una variable de estructura a otra. C#
asigna el valor de la variable de estructura que aparece antes del signo igual a los
valores correspondientes de la estructura que aparece después del signo igual,
como se puede observar en el listado 7.2
class Listing7_2
{
struct Point
{
public int X;
public int Y;
}
MyFirstPoint.X = 100;
MyFirstPoint.Y = 100;
MySecondPoint.X = 200;
MySecondPoint.Y = 200;
System.Console.WriteLine(MyFirstPoint.X);
System.Console.WriteLine(MyFirstPoint.Y);
MyFirstPoint = MySecondPoínt;
System.Console.WriteLine(MyFirstPoint.X);
System.Console.WriteLine(MyFirstP oint.Y);
}
}
Todos los valores de una estructura se sobrescriben en una asignación con los
valores de la variable de estructura indicada después del signo igual.
class Listing7_3
{
struct Point
{
public int X;
public int Y;
public Point(int InitialX)
{
X = InitialX;
Y = 10 0 0 ;
}
System.Console.WriteLine(MyFirstPomt.X);
System.Console.WriteLine(MyFirstPoint.Y);
System.Console.WriteLine(MySecondPoint.X);
System.Console.WriteLine(MySecondPoint.Y);
System.Console.WriteLine(MyThirdPoint.X);
System.Console.WriteLine(MyThirdPoint.Y);
}
)
La figura 7.2 ilustra el resultado de compilar y ejecutar el código del listado
7.3.
Esta declaración indica: "Crea una nuev a estructura Point usando el construc-
tor que tiene dos enteros. Asigna su valor a la variable MyThirdPoint". Debi-
do a las reglas de asignación de estructuras anteriormente descritas, los miembros
de la variable MyThirdPoint reciben los valores de los miembros de la nuev a
estructura. No es necesario hacer nada más con la nueva estructura creada cuan-
do se llamó a new. El entorno común de ejecución (CLR) detecta que la estruc-
tura ya no se usa y se deshace de ella mediante el mecanismo de recolección de
elementos no utilizados.
En el listado 7.3 también aparece la sintaxis del constructor sin parámetros:
Point MyFirstPoint = new Point();
}
}
class Listing7__5
{
struct Point
{
public int X;
public int Y ;
if (MySecondPoint.IsAtOriqin() == true)
S y s t e m . C o n s o l e .WriteLine ( "MySecondPoint is at the
origin.");
else
System.Console.WriteLine("MySecondPoint is not at the
origin.");
}
La estructura P o i nt del listado 7.5 declara un método llamado TsAtOriqin.
El codigo de ese método comprueba los valores de los métodos de la estructura y
devuelve truca si las coordenadas del punto son (0. 0) y false en cualquier otro
caso. El método Main() declara dos variables de tipo Point: a la variable
MyFirstPoint se le asignan las coordenadas (100. 200) usando el constructor
explícito v a la variable MySecondPoint se le asignan las coordenadas (0. 0)
usando el constructor por defecto sin parámetros. En esc momento el método
Main() llama al método IsAtOrigin con los dos puntos y escribe un mensaje
basado en el valor devuelto por el método. Si se compila y ejecuta el código del
listado 7.5. se obtiene el siguiente resultado en la consola:
MyFirstPoint is not at the origin.
MySecondPoint is at the origin.
Hay que asegurarse de prefijar los métodos con la palabra clave public si se
quiere que sean accesibles por todo el resto del código de la clase.
class Listing7_6
{
struct Point
{
private int x;
public int X
{
get
{
return x;
}
set
{
x = value;
}
}
}
MyPoint.X = 10;
RetValue = MyPoint.X;
System.Console.WriteLine(RetValue);
}
}
}
}
Como se puede ver. este ejemplo crea un nuevo objeto MyStruct y asigna a
los miembros data el valor 5. lo que indica que se usan cinco copias de esta
estructura. Para hacer referencia a cada copia de esta estructura se usa un número
indizador (de 0 a 5) y se almacenan los nombres dentro de la estructura. Para
asegurarse de que todos lo datos permanecen intactos, se aplica un simple bucle a
los posibles números de índice y se escribe el resultado en la consola.
En la figura 7.4 se ilustra el resultado del listado 7.7.
Figura 7.4. Para devolver fácilmente los datos se incluye un indizador dentro
de la estructura
Un indizador en una estructura puede ser muy útil cuando se trabaja con gran-
des cantidades de datos del mismo tipo. Por ejemplo, si se va a leer la información
de una dirección desde una base de datos, éste es un excelente lugar para
almacenarla. Todos los campos se mantienen mientras se proporciona un meca-
nismo para acceder fácilmente a cada dato de los registros.
interface IInterface
(
void Method();
}
struct MyStruct : IInterface
{
public void Method()
{
System.Console.WriteLine("Structure Method");
}
}
Este código crea una interfaz llamada IInterface. Esta interfaz contiene
la definición de un método llamado Method. Se crea una estructura y al final de
su nombre se incluyen dos puntos seguidos del nombre de la interfaz que desea
derivar. El método, que simplemente escribe una línea de texto en la consola, se
incluye en la estructura. En la figura 7.5 es ilustra el resultado del programa.
Para demostrar lo importante que es la interfaz, si elimine las cuatro líneas que
componen el método Method en la estructura MyStruct y vuelve a compilar
el programa, obtendrá el siguiente mensaje de error:
Classi.cs(8,9): error CS0535: 'Listing7_8.MyStruct' no implementa el
miembro de interfaz Listing7_8.IInterface.Method()'
sbyte System.SByte
byte System.Byte
short S ys tem.In116
ushort System.Uin116
int System.Int32
uint System.Uint32
long System.Int64
ulong System.Uint64
char System.Char
float System.Single
double System.Double
bool System.Boolean
decimal System.Decimal
Este esquema es parte de lo que hace que el código C# sea compatible con
otros lenguajes NET. Los valores C# se asigna a las estructuras NET que pue-
de usar cualquier lenguaje NET. porque el CLR puede usar cualquier estructura
NET La asignación de las palabras claves de C# con estructuras NET también
permite que las estructuras usen técnicas como la sobrecarga de operadores para
definir el comportamiento del valor cuando se usan en una expresión con un opera-
dor. Se analizará la sobrecarga de operadores cuando se traten las clases C#.
Si lo desea, puede usar los nombres reales de estructura .NET en lugar de las
palabras clave de C#. El listado 7.9 muestra el aspecto que tendría el listado 7.5
si se escribiera usando los nombres de estructuras NET.
Listado 7.9. Cómo usar los nombres de tipo de estructura NET
class Listing7_9
{
struct Point
{
public System.Int32 X;
public System.Int32 Y;
if (MyFirstPoint.IsAtOrigin() == true)
System.Console.WriteLine("MyFirstPoint is at the origin.");
else
System.Console.WriteLine("MyFirstPoint is not at the
origin.");
íf (MySecondPoint.IsAtOrigin() == true)
System.Console.WriteLine("MySecondPomt is at the
origin.");
else
System.Console.WriteLine("MySecondPomt is not at the
origm.");
}
}
Resumen
Las estructuras permiten agrupar un conjunto de variables con un solo nom -
bre. Las variables pueden declararse usando el identificador de estructura.
Las estructuras se declaran usando la palabra clave de C# struct. Todas las
estructuras de C# tienen un nombre y una lista de miembros de datos. C# no pone
ningún límite al número de miembros de datos que puede contener una estructura.
Se accede a los miembros de estructuras mediante la notación StructName .
MemberName. Los miembros se pueden usar en cualquier par te donde esté per-
mitido usar su tipo de datos, incluyendo expresiones y parámetros de métodos.
Las estructuras pueden implementar tanto métodos como variables. Los miem -
bros de estructuras se invocan usando la notación StructName . MethodName
y se usan igual que los nombres de método de clase. Las estructuras también
implementan métodos especiales llamados constructores, que micializan la es -
tructura con un estado conocido antes de usar la estructura.
Los tipos de valor de C# son asignados a estructuras definidas por el CLR de
NET Esto es lo que permite que otros códigos de NET usen los datos. Todas las
variables son compatibles con CLR de NET porque las variables se definen
usando estructuras compatibles con el CLR
Parte II
Programación
orientada a
objetos con C#
8 Escribir
código
orientado
a objetos
Los lenguajes de programación siempre se han diseñado en torno a dos con-
ceptos fundamentales: los datos y el código que opera sobre los datos. Los len-
guajes han evolucionado a lo largo del tiempo para cambiar el modo en que estos
dos conceptos interactúan. En un principio, lenguajes como Pascal y C invitaban
a los programadores a escribir software que tratara al código y a los datos como
dos cosas separadas, sin ninguna relación. Este enfoque dio a los programadores
la libertad, pero también la obligación, de elegir el modo en que su código gestio-
na los datos.
Además, este enfoque obligaba al programador a traducir el mundo real que se
quería modelar usando software a un modelo específico para ordenadores que
usara datos y código.
Los lenguajes como Pascal y C se construyeron en torno al concepto de proce-
dimiento. Un procedimiento es un bloque de código con nombre, exactamente
igual que los actuales métodos de C#. El estilo de software desarrollado usando
estos lenguajes se llama programación procedural.
En la programación procedural. el programador escribe uno o más procedi-
mientos y trabaja con un conjunto de variables independientes definidas en el
programa. Todos los procedimientos pueden verse desde cualquier parte del códi-
go de la aplicación y todas las variables pueden ser manipuladas desde cualquier
parte del código.
En los años 90. la programación procedural dio paso a lenguajes como Smalltalk
y Simula, que introdujeron el concepto de obj eto. Los inventores de estos lengua -
jes se dieron cuenta de que el ser humano no expresa ideas en términos de bloques
de código que actúan sobre un grupo de variables; en su lugar, expresan ideas en
términos de objetos. Los objetos son entidades que tienen un conjunto de valores
definidos (el estado del objeto) y un conjunto de operaciones que pueden ejecutar -
se sobre ese objeto (los comportamientos del objeto). Por ejemplo, imagine un
cohete espacial.
Un cohete espacial tiene estados, como la cantidad de combustible o el número
de pasajeros a bordo, y comportamientos, como "despegar" y "aterrizar". Ademas,
los objetos pertenecen a clases. Los objetos de la misma clase tienen el mismo
estado y el mismo conjunto de comportamientos. Un objeto es un caso conc reto de
una clase. El cohete espacial es una clase, mientras que el cohete espacial llamado
Discovery es un objeto, un caso concreto de la clase cohete espacial.
Abstracción
Es importante darse cuenta de que el objetivo de la programación no es repro-
ducir todos los aspectos posibles del mundo real de un concepto dado. Por ejem-
plo. cuando se programa una clase Person. no se intenta modelar todo lo que se
conoce sobre una persona. En su lugar, se trabaja dentro del contexto de la aplica-
ción específica. Sólo se modelan los elementos que son necesarios para esa apli-
cación. Algunas características de una persona, como la nacionalidad, pueden
existir en el mundo real, pero se omiten si no son necesarias para la aplicación en
cuestión. Una persona en una aplicación bancaria estará interesada en aspectos
diferentes de los de. por ejemplo, una persona en un juego de acción. Este concep-
to recibe el nombre de abstracción y es una técnica necesaria para el manejo de
los conceptos infinitamente complejos del mundo real. Por tanto, cuando se haga
preguntas sobre objetos y clases, tenga siempre en cuenta que debe hacerse estas
preguntas en el contexto de una aplicación específica.
Cuando se asigna un tipo de datos a una variable del código, se puede usar un
tipo de datos primitivo o un tipo de datos abstracto. Los tipos de datos primitivos
son tipos que C# reconoce en cuanto se instala. Tipos como int, long, char y
string son tipos de datos primitivos de C#.
Los tipos de datos abstractos son tipos que C# no admite cuando se instalan
Antes de poder usar un tipo de datos abstracto hay que declararlo en el código
Los tipos de datos abstractos se definen en el código en lugar d e hacerlo en el
compilador de C#.
Por ejemplo, imagine la estructura (o clase) Person. Si escribe código C#
que usa una estructura (o clase) Person sin escribir código que le indique al
compilador de C# a qué se parece una estructura (o clase) Person y cómo se
comporta, se obtiene un error de compilación. Observe el siguiente código:
class MyClass
{
static voicl Main()
{
Person MyPerson;
Person.FirstName = "Malgoska";
}
}
Encapsulación
Mediante la encapsulación. los datos se ocultan, o se encapsulan, dentro de
una clase y la clase implementa un diseño que permite que otras partes del código
accedan a esos datos de forma eficiente. Imagine la encapsulación como un envol-
torio protector que rodea a los datos de las clases de C#.
Cómo ejemplo de encapsulación. observe la estructura Point con la que
trabajó en un capítulo anterior.
struct Point
{
public int X;
public int Y;
}
Los miembros de datos de esta estructura son públicos, lo que permite que
cualquier parte de código que acceda a la estructura acceda también a los miem-
bros de datos. Como cualquier parte de código puede acceder a los miembros de
datos, el código puede asignar a los valores de los miembros de datos cualquier
valor que pueda representarse en un valor int.
No obstante, puede surgir un problema al permitir que los clientes asignen los
valores de los miembros de datos directamente. Supongamos que se usa la estruc-
tura Point para representar una pantalla de ordenador con una resolución de
800 x 600. En ese caso, sólo tiene sentido permitir al código que asigne a X
valores entre 0 y 800 y a Y valores entre 0 y 600. No obstante, con el acceso
público a los miembros de datos, no hay nada que impida al código asignar a X el
valor 32.000 y a Y el valor 38.000. El compilador de C# lo permite porque esos
valores son posibles en un entero. El problema es que no tiene sentido permitir
valores tan elevados.
La encapsulación resuelve este problema. Básicamente, la solución está en
marcar los miembros de datos como priv ados, para que el código no pueda acce-
der a los miembros de datos directamente. A continuación puede escribir métodos
en una clase de punto como SetX() y SetY(). Los métodos SetX() y SetY()
pueden asignar los valores y también pueden contener código que genere un
error si se trata de llamar a S e t X () o a S e t Y () con parámetros con valores
demasiado grandes. La figura 8.2 muestra el posible aspecto de una clase P o i n t .
Herencia
Algunas clases, como la clase Point. se diseñan partiendo de cero. El esta-
do y los comportamientos de la clase se definen en ella misma. Sin embargo,
otras clases toman prestada su definición de otra clase. En lugar de escribir otra
clase de nuevo, se pueden tomar el estado y los comportamientos de otra clase y
usarlos como punto de partida para la nueva clase A la acción de definir una
clase usando otra clase como punto de partida se le llama herencia.
Herencia simple
Supongamos que estamos escribiendo código usando la clase Point y nos
damos cuenta de que necesitamos trabajar con puntos tridimensionales en el mis-
mo código. La clase Point que ya hemos definido modela un punto en dos
dimensiones y no podemos usarlo para definir un punto tridimensional. Decidi-
mos que hace falta escribir un punto tridimensional llamado Point 3D. Se puede
diseñar la clase de una de estas dos formas:
• Se puede escribir la clase Point3D partiendo de cero, definiendo miem-
bros de datos llamados X, Y y Z y escribiendo métodos que lean y escriban
los miembros de datos.
• Se puede derivar de la clase Point. que ya implementa los miembros X e
Y. Heredar de la clase Point proporciona todo lo necesario para trabajar
con los miembros X e Y. por lo que todo lo que hay que hacer en la clase
Point3D es implementar el miembro Z. La figura 8.3 muestra el aspecto
que podría tener la clase Point3D en UML.
NOTA: La notación UML que indicó que ios miembros tienen visibilidad
protegida es el símbolo de libra delante de los miembros de datos. Visibi-
lidad protegida significa que la visibilidad es pública para las clases deri-
vadas y privadas para todas las demás clases.
La figura 8.3 refleja la herencia simple. La herencia simple permite que una
clase se derive de una sola clase base. Este tipo herencia también es conocido
como derivar una clase de otra. Parte del estado y del comportamiento de la
clase Point3D se derivan de la clase Point.
En este caso, la clase usada como punto de partida recibe el nombre de clase
base y la nueva clase es la clase derivada. En la figura 8.3 la clase base es
Point y la clase derivada es Point3D.
Figura 8.3. La clase Pomt3D hereda los métodos y las variables de la clase Point.
Derivar una clase de otra automáticamente permite que los miembros de datos
públicos (y protegidos) y a los métodos públicos (y protegidos) de la clase base
estén disponibles para la clase derivada. Como los métodos G e t X () . G e t Y () .
S e t X () y S e t Y () están marcados como públicos en la clase base, están
automáticamente disponibles para las clases derivadas. Esto significa que la elase
P o i n t 3 D dispone de los métodos públicos G e t X ( ) . G e t Y ( ) . S e t X ( ) y
S e t Y () va que se derivaron de la clase base P o i n t . Una sección de codigo
puede crear un objeto de tipo P o i n t 3 D y llamar a los métodos G e t X (). G e t Y () .
S e t X ( ) y S e t Y ( ) . aunque los métodos no esten implementados explícitamente
en esa clase. Se derivaron de la clase base P o i n t y pueden ser usados en la clase
derivada P o i n t 3 D .
Herencia múltiple
Algunos lenguajes orientados a objetos también permiten la herencias múlti-
ple. lo que permite que una clase se derive de más de una clase base. C# solo
permite herencias simples.
Esta restricción se debe a que el CLR de NET no admite clases con varias
clases base, principalmente porque otros lenguajes NET. como Visual Basic, no
admiten por sí mismos herencias múltiples. Los lenguajes que admiten herencia
múltiple, como C++. también han ev idenciado la dificultad de usar correctamente
la herencia múltiple.
NOTA: Si quiere usar la herencia simple (por ejemplo para que una
clase RadioReloj herede las clases Despertador y Radio) se pue-
de obtener un comportamiento muy parecido al deseado mediante la con-
tención. La contención incrusta una variable miembro de la clase de la que
se quíere derivar. En este caso, esta técnica solicita añadir una variable
Despertador y una variable Radio para la clase RadioReloj y
delega la funcionalidad en la variable miembro adecuada. Esta técnica tam-
bien se puede utilizar en herencias simples, pero es el único medio que hay
para simular herencias múltiples en .NET (a menos que Microsoft nos sor-
prenda añadiendo herencias múltiples en una versión posterior).
Polimorfismo
La herencia permite que una clase derivada redefina el comportamiento espe-
cificado para una clase base. Supongamos que creamos una clase base Animal.
Hacemos esta clase base abstracta porque queremos codificar animales genéricos
siempre que sea posible, pero también crear animales específicos, como un gato y
un perro. El siguiente fragmento de código muestra cómo declarar la clase con su
método abstracto.
Abstract class Animal
{
public abstract void MakeNoise();
}
Ahora se pueden derivar animales especificos. como Cat y Dog, a partir de la
clase base abstracta Animal:
class Cat : Animal
{
public override void MakeNoise()
{
Console.WriteLine("Meow!");
}
}
class Dog : Animal
{
public override void MakeNoise()
{
Console.WriteLine("Woof!");
}
}
Observe que cada clase tiene su propia implementación del método
MakeNoise(). Ahora la situación es la indicada para el polimorfismo. Como
se aprecia en la figura 8.4. tenemos una clase base con un método que está anula-
do en dos (o más) clases derivadas. El polimorfismo es la capacidad que tiene un
lenguaje orientado a objetos de llamar correctamente al método anulado en fun -
ción de qué clase lo esté llamando. Esto suele producirse cuando se almacena una
colección de objetos derivados.
El siguiente fragmento de código crea una colección de animales, que recibe el
apropiado nombre de zoo. A continuación añade un perro y dos gatos a este zoo.
ArrayList Zoo;
Zoo = new ArrayList(3);
C a t Sasha, K o s h k a ;
S a s h a = new C a t ();
K o s h k a = new C a t ();
Do g Milou;
Milou = new Dog();
Figura 8.4. Este ejemplo de herencia muestra una clase base y dos clases derivadas.
Woof!
Meow!
Meow!
¿Que está pasando? Como C# permite el polimorfismo, en el tiempo de ejecu-
ción el programa es lo suficientemente inteligente como para llamar a la versión
perro de MakeNoise cuando se obtiene un perro del zoo y la versión gato de
MakeNoise cuando se obtiene un gato del zoo.
El listado 8.1 muestra el código C# completo que explica el polimorfismo.
using System;
using System.Collections;
namespace PolyMorphism
{
abstract class Animal
{
public abstract void MakeNoise();
}
class PolyMorphism
{
static int Main(string[ ] args)
{
ArrayList Zoo;
Zoo = new ArrayList(3);
Dog Milou;
Milou = new Dog();
Zoo.Add(Milou);
Zoo.Add(Koshka);
Zoo.Add(Sasha);
foreach (Animal a in Zoo)
{
a.MakeNoise();
}
// espera a que el usuario acepte los resultados
Console.WriteLine ("Hit Enter to termínate ...");
Console.Read();
return 0;
}
}
}
Resumen
C# es un lenguaje orientado a objetos y los conceptos que se usan en los
lenguajes orientados a objetos también se aplican a C# .
Los tipos de dalos abstractos son tipos de datos que se definen en el propio
código. Los tipos de datos abstractos no forman parte de C#. a diferencia de los
tipos de datos primitivos. Se pueden usar tipos de datos abstractos en el código de
la misma forma que los tipos de datos primitivos, pero sólo después de haberlos
definido en el código.
La encapsulacion es el acto de diseñar clases que proporcionen un conjunto
completo de funcionalidad a los datos de las clases sin exponer directamente esos
datos. Si se quiere escribir codigo que evite que otras partes del código proporcio -
nen valores no válidos a la clase, la encapsulación permite "ocultar" los miembros
de datos al hacerlos privados, mientras crea métodos que acceden a ellos v esta -
blecen los valores de los miembros de datos como públicos. Otros fragmentos de
codigo pueden llamar a los métodos, que pueden comprobar los v alores y emitir
un error si los valores no son apropiados.
La herencia permite definir una clase por medio de otra. Las clases de rivadas
heredan de la clase base. Las clases derivadas automáticamente heredan el estado
y los comportamientos de la clase base. Se puede usar la herencia para añadir
nuevas funciones a clases ya existentes sin necesidad de rescribir desde cero una
clase completamente nueva. Algunos lenguajes orientados a objetos permiten tan -
to herencias simples como herencias múltiples, aunque C# solo permite la heren -
cia simple.
El polimorfismo permite tratar de forma uniforme a una colección de clases
derivadas de una sola clase base. Las clases derivadas se recuperan como una
clase base y C# automáticamente llama al método correcto en la clase derivada.
Este capítulo trata los conceptos orientados a objetos en general, sin estudiar
como se usan estos conceptos en el código C#. El resto de los capítulos de esta
parte del libro explican como se implementan estos conceptos en C#.
9 Clases
de C#
El diseño de C# está muy influido por los conceptos del desarrollo de software
orientado a objetos. Como la clase es la parte fundamental del software orientado
a objetos, no es de sorprender que las clases sean el concepto más importante de
C#. Las clases en el mundo del desarrollo de software orientado a objetos contie -
nen codigo y datos. En C#. los datos se implementan usando miembros de datos y
el código se implementa usando métodos. Los miembros de datos son cualquier
elemento al que se pueda pasar datos dentro y fuera de la clase. Los dos princi -
pales tip os de miembros de datos son los campos y las propiedades. En el códi go
C# se pueden escribir tantos miembros de datos y métodos como se desee, pero
todos deben incluirse en una o varias clases. El compilador de C# emite un error
si se intenta definir alguna variable o se implementa algún método fuera de una
definición de clase. Este capítulo trata de los fundamentos de la construcción de
clases. Explica cómo crear constructores y destructores, añadir métodos y miem -
bros. además de a usar clases después de crearlos.
El método Main
Cada aplicación de C# debe contener un método llamado Main. Esto es el
punto de entrada para que la aplicación se ejecute. Se puede colocar este método
dentro de cualquier clase del proyecto porque el compilador es lo bastante inteli-
gente como para buscarlo cuando lo necesite.
El método Main debe cumplir dos requisitos especiales para que la aplicación
funcione correctamente. En primer lugar, el método debe declararse como public.
Esto asegura que se pueda acceder al método. En segundo lugar, el método debe
declararse como static. La palabra clave static asegura que sólo se puede
abrir una copia del método a la vez.
Teniendo en cuenta estas reglas, observemos este código:
class Class1
{
public static void Main()
{
}
}
Como puede apreciarse, este ejemplo contiene una clase llamada Class1.
Esta clase contiene el método Main de la aplicación. Es en este método Main
donde se coloca todo el código necesario para ejecutar su aplicación. Aunque es
correcto colocar este método en la misma clase y el mismo archivo que el resto del
código de la aplicación, conviene crear una clase y un archivo separados para el
método M a i n . Esta operación sirve de ayuda a los demás programadores que
quieran trabajar con este código.
NOTA: Si se está usando Visual Studio .NET para programar C#, la ma-
triz de cadenas se añade automáticamente cuando se crea una aplicación de
consola.
retval.exe success
:answer0
echo Program had return code 0 (Success)
goto end
:answer1
echo Program had return code 1 (Failure)
goto end
:end
echo Done!
Si se edita este programa por lotes para que pase la palabra f a i l como
parámetro, entonces aparecerá un mensaje que confirma que el programa terminó
con un código de salida 1.
El cuerpo de la clase
El cuerpo de la clase puede incluir instrucciones cuya función se incluya en
una de estas categorías.
• Constantes
• Campos
• Métodos
• Propiedades
• Eventos
• Indizadores
• Operadores
• Constructores
• Destructores
• Tipos
if (Ratio == 3.14159)
System.Console.WriteLine("Shape ís a circle");
i f (Ratio == Pi)
System.Console.WriteLine("Shape ís a circle");
NOTA: Este capítulo usa la palabra clave public para indicar que los
elementos del cuerpo de clase están disponibles para el resto del código de
la aplicación. Posteriormente se examinan algunas alternativas a la palabra
clave public que pueden usarse cuando se hereda una clase de otra.
Al usar una estructura como ésta, los clientes pueden usar la sintaxis
structurename.field para trabajar con uno de los campos de la estructura:
Point MyPoint = new Point();
MyPoint.X = 1 0 0 ;
Los clientes pueden escribir fácilmente este código. Los clientes pueden acce -
der al campo directamente y usar su valor en una expresión.
El problema de este enfoque es que los clientes pueden establecer un campo
que C# permite, pero que no es logico para el código. Por ejemplo, si esta gestio -
nando una estructura Point que describe un punto en una pan talla de 640 \ 480
pixeles. el valor lógico más alto para X debería ser 630 (suponiendo que los
valores legales de X están entre 0 y 639). Sin embargo, como se especifica que la
coordenada X es un tipo int. los clientes pueden establecer como valor cualqui er
valor autorizado dentro de los límites de los tipos enteros:
MyPoint.X = -5OO;
MyPoint.X = 9000;
La ventaja de este enfoque es que obliga a los clientes que quieran asignar un
valor a X a llamar al método para realizar la tarea:
Point MyPoint = new Point();
MyPoint.SetX(100);
La ventaja del método es que se puede escribir código que valide el nuevo
valor antes de que se almacene realmente en el campo y el código del método
puede rechazar el nuevo valor si. por lógica, no es adecuado. Así pues, los clien-
tes llaman al método para establecer un nuevo valor.
Aunque este enfoque funciona, llamar a un método para asignar un valor re -
quiere un poco mas de código que asignar un valor directamente. Para el código
es mas natural asignar un valor a un campo que llamar a un método para que lo
asigne.
En el mejor de los casos, querremos lo mejor de los dos elementos, que los
clientes puedan leer y escribir directamente los valores de los campos usando
instrucciones de asignación simples, pero también querremos que el código inter-
venga por anticipado y haga todo lo necesario para obtener el valor de un campo
o validar el nuevo valor antes de que se asigne. Afortunadamente, C# ofrece esta
posibilidad con un concepto de clase llamado propiedad.
Las propiedades son miembros identificados que proporcionan acceso al esta-
do de un objeto. Las propiedades tienen un tipo y un identificador y tienen uno o
dos fragmentos de código asociados a ellos: una base de código g e t y una base
de código s e t . Estas bases de código reciben el nombre de descriptores de acce-
so. Cuando un cliente accede a una propiedad se ejecuta el descriptor de acceso
g e t de la propiedad. Cuando el cliente establece un nuevo valor para la propie-
dad se ejecuta el descriptor de acceso s e t de la propiedad.
Para mostrar cómo funcionan las propiedades, el listado 9.1 usa una clase
P o i n t que expone los valores de X e Y como propiedades.
class Point
{
prívate int XCoordinate;
prívate int YCoordinate;
public int X
{
get
(
return XCoordinate;
}
set
{
íf((value >= 0) && (value < 640))
XCoordinate = value;
}
}
public int Y
{
get
{
return YCoordínate;
}
set
{
íf ( (value >= 0) && (value < 480))
YCoordinate = value;
}
}
Figura 9.1. Las propiedades de clase ayudan a almacenar los valores del punto
class Rainbow
{
public int GetNumberOfColors()
{
return 7;
}
public bool GetColor (int Colorlndex, out string ColorName)
{
bool ReturnValue;
ReturnValue = true;
switch(Colorlndex)
{
case 0 :
ColorName = "Red";
break;
case 1 :
ColorName = "Orange";
break;
case 2:
ColorName = "Yellow";
break;
case 3:
ColorName = "Creen";
break;
case 4 :
ColorName = "Blue";
break;
case 5 ;
ColorName = "Indigo";
break;
case 6:
ColorName = "Violet";
break;
default:
ColorName = "";
ReturnValue = false;
break;
}
return ReturnValue;
}
public static void Main()
{
int ColorCount;
int Colorlndex;
Rainbow MyRambow = new Rainbow() ;
ColorCount = MyRaínbow.GetNumberOfColors();
string ColorName;
bool Success;
La clase mantiene una colección de valores del mismo tipo de valor, lo que es
muy parecido a una matriz. De hecho, esta clase Raínbow también puede
implementarse como una matriz y el elemento que realiza la llamada puede usar
los corchetes de la sintaxis del descriptor de acceso a la matriz de elementos para
recuperar un nombre de color en particula r:
ColorName = Raínbow[ColorIndex];
Los indizadores permiten que se acceda a las clases como si fuera una matriz.
Para especificar qué se debe devolver cuando el elemento que hace la llamada usa
corchetes para acceder al elemento de la clase, se usa un fragmento de código
llamado descriptor de acceso de indizador.
Teniendo esto en cuenta, se puede reescribir la clase R a i n b o w para que
permita a los elementos que la llaman acceder a los nombres de los colores usan -
do corchetes en su sintaxis. Se elimina el método G e t C o l o r () y se sustituye
por un indizador. y se sustituye el método G e t C o l o r C o u n t () por una propie-
dad de sólo lectura llamada C o u n t . como se muestra en el listado 9.3.
c]ass Rainbow
{
public int Count
{
get
{
return 7;
}
}
public string this [int Colorlndex]
{
get
{
switch(Colorlndex)
{
case 0:
return "Red";
case 1:
return "Orange";
case 2:
return "Yellow" ;
case 3:
return "Creen";
case 4 :
return "Blue";
case 5:
return "Indigo";
case 6:
return "Vi olet ";
default :
return "";
}
}
}
public static void Main()
{
int Colorlndex;
Rambow MyRambow = new Rainbow();
string ColorName;
string ColorName;
Class Point
(
public int X;
public int Y;
public P o i n t ( )
{
X = 0;
Y = 0;
}
publie Point()
{
X = 0;
Y = 0;
}
public Point(int InitialX, int InitialY)
{
X = InitialX;
Y = InitialY;
}
"Point()
{
X = 0 ;
Y = 0 ;
}
public static void Main()
{
Point MyFirstPoint = new Point(100, 200);
Point MySecondPoint = new Point ();
}
}
El método Main() crea dos objetos locales Point. Cu ando el método Main()
termina su ejecución, los objetos locales Point y a no pueden volver a ser usados
y el CLR los registra como objetos que pueden ser destruidos cuando se ejecute el
recolector de objetos no utilizados. Sin embargo, el recolector de obj etos no uti-
lizados no siempre se ejecuta inmediatamente, lo que significa que no siempre se
llama al destructor del objeto inmediatamente.
Por ejemplo, suponga que quiere escribir una clase de C# que gestione un
archivo en un disco. Escribiremos una clase llamada File con un constructor
que abra el archivo y un destructor que cierre el archivo:
class File
{
File (string Name)
{
// abre el archivo
}
~F i l e ( )
{
// cierra el archivo
}
)
Si queremos que esta clase trabaje en un método:
public statíc void Main()
{
File MyFile = new File("myfile.txt");
}
class Point
{
public int X;
public int Y;
class Point
{
public int X;
public int Y;
Point(int X, int Y)
{
this.X = X;
this.Y = Y;
}
public static void Main()
{
}
}
El modificador static
Cuando se define un campo o un método en una clase, cada objeto de esa clase
creado por el código tiene su propia copia de valores de campo y métodos. Me -
diante la palabra clave static, se puede invalidar este comportamiento, lo que
permite que varios objetos de la misma clase compartan valores de campo y
métodos.
class Point
{
public static int XCoordinate;
public static int YCoordinate;
public int X
{
get
{
return XCoordinate;
}
}
public int Y
{
get
{
return YCoordinate;
}
}
public static void Main()
{
Point MyPoint = new Point();
System.Console.WriteLine("Before");
System.Console.WriteLine;
System.Console.WnteLme(MyPoint.X);
System.Console.WriteLine(MyPoint.Y);
System.Console.WriteLine("After");
System.Console.WriteLine("=====");
System.Console.WriteLine(MyPoint.X);
System.Console.WriteLine(MyPoint.Y);
}
}
La clase Point del listado 9.10 tiene dos campos estáticos enteros llamados
XCoordinato e YCoordinate. También tiene dos propiedades de solo lectu -
ra. llamadas X e Y. que devuelven los valores de las variables estáticas. El método
Main() crea un nuevo objeto Point y escribe sus coordenadas en la consola . A
continuación cambia los valores de los campos estáticos y vuelve a escribir las
coordenadas del objeto Point. El resultado puede verse en la figura 9.3.
Hay que tener en cuenta que los valores de las coordenadas del objeto P o i n t
han cambiado, aunque los valores del propio objeto no hayan cambiado. Esto es
debido a que el objeto P o i n t comparte campos estáticos con todos los demás
objetos P o i n t y cuando los campos estáticos de la clase P o i n t cambian, todos
los objetos de esa clase se ven afectados.
Los campos estáticos que se usan en expresiones no están prefijados con un
identificador de objeto sino con el nombre de la clase que contiene los campos
estáticos. La siguiente instrucción es un error porque M y P o i n t hace referencia a
un objeto v X C o o r d i n a t e hace referencia a un campo estático:
MyPoint.XCoordinate = 100;
Este codigo hace que se produzca el siguiente error del compilador de C#:
error CS0176: No se puede obtener acceso al miembro estático
'Point.XCoordinate' con una referencia de instancia; utilice un
nombre de tipo en su lugar
El campo estático debe estar prefijado con el nombre de la clase:
Point. .XCoordinate = 10 0;
En general hay que intentar que todas las constantes sean estáticas d e modo
que sólo haya una copia del valor de la constante en memoria a la vez.
class Listing9_9
{
public static void Main()
{
CallMethod();
}
void CallMethod()
{
System.Console.WriteLine("Hello from CallMethod()");
}
}
El error del código del listado 9 . 1 1 es que hay un método estático, Main().
que intenta llamar a un método de instancia. CallMethod(). Esto no está
permitido porque los métodos de instancia son parte de una instancia de objeto y
los métodos estáticos no.
Para corregir el código, el método estático Main() debe crear otro objeto de
la clase y llamar al método de instancia desde el nuevo objeto, como muestra el
listado 9 . 1 2 .
Listado 9.12. Métodos estáticos que llaman a un método de instancia de clase
class Listing9_10
{
public static void Main()
{
Listing9_10 MyObject = new Listing9_10();
MyObject.CallMethod();
}
void CallMethod()
{
System.Console.WriteLine("Hel1o from CallMethod()");
}
}
Como todos los elementos de las clases estáticas, los métodos estáticos apare -
cen sólo una vez en memoria, por lo que se debe marcar el método Main() como
static. Cuando el código NET se carga en memoria, el CLR empieza a ejecu -
tar el método Main () Recuerde que sólo puede haber un método Main () en
memoria a la vez Si una clase tiene varios métodos Main (). el CLR no sabría
qué método Main () ejecutar cuando el código lo necesite. Usar la palabra clave
static en el método Main () hace que sólo haya disponible en memoria una
copia del método Main().
Resumen
C# es un lenguaje orientado a objetos y los conceptos que se emplean en los
lenguajes orientados a objetos se pueden aplicar a C#. Las clases de C# pueden
usar varios tipos de miembros de clase:
• Las constantes dan nombre a un valor que no cambia en todo el código. El
uso de constantes hace que el código sea más legible porque se pueden usar
los nombres de las constantes en lugar de los valores literales.
• Los campos contienen el estado de las clases. Son variables que están
asociadas a un objeto.
• Los métodos contienen el comportamiento de la clase. Son fragmentos de
codigo con nombre que realizan una acción determinada para la clase.
• Las propiedades permiten que los fragmentos que hacen llamadas tengan
acceso al estado de la clase. Los fragmentos que hacen la llamada acceden
a las propiedades con la misma sintaxis o b j e c t . i d e n t i f i e r que se
emplea para acceder a los campos. La ventaja de las propiedades sobre los
campos es que se puede escribir código que se ejecuta cuando se consultan
o se asignan los valores de la propiedad. Esto permite escribir códigos de
validación para evitar que se asignen nuevos valores a las propiedades o
para calcular dinámicamente el valor de una propiedad que está siendo
consultada. Se pueden implementar propiedades de lectura y escritura,
sólo de lectura o sólo de escritura.
• Los eventos permiten que la clase informe a las aplicaciones que hacen la
llamada cuando se produzcan determinadas acciones en su interior. Las
aplicaciones que hacen la llamada pueden suscribirse a los eventos de clase
y recibir avisos cuando se produzcan dichos eventos.
• Los indizadores permiten que se acceda a la clase como si fuera una ma-
triz. Las aplicaciones que hacen la llamada pueden usar la sintaxis de
elemento de matriz de corchetes para ejecutar el código descriptor de acce-
so del indizador de la clase. Los indizadores se usan cuando una clase
contiene una colección de valores y es más lógico considerarla una matriz
de elementos.
• Las clases pueden redefinir los operadores, como se vio en un capítulo
anterior. Los operadores pueden ayudar a determinar cómo se comporta
una clase cuando se usa en una expresión con un operador.
• Los constructores son métodos especiales que son ejecutados cuando se
crean objetos en la clase. Se puede definir más de un constructor, cada uno
con una lista de parámetros diferente. También se puede definir una clase
sin constructores. En ese caso, el compilador de C# genera un constructor
por defecto que inicializa todos los campos del objeto con el valor cero.
• Los destructores son métodos especiales que son ejecutados cuando se
destruyen objetos en la clase. Una clase sólo puede tener un destructor.
Debido a la interacción con el código NET y el CLR. los destructores se
ejecutan cuando el recolector de objetos no utilizados recoge un objeto, no
cuando el código ya no puede acceder al identificador del objeto.
• Las clases pueden definir tipos propios y estos tipos pueden contener de -
finiciones de estructura e incluso definiciones de otras cl ases. Una vez
definidos estos tipos, la clase puede usarlos de la misma forma que los
tipos enteros en C#.
La palabra clave this hace referencia a la instancia actual de un objeto. Se
usa como prefijo para diferenciar a un identificador de campo de un iden tificador
de parámetro con el mismo nombre.
La palabra clave static advierte al compilador de C# de que todos los
objetos de la clase comparten una sola copia de un campo o de un objeto Por
defecto, cada campo y cada método de una clase de C# mantiene su p ropia copia
de los valores del campo en memoria. Los elementos de la clase que no usan la
palabra clave static reciben el nombre de métodos de instancia. Los elemen-
tos de clase que usan la palabra clave static reciben el nombre de métodos
estáticos.
10 Como
Sobrecargar
operadores.
class Point
{
public int X;
public int Y;
if(RValue.X < 0)
NewPoint.X = -(RValue.X);
else
NewPoint.X = RValue.X;
if (RValue.Y < 0)
NewPoint.Y = -(RValue.Y);
else
NewPoint.Y = RValue.Y;
return NewPoint;
}
MyPoint.X = -100;
MyPoint.Y = 200 ;
System.Console.WriteLine(MyPoint.X);
System.Console.WriteLine(MyPoint.Y);
MyPoint = +MyPoint ;
System.Console.WriteLine(MyPoint.X);
System.Console.WriteLine(MyPoint.Y);
}
}
El método Main() crea un objeto de tipo Point y asigna a sus coorde nadas
iniciales los valores (100. 200). A continuación aplica el operador unario más al
objeto y vuelve a asignar el resultado al mismo punto. Por último, escribe las
coordenadas x e y en la consola. En la figura 10.1 se puede ver el resultado del
listado 10.1. Las coordenadas del punto han cambiado de ( -100. 200) a (100.
200). El código con el operador sobrecargado se ejecuta cuando se llega a la
siguiente instrucción:
MyPoint = +MyPoint;
Figura 10.1. Sobrecarga del operador unario
Este método crea un nuevo objeto Point y a continuación examina las coor-
denadas del rvalue proporcionado. Si alguno de los parámetros es negativo,
sus valores se cambian de signo, volviéndose por tanto valores positivos, y estos
nuevos valores positivos se asignan al nuevo punto. Los valores que no son nega-
tivos se asignan al nuevo punto sin ninguna conversión. A continuación el método
devuelve el nuevo punto. El valor devuelto por el operador se usa como lvalue
para la declaración original. El tipo de retorno de las sobrecargas del operador
para el unario más. el unario menos, la negación o los operadores de comple-
mento bit a bit no tienen el mismo tipo que el rvalue. Puede ser cualquier tipo
de C# que sea adecuado para el operador.
class Point
{
public int X;
public int Y;
if (RValue.X > 0)
NewPoint.X = - (RValue.X);
else
NewPoint.X = RValue.X;
if (RValue.Y > 0)
NewPoint.Y = -(RValue.Y);
else
NewPoint.Y = RValue.Y;
return NewPoint;
}
public static void Main()
{
Point MyPoint = new Point();
MyPomt.X = -100;
MyPoint.Y = 200;
System.Console.WriteLine(MyPoint.X);
System.Console.WriteLine(MyPoint.Y);
MyPoint = -MyPoint;
System.Console.WriteLine(MyPoint.X);
System.Console.WriteLine(MyPomt.Y);
}
}
class Point
{
public int X;
public int Y;
return NewPoint;
}
Input Output
0x0000000000000005 OxfffffffffffffffA
0x0000000000000006 0xfffffffffffffff9
NewPoint.X = RValue.X + 1;
NewPoint.Y = RValue.Y + 1;
return NewPoint;
}
MyPoint.X = 100;
MyPoint.Y = 200;
System.Console.WriteLine(MyPoint.X);
System.Console.WriteLine(MyPoint.Y);
MyPoint = ++MyPoint;
System.Console.WriteLine(MyPoint.X);
System.Console.WriteLine(MyPoint.Y);
}
}
Si se compila y ejecuta el código del listado 10.4 se escribe lo siguiente en la
consola:
1 00
2 00
101
201
Figura 10.4. Resultado de la ejecución del código compilado del listado 10.4
class Point
i
public int X;
public int Y;
NewPoint.X = RValue.X - 1;
NewPoint.Y = RValue.Y - 1;
return NewPoint;
}
MyPoint = —MyPoint;
System.Console.WriteLine(MyPoint.X);
System.Console.WriteLine(MyPoint.Y);
}
}
El listado 10.6 es un buen ejemplo. Modifica la clase point para que devuel-
va true si el punto está en el origen o false en caso contrario.
class Point
{
public int X;
public int Y;
MyPoint.X = 100;
MyPoint.Y = 200;
if (MyPoint)
System. Console.WriteLine ("The point is at the origin.");
else
System.Console.WriteLine( "The point is not at the ongin.");
}
}
Se pueden definir varias sobrecargas para el mismo operador, pero solo si las
listas de parámetros usan tipos diferentes:
static public bool operator + (Point MyPoint, float FloatValue)
static public bool operator + (Point MyPoint, int IntValue)
static public bool operator + (Point MyPoint, uint UlntValue)
class Point
(
public int X;
public int Y;
MyFirstPoint.X = 100;
MyFirstPoint.Y = 200;
MySecondPoint.X = 500;
MySecondPoint.Y = 750 ;
MyThirdPoint.X = 100;
MyThirdPoint.Y = 200;
if(MyFirstPoint == MySecondPoint)
System.Console.WriteLine("MyFirstPoint and
MySecondPoint are at the same coordinates.");
else
System.Console.WriteLine("MyFirstPoint and
MySecondPoint are not at the same coordinates.");
if(MyFirstPoint == MyThirdPoint)
System.Console.WriteLine("MyFirstPoint and MyThirdPoint
are at the same coordinates.");
else
System.Console.WriteLine("MyFirstPoint and MyThirdPoint
are not at the same coordinates.");
}
}
El método Main ( ) define tres puntos:
• MyFirstPoint. con coordenadas (100. 200)
• MySecondPoint. con coordenadas (500. 750)
• MyThirdPoint. con coordenadas (100. 200)
A continuación el método usa el operador de igualdad para determinar si los
puntos MyFirstPoint y MySecondPoint hacen referencia a la mismas
coordenadas. Entonces usa el operador de igualdad para determinar si los puntos
MyFirstPoint y MySecondPoint hacen referencia a las mismas coordena-
das. En la figura 10.7 se muestra el resultado que se obtiene si se compila y
ejecuta el código del listado 10.7.
double Distance;
MyPoint.X = 100;
MyPoint.Y = 200;
Distanee = MyPoint;
System.Console.WriteLine(Distance);
}
}
Resumen
C # permite personalizar el comportamiento de varios de los operadores inte -
grados. Las clases y las estructuras pueden incluir métodos llamados métodos de
sobrecarga de operador que definen el comportamiento de un operador cuan -
do aparece en una expresión con la clase o estructura.
Para sobrecargar los operadores unario más, unario menos, de negación o de
complemento bit a bit en una clase o estructura, hay que definir un método con un
tipo devuelto deseado, el operador que se está sobrecargando y un solo parámetro
del tipo de la clase o estructura que contiene el método de operador sobrecargado.
Para sobrecargar los operadores de incremento o decrcmento prefijo en una
clase o estructura, hay que definir un método con un tipo de devolución que
especifica el tipo de clase o estructura que contiene el método del operador sobre -
cargado. También es necesario definir el operad or que se está sobrecargando y un
solo parámetro del tipo de clase o estructura que contiene el método del operador
sobrecargado.
Para sobrecargar los operadores true o false en una clase o estructura,
hay que definir un método con un tipo de devolución bo oleano y un solo parametro
del tipo de la clase o estructura que contiene el método de operador sobrecargado.
Para sobrecargar cualquiera de los operadores binarios en una clase o estruc -
tura. hay que definir un método con un tipo de devolución, el operad or que se esta
sobrecargando y dos parámetros. Al menos uno de los dos parámetros debe ser del
tipo de clase o estructura que contiene el método del operador sobrecargado.
También se pueden definir nuevas conversiones para las clases o estructuras.
Se especifica si la conversión se considerara un operador implícito o explícito. El
método del operador de conversión especifica el tipo de la variable que se convier -
te y el tipo al que debe ser convertida.
11 Herencia
de clase
Los programas mas simples de C# pueden usar una o dos clases. Sin embargo,
probablemente se necesiten varias clases en los programas más grandes. Muchas
de estas clases pueden tener campos o métodos similares y seria logico compartir
el codigo común entre el conjunto de clases.
C# incluye el concepto orientado a objetos de herencia, que permite que una
clase adopte código de otras clases. Las clases de C# pueden derivarse de las
clases primarias y las instrucciones heredadas pueden ser usadas en otras clases.
La herencia se usa en el desarrollo de software orientado a objetos para
reutilizar los códigos mas comunes. Observ e, por ejemplo, los cuadros de lista de
selección múltiple y de selección simple de Windows. Estos cuadros de lista tie -
nen diferentes funcionalidades: uno permite que se seleccionen varios elementos
y el otro lo impide, pero también tienen muchas similitudes. Tienen el mismo
aspecto, se comportan de la misma forma cuando el usuario se desplaza por la
lista y usan el mismo color para marcar un elemento seleccionado. Si hubiera que
escribir estos dos cuadros de lista como clases de C'#. se podrian escribir por
separado, sin que uno conozca la existencia del otro. Sin embargo, eso sería un
desperdicio. La mayor parte del código seguramente sea idéntico. Sería más lo -
gico escribir una clase que contuviera el código común y disponer de clases deri -
vadas de la clase de código común y que implementasen las diferentes
funcionalidades. Se puede escribir una clase llamada List Box para que. por
ejemplo, contenga el código común y. a continuación, escribir una clase de C#
llamada S i n g l e S e l e c t i o n L i s t B o x que herede de L i s t B o x y proporcio-
ne el código único al cuadro de lista de selección simple. También se puede
escribir una clase de C# llamada M u l t i p l e S e l e c t i o n L i s t B o x que tam-
bién herede de L i s t B o x pero que proporcione el código único al cuadro de lista
de selección simple. Otra de las ventajas consiste en que. si encuentra un error en
el cuadro de lista, se le puede seguir fácilmente la pista hasta el error en el código
común. Si se puede reparar el error en el código común, al volver a compilar el
programa se repararán esos errores en las clases de cuadros de lista de selección
múltiple y selección simple. Basta reparar un error para solucionar el problema en
las dos clases. En terminología orientada a objetos, se habla de herencia en térmi-
nos de clase base y clase derivada. La clase de la que se hereda recibe el nombre
de clase base y la clase que hereda de la clase base recibe el nombre de clase
derivada. En el ejemplo de los cuadros de lista, la clase L i s t B o x es la clase base
y las clases S i n g l e S e l e c t i o n L i s t B o x y MultipleSelectionListBox
son las clases derivadas.
NOTA: Una, y sólo una, de sus clases debe especificar un método estáti-
co M a i n ( ) .
Cómo especificar una clase base en C#
Volvamos al ejemplo de la clase Point para ver cómo funciona la herencia
en C#. Supongamos que hemos designado una clase llamada Point2D. que
describe un punto en un espacio bidimensional con las coordenadas X e Y:
class Point2D
{
public int X;
public int Y;
// mas codigo
}
class Point2D
{
public int X;
public int Y;
}
public int Z;
}
class MyMainClass
{
public static void Main()
{
Point2D My2DPoint = new Point2D();
Point3D My3DPoint = new Point3D();
My2DPoint.X = 100;
My2DPoint.Y = 200 ;
My3DPoint.X = 150;
My3DPoint.Y = 250;
My3DPoint.Z = 350;
}
}
Ámbito
Al d i se ñar l a e st r uct ur a d e la her e nci a d e c la se, p ued e d e cid ir q ué mi e mb r o s d e
la c la se b a se no d eb e n ser v i sib le s p ar a l a s c la se s d e ri vad as o p ar a lo s d e ma s
p r o gr a mad o re s. P o r ej e mp lo , se p ued e e scr ib ir u n méto d o e n u n a c la se b ase q ue
a yud e a ca lc u lar u n v alo r . Si e se cál c ulo no e s d e u ti lid ad e n u n a cl as e d eri vad a,
se p u ed e e v it ar q ue la cl ase d er i v ad a ll a me al m éto d o .
En t er mi n o lo g ía d e la p r o gr a mac ió n, la v is ib i lid ad d e u na va riab le o mé t o d o se
co no ce co mo s u ámbito. Al g u n as var iab le s o mé to d o s p ued e n ser d e clar ad a s
co mo d e á mb i to p ub l ico , o tr a s p u ed e n ser d ec la rad a s co mo d e á mb ito p ri vad o y
o tr a s p ued e n e st ar e n tr e es to s d o s ca so s.
C# d e fi ne c i nco p alab r a s c la v e q ue p er mi te n d e fi nir el á mb ito d e c u alq ui er
mi e mb ro ( v ari ab le o mé to d o ) d e u na cl as e. E l á mb ito d e u n mi e mb ro a fecta a s u
vi s ib i lid ad p ar a l a s c la se s d e r i vad a s y e l có d i go q u e cr ea la s i n sta n c ia s d e la
cla se. E st as p a lab ra s c l av e, r es al tad a s e n la si g ui e nt e li st a, se co lo ca n an te s d e
cu alq u ier o tra p alab r a c l av e e n u n a d ec lar ac ió n d e mie mb ro .
• Lo s mi e mb ro s mar ca d o s co mo p ub li c so n v is ib l es p ara la s c la se s d eri -
vad as y p ara e l có d i go q ue cr ea lo s o b j e to s d e l a cl as e. Ha s ta a ho ra he m os
u sad o p ub li c.
• Los miembros marcados como prívate sólo son visibles para la clase
en la que se definen. Los miembros privados no son accesibles desde las
clases derivadas ni desde el código que crea los objetos de la clase.
• Los miembros marcados como protected sólo son visibles para la cla-
se en la que se definen o desde las clases derivadas de esa clase. No se
puede acceder a los miembros protegidos desde el código que crea los
objetos de su clase.
• Los miembros marcados como interna 1 son visibles para cualquier
código en el mismo archivo binario, pero no son visibles para otros archi -
vos binarios. Recuerde que NET Framework acepta el concepto de ensam -
blados. que son bibliotecas de código va compiladas que pueden ser usadas
por aplicaciones externas. Si se escribe una clase en C# y se compila la
clase para obtener un ensamblado, cualquier fragmento de código del en -
samblado podrá acceder a los miembros de clase interna. S in embargo, si
otro fragmento de código usa ese ensamblado, no tendrá acceso al miem -
bro. aunque derive una clase de la clase del ensamblado.
• Los miembros marcados como protected internal son visibles
para todo el código incluido en el mismo archivo binar io y para las clases
externas que se deriven de su clase. Si se escribe una clase en C# y se
compila la clase para obtener un ensamblado, cualquier fragmento de códi -
go del ensamblado puede acceder a los miembros de clase interna. Si otro
fragmento de codigo externo usa el ensamblado y deriva una clase de la
clase del ensamblado, el miembro interno protegido sera accesible para la
clase derivada. Sin embargo, el código que trabaja con los objetos de la
clase base no tendrá acceso al miembro.
class Point2D
{
public int X;
public int Y;
}
class MyMainClass
{
public static void Main()
{
Point2D My2DPoint = new Point2D();
Point3D My3DPoint = new Point3D();
My2DPoint.X = 100;
My2DPoint.Y = 200;
My3DPoint.X = 150;
My3DPoint.Y = 250;
My3DPoint.Z = 350;
}
}
La clase derivada Point3D define los campos X e Y que coinciden con los
identificadores usados en la clase base Point2D. El compilador de C # emite las
siguientes advertencias cuando se compila este código:
warning CS0108: La palabra clave new es necesaria en
'Point3D.X' porque oculta el miembro heredado 'Point2D.X'
warning CS0108: La palabra clave new es necesaria en
'Point3D.Y' porque oculta el miembro heredado 'Point2D.Y'
class Pomt2D
{
public int X;
public int Y;
}
class MyMainClass
{
public static void Main()
{
Point2D My2DPoint = new Point2D();
Point3D My3DPoint = new Point3D();
My2DPoint.X = 100;
My2DPoint.Y = 200;
My3DPoint.X = 150;
My3DPoint.Y = 250;
My3DPoint.Z = 350;
}
}
class Pomt2D
{
publie int X;
public int Y;
public virtual void PrintToConsole()
{
System.Console.WriteLine (" ({0}, {1}) ", X, Y);
}
}
class Point3D : Point2D
{
public int Z;
My2DPoint.X = 100;
MylDPoint.Y = 200;
My3DPoint.X = 150;
My3DPoint.Y = 250;
My3DPoint.Z = 350;
My2DPoint.PrintToConsole();
My3DPoint.PrintToConsole();
}
}
Sin embargo, está permitido reemplazar un método override. Si. por algu-
na extraña razón, se quiere implementar una clase Point4D y derivarla de
Point3D. es posible reemplazar el método de Point3D PrintToConsole().
Polimorfismo
El concepto de reemplazo de métodos lleva al concepto de polimorfismo.
Cuando
reemplazamos un método, queremos llamar al método apropiado desde cualquier
método que llame a este método reemplazado.
El listado 11.5 presenta este concepto en acción. Se ha añadido a Point2D
un método UsePrintToConsole() que llama al método virtual
PrintToConsole(). Point3D hereda este método de Point2D. Cuando
se llama a PrintToConsole() en esta función, se quiere llamar a la versión
que pertenece a la clase apropiada. En otras palabras, en el método
UsePrintToConsole() que pertenece a la clase Point2D, se pretende lla-
mar al método PrintToConsole() que pertenece a la clase Point2D. En el
método UsePrintToConsole() que pertenece a la clase Point3D. se pre-
tende llamar al método reemplazado PrintToConsole() que pertenece a la
clase Point3D. Como el método PrintToConsole() fue declarado como
un método virtual, la detección de la versión que debe ejecuta rse tiene lugar
automáticamente. El listado 11.5 escribe lo siguiente en la consola:
(1 0 0 , 200)
(150, 250, 350)
class Point2D
{
public int X;
public int Y;
My2DPoint.X = 100;
My2DPoint.Y = 200;
My3DPoint.X = 150;
My3DPoint.Y = 250;
My3DPoint.Z = 350;
My2DPoint.UsePrintToConsole();
My3DPoint.UsePrintToConsole();
}
}
Métodos abstractos
Algunas clases base pueden no ser capaces de proporcionar la implementación
de un método, pero puede que queremos que las clases derivadas proporcionen
una implementación. Supongamos, por ejemplo, que estamos escribiendo en C#
una aplicación de geometría y escribimos clases llamadas S q u a r e y C i r c l e .
Decidiremos las funciones comunes que usará cada forma de la aplicación y por
lo tanto implementamos una clase base llamada S h a p e y derivamos las clases
S q u a r e y C i r c l e de Shape:
Class Shape
{
}
Ahora supongamos que decidimos que todas las formas deben ser capaces de
calcular su arca, de modo que escribimos un método llamado G e t A r e a (). El
problema de escribir ese código en la clase base es que la clase base no tien e
suficiente información para calcular un área. Cada forma calcula su área usando
una fórmula diferente.
Lo que podemos hacer es definir un método abstracto en la clase base S h a p e .
Los métodos abstractos no proporcionan una implementación propia sino que
proporcionan una firma de método que las clases derivadas deben implementar.
Los métodos abstractos dicen "Yo no se implementar este método, pero mi clase
derivada lo hará, de modo que asegúrese de que la implementen con los
parámetros
y el codigo devuelto que yo especifico." Los siguientes fragmentos demuestran
cómo declarar un método abstracto en la clase S h a p e .
abstract class Shape
{
public abstract double GetArea();
}
Las clases abstractas también son. por definición, métodos virtuales y debe
usarse la palabra clave o v e r r i d e para reemplazarlos por clases derivadas:
class Square : Shape
{
public override double GetArea()
{
// implemente el calculo del área
}
}
Las clases abstractas suelen usarse para crear una clase base común a un
conjunto de clases. Esto permite usar el polimorfismo al almacenar clases deriva-
das en algún tipo de colección, como se vio en un capítulo anterior.
class Point2D
{
public int X;
public int Y;
class MyMainClass
{
public static void Main()
{
Point2D My2DPoint = new Point2D(100, 200);
Point3D My3DPoint = new Point3D(150, 250, 350);
My2DPoint.PrintToConsole();
My3DPoint.PrintToConsole();
}
También se puede invocar a métodos de clase base con esta otra sintax is:
ba s e . P r i n t . T o C o n s o l e ( ) ;
Clases selladas
Si no quiere que se derive codigo de una determinada clase, puede marcar la
clase con la palabra clave s e a l e d . No se puede derivar una clase de una clase
sellada. Se puede especificar una clase sellada escribiend o la palabra clave s e a l e d
antes de la palabra clave c l a s s . como en este ejemplo:
sealed class MySealedClass
Contención y delegación
Si la herencia es una relación ES-UN. la contención es una relación TIENE-
UN. Un gato de Birmania ES UN gato (por lo que puede heredar la clase
De Birmania de la clase genérica Gato); pero un Coche TIENE 4 rueda s (por lo
que la clase Coche puede tener cuatro objetos Rueda). El aspecto más interesante
de la contención es que se puede emplear como sustituto de la herencia. El princi -
pal inconveniente de usar la contención en lugar de la herencia es que se pierden
las ventajas del polimorfismo. Sin embargo, se obtienen los beneficios de la
reutilización del código.
En C#. hay dos casos comunes en los que prácticamente sólo se puede emplear
la contención y no la herencia; cuando se trabaja con herencias múltiples y c uan-
do se trabaja con clases selladas. A continuación se incluye un ejemplo que mues -
tra cómo funciona esta técnica. Además, verá trabajar al polimorfismo.
Supongamos que tenemos una clase AlarmClock y una clase Radio como
las que aparecen en el siguiente fragmento y queremos crear una clase
ClockRadio que combine estas dos clases. Si C# admitiese herencias múlti -
ples. se podría hacer que ClockRadio heredase de las clases AlarmClock y
Radio. A continuación se podria añadir una variable booleana radioAlarm
que determine si se activa la alarma o la radio y que reemplace SoundAlarm()
para usar esta variable. Por desgracia. C# no admite herencias múltiples. Por
suerte, se puede emplear la contención en lugar de la herencia y obtener todas las
ventajas de la reutilización de código. Observe cómo funciona, paso a paso;
class Radio
{
protected bool on_off;
public ClockRadio()
{
radio = new Radio();
// Establecer el valor de otras variables de miembro. . .
}
SetCurrentTime(currTime);
íf (GetCurrentTime() == GetAlarmTime())
{
Console.WriteLine("Current Time = {0}!", current.Time);
SoundAlarm();
break;
}
}
}
// Otros métodos . . .
}
Este ejemplo resalta uno de los puntos fuertes de la herencia: el polimorfismo.
Ademas, cuando se añaden nuevos métodos públicos (o protegidos) a la clase
base, están automáticamente disponibles en la clase derivada. El listado 11.7 es el
listado completo con un método main() de ejemplo para que pueda experimen-
tar eon él.
using System;
namespace Containment
{
class Radio
{
protected bool on_off;
public ClockRadio()
{
radio = new Radio();
radioAlarm = false;
}
// --------- Delegar en Radio -----------
public void RadioOn()
{
radio.On();
)
public void RadioOff()
{
radio.Off();
}
clockRadio.SetRadioAlarm(true);
clockRadio.SetAlarmTime(100);
clockRadio.Run();
La palabra clave o b j e c t puede usarse como si fuera un alias del ident ificador
System.Object:
class Point2D : object
Si se deriva desde una clase base, hay que tener en cuenta que la clase base se
deriva desde o b j e c t o desde otra clase base que herede de o b j e c t . Al final, la
jerarquía de las clases base siempre incluye la cl ase NET o b j e c t .
Gracias a las reglas de herencia de C#. la funcionalidad de la clase NET
o b j e c t esta disponible para todas las clases de C#. La clase NET o b j e c t
contiene los siguientes métodos:
• p u b l i c v i r t u a l b o o l E q u a l s ( o b j e c t o b j ) :Compara dos
objetos y devuelve t r u e si son iguales y f a l s e en caso contrario. Este
método esta marcado como virtual, lo que significa que se puede reempla -
zar en las clases de C#. Quizás quiera reemplazar este método para compa -
rar el estado de dos objetos de laclase. Silos objetostienenlos mismos
valores para los campos, puede devolver t r u e ; si los valores son
diferen-
tes puede devolver f a l s e .
• p u b l i c v i r t u a l i n t G e t H a s h C o d e (): Calcula un código hash
para el objeto. Este método esta marcado como virtual, lo que signif ica que
se puede reemplazar en las clases de C#. Las colecciones de clase de NET
pueden llamar a este método para generar un codigo hash que ayude en las
consultas y clasificaciones y las clases pueden reemplazar a este método
para que genere un codigo hash que tenga sentido para la clase.
Resumen
En l a ter mi n o lo g ía d e l a p r o gr a mac ió n d e so ft wa re o ri e nt ad o a o b j eto s, la
her e nc ia se u sa p ara d es cr ib ir u na c la se q u e her e d a mie mb ro s d e u na cl as e b a se.
C# ad mi te la her e nc ia s i mp le , e n la q u e u na cla se p u ed e d er i var se d e u na so la
cla se b a se. C# no ad mi t e l a her e nci a mú l t ip l e, q ue s i es ad mi t id a p o r al g u no s
le n g uaj e s o r ie n tad o s a o b j eto s p ar a p er mi tir q ue u na c la se p ued a d er i var s e d e ma s
d e u na c la se b as e.
Las cl as e s b a se d e C# se esp eci f ic a n a l d e cl arar u na cl as e. E l id e n ti ficad o r d e
cla se b as e si g u e a l no m b r e d e la c la se d er i v ad a cu a nd o se d e cl ar a la c la se
C# p er mit e q ue lo s mie m b r o s d e cla se p er t e nez ca n a u n atr ib uto d e á mb it o . El
á mb ito d e u n mi e mb r o d eter mi n a s u ac ce sib il id a d p ara la s c la se s d er i vad as y lo s
fr a g me nto s d e có d i go q u e tr ab aj an co n lo s o b j e to s d e la c la se . Lo s mi e mb ro s d e
cla se mar cad o s co mo public so n v i sib le s p ara la s cl a se s d eri vad a s y p ara e l
co d i go q ue cr ea lo s o b j eto s d e la cla s e. Lo s mi e mb ro s d e cl as e marc ad o s co mo
prívate so lo so n v i sib l e s p ar a la c la se e n la q ue est á n d e fi nid o s o d e sd e cla se s
d er i vad as d e la c la se. Lo s mi e m b r o s d e cl a se mar cad o s co mo internal s o n
vi s ib l es p ara to d o e l co d ig o e n s u mi s mo ar c h i vo b i nar io , p ero no so n vi s ib l es
f ue r a d e lo s arc hi vo s b i nar io s. Lo s mie mb r o s d e cla se mar cad o s co mo protected
internal so n v i sib le s p ar a c u alq u ier co d i go e n s u mi s mo ar c hi vo b i nar io y
p ar a l as cla se s e xter n a s q ue se d er i v e n d e l a cl a s e. Lo s mie mb ro s d e c la s e q u e no
tie n e n ni n g u n a p a lab r a c la ve d e á mb ito so n. p o r d efec to , p ri v ad o s.
Lo s mé to d o s y la s p r o p i ed ad e s d e c la se s b a se p u ed e n i mp le me n tar se d e n ue vo
en c la se s d er i va d as p ar a p r o p o r cio n ar n u e va s i m p le me n tae io ne s. Lo s mé to d o s y
p r o p ied ad es v irt u al es , q ue es tá n mar e ad o s co n l a p al ab ra c la ve d e C# virtual.
p ued e n i mp l e me n tar se n ue v a me n te e n c la se s d eri vad as, si e mp re q ue l a n ue va
i mp le me nt ac io n ma n te n ga el mi s mo id e n ti fica d o r, tip o d e v ue lto y l is ta d e
p ar a me tro s. Las n ue va s i mp le me nt ac io ne s d e mé to d o s vir t ua le s re cib e n el no m -
b r e d e r ee mp la za d a s x d eb e n e st ar ma r cad as co n la p al ab ra cl a ve d e C#
override.
Los métodos abstractos son métodos de clases base que no pueden ser
implementados. Los métodos abstractos no suelen implementarse en clases base
porque la clase base no tiene suficiente información como para ofrecer una
implementación completa. Las clases que contienen al menos un método abstracto
reciben el nombre de clases abstractas y deben usar la palabra clave abstract
en la declaración de la clase.
C# dispone de la palabra clave base que permite que las clases derivadas
accedan a los miembros de una clase base. Se puede anteponer a los identificadores
de miembros la palabra clave base y se puede usar esta palabra clave para
llamar a un constructor de clase base desde un constructor de clase derivada.
Por defecto, se puede usar cualquier clase de C# como clase base y cualquier
clase puede derivarse de cualquier otra cla se. Puede evitar este comportamiento
marcando una clase de C# con la palabra clave sealed. Las clases selladas no
pueden usarse como clase base y el compilador de C# no permite que se deriven
clases a partir de clases selladas.
Todas las clases de C# y. de hecho, cualquier clase implementada en un len-
guaje NET. deriva en última instancia de la clase NET System.Object. La
palabra clave de C# object es otro nombre para el identificador de clase
System.Object. La clase System.Object contiene algunos métodos que
pueden usar las clases derivadas y muchos de estos métodos pueden ser reempla -
zados. La clase System. Object proporciona funcionalidad para definir igualdad
de objetos, cálculo de código hash. representaciones de cadenas, finalización de
código y clonación de objetos. El tipo object puede ser usado como un método
o variable de nombre de tipo y cualquier variable de C# puede usarse como un
objeto object. C# convierte automáticamente algunos tipos de valor, como
tipos de valor y literales numéricos y obj etos de tipo object mediante las técni -
cas de boxing y unbo.xing. La técnica de Boxing encierra un valor en un objeto y
la técnica de unboxing devuelve el valor del objeto a su tipo de valor original.
Parte III
C# avanzado
12 Cómo
trabajar
con espacios
de nombre
C ua nd o lo s d o s ar c hi vo s f ue n te se co n st r u ye n en u n so lo e ns a mb l ad o , el
co mp il ad o r d e C# cr e a u n e n s a mb l ad o co n u n so lo e sp ac io d e no mb r e.
MyAssembly. co n d o s cl a se s e n el e sp ac io d e no m b re.
Est o t ie ne u na v e ntaj a p ar a e l p r o gr a mad o r e n ca so d e q ue q u iera s ep arar
al g u na s fu n cio n al id ad e s en d i st i nto s ar c h i vo s o s i mp le me n te, si q uier e r ed uc ir al
mí n i mo la lo n gi t ud d e c ad a ar c hi vo f u e nte .
namespace Namespace1
{
class TestClass
{
public TestClass()
{
System. Console.WriteLine("Hello from
Namespace1.TestClass!");
}
}
}
namespace Namespace2
{
class TestClass
{
public TestClass()
{
System.Console.WriteLine("Hello from
Namespace2.TestClass!");
}
}
}
class MainClass
{
public static void Main()
{
Namespace1.TestClass Object1 = new Namespace1.TestClass();
Namespace2.TestClass Object2 = new Namespace2.TestClass();
}
}
El código del listado 12.1 declara dos clases llamadas TestClass. Cada
una de las declaraciones de clase está en un espacio de nombre diferente y el
constructor de cada clase escribe un mensaje en la consola. Los mensajes son
ligeramente diferentes de modo que se puede saber cual es el mensaje que emite
cada clase.
El método M a i n () del listado 1 2 . 1 crea dos objetos, uno de tipo
Namespacel. T e s t C l a s s y otro de tipo Namespace. T e s t C l a s s . Como
los constructores de las clases escriben mensajes en la consola, si se ejecuta el
código del listado 1 2 . 1 obtendremos como resultado la figura 1 2 . 1 .
Observe que la clase M a i n C l a s s del listado 1 2 . 1 no está encerrada en una
declaración de espacio de nombre. Esto es perfectamente valido en C#. No es
necesario encerrar las clases en declaraciones de espacio de nombre. Sin embar-
go. las clases que no están encerradas en espacios de nombre no pueden usar el
mismo nombre en otra clase definida sin un espacio de nombre
Si necesita usar una clase que está declarada en un espacio de nombre, debe
usar el nombre de su espacio de nombre al usar el nombre de la clase. Si no hace
esto, el compilador de C# emitirá un mensaje de error. Suponga, por ejemplo, que
el método Main() del listado 1 2 . 1 intenta crear un objeto de la clase TestClass:
TestClass Objectl = new TestClass ();
Figura 12.1. Cómo hacer referencia a clases dentro de espacios de nombre
namespace Namespacel
{
class TestClass
{
public TestClass()
{
System. Console.WriteLine("Hello from
Namespace1.TestClass!");
}
}
}
namespace Namespace2
{
class TestClass
{
public TestClass()
{
System.Console.WriteLine("Hello from
Namespace2.TestClass!");
}
}
}
class MainClass
{
public static void Main()
{
C l a s s 1 O b j e c t 1 = new C l a s s 1 ( ) ;
Class2 Object2 = new Class2();
}
}
En capítulos anteriores ya vimos que las palabras clave de C# que definen los
tipos de variable son en realidad estructuras definidas por NET Framework.
Observe de nuevo la tabla 7.1 y preste atención a lo siguiente:
• Las estructuras de tipo de valor residen en el espacio de nombre de NET
S ystem.
• La palabra clave using se usa para crear alias de los nombres d e estruc-
turas de NET con las palabras clave de C # equivalentes. Puede imaginar
como se implementa la tabla 7.1 en NET Framework mediante instruccio -
nes de C# como las siguientes:
using sbyte = System.SByte;
using byte = System.Byte;
using short = System.Int16;
// ... mas declaraciones . . .
Puede crear alias de nombres de espacio de nombres así como de clases,
como demuestra el listado 12.3.
Listado 12.3. Creación de alias de espacios de nombre
using N1 - Namespace1;
using N2 - Namespace2;
namespace Namespace1
{
c1ass Testelass
{
public TestClass()
{
System.Console.WriteLine("Hello from
Namespace1.TestClass!");
}
}
}
namespace Namespace1
{
class Test Class
{
public TestClass()
{
System.Console.WriteLine("Hello from
Namespace2.TestClass!");
}
}
}
class MainClass
{
public static void Main()
{
N1.TestClass Object1 = new N1.TestClass();
N2.TestClass Object2 = new N2.TestClass();
}
}
namespace Namespace1
{
class TestClass
{
public TestClass()
{
Console.WriteLine("Hello from Namespacel.TestClass!");
}
}
}
namespace Namespace2
{
class TestClass
{
public TestClass()
{
Console.WriteLine("Hello from Mamespace2.TestClass!");
}
}
}
class MainClass
{
public static void Main()
{
Class1 Object1 = new Class1();
Class2 Object2 = new Class2();
}
}
La directiva de espacio de nombre System del listado 12.4 permite que el
código haga referencia a la clase Console sin que se le anteponga el espacio
de nombre System.
Para definir una propiedad de sólo lectura en una interfaz, basta con incluir la
palabra clave descriptora de acceso get:
interface Interfacel
{
int RecordCount { get; }
}
Para definir una propiedad de sólo escritura en una interfaz, basta con incluir
la palabra clave descriptora de acceso set:
mterface Interfacel
{
int RecordCount { set; }
}
Para definir un indizador de sólo lectura en una interfaz, basta con incluir la
palabra clave descriptora de acceso g e t :
interface Interfacel
{
int this [int Index] { get; }
}
Para definir un indizador de de sólo escritura en una interfaz, basta con incluir
la palabra clave descriptora de acceso s e t :
interface Interfacel
{
int this [int Index] { set; }
}
Es útil derivar de una interfaz base cuando una interfaz contiene un conjunto
de firmas de método, propiedad y evento que deben ser añadidas a una interfaz
ya programada. Por ejemplo. NET Framevvork define varias interfaces que pue -
den implementarse y usarse en C#. Como las interfaces ya son una parte de
NET Framevvork, la lista de métodos, propiedades, indizadores y evento s que
admiten se ha consolidado y no puede cambiar. Si quiere usar una interfaz defini -
da y necesita añadir más firmas a la interfaz para uso propio, debería considerar
la posibilidad de derivar desde la interfaz ya definida y añadir sus nuevas firmas
en la interfaz derivada.
Las clases que implementan una interfaz derivada deben proporcionar
implementaciones para todos los métodos definidos por las interfaces base. Cuan -
do una clase implementa una interfaz, debe proporcionar el código para cada uno
de los métodos definidos en la interfaz. Si una clase implementa Interface3.
por usar el ejemplo anterior, la clase debe proporcionar las implementaciones de
método para Method1() y Method2(). No se puede derivar una interfaz de
ella misma. Observe la siguiente definición de una interfaz:
interface Interface1 : Interface1
{
void Method1();
}
Este error hace que el compilador de C# produzca el siguiente mensaje de
error:
error CS0529: La interfaz heredada 'Interface1' crea un ciclo
en la jerarquía de la interfaz 'Interface1'
Este mensaje de error indica que el código está intentando deriv ar una interfaz
de sí misma, lo que no se permite en C#. Las interfaces sólo pueden derivarse de
otras interfaces.
Cómo usar la palabra clave new
para reutilizar identificadores
Se puede usar la palabra clave new para redefinir un identificador usado en
una clase base. Suponga que está trabajando con una interfaz que define una
propiedad llamada ID:
interface BaseInterface
{
int ID { get; }
}
Ahora suponga que quiere derivar de esa interfaz, pero le gustaría usar el
identificador ID como nombre de un método:
interface DerivedInterface : BaseInterface
{
int ID();
}
Esta construcción hace que el compilador de C# emita un aviso sobre la
reutilización del identificador ID:
warning CS0108: La palabra clave new es necesaria en
'Derivedlnterface.ID()' porque oculta el miembro heredado
'Baselnterface.ID'
interface Interface2
{
void DoWork();
}
void Interface2.DoWork()
{
}
}
class MainClass
{
public static void Main()
{
PrintClass PrintObject = new PrintClass();
PrintObject.PrintMessages();
}
}
class PrintClass
{
public void PrintMessages()
{
Class1 Object1 = new Class1();
Class2 0bject2 = new Class2();
PrintMessageFromObject(Objectl);
PrintMessageFromObject(Object2) ;
}
prívate void PrintMessageFromObject (object obj)
{
if(obj is IPrintMessage)
{
IPrintMessage PrintMessage;
PrmtMessage = (IPrintMessage)obj;
PrintMessage.Print();
}
}
}
El listado 13.1 define una interfaz llamada IPrintMessage. La interfaz
IPrintMessage define un método llamado Print. Al igual que t odos los
miembros de interfaz de C#. la interfaz IPri ntMessage define miembros pero
no los i mplementa.
A continuación el listado i mplementa dos clases de control llamadas Classl
y Classl. La claseClassl i mplementa un método llamado calledPrint() .
Como Classl no hereda de la interfaz IPr intMessage. el método Print()
implementado por la clase no tie ne ninguna relación con el método Print ( )
definido por la interfaz IPrintMessage. La clase Classl i mplementa un
método llamado Print( ) .
Como Classl hereda de la interfaz IPrintMessage. el compilador de C#
considera que el método P r i n t () implementado por la clase es una
implementación del método Print() definido por la interfaz IPrintMessage.
A continuación el listado 13.1 define una clase llamada MainClass. que
implementa el método M a i n () de la aplicación y otra clase llamada
PrintClass. El método Main() del listado 13.1 crea un obj eto de la clase
PrintClass y llama a su método público para que haga el trabaj o de verdad.
El listado 13.1 ter mina declarando una clase llamada PrintClass. Laclase
PrintClass i mplement a un método publico llamado PrintMessages() y
un método de ayuda privado llamado Pr intMessageFromObj ect() . El
método PrintMessages() es el método al que llama el método Main() .
Como PrintMessageFromObj ect() esta marcado como privado, solo se
le puede llamar desde otros fragmentos de eodigo del ob j eto PrintClass y no
puede ser llamado desde el código en otras clases. El método PrintMessages()
crea un obj eto de clase Classl y un obj eto a partir de Classl y pasa cada
obj eto al método pri vado PrintMessageFromObj ect() . El método priva -
do PrintMessage FromObj ect() acepta un parametro de tipo o b j e c t
como parametro.
Tras imcializar una variable del tipo de la interfaz, se puede acceder a los
miembros de la interfaz usando la habitual notación con punto:
PnntMessage.Print();
using System;
class Class1
{
public void Print()
{
Console.WriteLine("Hello from Class1!");
}
}
class Class2 : IPrintMessage
{
public void Print()
{
Console.WriteLine("Hello from Class2!");
}
}
class MainClass
{
public static void Main()
{
PrintClass PrintOb ject = new PrintClass();
PrintObject.PrintMessages();
}
}
class PrintClass
{
public void PrintMessages()
{
Classl Objectl = new Classl();
Class2 Object2 = new Class2();
PrintMessageFromObject(Objectl);
PrintMessageFromObject(Object2);
E l o p e r a d or a s s e u s a c o m o p a r t e d e u n a e x p r e s i ón q u e s e c on s tr u y e c o m o s e
i n d i c a a c on t i n u a c i ón :
• U n i d e n t i f i c a d o r d e o bj e t o.
• L a p a l a br a c l a v e a s .
• Un identificador de interfaz.
S i e l o bj e t o d e s i g n a d o e n l a e x p r e s i ón i m p l e m e n ta l a i n te r f a z d e s i g n a d a e n l a
e x p r e s i ó n , l a i m p l e m e n t a c i on d e l o bj e t o d e l a i n te r f a z s e d e v u e l v e c om o e l r e s u l -
ta d o d e l a e x p r e s i ón . S i e l o bj e t o d e s i g n a d o e n l a e x p r e s i ón n o i m p l e m e n ta l a
i n te r f a z , d e s i g n a d a e n l a e x p r e s i ón , s e a s i g n a a l r e s u l ta d o d e l a e x p r e s i ón u n v a l or
v a c í o r e p r e s e n t a d o p or l a p a l a br a c l a v e d e C # n u l l . L a n u e v a i m p l e m e n ta c i on
d e l m é t o d o p r i v a d o P r i n t M e s s a g e F r o m O b j e c t ( ) u s a e l o p e r a d or a s . D e -
c l a r a u n a v a r i a bl e l o c a l d e l t i p o d e l a i n t e r f a z I P r i n t M e s s a g e y u s a e l op e r a -
d or a s p a r a a c c e d e r a l a i m p l e m e n t a c i on d e l o bj e t o d e l m é t o d o.
U n a v e z q u e s e h a c o m p l e t a d o l a op e r a c i ón a s . s e c o m p r u e ba l a v a r i a b l e d e
i m p l e m e n t a c i on d e l a i n t e r f a z p a r a d e s c u br i r s i ti e n e e l v a l o r n u l l . S i l a v a r i a ble
n o e s n u l l , s e s a be q u e e l o bj e t o p r o p or c i on a d o i m p l e m e n ta l a i n t e r f a z y s e
p u e d e l l a m a r a l m é t od o d e l a i n t e r f a z .
E l l i s t a d o 1 3 . 2 e s f u n c i on a l m e n t e e q u i v a l e n t e a l l i s ta d o 1 3 . 1 y e s c r i be e l s i -
g u i e n te t e x t o e n l a c o n s ol a :
Hello from Class2!
ColorName = MyRainbow[Colorlndex];
System.Consolé.WriteLine(ColorName);
1
Se puede reducir aún más este código usando la palabra clave f oreach con
la clase, como se muestra en el siguiente fragmento de código:
Rambow MyRainbow = new Rambow();
Por defecto, las clases no admiten la palabra clave foreach y usarla para
acceder a los elementos de la clase hace que el compilador de C# genere el si -
guiente mensaje de error:
error CS1579: La instrucción foreach no funciona en variables
del tipo 'Rainbow' porque 'GetEnumerator' no contiene una
definición para 'miembro' o es inaccesible
Sin embargo, se puede usar foreach en las clases si la clase implementa una
interfaz de NET Framework llamada IEnumerable. La interfaz IEnumerable
contiene métodos que NET Framework usa para extraer elementos de los objetos.
Si la clase contiene una colección de elementos y se quiere que otras partes del
código usen la palabra clave foreach para iterar cada uno de los elementos de
la colección, hay que implementar la interfaz IEnumerable en la clase.
La interfaz IEnumerable contiene una sola definición de método:
IEnumerator GetEnumerator();
class MyClass:
IEnumerable,
IEnumerator
using System;
using System.Collections ;
class Rainbow : IEnumerable, IEnumerator
{
private short IteratorIndex = -1;
return "Orange";
return "Yellow";
case 3:
return "Oreen";
case 4 :
return "Blue";
case 5:
return "Indigo";
case 6:
return "Violet";
default:
return "*** ERROR ***";
}
}
}
public bool MoveNext()
{
IteratorIndex++;
if(IteratorIndex == 7
)
return false;
return true;
}
El r e sto d e l a cl as e R a i n b o w i mp le me n t a mie mb ro s d e la i nt er fa z
I E n u m e r a t o r . El p ri me r mi e mb r o p r o p o r c io na la i mp l e me n tac ió n p a ra la p ro -
p ied ad I E n u m e r a t o r . C u r r e n t . Exa mi na el valo r d e la p ro p ied ad p ri v ad a d e
la clase IteratorIndex y devuelve una cadena que representa el color del
arco iris en el índice al que hace referencia el valor de la propiedad
IteratorIndex. La propiedad Current devuelve una variable de tipo
object. pero como las cadenas son objetos igual que todos los otros tipos de
datos, el CLR acepta los valores devueltos basados en cadenas.
La implementación del método IEnumerator.MoveNext() incrementa
el valor de la propiedad privada IteratorIndex. Como el arco iris tiene siete
colores. la implementación MoveNext() da por sentado que los valores válidos
de IteratorIndex van de 0 a 6. Si el valor llega a 7. la implementación de
MoveNext() asume que el iterador ha llegado a su límite y devolverá False.
En caso contrario, la implementación devuelve True. La instrucción que
incrementa el valor de IteratorIndex exige que el valor inicial de
IteratorIndex sea -1. Cuando se llama a MoveNext() por vez primera, la
instrucción de incremento aumenta el valor de IteratorIndex de -1 a 0.
dándole a IteratorIndex un valor válido en la primera iteración del bucle.
La implementación del método IEnumerator.Reset() simplemente res-
tablece el valor de IteratorIndex a-1. A este método se le llama si se llama
a más de un constructor foreach y NET Framework necesita devolver el esta-
do de la enumeración a su valor inicial.
Toda esta implementación hace que el método Main() sea muy claro. El
método puede crear un objeto de la clase Rainbow y usar foreach para iterar
cada nombre de color de la clase.
~MyClass()
{
}
En este ejemplo, la palabra clave using se usa para crear un nuevo objeto
llamado MyObject. El nuevo objeto pertenece a la clase MyObject. El objeto
puede usarse en cualquier instrucción que esté incluida entre las llaves que si-
guen a la palabra clave using. El objeto es automáticamente destruido cuando
la ruta de acceso del código llega a la llave de cierre del bloque using. Si la
clase del objeto creado en la instrucción using admite IDisposable. enton-
ces el método Dispose() de la clase es invocado automáticamente sin que el
cliente deba hacer nada. El listado 13.4 muestra una clase llamada MyClass que
implementa la interfaz IDisposable.
using System;
~MyClass()
{
Console.WriteLine("destructor");
}
Resumen
Piense en una interfaz como en una promesa de que una clase implementará los
métodos, propiedades, indizadores y eventos de finidos en la interfaz. Las interfaces
proporcionan definiciones de miembros, pero no proporcionan ninguna
implementación. Se necesita una clase que implemento una interfaz para propor -
cionar una implementación de cada uno de los miembros de la interfaz. Una clase
puede implementar varias interfaces, aunque sólo puede derivarse de una de las
clases base. Las interfaces pueden derivarse de otras interfaces, del mismo modo
que las clases pueden derivarse de las clases base.
Las palabras clave de C# is y as pueden usarse para trabajar con objetos que
implementen interfaces. La palabra clave is se usa en una expresión booleana
que devuelve True si un objeto implementa una interfaz y False en caso con-
trario. La palabra clave as convierte una variable de objeto en una variable de un
tipo de interfaz. Las expresiones que usan la palabra clave as devuelven null si
el objeto no implementa la interfaz designada.
En este capítulo se ve un ejemplo sobre cómo implementar una interfaz defini -
da por NET Framework. NET Framework implementa muchas interfaces y es
aconsejable revisar la documentación y estudiarlas todas. Las interfaces de NET
Framework comienzan con la letra/. Revíselas todas. Puede usar las interfaces de
NET Framework para implementar cualquier cosa, desd e dar formato personali-
zado a la consola para mecanizarla hasta la semántica de eliminación de la reco -
lección de elementos no utilizados.
14
Enumeraciones
int FileStatus;
FileStatus = FileClosed;
También puede definir algunas constantes que se pueden usar para hacer el
código más legible:
public const int DoorStateOpen = 1;
public const int DoorStateClosed = 2;
La propiedad y las constantes permiten que el código que trabaja con los
objetos de la clase pueda escribir código legible como el siguiente:
DoorStateObject = new DoorClass ();
DoorObject.DoorState = DoorClass.DoorStateOpen;
DoorObject.DoorState = DoorClass.DoorStateClosed;
Cada uno de los miembros de las enumeraciones tiene un v alor numérico aso-
ciado. Por defecto, el valor numérico del primer valor es cero y el valor de cada
uno de los otros miembros es una unidad mayor que el valor de la anterior enume-
ración. Si se usan estas reglas y la enumeración definida anteriormente, el valor
por defecto de DoorStateOpen es 0 y el valor de DoorStateClosed es
1. Silo desea, puede invalidar estos valores asignando valores a los miembros cuan-
do se definen usando el operador de asignación:
enum LegalDoorStates
{
DoorStateOpen = 100,
DoorStateClosed = 150
}
Todas las asignaciones explícitas de valor deben usar valores que incluidos
dentro de los límites válidos del tipo subyacente de la enumeración. Observe el
error en la siguiente enumeración:
enum Weather : uint
{
Sunny = -1,
Cloudy = -2,
Rain = -3,
Snow = -4
}
class DoorController
{
prívate LegalDoorStates CurrentState;
public LegalDoorStates State
{
get
{
return CurrentState;
}
set
{
CurrentState = value;
}
}
}
class MamClass
{
public static void Main()
{
DoorController Door;
Door.State = LegalDoorStates.DoorStateOpen;
}
}
La enumeración LegalDoorStates está definida fuera de una declara-
ción de clase Esto está permitido en C# y hace que la enumeración sea visible
para todas las clases del archivo fuente. Una alternativa es definir las enumera -
ciones dentro de una declaración de clase usando palabras clave de ámbito
(public. protected. internal o private) para especificar el modo en
que la enumeración es visible para las otras clases.
Tras definir la enumeración LegalDoorStates. se puede usar su nombre
como un tipo de variable. Se usa como tipo para el campo privado Currentstate
de la clase DoorController y también como tipo de la propiedad publica
State de la misma clase.
Para referirse a una enumeración del código se usan el nombre de la enumera -
ción y uno de los identificadorcs de la enumeración. Estos identificadores están
separados por un punto, como se muestra en la siguiente instrucció n:
Door.State = LegalDoorStates.DoorStateOpen;
using System;
class MainClass
{
pubJic static void Main()
{
FileAttributes FileAttr;
FileAttr = FileAttributes.AttrReadOnly |
FileAttributes.AttrHidden;
Console.WriteLine(FileAttr);
}
}
El có d i go d el li st ad o 1 4 . 2 d ef i ne u n co nj u nto d e va lo re s e n u me rad o s q ue esp e -
ci fi ca n a trib u to s p ar a u n ar c hi vo o u n d i sco . U n arc hi vo p u ed e no te ner a t rib u to s
esp e ci ale s ( F ile At trib u t es. At tr No n e) . a tr ib uto s d e so lo l ec t ura
(Fi le At trib u te s. Attr R ead O nl y) . a tr ib u to s o cu lto s (F ile At tri -
b ut e s.H id d e n) o a tr i b ut o s li sto s p ar a se r arc hi vad o s ( Fi le At t rib ute s.
Att r Read yF o r Arc hi v e) .
El co d i go d e l mé to d o M ai n( ) e sp eci f ic a u na v ari ab le lo ca l lla ma d a Fi le Att r.
q ue e s d e tip o F ile At tr ib ut e s.
El có d i go a si g na e l v al o r a u n ar c hi vo o c ul to d e so lo le ct ur a me d ia n t e u na
o p er ac ió n OR so b r e lo s atr ib uto s F ile At trib u te s. At tr Read O nl y y
Fi le Att r ib u te s.H id d e n. El v alo r d e l a v ar i ab le lo c al se e scrib e a co nt i -
n ua ció n e n la co n so la . S i se co mp i la y ej e c uta el li st ad o 1 4 .2 se e sc rib e lo s ig u ie n -
te e n la co n so la :
3
IntEnum IntEnumValue;
int IntValue;
IntEnumValue = EnumTwo;
IntValue = (int) IntEnumValue; // el valor es 2
using System;
class DoorController
{
private LegalDoorStates CurrentState;
set
{
CurrentState = value;
}
}
}
class MainClass
{
public static void Main()
{
DoorController Door;
string EnumName;
Door.State = LegalDoorStates.DoorStateOpen;
EnumName = LegalDoorStates.GetName(typeof
(LegalDoorStates), Door.State);
Consolé.WriteLine(EnumName);
}
}
El método M a i n () del listado 14.3 usa el método G e t N a m e () de la clase
S y s t e m . E n u m para obtener una cadena que represente un valor de enumera -
ción El primer parametro es un objeto T y p e que especifica la enumeración a la
que se consulta. La expresión t y p e o f ( L e g a l D o o r S t a t e s ) devuelve un objeto
NET T y p e para el tipo especificado (en este caso, la enumeración
L e g a l D o o r S t a t e s ) . El segundo parámetro es el valor de la enumerac ión ac-
tual. de la que debe devolverse la representación de su cadena. La siguiente ins -
trucción muestra cómo puede usarse el método G e t N a m e () para obtener el
nombre de un valor enumerado:
EnumName = LegalDoorStates.GetName(typeof(LegalDoorStates),
Door.State);
using System;
MyColor = Color.Green;
Console.WriteLine (" {0} ", MyColor.Comparelo(Color.Red));
Console.WriteLine (" {0} ", MyColor.Comparelo(Color.Green));
Console.WriteLine (" {0} ", MyColor.Comparelo(Color.Violet));
}
}
El listado 14.4 declara una clase con una enumeración pública llamada Co-
lor. Sus valores varían de 0 a 6. El método Main() declara una variable de
tipo Color llamada MyColor y asigna el valor Green a la variable. A conti-
nuación invoca a CompareTo() para comparar el valor de la variable con otros
valores de la enumeración. El método CompareTo() devuelve uno de estos tres
valores:
• -1 si el valor que se pasa como argumento a CompareTo() tiene un
valor superior al valor enumerado usado para inv ocar al método
• 1 si el valor que se pasa como argumento a CompareTo() tiene un valor
inferior al valor enumerado usado para invocar al método
• 0 si los dos valores son iguales
En el listado 14.4. se llama tres veces al método CompareTo(). En la prime-
ra llamada, la variable MyColor se compara con el valor Red. Como Green.
que tiene el valor 3. tiene un valor superior a Red. que tiene el valor 0.
CompareTo() devuelve 1. En la segunda llamada, la variable MyColor se
compara con el valor Green. Como los valores son iguales. CompareTo()
devuelve 0. En la ultima llamada, la variable MyColor se compara con el valor
Violet. Como Green. que tiene el valor 3. tiene un valor inferior a Violet.
que
tiene un valor 6. CompareTo() devuelve -1.
El argumento usado en la llamada a CompareTo() debe ser del mismo tipo
que la enumeración usada para llamar al método. Usar cualquier otro tipo, inclu -
so el tipo subyacente de la enumeración, produce un error en tiempo de ejecución
Cómo descubrir el tipo subyacente en tiempo
de ejecución
Descubrir el tipo subyacente de una enumeración en tiempo de ejecución es
sencillo con el método GetUnderlyingType(). Este método, al que se llama
en el tipo de enumeración, en lugar de una variable del tipo, toma un parámetro
Type que representa el tipo de la enumeración y devue lve otro objeto Type que
representa el tipo subyacente de la enumeración.
El método ToString() puede ser llamado en el objeto Type devuelto
para obtener un nombre legible para el tipo, como se muestra en el siguiente
código:
string FormatString;
Type UnderlyingType;
UnderlyingType =
BitsToSet.GetUnderlyingType(typeof (BitsToSet));
Console.WriteLine(UnderlyingType.ToString());
ValueArray = Color.GetValues(typeof(Color));
foreach(Color ColorItem in ValueArray)
Console.WriteLine(ColorItem.ToString());
Esta llamada devuel ve un obj eto que representa el valor enumerado llamado
B l u e en una enumeración llamada C o l o r Como muchos otros métodos de
enumeración, el mét odo P a r s e () es llamado en el tipo, en lugar de una variable
del tipo.
El método P a r s e () devuelve un obj eto, que necesita ser convertido explíci -
tamente en un valor del tipo apropiado. El siguiente ej emplo muestra cómo el
método P a r s e () pude ser usado como uno de los muchos modos de representar
un valor enumerado:
Color ColorValue;
object ParsedObject;
NewRequestHandler Handlerlnstance;
using System;
public Counter()
{
OnEvenNumber = null;
}
public void CountTo'100()
{
int CurrentNumber;
using System;
class Counter
{
public event EvenNumberHandler OnEvenNumber;
public Counter()
{
OnEvenNumber = null;
}
EventArguments = new
OnEvenNumberEventArgs(CurrentNumber);
OnEvenNumber(this, EventArguments);
)
}
)
}
}
class EvenNumberHandlerClass
{
public void EvenNumberFo und (object Originator,
OnEvenNumberEventArgs EvenNumberEventArgs)
{
Console.W riteLi ne(E v enN umberEv entArgs.Num ber);
)
)
Cla s s M a i n C I a s s
{
public static void Main()
{
Cunter MyCounter = new Counter();
EvenNumberHandlerClass MyEvenNumberHandlerClass = new
EvenNumberHandlerClass();
M y C o u n t e r . 0n E v e n N u m b e r + = n e w
EventN um berH andl er(MyE venNum berH andler Cl ass.E venNumberFound);
MyCounter.CountT o100();
}
Eventos estáticos
Los eventos modificados con la palabra clave s t a t i c se comportan de forma
parecida a los campos estáticos, en el sentido de que. aunque cada copia de una
clase contiene copias separadas de todos los campos, sólo puede haber una copia
de un miembro estático en un momento dado. Todos los objetos de la clase com -
parten los eventos estáticos. Cuando se les hace referencia, debe ser a través del
nombre de la clase y no mediante el nombre del objeto, como se aprecia a conti -
nuación:
public class Counter
{
public static event EvenNumberHandler OnEvenNumber;
// ...
Counter.OnEvenNumber += new
EvenNumberHandler(MyEvenNumberHandlerClass.EvenNumberFound);
Eventos virtuales
Los eventos modificados con la palabra clave virtual marcan cualquier
descriptor de acceso add o remove como virtual. Los descriptores de acceso
virtuales en clases derivadas pueden ser reemplazados.
Eventos de reemplazo
Los eventos modificados con la palabra clave override marcan cualquier
descriptor de acceso add o remove como eventos de reemplazo add o remove
con el mismo nombre en una clase base.
Eventos abstractos
Los eventos modificados con la palabra clave abstract marcan cualquier
descriptor de acceso add o remove como abstracto. Los descriptores de acceso
abstractos no proporcionan una implementacion propia: en su lugar, un evento de
reemplazo de una clase derivada proporciona una implementacion.
Resumen
Las clases pueden desencadenar eventos cuando un programa pide a sus clien -
tes que le avisen de las acciones llevadas a cabo por la clase. Sin eventos, los
usuarios llaman a un método para realizar una operación, pero en realidad no
saben lo avanzada que está la operación. Imagine, por ejemplo, un método que
recupera una página Web de un servidor Web. Esa operación consiste en varios
pasos:
• Conectar con el servidor Web.
• Solicitar la página Web.
• Recuperar la página Web devuelta.
• Desconectar del servidor Web.
Es posible diseñar una clase como ésta con eventos que se desencadenen
cuando comience cada una de estas acciones. Al desencadenar los eventos en
los pasos críticos del proceso le da pistas a los usuarios de su clase sobre en qué
parte del proceso se encuentra el código.
Los invocadores responden a los eventos registrando métodos llamados
controladores de eventos. Los controladores de eventos se invocan cuando una
clase desencadena algún evento. Estos métodos se corresponden con la lista de
parámetros y devuelven un valor de un método patrón especial llamado delegado.
Un delegado describe el diseño de un controlador de eventos, indicando qué
parámetros debe admitir y cómo debe ser su código devuelto.
Los controladores de eventos se instalan usando el ope rador +=. Los ex entos
se declaran normalmente como campos públicos en una clase y los invocadores
añaden sus controladores de eventos a la clase creando un nuevo objeto de la clase
delegado y asignando el objeto al evento usando el operador +=. C# permite
especificar varios controladores de eventos para un solo evento y también permite
usar el operador -= para eliminar un controlador de eventos de un evento.
C# no obliga a usar un único patrón de diseño para los delegados, pero NET
Framework recomienda un diseño. Usar el diseño recomendado proporciona un
estándar que. cuando se respeta, puede dar a los delegados una lista de parámetros
de método: un objeto que especifica los objetos que desencadenan el evento y un
objeto de una clase derivada de la clase System.EventArgs. que contiene los
argumentos para el evento.
C# hace que sea muy sencillo ímplementar exentos en las clases. En el nivel
mas básico, cada exento se declara como un campo público y se pueden gestionar
tantos ex entos como se desee. Si la clase va a gestionar varios ex entos y el numero
de campos públicos dentro de la clase parece tener una cantidad excesiva de
código, se pueden escribir descriptores de acceso de exentos que permitan contro -
lar el modo en que la clase gestiona los controladore s de exentos. En lugar de
definir campos públicos para los exentos, los descriptores de acceso de exentos
permiten definir los exentos como propiedades con bloques de código add y
remove. El bloque de codigo add se invoca cuando se añade un controlador de
exentos a un exento y el bloque de código remove se invoca cuando se elimina
un controlador de exentos de un exento. Estos bloques de código se pueden
ímplementar almacenando los controladores de eventos en una matriz o en una
lista para ser usados posteriormente.
El concepto de C# de ex entos y delegados es un concepto nuevo para la familia
de lenguajes C. Los ex entos pueden ser desencadenados en C y C++ usando otros
mecanismos, pero estos lenguajes no definen palabras clave para hacer que fun -
cionen los ex entos. En C#. los exentos y delegados son elementos completamente
definidos por sí mismos y tanto el lenguaje como el compilador tienen compatibi -
lidad integrada para el control de exentos.
Otra ventaja de usar exentos en C# es que son completamente c ompatibles
con el CLR. lo que significa que se puede preparar un mecanismo de exento en
C# y desencadenar eventos que se controlen por otro lenguaje NET Como el
CLR admite eventos en el nivel de tiempo de ejecución, se puede desencadenar
un evento en C# y controlarlo y procesarlo con otro lenguaje, como Visual Basic
NET
16 Control
de excepciones
La clase exception
Todas las excepciones iniciadas por .NET Framework son clases derivadas de
la clase S y s t e m . E x c e p t i o n . La tabla 16.1 describe algunos miembros útiles
de esta clase.
Miembro Descripción
using System;
class MainClass
{
public static void Main()
{
int [] LargeArray;
try
{
LargeArray = new int [2000000000];
}
catch (OutOfMemoryException)
{
Console.WriteLine("The CLR is out of memory.");
}
)
}
El codigo del listado 16.1 intenta asignar un espacio a una matriz de dos mil
millones de números enteros. Dado que un número entero requiere cuatro botes de
memoria, se necesitan ocho mil millones de botes para contener una matriz de este
tamaño. Es bastante probable que su ordenador no disponga de esta cantidad de
memoria o de que la asignación falle. El codigo encierra a la asignación en un
bloque try y define un bloque catch para que controle cualquier excepción
OutOfMemoryException iniciada por el CLR.
StackOverflowException
El CLR inicia la excepción StackOverflowException cuando agota el
espacio de la pila. El CLR gestiona una estructura de datos llamada stack, que
registra los métodos que han sido llamados y el orden en el que fueron llamados.
El CLR tiene una cantidad limitada de espacio de pila disponible y si se llena, se
inicia la excepción. El listado 16.2 muestra la excepción StackOverflow-
Exception.
Listado 16.2. Excepción StackOverflowException
using System;
class MainClass
{
public static void Main()
{
try
{
Recursive();
}
catch(StackOverflowException)
{
Console.WriteLine("The CLR is out of stack space.");
}
}
public static void Recursive ()
{
Recursive();
}
}
El código del listado 16.2 implementa un método llamado Recursive().
que se llama a sí mismo antes de regresar. Este método es llamado por el método
Main() y con el tiempo, hace que el CLR agote su espacio de pila porque el
método R e c u r s i v e () nunca regresa. El método M a i n () llama a
Recursive(). que a su vez llama a Recursive(), que a su vez llama a
Recursive() y así sucesivamente. A la larga, el CLR se quedará sin espacio
de pila e iniciará la excepción StackOverflowException.
NullReferenceException
En este ejemplo, el compilador atrapa un intento de eliminar la referencia de
un objeto nuil. El listado 16.3 muestra la excepción NullReference-
Exception.
Listado 16.3. Excepción NullReferenceException
using System;
class MyClass
(
public int Value;
}
class MainClass
{
public static void Main()
{
try
{
MyObject = new MyClass();
MyObject = null;
MyObject.Value = 123;
TypelnitializationException
El C LR i n ici a l a ex cep c ió n TypelnitializationException c ua nd o
u na c la se d e fi ne u n co n str u cto r e st át ico y el co n str u cto r i n ic ia u na e xc ep ci ó n. Si
no ha y b lo q ue s catch e n e l co n str u cto r p ar a a tr ap ar l a e xcep ció n, el C LR i nic ia
u na e xcep ció n TypelnitializationException.
InvalidCastExpression
El CE R i n ic ia la e xc ep ció n InvalidCastExpression si fa ll a u na co n -
ver s ió n e xp l íc it a. E sto p ued e o c ur r ir e n co n te xto s d e i n ter fa z. E l li s tad o 1 6 .4
mu e s tr a u n a e xc ep c ió n InvalidCastExpression.
Listado 16.4. Excepción i n v a l i d C a s t E x c e p t í o r .
using System;
Formattable = (IFormattablejMyObject;
/ / e s p e r e a q u e e l u s u a r i o r e c o n o z c a l o s r e s u l t a d os
Console.Read();
}
}
}
ArrayTypeMismatchException
El C LR i n ic ia l a e x cep ció n A r r a y T y p e M i s m a t c h E x c e p t i o n cua n d o el
có d i go i nt e nt a al ma ce n a r u n el e me n to e n u n a ma triz c u yo tip o no co i n ci d e co n e l
tip o d e l e le me n to .
IndexOutOfRangeException
El C LR i n ici a la e xcep ció n I n d e x O u t O f R a n g e E n c e p t i o n cua nd o el c o d i go
in te n ta a l ma ce nar u n e l e me nto e n u n a ma tr iz e mp lea nd o u n Í nd ice d e ele me n to
q ue e sta f uera d el ra n go d e la ma tr i z. E l l is tad o 1 6 .5 d es crib e l a e xc ep c i ó n
IndexOutOfRangeException.
Listado 16.5. Excepción IndexOutOf RangeException
using System;
class MainClass
{
public static void Main()
{
try
{
int[] IntegerArray = new int[5];
IntegerArray[10] = 123;
El código del listado 16.5 crea una matriz con cinco elementos y a continua-
ción intenta asignar un valor al elemento 10 de la matriz. Como el indice 10 está
fuera del rango de la matriz de números enteros, el CLR inicia la excepción
IndexOutOfRangeEzception.
DivideByZeroException
El CLR inicia la excepción DivideByZeroException cuando el código
intenta realizar una operación que da como resultado una división entre cero.
OverflowException
El CLR inicia la excepción OverflowException cuando una operación
matemática guardada por el operador de C# checked da como resultado un
desbordamiento. El listado 16.6 muestra la excepción OverflowException.
Listado 16.6. Excepción OverflowException
using System;
class MainClass
{
public static void Main()
{
try
{
checked
{
int Integerl;
int Integer2;
int Sum;
Integerl = 2000000000;
Integer2 = 2000000000;
Sum = Integerl + Integerl;
}
using System;
MyOb]ect.ThrowException();
}
}
public int X
{
get
{
return XCoordinate;
}
set
{
if ( (value >= 0) && (value < 640))
XCoordinate = value;
else
throw new CoordinateOutOfRangeException();
}
}
public int Y
{
get
{
return YCoordinate;
}
set
{
if ((value >= 0) && (value < 480))
YCoordinate = value;
else
throw new CoordinateOutOfRangeException();
)
}
public static void Main()
{
Point MyPoint = new Point();
try
{
MyPoint.X = 100;
MyPoint.Y = 200;
Console.WriteLine("((0),{1})", MyPoint.X , MyPoint.Y);
MyPoint.X = 1500;
MyPoint.Y = 600;
Console.WriteLine("({0}, {1})", MyPoint.X, MyPoint.Y);
}
}
}
El có d i g o d el li st ad o 1 6 . 8 co mp r ueb a e l va lo r d e lo s d es crip to re s d e a cce s o d e
la p r o p i ed ad set p ara g ar a nt iza r q u e el v alo r p ro p o rcio nad o es tá d e n tr o d e lo s
lí mi te s v al id o s. Fi n ca s o co ntr a r io , s e i n ic ia u na e xc ep ció n . La a si g n a ció n d e l
p r i mer p u n to t ie ne é xi to ya q u e lo s d o s va lo r e s e s tá n d e n tro d e lo s lí mit e s va lid o s.
Si n e mb ar go , el se g u nd o p u n to no t ie ne é xi t o , ya q ue la co o rd e nad a x e s tá fue ra d e
lo s l í mi te s vál id o s E s te va lo r f u er a d e lo s l i mi t es val id o s h ace q ue s e i ni ci e u n
o b |e to d e cl as e C o o r d i n a t e O u t O f R a n g e E x c e p t i o n .
Si se co mp ila y ej ec u ta el l i st ad o 1 6 .8 se es cr ib e lo si g u ie n te e n l a co n s o la:
(100, 2 0 0 ))
The supplied coordínate is out ot range.
Resumen
NET Fra me wo r k u sa l a s ex cep c io ne s p ar a i n fo r m ar d e d i fe re nt es erro re s a la s
ap li cac io ne s NET El le n g uaj e C# ad mi te p er fe cta me n te e l tr ata mie n to d e las
ex cep c io ne s y p er mi te a l u s ua r io d e f i nir s u s p ro p ia s e xcep cio n es, ad e ma s d e tr a -
b aj ar co n l as e x cep c io ne s d e fi n id a s p o r NET Fra me wo r k. E l co d i go d e C # p ued e
in ic iar y a trap ar e xc ep c io n e s. T a mb i é n p ued e a trap ar e xc ep cio n es i n ic i ad as p o r
NET Fr a me wo r k
La ve n taj a d e u sar e xc e p cio ne s r e sid e e n q ue n o e s ne ce s ar io co mp ro b a r cad a
lla ma d a d e mé to d o e n b u sca d e u n er r o r . Se p ue d e e n cerrar u n gr up o d e lla ma d a s
d e méto d o e n u n b lo q ue tr y y se p u ed e e sc r ib i r c ó d i go co mo s i cad a l la m ad a d e
método del bloque tuviera éxito. Esto hace que el codigo del bloque try sea
mucho mas limpio porque no necesita ninguna comprobación de errores entre
líneas. Cualquier excepción iniciada desde el codigo del bloque try es
gestiona-
da en un bloque catch.
17 Cómo
trabajar
con atributos
Atributos
C# permite que los atributos se antepongan a los siguientes constructores de
C #:
• Clases.
• Miembros de clase, incluyendo constantes, campos, métodos, propiedades,
eventos, indizadores. sobrecargas de operador, constructores y destructo -
res.
• Estructuras
• Interfaces.
• Miembros de interfaces, incluyendo métodos, propiedades, eventos e
indizadores.
• Enumeraciones y miembros de enumeraciones.
• Delegados.
Para especificar un atributo en el código se escribe su nombre entre corchetes.
La especificación de atributo debe aparecer antes de la declaración en la que se
debe aplicar el atributo. Los atributos mas simples pueden tener este aspecto:
[ M y A t t r i b u te]
Declaración Destino
Assembly Assembly
Module Module
Class Type
Struct Type
Interface Type
Enum Type
Field Field
Property - Indexer Property
Property - Get Accessor Method (por defecto) o Return Valué
Property - Set Accessor Method (por defecto), Param or Return Valué
Event - Field Event (por defecto), Field o Method
System. Diagnostics.ConditionalAttribute
El at rib u to C o n d i t i o n a l es el a lia s d e S y s t e m . D i a g n o s t i c s .
C o n d i t i o n a l A t t r i b u t e . q ue so lo p u ed e ap l icar se a d ec lara cio n es d e me to -
d o d e cla se. E sp e ci fica q ue e l mé to d o so lo d eb e ser i n cl u id o co mo u na p arte d e la
cla se si el co mp i lad o r d e C # d e fi ne el sí mb o lo q ue ap are ce co mo p ara me tro d e l
atr ib u to . E l li st ad o 1 7 .1 mu e s tr a el f u n cio n a mi e n to d el at rib u to C o n d i t i o n a l .
Listado 17.1. Cómo trabajar con el atribut o Conditional
using System;
using System.Diagnostics;
[Conditionall ("DEBUG")]
pu b1ic void Method2()
{
Console.WriteLine("Hello from Method2!");
}
pub1c void Method3()
{
Console.WriteLine("Helio from Method3!");
}
}
class MainClass
{
public static void Main()
{
TestClass MyTestClass = new TestClass();
MyTestClass.Method1();
MyTestClass.Method2();
MyTestClass.Method3();
}
}
System.SerializableAttribute class
El atributo Serializable es el alias de la clase System.Seriali-
zableAttribute. que puede ser aplicado a clases. Indica a NET Framework
que los miembros de la clase pueden ser señalizados a y desde un medio de
almacenamiento, como un disco duro. El uso de este atributo hace que no resulte
necesario agregar la función de estado en las clases para que su almacenamiento
en el disco y su posterior recuperación. Cuando se serializan tipos, todos los
datos de la clase marcada como Serializable se guardan en el estado en el
que se encuentran cuando el dato es persistente. Si hay tipos dentro de la clase que
no quiere que sean persistentes, puede marcarlos con el atributoNonSerialized.
que es el alias de la clase System.NonSerializableAttribute. En el
siguiente fragmento de código, los datos de la cadena password marcados como
NonSerialized no son persistentes para el archivo o flujo para el que se
escriben los datos de clase:
[serializable()]
public class Users{
public FillData() {
username = "admin";
password = "password";
emailaddress = "billg@microsoft.com";
phonenumber = "555-1212";
}
}
Para mostrar un ejemplo de serialización completo, el listado 17.2 vuelve a la
clase Point2D con la que ya hemos trabajado. La clase está marcada con el
atributo Serializable. lo que significa que puede ser guardada y leída
desde
un flujo de datos.
Listado 17.2. Cómo trabajar con el atributo Serializable
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
[Serializable]
class Point2D
{
public int X;
public int Y;
}
class MyMainClass
{
public static void Main()
{
Point2D My2DPoint = new Point2D();
My2DPoint.X = 100;
My2DPoint.Y = 200 ;
System.ObsoleteAttribute class
El atributo O b s o l e t e puede ser aplicado a cualquier tipo de C# excepto a
ensamblados, módulos, parámetros y valores devueltos. El atributo O b s o l e t e
permite definir fragmentos de código que se van a reemplazar o que ya no son
válidos. Las propiedades Message e IsError de la clase Obsolete otorgan
el control del modo en el que el compilador controla los tipos marcados con el
atributo Obsolete. Al asignar a la propiedad IsError el valor True. el
compilador produce un error y el mensaje de error es la propiedad de cadena
asignada a la propiedad Message. El valor por defecto de la propiedad IsError
es False. lo que hace que se produzca un aviso cuando se compila el código. En
el siguiente código, el método HelloWorld está marcado como Obsolete.
using System;
public class RunThis
{
public static void Main()
{
// Esto genera un aviso de tiempo de compilación.
Console.WriteLine(HelloWorld());
Console.ReadLine();
}
// Marca HelloWord como Obsolete
[Obsolete("Next versión uses Hello Universe")]
public static string HelloWorld()
{
return ("HelloWorld");
}
}
La figura 17.1 muestra la lista de tareas de los avisos producidos al compilar
el código anterior.
Como puede ver. el uso del atributo Obsolete permite mantener el código
existente mientras nos aseguramos de que los programadores no están usando
tipos desfasados.
Cómo escribir sus propias clases de atributo
NET Framework parte con un buen número de clases de atributo que pueden
usarse para diferentes propósitos. Sin embargo, podria necesitar un atributo que
cumpliera una función no incluida en NET Framework. Por ejemplo, podría
desear disponer de un atributo de revisión de código que etiquetara una clase con
la fecha de la última vez que el código de esa clase fue revisado por sus compañe-
ros. En ese caso, necesitará definir sus propios atributos y hacer que funcionen
como cualquier atributo incluido en NET Framework. Por suerte. NET Framework
admite perfectamente la construcción de nuevas clases de atributos. En esta sec-
ción aprenderemos cómo el código NET desarrolla y usa las nuevas clases de
atributos.
Puede escribir sus propias clases de atributos y usarlas en su código del mismo
modo que usaría un atributo procedente de NET Framework. Las clases de atri-
buto personales funcionan como clases normales; tienen propiedades y métodos
que permiten al usuario del atributo asignar y recuperar datos.
Los atributos se implementan con clases de atributo. Las clases de atributo
derivan de una clase del espacio de nombre System de NET llamada
Attribute. Por norma, las clases de atributo llevan antepuesta la palabra
Attribute:
public class CodeAuthorAttribute : Attribute
{
}
using System;
using System.Diagriostics;
using System.Reflection;
[AttributeUsage(AttributeTargets.Class)]
public class ClassAuthorAttribute : Attribute
{
prívate string AuthorName;
[Conditional("DEBUG")]
public void Method2()
{
Console.WriteLine("Hello from Method2!");
}
MyTestClass.Methodl () ;
MyTestClass.Method2 () ;
MyTestClass.Method3 () ;
object [] ClassAttributes ;
Memberlnfo TypeInformation;
C1assAttribute =
(ClassAuthorAttribute) (ClassAttnbutes[0]);
Console.WriteLine("Class Author:{0}",
ClassAttribute.Author);
}
}
}
El co d i go d ed l i stad o 1 7 .3 co mie nz a co n u n a n ue v a cla s e d e atr ib uto l la mad a
CodeAuthorAttribute. La c la se sir v e co mo u na cl as e d e a tr ib uto p ara u n
atr ib u to q u e só lo p ued e ap l icar se a o tr a s c la se s . La cla se rec ib e u n p ar a metro d e
cad e na, q ue se a l mac e na e n u na v ar i ab l e p r i vad a y al q ue s e acc ed e p ub l ica me n te
med ia n te u na p ro p ied a d d e so lo lec t ur a l la mad a Author. La i nt en ció n d e l
p ar a me tr o es mar car u na c la se co m o p o se ed o ra d e u n no mb r e d e p r o gra mad o r
esp e cí f ico ad j u nto , d e mo d o q u e lo s d e má s p r o gra mad o re s sep a n co n q ui e n d eb e n
co n tac tar s i t ie n e n a l g u n a d ud a so b r e la i mp l e me nt ac io n d e la cla s e.
La cl a se TestClass us a el atr ib uto CodeAuthor y p ro p o rc io na e l p a ra me tr o
Jeff Ferguson.
b l r a s go ma s i nte re sa nt e d el li s tad o 1 7 .3 es el mé to d o M ai n() . q ue o b ti en e u n
o b j eto d e atr ib uto d e l a cla se y e scr ib e el n o mb r e d el a u to r. E s to lo hac e med i a nte
u n co ncep to lla mad o re f lex ió n , q ue i mp le me n ta la s c la se s d e u n e sp a cio d e no m-
b res NET lla mad o System.Reflection. Me d ia nt e la r ef lex ió n , e l có d i go
p ued e, e n ti e mp o d e ej e cu ció n, e st ud iar la i mp l e me nt ació n d e u na cl a se y d es c u -
brir cómo está construida. La reflexión permite que el código examine otros frag-
mentos de código para derivar información, como los métodos y propiedades que
admite y la clase base de la que deriva. La reflexión es una función muy potente y
es completamente compatible con .NET Framework.
El código del listado 17.3 usa la reflexión para obtener una lista de atributos
asociados a una clase particular. El código de atributo comienza recuperando un
objeto Type para la clase TestClass. Para conseguir el objeto Type se usa el
operador de C# typeof() . Este operador toma como argumento el nombre de
la clase cuyo tipo de información se va a recuperar. El objeto Type devuelto, que
está definido en el espacio de nombre de NET Framework System, funciona
como una tabla de contenidos, describiendo todo lo que se debe saber sobre la
clase requerida.
Después de recuperar el objeto Type para la clase, el método Main() llama
a un método llamado GetCustomAttributes() para conseguir una lista de
los atributos que permite la clase descrita por el objeto Type. Este método de-
vuelve una matriz de objetos y acepta como parámetro el tipo del atributo que
debe recuperarse. En el listado 17.3, el método GetCustomAttributes()
es invocado con información de tipo para la clase CodeAuthorAttribute
como parámetro. Esto obliga al método GetCustomAttributes() a devol-
ver sólo información sobre los atributos de clase que sean del tipo
CodeAuthorAttribute. Si la clase hubiera usado algún otro atributo, la
llamada no podria devolverlos. El código del listado 17.3 finaliza tomando el
primer atributo CodeAuthorAttribute de la matriz y solicitándole el valor
de su propiedad Author. El valor de la cadena se escribe en la consola.
Si se ejecuta el código del listado 17.3 se escribe lo siguiente en la consola (si
compila el código sin definir el símbolo DEBUG):
Hello from Methodl!
Hello from Method3!
Class Author:Jeff Ferguson
Resumen
NET Framework permite usar atributos en los lenguajes que se ejecutan con
el CLR. El concepto de atributo abre la puerta a la expansión de la funcionalidad
de los lenguajes NET con clases que pueden agregar comportamientos al código.
El lenguaje C# permite el uso de atributos creados por otras personas en su
código C# y también la creación de atributos propios, que pueden ser usados por
otros programadores de NET.
El concepto de atributo no es exclusivo de C # : más bien, está disponible para
cualquier lenguaje que se ejecute con el CLR. Los atributos le conceden la posibi-
lidad de extender el entorno del lenguaje y aporta nuevas herramientas para que
los programadores trabajen con código NET. El proceso de serialización es un
buen ejemplo de esto. La señalización no está integrada en la especificación del
lenguaje C#. pero su funcionalidad está disponible por medio de una clase de
atributo escrita por Microsoft. La clase de atributo extiende el lenguaje en tiempo
de ejecución para que admita una característica que no fue diseñada en ese len -
guaje.
Al igual que los demás constructores de .NET Framework. los atributos son
objetos. Se definen por clases que derivan de la clase System.Attribute de
NET Framework. Puede usar C# para desarrollar nuevas clases de atributo con
sólo derivar una nueva clase de la clase base System. Attribute. Los atri-
butos que desarrolle en C# y los atributos ya definidos por NET Framework
pueden ser usados por cualquier lenguaje compatible con el C LR.
Los atributos se usan especificando entre corchetes el nombre de clase del
atributo, inmediatamente antes del constructor de C# al que se aplica el atributo.
Los atributos pueden aceptar datos en forma de parámetros, que pueden asociar
datos de estado al atributo. Estos datos pueden ser recuperados por código de
reflexión que puede consultar el código y buscar atributos.
18 Cómo
utilizar
versiones
en sus clases
Casi todo el código escrito para las modernas aplicaciones evoluciona con el
tiempo. Los proyectos de software comienzan con un conjunto de requisitos y se
diseñan las clases para que cumplan esos requisitos. Este primer código base
sirve como código fuente de la versión 1.0 de la aplicación. Sin embargo, casi
todas las aplicaciones van más allá de la versión 1.0. Las actualizaciones de la
aplicación proceden de un grupo de requisitos mejorado y la versión 1.0 del códi -
go base debe ser revisada para implementar los requisitos actualizados.
El lenguaje C# admite constructores que hacen las clases lo suficientemente
estables como para evolucionar mientras cambian los requisitos de la aplicación.
En este capítulo, aprenderemos a usar las palabras clave new y override en
métodos de clase de C# para asegurarnos de que las clases pueden continuar
usándose a medida que cambian los requisitos de la aplicación.
using System;
public BaseClass()
{
Value = 123;
}
}
public class DerivedClass : BaseClass
{
public void PrintValue ()
{
Console.WriteLine("Value = " + Value);
}
}
class MamClass
{
public static void Main()
{
DerivedClass DerivedeClassObject = new DerivedClass();
DerivedClassObject.PrintValue();
}
}
El código del listado 18.1 es relativamente sencillo. Contiene una clase base
llamada BaseClass que incluye una variable entera protegida. Otra clase, lla-
mada DerivedCl ass. deriva de la clase BaseClass e implementa un méto-
do llamado PrintValue(). El método Main() crea un objeto de tipo
DerivedClass y llama a su método PrintValue(). Si se ejecuta el codigo
del listado 1 8.1 se escribe lo siguiente en la consola:
Valué = 121
Ahora suponga que los requisitos cambian y otro programador decide desarro -
llar la clase B a s e C l a s s mientras continuamos trabajando en nuevas mejoras
para la clase D e r i v e d C l a s s . ¿Que ocurrirá si el otro programador agrega un
método a la clase B a s e C l a s s llamado P r i n t V a l u e () y proporciona un
implementación ligeramente diferente? El código seria como el listado 18.2.
using System;
public BaseClass()
{
Value = 123;
}
public virtual void PrintValue()
I
Console.WriteLine ("Value: " + Value);
(
}
public class DerivedClass : BaseClass
i
public void PrintValue()
{
Console.WriteLine ( "Value = " + Value) ;
}
}
class MainClass
{
public static void Main()
{
DerivedClass DerivedClassObject = new DerivedClass();
DerivedClass Object.PrintValue)) ;
}
}
Ahora tenemos un problema. La clase D e r i v e d C l a s s deriva de la clase
B a s e C l a s s y ambas implementan un método llamado P r i n t V a l u e (). La
clase B a s e C l a s s ha sido actualizada a una nueva versión, mientras que la clase
D e r i v e d C l a s s ha permanecido con su implementación original. En el listado
18.2. la relación entre el método P r i n t V a l u e () de la clase base y el método
P r i n t V a l u e () de la clase derivada no está clara. El compilador debe saber
que método reemplaza a la versión de la clase base. Y el compilador no sabe que
implementación debe ejecutar cuando el método M a i n () llama al método
PrintValue().
Como resultado, el compilador de C# remarca esta ambigüedad con un aviso:
warning CS0I14: 'DerivedClass.PrintValue()' oculta el miembro
heredado 'BaseClass.PrintValue() '. Para hacer que el método
actual reemplace esa rmplementacion, agregue la palabra clave
override. De lo contrario, agregue la palabra clave new.
Este es un buen aviso, porque la filosofía del lenguaje C# fomenta la claridad
y el compilador de C# siempre avisa sobre los constructores de código que no
están claros.
using System;
public BaseClass()
{
Value = 123;
}
public void PnntValue ()
{
Console.WriteLine("Value: " + Value);
}
}
public class DerivedClass : BaseClass
{
new public void PrintValue ()
{
Console.WriteLine ("Valué = " + Value);
}
}
class MainClass
{
public static void Main()
{
DerivedClass DerivedClassObject = new DerivedClass() ;
DerivedClassObject.PrintValue();
}
}
El codigo del listado 18.3 usa la palabra clave new en la implementación del
método PrintValue() de la clase DerivedClass. Esto indica al compilador
de C# que debe tratar este método como distinto del método de la clase base,
aunque los dos métodos tengan el mismo nombre. El uso de la palabra clave
resuelve la ambigüedad y permite que el compilador de C# compile el codigo sin
emitir advertencias. En este caso, el método Main () llama al método de la clase
derivada y el listado 18.3 escribe lo siguiente en la consola:
Value = 123
BaseClassObject.PrintValue();
using System;
public BaseClass()
{
Value = 123;
}
public virtual void PrintValue()
{
Console.WriteLine("Value: " + Value);
}
}
public class DerivedClass : BaseClass
{
override public void PrintValue()
{
Console.WriteLine ("Value = " + Value);
}
}
class MainClass
{
public static void Main()
{
DerivedClass DerivedClassObject = new DerivedClass();
DerivedC1assObject.PrintValue();
}
}
En el listado 18.4. la palabra clave o v e r r i d e indica al compilador de C#
que la implementación de P r i n t V a l u e ( ) en la clase derivada reemplaza a la
implementación del mismo método en la clase base. La implementación de la clase
base está oculta básicamente a los invocadores. A diferencia del listado 18.3. el
código del listado 18.4 sólo contiene una implementación de P r i n t V a l u e ().
La implementación de la clase base de P r i n t V a l u e () no es accesible al
código del método M a i n (). incluso si el código convierte explícitamente el obje-
to de la clase derivada en un objeto de clase base y llama al método en el objeto de
la clase base.
Debido a que la palabra clave o v e r r i d e se usa en el listado 18.4. todas las
llamadas al método realizadas mediante un objeto convertido explícitamente son
dirigidas a la implementación reemplazada de la clase derivada.
Observe el código usado en la llamada a la implementación de la clase base de
P r i n t V a l u e () cuando se usa el operador n e w para resolver la ambigüedad:
BaseClass BaseClassObject = (BaseClass)DerivedClassObject;
BaseClassObject.PrintValue();
406
19 Cómo
trabajar
con código
no seguro
Al usar la palabra clave new para crear una nueva instancia de un tipo de
referencia, está pidiendo al CLR que reserve suficiente memoria para la variabl e.
El CLR asigna suficiente espacio en la memoria para la variable y asocia la
memoria a la variable. En condiciones normales, el codigo desconocería la locali -
zación actual de esa memoria, por lo que respecta a la dirección de memoria.
Después de que la operación new tenga éxito, el codigo puede usar la memoria
asignada sin saber ni importarle en que parte del sistema está realmente situada
la memoria.
En C y C ++. los programadores tienen acceso directo a la memoria. Cuando
un fragmento de código de C o de C++ solicita acceso a un bloque de memoria, se
le otorga la dirección específica de la dirección asignada y el código lee y escribe
directamente en esa posición de memoria. La ventaja de este enfoque es que el
acceso directo a la memoria es extremadamen te rápido y contribuye a la eficien-
cia del codigo. Sin embargo, hay algunos inconvenientes que superan a las venta -
jas. El problema de este acceso directo a la memoria es que es fácil usarlo
incorrectamente v esto hace que el código falle. Un mal funcio namiento del códi-
go de C o C++ puede fácilmente escribir en la memoria de otra variable. Estos
problemas de acceso a memoria producen numerosos fallos y errores de software
difíciles de localizar. La arquitectura del CLR elimina todos estos problemas rea -
lizando toda la gestión de memoria por nosotros. Esto significa que su codigo de
C# puede trabajar con variables sin que necesitemos conocer los detalles de
cómo y dónde se almacenan las variables en memoria. Como el CLR protege al
código de C# de estos detalles de la memoria, el código de C# está libre de
errores relacionados con el acceso directo a la memoria.
No obstante, alguna vez tendrá que trabajar con una dirección de memoria
especifica del código C#. El código puede necesitar un poco más de rend imiento o
quizas el código de C# quiera trabajar con código heredado que necesite que le
proporcione la dirección de un fragmento de memoria específico. El lenguaje C#
admite un modo especial, llamado modo no seg uro . que permite trabajar directa-
mente con memoria desde el interior del código C#.
Este constructor de C# especial recibe el nombre de modo no seguro porque el
código ya no dispone de la protección que ofrece la gestión de memoria del CLR.
En el modo no seguro, el código de C# puede acceder direc tamente a la memoria
y puede tener los mismos fallos relacionados con la memoria que los códigos de C
y C++ si no es extremadamente cuidadoso con su forma de gestionar la memoria.
En este capítulo, estudiaremos el modo no seguro del lenguaje C# y como
puede ser usado para permitirle acceder directamente a las direcciones de memo -
ria usando constructores de estilo puntero de C y C++.
Console.WriteLine(*MylntegerPomter);
Tipos de puntero
Los punteros pueden tener uno de los siguientes tipos:
• sbyte
• byte
• short
• ushort
• int
• uint
• 1on g
• ulong
• char
• float
• double
• decimal
• boo1
• un tipo de enumeración
• void usado para especificar un puntero para un tipo desconocido
No se puede declarar un puntero para un tipo de referencia, como un objeto.
La memoria para los objetos está gestionada por el CLR y la memoria puede ser
borrada cada vez que el recolector de elementos no utilizados necesite liberar la
memoria del objeto. Si el compilador de C# le permite mantener un puntero sobre
un objeto, su código corre el riego de apuntar a un objeto cuya memoria puede ser
liberada en otro punto por el recolector de elementos no utilizados del CLR.
Imagine que el compilador de C # permitiese escribir código como el siguiente:
MyClass MyObject = new MyClass();
MyClass * MyObjectPointer;
MyObjectPointer = &MyObject;
Los punteros sólo son válidos en C# en código no seguro y hay que definir
explícitamente el código no seguro al compilador. Para hacerlo se emplea la pala -
bra clave de C# u n s a f e . La palabra clave u n s a f e debe aplicarse a un bloque
de codigo que use punteros.
Para especificar que un bloque de codigo se ejecute en el modo no seguro de
C# se aplica la palabra clave u n s a f e a la declaración del cuerpo de codigo.
como se muestra en el listado 1 9 . 1 .
using System;
using System;
using System;
Expresión Resultado
sizeof(sbyte)
1
sizeof(byte) 1
sizeof(short) 2
sizeof(ushort) 2
sizeof(int) 4
sizeof(uint) 4
sizeof(long) 8
sizeof(ulong) 8
sizeof(char) 2
sizeof(float) 4
sizeof(double) 8
sizeof(bool) 1
Cómo asignar espacio de la pila
para la memoria
C# proporciona un sencillo mecanismo de asignación de memoria en código no
seguro. Puede solicitar memoria en modo no seguro usando la palabra clave de
C# stackalloc. como se muestra en el listado 19.4.
Listado 19.4. Cómo asignar espacio de la pila para la memoria
using System;
Resumen
El modo no seguro de C# permite a su codigo trabajar directamente con la
memoria. Su uso puede mejorar el rendimiento porque el código accede directa -
mente a la memoria, sin tener que moverse con cuidado por el CLR. Sin embargo,
el modo no seguro puede ser peligroso y puede hacer que el codigo falle si no
trabaja adecuadamente con la memoria.
En general, evite el uso del modo no seguro de C#. Si necesita un poco más de
rendimiento para su código o si está trabajando con código heredado de C o C++
que necesita que especifique una posición de memoria, siga con el modo seguro
que se ofrece por defecto y deje que el CLR gestione los detalles de la asignación
de memoria.
La pequeña reducción en el rendimiento que se produce es compensada con
creces por no tener que realizar la pesada tarea de gestionar la memoria de su
código y por conseguir la posibilidad de escribir código libre de errores relaciona -
dos con la gestión de memoria.
20
Constructores
avanzados
de C#
En este capítulo examinaremos algunas facetas interesantes del lenguaje C#.
También veremos algunos ejemplos de código y aprenderemos por qué el codigo
funciona como lo hace. La comprensión de problemas de programación como los
presentados en este capítulo le ayudarán a enfrentarse a sus propias dudas sobre
programación en C#.
En primer lugar, observe la función de conversión implícita de C# y como se
aplica a objetos de clases derivadas a las que se accede como objetos de la clase
base de la clase derivada. Recuerde que puede escribir métodos de operadores
implícitos que definan cómo se gestionan las conversiones implícitas de un tipo u
otro; pero, como verá, las cosas se vuelven un poco más complicadas cuando se
trabaja con tipos de tiempo de ejecución y de tiempo de compilación.
A continuación, nos adentraremos en la inicialización de estructuras. Las es -
tructuras. al igual que las clases, pueden contener campos y propiedades. Sin
embargo, la inicialización de estructuras con campos se realiza de forma ligera-
mente diferente a la inicialización de estructuras con propiedades. En este capítu -
lo, descubrirá por que y cómo resolver este problema.
En la tercera parte de este capítulo, investigaremos el paso de un objeto de una
clase derivada a una llamada de método en el que se espera un objeto de una clase
base. Dado que los objetos de las clases derivadas son inherentemente objetos de
la clase base, pasar un objeto de clase derivada a un elemento que espera una
clase base puede parecer bastante sencillo. En este apartado estudiaremos por qué
esta técnica no es tan simple como podría parecer.
Finalmente, nos adentraremos en el uso avanzado de los indizadores de clase.
En la inmensa mayoría de los casos, los indizadores que esc riba servirán para
hacer que una clase se comporte como una matriz de elementos. Por lo general,
las matrices aceptan valores enteros como para especificar el elemento del índice.
En este apartado estudiaremos la técnica de usar tipos de datos distintos de ente-
ros para los índices de matriz.
public TestClass()
{
MyMainClassObject = new MainClass();
public static implicit operator MainClass(TestClass Source)
{
return Source.MyMainClassObject;
}
}
public class MainClass
{
public static void Main()
{
object MyObject;
MainClass MyMainClassObject;
MyObject = new TestClass();
MyMainClassObject = (MainClass)MyObject ;
}
}
El código del métodoMain() crea un nuevo objeto TesteClass y convierte
explícitamente el objeto en otro objeto de tipo MainClass. Como en
TestClass
se define un operador implícito, esta conversión debe tener éxito. Sin embargo, si
ejecuta el listado 20.1, recibirá el siguiente mensaje en la consola:
Excepción no controlada: System.InvalidCastException: La
conversión especificada no es valida,
at MainClass.Main( )
¿Por qué el CLR considera que la conversión es invali da, incluso cuando se
define un operador implicito? El problema aquí es que la conversión funciona
entre tipos de tiempo de compilación, no entre tipos de tiempo de ejecución.
El método Main() crea un nuevo objeto de tipo TestClass y asigna el
nuevo objeto a la variable de tipo object. A continuación, esta variable se
conv ierte en un tipo de clase MainClass. Como el objeto fue creado como un
objeto de tipo TestClass. puede esperar que la conversión explícita convierta
un objeto de tipo TestClass en un objeto de tipo MainClass.
Sin embargo. C# no realiza conversiones explícitas basadas en el tipo usado en
el momento de crear el objeto. En su lugar, realiza conversiones basadas en el tipo
de variable que contiene el nuevo objeto. En el listado 20.1. el nuevo objeto se
asigna a una variable de tipo object. Por tanto, el compilador de C# genera
código que convierte un objeto de tipo object a tipo MainClass. Como no se
ha definido una conversión de object a MainClass. no se produce la conver-
sión explícita.
Inicialización de estructuras
Como sabe, las estructuras pueden contener constructores de lenguaje que
también se encuentran en las clases, incluyendo métodos, campos y propiedades
Para asignar valores a los métodos y campos de una estructura se usa una simple
in s tr ucc ió n d e a si g n ac ió n. Si n e mb ar go , e s i mp o rta nt e reco rd ar q u e la d i fer e nc ia
en tr e u n a p ro p ied ad y u n ca mp o es q u e si s e a si g na u n va lo r a u n a p ro p i ed ad , se
ej ecu ta el có d i go co mp i lad o e n l a e s tr uc t ur a. E sto s i g ni fic a q ue s e d eb e t e ner
esp e ci al c uid a d o c ua nd o s e a s i g na n valo r e s a p r o p ied ad e s d e e str u ct ur as rec ié n
cr ead a s. Es te cap it u lo e s tud ia e ste te ma.
Listado 20.2. Cómo acceder a las estructuras con propiedades y métodos públicos
public int X
{
get
{
return PrivateX;
}
set
{
PrivateX = value;
}
}
public int Y
{
get
{
return PrivateY;
}
set
{
PrivateY = value;
}
}
}
MembersStruct.X = 100;
MembersStruct.Y = 200;
PropertiesStruct.X = 100;
PropertiesStruct.Y = 200;
}
}
El l i stad o 2 0 .2 no se co mp ila . E l co mp ilad o r d e C# e mi te el si g u ie n te er ro r:
error CS0165: Uso de la variable local no asignada
'PropertiesStruct'
Clases derivadas
Cuando tratamos las clases de C# vimos como las clases pueden derivarse de
otras clases. Las clases derivadas heredan la funcionalidad de su clase primaria o
base. La relación entre una clase derivada y una clase base recibe el nombre de
una relación "es un" en términos orientados a objetos. Por ejemplo, todas las clase
de C# derivan en última instancia del tipo System.Object de NET. de modo
que se puede decir que la clase "es un" System.Object.
Como todas las clases derivadas heredan funcionalidad de su clase base, pode -
mos presuponer que los objetos de una clase derivada pueden ser usados en cual -
quier lugar donde se pueda usar un objeto de la clase base de la clase. Sin embargo,
la seguridad de tipo integrada en C# tiene preferencia y el código de este apartado
explica este tema.
Test.Object.Test(ref TestString);
}
}
using System;
public Chessboard()
(
Board = new int[8,8];
}
public int this [char Column, int Rowlndex]
{
get
{
int ColumnIndex;
switch(Column)
{
case 'A':
case 'a':
ColumnIndex = 0;
break;
case 'B':
case 'b':
Columnlndex = 1;
break;
case ' C' :
case ' c' :
Columnlndex = 2;
break;
case 'D':
case 'd':
Columnlndex = 3;
break;
case 'E':
case 'e':
ColumnIndex = 4;
break;
case 'F':
case 'f':
Columnlndex = 5;
break;
case 'G':
case 'g ' :
ColumnIndex = 6;
b r e a k ;
case ' H' :
case 'h':
Columnlndex = 7;
break;
default:
throw new Exception("Illega 1 column specifier."¡ ;
}
Console.WriteLme (" (returning cell [ { 0 } , { 1} ] " ,
Columnlndex, Rowlndex) ;
return Board[Columnlndex, Rowlndex];
}
}
}
public class MamClass
{
public static void Main( )
{
int CellContents;
Chessboard MyChessboard = new Chessboard();
Resumen
El mejor modo de resolver un difícil problema de programación de C# es
intentar diferentes métodos de codificación con el compilador. No tenga miedo de
experimentar. El compilador de C# le avisará si hace algo equivocado.
También podria considerar el uso la herramienta ILDASM para colars e en sus
ensamblados y ver cómo el compilador de C# crea codigo ejecutable a partir de su
código fuente. La comprensión del código que genera el compilador de C# puede
ayudarle a entender por que el código funciona de la manera que lo hace. En este
capitulo, aprendió que el código descriptor de acceso de propiedades se convierte
en métodos especiales por el compilador de C#. Esto puede ser difícil de descubrir
sin observar el codigo generado por ILDASM. La herramienta ILDASM funciona
con cualquier ensamblado generado por un compilador de NET que genere MSIL
Examine con ILDASM algunos ensamblados y aplicaciones de NET. Abralos
con la herramienta y vea cómo están construidos. El análisis de otros ensambla -
dos v su contenido puede darle una mejor comprensión d e cómo funciona el códi-
go NET y puede ayudarle a ser un mejor programador NET.
Parte IV
Cómo
desarrollar
soluciones .NET
usando C#
21 Cómo
construir
aplicaciones
WindowsForms
La mayor parte del material escrito sobre NET Framework se centra en la
ayuda que reciben los programadores que escriben aplicaciones para Internet. El
motor ASP.NET y los modelos de desarrollo del "software como un servicio" son.
sin lugar a dudas, unas potentes herramientas para el desarrollo de aplicaciones
de Internet. Sin embargo. NET Framework no trata sólo con Internet.
Microsoft se dio cuenta de que. aunque muchos programadores están escri -
biendo aplicaciones para Internet, otros muchos se dedican a desarrollar aplica -
ciones de escritorio al estilo Win32. NET Framework no ha olvidado a estos
programadores. Incluye un conjunto de clases de NET que facilita el desarrollo
de aplicaciones Windows de escritorio que usan un lenguaje compatible con NET
Este conjunto de clases y el modelo de programación que admite se llama
WindowsForms.
En este capítulo estudiaremos la estructura básica de una aplicación
WindowsForms. Verá cómo crear una forma básica y cómo añadir controles a los
formularios.
Examinaremos las clases de NET Framework que puede usar una aplicación
WindowsForms y también estudiaremos algunos de los atributos de ensamblado
que se pueden usar para agregar la información de versión y derecho de autoría a
sus aplicaciones.
Arquitectura de WindowsForms
Para programar una aplicación WindowsForms es necesario comprender cóm o
se usan las clases base de WindowsForms con las aplicaciones NET Este capítu -
lo examina la arquitectura de la biblioteca de clases de WindowsForms.
Todas las clases que se usan para construir aplicaciones de WindowsForms se
incluyen en un espacio de nombre de NET Framework llamado System.
Windows.Forms. Este espacio de nombre contiene todas las clases necesarias
para construir elaboradas aplicaciones de escritorio para Windows. Estas clases
permiten trabajar con formularios, botones, controles de edición , casillas de veri-
ficación. listas y muchos otros elementos de interfaz de usuario. Todas estas
clases están a su disposición listas para ser usadas en sus aplicaciones
WindowsForms.
Las aplicaciones WindowsForms usan dos clases fundamentales de NET
Framework: La clase Form. que gestiona los formularios de la aplicación v los
controles del formulario, y la clase Application, que gestiona el modo en que
la aplicación controla los mensajes Windows enviados y recibidos de los formula -
rios de la aplicación. Estas dos clases se inclinen en el espacio de nombre
System. Windows.Forms de NET Framework y componen la estructura basica
de una aplicación WindowsForms.
La clase Form
El espacio de nombre System.Windows.Forms incline una clase base
llamada Form. La clase Form representa una forma o ventana de su aplicación
Al crear una aplicación de WindowsForms en C#„ se diseña una clase de ventana
y se usa la clase Form como clase base para la clase de ventana. Esta clase de
ventana hereda todo el comportamiento básico de una ventana y agrega la
funcionalidad necesaria para la aplicación. Todos los comportamientos básicos
de ventana están integrados en la clase base Form y esos comportamientos se
heredan automáticamente si se deriva una clase de ventana de la clase Form.
La clase Application
Las clases Form definen el aspecto y comportamiento de las ventanas de una
aplicación paro no se ejecutan por sí mismas. WindowsForms debe ejecutarse
dentro del contexto de una aplicación. El espacio de nombres System.
Windows.Forres incluye una clase llamada Application, que contiene
métodos que ayudan a gestionar las aplicaciones WindowsForms.
La clase Application contiene métodos que permiten iniciar, gestionar y
detener las aplicaciones WindowsForms.WindowsForms responde a eventos
iniciados por el usuario, como mover el ratón o pulsar una tecla del teclado.
De sd e lo s co mi e n zo s d e W i nd o ws , l a ar q u it ect u ra d e la s ap l ica cio n es d e e scr ito -
rio d e W i nd o ws h a s id o d is e ñad a so b r e e l co nc e p to d e bucle de mensajes.
En u na ap l ica ció n W i nd o w s e s tá nd ar , la p a rte ma s i mp o rt a nte d el co d igo s e
in cl u ye e n u n b u cl e y r e cib e me n s aj es d e l s i ste ma o p era ti vo W i nd o ws . C ua nd o e l
u s uar io mu e v e el r ató n, p u ls a u na t ec la o r e al i za al g u n a o tr a o p era ció n so b re la
q ue p ued e ac t uar u na ve nt a na, e l si st e ma o p er a t i vo to ma no ta d e la a cc ió n y e n ví a
u n me n s aj e a l a ve n ta na co r r e sp o nd ie n te i n fo r m ánd o le d e la acc ió n. E s el co d i go
d e la y e nt a na el q u e d e b e uti li zar co n ve n ie nt e me n te el me n saj e.
En la arq u it ect ur a W i n d o ws Fo r ms . lo s co ncep to s b á s ico s si g ue n si e n d o lo s
mi s mo s . Las ap lic ac io n es W i nd o v v s Fo r ms t ie n en u n fr a g me n to d e co d ig o q ue
esp e r a l a l le gad a d e me n saj e s d e i nter acc ió n d e l u s uar io d e sd e e l si s te ma o p erat i -
vo y l u e go lo s e n v ía n a la ve n ta na ap r o p iad a. E n l a arq u ite ct ur a W i nd o ws F o r ms .
la cla se Ap p li cat io n es el có d i go p r i n cip a l q u e ge st io na e ste co n tro l d e me n -
saj e s y la c la se Fo r ms es la cla s e q ue co ntr o la lo s me n saj e s q ue e n ví a la cla se
Ap p l ica tio n.
La c la se Ap p l ica tio n es tá se ll ad a. No se p ued e n d er i var cl as e s d e el la.
S us mé to d o s so n e st át ic o s, lo q ue s i g ni f ic a q ue p ued e n ser ll a mad o s si n q u e ha ya
u n o b j eto d e la c la se Ap p lic at io n d i sp o n ib le .
using System.Windows.Forms;
Como todas las aplicaciones de C#. este código genera un ejecutable llamado
Listinq21.-1.exe. Si ejecuta este archivo desde una consola, aparecerá la
ventana de la aplicación. Sin embargo, ocurre algo interesante cuando se hace
doble clic sobre el icono del ejecutable en el explorador de Windows. Si se inicia
el ejecutable desde el Explorador de Windows aparecen dos ventanas: una v enta -
na de consola, que esta vacía y la ventana WindowsForms de la aplicación.
Este comportamiento es diferente de la mayoría de las aplicaciones Windows. La
mayoría de las aplicaciones Windows no presentan una ventana de consola vacía
cu a nd o s e i n ici a l a ap l ic ació n. ¿P o r q u e el ej ec u tab l e d el li s tad o 2 1 .1 m ue s tra u na
ve n ta na d e co n so la y. lo más i mp o r ta n te, co mo p o d e mo s el i mi n arl a?
P o r d efe cto , el co mp i lad o r d e C# g e ner a ap l ica ci o ne s d e co nso la. Es ta s a p lic a -
cio ne s ne ce si ta n u n a v e nta n a d e co nso la p ara q ue lo s mé to d o s, co mo
C o n s o l e . W r i t e L i n e ( ) . d i sp o n ga n d e u na c o n so l a e n la q u e es crib ir s u s
me n s aj es. E l ti e mp o d e ej ecu ció n d e N ET co n s u lta e l ej ec ut ab l e c ua nd o es i n ic ia -
d o y s i e l ej ec u tab le es t á co mp ilad o co mo u n a a p lic ació n d e co nso la, el car gad o r
d el t ie mp o d e ej ec ució n cr ea u na n ue v a v e nt a na d e co n so la p ar a la ap li cació n . S i
se co mp il a el l is tad o 2 1 .1 med ia n te el co mp i lad o r d e C# p o r d e fec to , s e ge ner ara
u na ap l ica ció n NET p ar a co ns o la. E sto e xp li ca la ve n ta na d e co n so l a va cia q ue
ap ar ec e c u a nd o s e ej ec u ta e l ej ec u tab le co mp i la d o .
W ind o ws Fo r ms tr ab aj a co n v e nt a na s, no co n co n so la s y l as ap l ic acio ne s
W ind o v v s Fo r ms no nec e si ta n l a ve nt a na d e co n s o la. E l co mp i lad o r d e C # ad mit e
u n ar g u me n to d e li n ea d e co ma nd o l la mad o t a r g e t q ue se u sa p ara e sp ec i fi car
el t ip o d e la ap l ica ció n q ue s e q ui er e co mp i lar . P ued e u sar e s te a r g u m en to p ar a
ind ic ar l e al co mp i lad o r d e C# q ue q u ier e co mp i lar u na ap li cac ió n W i nd o ws q ue
no n ece si ta u n a co n so l a. La si g u ie nt e l í ne a d e co ma nd o :
csc /target : winexe Listmg21-l.cs
o r d en a al co mp i lad o r d e C# q ue c o mp i le e l arc h i vo L i s t i n g 2 1 - 1 . c s y
u se s us co n te n id o s p ar a cr ear u n ej ec ut ab le W i nd o ws q ue no nec e si ta u na co n -
so l a. Si u sa e st a li n ca d e co ma nd o p ar a co mp i lar el có d i go d e l li st ad o 2 1 . 1 e i ni cia
el ej ec ut ab l e d esd e el exp lo r ad o r d e W i nd o ws , ap a rec erá el fo r mu l ar io d e la
ap li cac ió n, si n q ue ap ar ezca ta mb ié n u n a ve n ta n a d e co n so la va cí a.
AssemblyTitle
El atributo AssemblyTitle permite asignar un título al ensamblado. El
atributo toma un parámetro de cadena en su constructor que especifica el título,
como muestra el siguiente ejemplo:
[assembly: AssemblyTitle ("Listing 21-2")]
El titulo del ensamblado no se escribe en su manifiesto, pero esta disponible en
el campo Descriptíon del bloque de información de versión del archivo com -
pilado.
AssemblyDescription
El atributo AssemblyDescription permite proporcionar una descrip-
ción del ensamblado. El atributo toma un parámetro de cadena en su constructor
que especifica la descripción, como muestra el siguiente ejemplo:
[assembly: AssemblyDescnption("This executable was produced by
compiling the code in Listíng 21-2.")]
AssemblyConfiguration
El atributo AssemblyConfiguration permite especificar información
de configuración de compilación del ensamblado. Por ejemplo, la información de
configuración de ensamblado puede especificar si el ensamblado se compilo con
configuración para su distribución o depuración. El atributo toma un parámetro
de cadena en su constructor que especifica la información de configuración.
[assembly: AssemblyConfiguration("retail")]
AssemblyCompany
El atributo AssemblyCompany permite especificar un nombre de compañía
para asociarlo al ensamblado. El atributo toma un parametro de cadena en su
constructor que especifica el nombre de la compañía:
[assembly: AssemblyCompany("John Wiley, Inc.")]
AssemblyProduct
El atributo AssemblyProduct permite especificar un nombre de producto
para asociarlo al ensamblado. El atributo toma un parámetro de cadena en su
constructor que especifica el nombre del producto compañía, como muestra el
siguiente ejemplo:
[assembly: AssemblyProduct ("C# Bible")]
AssemblyCopyright
El atributo AssemblyCopyright permite especificar información de dere-
chos de autoría para el ensamblado. El atributo toma un parámetro de cadena en
su constructor que especifica la información de derechos de autoría, como mues -
tra el siguiente ejemplo:
[assembly: AssemblyCopyright(" (c) 2002 John Wiley, Inc.")]
AssemblyTrademark
El atributoAssemblyTrademark permite especifi car información de mar-
ca registrada para el ensamblado. El atributo toma un parámetro de cadena en su
constructor que especifica la información de marca registrada, como muestra el
siguiente ejemplo:
[assembly: AssemblyTrademark("Windows is a trademark o f
Microsoft Corporation.")]
AssemblyCulture
El atributo AssemblyCulture permite especificar información de referen-
cia cultural para el ensamblado.
La información de referencia cultural especifica la información de lenguaje y
pais que el ensamblado usa para hacer su trabajo. El atributo toma un parámetro
de cadena en su constructor que especifica la información de referencia cultural,
como muestra el siguiente ejemplo:
[ a s s e m b l y : A s s e m b l y C u l t u r e ("us-en")]
Las cadenas de referencia cultural son definidas mediante un estándar de
Internet
llamado RFC 1766. El estándar se titula T a g s f o r t h e I d e n t i f i c a t i o n
o f L a n g u a g e s y está disponible en Internet en w w w . i e t f . o r g / r f c /
r f c l 7 6 6 . t x t . El nombre del producto del ensamblado se escribe en el mani -
fiesto del ensamblado; también está disponible en el campo L e g a l T r a d e m a r k s
del bloque de información de versión del archivo compilado.
AssemblyVersion
El atributo A s s e m b l y V e r s i o n permite asignar un número de versión al
ensamblado. Los números de versión de NET constan de cuatro partes:
• Un númerode versión principal
• Un númerode versión secundaria
• Un númerode revisión
• Un númerode compilación
Cada una de estas partes está separada por un punto. El atributo toma un
parámetro de cadena en su constructor que especifica el número de versión, como
muestra el siguiente ejemplo:
[assembly: AssemblyVersion("1.O.O.O")]
Este ejemplo asigna al ensamblado una versión principal igual a 1. una versión
secundaria igual a 0. un número de revisión igual a 0 y un número de compilación
asignado automáticamente por el compilador de C#.
Si usa el carácter asterisco para un número de revisión, el compilador de C#
asigna automáticamente un número de revisión y un número de compilación. El
número de revisión generado es igual al número de días desde el 1 de enero del
2000. como muestra el siguiente ejemplo:
[assembly: AssemblyVersión ("1.0.*") ]
Este ejemplo asigna al ensamblado una versión principal igual a 1. una versión
secundaria igual a 0. un numero de revisión igual a 0 y un número de revisión
asignado automáticamente por el compilador de C #.
El nombre del producto del ensamblado se escribe en el manifiesto del ensam -
blado: también está disponible en los campos Assembly Versión.Product
Versión y File Versión del bloque de información de versión del archivo
compilado. El listado 2 1.2 añade atributos de versión de ensamblado al código del
listado 21.1.
Listado 21.2. Sencilla aplicación de formulario Windows con información de versión
using System.Reflection;
using System.Windows.Forms;
Eventos Application
Ea clase A p p l i c a t i o n admite cuatro eventos que pueden usarse en las
aplicaciones W i n d o w s F o r m s :
• El evento A p p 1 i c a t i o n E x i t . se desencadena cuando la aplicación esta
a punto de cerrarse. A este evento se le pueden asignar los delegados de
tipo E v e n t H a n d l e r . El delegado E v e n t H a n d l e r esta definido en el
espacio de nombre S y s t e m de NET. El delegado toma dos parametros: un
objeto que hace referencia al objeto que envía el evento y un objeto
E v e n t A r g s que especifica los argumentos del evento. No devuelve nin -
gún valor.
• El evento I d l e se desencadena cuando la aplicación termina de enviar
mensajes desde el sistema operativo y está a punto de entrar en estado de
inactividad. La aplicación abandona el estado de inactividad cuando consi -
dera que hay más mensajes que procesar. A este evento se le pu eden asig-
nar delegados de tipo E v e n t H a n d l e r . El delegado E v e n t H a n d l e r
está definido en el espacio de nombre NET S y s t e m . El delegado toma
dos parámetros: un objeto que hace referencia al objeto que envía el evento
y un objeto E v e n t A r g s que especifica los argumentos del evento. No
devuelve ningún valor.
• El evento T h r e a d E x c e p t i o n se desencadena cuando se inicia una ex-
cepción de subproceso no capturada. A este evento se le pueden asignar
delegados de tipo ThreadExecptíonEventHandler. El delegado
T h r e a d E x e c p t i o n E v e n t H a n d l e r se define en el espacio de nom-
bre .NET S y s t e m . T h r e a d i n g . El delegado toma dos parametros: un
objeto que hace referencia al objeto que envía el evento y un objeto
T h r e a d i n g E x c e p t í o n E v e n t A r g s que especifica los argumentos del
evento. No devuelve ningún valor.
• El evento T h r e a d E x i t se desencadena cuando un subproceso está a
punto de cerrarse. Cuando el proceso principal de una aplicación esta a
punto de cerrarse, antes se desencadena este evento, seguido de un evento
A p p l i c a t i o n E x i t . A este evento se le pueden asignar delegados de
tipo E v e n t H a n d l e r . El delegado E v e n t H a n d l e r esta definido en el
espacio de nombre S y s t e m de NET. El delegado toma dos parametros:
un objeto que hace referencia al objeto que envía el evento y un objeto
EventArgs que especifica los ar gumentos del evento. No devuel ve nin -
gún valor.
}
catch(MotSupportedException)
{
}
}
public void OnIdle (object sender, EventArgs e)
{
Console.WriteLine ("The application ís idle.");
}
public void OnThreadException(object sender,
ThreadExceptionEventArgs e )
{
Console.WriteLine ("an exception thrown from an application
thread was caught!");
}
public void OnThreadExit (object sender, EventArgs e)
{
Console.WriteLine("The application's main thread ís
shutting down.");
}
}
Application.ApplicationExit += new
EventHandler (AppEvents.OnApplicationExit);
Application.Idle += new EventHandler(AppEvents.Onldle);
Application.ThreadException += new
ThreadExceptionEventHandler (AppEvents.OnThreadException);
Application.ThreadExit += new
EventHandler (AppEvents.OnThreadExit);
Application.Run(FormObject);
}
}
La n ue v a c la se d e 1 li s tad o 2 1 .3 r e cib e el no mb r e d e Ap p li c atio n -
Ev e nt Ha nd l erd a s s y c o nt ie n e mé to d o s q u e co n tro l a n lo s e v e nto s d e se nc a -
d en ad o s d e sd e e l o b j e to Ap p li ca tió n. El mé t o d o Ma i n() crea u n o b j eto d e
la cl as e Ap p l ic at io nE ve n tH a nd le r C la s s y a gre g a s u co d i go a la li s ta
d e co n tro l ad o re s d e e xe n to s d e la c la se Ap p lic at io n. Lo s co n tro la d o res d e
ex e nto d e la c la se Ap p li ca tio nE v e nt Ha n d ler Cl as s e s crib e n e n la co n -
so l a. E sto es p er fec ta m en te vá lid o , i nc l u so e n u na ap l ica ció n W i nd o ws F o r ms .
Si co mp i la e l có d i go d e l li st ad o 2 1 .3 u sa nd o e l d es ti no ej e c utab le d e co n so l a (c sc
/tar g et :e xe Li st i n g2 1 - 3 .cs) , lo s me n saj es d el c o ntro lad o r d e exe n to s
se e scr ib e n e n l a v e nt a n a d e la co n so l a q u e se u só p ara i n ic iar l a ap l ica ció n. S i
co mp il a e l có d i go d e l l i st ad o 2 1 .3 u sa nd o el d e st i no d e ej ec ut ab le d e W ind o ws
(cs c /t ar ge t: wi ne x e Lis ti n g2 l -3 .c s) . n i n g u na co nso la e s ta aso c ia -
d a al p r o ce so y no s e m ue s tr a n me n s aj es e n l a c o n so l a.
Propiedades Application
La cl a se Ap p li ca tio n es co mp at ib l e co n v ar ia s p ro p i ed ad e s q ue p ued e n
u sar se e n l as ap l ica cio n es d e C# . Lo s s i g ui e nt e s ap a rtad o s d es crib e n c ó mo u sar
cad a u na d e e s ta s p ro p ie d ad es .
AllowQuit
La propiedad booleana AllowQuit especifica si el código puede o no termi -
nar la aplicación mediante programación. La propiedad devuelve True si el códi-
go puede ordenar a la aplicación que termine y False en caso contrario. Esta
propiedad es de sólo lectura y no se le puede asignar un valor. Por lo general, los
usuarios pueden terminar las aplicaciones cerrando el formulario principal de la
aplicación. Algunas aplicaciones se incluyen en contenedores, como clientes Web.
v no pueden cerrarse mediante programación. Sólo se cierran cuando su contene -
dor se cierra. Si la propiedad AllowQuit devuelve true. se puede llamar a un
método de Application llamado Exit() para terminar la aplicación me -
diante programación. Los métodos del objeto Application se estudiaran en el
apartado titulado "Métodos Application."
CommonAppDataRegistry
La propiedad CommonAppDataRegistry devuelve una referencia a un
objeto de la clase RegistryKey. El objeto RegistryKey hace referencia a
una clave en el registro que la aplicación puede usar para almacenar datos de
registro que deberían estar disponibles para todos los usuarios de la aplicación.
Esta propiedad es de sólo lectura y no se le puede asignar un valor.
La clase RegistryKey es parte de NET Framework y está disponible en u n
espacio de nombre llamado Microsoft.Win32. Representa una clave especí-
fica del registro y contiene métodos que permiten crear subclaves, leer valores y
realizar otras tareas relacionadas con las claves del registro.
CommonAppDataPath
La propiedad de cadena CommonAppDataPath hace referencia a una ruta
en el sistema de archivos que la aplicación puede usar para almacenar datos
basados en archivos que deberían estar disponibles para todos los usuarios de la
aplicación. Esta propiedad es de sólo lectura y n o se le puede asignar un valor.
La ruta de datos de la aplicación se almacena dentro de la ruta de carpeta de
documentos de Windows para todos los usuarios, que suele encontrarse en
C:\Documents and Settings\All Users\Datos de programa.
La ruta real de la aplicación apunta a una carpeta dentro de esta ruta de documen -
tos " a l l u s e r s " que tiene la forma CompanyName\ ProductName\
ProductVersion. Los nombres de carpeta CompanyName. ProductName
v ProductVersion se basan en los valores de las propiedades de la a plicación
del mismo nombre. Si no se asignan valores a estas propiedades, la clase
Application proporciona unos valores por defecto válidos. Por ejemplo, el
codigo del listado 2 1 . 1 tiene una ruta de datos comunes de la aplicación
C:\Documents and Settings\All Users\Datos de
programa\SimpleHelloWorld\SimpleHelloWorld\V0.0. Si el código
del listado 2 1 . 1 va a asignar valores a las propiedades CompanyName.
ProductName o ProductVersion de la clase Application, los nom-
bres de carpetas en la ruta de datos comunes de la aplicación puede cambiar para
reflejar los valores de esas propiedades.
La ruta de la carpeta de la versión del producto sólo usa los números de
versión principal y secundario especificados en la aplicación, independientemente
del número de valores que asigne en el atributo de la aplicación [ A s s e m b l y -
V e r s i o n ] . Si la aplicación usa un atributo [ A s s e m b l y V e r s i o n ] con un
valor, por ejemplo, 1. 2 .3.4. la parte de número de versión de la ruta de datos
comunes de la aplicación será 1 . 2 . La letra V siempre se antepone al número de
versión principal de la aplicación en la parte de la versión de numero de la ruta de
datos.
CompanyName
La propiedad de cadena CompanyName devuelve el nombre de la compañía
asociado a la aplicación. Esta propiedad es de sólo lect ura y no se le puede
asignar un valor. Por defecto, este valor se asigna al nombre de la clase que
contiene el métodoMain() de la aplicación. Si la aplicación especifica un nom -
bre de compañía con el atributo [AssemblyCompany] . el valor de ese atribu-
to se usa como el valor de la propiedad CompanyName de la aplicación.
CurrentCulture
La propiedad CurrentCulture permite trabajar con la información de
referencia cultural de la aplicación. Esta propiedad se puede leer y escribir. La
propiedad tiene una clase de tipo Culturelnfo. que es una clase definida por
NET Framevvork y que se encuentra en el espacio de nombres System.
Globalization. La clase Culturelnfo contiene métodos y propiedades
que permiten al código trabajar con datos específicos del entorno cul tural en el
que se ejecuta la aplicación. Los objetos Culturelnfo ofrecen información
como el formato preferido para mostrar la fecha y la hora, la configuración de la
hora y el formato numeral.
CurrentlnputLanguage
La propiedad CurrentlnputLanguage permite trabajar con el idioma
actual de la aplicación. La propiedad tiene una clase de tipo InputLanguage.
que es una clase definida por NET Framework y que se encuentra en el espacio
de nombres System.Windows.Forms.
La clase InputLanguage contiene métodos y propiedades que permiten al
codigo trabajar con el conocimiento que tenga la aplicación de las teclas del
teclado y cómo se relacionan con los caracteres que pueden introducirse en la
aplicación. Las diferentes versiones específicas de cada lenguaje de Windo ws
establecen equivalencias entre las teclas del teclado y los caracteres específicos
de los diferentes lenguajes y la clase CurrentlnputLanguage especifica el
aspecto de esta asignación.
ExecutablePath
La propiedad de cadena ExecutablePath devuelve la ruta del ejecutable
de la aplicación. Esta propiedad es de sólo lectura y no se le puede asignar un
valor.
LocalUserAppDataPath
La propiedad de cadena LocalUserAppDataPath hace referencia a una
ruta en el sistema de archivos que la aplicación puede usar para almacenar datos
basados en archivos que deberían estar disponibles para el usuario conectado en
ese momento al equipo.
Esta propiedad es de solo lectura y no se le puede asignar un valor. Es usada
por los usuarios locales con perfiles de sistema operativo del equipo local. Los
usuarios con perfiles móviles usados a través de un sistema de redes tienen una
propiedad distinta, llamada UserAppDataPath. para especificar dónde deben
almacenarse los datos de la aplicación.
Al igual que la propiedad CommonAppDataPath. la ruta de datos de usua-
rio local señala a una carpeta incluida dentro de la carpeta de documentos de
usuario conectada con una estructura de carpetas que tiene la forma
CompanyName\ProductName\ProductVersion. Los nombres de
carpeta CompanyName.ProductName y ProductVersion se basan en
los valores de la aplicación del mismo nombre. Si no se asigna un valor a estas
propiedades, la clase Application proporciona unos valores válidos por de -
fecto. Si el código asigna un valor a las propiedades CompanyName.
ProductName o ProductVers ion de la clase Application, los nom-
bres de carpetas en la ruta de datos de la aplicación de usuario local cambia para
reflejar los valores de esas propiedades.
Al igual que la ruta de datos comunes de la aplicación, la ruta de carpeta de
versión de producto solo usa los números de v ersión principal y secundario espe -
cificados en la aplicación, independientemente del número de valores especifica -
dos en el atributo de la aplicación [ AssemblyVersion] . Si la aplicación usa
un atributo [As.semblyVersion] con un valor, por ejemplo. 1.2.3.4. la
parte correspondiente al numero de v ersión de la ruta de datos de usuario local de
la aplicación será 1.2. La letra V siempre se antepone al numero de versión
principal de la aplicación en la parte de la v ersión de número de la ruta de datos
MessageLoop
La propiedad booleana MessageLoop devuelve True si existe un bucle de
mensajes para la aplicación y False en caso contrario. Esta propiedad es de solo
lectura y no se le puede asignar un valor. Como todas las aplicaciones
Windows Forms necesitan un bucle de mensajes para que los mensajes de
Windows puedan ser enviados al formulario correcto, las aplicaciones
WindowsForms devuelven True para esta propiedad.
ProductName
La propiedad de cadena ProductName devuelve el nombre del producto
asociado a la aplicación. Esta propiedad es de sólo lectura y no se le puede
asignar un valor. Por defecto, a este valor se le asigna el nombre de la clase que
contiene el método Main() de la aplicación. Si la aplicación especifica un nom-
bre de producto con el atributo [AssemblyProduct]. el valor de ese atributo
se usa como el valor de la propiedad de la aplicación ProductName.
ProductVersion
La propiedad de cadena ProductVersion devuelve el número de versión
asociado a la aplicación. Esta propiedad es de sólo lectura y no se le puede
asignar un valor. Por defecto, este valor es 0 . 0 . 0 . 0 . Si la aplicación especifica
un numero de versión con el atributo [ AssemblyVersion] . el valor de ese
atributo se usa como el valor de la propiedad de la aplicación ProductVersion.
SafeTopLevelCaptionFormat
La propiedad de cadena SafeTopLeveICaptionFormat hace referencia
a la cadena de formato que el tiempo de ejecución aplica a los títulos de la v entana
de nivel superior cuando las aplicaciones se ejecutan desde un contexto no seguro.
La seguridad es una parte integral de NET Framework y el entorno común de
ejecución (CLR). El CLR respeta la configuración de las diferentes zonas de
seguridad en Internet Explorer (Internet. Intranet local. Sitios de confianza y
Sitios restringidos) y restringe los servicios de tiempo de ejecución para las apli -
caciones que se ejecutan en zonas no fiables. Las aplicaciones Windows Forms
que se ejecutan desde zonas no fiables, como la zona Intern et, tienen una etiqueta
de aviso que describe la aplicación como procedente de una localización no fia -
ble El texto de esta etiqueta de aviso esta basado en la plantilla de formato de
cadena almacenada en la propiedad SafeTopLevelCaptionFormat.
StartupPath
La propiedad de cadena StartupPath devuelve la ruta al archivo ejecuta-
ble que inició la aplicación. Esta propiedad sólo devuelve la ruta. No incluye el
nombre del archivo ejecutable. Esta propiedad es de sólo lectura y no se le puede
asignar un valor.
UserAppDataPath
La propiedad de cadena UserAppDataPath hace referencia a una ruta en
el sistema de archivos que puede usar la aplicación para almacenar datos basados
en archivos que deberían estar disponibles para el usuario de red que esta conec -
tado en ese momento al equipo local. Esta propiedad es de sólo lectura y no se le
puede asignar un valor. Es usada por los usuarios locales con perfiles de sistema
operativo en la red. Los usuarios que tengan perfiles de equipo local no utilizados
en la red usan una propiedad distinta, llamada L o c a l U s e r A p p D a t a P a t h . para
especificar dónde deben almacenarse los datos de la aplicación.
Al igual que la propiedad C o m m o n A p p D a t a P a t h . la ruta de datos de usua-
rio local señala a una carpeta incluida dentro de la carpeta d e documentos de
usuario conectado con una estructura de carpetas que tiene la forma
CompanyName\ ProductName\ ProductVersion. Los nombres de
carpeta C o m p a n y N a m e . P r o d u c t N a m e y P r o d u c t V e r s i o n se basan en
los valores de la aplicación del mismo nombre. Si no s e asigna un valor a estas
propiedades, la clase A p p l i c a t i o n proporciona unos valores válidos por de-
fecto. Si el código asigna un valor a las propiedades C o m p a n y N a m e .
P r o d u c t N a m e o P r o d u c t V e r s i o n de la clase A p p l i c a t i o n , los nom-
bres de carpetas en la ruta de datos de la aplicación de usuario local cambia para
reflejar los valores de esas propiedades.
UserAppDataRegistry
El metodo U s e r A p p D a t a R e g i s t r y devuelve una referencia a un objeto de
clase R e g i s t r y K e y . Al igual que la propiedad C o m m o n A p p D a t a R e g i s t r y .
la propiedad devuelve un objeto de tipo R e g i s t r y K e y . El objeto R e g i s t r y K e y
devuelto hace referencia a una clave en el registro que la aplicación puede usar
para almacenar datos de registro que sólo deberían estar disponibles para el usua -
rio actual de la aplicación. Esta propiedad es de sólo lectura y no se le puede
asignar un valor.
Métodos Application
La clase A p p l i c a t i o n admite ocho métodos que pueden ser llamados desde
las aplicaciones de C#. Estos métodos se describen en las siguientes secciones.
AddMessageFilter
El método A d d M e s s a g e F i l t e r () añade un filtro de mensajes a la aplica-
ción para controlar los mensajes de Windows mientras los mensajes son enviados
a sus destinos. El filtro de mensajes que se instala en la aplicación recibe los
mensajes de Windows antes de que se envíen al formulario. Un filtro de mensajes
instalado por A d d M e s s a g e F i l t e r () puede controlar un mensaje que se le
envíe y puede decidir si el mensaje debe enviarse al formulario.
El listado 21.4 muestra cómo se puede usar el filtro de mensajes en una aplica-
ción WindowsForms. El controlador de mensajes busca mensajes que anunciar
cuando se hace clic con el botón izquierdo en el formulario.
using System;
using System.Windows.Forms ;
public class BlockLeftMouseButtonMessageFi1ter : IMessageFilter
{
const int WM_LBUTTONDOWN = 0x201;
const int WM_LBUTTONUP = 0x202;
Application.AddMessageFilter(MsgFilter);
Application.Run(MyForm);
}
public MamForm()
{
Text = "Message Filter Test";
}
}
El método AddMessageFilter() recibe un argumento: una implementación
de una interfaz llamada IMessageFilter. La interfaz IMessageFilter
es definida por NET Framework y se incluye en el espacio de nombres
System.Windows.Forms.IMessageFilter declara un método:
Exit
El método E x i t () obliga a la aplicación a terminar. El método informa a la
cola de mensajes de la aplicación que debe finalizar y cierra los formularios cuando
se procesa el ultimo mensaje de la cola de mensajes de Windows.
Por lo general, el codigo no necesita inv ocar al método Exit(). Eos formula -
rios de Windows incluyen por defecto un cuadro de cierre en la e squina superior
derecha del formulario y al hacer clic en ese cuadro se envía un mensaje de cierre
a la cola de mensajes de Windows. Sin embargo, puede ser útil llamar a E x i t ( )
si el formulario incluye un control, como un botón o un elemento de menú que
debe finalizar la aplicación al ser seleccionado.
ExitThread
El método E x i t T h r e a d () sale del bucle de mensajes y cierra todos los
formularios en el subproceso en curso. El método no recibe argumentos m devuel -
ve ningún valor. Si la aplicación W i n d o w s F o r m s contiene un solo subproceso
(como es habitual), entonces la acción de llamar a E x i t T h r e a d () es igual que
a llamar a E x i t () . Sin embargo, si la aplicación usa vanos subprocesos, enton -
ces los dos métodos se comportan de forma diferente. El método E x i t T h r e a d ()
cierra un subproceso pero permite que los otros subprocesos sigan ejecutándose
Sin embargo, el método E x i t () . cierra todos los subprocesos a la vez. Como
todos los procesos de Windows, las aplicaciones W i n d o w s F o r m s siguen ejecu-
tándose hasta que finaliza el último subproceso.
OleRequired
El método O l e R e q u i r e d () inicializa OLE en el subproceso en curso de la
aplicación. Si la aplicación va a trabajar con tecnología COM. como COM. DCOM.
ActiveX o OLE. se debe llamar a este método de la aplicación antes de usar
COM
El método no recibe argumentos, pero devuelve un valor desde una enumera -
ción llamada A p a r t m e n t S t a t e que describe el tipo de apartamento que intro -
dujo el subproceso. La enumeración A p a r t m e n t S t a t e está definida en un
espacio de nombre NET Framework llamado S y s t e m . T h r e a d i n g y puede
tener uno de los siguientes valores:
• STA. se devuelve cuando el CLR decide inicializar COM para el
subproceso
entrando en un apartamento de un único subproceso.
• MTA. se devuelve cuando el CLR decide inicializar CO M para el
subproceso entrando en un apartamento de multiproceso.
OnThreadException
El método O n T h r e a d E x c e p t i o n ( ) desencadena un evento T h r e a d -
E z c e p t i o n . El evento puede ser capturado por un controlador de eventos
O n T h r e a d E x c e p t i o n () instalado en el objeto A p p l i c a t i o n .
El listado 21.5 muestra cómo puede usarse una excepción de subproceso en
una aplicación W i n d o w s F o r m s .
Listado 21.5. Cómo trabajar con excepciones de subprocesos
using System;
using System.Threading;
using System.Windows.Forms;
public class BlockLeftMouseButtonMessageFi1ter : IMessageFilter
{
const int WM_LBUTTONDOWN = 0x201;
LeftButtonDownException = e.Exception;
Consolé.WriteLine(LeftButtonDownException.Message);
}
}
public class MainForm : Form
{
public static void Main()
{
ApplicationEventHandlerClass AppEvents = new
ApplicationEventHandlerClass();
MainForm MyForm = new MainForm() ;
BlockLeftMouseButtonMessageFilter MsgFilter = new
BlockLeftMouseButtonMessageFilter();
Application.AddMessageFi1ter(MsgFilter);
Application.ThreadException += new
ThreadExceptionEventHandler(AppEvents.OnThreadException);
Application.Run(MyForm);
}
public MainFormf)
{
Text = "Application Exception Test";
}
}
El li s tad o 2 1 .5 e s p ar ec id o al l is tad o 2 1 .4 ya q ue i n cl u ye u n co n tro l ad o r d e
me n s aj es q ue b us ca me n saj e s q u e i n fo r ma n c ua n d o s e p u ls a el b o tó n i zq u ierd o d el
rató n e n el fo r mu l ario d e la ap l ica ció n
La d i fere n ci a e s q ue e n el l i sta d o 2 1 .5 se i ni ci a u na e xc ep c ió n a l rec i b ir e l
me n s aj e le ft mo u se b ut to n d o wn . E l co nt ro lad o r d e me n saj e s crea u n
n ue vo o b j eto E x cep t io n y lo i n ic ia u s and o el mé to d o O n T hread -
Ex cep t io n() d el o b j eto Ap p l ica tio n . E l co d i go d el li s tad o 2 1 .5 t a mb ié n
in cl i ne u n co nt ro lad o r d e e ve n to s d e la ap l ica ció n, q u e se i mp l e me n t a e n u n a
cla se ll a mad a Ap p li cat io nE ve n tH a nd ler C la s s. E s ta c la se co nt ro la el
ev e nto O nT hre ad E xcep t io n( ) y el mé to d o p ri nc ip a l d e l a ap l ica ció n in s tal a
el co ntr o lad o r d e e ve n to s u sa nd o la p r o p ied a d T hre ad E xcep tio n d e l o b j eto
Ap p l ica tio n.
El co ntro lad o r d e e x cep cio n e s d el s ub p ro c eso i n st al ad o e n la cla se
ApplicationEventHandlerClass ext r ae la e x cep c ió n del o b j eto
T hr ead E x cep t ío nE ve n t Ar g s d e l co ntr o lad o r y e scr ib e e l me n saj e d e la
ex cep c ió n e n la co n so la. C ua nd o se ej ec u ta el co d igo e n el l is tad o 2 1 .5 . ap arece e l
fo r mu l ar io p ri n cip al d e la ap li cac ió n. C ua nd o a p arezc a e l fo r mu l ario , mu e v a e l
rató n hac ia s u i nt erio r y ha g a cl ic co n e l b o tó n iz q ui erd o d el r ató n. E l co n tro lad o r
de mensajes iniciara una excepción y el controlador de excepciones de la aplica -
ción escribirá mensajes de excepción en la consola de la aplicación.
RemoveMessageFilter
El método R e m o v e M e s s a g e F i l t e r () elimina un filtro de mensajes insta-
lado por el método A d d M e s s a g e F i l t e r (). Elimina el filtro de mensajes del
generador de mensajes de la aplicación. El método R e m o v e M e s s a g e F i l t e r ()
recibe un argumento: una implementación de una interfaz llamada I M e s s a g e -
F i l t e r . Este argumento debe hacer referencia a una clase que implemcnta
I M e s s a g e F i l t e r y que ya ha sido usada en una llamada a A d d M e s s a g e -
F i l t e r () El listado 2 1.6 muestra cómo funciona este método.
Application.AddMessageFilter(MsgFilter);
Application.Run(MyForm);
)
public MainForm()
{
Text = "Message Filter Removal Test" ;
}
}
El codigo del listado 21.6 ínstala un filtro de mensajes que busca el mensaje
left mouse button down. igual que hacía el listado 2 1.4. La diferencia es
que la implementación del filtro de mensajes en del listado 21.6 elimina el filtro de
mensajes cuando se recibe el mensaje.
Observe que. cuando se ejecuta el código del listado 21.6. solo se escribe un
mensaje en la consola, independientemente del numero de veces que pulse el boton
izquierdo del ratón con el puntero sobre el formulario. Esto es debido a que el
filtro de mensajes es eliminado de la aplicación cuando se recibe el primer mensa -
je y. como se elimina el filtro de mensajes, no se pueden detectar nuevos mensajes.
Todavía se envían mensajes al formulario, pero el codigo del listado 21.6 elimina
el objeto que detecta por primera vez los mensajes después de que se haya elimi -
nado el objeto de la lista de filtros de eventos de la aplicación.
TRUCO: El listado 21.6 usa la palabra clave this como parámetro para
la llamada al método RemoveMessageFilter () del objeto
Application. Recuerde que la palabra clave this se emplea para ha-
cer referencia al objeto cuyo código se está ejecutando. Puede pensar en la
instrucción del listado 21.6 que llama a RemoveMessageFilter()
como si indicara "elimina la referencia a este filtro de mensajes del objeto
Application".
Run
El método Run( ) inicia el bucle de mensajes de Windows para una aplica -
ción. Todos los listados de este capítulo han usado el método Run( ) . que acepta
como parámetro una referencia a un objeto de formulario. Ya debería estar fami -
liarizado con el funcionamiento del método Run( ) .
using System;
using System.Drawing;
using System.Windows.Forms;
Application.Run(MyForm);
}
public MainForm ()
{
Button MyButton = new Button();
El siguiente concepto importante del listado 21.7 está relacionado con el con -
trol de exentos de un control. Las clases de control admiten muchos exentos que
se desencadenan cuando el usuario interactúa con el control. Muchos de estos
ex entos se incluyen en la clase base Control. aunque la clase de control especí-
fica controla otros ex entos. El ex ento mas ex idente de un botón de control sería un
exento Click. Los botones de los formularios no son de utilidad a menos que
puedan responder a una acción de un usuario que haga clic con el boton.
Los controles de NET Framework usan el modelo estándar delegado/exento
para compatibilizar sus exentos. Los ex entos de control se instalan usando instan -
cias de un delegado llamado EventHandler. Este delegado admite dos argu-
mentos. un objeto que especifica el elemento que enx io el ex ento y un objeto de
una clase llamada EventArqs que encapsula los argumentos del exento. El
código del formulario del listado 21.7 incluye un método llamado MyButton -
C1icked que modela el delegado EventHandler. Este método se usa como
un nuevo controlador de exentos y está conectado al exento C1ick del boton:
MyButton.Click += new EventHandler (MyButtonClicked);
L a p r o p i e d a d Control e s t a d e f i n i d a e n l a c l a s e ba s e Controls , ( r e c u e r d e
q u e l a c l a s e Form s e d e r i v a d e l a c l a s e Control ) . E s u n o bj e t o d e u n a c l a s e
l l a m a d a ControlsCollection y g e s t i on a u n a l i s ta d e c on t r ol e s s e c u n d a r i o s
q u e s on g e s t i on a d o s p o r e l c on t r o l a c t u a l . E l c od i g o WindowsForms d e be a ñ a -
d i r c on t r ol e s a s u c o l e c c i ón Controls d e l o s f or m u l a r i o s c on te n e d or e s a n te s d e
que pueda ser usada realmente.
# =============================
# String Table
# =============================
Message = Helio from the string table!
using System;
using System. Drawing;
using System.Windows.Forms;
using System.Resources;
using System.Reflection;
Application.Run(MyForm);
}
public MainForm()
{
Button MyButton = new Button();
Resumen
Este capitulo estudia los fundamentos del proceso de desarrollo para la elabo -
ración de aplicaciones WindowsForms en C#. También se estudian algunas clases
elementales, como la clase Application, que gestiona la aplicación
WindowsForms como un todo y la clase Form. que gestiona un formulario de la
aplicación. También se hace un repaso a la arquitectura de las clases de control
WindowsForms y se examinan los atributos de ensamblado que pueden agregar
información de versión y descriptiva al ensamblado.
NET Framework contiene un variado conjunto de clases para elaborar aplica -
ciones WindowsForms. El subsistema WindowsForms esta compuesto por varias
clases; por desgracia, las limitaciones de espacio no permiten una completa des -
cripción de todas ellas en este libro. Puede examinar la documentación de cada
clase WindowsForms. Use los conceptos de este capítulo para comenzar a
investigar todas las clases del espacio de nombres WindowsForms.
22 Cómo crear
aplicaciones
Web
con WebForms
La última década ha sido testigo del crecimiento sin precedentes de Internet
como plataforma de negocios. Hoy en día. la mayoría de los modelos de negocios
están basados en. o al menos incluyen, el concepto de Internet. Por tanto, el
enfoque ha cambiado de las aplicaciones de escritorio a las aplicaciones Web.
Este cambio ha subrayado la necesidad de tecnologías que puedan simplificar el
desarrollo de aplicaciones Web.
Para crear aplicaciones Web, NET Framework incluye ASP.NET. que es la
nueva versión de ASP 3.0. Se pueden crear aplicaciones en ASP.NET usando
Visual Basic NET o Visual C# como lenguaje de programación del senador.
Visual C# permite a los programadores desarrolla r potentes aplicaciones Web.
Pero lo más importante es que ayuda a los programadores a luchar contra los
ciclos cada vez más rápidos, porque les permite realizar más operaciones con
menos líneas de código y con menos errores, lo que reduce considerablement e el
coste del proyecto.
Aunque para crear aplicaciones Web ASP.NET sólo se necesita un editor de
texto, como el bloc de notas, lo más habitual es usar una plataforma de desarro -
llo. como Visual Studio NET. que proporciona un enorme conjunto de herra -
mientas para diseñar páginas Web. En comparación con los primeros lenguajes de
programación de páginas Web, en los que había que realizar una gran cantidad de
codificación. Visual Studio NET proporciona de una interfaz WYSIWYG. Esta
i n te r f a z p e r m i t e a r r a s t r a r y c ol o c a r c on t r ol e s e n W e bF o r m s . q u e l u e g o p u e d e n s e r
p r og r a m a d a s e n V i s u a l C # . A l p r og r a m a r e n V i s u a l S tu d i o . N E T s e p u e d e s e p a -
r a r e l c on t e n i d o e n c o d i g o y e n H T M L d e u n W e bF or m . E s t o h a c e q u e r e s u l te m u y
s e n c i l l o s e p a r a r l a p r o g r a m a c i ón l óg i c a d e l a p r e s e n ta c i ón l ó g i c a , l o q u e n os
p e r m i te c on c e n t r a r n os e n i m p l e m e n t a r l a f u n c i o n a l i d a d d e l p r oy e c t o , m a s q u e e n
l a p r e s e n t a c i ón d e d a t o s
E n e s t e c a p i t u l o a p r e n d e r e m os a c r e a r u n a a p l i c a c i ó n W e b A S P . N E T m e d i a n -
te V i s u a l C # . M i e n t r a s c r e a m os l a a p l i c a c i ón , d i s e ñ a r e m os u n W e bF o r m q u e u s a
c on tr ol e s d e s e r v i d or , c om o e t i q u e t a s , c u a d r o s d e te x t o , c u a d r os d e l i s ta s ,
h i p e r v i n c u l os y b o t on e s . P or u l t i m o, a p r e n d e r e m os a c o n tr o l a r l os e v e n t os g e n e -
r a d os p or l o s c o n t r ol a d o r e s d e s e r v i d o r .
Presentación de WebForms
E o s W e b F o r m s s on l a ba s e d e u n a a p l i c a c i ón b a s a d a e n W e b. L a a p l i c a c i ó n
W e b l os u s a p a r a i n t e r a c t u a r c on e l u s u a r i o . U n W e bF or m p u e d e i n c l u i r v a r i os
c on tr ol e s d e s e r v i d or . c o m o c u a d r os d e t e x t o, e ti q u e ta s , c u a d r os d e l i s ta s , b o t on e s
d e o p c io n. ca si ll a s d e ve r i fic ac ió n y b o to n es . lo s c ua le s fac il ita n la i nt er acció n d e l
u s uar io co n la ap l ica ció n.
U n W eb Fo r m co n s ta d e d o s co mp o ne n te s: la in te r fa z d e u su a rio (I U) y l a
ló g ic a d e l a ap lic ació n ( ap li cac ió n) . La i n ter faz d e u s uar io e s el co mp o ne n te
vi s u al d e u n W eb Fo r m. Se co mp o ne d e HT M L y co ntro le s e sp e cí fico s d e la ap l i -
cació n W eb . La i nt er faz d e u s u ar io e s e l co nt e n ed o r d el te xto y lo s co n tro le s q ue
d eb e n ap a rec er e n la p a gi n a W eb . S e e sp e ci f ica en u n arc h i vo co n la e xt en s ió n
.aspx.
La ló g ic a d e p ro gr a ma ció n d e u na ap l ica ció n W eb d e ASP N ET es t a e n u n
ar c hi vo sep ar ad o q u e co nt ie n e el có d i go e n car g ad o d e co n tro la r la s i nt eracc io ne s
d el u s uar io co n el fo r m ul ar io . E st e ar c hi vo r ec i b e el no mb re d e ar c hi vo d e " co d i -
go o cu lto " . C ua nd o se e j ecu ta u n fo r mu l ar io e s crito e n C '# . el arc h i vo d e co d i go
o cu lto ge ner a d i n á mi ca me n te el r e s ul tad o HT M L d e la p a g i na. E l a rch i vo d e
co d i go o c u lto d e C# tie n e u n a e x te n sió n . a sp x.c s
La ve n taj a d e sep ar ar el co d i go d el co nte n id o e s q u e e l p ro gra ma d o r no ne ce si -
ta co n ce nt rar se e n l a l ó gi ca q u e se u sa p ar a mo st rar e l r es u lt ad o E l d is e ñad o r
W eb p u ed e co ntro lar es t a tar ea.
Control Label
El control Label se usa para mostrar texto estático en un WebForm. Los
usuarios no pueden editar el texto de un contr ol Label. Al añadir un control
Label. el texto Label aparece como su título. Sin embargo, asignando un valor a
la propiedad Text del control, es posible modificar el título del control. Se puede
asignar un valor a las propiedades del control Label en tiempo de ejecución en
el archivo de código oculto ( cs file). Por ejemplo, si se quiere cambiar el texto de
una etiqueta cuando un usuario pulsa un botón. Para ello, puede utilizar el si -
guiente código:
Label1.Text="Welcome"
Control TextBox
El control T e x t B o x se usa para obtener información, como texto, números y
fechas, de los usuarios de un WebForm. Por defecto, un control TextBox es un
control de una línea que permite a los usuarios escribir caracteres en una sola
línea. Sin embargo, también se puede establecer el control T e x t B o x como un
control multilínea. Un cuadro de texto multilínea muestra varias líneas y permite
el ajuste de texto. Un control T e x t B o x también puede usarse para aceptar con-
traseñas. Los controles T e x t B o x utilizados para aceptar contraseñas ocultan
los caracteres escritos por los usuarios, mostrándolos como asteriscos (*).
Puede establecer la apariencia de un control T e x t B o x mediante sus propie-
dades. como B a c k C o l o r o F o r e C o l o r . También puede cambiar la propiedad
T e x t M o d e de un control T e x t B o x para determinar si un control T e x t B o x
actúa como un cuadro de texto para aceptar una contraseña, una sola linea de
texto o varias líneas de texto.
Control DropDownList
El control D r o p D o w n L i s t permite a los usuarios seleccionar un elemento
de un conjunto de elementos predefinidos (siendo cada elemento un objeto dife -
rente con sus propias propiedades).
Se pueden agregar elementos a un control D r o p D o w n L i s t mediante su pro-
piedad I t e m s . A diferencia del control L i s t B o x . solo se puede seleccionar un
elemento cada vez y la lista de elementos permanece oculta hasta que el usuario
hace clic en el boton desplegable.
Control HyperLink
El control H y p e r L i nk permite a los usuarios moverse de un WebForm a otro
dentro de una aplicación. También permite a los usuarios desplazarse hasta una
URL que puede estar asociada con el control
Con el control H y p e r L i n k . el texto o una imagen pueden funcionar como un
hipen incido. Cuando un usuario hace clic en el control, se abre el WebForm de
destino o la URL.
El siguiente fragmento de código muestra cómo establecer la propiedad
NavigateUrl:
Hyperlink1.NavigateUrl="http://www.amazon.com";
Control ImageButton
El control I m a g e B u t t o n permite a los programadores mostrar imágenes en
un WebForm y gestionarlas durante el diseño o en tiempo de ejecución. Este
control representa un botón gráfico, que mejora la apariencia del WebForm. Se
puede establecer la propiedad I m a g e U r l para que apunte a una imagen especi-
fica.
NOTA: Quizás tenga que esperar unos instantes a que ASP.NET cree el
proyecto. También debe asegurarse, antes de crear la aplicación, de que el
servidor Web está funcionando. Para activar el servidor Web, seleccione
lnicio>Ejecutar. En el cuadro de diálogo Ejecutar, escriba net start
iisadmin y pulse Intro.
Figura 22.3. Visual Studio NET crea el nuevo proyecto.
Figura 22.4. La ventana de proyecto muestra todos los archivos creados por el
asistente de la aplicación Web.
Cada control tiene una propiedad ID que se usa para identificar unívocamente
al control.
Para establecer la propiedad de un control en tiempo de ejecución se usa la
siguiente sintaxis:
Control_ID.Property=Value
En la anterior sintaxis:
• Control_ID representa la propiedad ID del control.
• Property representa la propiedad del control.
• Value representa el valor asignado a la propiedad del control.
La figura 22.5 muestra un WebForm que contiene los habituales controles Web
como etiquetas, cuadros de texto, hipervíncu los. botones de opción, casillas de
verificación y botones. Como puede ver. el WebForm es un formulario de registro
de usuario. El formulario está diseñado para aceptar entradas de usuario desde
varios controles. Tras rellenar el formulario, el usuario pue de hacer clic en el boton
Submit (Enviar) para completar el registro. El botón Submit abre otro WebForm
que muestra un mensaje junto al nombre que el usuario introdujo en el control
TextBox. Si el usuario hace clic en el botón Reset (Restablecer), la información
que ha introducido el usuario se elimina de los controles de ese formulario.
Figura 22.5. El formulario de registro de usuario muestra los controles comunes que
se pueden agregar a un WebForm.
Para crear el formulario mostrado en la figura 22.5. rea lice los siguientes
pasos:
1. Seleccione el formulario WebForm1.aspx y pulse F4 para que aparezca la
ventana Propiedades.
2. En la ventana Propiedades, pulse el botón de puntos suspensivos para
que aparezca la propiedad bgColor y seleccione Propiedades. Se abri-
rá el cuadro de diálogo Selector de colores.
3. En el cuadro de diálogo Selector de colores, seleccione un matiz rosa y
haga clic en Aceptar. El color del WebForm cambiará al color que ha
especificado.
4. Agregue controles al formulario y cambie sus propiedades, como recoge la
tabla 22.2.
Font
Bold=True
El texto de cada etiqueta debe Una bajo otra en el lado
Size=Larger
Etiquetas para
ser la misma que el título de- izquierdo de la pantalla
Nombre, Correo
seado.
electrónico, Esta-
do y Suscripción
TextBox ID=txtName
Junto a la etiqueta
Ñame
TextBox ID=txtEmail
Junto a la etiqueta E-
Mail
DropDownList ID = lstState Junto a la etiqueta
Items=Arizona, State
California, Florida
Text=Reset
La interfaz de su WebForm. como aparece en la figura 22.6. está lista.
Agregará la funcionalidad de los botones Submit y Reset en la siguiente sec-
ción. Sin embargo, antes de continuar, agregue a la aplicación Web otro formula-
rio que muestre los detalles sobre el usuario registrado cuando éste haga clic en el
botón Submit. Para agregar el WebForm. siga estos pasos:
1. Seleccione Proyecto>Agregar WebForm. Se abrirá el cuadro de dialo-
go Agregar nuevo elemento.
Figura 22.6. El WebForm deberla tener este aspecto una vez completa.
NOTA: Los eventos que tienen lugar con bastante frecuencia en lenguajes
de secuencia de comandos, como OnMouseOver, no son compatibles con
los controles de servidor. Sin embargo, algunos controles de servidor admi-
ten eventos que tienen lugar cuando cambia el valor del control.
Figura 22.7. El proceso de ida y vuelta
La tabla 22.3 describe los eventos más comunes asociados a diferentes con -
troles de servidor ASP.NET
TRUCO: Para codificar el evento del botón Submit, haga doble clic en la
vista Diseño.
i ____________________________________________________________
En el anterior código, el método R e d i r e c t de la clase H t t p R e s p o n s e
redirige al usuario a la página WebForm2 aspx y pasa el valor del parámetro
t x t N a m e a la página de destino.
Tras pasar el valor del cuadro de texto txtName. debe inicializar WebForm2
para controlar la cadena pasada desde el formulario de registro. Para hacerlo.
WebForm2.aspx debe tener el siguiente código en el evento Load:
prívate void Page Load (object sender, System.EventArgs e)
{
lblMessage .Text="Hi! " + Request.QuerySt ring .Get("strName");
}
Figura 22.8. Cuando un usuario es redirigido a otra página, el nombre del usuario se
pasa en la cadena de consulta.
Cuando el usuario hace clic en el botón Reset. debe generarse un evento que
elimine todos los controles rellenados por el usuario en WebForm1.aspx. Para
implementar esta funcionalidad, codifique el evento Click del botón Reset como
se indica a continuación:
prívate void BtnReset_Click(object sender, System.EventArgs e)
{
txtName.Text ="";
txtEmail.Text ="";
lstState.ClearSelection();
lstOptíons.ClearSelection();
}
En e l có d i go a nter io r , c ua nd o se ha ce c lic e n el b o tó n R es et d e l fo r mu l a rio d e
reg i str o , el fo r mu l ario s e r ei n ic ia y v u el v e a s u es tad o o r i gi na l.
• Escr ib i e nd o u n co n tr o l a d o r d e e ve n to s p ar a e l e ve n to Cl ic k : d e l b o tó n .
• Escr ib i e nd o el co ntr o la d o r d e e ve nt o s p ara e l ev e nto L o a d d el W eb Fo r m.
El e v e nto L o a d se ge n er a c u a nd o se ab re e l fo r mu l ar io . P ue d e us ar la
p r o p ied ad I s P o s t B a c k del e ve n to L o a d p ara d eter mi n ar s i la p a gi na h a
sid o p ro ce sad a p o r p r i m er a vez o s i ha sid o p ro c esad a p o r u n c lic d e b o t o n.
El s i g ui e nte có d i go mu e str a el co n tr o l ad o r d e e v en to s p ara u n e ve n to L o a d
d e u n W eb Fo r m:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
//Evalúa true la primera vez que el navegador llega a
la p a g i na
}
}
Resumen
En este capítulo, ha aprendido a crear una sencilla aplicación Web mediante
Visual C# en el entorno ASP.NET. Se han estudiado los fundamentos de ASP.NET
y cómo se crean aplicaciones Web en Visual C#.
ASP.NET incluye un entorno de tiempo de ejecución separado que gestiona la
ejecución de las aplicaciones ASP.NET. También incluye nuevos componentes de
servidor, llamados WebForms. que encapsulan la funcionalidad de una página
Web. Puede agregar uno o más controles de servidor a un WebForm. Los contro-
les de servidor son los responsables de mostrar los datos a los usuarios y procesar
sus interacciones.
Hemos creado un proyecto de aplicación Web y le hemos agregado un WebForm.
Al crear la aplicación, usamos la plantilla ASP.NET para aplicaciones Web para
crear una solución y agregar un proyecto ASP.NET a la solución. A continua-
ción. diseñamos una página Web usando controles Web corrientes, como los con-
troles que representan etiquetas, cuadros de texto, cuadros de listas, hipervínculos.
botones y similares. Por último, aprendimos a controlar los eventos que generan
los controles en el WebForm.
23 Programación
de bases
de datos
con ADO.NET
Clase Descripción
DataSet
Una caché en memoria de datos, que pueden consistir
en varias D a t a T a b l e s relacionadas entre sí. Está di-
señada para su uso sin conexión en las aplicacio-
nes distribuidas.
DataTable Un contenedor de datos, que puede componerse de
varias D a t a C o l u m n s . Cada fila de datos se contiene
en una D a t a R o w .
DataRow Una fila de datos concreta en una D a t a T a b l e
DataColumn La definición de una columna (nombre, datos, tipo,
etc.) en una D a t a T a b l e
DataRelation Una relación entre dos D a t a T a b i e s de una
DataSet,
normalmente usada para representar relaciones de
Constraint clases externas
Una restricción a una o más D a t a C o l u m n s , usada
para representar limitaciones como la exclusividad
DataColumnMapping Asigna los nombres de columna desde la tabla de
una base de datos hasta los nombres de columna de
D a t a T a b l e en el D a t a S e t
DataTableMapping Asigna los nombres de tabla en una base de datos
hasta los nombres de D a t a T a b l e s en D a t a S e t
DataView Una vista personalizada de una D a t a T a b l e que pue-
de usarse en la clasificación, filtrado y búsqueda.
SqIError OleDbError
Un aviso o error devuelto por la
base de datos, perteneciente a una
colección E r r o r s .
SqIException OleDbException
El tipo de excepción iniciado cuan-
do ocurren errores en la base de
datos
SqlParameter OleDbParameter
Un parámetro para un procedimien-
to almacenado
El listado 23.3 muestra lo sencillo que resulta ejecutar una instrucción SQL
que no devuelve filas en ADO.NET. En este proceso se emplean dos objetos. En
primer lugar se usa un objeto O l e D b C o n n e c t i o n para establecer una co-
nexión con una base de datos. El listado 23.1 muestra un ejemplo de cadena de
conexión usada para acceder a la base de datos Northwind de una instrucción de
SQL Server instalada localmente.
Listado 23.1. Ejemplo de cadena de conexión para SQL Server
Listado 23.3. Plantilla para usar un comando y ejecutar una instrucción SQL que no
devuelve filas
USE [Northwind]
GO
CREATE PROCEDURE [pc_insCustomers]
(@CustomerID_1 [nchar] (5) ,
@CompanyName_2 [nvarchar] (40) ,
@ContactName_3 [nvarchar] (30) ,
@ContactTrt1e_4 [nvarchar] (30),
@Address_5 [nvarchar] (60) ,
@Crty_6 [nvarchar] (15),
@Region_7 [nvarchar](15),
@Posta1Code_8 [nvarchar] (10) ,
@Country_9 [nvarchar] (15) ,
@Phone_10 [nvarchar] (24) ,
@Fax_11 [nvarchar] (24) )
AS
INSERT INTO [Northwind].[dbo].[Customers]
( [CustomerID] ,
[CompanyNa me] ,
[ContactName] ,
[ContacTit1e],
[Address] ,
[City],
[Region] ,
[Posta1Code],
[Country] ,
[Phone] ,
[Fax])
VALUES
(@CuslomerID_1,
@CompanyMame_2,
@ContactName_3,
@ContactTitle_4 ,
@Address_5,
@City_6,
@Region_7,
@Posta1Co de_8,
@Country_9,
@Phone_10,
@Fax_11)
s t a t i c v o i d T e s t l n s e r t W i t h S P S t a t e m e n t ( s t r i n g custo m e r I D )
i
/ / Establece la cadena de instruccion, SQL
string strSQLInsert - " [pc_insCustomers]";
try
{
// Establezca la conexión de la base de datos
databaseConnection.Open() ;
USE [Northwind]
GO
CRE ATE PROCEDURE [pc_gefContact B yCustomerID]
( @CustomerID_1 [nchar] (5),
@ContactName_2 [nvarchar] ( 3 0 ) output,
@ContactTirle_3 [nvarchar| (30) output)
SELECT
@ContactName_2 = [ContactName ] ,
@ContactTit1e_3 =[ContactTitle]
FROM [Northwind].[dbo].[Customers]
WHERE
[CustomerID] = @CustomerID_1
param.Value = customerID;
// Agregue cada parametro (2 de 3)
param = selectCommand.Parameters.Add("@ContactName 2",
OleDbType.VarChar , 30);
param.Direction = ParameterDirection.Output ;
// Agregue cada parametro (3 de 3)
param = selectCommand.Parameters.Add("@ContactTitle 3",
OleDbType.VarChar, 30);
param.Direction = ParameterDirection.Output;
try
{
// Establezca la conexión de la base de datos
databaseConnection.Open();
try
{
// Establezca la conexión de la base de datos
databaseConnection.Open();
try
{
// Establezca la conexión de la base de datos
databaseConnec1 1on.Open();
try
{
// Establezca la conexión de la base de datos
databaseConnection.Open();
resultDataSet.Tables["Customers"].Rows.Add(workRow);
USE [Northwind]
GO
CREATE PROCEDURE [ pc_getCustorner_ByCustornerID]
(@CustomerID_'l [nchar] (5))
AS
SELECT
[CustomerID],
[CompanyName],
[ContactName],
[ContactTit1e],
[Addres s] ,
[City],
[Región],
[PostalCode],
[Country],
[Phone],
[Fax]
FROM [Northwind].[dbo].[Customers]
WHERE
[CustomerID] = @CustomerID_I
param.SourceColumn = "Fax";
try
{
// Establezca la conexión de la base de datos
databaseConnection.Open();
resultDataSet.Tables["Customers"].Rows.Add(workRow);
try
{
// Establezca la conexión de la base de datos
databaseConnection.Open();
Listado 23.14. Cómo borrar un registro único mediante una instrucció n SQL
try
{
// Establezca la conexión de la base de datos
databaseConnection.Open();
// Ejecute el comando SQL
int numRows = deleteCommand.ExecuteNonQuery();
try
{
// Establezca la conexión de la base de datos
databaseConnection.Open();
// Ejecute el comando SQL
dsCmd.SelectCommand = selectCommand;
int numRows = dsCmd.Fill(resultDataSet, "Customers");
USE [Northwind]
GO
CREATE PROCEDURE [ pc_getCustorners ]
AS
SELECT TOP 5
[CustomerID],
[CompanyName],
[ContactName],
[ContactTi11e] ,
[Address],
[City],
[Región],
[PostalCode] ,
[Country],
[Phone] ,
[Fax]
FROM [Northwind].[dbo].[Customers]
try
{
// Establezca la conexión de la base de datos
databaseConnection.Open();
try
{
// Establezca la conexión de la base de datos
databaseConnection.Open();
try
{
// Establezca la conexión de la base de datos
databaseConnection.Open();
resultDataSet.Tables["Customers"].Rows.Add(workRow);
resultDataSet.Tables ["Customers"].Rows.Add(workRow);
USE [Northwind]
GO
CREATE PROCEDURE [pc_getOrdersAndDetai1s]
AS
SELECT TOP 5
OrderID,
CustomerID,
EmployeeID,
OrderDate,
RequiredDate,
ShippedDate,
ShipVia,
Freíght,
ShipName,
ShipAddress,
ShipCity,
ShipRegion,
ShipPostalCode,
ShipCountry
FROM Orders
SELECT TOP 5
OrderID,
ProductID,
UnitPrice,
Quantity,
Discount
FROM [Order Detaxis]
GO
try
{
// Establezca la conexion de la base de datos
databaseConnection.Open();
}
}
catch (Exception e)
{
Console.WriteLine ("****** Caught an exception:\n{0}" ,
e.Message);
}
finally
{
databaseConnection.Close();
}
}
En la ul ti ma se cció n d el co d i go es ta u no d e lo s p u nto s fu ert es d e l o b j eto
Dat a S et : la r ec up er aci ó n d e d a to s r e lac io nad o s. Co mo e l Da ta Se t fue d ise ñ ad o
p ar a tr ab aj ar co mo u na b a se d e d a to s e n me m o ri a, t ie ne to d a l a fu n c io n al id ad
ne ce sar ia p ara trat ar co n l as r e la cio ne s p r i mari as y s ec u nd ar ia s. Lo s s ig u ie n te s
d o s li s tad o s ej e mp li fic a n la r e c up er a ció n d e d at o s. El l is tad o 2 3 .2 2 mu e str a có mo
se r ec up e ra n d ato s r ela c io n ad o s med ia n te u n a i n str u cció n SQ L y e l li st a d o 2 3 .23
mu e s tr a co mo r ec up er ar d ato s r e la cio nad o s u s a nd o u n o b j eto Dat aS et. El li st a -
d o 2 3 .2 3 d e mue s tra co m o tr ata r co n es te t ip o d e relac io ne s. D e n u e vo , s e u sa u n
p r o ced i mi e n to al ma ce n a d o q ue d e v u el ve d ato s d e d o s i n st r ucc io ne s S e l e c t . Las
in s tr ucc io ne s Se lec t e s tá n r e lac io nad as y se q ui ere co ns e g uir el mi s m o res u lta -
d o en ADO. NET co mo s i se r e c up er a se n lo s d a t o s co n u n a i n str u cció n SQ L i g ual
a la q u e ap arec e e n el li st ad o 2 3 .2 2 .
SE LE CT
Orders.OrderID,
Orders.CustomerID,
Orders.EmployeeID,
Orders.OrderDate,
Orders.RequiredDate,
Orders.ShippedDate,
Orders.ShipVia,
Orders.Freight,
Orders.ShipName,
Orders.ShipAddress,
Orders.ShipCity,
Orders.ShipRegion,
Orders.ShipPostalCode,
Orders.ShipCountry,
[Order Details] . ProductID,
[Order Details] .UmtPnce,
[Order Details].Quantity,
[Order Details].Discount
FROM Orders
INNER JOIN [Order Details]
ON Orders.OrderID = [Order Details].OrderID
try
{
dsCmd.TableMappings.Add ("Orders" , "Orders”) ;
dsCmd.TableMappings.Add ("Ordersl" , "Order Details") ;
// Establezca la conexión de la base de datos
databaseConnection.Open();
{ 1} .
detailRow["ProductID"],
detailRow["Quantity"] ) ;
}
}
catch (Exception e)
{
Console.WriteLine ("****** Caught an exception: \n{0}" ,
e.Message);
}
finally
{
databaseConnection.Close();
}
}
Resumen
Este capítulo describe todos los tipos de operaciones que puede realizar en
ADO.NET. También se muestra lo sencillos y ve rsátiles que son los objetos en el
espacio de nombres S y s t e m . D a t a , incluyendo el potente objeto D a t a S e t .
que funciona como una base de datos en memoria. También describe cómo devol -
ver filas con entidades únicas y cómo borrar y actualizar operaciones.
Tenga en cuenta que la arquitectura de proveedor ADO.NET se refleja en las
clases NET Framework que admiten ADO.NET. Podemos usar las clases S q l
para aprovechar el proveedor SQL Server de AD0.NET. Si su aplicación sólo va
a admitir SQL Server, debe usar las clases SQL. porque el proveedor SQL Server
para AD0.NET es más eficiente y funciona mejor que el proveedor OLE DB
cuando se trata de SQL Server. Si su aplicación necesita incluir compatibilidad
con otras bases de datos que no son SQL Server, es aconsejable elegir clases
OleDb.
24 Cómo trabajar
con archivos
y con el registro
de Windows
Las operaciones de archivo son algo a lo que todo programador debe enfren -
tarse en un momento u otro. La clase S y s t e m . I 0 contiene una cantidad ingente
de métodos para leer y escribir en y desde archivos. Esta clase simplifica la E/S
de archivos y su manipulación y brinda un gran control de acceso a archivos. De
forma parecida a la E/S del pasado, el acceso al registro de Windows siempre ha
sido una tarea muy pesada. Esta tarea requería muchas llamadas al API que
reducían el rendimiento de la aplicación y solian obligar al programador a escri -
bir sus propias clases contenedoras para estas operaciones. Con NET todo esto
cambio. En este capítulo aprenderá a leer y escribir datos en y desde archivos. El
capitulo estudia las operaciones de E/S que operan con texto normal y con datos
binarios. También estudia algunas operaciones de archivos útiles, como mover,
renombrar y eliminar archivos. Por ultimo, aprendera a supervisar el sist ema de
archivos para buscar cambios en archivos específicos y seguir recorriendo el
registro de Windows.
Acceso binario
Las clases BinaryReader y BinaryWriter son compatibles con el ac-
ceso binario a archivos. Estas clases permiten el acceso a archivos binarios y las
operaciones binarias hacia y desde secuencias. Como se usan secuencias, las
clases pueden ser muy flexibles y no tienen que tratar con detalles, como la posi -
ción de la secuencia o el acceso a la misma. El sigu iente apartado examina la
clase de acceso BinaryWriter.
BinaryWriter
La clase BinaryWriter permite escribir tipos de datos primitivos en se -
cuencias y con el uso de las subclases, se pueden reemplazar los métodos de esta
clase y cumplir con los requisitos de la codificación única de caracteres. Como
esta clase usa una secuencia subyacente, hay que trabajar con muy pocos métodos
y propiedades. La tabla 24.1 contiene las cinco propiedades y métodos básicos
que mas usará para trabajar con la clase BinaryWriter.
Para escribir datos en un archivo, antes hay que crear una secuencia de archi -
vos. Seguidamente se puede instanciar una nueva clase BinaryWriter. pasán-
dola a la secuencia. Tras crear esta clase BinaryWriter. sólo hay que llamar
a su método Write ( ) y pasarle los datos que se deben escribir, como se puede
ver en el listado 24.1.
Listado 24.1. Cómo escribir datos en una secuencia de archivos mediante l a clase
BinaryW riter
BinaryReader
La clase B i n a r y R e a d e r . al igual que la clase B i n a r y W r i t e r . se basa
en un objeto F i l e S t r e a m para acceder a los archivos.
Para comprender la clase B i n a r y R e a d e r . examine la aplicación del listado
24.2. que lee la información del título binario de un archivo de mapa de bits A
partir de la información de este título, se puede determinar el tamaño horizontal y
vertical del archivo de imagen, además de su profundidad de color en bits.
using System;
using System.I0;
namespace BitmapSize
{
class Class1
{
static void Main(string[] args)
{
long bmpWidth = 0;
long bmpHeight = 0;
int bmpPlanes = 0;
int bmpBitCount = 0;
string [] cma = Environment.GetCommandLineArgs();
íf (cma.GetUpperBound(0) >= i)
(
FileStream myFStream = new
FileStream(cma[1],FileMode.Open,FileAccess.Read);
BinaryReader binRead = new BinaryReader(myFStream);
binRead.BaseStream.Position=0xl2;
bmpWidth = binRead.Readlnt32();
bmpHeight= binRead.Readlnt32();
bmpPlanes= binRead.ReadIntl6();
bmpBitCount = bmRead.ReadInt16();
Método Descripción
}
prívate void fi]eSystemWatcherl Changed(object sender,
System.IO.FileSystemEventArgs e)
{
listBox1.Items.Add( "[" + e.Name + "] Changed” );
}
prívate void f 11eSystemWatcher1 Created(object sender,
System.IO.FileSystemEventArgs e )
{
listBox1.Items.Add ("["+ e.Name + " ] Created");
)
Estos controladores de eventos son bastante simples: solamente muestran un
mensaje en el cuadro de lista cuando se produce una operación. Antes de compro -
bar esta aplicación, es importante tener en cuenta algunas cosas. C omo puede ver
en cada una de estas funciones, los eventos C h a n q e d . C r e a t e d y D e l o t e c J
muestran los mismos datos; por tanto, tienen la misma firma. Lo extraño aqui es
el evento R e n a m e d . Como tres de los cuatro eventos pasan los mismos datos al
procedimiento del evento, es posible usar un con trolador de eventos que controle
estos tres eventos, pero seguirá necesitando que un controlador distinto se haga
cargo del evento R e n a m e d .
Pulse F5 para ejecutar el programa y empezar a examinarlo como se indica a
continuación:
1. Cuando el programa se abra, haga clic en el boton Start. La aplicación
esta supervisando la raiz de la unidad C. buscando cambios en los archi -
vos de texto.
2. Abra el Explorador de Windows. Haga clic con el boton derecho en la
ventana del explorador y seleccione Nuevo>Documento de texto para
crear un nuevo documento.
3. En el nuevo documento, observará una entrada en el cuadro de lista de su
aplicación File Monitor que índica que se ha creado un archivo. Abra este
nuevo archivo de texto, agregúele algo de texto y guárdelo. De nuevo, vera
entradas en la v entana de registro de la aplicación.
4. Ahora intente renombrar el archivo y borrarlo a continuación. Los resulta -
dos deberían ser similares a los que aparecen en la figura 24.4.
this.MyFileWatcher.Renamed += new
System.IO.RenamedEventHandler( this.MyFi1eWatcher Renamed);
this.MyFi1eWatcher.Changed += new
System.IO.FileSystemEventHandler( this.MyFi1eWatcher Changed );
btnStart.Enabled = false;
btnStop.Enabled = true;
}
A continuación, agregue codigo al botón Stop y cree controladores de eventos
para Fi l eS ys t e m Wa t ch er . Los nombres de función para estos controladores
de eventos deben coincidir con la declaración que colocó en el evento c l i c k d e
los botones Start. El listado 24.K contiene el evento Stop Click y los
controladores de evento para los eventos Changed y Renamed.
using System;
using System.IO;
namespace cp
{
class Class1
{
s t a t i c void Main (s t r i n g [ ] a rgs )
(
string [] cla = Environment.GetCommandLineArgs();
if (cla.GetUpperBound(0 ) == 2)
{
F i l e I n f o f i = n e w F i l eI n f o ( cla [ 1 ] ) ;
fi.CopyTo(cla[2] ,true);
Console.WriteLine("Copied " + fi.Length + " bytes.");
}
else
Console. W r i t e L i n e ( "Usage : c p < in p u t . f ile > <o u t p u t
file> ");
}
}
Figura 24.5. C o p y T o p e r m i t e c o p i a r u n a r c h i v o y m o s t r a r i n f o r m a c i ó n a d i c i o n a l
using System;
using System.IO;
namespace rm
{
class Class1
{
static void Main(string[] args)
{
string [] cla = Environment.GetCommandLmeArgs();
íf ( cla.GetUpperBound(0) == 1)
{
Filelnfo fi = new Filelnfo (cla[1]);
fi.Delete();
Console.WriteLine("File : " + cla[1]);
Console.WriteLine("Attributes : " +
fi.Attributes.ToString();
Console.W riteLine("File D e l e t e d . . . " ) ;
}
else
Console.W riteLine ("Usage: rm <filename>") ;
Tras invocar al método Delete() de la clase Filelnfo, puede mostrar al
usuario el nombre del archivo y sus atributos, indicando que ha sido eliminado.
Mediante la propiedad Attributes. puede determinar de un modo seguro,
antes de eliminar el archivo, si tiene asignado su atributo Read-Only. En ese
caso, puede avisar al usuario y/o eliminar el atributo Read-Only usando la
propiedad Attributes junto con el enumerador FileAttributes.
Una vez que su programa haya sido compilado, abra un intérprete de coman -
dos y pruébelo. Simplemente escriba rm seguido del nombre del archivo que
quiere eliminar. Los resultados deberían ser similares a los de la figura 24.6.
Para leer una clave del registro use el objeto R e g i s t r y K e y . Para empezar a
estudiar este objeto, examine el listado 24.12. una aplicaci ón que recupera dos
fragmentos de información del registro.
Listado 24.12. Recupera el tipo de CPU y su velocidad del Registro
using System;
using Microsoft.Wm32;
namespace CPUInfo
{
class Class1
{
static void Main(string[] args)
{
RegistryKey RegKey = Regístry.LocalMachine ;
RegKey = RegKey.OpenSubKey (
"HARDWAREWDESCRIPTION\\System\\Centra1Processor\\0");
Object cpuSpeed = RegKey.GetValue("~MHz");
Object cpuType = RegKey.GetValue("Vendorldentifíer");
Console.WriteLine("You have a {0} running at {1}
MHz.",cpuType,cpuSpeed) ;
}
}
}
Al instanciar una instancia de R e g i s t r y K e y . hace que su valor sea igual a
un miembro de la clase R e g i s t r y . El anterior ejemplo asigna al objeto
R e g i s t r y K e y el valor del campo R e g i s t r y . L o c a I M a c h i n e , que permite
el acceso a la clave base H K E Y L O C A L M A C H I N E . La tabla 24.3 tiene una lista
de todos los campos públicos de la clase de registro.
Tras establecer el objeto R e g i s t r y K e y . invoque a su método
O p e n S u b K e y () y proporcione la clave que quiere abrir. En este caso concre -
to, debe dirigirse a la clave H K E Y L O C A L M A C H I N E \ H A R D W A R E \
DESCRIPTION \S ystem\Central\Processor\0\ y leer dos valores de
esa clave. Tenga en cuenta que debe incluir dos barras invertidas en la cadena
para que no sean confundidas con un signo de escape.
Tras abrir la subclave. use las siguientes dos líneas de código para recuperar
los valores " ~ M H z " y " V e n d o r I d e n t i f i e r " de esa subclave:
Object cpuSpeed = RegKey.GetValue("~MHz");
Object cpuType = RegKey.GetValue("VendorIdentifier");
Ahora tiene los valores almacenados en las variables adecuadas, de modo que
puede mostrar la información en la ventana. Examine el programa desde una
v entana de consola, como muestra la figura 24.8.
Campo Descripción
ClassesRoot
ClassesRoot define los tipos de documentos y
las propiedades asociadas a esos tipos. Este cam-
po empieza en la clave H K E Y C L A S S E S R O O T del
registro de Windows.
CurrentConfig C u r r e n t C o n f i g contiene información relativa al
hardware de su equipo. Este campo empieza en la
clave H K E Y C U R R E N T C O N F I G del registro de
Windows.
CurrentUser
Aquí se almacenan todas las preferencias del ac-
tual usuario. Este campo empieza en la clave
H K E Y C U R R E N T U S E R del registro de Windows.
DynData DynData.
LocalMachine LocalMachine contiene información para el equi-
po. Este campo empieza en la clave H K E Y L O C A L
M A C H I N E del registro de Windows.
545
Campo Descripción
PerformanceData
La clave base almacena información relativa al rendi-
miento de los diferentes componentes de software.
Este campo empieza en la clave HKEY PERFORMAN-
CE DATA del Registro de Windows.
Users Esta clave base contiene información para la confi-
guración de usuario por defecto. Este campo empie-
za en la clave HKEY USERS del registro de Windows.
using System;
using Microsoft.Win32;
namespace WriteRegValues
{
class Class1
{
static void Main (string[] args)
{
RegistryKey RegKeyWrite = Regístry.CurrentUser ;
RegKeyWrite = RegKeyWrite.CreateSubKey
("Software\\CSHARP\\WriteRegistryValue");
RegKeyWrite.SetValue ("Success", "TRUE") ;
RegKeyWrite.SetValue("AttemptNumber",1);
RegKeyWrite.Close();
Esta aplicación muestra una sencilla técnica para escribir valores en el regis -
tro. Este método ha demostrado ser útil para controlar la configuración de los
programas, guardar la última posición y tamaño de la interfaz de las aplicaciones
y acciones similares. Las posibilidades son ilimitadas.
using System;
using Microsoft.Win32;
namespace Installed
{
class Classl
{
static void Main(string[] args)
{
RegístryKey myRegKey = Registry.LocalMachine;
myRegKey = myRegKey.OpenSubKey
("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall");
String[] subkeyNames = myRegKey.GetSubKeyNames();
foreach (String s in subkeyNames)
{
RegistryKey UninstallKey = Registry.LocalMachine;
UninstallKey = UnmstallKey.OpenSubKey
("SOFTWAREX\Microsoft\\Windows\\CurrentVersion\\Uninstall\\" +
s) ;
try
{
Object
oValue = UninstallKey.GetValue ("DisplayName");
Console.WriteLine (oValue.ToString());
}
catch (NullReferenceException)
{
}
}
}
}
}
Tras crear un objeto RegistryKey. abra la subclave Uninstall. que
contiene una lista de todos los programas instalados. A partir de aquí, use
GetSubKeyNames, que devuelve una matriz de cadenas de todas las subclaves.
Ahora que tiene una lista de subclaves. use el operador foreach para recorrer
todos los elementos de la matriz de cadenas de subclave.
Al recorrer cada clave, se busca un valor llamado DisplayName. Este va-
lor es el nombre que se muestra en la sección Agregar o quitar programas del
Panel de control. Recuerde que no todas las claves tendrán este valor. Por
tanto, debe encapsular el método GetValue con una instrucción try..catch
para capturar todas las posibles excepciones. Cuando se encuentra un valor
DisplayName. se recupera el valor y se muestra en la ventana. A continua -
ción. la instrucción foreach pasa a la siguiente clave de registro de la matriz de
cadenas.
Pulse F5 para probar la aplicación. Probabl emente vera una larga lista de
aplicaciones desplazándose a medida que el programa inspecciona el registro
(véase la figura 24.1 1).
Una cosa que no se intenta en este programa es ordenar las aplicaciones
alfabéticamente. Los elementos del registro no se a lmacenan así. pero para solu-
cionar esto, puede simplemente almacenar los resultados en una matriz de cade -
nas y llamar al método Sort para ordenar el resultado de la manera deseada.
Resumen
NET Framework ha reducido enormemente la cantidad de código y tiempo
necesario para tratar efectivamente con archivos y con el registro de Windows.
Entre los muchos beneficios de NET Framework. ahora tiene acceso a compo -
nentes como FileSystemWatcher que permite examinar un sistema de archi -
vos para buscar cambios realizados en cualquier archivo. Sin embargo, debe
tener
cuidado al escribir aplicaciones que traten con el registro de Windows, porque si
elimina por error claves de registro puede hacer que su sistema se vuelva inesta -
ble o incluso que deje de funcionar.
25 Cómo
acceder
a secuencias
de datos
E/S sincrónica
El listado 25.1 muestra una E/S de secuencia sincrónica. Crea un archivo y
escribe 256 bytes de datos binarios en él. A continuación, lee los 256 bytes del
archivo y se asegura de que la lectura de datos coincida con los datos escritos.
using System;
using System.IO;
class FileTestClass
{
prívate FileStream BinaryFile;
prívate byte[] ByteArray;
public FileTestClass()
{
BinaryFile = new FileStream("test.dat", FileMode.Create,
FileAccess.ReadWrite);
ByteArray = new byte [256];
}
public bool ReadBytes()
{
int Arraylndex;
BinaryFile.Seek(0, SeekOrigin.Begin);
BinaryFile.Read(ByteArray, 0, 256);
for(Arraylndex = 0; ArrayIndex < 256; ArrayIndex++)
{
if(ByteArray [Arraylndex] != (byte) Arraylndex)
return false;
}
return true;
}
}
class MainClass
{
static public vord Main()
{
FileTestClass FileTest = new FileTestClass();
bool ReadTest;
FileTest.WriteBytes();
ReadTest = FileTest.ReadBytes();
íf(ReadTest == true)
Console.WriteLine("The readback test was successful.");
else
Console.WriteLine("The readback test failed.");
}
}
El listado 25.1 implementa dos clases C#: Fi leTestClass. que contiene el
codigo de E/S de la secuencia y MainClass. que contiene el método Main()
de la aplicación. El método Main() crea un objeto de la clase FileTestClass
y pide al objeto que escriba y lea datos.
La clase FileTestClass contiene un m iembro privado que representa un
objeto File S t r e a m . El constructor de la clase crea un nuevo objeto
FileStream mediante un constructor que acepta tres argumentos:
Este ejemplo ajusta el puntero de la secuencia para que apunte dos bytes antes
del final de la secuencia. Los valores de desplazamiento negativos desplazan el
puntero de la secuencia hacia el principio de la secuencia.
El código del listado 25.1 usa el siguiente código de búsqueda antes de que se
lean los 256 bytes:
BinaryFile.Seek(0, SeekOrigin.Begin);
E/S asincrónica
El listado 25.2 es una modificación del listado 25 1 que refleja la E/S
asincrónica. A diferencia de la E/S sincrónica, en la que las llamadas para leer y
escribir operaciones no regresan hasta que la operación esta completa, las llama -
das a operaciones de E/S asincrónicas regresan poco después de ser llamadas. La
operación de E/S actual se realiza de forma oculta, en un subproceso distinto
creado por la implementación de los métodos de E/S asincrónicos de NET
Framework y cuando la operación se ha completado se advierte al código median -
te un delegado. La ventaja de la E/S asincrónica es que el código princ ipal no
necesita depender de que se complete una operación de E/S. La realización de
largas operaciones de E/S en segundo plano evita que la aplicación tenga que
realizar otras tareas, como procesar los mensajes de Windows en aplicaciones de
Windows Forms.
using System;
using System.IO;
using System.Threading ;
class FileTestClass
{
prívate FileStream BinaryFile;
prívate byte [] ByteArray;
prívate IAsyncResult AsyncResultImplementation;
prívate AsyncCallback ReadBytesCompleteCallback;
public FileTestC1ass ()
{
AsyncResult.Implementation = null;
BinaryFile = new FileStreamCtest.dat", FileMode.Create,
FileAccess.ReadWrite);
ByteArray = new byte [256];
ReadBytesCompleteCallback = new
AsyncCallback(OnReadBytesCompiete);
}
}
}
Console.WriteLine ("Read test failures: (0}”, Failures);
)
public void WaltForReadOperationToFinish()
{
WaitHandle WaitOnReadIO;
WaitOnReadIO = AsyncResultImplementation.AsyncWaitHandle;
WaitOnReadIO.WaitOne();
}
}
class MainClass
{
static public void Main()
(
FileTest.WriteBytes();
FileTest.ReadBytes();
FileTest.WaitForReadOperatíonToFinish();
}
}
El codigo de las operaciones de escritura del listado 25.2 se controla de forma
sincrónica v su código es idéntico al código de las operaciones de escritura del
listado 25.1. Sin embargo, la operación de lectur a es bastante diferente.
El código de las operaciones de lectura del listado 25.2 no empieza con una
llamada al método sincrónico Read() de la secuencia sino con una llamada al
método asincrónico BeginRead(). Esta llamada admite cinco parametros. Los
tres primeros concuerdan con los parámetros admitidos con el método sincrónico
Read(). pero los dos últimos parámetros son nuevos:
• Una referencia al búfer de bytes que se usará para contener la lectura de
bytes de la secuencia.
• Un número entero que especifica el elemento de matriz del primer byte en
el búfer que contendrá la lectura de datos de la secuencia.
• Un número entero que especifica el número de bytes que se van a leer.
• Datos específicos de llamada.
El delegado callback debe ser un objeto de una clase llamada
AsyncCallback. La clase AsyncCallback se declara en el espacio de nom-
bres System de NET Framework y gestiona un método que no devuelve nada v
admite una referencia a un interfaz llamada IAsyncResult. El listado 25.2
crea una instancia de este delegado en el constructor de la clase
FileTestClass:
ReadBytesCompleteCallback = new AsyncCallback(OnReadBytesComplete);
using System;
using System.IO;
using System.Threading;
{
prívate FileStream BinaryFile;
prívate byte[] ByteArray;
prívate IAsyncResultAsyncReadResultImplementation;
prívate IAsyncResultAsyncWriteResultImplementation;
prívate AsyncCallbackReadBytesCompleteCallback;
prívate AsyncCallbackWriteBytesCompleteCallback;
public FileTestClass()
{
AsyncReadResultImplementation = null ;
BinaryFile = new FileStreamOtest.dat", FileMode.Create,
FileAccess.ReadWrite);
ByteArray = new byte [256];
ReadBytesCompleteCallback = new
AsyncCallback(OnPeadBytesComplete);
WriteBytesCompIeteCallback = new
AsyncCallback(OnWriteBytesComplete);
}
public void WriteBytes()
{
ínt ArrayIndex;
}
}
Console.WriteLine ("Read test failures: {0}", Failures)
}
public void WaitForReadOperationToFinish()
{
WaitHandle WaitOnReadIO;
WaitOnReadIO =
AsyncReadResultImplementation.AsyncWaitHandle;
WaitOnReadIO.WaitOne();
}
public void OnWriteBytesComplete(IAsyncResult AsyncResult
{
BinaryFile.EndWrite (AsyncResult);
}
private void WaitForWriteOperationToFinish()
{
WaitHandle WaitOnWriteIO;
WaitOnWriteIO =
AsyncWriteResultImplementation.AsyncWaitHandle;
WaitOnWriteIO.WaitOne();
}
}
class MainClass
(
static public void Main()
{
FileTestClass FileTest = new FileTestClass();
File Test.WriteBytes();
FileTest.ReadBytes();
}
}
E l m é t od o E n d W r i t e ( ) n o d e v u e l v e u n v a l o r , a d i f e r e n c i a d e l m é t od o
E n d R e a d ( ) . S i n e m ba r g o, l o s d os m é t o d os s e bl o q u e a n h a s ta q u e s e t e r m i n a l a
o p e r a c i ón d e E S
Escritores y lectores
N E T F r a m e w or k t i e n e m u c h a s c l a s e s e s c r i t or a s y l e c t o r a s q u e f a c i l i t a n e l
tr a ba j o c on d a t os m a s c o m p l e j o s q u e s i m p l e s s e c u e n c i a s d e by te s . L os l e c t or e s \
l o s e s c r i t or e s e n c a p s u l a n u n a s e c u e n c i a y p r o p o r c i on a n u n n i v e l d e c on v e r s i ón
q u e c on v i e r t e l o s v a l o r e s e n s u s s e c u e n c i a s d e by t e s e q u i v a l e n t e s ( p a r a l o s e s c r i -
t or e s ) y v i c e v e r s a ( p a r a l os l e c t or e s ) . L a s c l a s e s l e c t or a s y e s c r i t or a s d e N E T
s u e l e n t e n e r u n n o m br e q u e r e f l e j a e l t i p o d e c a m bi o d e f or m a t o q u e r e a l i z a n . P or
e j e m p l o, l a c l a s e H t m l T e x t . W r i t e r e s c r i be v a l or e s d e s ti n a d os p a r a l a i n f or -
m a c i ón d e r e s p u e s t a d e H T T P e n v i a d a p or A S P . N E T y l a c l a s e S t r i n g R e a d e r
l e e v a l o r e s e s c r i t o s u s a n d o s u r e p r e s e n t a c i ón d e c a d e n a .
L a s c l a s e s e s c r i t or a s y l e c t o r a s t a m bi é n c on tr ol a n v a n os e s q u e m a s d e c od i f i -
c a c i ó n . a l g o i m p os i bl e d e r e a l i z a r m e d i a n t e o bj e t os d e s e c u e n c i a d e ba j o n i v e l .
P or e j e m p l o, l a s c l a s e s d e r i v a d a s d e l a c l a s e a bs tr a c ta T e x t W r i t e r . p e r m i te n
a l c od i g o C # e s c r i bi r t e x t o y c od i f i c a r l o e n l a s e c u e n c i a m e d i a n t e l os a l g o r i tm o s
c od i f i c a d o r e s A S C I I . U n i c od e . U T F 7 o U T F 8 .
using System;
using System.IO;
class FileTestClass
{
prívate BinaryWriter Writer;
prívate FileStream BinaryFile;
public FileTestClass()
{
BinaryFile = new FileStream("test.dat" , FileMode.Create,
FileAccess.ReadWrite);
Writer = new BinaryWriter(BinaryFile);
}
public void WriteBinaryData()
{
Writer.Write('a');
Writer.Write(123);
Writer.Write(456.78S);
Writer.Write("test string") ;
}
}
class MainClass
{
static public void Main()
{
FileTestClass FileTest = new FileTestClass();
FileTest.WriteBinaryData();
}
}
El código del listado 25.4 tiene un diseño de clases igual al del listado 25.3. El
código contiene una clase M a i n C l a s s y una clase F i l e T e s t C l a s s . El cons-
tructor de la clase F i l e T e s t C l a s s del listado 25.4 crea una secuencia de
archivos y. a continuación, un objeto B i n a r y W r i t e r . El constructor del objeto
B i n a r y W r i t e r . que establece la relación entre el escritor binario y la secuen -
cia en la que escribe sus datos, recibe una referencia a la secuencia de archivos.
En el listado 25.4. todos los datos escritos en el escritor binario terminan en la
secuencia de archivos establecida en el constructor. El método W r i t e B i -
n a r y D a t a () escribe un carácter, un número entero, uno doble y una cadena, en
la secuencia subyacente. La clase B i n a r y W r i t e r implementa varias sobrecar-
gas de un método llamado W r i t e (). Las sobrecargas del método W r i t e ()
admiten la escritura en la clase escritora de los siguientes tipos de datos:
• Booleanos
• Bytes
• Matrices de bytes
• Caracteres
• Matrices de caracteres
• Valores decimales
• Valores dobles
• Valores enteros cortos con y sin fir ma
• Valores enteros con y sin firma
• Valores enteros ampliados con y sin fir ma
• Sbytes
• Valores de coma flotante
• Cadenas
Si compila y ej ecuta el código del listado 25.4. se crea un archivo llamado
test.dat Puede exami nar los cont enidos del nuevo archi vo en un editor
hexadeci mal para comprobar que las representaciones binarias de los v alores se
han escrito en el archi vo.
using System;
using System.IO;
class FileTestClass
{
private BinaryReader Reader;
private BinaryWriter Writer;
private FileStream BinaryFile;
public FileTestClass()
{
BinaryFile = new FileStream("test.dat:" , FileMode.Create,
FileAccess.ReadWrite);
Writer = new BinaryWriter(BinaryFile);
Reader = new BinaryReader(BinaryFile);
}
public void ReadBinaryData()
{
char ReadCharacter;
double ReadDouble;
int ReadInteger;
string ReadString;
BinaryFile.Seek( 0, SeekOrigin.Begin);
ReadCharacter = Reader.ReadChar();
ReadInteger = Reader.Readlnt32();
ReadDouble = Reader.ReadDouble();
ReadString = Reader.ReadString();
FileTest.WriteBinaryData();
FileTest.ReadBinaryData();
}
}
A d i fer e nc ia d e la c la s e B i nar yW r ite r . q ue co nt ie n e u n méto d o so b r e car -
gad o p ara to d a s la s o p er ac io ne s, l a cl as e B i na r yRe ad er co n ti e ne u n m éto d o
d e lec t ura d i st i nto p ar a c ad a t ip o d e d a to s . El eo d i go d e l l i stad o 2 5 .5 u s a al g u no s
d e e sto s m é to d o s d e l ect ur a, co mo Re ad C har () y Re ad I nt3 2 () . p ar a l eer
va lo r e s d e l a s ec ue n ci a escr it a e n el mé to d o W riteB i nar yD a ta() . Lo s valo -
r es q ue se lee n d e la se c u en cia se e n vía n a la co ns o la. Al ej ec u tar e l l is tad o 2 5 .5
se
o b tie n e e l si g u ie nt e r e s u ltad o e n l a co n so la.
Integer: 123
Double: 456.789
String: test string
using System;
usmg System.IO;
using System.Xml;
class XMLStreamWriterClass
{
prívate XmlTextWriter XmlWriter;
XmlWriter.WriteStartDocument();
XmlWriter.WriteComment("This XML document was
automatically generated by C# code.");
XmlWriter.WriteStartElement("BOOK");
XmlWriter.WriteElementString("TITLE", "C# Bible");
XmlWriter.WriteElementString("AUTHOR", "Jeff Ferguson");
XmlWriter.WriteElementString("PUBLISHER", "Wiley");
XmlWriter.WriteEndElement();
XmlWriter.WriteEndDocument();
}
}
class MainClass
{
static public void Main()
{
XMLStreamWriterClass XMLStreamWriter = new
XMLStreamWriterClass();
XMLStreamWriter.WriteXML();
}
}
El codigo del listado 25.6 crea una nueva instancia de la clase XmlWriter y
la asocia a la secuencia de salida de la consola. El código simplemente llama a
varios métodos de la clase XmlWriter para producir datos y los métodos ro-
dean a esos datos con nombres de elementos XML que se especifican cuando se
llama al método.
Observe la siguiente línea del listado 25.6:
< BOOK>
<TITLE>C# Bib1e </TITLE>
<AUTHOR> Jeff Ferguson </AUTHOR>
< PUBLISHER> Wi1ey </PUBLISHER>
</BOOK>
Resumen
Las s ec ue n cia s p ro p o r ci o na n co mp a tib il id ad co n la L S s i ncró n ica y a si n c ró n ica
en la s ap lic ac io ne s d e C # . Las se c ue nc ia s tr ab aj an e n el n i ve l d e b yt es y n e ce si -
ta n q ue se l ea n y e scr ib an b lo q ue s d e b yt es . Lo s le cto r es y es cri to re s e nc ap s u la n
sec u e nc ia s y p ro p o rc io n an a cce so a lo s d a to s e n u n n i vel s up er io r. P u ed e u sar
lec to r e s y e scr ito r e s p a r a tr ab aj ar co n lo s tip o s d e d a to s e s tá nd ar d e C# . lo q ue
p er mi te q ue lo s l ec to r e s y lo s e scr ito r es ha ga n c o n ver s io ne s e n tre lo s va lo re s d e
tip o s d e d ato s y s u s r ep r ese n ta cio ne s d e b yt es .
S u co d i go C# s e g ur a me nt e tr ab aj ar a co n l ecto r es y es cri to re s, ya q u e p ro p o r -
cio na n co mp a tib il id ad p ar a tr ab aj ar co n lo s tip o s d e d a t o s e st á nd ar s i n t en er q ue
o cup ar se d e r eal iz ar la c o n ver s ió n e nt r e u n va lo r d e u n tip o d e d ato y s u r ep re se n -
tac ió n b i n ari a. Si n e mb a r go , la s se c ue nc ia s ta mb ié n e s tá n d i sp o nib le s si cree q u e
ne ce si ta tr ab aj ar co n el l as d ir ec ta me n te. Q uiz ás ta mb i é n q u iera trab aj ar co n se -
cu e nci a s si lo s d a to s q u e e st á l e ye nd o es tá n e n u n fo r mato p ro p i et ario q ue no e s
co mp at ib l e co n l as c la se s l ecto r a s y es cr i to ra s e st á nd ar d e NET Fra me wo r k.
T a mb i é n p ued e co ns id e r ar la p o s ib i lid ad d e cr ear s us p ro p ia s c la se s lec to ra s,
d er i vad as d e l a s c la s es b a se T e x t R e a d e r o S t r e a m R e a d e r y us ar la s p ara
leer la se c ue nc ia d e fo r ma to p r o p ie tar io .
26 Cómo
dibujar
con GDI+
Propiedad Descripción
Método
Método Descripción
Descripción
FilIRectangle
Rellena el interior de un rectángulo especificado
por un par de coordenadas, un valor de anchura y
un valor de altura
FilIRectangles Rellena el interior de una serie de rectángulos es-
pecificados por estructuras R e c t a n g l e
FilIRegion Rellena el interior de un objeto R e g i ó n
Flush
Fuerza la ejecución de todas las operaciones de
gráficos pendientes y devuelve inmediatamente el
control sin esperar a que finalicen las operaciones
FromHdc Crea un nuevo objeto G r a p h i c s a partir del
identificador especificado en un contexto de dispo-
sitivo
FromHwnd
Crea un nuevo objeto G r a p h i c s a partir del
identificador especificado de una ventana
Fromlmage
Crea un nuevo objeto G r a p h i c s a partir del objeto
i m a g e especificado
Método Descripción
GetHalftonePalette
Obtiene un identificador de la paleta actual de
semitonos de Windows
GetHdc
Obtiene el identificador del contexto de dispositivo
asociado a este objeto G r a p h i c s
GetNearestColor
Obtiene el color más próximo a la estructura c o -
l o r especificada
IntersectClip Actualiza la región de recorte de este objeto
G r a p h i c s como la intersección de la región de re-
corte actual y la estructura R e c t a n g l e especifica-
da
IsVisible
Indica si el punto especificado por un par de coor-
denadas está contenido en la región de recorte vi-
sible de este Objeto G r a p h i c s
MeasureCharacterRanges Obtiene una matriz de objetos R e g i ó n , cada uno
de los cuales delimita un intervalo de posiciones
de caracteres dentro de la cadena especificada
MeasureString
Mide la cadena especificada al dibujarla con el ob-
jeto F o n t especificado
MultipIyTransform
Multiplica la transformación universal de este obje-
to G r a p h i c s y especificada en el objeto M a t r i z
ReleaseHdc
Libera un identificador de contexto de dispositivo
obtenido mediante una llamada anterior al método
G e t H d c de este objeto G r a p h i c s
ResetClip
Restablece la región de recorte de este objeto
G r a p h i c s en una región infinita
ResetTransform Restablece la matriz de transformación universal
de este objeto G r a p h i c s en la matriz de identida-
des
Restore
Restaura como estado de este objeto G r a p h i c s el
estado representado por un objeto G r a p h i c s S t a t e
RotateTransform
Aplica la rotación especificada a la matriz de trans-
formación de este Objeto G r a p h i c s
Save
Guarda el estado actual de este objeto G r a p h i c s
e identifica el estado guardado con un objeto
GraphicsState
ScaleTransform
Aplica la operación de cambio de escala especifi-
cada a la matriz de transformación de este objeto
G r a p h i c s , anteponiéndola a esta última
Método Descripción
SetClip
Establece la región de recorte de este objeto
G r a p h i c s en la propiedad c l i p del objeto
G r a p h i c s especificado
T ransformPoints
Transforma una matriz de puntos de un espacio de
coordenadas a otro utilizando las transformaciones
universal y de página actuales de este objeto
Graphics
TranslateClip
Convierte la región de recorte de este objeto
G r a p h i c s usando las cantidades especificadas en
las direcciones horizontal y vertical
TranslateTransform Antepone la conversión especificada a la matriz de
transformación de este objeto G r a p h i c s
Graphics g;
g = this.CreateGraphics() ;
Pen p;
p = new Pen(Color.Red, 50);
+ +
pasa las coordenadas x1, yl, x2, y2 /
Icon i ;
i = new Icon(@"C:\Desktop.ico");
// dibuje un rectángulo
Pen p2 ;
p2 = new Pen(Color.PapayaWhip, 7);
namespace HenderJPG
{
public class Form1 : System.Windows.Forms.Form
{
prívate System.ComponentModel.Container
components = null;
// declara la variable de imagen
prívate Image img;
public Form1 ()
{
InitializeComponent();
// abre la imagen
img = new Bitmap(@"C:\money.jpg" );
//
}
protected override void Dispose ( bool disposmg )
{
if(disposing)
(
// Llama a DISPOSE sobre el objeto Img
img.Dispose();
//
if (components != null)
{
components.Dispose();
}
}
base.Dispose(disposing);
}
static void Main()
{
Application.Run(new Form1());
}
Figura 26.2. Resultado del listado 26.2 usando un JPG con GDI +
Propiedad Descripción
Método Descripción
Figura 26.4. Dibujo de una elipse usando las propiedades Color y DashStyle
La tabla 26.5 recoge los valores posibles de la enumeración usada para esta -
blecer el estilo de la linea discontinua de la elipse.
Tabla 26.5. Enumeración DashStyle
Valor Descripción
// establece la flecha
p.StartCap = LineCap.ArrowAnchor
p.EndCap = LineCap.ArrowAnchor
g.DrawLine (p, 30, 30, Width-50, 30);
// extremos redondeados
p.StartCap = LineCap.Round;
p.EndCap = LineCap.Round;
g.DrawLine(p, 30, 80, Width-50, 80);
// delimitador redondo
p.StartCap = LineCap.RoundAnchor;
p.EndCap = LineCap.RoundAnchor;
g.DrawLrne (p, 30, 120, Width-50, 120);
// triangulo
p.StartCap = LineCap.Tríangle;
p.EndCap = LineCap.Triangle;
g.DrawLine (p, 3O, 150, Width-5O , 150);
// delimitador cuadrado
p.StartCap = LineCap.SquareAnchor ;
p.EndCap = LineCap.SquareAnchor
g.DrawLine (p, 30, 190, Width-50, 190);
)
La figura 26.5 muestra el resultado de la ejecución del codigo anterior usando
la enumeración L i n e C a p .
HatchBrush hb =
new HatchBrush
(HatchStyle.Plaid,
Color.AntiqueWhite ,Color.Black);
g.FillEllipse( hb, 30, 30, Width-50, 30);
HatchBrush hb3 =
new HatchBrush
(HatchStyle.DashedHorizontal,
Color.AntiqueWhite ,Color.Black);
g.FillElli p se ( hb 3, 30, 130, Width-50,
30);
HatchBrush hb4 =
new HatchBrush
(Hatch.Style.ZigZag,
Color.AntiqueWhite ,Color.Black);
g.FillEllipse (hb 4, 30, 180, Width-50, 30);
Si se ejecuta el código anterior se produce una imagen como la que aparece
en la figura 26.7.
La tabla 26.6 describe cada uno de los tipos de lápiz disponibles en la enume -
ración P e n T y p e que puede usar con la clase B r u s h . Ya hemos visto H a t c h F i l l
y S o l i d C o l o r en funcionamiento.
Basándose en sus descripciones, probablemente pueda imaginar los otros tipos
de pincel sin verlos en funcionamiento.
Miembro Descripción
Figura 26.8. Cómo usar el método DrawString y la clase Font para producir texto
Propiedad Descripción
Resumen
GDI + ofrece una consistente matriz de clases que le permite* escribir cualquier
tipo de soporte gráfico en sus aplicaciones. Este capitulo presento una vista gene -
ral de las funciones de GDI+. pero puede hacer muchas más cosas con los espa -
cios de nombre System.Drawing y System.Drawing.Drawing2D que
pueden explicarse en un sólo capítulo.
Para manipular o crear gráficos usando GDI+. primero debe crear un objeto
Graphics que le proporciona una superficie en la que pintar. Una vez creado
el objeto Graphics, puede usar lápices, pinceles, mapas de bits o fuentes para
procesar el tipo de imagen deseado.
27 Cómo
construir
servicios Web
Figura 27.1. Los servicios Web constan dos partes: un consumidor y un creador
Los servicios Web usan un conjunto de estándares para definir cómo se realiza
la interacción entre cliente y servidor. Estos estándares definen el mecanismo de
transporte que se va a usar y el formato y contenido de la interacción. En el nivel
de transporte, se usa el omnipresente protocolo de Internet HTTP El servidor y el
cliente se comunican entre sí mediante mensajes XML. El contenido de estos
mensajes también esta estandarizado y debe cumplir las reglas de SOAP (mas
adelante se explicaran estas reglas). La naturaleza de los servicios disponibles en
un servicio Web puede ser descrita en un archivo XML cuyo contenido cumpla
con las reglas del Lenguaje de descripción de servicio W eb (WSDL). Por ultimo,
un cliente puede descubrir dinámicamente qué servicios Web estás expuestos en
un servidor recuperando el archivo XML cuyo contenido cumple con las reglas de
DISCO
Observe que aún no se ha mencionado ninguna tecnología específica de c om-
pañías. En ninguna parte se ha presupuesto el sistema operativo del cliente o el
servidor. el lenguaje de programación usado para escribir el componente del ser -
vidor o el mecanismo usado para invocar el componente del servidor. Estas elec -
ciones no tienen importancia para un servicio Web. Un cliente escrito en C# que
se ejecuta en Windows XP. por ejemplo, puede invocar un servicio Web escrito en
Java ejecutándose en Sun Solaris. De hecho, un cliente no tiene modo de saber
que tecnologías se usan para mostrar el servicio Web.
Las implicaciones de los serv icios Web son enormes. Microsoft comenzó la
programación basada en componentes con la introducción de los cont roles OLE
(OCX), una versión depurada de los innovadores conceptos presentados por los
Controles de Visual Basic (VBX). Los OCX se basan en el Modelo de objetos de
componentes de Microsoft (COM) y funciona perfectamente. Sin embargo. COM
y su contrapartida distribuida, el Modelo de objetos de componentes distribuido
de Microsoft (DCOM). tienen un alcance limitado. Aparte de la familia de siste -
mas operativos Windows, muy pocos sistemas admiten COM/DCOM. Ademas,
aunque la familia Windows tiene una amplia ac eptación para aplicaciones de
escritorio, en la modalidad de servidor se usan una gran variedad de sistemas
operativos. Por ejemplo, muchas variedades del sistema operativo UNIX, como
Solaris y Linux, tienen una importante presencia Los servicios Web elimi nan la
necesidad de decidir el sistema operativo que se ejecuta en el servidor al integrar
dos aplicaciones Web. Al usar servicios Web puede ensamblar aplicaciones Web
usando componentes de otros servidores. Por ejemplo, puede usar un servicio
Web de una compañía de tarjetas de para validar tarjetas de crédito, un servicio
Web de una compañía distribuidora para determinar los gastos de envío v asi
sucesivamente. Esta es la esencia y la oferta de los servicios Web: la siguiente
generación de programación basada en componentes para la siguiente generación
de aplicaciones distribuidas.
namespace MyFirstWebService
{
public class GameWS
{
// Ejemplo de un sencillo juego
// El juego de ejemplo devuelve la cadena "Sony, you
// lose!"
// Para probar este juego, pulse F5
publica string Play (string opponentName)
(
return "Sorry " + opponentName + ", you lose!";
}
}
}
El primer paso para convertir este fragmento de código en un servicio Web es
guardar el código en un nuevo archivo llamado GameWS.asmx. A continuación,
realice estos cuatro pasos:
l. Agregue un título que indique tres cosas: que el archivo contiene un servi -
cio Web. el lenguaje que usa y la clase que contiene la implementación:
<@ WebService Language="c#"
Class="MyFirstWebService.GameWS">
2. Agregue una directiva S y s t e m . W e b . S e r v i c e inmediatamente debaj o
del titulo del servicio Web:
using System.Web.Services;
using System.Web..Services ;
namespace MyFirstWebService
{
WebServíce (Name space-"http: //www.boutquín.com / GameWS/") ]
public class GameWS : System.Web.Services.WebService
{
// EJEMPLO DE SERVICIO WEB
// El método Play() devuelve la cadena "Sony, you lose!"
// Para comprobar este servicio web, pulse F5
[WebMethod]
public string Play(string opponentName)
{
return "Sony " + opponentName + ", you lose!";
}
}
}
WSDL describe las funciones que están expuestas (el formato que se muestra
es en realidad una simplificación del formato real, pero los conceptos siguen
siendo los mismos).
Puede llamar al servicio mediante una URL (en este caso. www22.
brinkster.com/boutquin/GameWS.asmx/Play?opponentName
=Pierre) o enviar un mensaje XML con el formato apropiado a la URL (me -
diante una instrucción post o get HTTP). Este mensaje XML puede ser un
mensaje SOAP como el siguiente:
<?xml version=" 1.0" encoding-" utf-8"?>
<soap:Envelope
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
< soap:Body>
<Play xmlns = "http://www.boutquin.com/GameWS/">
<opponentName>Pierre</opponentName>
</Play>
< /soap:Body>
</soap:Envelope>
Las siguientes secciones examinan las bases de SOAP y WSDL. tras lo cual
pasaremos a estudiar los detalles de la creación e invocación de servicios Web.
<"/service>
</definitions>
El primer atributo de una descripción de servicio define la posición desde la
que se puede llamar al servicio. Esto se describe en el elemento a d d r e s s dentro
de un elemento p o r t . (Elemento y atributo son términos de XML. como puede
ver en el apéndice.) El siguiente ejemplo muestra el atributo b i n d i n g :
<?xml versiom"1.0" encoding = "UTF-8"?>
•"definitions xmlns ='http://schemas.xmlsoap.org/wsdl/'>
</service>
</definitions)
< /message>
</message>
</soap:Header>
<soap:Body>
<MethodName>
<Param1Name>value1</Param1Name>
<Param2Name>value2</Param2Name>
<!--etc. -->
</MethodName>
</soap:Body.">
</soap:Envelope>
<faultcode>x00c</faultcode>
<faultstring>description<faultstring>
<runcode>Yes<runcode>
< / s o a p : Fault>
Cómo crear servicios Web con Visual Studio
.NET
La operación de crear (y. en la siguiente sección, acceder a) un servicio Web
usando Visual Studio NET es aparentemente simple. Ni siquiera sera consciente
de estar usando XML.
El servicio Web que crearemos en esta sección simplemente recuperara y mos -
trara una lista de libros. Esto simula la función de catálogo de una pagina Web
comercial, aunque en el mundo real probablemente desearía introducir categorías
para evitar que se devolviera una enorme lista de libros. Dado que el objetivo de
este capítulo es mostrar los elementos necesarios para construir un servicio Web.
el aspecto comercial ha sido simplificado.
El ejemplo que creamos aquí usa una tabla y un procedimiento almacenado. El
codigo para crearlos aparece en el siguiente ejemplo (quizás también quiera colo -
car algunos datos de muestra en la tabla Books):
Ya esta preparado para construir un sencillo servicio Web que devuelve una
lista de libros usando el procedimiento almacenado:
}
}
// EJEMPLO DE SERVICIO WEB
[WebMethod]
public DataSet getList()
{
// Establece las cadenas de instrucción SQL
string strSQLSelect ="[pc_getBooks]";
try
{
// Establezca la conexión a la base de datos
databaseConnection.Open();
}
catch (Exception e)
{
Console.WriteLine ("****** Caught an exception:\n{0) ",
e.Message);
}
fina11y
{
databaseConnection.Close();
}
return resultDataSet;
}
La propiedad oleDbConnectionString contiene la cadena de co-
nexión a la base de datos SQL Server. En código de producción, puede
usar una cuenta con derechos de seguridad apropiados (y una contraseña)
en lugar de la todopoderosa cuenta "sa". El método getList() abre una
conexión a la base de datos SQL Server y recupera un conjunto de datos
que contiene la lista de libros invocando al comando pc_getBooks.
Como puede ver. usar ADO.NET es muy sencillo.
8. !Eso es todo! También puede agregar a la clase una declaración de espacio
de nombre, como muestra el siguiente ejemplo:
[WebService(Namespace="http://microsoft.com/webservices/")]
public class Books : System.Web.Services.WebService
Resumen
En este capítulo ha estudiado los estándares XML que hay tras los servicios
Web. Vimos las dos tecnologías opcionales que toman parte en la definición de
servicios Web: UDDI para el mecanismo de descubrimiento y WSDL para la
descripción de servicio. También estudiamos el formato de mensaje que se usa
durante la invocación real de un servicio Web: SOAP. Creamos un sencillo servi -
cio Web usando Visual Studio. Por último, creamos un segundo proyecto que
usaba el servicio Web que había construido anteriormente.
28 Cómo usar
C# en ASP.NET
Money
Se usa para almacenar datos monetarios que requie-
ren gran precisión.
TRUCO: Cada tabla debe tener al menos una columna que identifica
unívocamente una fila (a la que llamaremos registró) de la tabla. Esta
columna es la clave primaria de la tabla. Por ejemplo, la columna ProductID
de una tabla Products identifica cada fila unívocamente y por tanto es la
clave primaria. No puede haber dos valores iguales en una clave primaria.
TRUCO: También puede usar SQL Server Enterprise Manager para crear
una base de datos y tablas. SQL Server Enterprise Manager proporciona
una interfaz gráfica para realizar los mismos pasos que realizamos median-
te las instrucciones T-SQL.
Para crear una base de datos o una tabla mediante T -SQL. se emplea la ins-
trucción Create. Por ejemplo, para crear una base de datos Sales, escriba el
siguiente codigo en la ventana del analizador de consultas:
Tras crear la base de datos, puede agregarle tablas. Agregue la tabla Products
a la base de datos Sales mediante la siguiente sintaxis:
• Agregar un registro: Para agregar una nueva fila a una tabla SQL Server,
use la instrucción Insert. Por ejemplo, para agregar un nuevo registro a
la tabla Products, se usa la siguiente instrucción:
Insert luto Products (ProductID, ProductName, UnitPrice,
QtyAvailabio)
Values ('P001' , 'Baby Food', 2.5, 12000)
El anterior código actualiza el precio por unidad del registro cuya identifi-
cación de producto es P010 hasta 75.
• Eliminar un registro: Para eliminar un registro de una tabla, use la ins -
trucción De le te. Por ejemplo, para eliminar un registro de la tabla
Products con la identificación de producto P011. puede especificar la si -
guiente instrucción:
Delete From Products where ProductID="P011”
Para ejecutar el procedimiento anterior para que muestre el precio del produc -
to con la identificación ID P010. use el siguiente código:
Ejecute ProductPrice "P01Ü"
Una vez que Visual Studio NET ha creado el servicio Web. puede configurar -
lo para que gestione datos en el ser vidor. Esto lo h aremos en l a si guiente
sección.
try
{
ProductName = ProductName.Trim();
if(Price < 0)
return "Please specify a valid value for price";
if (Qty < 0)
return "Please specif y a valid value for quantity";
SqlConnection1.open();
SqlCommand1. CommandText="INSERT INTO Products (ProductID,
ProductName, UnitPrice, QtyAvailable) VALUES ('" + PID +
"', '" + ProductName + "', '" + Price +
"', '" + Qty
+
"')";
sqlCommand1.ExecuteNonQuery();
sqlConnection1.Close();
return "Record updated successfully";
}
catch (Exception e)
{
return e.Message;
}
{
El codigo anterior usa el control sqlConnection1 para agregar un regis-
tro a la tabla Products. Al agregar registros a un servicio Web. se usan los
valores que la función AddProduct proporciona como parámetros.
Mientras codificad método Add Product del servicio Web. puede codificar
otros métodos para recuperar detalles de producto de la base de datos Products o
realizar otras acciones personalizadas. Para probar el servicio Web tras agregarle
los métodos, siga estos pasos:
1. Seleccione Generar>Generar solución para generar el servicio Web.
2. Para empezar a depurar el servicio Web. seleccione la opcion de menú
Depurar >iniciar.
El servicio Web se abre en Internet Explorer. El primer cuadro de diálogo
muestra los métodos que codifico para su servicio Web.
P ar a co mp ro b a r c i mé to d o AddProduct. s i ga e s to s p a so s :
1. Ha ga cli c e n Ad dP ro d u ct e n e l c uad r o d e d i álo go Ser v icio W eb S er vi ce 1 .
2. Se ab rir á e l c u ad r o d e d ialo go AddProduct. co m o mu e stra la fi g ur a 28.4.
En e ste c uad ro d e d ia l o go , p u ed e i n vo c ar l a fu nc ió n A d d P r o d u c t tras
p r o p o rcio nar lo s p ar a me tr o s r eq uer id o s p ar a p ro b ar s u re s ul tad o
Para crear un cliente de servicio Web solo ti ene que seguir estos pasos:
1 Cree un nuevo proyecto de Aplicación Web ASP.NET.
2. Agregue una referencia Web a la aplicación Web.
3. Implemente los métodos del ser vicio Web e n la aplicación Web.
La si guiente sección estudia detenidamente cada uno de estos pasos.
P ar a d i se ñ ar e l fo r mu l ar io , p u ed e a gr e ga r lo s co ntro le s q ue ap a rec e n e n l a
tab l a 2 8 .2 .
T r as d i se ñar el W eb Fo r m. es cr ib a e l co d i go p a ra el e ve nto C l i c k d el b o to n
Submit. C ua nd o e l u s u ar io h a ga cl ic e n el b o t o n Submit. el co d i go d el e ve nto
Cl ic k li ara lo si g u ie n te:
1. Creara una instancia del servicio Web.
2. Llamara al método AddProduct del ser vicio Web. pasando como
para metros al WebFor m los valores introducidos por el usuario.
Tipo de control
Propiedades cambiadas
Label
Se ha agregado seis etiquetas: el encabezamiento
principal de la página, Product ID, Product Ñame,
Price, Quantity y Message, respectivamente. Se ha
cambiado el Id. de la última etiqueta a LabelMessage
y se ha limitado su texto.
TextBox ID=PID
TextBox ID=ProductÑame
TextBox ID=Price
TextBox ID=Quantity
Text=Submit
Figura 28.11. El cuadro de diálogo Agregar grupo de resultados del proyecto muestra
las opciones que ha seleccionado
8. Haga clic en Aceptar para cerrar el cuadro de dialogo Agregar grupo de
resultados del proyecto.
9. En la ventana del editor del sistema de archivos, haga clic con el boton
derecho del ratón en la Carpeta de aplicación Web. En el menú desple-
gable. seleccione la ventana Propiedades para abrir la ventana Propie-
dades.
10. Escriba OrderWebApplication como nombre del directorio virtual en la
propiedad VirtualDirectory y cierre la v entana Propiedades.
11. Seleccione la opción de menú Generar>Generar solución.
Se ha creado un archivo Microsoft Installer (MSI) para su provecto Puede
hacer doble clic en el archivo para instalar su aplicación en un equipo
Resumen
El papel de C# en ASP.NET es muy útil para los servicios y aplicaciones Web.
Puede crear un servicio Web mediante la sintaxis del lenguaje C# en ASP.NET.
También puede crear una aplicación Web que acceda al servicio Web y aproveche
su funcionalidad.
Cuando crea una aplicación en ASP.NET, primero diseña la base de datos que
debe usar la aplicación. A continuación, crea un nuevo provecto en ASP.NET
mediante la Aplicación Web de ASP.NET o las plantillas de proyecto servicio
Web ASP.NET.
Tras crear un proyecto en ASP.NET. use los controles de datos proporciona -
dos por Visual Studio NET para conectar con un origen de datos y utilizarlo.
Si ha creado un servicio Web. también puede crear una aplicación Web para
utilizar los métodos Web proporcionados por el servicio Web.
29 Cómo
construir
controles
personalizados
Figura 29.1. El asistente para agregar propiedades permite agregar una propieda d
a su proyecto
Métodos
Un método se define como una función que forma parte de una clase. Los
métodos se implementan del mismo modo que en muchos otros lenguajes, como
C++. C. VB y similares. Los métodos suelen usarse para iniciar una acción den -
tro de un control. Un ejemplo podría ser el método S t a r t del control T i m e r .
Puede agregar un método a un control de una de estas dos formas. Puede usar
el asistente para métodos de C#. al que se puede acceder mediante el Explorador
de soluciones mientras se está en la Vista de clases o simplemente agregando
una función pública al proyecto
El asistente para métodos de C#. que se muestra en la figura 29.2. permite
especificar el nombre del método, el tipo devuelto, el método de acceso y la infor -
mación de parámetro de una forma muy sencilla.
Figura 29.2. El asistente para métodos de C# crea la estructura básica del código
necesario para el método
Campos
Los campos son simples variables publicas definidas en un control La aplica -
ción contenedora puede asignar al campo un valor determinado y recuperarlo.
Como sólo son variables, no pueden tener fun ciones asociadas a ellos. Los cam-
pos no pueden realizar cálculos ni llamar a métodos cuando se les asigna un valor
o se recupera de ellos un valor.
Hay dos tipos de campos: de instancias y estáticos. Un campo es. por defecto,
un campo de instancia. Un campo de instancia puede contener sus propios datos
en cada objeto que lo contenga. Cuando un campo es declarado como estático,
contiene los mismos datos en todas las instancias del objeto. Un campo estático
no suele ser aconsejable porque otras instancias d el objeto pueden cambiar el
v alor y las instancias restantes no se percatarán de ello.
Los campos proporcionan elementos de código de bajo nivel pero también
están limitadas en términos de funcionalidad. Puede asignar y recuperar los valo -
res de un campo, pero al hacerlo no puede desencadenar otras acciones dentro del
control. Los campos son útiles cuando se necesita asignar parámetros de opera -
ción a un control.
Eventos
Para definir un evento, antes debe hacer una declaración de clase publica,
como la siguiente:
Public event EventHandler Expired;
Puede usar eventos con controles de Windows de uno de estos dos modos:
puede hacer clic en el icono Event en la ventana Propiedades y hacer doble
clic en el evento para agregar el controlador automáticamente o p uede crear un
controlador de eventos usted mismo.
Un evento básico se declara como un tipo EventHandler. pero tiene mu-
chos más tipos de evento entre los que elegir y cada uno tiene una firma única. El
parámetro de cada evento contiene una estructura System. EventArgs. Esta
estructura permite al control pasar información al evento, con lo que define mu -
chas aspectos, como qué desencadena el evento, qué control hace que se desenca -
dene el evento y aspecto similares. Antes de escoger un tipo de evento para un
control, asegúrese de que conoce los diferentes tipos, además de los parámetros
que pasa cada uno.
Aprender con un ejemplo
Empecemos creando un control temporizador de cuenta atrás. Este ejercicio le
permitirá estudiar los entresijos de la creación de controles.
}
}
9. Haga doble clic en el botón Start de su aplicación para que pueda empezar
a codificar. Agregue el código del listado 29.6 al evento Click del botón
Start
Listado 29.6. Inicia el control
Listado 29.9. Código creado para una biblioteca de clases por Visual Studio
using System;
ña mespace Temperature
{
///<summary>
/// Descripción breve de Classl.
/// </summary>
public class Classl
{
public Classl()
{
//
// TODO: Agregar aqui la lógica del constructor
//
}
}
}
Visual Studio ha creado un espacio de nombres, una clase y un constructor de
clase. La clase y el constructor de clase son p úblicos. Esto no es una
coincidencia.
Cualquier aplicación que use esta biblioteca necesita acceder tanto a la clase
como a los miembros específicos dentro de la clase.
1. Para empezar nuestro ejemplo, elimine la clase y el constructor de clase y
reemplácelos por el codigo del listado 29.10.
Listado 29.10. La clase y el constructor Calc
using System;
Resumen
NET p re se nt a a l g u na s i n no va cio ne s e n la fo r m a d e co n str u ir co n tro le s e xtr e -
mad a me n te i mp o r ta n te s. A med id a q u e c r ee co n tro le s p ar a s us p ro p i o s p ro ye cto s
o p ar a i mp l e me n tar e n l a W eb . ir a d e sc ub r ie nd o al g u no s a v a nce s e n la p ro gr a ma -
ció n. co mo e l o b j eto St a te, q ue le a ho r r a r á n gr a n d es ca nt id ad es d e t ie mp o .
Figura 29.8. DLLTest le enseña a usar un control de biblioteca de clases
30 Cómo
construir
aplicaciones
móviles
La red inalámbrica
Emuladores
Los emuladores permiten escribir aplicaciones para dispositivos y probarlas
sin tener que comprar realmente uno de los dispo sitivos. Hay emuladores para
telefonos móviles. PDA. equipos de escritorio y dispositivos intermedios. Los
siguientes apartados tratan sobre algunos de los emuladores más populares, que
puede descargar y empezar a escribir aplicaciones para ellos.
Nokia
En la página Web de Nokia (http://www.nokia.com). puede descar-
garse un emulador de teléfono Nokia para probar sus aplicaciones Web para
telefonos móviles. Esta descarga es de casi 22MB y requiere el entorno de ejecu -
ción Java, lo que añade otros 5MB a su descarga. El emulador Nokia actualmente
le permite elegir entre uno de los tres diferentes modelos Nokia que ofrece para
que realice sus pruebas. Esta aplicación es un valioso recurso para probar sus
aplicaciones.
Pocket PC
El emulador de Pocket PC se incluye en el Microsoft Embedded Devices Toolkit
Es un excelente emulador por si no posee un Pocket PC va que Pocket Internet
Exp lo rer ad mi t e mu c ha s ma s f u nc io nal id ad es q u e la ma yo r ía d e lo s d i sp o si ti vo s
mó v ile s d el merc ad o a ct ua l. T e n ga e n c u e nta q ue el ta ma ñ o re al d e e st e c o nj u nto
d e herr a mi e nt as e s s up er io r a lo s 3 0 0 MB . Si no p o see u na co n e xió n a I nte rne t d e
alt a ve lo c id ad , e s mej o r q ue us e Micr o so f t Mo b i l e E xp lo r er, d el q ue le h a b la mo s
en la s i g ui e nte s ecc ió n.
Microsoft Mobile Explorer
Micro so ft Mo b ile E xp lo r er e s u na d e sc ar ga d e s o lo 3 MB y s e u sa p ri n ci p al -
me n te p ar a i nt e grar se c o n V is u al St ud io . C u a nd o p rueb e s u s ap l ica cio n es, e s ta -
b lezc a M ME co mo s u na ve g ad o r p o r d e fec to .
ActiveForm = Form2;
En esta parte del codigo. deberá realizar algunas tareas. En primer lugar, debe
convertir en un valor entero el año que se introduce mediante la clase Convert
A continuación, dependiendo del tamaño de ese año (dos o cuatro dígitos), calcule
la edad en consecuencia, usando como año actual 2002.
lina vez. que se ha calculado la edad, asigne la edad y una cadena a su etiqueta
Current_age. Su aplicación ya es funcional, pero aunque ha asignado el
resultado a la etiqueta adecuada, todavía no se muestra la pantalla que contiene la
información. En la ultima linea del listado 30.2. asigne A c t i v e F o r m a F o r m 2 . " .
Esto carga Form2 en el navegador del usuario para mostrar los resultados.
Puede ejecutar esta aplicación pulsando F5 y si todo el codigo se ha realizado
correctamente, se abrirá el navegador Web que tenga por defecto. Para ver esta
aplicación en un dispositivo móvil, ejecute su emulador preferido e introduzca la
URL adecuada. La figura 30.3 muestra la aplicación ejecutándose en un emulador
de Pocket PC
Como puede ver. la pagina presenta un aspecto muy parecido al que mostraba
en los anteriores emuladores, excepto en algunos detalles de desplazamiento entre
opciones. Como los Pocket PC son dispositivos en los que se señala y se hace clic,
el desplazamiento se realiza mucho mas suavemente. El telefono móvil, por otra
parte, realiza todos sus desplazamientos entre opciones usando sus. aproximada -
mente. 20 botones. Es decir, no puede simplemente hacer clic en un botón o un
vínculo para avanzar; debe usar los botones para desplazarse hacia arriba o abajo
y seleccionar los vínculos antes de poder seguirlos .
Funciones de los dispositivos móviles
La cl as e System.Web.Mobile.MobileCapabilities co ntie n e va -
r ia s d o ce n as d e p ro p ied a d es q ue se u sa n p ar a d e t ecta r la s fu nc io ne s d e u n d isp o -
si ti vo mó vi l. P o r ej e mp lo , p ued e co n s ul tar la reso l uc ió n d e p a nt al la d e u n
d isp o s iti v o , co mp ro b ar s i e s d e co lo r o d e b la nco y n e gro y co n s u lta r s i e l d isp o -
si ti vo es cap az d e ma nd ar co r r eo , p o r no mb r ar s o lo al g u n a s.
Al cr ear ap lic ac io ne s W e b mó vi le s, e s i mp o rt a nte te ner e n c ue nt a lo s d i st i n to s
tip o s d e d isp o s it i vo q u e p u ed e n a c ced er a la ap li cac ió n. A d i fer e nc i a d e lo s
na v e gad o re s co n ve n cio n ale s, c u ya f u nc io na lid ad vari a mu y l i ger a me n te, p ued e
hab er e no r me s d i fer e n ci as e ntr e ver u na p á g i na e n u n te lé fo no mó vi l o . p o r ej e m -
p lo . e n u n P D A q u e ut il iza W i nd o ws CE . D eb e ase g u rar se d e q ue s u s p ag i na s
ser á n p ro ce s ad a s ad e c ua d a me nt e p o r c ad a d isp o s iti vo .
E mp ie ce us a nd o la c la s e D e v i c e p ar a co mp ro b ar el n u mero d e cara ct ere s
má x i mo s, ho r izo n tal v v er ti ca l me nt e, q ue ad mi t e la p a nta ll a d e s u d isp o si ti vo
mó v il. Ab ra la ap li cac i ó n Y o u r A g e q ue cr eó e n el li st ad o 3 0 .2 y a gre g ue d o s
co n tr o l es L a b e l a F o r m 2 . Ca mb ie la s p r o p i ed a d es N a m e d e e s ta s et iq ue ta s a
lb l H e i g h t y l b l W i d t h .
Ah o ra d eb e mo d i fi car el có d i go f u e nte o r i g i na l p a ra l le nar e st as et iq ue ta s d es -
p ué s d e q ue se mu e str e F o r m é . El li stad o 3 0 .3 co n tie n e el co d i go q u e d eb e añ ad ir
( en ne gri ta) p ara q u e l a n ue v a f u nc io na lid ad s ur ta e fec to .
íf (iDOB 1000)
YearsOld = 2002 - iDOB;
else
YearsOld = 100 - iDOB + 2 ;
Si intenta ver esta misma pagina con el emulador de teléfono móvil Nokia,
recibirá un mensaje de error indicando que no se pudo cargar la página. Esto
obviamente es debido a que la página no es de un tipo que se pueda ver en una
pantalla en blanco y negro de un tamaño tan pequeño.
Sin embargo, se puede solventar este problema. Puede convertir su imagen
JPG en una imagen de mapa de bits de dos colores (blanco y negro), también
conocido como de mapa de bits inalámbrico (WBMP). que el dispositivo móvil
puede procesar. En el evento Page Load del Form1. puede comprobar la
propiedad Device.PreferredRenderingType para ver qué imagen debe
ser cargada. Si esta propiedad devuelve wmll1. debe ajustar la propiedad
ImageUrl del control Image a WBMP picture; en caso contrario, puede
mostrar el original.
Estos 11 elementos se agregan a su lista. Por tanto, deberia de ver siete ele-
mentos en la primera pagina, junto a un vínculo Next a una segunda página que
contiene cuatro elementos. Ejecute la aplicación en el dispositivo móvil preferido.
Como se esperaba, sus primeros siete elementos aparecen en la primera página,
junto a un vínculo hacia la siguiente pagina, como muestra la figura 30.10.
Figura 30.10. Los siete primeros elementos de la lista
Ensamblados
Los ensamblados pueden contener código, recursos o una combinación de am -
bos. El código de un ensamblado debe contener las instrucciones reales del Len -
guaje intermedio de Microsoft (MSIL) que pueden ser ejecutadas por el Entorno
común de ejecución (CLR) y un manifiesto que describa el contenido del código.
Los manifiestos contienen tipos y más información descriptiva que describen el
código al CLR. Los ensamblados también forman los límites del código encerrado
entre ellos. Los ensamblados forman los límites de tipos, en los q ue cualquier tipo
que pueda usarse en cualquier código .NET procede de un sólo ensamblado y los
tipos con el mismo nombre procedentes de diferentes ensamblados son. de hecho,
tipos diferentes. Los ensamblados también forman un límite de seguridad, por
medio del cual todo el codigo del ensamblado tiene el mismo conjunto de informa -
ción de seguridad, restricciones y permisos.
Los ensamblados se empaquetan mediante el archivo ejecutable portable de
Win32 y pueden ser empaquetados como DLL o EXE. Cualquier co digo generado
por un compilador compatible con el CLR y compilado en un ejecutable de conso -
la. un ejecutable Windows o una biblioteca es empaquetado en un ensamblado.
Este paquete forma una unidad de implementación para un conjunto de tipos de
un ensamblado. No piense que solo se considera ensamblado al codigo NET
basado en DEL. Cualquier paquete de codigo NET. recurso o metadato que tenga
como objetivo un ejecutable o una biblioteca es un ensamblado, incluso si el
paquete tiene la forma de un ejecutable. Las aplicaciones WinForms. por ejemplo,
son ensamblados NET válidos, igual que las bibliotecas de clase basadas en
DLL. Tenga en cuenta que el compilador de C# también puede generar módulos,
pero esos módulos no son ensamblados. Los módulos son fragmentos de codigo (y
probablemente recursos) compilados que se convertirán en un ensamblado mas
tarde. Los módulos pueden contener MSIL y metadatos que describen los tipos
que se encontraron en el modulo, pero no contienen un manifiesto. El CLR no
puede abrir ni ejecutar módulos y por tanto, éstos no pueden ser considerados
ensamblados.
using System;
using System.Reflection;
EntryAssembly = Assembly.GetEntryAssembly();
foreach(AssemblyName Name in
EntryAssembiy.GetReferencedAssemblies())
Console.Writelane("Name: {0}" , Name.ToString();
}
}
El li st ad o 3 1 .1 p re se n ta mu c ho s co nc ep to s i mp o r ta nt es . E n p ri me r l u gar, p re -
se n ta u na cl a se NET ll a mad a As s e mb l y. q ue s e i nc l u ye e n e l e sp a cio d e no m -
b r es S ys t e m. Re flec tio n d e.NET . L a cl a se A s se mb l y e s l a c la se med ia n te
la c u al c ualq u ier co d i go NET p u ed e e xa mi n ar y trab aj ar co n lo s co nte n i d o s d e u n
en sa mb lad o NET . S i n ece si ta tr ab aj ar co n u n en sa mb lad o NET . d eb e u sa r la
cla se As s e mb l y p ara e xa mi n ar lo s co nt e nid o s d el en s a mb l ad o .
El s e g u nd o co nc ep to i m p o r ta nt e d e l li s tad o 3 1 . 1 es e l de ensamblado de entra-
da. El e n sa mb lad o d e e n tr ad a e s e l e n sa mb lad o q ue s e ej ec u ta e n p r i mer l u gar e n
el p r o c e so . P ara ej ec u ta b le s, co mo e l ej ec u tab le d e co n so l a cr ead o p o r e l l is tad o
3 1 . E e l e n sa mb lad o d e en tr ad a e s el e ns a mb l ad o q u e co n ti e ne e l p u n to d e e nt rad a
d e la f u nc ió n. No r m a l m en te, e l p u nto d e e n tr ad a recib e el no mb r e Ma i n() e n lo s
en sa mb lad o s b a s ad o s e n ej ecu tab le s, a u nq u e e n C# se p ued e ca mb iar me d ia nt e el
ar g u me nto / ma i n y e sp e ci fi ca nd o o tr o p u nto d e en trad a p ara el e n sa mb lad o . E l
acce so a l e ns a mb l ad o d e en tr ad a se r e al iz a me d ia nt e u n mé to d o e st át i co d e la
cla se As s e mb l y l la ma d o Get E ntr y As s e mb l y( ). Es te méto d o d e v u el ve u n a
in s ta nc ia d e u n o b j eto As se mb l y q ue h ace r e fer e nc ia al e ns a mb l ad o d e e nt rad a.
El t erce r co nc ep to i mp o r ta nt e d e l l i stad o 3 1 .1 es el d e q ue u n e n sa m b lad o
p ued e co nt e ner e n sa mb l ad o s a lo s q ue se ha ce r efere nc ia. La i n fo r ma c i ó n d e lo s
en sa mb lad o s a lo s q u e s e ha ce r e fer e nci a se o b t ie ne med ia nt e u n a ll a m ad a a u n
mé to d o d e e n sa mb la d o ll a mad o Ge tR e fer en ced As se mb li e s(). E s te mé -
to d o d e v ue l ve u na ma tr iz d e o b j eto s d e u n a cl ase ll a mad a As s e mb l yN a me. Lo s
o b j eto s d e As s e mb l yN a me d es cr ib e n co mp let a me n te el no mb r e d e u n e n sa m -
b lad o o se p ued e n co n v e r tir e n u na s e nc il la cad e na med ia n te e l co no cid o mé to d o
T o Str i n g(). Ob te ner u n a r ep r e se n ta ció n d e ca d en a d e u n no mb re d e en s a m-
b lad o fa ci li ta e l q ue la s ap li cac io ne s mu e s tr e n i n fo r ma ció n d el no mb re d e l e n sa m -
b lad o e n la s i nt er fac es d e u s u ar io .
Co n e s ta i n fo r ma ció n, r es u lta s e nci llo ad i v i nar lo q ue ha ce e l l i st ad o 3 1 .1 . El
có d i go o b ti e ne u na re fe r en ci a a l e n sa mb lad o d e e ntr ad a y e n vía lo s no mb r es d e
lo s e ns a mb l ad o s a lo s q ue se h ace r e fer e nci a a l a co nso la. S i s e co mp ila y ej ec u ta
el có d i go d e l l i st ad o 3 1 . 1 , se e n ví a l a si g u ie nt e i n fo r ma ció n a la co n so la :
Name: mscorlib, Version=1.0.3300.0, Culture=neutral,
PublicKeyToken = b77a5c561934e089
using System;
using System.Reflection;
EntryAssembly = Assembly.GetEntryAssembly();
Console.WriteLine ("Location:{0}" ,
EntryAssembly.Location);
Console.WriteLine("Code Base: {0}",
EntryAssembly.CodeBase);
Console.WriteLine("Escaped Code Base: {0}",
EntryAssembly.EscapedCodeBase);
Console.WriteLine ("Loaded from GAC : {0}",
EntryAssembly.GlobalAssemblyCache);
}
EntryAssembly = Assembly.GetEntryAssembly();
if(EntryAssembly.EntryPoint == null)
Console.WriteLine("The assembly has no entry point.");
else
Console.WriteLine(EntryAssembly.EntryPoint.ToString());
}
}
Si se compila y ejecuta el listado 31.3 se envía la siguiente información a la
consola:
Void Main(System.String[])
using System;
using System.Reflection;
using System.IO;
I Loading Assembly.System.Xml
5c96l934e089/system.xml.dll
Loaded from GAC: True
using System;
using System.Reflection;
La i ma g e n n at i va se co l o ca e n u n a c ac he d e i má ge n es nat i va s p ara e ns a mb la -
d o s T en g a e n c ue nt a q u e n g e n, d eb e ej ec u tar se en el d i sp o s it i vo q ue ej ec uta el
co d i go ge n erad o . P o r ej e mp lo , no p ued e co n str u ir e n sa mb lad o s co mo p a rte d e s u
p r o ce so d e co mp i lac ió n , ej ec ut ar n q e n so b r e e so s e n sa mb lad o s y e n v i ar e sa s
i má ge n es nat i va s a s u s c lie n te s.
El eq u ip o e n e l q u e se c o mp ila p ued e p er f ec ta m en te te n er u na CP U d i fe ren te
d e l a d e lo s eq uip o s d e s u s c li e nte s y n e j e n gen e ra co d i go p a ra la CP U e n la q ue
n ge n s e e s ta ej ec uta nd o . S i p ar a s us c lie n te s es i mp o r ta nt e t e ner i má g e ne s n a -
ti va s p ara s u s e n sa mb la d o s, d eb e ej ec u tar n q e n en lo s eq uip o s d e s us cli e nte s
co mo p ar te d e l p ro c e so d e i n st ala ció n.
T a mb i é n e s i mp o r t a nt e te ner e n c u e nt a q ue lo s en sa mb lad o s NET o ri g i na le s
d eb e n e s tar d i sp o nib le s e n to d o mo me nto , a u nq u e el co d i go n at i vo e s te d i s p o n ib le
en la cac h e d e i ma g e n n ati v a.
Fa s i má g e ne s na ti va s so n ar c hi vo s Ej ec u tab l e p o rtab l es (P E) W i n3 2 e st ánd ar
y car ece n d e lo s me tad at o s e xi st e nt e s e n u n e n sa mb lad o NET . S i el co d i g o car g a
s u e n sa mb lad o d e i ma g e n n at i va y ej ec u ta có d i g o q u e o b l i ga a NET Fr a me wo r k
a e xa mi n ar me tad ato s ( p o r ej e mp lo , u sa nd o r e fle xió n p ara o b t e ner i n fo r mació n
p ar a e l e n sa mb lad o ), e n t o nc es el e n sa mb lad o NE T o rig i nal d eb e e sta r d i s p o n ib le
p ar a q u e el CE R p ued a co n s ul tar s us me tad ato s. Eo s me t ad ato s no p u ed e n ser
tr a n sp o rt ad o s j u n to a la i ma ge n n at i va.
Resumen
En este capítulo, se ha examinado el concepto de ensamblado NET desde la
perspectiva de las aplicaciones de C# que pueden acceder a la información de los
ensamblados. El acceso a la información de los ensamblados se realiza mediante
la clase Assembly. La clase Assembly muestra la información de nombre
para el ensamblado y permite que los ensamblados se carguen dinámic amente.
Los tipos gestionados por el ensamblado pueden ser creados al instante.
Puede aplicar los conceptos mostrados en este capítulo en la construcción de
potentes aplicaciones NET. Algunas de las herramientas que incorpora NET
Framework. como la herramienta ILDASM. usan una combinación de métodos de
la clase Assembly y otras clases en el espacio de nombres System.
Reflection para proporcionar una vista con todos los detalles de los ensam -
blados ya compilados. Se puede obtener una gran cantidad de inform ación de los
ensamblados, usando los métodos de la clase Assembly y otras clases de re-
flexión. aunque el código fuente usado para construir el ensamblado no esté dis -
ponible.
32 Reflexión
using System;
using System.Reflection;
class NameType
{
public static void Main()
{
Type t = Type.GetType("System.String");
Console.WriteLine("Name:{0}" ,t.Name);
Console.WriteLine("Underlying System Type :
{0}" ,t.UnderlyingSystemType);
Console.WriteLine("Is Class : {0} ",t.IsClass) ;
}
}
La f i g ura 3 2 .1 i nd ica q ue S y s t e m . S t r i n g es el tip o d e si st e ma b a se y q ue
el o b j eto e s. si n l u gar a d ud a s, u n a c la se.
Se es tara p re g u n ta nd o q ué uti lid ad t ie n e e st a i n fo r ma ció n . I ma g i ne q u e e sta
cr ea nd o u n a ap li cac ió n q ue nec es it a ge n er ar i n str u ccio n e s i n s e r t p ara in tro d u -
cir i n fo r ma ció n e n S Q L S er ver . E scr ib ir u na g ran c a nt id ad d e i n fo r m ació n re -
q ui er e u na gr a n ca nt id a d d e tie mp o . U sa nd o la refle x ió n y la c la se T y p e . p ued e
ex a mi n ar e l t ip o s ub ya ce nt e d e cad a fr a g me n to d e i n fo r ma c ió n q ue q u ier a i n ser tar
en SQ L. Ser ver y d e scr i b ir e s to s t ip o s a u n t ip o d e d a to s SQ L Ser ver va l id o E s to
si mp li f ic a mu c ho el p r o ceso d e ge ne r ar me d ia n t e p ro g ra ma ció n la s i n s tr uc cio ne s
i n s e r t q ue n ece s ita .
using System;
using System.Reflection;
class InstanceType
{
public static void Main()
{
String myVar = "Brian Patterson";
Type t = myVar.GetType();
Console.WriteLine("Name : {0}",t.Name);
Concole.WriteLine("Underlying System Type :
{0} ", t.UnderlyingSystemType);
Console.WriteLine("Is Class : {0}", t.IsClass);
I
E n e s te e j e m p l o , e n l u g a r d e e s p e c i f i c a r qu e qu i e r e v e r l a i n f o r m a c i ó n d e ti p o
d e S ystem.String. s e c r e a u n a i n s t a n c i a d e u n a v a r i a b l e d e c a d e n a s o b r e l a
qu e a c o n t i n u a c i ó n , l l a m a a l m é to d o G e tT y p e ( ) . L a i n f o r m a c i ó n o b te n i d a
a qu í e s l a m i s m a qu e l a q u e s e o b tu v o e n e l e j e m p l o a n te r i o r , l a d i f e r e n c i a e s ta e n
qu e n o ti e n e qu e s a b e r e l ti p o a n te s d e ti e m p o S i m p l e m e n te l l a m e a l m é to d o
G e tT y p o ( ) a s í g n e l o a u n o b j e to T y p e . a l qu e p u e d e c o n s u l ta r e l n o m b r e , e l
ti p o d e s i s te m a s u b y a c e n te y s i m i l a r e s .
usinq System;
using System.Reflection;
usinq System.Diagnostics;
class AssemType
T y p e [ ] types = a . G e t T y p e s ( ) ;
f o reach (Type t in t y p e s )
{
Conso le. Wr it eLine( "\ nT ype : {0}", t . FullName) ;
Console.WriteLine("\tBase class :
{0}", t.BaseType.FullName);
}
}
}
E l c o d i go a n t e r i o r p r e s e n t a a l gu n o s e l e m e n t o s n u e vo s . E l t i p o p r o c e s s s se
u s a p a r a e x a mi n a r p r o c e s o s q u e s e e s t á n e j e c u t a n d o . E n e s t e c o n t e xt o , s e e m p l e a
p ara co n se g uir el no mb r e d e s u p r o gr a ma y l u ego a gr e ga .e x e al fi nal d el no m -
b re p ar a q ue p ued a e x a mi nar e l e n sa mb lad o . P ued e i g u al me n te e sc rib ir e n e l
co d i go el no mb re d e s u ap li cac ió n, p er o es te mé to d o gar a nt iza q ue fu nci o nar a, s i n
q ue i mp o r te co mo l la me a s u ap l ic aci ó n .
La fi g u ra 3 2 .2 mu e st r a el r es u lt ad o d e e st a ap li cació n No e s u n r e s ul ta d o mu y
esp e ct ac ul ar, ya q ue s u p r o gr a ma so lo co nt ie n e u na cl as e
namespace ReflectionTest
{
u s i n g Sys t em;
u s i n g Sys t em.Reflect i on ;
Console.WriteLine("Constructors are:");
foreach(ConstructorInfo i ín ci )
Console-WriteLine(i);
}
PropertyInfo[] pi = t.GetProperties();
Console.WriteLine("Properties are:" )
foreach ( ProperteyInfo i in pi )
{
Console.WriteLine( i )
}
MethodInfo[] mi = t.GetMethods();
Console.WriteLine(" ------------------------- ");
return 0 ;
}
public class aUsefulClass
{
public int pubInteger
prívate int privValue;
public aUsefulClass()
{
}
public aUsefulClass ( int IntegerValueI n )
{
pubInteger = IntegerValueIn ;
}
}
}
}
Aq uí he mo s cr ead o d o s cl as e s. C la s s1 y aU se fu lC la s s. Cla s s1 co n -
tie n e e l p r i ncip al p u nto d e e n tr ad a a s u ap l ic ac ió n ( vo id M ai n) . mi e nt ras q ue
la o t r a c la se so lo e x is te p ar a ser e xa mi n ad a.
P ar a e x a mi n ar la cl as e aU se f u l Cl as s. r e al ice lo s si g ui e nt es p a so s e n el
p r o ced i mi e n to p ri nc ip a l: en p r i mer l u gar , d e cla r e u n o b j eto T yp e y. med ia nt e la
p alab r a c la v e t yp eo f. d ir ij alo ha cia a Us e f ul Cl a ss. A co n ti n u ació n , mu e s -
tr a la c la se T yp e y el es p acio d e no mb r e .
De sp ué s, u se Ge tCo n s t r uc to r s p ar a r ec up erar u na l is ta d e lo s co n s tr uc -
to r e s d e la cla s e. A co nt i n ua ció n, ap liq ue u n b u cl e a lo la r go d e lo s co n str uc to re s
y mu é st relo s e n l a p an ta lla . Al i g ual q u e co n lo s co n s tr uc to r es, u se
GetP r o p e rti e s p ara r e c up er ar u na l i sta d e to d as l as p ro p ied ad e s d e mo d o
q ue p u ed e it erar a lo lar go d e la l i s ta y d e l as p r o p ied ad e s fu era d e l a ve nt a na d e
co n so la.
Get Me t ho d s rec up er a to d o s lo s mé to d o s, ad e ma s d e lo s méto d o s q ue c o m -
p o ne n lo s d e scr ip to r e s d e acc e so get y set d e s u s p ro p i ed ad e s. A co nt i n ua ció n,
se i ter a a lo lar go d e es ta i n fo r mac ió n y e sta se mue s tra e n p a nta ll a. T a mb ié n
in v o ca a G et P a ra me ter s p ar a q ue r ec up er e u n a l is ta d e lo s p ará me tr o s p ar a
cad a mé to d o y ta mb ié n mu e s tr a es a i n fo r mac ió n .
Co mo p ued e ve r e n la fi g ur a 3 2 .3 . s u ap l ic ació n mu e s tra u na gra n c a nt id ad d e
in f o r mac ió n so b re el o b j eto d e la cl as e.
Ob via me n t e, es ta ap lic ac ió n no e s e sp e ci al me n te út il , v a q u e ya tie n e el eo d ig o
fue n te d e l a c la se e n c ue st ió n y no n ece s ita la re fle xió n p ara d arl e d e ta ll es. Lo
i mp o rt a nte aq uí e s q ue la r e fle x ió n f u n cio na d e l a mi s ma ma n era, a u nq ue e st e mo s
tr at a nd o co n u n e n sa mb l ad o p ar a el q u e no te n ga mo s e l co d i go fue n te.
H1 listado 32.5 contiene el codi go que puede usarse para generar codigo en
tiempo de ej ecución.
Listado 32.5. Cómo generar código dinámicamente en tiempo de ejecución
using System;
using System.Reflection;
using System.Reflection.Emit;
namespace DynamicCode
{
class CodeGenerator
{
Type t;
AppDomain current Domain;
AssemblyName assemName;
AssemblyBuilder assemBuilder;
ModuleBuilder modu1eBuilder;
TypeBuilder typeBuilder;
MethodBuilder methodBuilder;
ILGenerator msilG;
if (t != null)
{
object o = Activator.CreateInstance(t);
MethodInfo helloWorld = t.GetMethod("HelloWorld");
if( helloWorld != null)
{
// Ejecuta el método HelloWorld
helloWorld.Invoke (o, null);
}
else
{
Console.WriteLine("Could not retrieve
MethodInfo");
}
}
else
{
Console.WriteLine ("Could not access the Type");
}
public CodeGenerator()
{
// Obtiene el dominio de aplicación actual.
// Esto es necesario cuando se construye codigo.
currentDomam = AppDomam.CurrentDomam;
assemBuilder =
currentDomain.DefineDynamicAssembly(assemName,
AssemblyBuilderAccess.Run);
// Crea un nuevo modulo en este ensamblado
moduleBuilder =
as semBuilder.DefineDynamicModule ("BibleModule");
NOTA: La lógica difusa es un tipo de álgebra que usa los valores verdade-
ro y falso para tomar decisiones basándose en datos imprecisos. La lógica
difusa suele relacionarse con los sistemas de inteligencia artificial.
Resumen
Las clases R e f l e c t i o n y T y p e van unidas cuando necesita descubrir in-
formación de tipo en tiempo de ejecución. Estas clases permiten examinar obje -
to s. c ar gar o b j eto s d i ná mi ca me n t e e n ti e mp o d e ej ecu ció n e i n cl u so ge ne rar có d i -
go e n ca so d e nec e sid ad .
Subprocesamiento
Antes empezar a escribir aplicaciones con múltiples subprocesos, debe com -
prender lo que sucede cuando se crean los subprocesos y cómo gestiona el sistema
operativo los subprocesos.
Cuando se ejecuta una aplicación, se crea un subproceso primario y el ámbito
de la aplicación se basa en ese subproceso. Una aplicación puede crear subprocesos
adicionales para realizar tareas adicionales Un ejemplo de creación de subprocesos
primarios sería iniciar Microsoft Word. La ejecución de la aplicación comienza
en el s ub p ro ce so p r i nci p al. E n la ap l ic ació n W o rd , la i mp r es ió n e n s e g u nd o p la no
d e u n d o c u me nto s erí a u n ej e mp lo d e u n s ub p r o ceso ad icio n al cre ad o p ara co n tro -
lar o tr a tar ea. M ie nt ra s es ta i n ter a ct u a nd o co n el s ub p ro ce so p r i nc ip a l (e l d o c u -
me n to W o rd ), e l s is te ma ll e va a cab o s u p eti ció n d e i mp re sió n. C ua nd o e l
s ub p r o c eso d e la ap l ica ció n p r i nc ip a l ter mi n a, to d o s lo s o tr o s s ub p ro c eso s cre a -
d o s a p ar t ir d e e se p ro c e so t a mb i é n fi n al iza n . Co n sid ere e s ta s d o s d e fi ni cio ne s d e l
Kit d e d e s arro l lo d e so f t wa r e d e M icr o so f t Fo u n d atio n C la s s (M F C SD K) :
• P r o ceso : U n a i n s ta nc ia q ue s e ej ec u ta d e u na ap lic ació n
• S ub p ro ce so : U n a r ut a d e ej ec ució n d e nt r o d e u n p ro ce so
C+ + y M FC l le va n m uc ho s a ño s ap o ya n d o el d es arr o l lo d e ap li c acio ne s
mu l t ip r o ce so . Co mo el co r azó n d el si st e ma o p erat i vo W i nd o ws e stá e scr ito co n
es ta s her r a mi e nt as, e s i mp o r ta n te q ue sea n co m p atib le s co n l a cap ac id a d d e cr ear
s ub p r o c eso s e n lo s q ue se p ued a n a s i g nar y cr e ar tare as . E n lo s p r i mer o s ti e m p o s
d e W i nd o ws 3 .1 . l a m ul ti tar ea no e xi st ía : e s te co n cep to s e hi zo re alid ad co n
W ind o ws NT 3 .5 y NT 4 .0 y l u e go e n W i nd o ws 9 5 . 9 8 . 9 8 S E. ME . 2 0 0 0 y XP .
P ar a ap r o vec h ar l as ca r act er í st ic as d el si st e m a o p erat i vo , la s ap l ic a cio ne s co n
var io s s ub p ro ce so s se h i ci er o n má s i mp o r t a nt es. Ho y e n d ía. la cap acid ad d e
real izar má s d e u na tar ea a l a v ez es u n r a s g o n ece s ario p ara u na a p lic ació n .
Vi s ua l B as ic 6 .0 y ver s io n e s a nte r io r e s co mp il ab a n ap l ica cio n es d e u n so lo p ro -
ceso . lo q u e si g n i fi cab a q ue. s i n i mp o r tar lo q u e p a sar a, la ap lic ac ió n d e VB so lo
p o d ia ha cer u n a co sa a l a v ez.
En r eal id ad , e n u n s is te ma co n u n so lo p r o ce sa d o r, no i mp o rt a q u é her ra mie n ta
u se p ar a e scr ib ir s u ap li cació n ya q ue to d o s i g u e s uc ed i e nd o e n u n p ro c eso li ne al.
Si e s u n p ro gra mad o r d e C+ + . p ued e cr ear n ue vo s s ub p ro c e so s y rea li zar tare a s
mi e nt r a s tie n e n l u gar o t r o s s uc e so s, p er o e n r e alid ad só lo se co mp ar te el mi s mo
tie mp o co n e l r e sto d e p r o ce so s q ue se e stá n ej e cu ta nd o e n el si s te ma . S i so lo ha y
u n p r o ce sad o r , so lo p u ed e s uced er u n a co s a c ad a ve z. Es te co nc ep to s e l la ma
multitarea preferente.
Multitarea preferente
La mu l ti tare a p re fe r e nt e d i vid e el ti e mp o d e l p ro ces ad o r e n tre la s t area s o
s ub p r o c eso s e n ej ec uc ió n. C ua nd o u n a ta r ea se es tá ej ec u ta nd o , u sa un espacio de
tiempo. C u a nd o e l e sp ac i o d e ti e mp o d e l a tar e a q ue se es tá ej ec u ta nd o c ad u ca, e n
ap r o x i mad a me n te 2 0 m ili s e g u nd o s . d ep e nd i e nd o d el s is te ma o p era ti vo q ue e st e
u sa nd o , se i n v al id a y o t r a tar e a r ec ib e e l esp ac i o d e tie mp o . E l s i st e ma g uard a el
co n te x to a ct ua l d e l a ta r ea i n val i d ad a y c ua nd o se a si g n a a la t area o tr o esp ac io
d e t ie mp o , se re st a ur a s u co nt e xto y el p r o ce so co nt i n ua. E st e b uc le d e tar ea
co n ti n u a rep e tid a me nt e ha st a q u e la t ar ea e s ab o rtad a o fi nal iz a. La mul ti tar ea
p re fer e n te d a al us u ar io la i mp r e sió n d e q u e se re al iza má s d e u n a tare a a la vez.
¿P o r q ue fi n al iza n al g u n as t ar ea s a n te s q ue o tr as , au nq u e se i nic ie a n te s la ul ti ma
en ter mi nar?
Prioridades de subproceso y bloqueo
C ua nd o se cr ea n s ub p r o ceso s, e l p r o gr a ma d o r o el s is te ma o p era ti vo le s as i g -
na u n a p rio r id ad . Si u n a ap l icac ió n p ar ece e s t ar b lo q uea nd o el si s te ma , ti e ne la
p rio r id ad má s al ta y es tá b lo q u ea nd o e l ac ceso a lo s t ie mp o s d e lo s d e ma s
s ub p r o c eso s. La s p r io r i d ad es d e ter mi n a n lo q u e s uc ed e y e n q ue o rd e n. S u ap l ica -
ció n p ued e e st ar co mp l eta e n u n 9 0 p o r cie n to co n u n p ro c eso d et er m in ad o c u a n -
d o . d e r ep e nt e, s e i n ici a u n n ue vo s ub p r o c eso y s e co lo c a p o r d el a nte d e l s ub p ro c eso
q ue e s ta ej ec ut a nd o s u ap li cac ió n, hac ie nd o q u e a e se s ub p ro ce so se le a si g n e u na
p rio r id ad me no r. E sto s ue le s uc ed er e n W i n d o ws . Al g u na s t area s so n má s p rio r i -
tari as q ue o tra s. T o me mo s co mo ej e mp lo e l n ue vo Rep ro d uc to r d e W ind o ws Me -
d ia. Al i ni ci ar es te p ro c eso hac e, b á si ca me n te, t o d o lo q ue se e st a ej ec u ta nd o d ej a
d e r esp o nd er ha sta q u e est a co mp l et a me n te ca rgad o , i nc l u ye nd o l a p ag i na d e la
G uia mu l ti me d ia .
U no d e lo s ma yo r es p el ig r o s a lo s q ue se e n fre nt a n lo s p ro gra mad o re s al e sc ri -
b ir ap lic ac io ne s q ue us an var io s s ub p r o ce so s s o n la s si t uac io ne s d e b lo q ueo , e n
la s q ue d o s o má s s ub p r o ce so s i n te nt a n u sar el mi s mo r ec ur so . U n b lo q ueo d e
s ub p r o c eso ti e ne l u g ar cu a nd o u n s ub p r o ce so acced e a u n rec u rso co mp a rt id o y
o tro s ub p ro ce so co n la mis ma p r io r id ad i nt e n ta ac ced e r a d i c ho rec ur so . Si lo s
d o s s ub p r o c eso s t ie ne n la mi s ma p r io r id ad y n o se ha co d i fic ad o e l b lo q ueo co -
rrect a me n te. e l si st e ma s uc u mb e le n ta me n te p o rq ue no p u ed e lib er ar ni n g u no d e
lo s s ub p r o ce so s d e al t a p r io r id ad q u e se es t án ej ec u ta nd o . Es to p u ed e s uced er
fá ci l me n te e n la s ap l ic acio ne s co n v ar io s s ub p ro ce so s. C ua nd o el p r o gra mad o r
as i g na p r io r id ad es a lo s s ub p r o ce so s y es tá n co mp art ie nd o d a to s g lo b ale s, d eb e
b lo q uear el co nt e xto co r r ect a me n te p ar a q u e e l s is te ma o p er at i vo ge st i o ne co rre c -
ta me n te el esp acio d e ti e mp o .
Multiprocesamiento simétrico
En u n si st e ma multiproccsador. p ued e te ner l u g ar real me nt e ma s d e u na tarea
a la v ez. Co mo cad a p r o ces ad o r p ued e a si g n ar esp a cio s d e t ie mp o a l a s tar ea s q u e
q ui er e n ej ec u tar se, p ue d e r ea li zar ma s d e u na tar ea a la v ez. C u a nd o n ece s ita
ej ecu tar u n s ub p ro c es o lar go q ue r eq u ier a mu cho trab aj o d e l p ro ce s ad o r, co mo
o rd en ar 1 0 mi llo n es d e r eg i str o s p o r no mb r e , d i recc ió n, co d i go p o s tal , ap el lid o s y
p aí s, si s e u sa n va rio s p r o ce sad o r e s el tr ab aj o co ncl u irá a nt es q ue si se us a u n
so lo p r o ce s ad o r. Si p u ed e d e le g ar e s e t r ab aj o e n o tro p ro ce sad o r, l a ap l ica ció n
act ua l me n te e n c ur so n o se v er a a fec tad a e n ab so l u to . T e ner ma s d e u n p ro ce sad o r
p er mi te e st e t ip o d e multiprocesamiento simétrico ( SMP ). La fi g ura 3 3 .1 mu es -
tra la s o p c io ne s d e p ro c esad o r p ar a S Q L Ser v er 2 0 0 0 .
Si es tá ej ec ut a nd o S Q L Ser v er e n u n eq uip o m ul tip ro ce sad o r . p ued e d efi nir el
n ú me r o d e p ro ce sad o r e s q ue d eb e u s ar p ar a l a s tarea s l ar ga s y q ue e x i ge n e l t ipo
me n cio n ad o . S Q L lle v a es to u n p o co ma s a llá , real iza nd o co ns u lt as a l o lar go d e
lo s d i fer e nt es p ro ce sad o r es, u nie nd o lo s d a to s c ua nd o s e co mp le ta e l u lt i mo
subproceso y mostrando los datos al usuario. Esto se conoce como sincronización
de subprocesos. El subproceso principal, que crea varios subprocesos, debe espe -
rar a que todos los otros subprocesos estén completos antes de continuar con el
procesado.
Observe que. cuando se usa un sistema SMP. un solo subproceso sigue ejecu -
tándose en un solo procesador. La aplicación VB6 se ejecuta exactamente igual
que si le añade otro procesador adicional . Su aplicación de Access 2.0 de 16 bits
tampoco se ejecuta mejor porque 16 bits siguen siendo igual a un solo proceso.
Deberá crear nuevos procesos en los demás procesadores para aprovecharlos.
Esto significa que no diseñamos una GUI multiprocesador. Desa rrolle una GUI
que cree otros procesos y pueda reaccionar cuando esos procesos se completen o
sean interrumpidos, mientras sigue permitiendo al usuario usar la GUI para otras
tareas.
Dominios de aplicación
An ter io r me n te se i nd i có q ue el MF C SD K d e fi n e u n p ro ce so co mo u n a in s ta n -
cia d e u na ap l ica ció n q ue s e ej ec u ta. Cad a ap l i cació n q ue se e st a ej ec ut a nd o cr ea
u n n ue vo s ub p ro c eso p r i ncip al, q u e d ur a lo q ue la i n s ta nc ia d e la a p lic ació n .
Co mo cad a ap l ica ció n e s u n p r o c e so , cad a i n s ta nc ia d e u na ap lic ació n d eb e te ne r
ai sla mie n to d e p ro ce so s . Do s i n st a nci a s d e M i cro so ft W o rd a ct úa n i n d ep e nd ie n -
te me n te e n tre sí. C u a n d o ha ce cl ic e n Or to g r afía y gra má t ica , la I n st a nci a A d e
W o r d no co mp r ueb a lo s er r o r es o r to gr á f ico s d e l d o c u me n to q u e se e s t á ej ec uta n -
d o e n l a I n st a nc iaB d e W o r d . I ncl u so s i la I n s ta nc ia A d e W o rd i n te n t a p a sar u n
p u n ter o d e me mo ria a l a I n st a nc iaB d e W o r d , la I ns ta n ciaB no sab ra q ue ha cer
co n él, o s iq ui era d ó nd e b u sc ar lo , ya q ue lo s p u nt ero s d e me mo ri a só lo se r e fi ere n
a lo s p r o c eso s e n lo s q u e s e ej ec u ta n.
En . NET Fra me wo r k . l o s d o mi n io s d e ap lic ac ió n se u sa n p ara p ro p o rcio nar
se g ur id ad y a is la mi e n to d e a p li cac ió n p ar a el có d i go g es tio n ad o . Al g u no s d o mi -
nio s d e ap l ica ció n p ue d en ej ec uta r s e e n u n s o lo p ro ce so o s ub p r o ce so , co n la
mi s ma p r o t ecc ió n q ue s i la s ap lic ac io ne s s e e st u vi e se n ej ec u ta nd o e n v ario s p ro -
ceso s. E l co n s u mo d e r ec ur so s se r ed u ce co n e st e co ncep to , ya q ue la s l la mad as
no nec es it a n es tar c irc u n scr ita s a lo s l í mi te s d e lo s p ro c eso s si l as a p lic acio n e s
ne ce si ta n co mp ar tir d at o s. P o r el co ntr ar io , u n so lo d o mi n io d e ap li ca ció n p ued e
ej ecu tar se e n va rio s s ub p r o ce so s.
Est o es p o s ib le gr aci a s al mo d o e n el q u e el C LR ej ec u ta e l có d i go U na vez
q ue el có d i go e st á p rep ar ad o p ar a ser ej ec ut ad o , el co mp i lad o r J IT ya l e ha he c ho
p asa r e l p ro c eso d e v e r i fic ac ió n. E s te p r o ce so d e ver i fi cac ió n g ara nt iza q u e e l
có d i go no va a re al izar acc io ne s i n vá lid a s, co mo ac ce so s a me mo r ia i mp r e vi sto s,
q ue p r o vo q ue n u n fal lo d e p á gi n a.
Est e co ncep to d e có d i g o de tipo separo gar a n ti z a q ue u n có d i go no v a a vio lar
ni n g u n a r e g la d esp u és d e q u e e l ver i fi cad o r lo ha ya ap ro b ad o a l p a sar d e có d i go
MSI L a P E. E n la s típ i cas a p li cac io ne s W i n3 2 . no e x i st ía n mec a ni s m o s d e p ro -
tecc ió n q ue e v it ara n q u e u n fr a g me nt o d e co d i go s up la n ta se a o tro , d e mo d o q ue
cad a ap l ic ació n ne ce si t ab a ai sl a mi e nto d e p r o ceso . E n NET . co mo l a s e g urid ad
d e tip o e st á g ara nt iz ad a, r es u lt a se g u r o ej ec u tar vari a s ap l ica cio ne s d e var io s
p ro ve ed o r e s e n el mi s m o d o mi nio d e ap li cac ió n .
Ventajas de las aplicaciones de varios
subprocesos
Al g u na s ap l ica cio ne s p u ed e n sac ar p ar tid o d el m ul tip ro ce sa mi e n to .
• Ap l icac io ne s co n p r o ce s o s l ar go s.
• Ap l icac io ne s d e so n d eo y e s c uc ha.
• Ap l icac io ne s co n b o to n Cancelar en la GUI.
Las s i g ui e nte s se cc io ne s mu es tr a n cad a u na d e e st as razo n es .
Miembro Descripción
using System;
using System.Threading;
{
}
}
public class ThreadTest
{
public static int Main(String[] args)
{
Thread ti = new
Thread(new ThreadStart(testThreading.Threader1));
tl.Start();
Thread t 2 = new
Thread (new ThreadStart(testThreading.Threader2));
t2.Start();
Console.ReadLine();
return 0;
}
}
Thread t1 = new
Thread(new ThreadStart(testThreading.Threaderl));
t1.Name = "Threaderl";
t1.Start();
Thread t2 = new
Thread (new ThreadStart(testThreading.Threader2));
t2.Name = "Threader2";
t2.Start();
Console.ReadLine();
return 0;
}
}
Cuando ejecuta la aplicación, el resultado en su consola debería parecerse al
mostrado en la figura 33.2.
El resultado que muestra la figura 33.2 no es muy bonito. Si realiza otra
llamada, estará trabajando con subprocesos. Sin establecer una propiedad o dos.
el procedimiento T h r e a d e r l nunca terminará antes de que empiece T h r e a d e r 2 .
Cuando se ejecute el siguiente codigo:
tl.Start();
Co me n zará l a ej ec uc ió n d el có d i go d e T h r e a d e r 1 . Co mo e s u n s ub p ro ceso ,
tie n e ap e na s 2 0 mi l is e g u nd o s d e e sp acio d e t ie mp o , e n e se p er ío d o d e t ie mp o
alca n zar á la s e g u nd a l í n ea d e có d i go d e la f u n ció n, d e vo l ver á e l co nt ro l a l s i ste ma
o p er at i vo y ej e c utar á l a si g u ie n te li ne a d e có d i g o :
t2.start();
C ua nd o e st ab l ezc a la p r io r id ad a l má x i mo , t 1 fi na li zara a n te s q ue t 2 . Si
ej ecu ta la ap l ica ci ó n d e n u e vo , e l r e s u ltad o d e b erá ser co mo e l q ue m ue s tra la
fi g ur a 3 3 .3 . La e n u me r ació n T h r e a d P r i o r i t y es tab l ece co mo s e p l a ni fic a u n
d eter mi n ad o s ub p ro c es o en r el ació n a lo s o tro s s ub p ro ce so s e n ej ecu ció n.
ThreadPriority p ued e ad o p tar c ua lq ui er a de lo s si g u ie n te s v alo re s :
AhoveNormal. B elowNormal. Highest. Lo we st o Normal. El a l go ri t mo q ue d et er -
mi n a el o rd e n d e lo s s ub p r o ce so s var í a e n f u nc ió n d el s is te ma o p er at i vo en el q ue
se ej e c uta n lo s s ub p ro ce so s. P o r d e fec to , c u a nd o se cr ea u n n ue vo s ub p r o ceso , s e
le co n ced e la p rio r id ad 2 . q u e e s No r ma l e n la e n u me rac ió n.
Figura 33.3. Resultado tras establecer la prioridad del subproceso
using System;
using System.Threading;
}
public void Threader2()
{
for (int intX = 0; intX < 50;intX ++)
{
if (intX == 5) {
Thread.Sleep ( 5 00) ;
Console.WriteLine("Thread2 Sleeping");}
}
}
}
public class ThreadTest
{
public static int Main(String[] args)
{
Threads testThreading = new Threads();
Thread t1 = new
Thread (new ThreadStart(testThreading.Threader1));
t1.Priority = ThreadPriority.Highest;
t1.Start();
Thread t2 = new
Thread (new ThreadStart(testThreading.Threader2));
t2.Start();
Console.ReadLine();
return 0;
}
}
Observe que se ha establecido la propiedad Priority del subproceso ti al
máximo. Esto significa que. pase lo que pase, se ejecutará antes de que comience
t2. Sin embargo, en el procedimiento Threaderl. tiene el siguiente bloque if:
for (int intX = 0; intX < 50;mtX ++)
{
if (intX == 5) {
Thread.Sleep (500) ;
Console.WriteLine(”Thread! Sleeping");)
}
Esto indica al subproceso ti que se bloquee durante 500 milisegundos. aban -
donando su actual espacio de tiempo y permitiendo que el subproceso t2 comien -
ce a ejecutarse. Cuando los dos subprocesos se han completado, se invoca al
método Abort y los subprocesos son eliminados.
Las llamadas al método Thread . Suspend bloquean un subproceso indefi -
nidamente. hasta que otro subproceso lo active de nuevo. Si alguna vez ha obser -
vado el contador del procesador en el Administrador de tareas llegar al 100 por
ciento cuando no está gastando memoria, puede entender lo que sucede cuando un
subproceso se bloquea. Para que el subproceso vuelva a funcionar, debe llamar al
método R e s u m e desde otro subproceso para que pueda reanudarse. El siguiente
código muestra los métodos S u s p e n d y R e s u m e :
Thread.CurrentThread.Suspend;
Console.WriteLine("Thread1 Suspended");
Thread.CurrentThread.Resume;
Console.WriteLine("Threadl Resumed");
Miembro Descripción
por
Console.Writeline("Writing");
using System;
using System.Threading;
}
}
}
T h r e a d t2 = n e w
Thread (new ThreadStart (testThreadinq.Threader2));
t2.Start();
Thread t1 = new
Thread (new ThreadStart (testThreading.Threader1));
t1.Priority = ThreadPríority.Highest;
t1.Start() ;
t2.Join();
Console.PeadLine();
return 0 ;
}
}
Como puede ver. el est ablecimiento de varias propiedades en los subproces os
simplifica mucho su control. Tenga en cuenta que tras suspender un subproceso,
debe reactivarlo o su sistema consumirá recursos innecesariamente.
Cómo sincronizar subprocesos
La sincronización de datos es un aspecto importantísimo en la utilización de
subprocesos. Aunque no es una tarea compleja de programación, los datos corren
el nesgo de estropearse si no los dirige correctamente
Cuando los subprocesos están ejecutándose, comparten tiempo con otros
subprocesos en ejecución. Esto se aprecia perfectame nte en el ejemplo que ejecu-
tamos en este capitulo. Si tiene un método que se esta ejecutando en varios
subprocesos, cada subproceso solo tiene varios milisegundos del tiempo del
procesador antes de que el sistema operativo sustituya el subproceso para da rle
tiempo a otro subproceso del mismo método. Si esta en medio de una instrucción
matemática o de la concatenación de un nombre, es probable que el subproceso se
detenga algunos milisegundos y otro subproceso en ejecución sobrescriba sus
datos. Sin embargo, esto no es el fin del mundo porque \ arios métodos le permiten
hacer que esto no suceda. Observe el siguiente codigo:
int Y;
int V;
for(int Z = 0;Z < 20; Z++) {
return Y * V;}
}
Es muy probable que durante el bucle, un subproceso en ejecución se detenga
para permitir que otro subproceso use este método. Recuerde que esto solo ocurre
si permite que haya múltiples subprocesos accediendo a este bloque de codigo. Al
escribir aplicaciones con múltiples subprocesos, esto sucede muy a menudo, de
modo que necesita saber como solucionar la situación. El siguiente codigo resuel -
ve este problema:
lock (this) {
int Y;
int V;
for (int Z = 0; Z < 20;z++){
return Y * V;}
}
La instrucción Lock es un modo de obligar a los subprocesos a unirse. Su
implementacion es un poco diferente a la del método Join. Con Lock. evalua -
mos una expresión que se ha pasado al bloque Lock;. Cuando un subproeeso
llega al bloque Lock. espera hasta que puede conseguir un bloqueo exclusivo de
la expresión que se esta evaluando antes de intentar seguir con el proceso. Esto
asegura que no se estropeen los datos compartidos al usar vanos subprocesos.
La clase M o n i t o r permite la sincronización mediante los métodos
Monitor.Enter.Monitor.TryEnter y Monitor.Exit. Tras usar un
bloqueo en una región de código, puede usar los métodos M o n i t o r . W a i t .
M o n i t o r . P u l s e y M o n i t o r . P u 1 s e A l l para determinar si un subproceso
debe continuar con un bloqueo o si algunos métodos bloqueados anteriormente
están ya disponibles. W a i t levanta el bloqueo si éste se mantiene y espera a que
se le notifique. Cuando se invoca W a i t . se rompe el bloqueo y vuelve para volver
a cerrarlo.
Sondeo y escucha
El sondeo y la escucha son otras dos instancias que representan la utilidad del
multiprocesamiento. Las bibliotecas de clases, como S y s t e m . N e t . S o c k e t s .
inclinen un rango completo de clases de multiproceso que pueden ayudarle a
crear agentes de escucha TCP. agentes de escucha UDP y una gran cantidad de
tareas de red que requieren multiproceso. Observe la clase T i m e r C a l l B a c k
del espacio de nombres S y s t e m . T h r e a d i n g . Esta clase es muy parecida a las
otras que hemos estado usando hasta ahora, excepto que una de las partes del
constructor es un temporizador. lo que le permite realizar sondeos en busca de
acciones cada ciertos intervalos.
Puede conseguir el mismo resultado agregando un control temporizador al
formulario, pero usando la clase T i m e r C a l l B a c k . el temporizador y la res-
puesta al procedimiento son automáticos.
El listado 33.5 usa una devolución de llamada temporizada para sondear un
directorio en busca de archivos. Si se encuentra un archivo, es rápidamente borra -
do. Sólo debe ejecutar este código en un directorio de prueba, porque elimina
archivos. El siguiente código de ejemplo busca en el directorio C : \ P o l l . El
constructor de la clase T i m e r C a l l B a c k espera una dirección en la que se va a
ejecutar el subproceso; un tipo de datos objeto que representa el estado del
temporizador; un tiempo de vencimiento, que representa el período de tiempo
hasta el que se van a realizar sondeos; y un período, que es una variable en
milisegundos que indica cuándo se produce el intervalo de sondeo.
using System;
using System.IO;
using System.Threading;
Resumen
En este cap itulo ha ap re ndido a imple men tar múltip les su bproceso s en C# con
el espac io de no mb res S yste m. Thread.
La id ea basica tra s el mu ltip roce sa mien to es simple: al crear mas de un
subproceso, puede rea lizar mas de una ta rea a la vez. T iene qu e probar antes el
n u me r o d e s ub p ro ce so s q ue v a ya a cr ea r . D e ma si ad o s s ub p ro ce so s p u ed en ca u sar
p ro b le ma s d e r ec ur so s. Si no se cr ea n s u fic ie n t es s ub p ro ce so s, p ued e q ue la ap li -
cació n no tr ab aj e co n to d o s u p o te nc ia l.
Co n e l ej e m p lo q ue ha cr ead o aq uí, d eb er ía e s t ar p rep ar ad o p ar a i mp le me n tar
s ub p r o c eso s e n s u s p r o p ia s ap l icac io ne s. Si mp le me n te e v ite t o mar ri e s go s p o r -
q ue . co mo s ab e, la s ap l i cacio n e s co n v ar io s s ub p ro ce so s p ued e n ge ner ar v ario s e n
p ro b le ma s.
Co mo sie mp re, p l a ni f i q ue s u ap l ic ació n co n a n tel ació n y d e cid a si el u so d el
mu l t ip r o ce sa mie n to e s u na p ar t e i mp o r ta n te d e e st e p ro ce so d e p l a ni fica ció n.
34 Cómo
trabajar
con COM
Figura 34.1. Código administrado y no administrado conviviendo juntos sin pro blema
Cómo crear ensamblados .NET a partir
de componentes COM
P ara u sar s u s co mp o ne n te s C OM e n u na ap lic ac ió n NET . d eb e crea r el en -
sa mb lad o d e i nt ero p er a b ilid ad . o R CW . q ue o r ga n iza la s lla mad a s d e mé to d o
d esd e el c li e nte NET al ser v id o r C OM . E n .NE T ha y \ar io s mo d o s d e real izar
es to . Lo s d o s mo d o s ma s co mu n e s so n lo s si g u i en te s:
• La u ti lid ad I mp o r t ad o r d e la b ib lio tec a d e tip o s, o T lb i mp . e xe. p ro p o rc io -
nad a j u n to a .NET F r a m e wo r k
• Hace r re fer e nc ia d i r ec ta me n te al C OM d e sd e la a p lic ació n d e C# VS .N E T
El p ro x y cr ead o p ar a la in ter o p er ab il id ad se b as a e n lo s me t ad ato s e xp u es to s
en l a b ib l io t eca d e tip o s d el co mp o ne n te CO M al q ue se e st a i nt e nta nd o a cced er .
Las b ib lio tec a s d e t ip o s C OM p ued e n ha cer se acce s ib le s d e u n a d e e s ta s d o s
fo r ma s :
• Las b ib lio tec a s d e t ip o s p ued e n e nco n trar se co mo arc h i vo s i nd i v id ual e s.
Las b ib l io t eca s i nd i vid u ale s d e tip o s s ue le n t e n er la e xte n s ió n T LB , La s
b ib lio te ca s i nd i v id ua le s d e tip o s ma s a nt i g ua s p ued e n te ner l a e xte n s i ó n
O L B . Si e st a cr ea nd o u n a D LL. Act i ve X d e V is u al B a s ic, p u ed e crear u n a
b ib lio te ca i nd i v id ua l d e tip o s p a r a s u co mp o ne n t e s el ecc io na nd o la o p ció n
Archivos de servidor remoto en el c uad ro d e d ia lo go Propiedades d el
p ro ye cto .
• Las b ib lio tec as d e tip o t a mb ié n p ued e n e nco n tra rse i ncr u s tad a s e n u n s er -
vid o r C OM co mo u n r ec ur so b i n ar io . Lo s ser v id o res C OM e n c urso , e m -
p aq u etad o s co mo D LL y lo s ser v id o re s C OM d ete n id o s , e mp aq u etad o s
co mo EXE. p u ed e n i nc l u ir la b ib lio te ca d e t ip o s co mo u n re c ur so d el p ro -
p io ser v id o r C OM. Lo s co mp o n e nt es CO M crea d o s co n Vi s ua l B a s ic t ie -
ne n la b ib l io t eca d e tip o s co mp il ad a d e ntro d e l a D LL.
En l a si g u ie n te s ecc ió n a p r end er a a cr e ar el e n sa mb lad o d e i nt ero p er ab i li d ad a
p ar tir d e u na D LL CO M med ia n te lo s d o s mé to d o s d e scr ito s al p r i nc ip io d e e sta
sec ció n : u sa nd o la ut ili d ad T lb i mp y h ac ie nd o refere n cia d ir ect a me n te al D LL
d esd e V is u al S t ud io NE T .
E s te c o m a n d o p r od u c e u n e n s a m bl a d o . N E T c o n u n a e x te n s i ón D L L q u e r e c i -
be e l m i s m o n o m br e ba s e q u e l a bi bl i o t e c a i n c r u s ta d a e n e l a r c h i v o d e l a bi b l i o te -
c a d e ti p os ( q u e p u e d e s e r d i f e r e n t e d e l n om br e d e a r c h i v o d e l a p r op i a bi bl i o te c a
d e a r c h i v o s ) . E l c o m a n d o t l bi m p a d m i t e e l n o m b r e d e u n a r c h i v o d e bi bl i o t e c a d e
ti p o s c o m o d a t o i n t r od u c i d o:
tlbimp server.tlb
T a m bi é n p u e d e a d m i t i r e l n om br e d e u n s e r v i d o r C O M q u e c on ti e n e l a b i bl i o -
te c a d e ti p o s i n c r u s t a d a :
tlbímp server.dll
A l u s a r l a op c i ón / o u t . s e p u e d e e s p e c i f i c a r u n n o m br e a l t e r n a ti v o p a r a e l
e n s a m bl a d o NET’ c r e a d o:
tlbimp server.dll /out : dotNetServer.dll
E l e n s a m bl a d o p r o d u c i d o p or l a h e r r a m i e n ta T l bi m p . e x e e s u n e n s a m b l a d o
. N E T e s tá n d a r q u e p u e d e s e r v i s t o c on I l d a s m . e x e . E l e n s a m b l a d o n o c o n ti e n e e l
c ód i g o d e l s e r v i d or C O M ; e n s u l u g a r , c on t i e n e r e f e r e n c i a s q u e a c u d a n a l C L R a
e n c on tr a r l o s o bj e t os C O M a l m a c e n a d o s e n e l s e r v i d or . c om o l o s G U I D d e l o bj e -
t o C O M . I m a g i n e e l e n s a m b l a d o g e n e r a d o p or t l b i m p c om o u n p u e n te q u e c on e c ta
e l c od i g o . N E T c on e l s e r v i d or C O M C o m o e l c od i g o C O M s i g u e e s ta n d o e n e l
s e r v i d o r C O M . n o d e b e o l v i d a r i n s t a l a r e i n s c r i bi r e n e l r e g i s tr o t od os l os s e r v i d o -
r e s C O M q u e p i e n s e u s a r e n s u s a p l i c a c i on e s . N E T . E n r e a l i d a d , e s t o s u p on e u n a
g r a n v e n t a j a p a r a e l p r og r a m a d or . C o m o e l s e r v i d or C O M s i g u e r e g i s t r a d o c on
W i n d ow s , l a s a p l i c a c i on e s C O M e s t á n d a r q u e n o s on c on s c i e n te s d e l a e x i s te n c i a
d e N E T p u e d e n s e g u i r u s a n d o e l m i s m o s e r v i d or C O M s i n tr a s l a d a r c o d i g o a u n a
p l a ta f o r m a e s p e c í f i c a N E T .
Option Explicit
P u b l i c E v e n t COMEvent( M e s s a g e A s S t r i n g )
Est e co d i go se co lo ca e n u n mo d u lo d e cl as e ll a mad o C O M O b j e c t . El m o d u lo
d e cla s e e sta e n u n p r o ve cto lla mad o V B 6 C O M S e r ve r . Vi s u al B a s ic 6 .0 co mp il a
es te co d i go e n u n se r vid o r C OM e n p r o ce so e i n cru s ta u n a b ib lio te ca d e tip o e n e l
ser v id o r . La rep r es e nta c ió n le gib le d e l a b ib l io t eca d e t ip o s, e sc ri ta e n Le n g uaj e
d e d e fi n ic ió n d e i nt er fa z ( I DE) d e CO M se mu e s t ra e n e l li st ad o 3 4 .2 .
Listado 34.2. Fuente IDL para el servidor COM del listado 34 1
// Archivo .IDL generado (por el Visor de objetos OLE/COM)
//
// typelib filename: VBcCOMServer.dll
{
u n i d ( B 4 0 9 6 c 5 0 - ACA4- 4 E 1 F - 8 D 3 6 - F 3 6 F 1 E E5F03 B ) ,
version(1.0)
}
library VB6COMServer
{
//TLib://TLib:OLE Automation :{00020430 - 000U-0000 -
COOO-O00000000046}
importlib("stdole2.tlb");
[
odl,
uuid(5960D780-FEA2-4383-B2CB-5F78E4677142),
version(1.0),
hidden,
dual,
nonextensible,
oleautomation
]
interface _COMObject : IDispatch {
[id(0x68030000) , propget]
HRESULT Message ( [out, retval] BSTR* );
[id(Ox68030000) , propput]
HRESULT Message ([in] BSTR);
[id(0x60030002) ]
HRESULT SquareIt(
[in, out] short* int1,
[in, out] short* int2,
[out, retval] short* ) ;
[id(0x60030003)]
HRESULT FireCOMEvent();
} ;
[
uuid (50730C97-09EB-495C-9873BEC6399AAc3A),
versión(1.0)
]
coclass COMObject (
[default] interface COMObject;
[default, source] dispinterface COMObject;
} ;
[
uuid(A4D4C3D8-DFFF-45DB-9A14-791E4F82EF35),
versión (1.0) ,
hidden,
nonextensible
]
dispinterface _ COMObject {
properties :
methods:
[ id ( 0x00000001) ]
void COMEvent ([ in, out] BSTR* Message) ;
} ;
} ;
P ara crear el e n sa mb lad o d e i nter o p er ab il id ad q ue p er mit e a l a ap l ica ció n C#
u sar la D LL no ad mi n i s tr ad a, d eb e ej ec ut ar l a u til id ad T lb i mp d e scr it a en la
an ter io r sec ció n. E n l a f i g ur a 3 4 .2 . p ued e v er q ue el p ará me tro / o u t : se u s a
p ar a d ar al e ns a mb l ad o d e i n ter o p er ab i lid ad el no mb r e c o m p i n t e r o p . d l l . El
no mb r e d el e n sa mb lad o r es u lt a nte p u ed e ser el q ue p re fi era ( i nc l uso p ued e
te ner
el mi s mo no mb re q u e e l co mp o n e nt e CO M o ri g i n al) .
/// <sumary>
/// El principal punto de entrada para la aplicación.
/// </sumary>
[STAThread]
static void Main()
{
Application.Run(new Form1());
}
// Cree un controlador para el evento
prívate COMObject COMEventEventHandler
COMEvent Handlerlnstance;
Numl = 5;
Num2 = 6;
// Llame al método SquareIt
Sum = ObjectInstance.SquareIt(ref Num1, ref Num2);
listBox1.Items.Add(Sum.ToString());
listBox1.Items.Add(ObjectInstance.Message);
COMEventHandlerInstance = new
_ COMObject_COMEventEventHandler(COMEventHandler);
Objectlnstance.COMEvent += COMEventHandlerlnstance;
Objectlnstance.FireCOMEvent();
}
void COMEventHandler(ref string Message)
{
listBox1.Items.Add(Message) ;
}
}
}
Figura 34.6. Resultado del cliente C# usando el componente COM
COR_E_CO RE CoreException
NTE_FAIL CryptographicException
COR_E_TYPELO AD EntryPointNotFoundException
COR_E_EXECUTIONENGINE ExecutionEncjineException
COR_E_FIELDACCESS FieldAccessException
COR_E_FILENOTFOUND o ERROR_FILE_NOT_FOUND FileNotFoundException
COR_E_FORMAT FormatException
COR_E_INDEXOUTOFRANGE IndexOutOfRangeException
COR_E_INVALIDCAST o E_NOINTERFACE InvalidCastExceptlon
COR_E_INVALIDCOMOBJECT InvalidComObjectExceptlon
COR_E_INVALIDFILTERCRITERIA InvalidFilterCrlteriaException
COR_E_INVALIDOLEVARIANTTYPE InvalidOleVaiiantTypeExceptlon
COR_E_INVALIDOPERATION InvalidOpeiationExcoption
COR_E_IO lOException
COR_E_MEMBERACCESS AccessExceptlon
COR_E_METHODACCESS MethodAccessException
COR_E_MISSINGFIELD MissingFifildException
COR_E_MISSINGMANIFESTRESOURCE MissinyManifestResourceException
COR_E_MISSINGMEMBER MissingMemberException
COR_E_MISSINGMETHOD MissIngMethodException
COR_E_MULTICASTNOTSUPPORTED MulticastNotSupportedException
COR_E_NOTFINITENUMBER NotFiniteNunibetException
E_NOTIMPL NotlmplementedException
COR_E_NOTSUPPORTED NotSupportedException
COR_E_NULLREFERENCE o E_POINTER NullReferenceExceptlon
COR_E_OUTOFMEMORYo E_OUTOFMEMORY OutOfMemoryException
COR_E_OVERFLOW OverflowException
COR_E_PATHTOOLONG o ERROR_FILENAME_EXCED_RANGE PathTooLongException
COR_E_RANK RankException
COR_E_REFLECTIONTYPELOAD ReflectionTypeLoadException
COR_E_REMOTING RemotingException
COR_E_SAFEARRAYTYPEMISMATCH SafeArrayTypeMismatchException
COR_E_SECURITY SecurityException
COR_E_SERIALIZATION SerializationException
COR_E_STACKOVERFLOW o ERROR_STACK_OVERFLOW StackOverflowException
COR_E_SYNCHRONIZATIONLOCK SynchronizationLockException
COR_E_SYSTEM SystemException
COR_E_TARGET TargelException
COR_E_TARGETINVOCATION TargetlnvocationException
COR_E_TARGETPARAMCOUNT TargetParameterCountException
C O R _ E _ THREADABORTED ThreadAbortException
HRESULT Excepción .NET
COR_E_THREADINTERRUPTED ThreadInterruptedException
COR_E_THREADSTATE ThreadStateException
COR_E_THREADSTOP ThreadStopException
COR_E_TYPELOAD TypeLoadException
COR_E_VERIFICATION VerificationException
Si e s u n p ro gra ma d o r d e V i s ual B a si c 6 . e l AP I W i n3 2 h a s id o s u mo d o d e
ap r o v ec har to d a la p o t e nc ia d e l a p r o gr a mac ió n . E n NET . to d a v ía p ued e a cced er
al AP I W i n3 2 d e sd e C# . au nq u e ca s i to d a o to d a la fu n cio na lid ad q ue p ro b ab le -
me n te es te u sa nd o y a es tab a p r es e nte e n NET Fra me wo r k. La i n vo ca ció n d e
f u nc io ne s d e D LL d e C se co n s i g ue u sa nd o el se rv icio i n vo c ació n d e p l a ta fo r ma .
La i n vo c ació n d e p la ta f o r ma es u n ser v ic io q u e p er mit e al co d i go ad mi ni s trad o
lla ma r a fu ncio n e s d e D LL CO M no ad mi n i s trad as .
Med i a nte l a c la se D L L I m p o r t A t t r i b u t e , p ued e e sp ec i fi car el no mb re d e
la D LL y d e l a fu nc ió n D LL q ue d eb e u sar se e n s u ap lic ació n C# . I g u al q ue e l
acce so a l AP I W m3 2 e n VB 6 . d eb e sab er el no m b re d e la D LL y la fu nc i ó n d e la
D LL q ue se q u iere ej ec u tar . U n a vez h a lo gr ad o es to , p ued e lla mar si mp l e me nt e a
la f u n ció n u sa nd o el a tr ib u to D L L I m p o r t en u n mé to d o se ña lad o co n mo d i fi ca -
d o r es es tá ti co s y e xt er n o s, co mo mu e s tr a e l si g ui e nt e co d i go :
usinq System.Runtime.InteropServices;
[DllImport("user32.dll")]
public static extern int MessageBox (int hWnd, strinq text,
String caption, uint type);
Resumen
Este capítulo describe cómo usar objetos COM en código NET y cómo usar la
utilidad Tlbimp para generar ensamblados NET. También se estudia bre \emente
como interpretar los ensamblados generados. Ademas, se ha explicado cómo es -
cribir codigo diente COM en C#. incluyendo llamadas a métodos COM y a traba -
jar con propiedades COM Como puede ver. NET Framework permite integrar
fácilmente el codigo COM existente en sus aplicaciones NET. Esta sencilla inte -
gración le da la oportunidad de mover poco a poco partes de una aplicación a
NET. sin tener que reescribir toda la lógica de componentes COM de C#.
35 Cómo
trabajar
con servicios
COM+
Micr o so ft h a mej o r ad o mu c ho la f u nc io na lid ad d el s ub s is te ma C OM d e sd e q ue
se p ub li co p o r p ri mera ve z e n 1 9 9 3 . U na d e l a s mej o ras ma s si g n i fic at i va s i ntro -
d uc id a s e n el mo d elo d e p r o gr a mac ió n CO M s e in tro d uj o e n 1 9 9 7 co n l a p ub li ca -
ció n d e M icro so ft T r a n sac tio n Ser ver ( MT S) . MT S. p ub l ic ad o p o r p r i mera vez
co mo co mp le me n to d e W ind o ws NT 4 .0 . p er m ití a a lo s p ro gra ma d o re s d e sarro -
llar co mp o ne nt e s med i an te u n i n ter me d iar io q ue p ro p o r cio nab a tra n sac cio n es,
se g u nd ad b a sad a e n fu nc io n e s v ser v icio s d e a gr up a ció n d e re c urso s.
Co n l a p ub l ic ació n d e W ind o ws 2 0 0 0 . Micr o so ft el e vo e l mo d e lo d e p r o gra ma -
ció n q ue o frec ía MT S a u n s ub s is te ma d e p r i me r a cla se. C OM+ e s. e n gr an p ar te,
u na co mb i nac ió n d e l m o d elo d e p r o gr a ma ció n CO M trad icio n al y el mo d e lo d e
p ro gr a mac ió n MT S. P o r p r i me r a vez. W i nd o ws p ro p o rc io nab a co m p atib il id ad
co n lo s d o s co mp o ne n te s CO M ( o no co n f i g ur a d o s) trad icio n ale s co n c o mp o ne n -
te s atr ib uid o s (o co n fi g ur ad o s) d e l e st ilo MT S d irec ta me n te d esd e el si st e ma o p e -
rati vo . . NET Fr a me wo r k o f r ec e a mb o s e st ilo s d e co mp o ne n te s a lo s p ro g ra mad o re s
q ue e scr ib e n so ft ware b asad o e n co mp o n e nt es . Est e c ap i t ulo e s t ud i a co mo d es a -
rro lla r c la s e s C# q ue p u ed e n u sar se co mo co mp o ne n te s co n fi g urad o s co n C OM+ .
El espacio de nombres
System. EnterpriseServices
C ua lq ui er cla se C # p ue d e ser us ad a p o r cl ie n te s C OM co mo u n co mp o ne n te
CO M. i nd ep e nd ie n te me n te d e l ár b o l d e her e nc ia d e la cla se . La s c la se s C# so lo
p ued e n d er i var se d e S y s t e m . O b j e c t y se g u ir si e nd o u sad a s co mo co mp o ne n -
te s C OM. S i n e mb ar go , ap r o ve c har lo s ser v ic i o s C OM+ e n c la se s C# req ui ere
u na s no r ma s d e h ere nc ia mas r i g ur o sa s.
El es p a ció d e no mb r es S y s t e m . E n t e r p r i s e S e r v i c e s p ro p o rcio n a l a s
cla se s, e n u mera cio n es, es tr uct u r a s, d e le gad o s e in ter face s ne ce sar ia s p ara e scr i -
b ir ap l ica cio ne s q ue ap r o ve c he n CO M+ y s u s ser vi cio s d e n i vel d e e mp re sa. Si ha
esc r ito co mp o ne nt e s e n C+ + o V i s ual B a s ic 6 q u e t er mi n ará n ej ec u tá nd o se e n u n
tie mp o d e ej e c ució n d e s er v ic io s C OM+ . la ma yo r p arte d e e st e cap í t ulo l e res u l -
tar a f a mi li ar De sd e el p u nt o d e vi st a d e u n p ro g ra mad o r d e C OM+ e xp e ri me n ta -
do. el esp ac io de no mb r e s System.EnterpriseServices co n tie n e la
f u nc io na lid ad a la q ue an te s te n ía acc eso med i an te p ro gra ma ció n. S i h a e s cr ito
co mp o n e nt es e n VB 6 . l e al e gr ar á sab e r q ue l a s c arac terí s ti ca s q ue a n ter i o r me nte
no e stab a n d isp o n ib l es , co mo l a a g r up ac ió n d e o b j eto s, a ho ra e st á n a s u e nt era
d isp o s ició n med ia n te Fr a me wo r k . Ser v icio s co mo la ac ti v ac ió n j us to a t ie mp o
( J I T ) . la a gr up a ció n d e o b j eto s, e l p r o ce sa mi e n t o d e tr a ns acc io ne s y la ad mi ni s -
tr ac ió n d e p ro p ied ad e s c o mp a r t id a s es tá n d isp o n ib le s co mo c la se s o atr i b ut o s e n
el e sp a cio d e no mb r e s S y s t e m . E n t e r p r i s e S e r v i c e s .
La t ab la 3 3 .1 d es cr ib e cad a u n a d e la s cla s es d isp o n ib l es e n el esp ac io d e
no mb r es S y s t e m . E n t e r p r i s e S e r v i c e s .
Clase Descripción
ApplicationAccessControlAttribute
Permite establecer una configuración
de seguridad para la biblioteca o apli-
cación de servidor que alberga la apli-
cación. No se puede heredar esta clase.
Clase Descripción
ApplicationActivationAttribute
Especifica si los componentes del en-
samblado se ejecutan en el proceso del
creador o en un proceso del sistema.
ApplicationIDAttribute
Especifica el identificador de la aplica-
ción (como GUID) para este ensambla-
do. No se puede heredar esta clase.
ApplicationNameAttribute
Especifica el nombre de la aplicación
COM+ que se utilizará para la instala-
ción de los componentes del ensam-
blado. No se puede heredar esta clase.
ApplicationQueuingAttribute
Habilita el uso de una cola para el en-
samblado marcado y permite a la apli-
cación leer llamadas a métodos desde
colas de Message Queue Server. No
se puede heredar esta clase.
AutoCompleteAttribute Marca el método con atributos como
un objeto A u t o C o m p l e t e . No se pue-
de heredar esta clase.
BYOT
Ajusta la clase B y o t S e r v e r E x y las
interfaces DTC ICr e a t e W i t h
TransactionEx y ICreateWithTip-
T r a n s a c t i o n E de COM+ . No Se puede
heredar esta clase.
ComponentAccessControlAttribute
Habilita la comprobación de seguridad
en las llamadas a un componente. No
se puede heredar esta clase.
COMTIIntrinsicsAttribute Permite pasar propiedades de contex-
to desde el Integrador de transaccio-
nes COM (COMTI) al contexto de
COM+.
ConstructionEnabledAttribute
Habilita la posibilidad de construcción
de objetos COM+. No se puede here-
dar esta clase.
ContextUtil
Obtiene información acerca del contex-
to de objetos de COM+. No se puede
heredar esta clase.
DescriptionAttribute
Especifica una descripción para un en-
samblado (aplicación), componente,
método o interfaz. No se puede here-
dar esta clase.
Clase Descripción
EventClassAttribute Marca la clase con atributos como una
clase de eventos. No se puede here-
dar esta clase.
EventTrackingEnabledAttribute Habilita el seguimiento de eventos para
un componente. No se puede heredar
esta clase.
ExceptionClassAttribute Establece la clase de excepción de cola
para la clase en cola. No se puede he-
redar esta clase.
IISIntrinsicsAttribute Permite el acceso a valores intrínse-
cos de ASP desde c o n t e x t u t i l .
G e t N a m e d P r o p e r t y . No se puede
heredar esta clase.
InterfaceQueuingAttribute
Habilita la capacidad de utilizar una
cola para la interfaz marcada. No se
puede heredar esta clase.
JustInTimeActivationAttribute Habilita o deshabilita la activación jus-
to a tiempo (JIT). No se puede heredar
esta clase.
LoadBalancingSupportedAttribute Determina si el componente participa
en el equilibrio de carga, en caso de
que el servicio de equilibrio de carga
de componentes esté instalado y habi-
litado en el servidor.
MustRunlnClientContextAttribute
Obliga a crear el objeto con atributos
en el contexto del creador, si es posi-
ble. No se puede heredar esta clase.
0bjectPoolingAttribute
Habilita y configura el agrupamiento de
objetos para un componente. No se
puede heredar esta clase.
PrivateComponentAttribute Identifica un componente como com-
ponente privado que sólo puede ser
visto y activado por otros componen-
tes de la misma aplicación. No se pue-
de heredar esta clase.
RegistrationErrorlnfo Recupera información de error exten-
dida sobre métodos relativos a múlti-
ples objetos COM+ . Esto también
incluye métodos que instalan, impor-
tan y exportan componentes y aplica-
Clase Descripción
La clase ServicedComponent
C ua lq ui er c la se d i se ñad a p ar a ap r o ve c har lo s s erv ic io s d e C OM + d eb e d eri -
var s e d ir ec ta me n te d e S e r v i c e d C o m p o n e n t o d e u na cl as e q ue te n ga
S e r v i c e d C o m p o n e n t en al g u na p ar te d e s u árb o l d e her e nci a. Lo d o s lo s
ser v icio s C OM+ q ue p u ed e us ar e st á n d i sp o n ib l es a s i g na nd o a trib u to s a la s cl a -
se s q ue s e d er i va n d e l a cla se S e r v i c e d C o m p o n e n t .
La c la se S e r v í c e d C o m p o n e n t no ad mi t e n i n g u na p ro p i ed ad ; si n e mb a r -
go . ad mi t e u n a s e ne d e m éto d o s p úb li co s q ue p ue d en ser i n vo cad o s p o r cl i en te s d e
cla se. La ma yo r p a r te d e e s to s méto d o s, i n cl u ye n d o A c t í v a t e ( ) .
D e a c t i v a t e () y C a n R e P o o l e d ( ) . d es cr ib e n lo s méto d o s d e fi n id o s p o r
in ter f ace s CO M+ . co mo I O b j e c t C o n t r o l . Esto s méto d o s so n v irt u ale s y p ue -
d en ser re e mp l azad o s p o r cla se s d er i v ad a s p ara p ro p o rcio nar fu n cio na lid ad esp e -
cí fi ca.
El l i stad o 3 5 .1 mu e st r a u na s e nci ll a c la se CO M + e scri ta e n C# . Est e o b j eto
p ar ti cip a e n el a gr up a mi en to d e o b j e to s CO M + .
Listado 35.1. Componente COM+ agrupable en C#
using System.EnteipiiseServices;
[assembly : As semblyKeyFile("keyfile.snk")]
[assembly:AssemblyVersion ("1.0.*") ]
ApplicationAccessControl
El a trib u to A p p l i c a t i o n A c c e s s C o n t r o l esp e ci fi ca si se p ued e co n fi -
g ur a r la se g urid ad d e u n e n sa mb lad o . E st e atr i b ut o reci b e u n v alo r b o o lea no q ue
d eb e s er T rue s i s e p er mi te l a co n f i g ur ac ió n d e se g ur id ad y F a l s e en ca so
co n tr ar io . E l v alo r p r ed eter mi n ad o no co n f i g ur ad o es F a l s e y e l valo r p red eter -
mi n ad o co n fi g urad o e s T r ue.
ApplicationActivation
El a trib u to Ap p l ica tio n Act i va t i o n es u n a t rib uto d e ni v el d e e n sa m -
b lad o q u e e sp ec i fi ca s i s e d eb e a gr e gar la cl as e a u na b ib lio tec a o a u na a p lic ació n
d e ser v id o r COM + . E l atr ib u to r ec ib e co mo p ara me tro u n t ip o d e e n u me rac ió n
lla ma d o Act i va tio n Op t io n q u e ad mi te lo s si g u ie n te s v a lo re s:
• Lib rar y. q ue e sp e ci f ic a u na ap l ica ció n d e b ib lio teca CO M+
• Ser ve r, q ue esp ec i fic a u na ap l ica ció n d e ser v id o r C OM+
El va lo r p red et er mi n ad o no co n fi g ur ad o e s Lib ra r y.
ApplicationID
El atributo Applica tio n ID puede usarse para especificar el GUID que se
va a asignar a la aplica ción COMt cread a pa ra que con tenga la clase COM+ . El
GUID se especifica u sa ndo su rep resen tac ión d e cadena, que se en vía al constru c -
tor d el atrib uto . El atribu to Applica tion ID debe ap licarse en el n ivel de
ensamb lado , co mo en e l sigu ien te frag men to d e códig o:
[ a s s e m b l y : Appl i c a t i o n I D ( " { E3868E 1 9 - 486E - 9 F 1 3 - F C 8 443 1 1 3 7 3 1 } " ) ]
public : class MyClass
{
}
ApplicationName
El atribu to App licatio nName se u tiliza para especificar el no mbre que se
va a asig nar a la ap lica ción COM+ creada pa ra que conteng a la clase COM+ . Se
le deb e proporcionar e l no mbre del construc tor del atribu to. Si se especifica este
atribu to en el cod igo. no se ra necesario u sar el argu mento /appn ame de la
herramienta de linea de co mando reg svcs.
El atribu to A p p l i c a t i o n N a m e debe ap licarse en el n ivel d e ensamb lado ,
co mo en el siguiente frag men to de c odig o:
[assembly : ApplícationName("MyName")]
public : class MyClass
ApplicationQueuing
El atributo A p p l i c a t i o n q u e u i n g se usa p ara esp ecificar que la clase
debe ser configu rada co mo un co mponen te en co la. El atrib uto no ad mite
parametros. El atribu to A p p l i c a t i o n Q u e u i n g debe ap licarse en el n ivel de
ensamb lado , co mo en e l sigu i en te frag men to d e codig o:
[assembly : ApplicationQueuing]
public class MyClass
{
}
{}
}
El atributo Au toCo mp lete no a cep ta pa ra metros, El v alo r por defecto para
el valor predeterminad o no configu rado e s Fa lse y para el valor p redetermin ado
configurado es T rue.
ComponentAccessControl
El atribu to Co mponen tAccessCon trol ae tiva o d esactiva las co mproba -
ciones de segundad en las lla mad as a instanc ias de clase, El atributo recibe co mo
parametro u n v alo r boo leano. que deb e ser Tru e si la co mpro bación del n ivel de
segundad d e la llamada debe e sta r a c túa o Fa1 se en caso contrario.
El atribu to Co mp onen tAccessContro 1 debe aplicarse en el n ivel d e cla -
se. co mo en el siguiente frag men to de cod igo.
[ComponentAccessControl]
public : class MyClass
{
}
El atribu to Co mpone ntAccessControl no acepta parametros, E l valor
por defecto para el va lor p rede te rminado no configurado es False y p ara el
va lor predeterminado c onfigu rado es T rue.
ConstructionEnabled
El atribu to ConstructionEnab led permite la construcción de o bjetos
COM+ . ti mecan ismo de constru cció n de ob j etos COM+ permite q ue se pueda
p a s a r u n a c a d e n a c o m o c a d e n a d e c on s t r u c t or a l a s i n s ta n c i a s d e o bj e t os c on
i n s ta n c i a s , E l a t r i b u t o n o e s p e c i f i c a l a c a d e n a : e n c a m b i o, s i m p l e m e n te p e r m i t e
q u e s e a d m i t a l a c o n s t r u c c i ón d e o bj e t os C O M + . E l a tr i b u t o a d m i t e u n v a l or
b o o l e a n o c om o p a r a m e t r o, q u e d e be s e r T r u e s i l a c on s tr u c c i ón d e o bj e t o s C O M +
d e b e e s t a r a c t i v a d a p a r a l a c l a s e y F a 1 s e e n c a s o c on tr a r i o . L a s c l a s e s d e C #
q u e a d m i t e n l a c on s t r u c c i ón d e o bj e t os d e be n i m p l e m e n ta r l a i n te r f a z
I O bj e c t C on s t r u c t . El método C o n s t r u c t () de la i n te r f a z es invocado
p or C O M + p a r a p a s a r l a c a d e n a d e c on s t r u c t or a l o bj e t o.
E l a t r i b u t o C o n s t r u c i t i o n E n a b l e d d e be a p l i c a r s e e n e l n i v e l d e c l a s e ,
c om o e n e l s i g u i e n t e f r a g m e n t o d e c o d i g o:
[ConstructionEnabled]
public class MyClass{
E l a t r i b u t o C o n s t r u c t i o n E n a b l e d n o a d m i te p a r á m e tr os . E l v a l o r p or
d e f e c t o d e l v a l or p r e d e t e r m i n a d o n o c on f i g u r a d o e s F a l s e y e l d e l v a l o r p r e d e -
te r m i n a d o c on f i g u r a d o e s T r u e .
JustlnTimeActivation
E l a t r i b u t o J u s t l n T i m e A c t i v a t i o n h a bi l i ta o d e s h a bi l i ta l a a c ti v a c i ón
p i s t o a t i e m p o ( J E T ) d e u n a c l a s e . E l a t r i bu t o a d m i t e u n v a l or b o o l e a n o c o m o
p a r a m e tr o, q u e d e b e s e r T r u e s i s e v a a p e r m i t i r l a a c ti v a c i ón J I T e n l a c l a s e y
F e l s e e n c a s o c on t r a r i o. L a a c t i v a c i ó n J I T s i e m p r e d e be e s ta r h a bi l i ta d a e n l os
o bj e t o s q u e t om a n p a r t e e n t r a n s a c c i o n e s .
E l a t r i b u t o J u s t I n T i m e A c t i v a t i o n d e be a p l i c a r s e e n e l n i v e l d e c l a s e ,
c om o e n e l s i g u i e n t e f r a g m e n t o d e c o d i g o:
[ JustInTimeActivation]
{
}
E l a t r i b u t o J u s t I n T i m e A c t i v a t i o n n o a d m i te p a r a m e tr os . E l v a l o r p or
d e f e c t o p a r a e l v a l o r p r e d e t e r m i n a d o n o c o n f i g u r a d o e s F a l s e y p a r a e l v a l or
p r e d e te r m i n a d o c on f i g u r a d o e s T r u e .
LoadBalancingSupported
E l a t r i b u t o L o a d B a l a n c i n g S u p p o r t e d h a bi l i ta o d e s h a bi l i ta l a c o m p a -
ti bi l i d a d c o n e l e q u i l i br i o d e c a r g a s d e u n a c l a s e . E l a tr i b u t o a d m i te u n v a l or
b o o l e a n o c o m o p a r a m e t r o q u e d e be s e r T r u e s i l a c o m p a ti bi l i d a d c on e l e q u i l i -
br i o d e c a r g a s v a a e s t a r h a bi l i t a d a y F a l s e e n c a s o c on tr a r i o.
El atr ib uto L o a d B a l a n c i n g S u p p o r t e d d eb e ap lic ars e e n el n iv el d e cl a -
se. co mo e n el s i g ui e nte fr a g me n to d e có d i go :
[LeadBalancingSupported]
public class MyClass
{
}
El atr ib uto L o a d B a 1 a n c i n g S u p p o r t e d no ad mi te p ara me tro s. El v a lo r
p o r d e fec to p ara el v al o r p r ed e ter mi n ad o no c o n fi g urad o es F a l s a y p ara e l
va lo r p red et er mi n ad o co n f i g ur ad o e s T r u e .
SecurityRole
El at rib u to S e c u r i t y R o l e esp eci f ica u na fu n c ió n d e s e g u nd ad . E s te atri -
b ut o p ued e ap li car se a u na c la se, u n mé to d o o u n e n sa mb l ad o co mp l eto . El co n s -
tr ucto r re cib e co mo ar g u me n to u n a c ad e na q ue d eb e e sp e ci fica r e l no m b re d e la
f u nc ió n a la q u e d eb e n p er te n ecer lo s mie mb r o s , co mo mu e s tra el si g u ie n te fr a g -
me n to d e co d i go .
[assembly : SecurityRole ( "MySecurityRole")]
public class MyClass
{
}
Propiedades ACID
P a r a q u e f u n c i on e n l a s t r a n s a c c i on e s , d e be n a te n e r s e a l a s p r o p i e d a d e s A C I D
A C I D e s e l a c r on i m o d e a t o m i c i d a d , c o h e r e n c i a , a i s l a m i e n t o y p e r m a n e n c i a L a
ta b l a 3 5 . 2 d e s c r i b e l a s d e f i n i c i on e s d e l a s p r op i e d a d e s A C I D .
Propiedad Descripción
na mespace TransactionSupport
{
using System;
using System.Data.SqlC1ient;
u s i n g Sy s t e m . E n t e r p r i s e S e r v i c e s;
[ T r a n s a c t i o n ( T r an s a c t i o n O p t i o n . R e q u i r e d ) ]
public class ExAirMain : ServícedComponent
{
pub1ic void Process ()
{
/* lla ma a métodos para agregar información de Food y de
Ticket. */
A d d F o o d p r o c e s s 1 = n e w AddFood ( ) ;
AcidAirline process2 = new AddAirline () ;
process1.Add();
process2.Add();
}
}
[ Transaction (TransactionOption.Supported) ]
[ AutoComp1ete]
public class AddFood : ServicedComponent
{
public void Add ()
{
SQLConnection cnn = new
SQLConnection ( "FoodSupplíerConnection");
SQLCommand cmd = new SQLCommand() ;
cnn.Open();
c md .A cti veCo nnecti on = cnn;
c md .Co m ma ndT e xt = / / In tr oduce una i nstr uc ci ón en DB,
c md .E xecu teNon Quer y ( ) ;
cnn.Close() ;
}
}
[Transaction(TransactionOption.Supported) ]
[AutoComplete]
public class AddAirline : ServícedComponent
{
public void Add ()
{
SQLConnection cnn = new
SQLConnection ("AirlineConnection");
SQLCommand cmd = new SQLCommand() ;
cnn.open() ;
c md .A cti veCo nnecti on = cnn;
c md .Co m ma ndT e xt = " " // Intr odu ce una i ns tr ucci ón e n D B
c md .E xecu teNon Quer y( ) ;
cnn.Close();
}
}
}
Propiedad Descripción
i
Activityld Obtiene un identificador GUID que representa la
actividad que contiene el componente
Application Id Obtiene un identificador GUID para la aplicación
actual
Propiedad Descripción
DisableCommit Asigna a los bits consistente y hecho el valor False
en el contexto de COM+.
EnableCommit Asigna al bit consistente el valorTrue, y al bit hecgi
el valor F a l s e , en el contexto de COM+
Propiedad Descripción
Listado 35.4. C ó m o a c c e d e r a l c o n t e xt o C O M + m e d i a n t e l a c l a s e C o n t e x t U t i l
using System.Reflection;
using System.EnterpriseServices;
[assembly:AssemblyKeyFile("keyFile.snk")]
[assembly:AssemblyVersion("1.0.*")]
[objectPooling(5, 10)]
[Transaction(TransactionOption.Required)]
[SecurityRole("ClientPole")]
public RooledClass()
{
RooledClass ()
{
}
public ov erride bool CanBeRooled()
{
return true;}
p u b l i c o v e r r i d e v o i d Act i v a t e ( )
{}
p u b 1 i c o v e r r i d e v o i d D e a c ti va t e ( )
{
}
pub1ic v oid DoWork()
{
bool IsInRole;
IsInRole = ContextUtil.IsCallerInRole("ClientRole") ;
if(IsInRole == true)
ContexyUtil.SetComplete();
else
C o n t e x t U t i l . S e t Abo r t () ;
Resumen
La e xp o s ició n d e u n a c l ase C# co mo si f uera u n a ap l ic ació n CO M+ no s up o ne
ni n g ú n es fu erzo y r e s ul t a mas se n ci llo q ue i mp l e me nt ar la mi s ma fu nc i o na lid ad
u sa nd o ve rs io ne s a n ter i o r es d e V i s u al St ud io 6 .0 . La s ap l ic acio n e s d e C OM+
esc r it a s e n V is u al C++ 6 .0 nec es it ab a n mu c ho ma s co d i go p ar a re al iza r la s mi s -
ma s tare as v al g u na s c ar acte r í st ica s d e C OM + ( c o mo la a gr up ació n d e o b j eto s) n i
siq u iera e st ab a n d isp o n i b le s e n Vi s ua l B a s ic 6 .0 .
El d e sarro llo d e co mp o ne n te s CO M t - med ia n t e C# i mp l ica c u atro se n cil lo s
co n cep to s :
na mespace ServidorObject
{
public class Class1: MarshalByRefObject
(
public string thisCustomer;
public Class1()
{
Console.WriteLine ("ServidorObject has been activated") ;
}
public string ReturnNa me (string customerID)
{
// Crea una conexión, envía al objeto al SQL
string strSQL =
("Select CompanyNa me from Customers " +
cn.Open();
while (rdr.Read())
{
Console.WriteLine(thisCustomer,
" was returned to the client ");
Clase Descripción
ActivatedClientTypeEntry
Almacena valores de un tipo de objeto regis -
trado en el cliente como un tipo que puede
activarse en el servidor
ActivatedServiceTypeEntry Almacena valores de un tipo de objeto regis -
trado en el servicio como un tipo que puede
activarse cuando se solicita desde un cliente
Clase Descripción
ObjectHandle
Ajusta referencias de objetos calculadas por
valor, de este modo, se pueden devolver a
través de un direccionamiento indirecto
ObjRef Almacena toda la información relevante ne-
i
SoapServices
Proporciona varios métodos para utilizar y
publicar objetos remotos en formato SOAP
TypeEntry
Implementa una clase base que contiene la
información de configuración utilizada para
activar una instancia de un tipo remoto
WelIKnownClientTypeEntry
Contiene valores de un tipo de objeto regis-
trado en el cliente como objeto de tipo cono-
cido (llamada única o singleton)
WelIKnownServiceTypeEntry Contiene valores de un tipo de objeto regis-
trado en el servicio como objeto de tipo co-
nocido (llamada única o singleton)
Aunque no use todas estas clases cuando escriba aplicaciones remotas, varias
de estas clases son extremadamente importantes para implementar una infraes -
tructura remota; es decir, la clase O b j R ef . la clase R e mo t i n gC o n f i gu ra t i -
o n . la clase R e mo t i n gS er v i c es y la enumeración We1 l Kn o wn Ob j ect Mo d e.
Aprenderá más de cada una de ellas más tarde en esta misma sección mientras
escribe el codigo de su aplicación anfitriona.
P ar a e mp ez ar a e scr ib ir la ap l ica ció n a n f itr io na . d eb e co mp re nd er lo q ue la
in f r ae s tr uct u ra r e mo ta n ece si ta p ar a f u n cio na r P ara re co rd ar le lo s p a so s ne ce sa -
rio s p ar a crea r la ap l ic ac ió n a n f i tr io na . r e vi se lo s s i g ui e nt es p a so s re se ña d o s co n
an ter io r id ad :
1. Esp e ci fiq ue lo s ca na le s y p u er to s q ue r eco d i fi ca n lo s o b j e to s e n tre el ser vi -
d o r y el cl ie n te.
2. Us e fo r ma te ad o r e s p ar a esp e ci f ica r e l fo r ma to e n e l q ue lo s d a to s so n
se ña li zad o s y d e s er ia li z ad o s e ntr e el s er vid o r y el c li e nte .
3. Det er mi n e có mo s e ac ti v an lo s o b j eto s d e l s er vid o r y c uá n to d ura la act i v a -
ció n.
Las s i g ui e nte s se cc io ne s es t ud i a n cad a u no d e e s to s p a so s.
Método Descripción
AsyncDispatchMessage
De forma asincrónica, envía el mensaje
especificado a las cadenas del servidor,
en función de la dirección URI incrustada
en el mensaje
Método Descripción
GetChannel
Devuelve un canal registrado con el nom-
bre especificado
GetChannelSinkProperties
Devuelve una ID i c t i o n a r y de propie-
dades para un proxy determinado
GetUrlsForObject
Devuelve una matriz de todas las direc -
ciones URL que pueden utilizarse para
alcanzar el objeto especificado
RegisterChannel Registra un canal con los servicios de
canal
SyncDispatchMessage
De forma asincrónica, envía el mensaje
de entrada a las cadenas del servidor, en
función de la dirección URI incrustada en
el mensaje
UnregisterChannel Anula el registro de un canal determina-
do de la lista de canales registrados
Clase Descripción
TcpChannel
Proporciona una implementación de un canal emi-
sor-receptor que utiliza el protocolo TCP para trans-
mitir mensajes. Esta clase es una combinación de
la clase T c p C I i e n t C h a n n e l y la Clase T c p S e r -
v e r C h a n n e l , lo que permite la comunicación en
ambos sentidos a través de TCP.
TcpCIientChannel
Proporciona una implementación de un canal de
cliente que utiliza el protocolo TCP para transmitir
mensajes.
TcpServerChannel Proporciona una implementación de un canal de
servidor que utiliza el protocolo TCP para transmi-
tir mensajes.
Clase Descripción
HttpChannel Proporciona una implementación de un
ca-
nal emisor-receptor que utiliza el
protocolo
HTTP para transmitir mensajes. Esta
clase
es una combinación de las clases H t t p
ClientChannel HttpServerChannel, lo
HttpCIientChannel que permite la implementación
comunicación ende ambos
Proporciona una un ca-
sen-
nal de cliente que utiliza el protocolo HTTP
tidos
para a travésmensajes.
transmitir de HTTP.
HttpRemotingHandler Implementa un controlador ASP.NET que
envía solicitudes al canal HTTP remoto.
using System;
using System.Runtíme.Remoting;
using System.Runtíme.Remoting.Channels;
using System.Runtíme.Remoting.Channe1s.Tcp;
using System.Runtíme.Remoting.Channels.Http;
namespace Client
{
/// <summary>
/// Descripción resumida de Class1.
/// </summary>
class RemotingClient
{
[STAThread]
static void Main(string[] args)
{
TcpChannel chan1 = new TcpChannel(8085);
ChannelServices.RegisterChannel(chan1);
Método Descripción
Configure
Lee el archivo de configuración y
configura la infraestructura remo-
ta.
GetRegisteredActivatedClientTypes
Recupera una matriz de tipos de ob-
jetos registrados en el cliente como
tipos que se activarán de forma re-
mota.
GetRegisteredActivatedServiceTypes
Recupera una matriz de tipos de ob-
jetos registrados en el servicio que
se pueden activar cuando lo solici-
ta un cliente.
GetRegisteredWelIKnownClientTypes
Recupera una matriz de tipos de ob-
jetos registrados en el cliente como
tipos conocidos.
GetRegisteredWelIKnownServiceTypes
Recupera una matriz de tipos de
objetos registrados en el servicio
como tipos conocidos.
GetType (heredado de Object) Obtiene el tipo de la instancia ac-
tual.
IsActivationAllowed
Devuelve un valor booleano que in-
dica si el tipo especificado está au-
torizado para ser cliente activado.
IsRemotelyActivatedClientType
Sobrecargado. Comprueba si el tipo
de objeto especificado se registra
como tipo de cliente activado de
forma remota.
IsWelIKnownClientType
Sobrecargado. Comprueba si el tipo
de objeto especificado está regis-
trado como tipo de cliente conoci-
do.
RegisterActivatedClientType
Sobrecargado. Registra un tipo de
objeto en el cliente como un tipo
Método Descripción
que se puede activar en el servi-
dor.
RegisterActivatedServiceType Sobrecargado. Registra un tipo de
objeto en el servicio como un tipo
que se puede activar a petición del
cliente.
RegisterWelIKnownClientType
Sobrecargado. Registra un tipo de
objeto registrado en el cliente como
objeto de tipo conocido (llamada
única o singleton).
RegisterWelIKnownServiceType Sobrecargado. Registra un tipo de
objeto en el servicio como objeto
de tipo conocido (llamada única o
singleton).
Propiedad Descripción
Obtiene el ID de la aplicación que se ejecuta ac-
Applicationld
tualmente
ApplicationName Obtiene o establece el nombre de una aplicación
remota
Processld Obtiene el ID del proceso que se ejecuta actual-
mente
using System;
using System.Runtíme.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
using System.Runtime.Remoting.Channels.Tcp;
using ServidorObject;
namespace Servidor
{
/// <summary>
/// Descripción resumida de Classl.
/// </summary>
class Class1
{
/// <summary>
/// El principal punto de entrada a la aplicación.
[STAThread]
ChannelServices.RegisterChannel(chanl );
RemotingConfiguratíon.RegisterWellKnownServiceTypef
typeof (ServidorObject.Classl), "ReturnName" ,
We11KnownObjectMode.SingleCall);
}
}
}
Como la aplicación anfitríona es una aplicación de consola, agregue la ins -
trucción C o n s o l é . R e a d L i n e al final para que la ventana de la consola per -
manezca abierta mient ras los obj etos están usando el obj eto remoto. La duración
del canal es la cantidad de tiempo que la ventana per manece abiert a Tras cerrar
la ventana de consola, el canal se destruye y t er mina la concesión de ese canal en
particular en el entorno remoto.
La enumeración W e l l K n o w n O b j e c t M o d e contiene dos miembros que de -
finen como se crean los obj etos. Si W e l l K n o w n O b j e c t M o d e es S i n g l e C a l l .
cada petición de un cliente es atendida por una nueva instancia de obj eto. Esto
puede representarse mediante el siguiente pseudo -codi go:
Cree objeto X
Llame al método del objeto X
Devuelva datos al llamador
Destruya el objeto X
Recogida de elementos no utilizados
Este bucle continua hasta que el canal con el que se registra este obj eto en el
entorno remoto es dest ruido.
Dependiendo del tipo de aplicación que este e scribiendo, deter mine el modo
de
activación que debe usar según los si guientes factores:
• Coste: Si crear el obj eto remoto consume recursos y tiempo, usar el modo
SingleCall puede no ser el modo mas efectivo de crear su obj eto. \a
que el obj eto es destruido después de que cada cliente lo use.
• Información de estado: Si esta almacenando i nfor mación de estado, como
propiedades, en el obj eto remoto, use obj etos Singletón, que pueden
mantener datos de estado.
<configuration>
<system.runtime.remoting>
<application>
< lifetime>
<channels> (Instance)
< channel> (Instance)
<serverProviders> (Instance)
<provider> (Instance)
<formatter>(Instance)
<clientProviders> (Instance)
<provider> (Instance)
<formatter> (Instance)
<client>
<wellknown> (Client Instance)
<activated> (Client Instance)
<service>
<wellknown> (Service Instance)
<activated> (Service Instance)
<soapInstance>
<interopXmlType>
<interopXmlElement>
<preLoad>
<channels> (Template)
<channel> (Template)
<s erverProviders> (Instance)
<provider> (Instance)
<formatter> (Instance)
<clientProviders> (Instance)
<providers> (Instance)
<formatter> (Instance)
<channelSinkProviders>
<serverProviders> (Template)
< p r o v i d e r > ( T e m p l a t e )
<formatter> (Template)
<clientProviders> (Template)
<provider> (Template)
<formatter> (Template)
<debug>
Aunque hay muchas opcio nes en el archi vo de confi guración, solo necesita
usar las necesarias par a su aplicación.
Por ej emplo, el fragmento de codi go del listado 36.5 representa un archivo de
configuración para un obj eto activado con HTTP que es un obj eto de modo
Sing1e Ca ll.
<configuration>
<system.runtime.remoting>
<application>
<channels>
<channel ref = "http" />
</channels>
Tras crear el archivo de confi guración, la operación de crear el obj eto anfi -
trión es mucho mas simple que registrar un obj eto con el mé todo Register -
WellKnownServiceT ype() de la clase RemotingConfi g u r a t ion. El
codigo del listado 36.6 muestra cómo registrar el obj eto mediante el método
Con-
f i g u r e ().
na mespace Servidor
{
/// <summary>
/// Descripción resumida de Ciassl.
/// </summary>
c1ass C1ass1
{
/// <summary>
/// El principal punto de entrada a La aplicación.
/ / / < / s ummar y >
[STAThread]
static void Main(string[] args)
{
R e m o t i n g C o n f i g u r a t i o n . C o n f i g u r e ( " Se r v i d o r . E x e . C o n f i g " ) ;
}
}
}
Como puede ver. el código se ha visto reduci do de quince lineas a una
La tabla 36.7 recoge todos los elementos disponibles y sus usos para el esque -
ma de archi vos de configuración remota.
Tabla 36.7. Esquema para el archivo de configuración de valores remotos
Elemento Descripción
Hasta ahora, este capi tulo ha explicado los entre sij os de la creación de la
aplicación anfitriona que registra el obj eto remoto con el entorno remoto. Ahora
debe escribir la aplicación cliente que realiza l as peticiones al obj eto remoto, que
es el tema de la si guiente sección.
Método Descripción
CreateComlnstanceFrom
Crea una instancia del objeto COM cuyo nom-
bre se especifica, utilizando el archivo de en-
samblado con nombre y el constructor que mejor
coincida con los parámetros especificados
Createlnstance
Sobrecargado. Crea una instancia del tipo es-
pecificado utilizando el constructor que mejor
coincida con los parámetros especificados
CreatelnstanceFrom
Sobrecargado. Crea una instancia del tipo cuyo
nombre se especifica, utilizando el archivo de
Método Descripción
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting .Channels ;
u s i n g S ystem.R . u n t i m e . R e m o t ing.Channel s. T c p ;
u s i n g S e r v i d o r O b j ec t ;
namespase Client
{
/// <summary>
/// Descripcion resumnida de Class1
/ / / < / s a m ma r y >
class RemotingClient
{
[STAThread]
static void Main (string[] args)
{
ChannelServices.RegisterChannel(new TcpChannel());
ServidorObject.Class1 x = (Class1)Activator.GetObject(
typeof( Cl ass1),
"tcp://localservidor:8085/ReturnName",null);
Console.WriteLine(x.ReturnName ("ALEKI"));
Console.ReadLine();
í
}
}
Tras escribir el cliente, puede ejecu tar la aplicación Servido r.exe y. a con ti -
nuación . ejecutar la ap lic ación clien te : y d eb ería ob ten er uno s resu ltados simila -
res a lo s de la figura 3 6.2.
La aplicación anfitrio na permanec e sie mpre abierta, o hasta que la cierre,
mien tras cada llamada de c lien te a la aplica ción anfitriona de vuelve el no mb re d e
la co mpañ ía para A LFKI c usto me rID. qu e es la iden tificación de co mprado r
pasada a la ap licación cliente.
Si deja el cod igo de la ap lic ació n an fitrion a original en modo Sin gleCall.
cada vez qu e ejecu te la ap licac ión c lien te, el objeto servidor se destru irá y se
vo lverá a crear.
Al camb iar WellKnown ObjectMo de a S ing letó n. observara la d ife -
rencia en tre lo s modos SingleCall v S ing le ton.
F2 siguiente frag me nto de cod igo muestra la ap licación an fitriona que crea el
objeto en modo S ing le ton:
RemotingConfiguration.RegisterWellKnownServiceType(
typeof (ServidorObject.Class1, "ReturnName",
WellKnownObjectMode.SingleCall ;
Resumen
Est e cap í t ulo a na li za d eta llad a me n te el e nto r n o re mo to d e .NET . Al u sar el
en to r no r e mo to , p ued e a cti v ar o b j eto s a tr a v és d e l i mi te s d e p ro ce so , d o mi n io s d e
ap li cac ió n y li mi t es d e eq u ip o . Si v a a i mp le me nt ar u n e nto r no r e mo to , ha y al g u -
no s te ma s ma s a va nz ad o s e n e l SD K q u e q u iza s le co n ve n ga l eer a nt es d e e mp e -
zar :
• Cre ar fo r mat ead o r e s d e u s u ar io : P ued e cr ear f o r mate ad o re s d e u s uar i o s i
lo s fo r ma tead o r es T CP y HT T P no s at i s fac e n s u s ne ce sid ad e s d e us o d e
d ato s B usq u e Re cep to r e s y Cad e na s d e rec ep to r es e n F ra me wo r k SD K
• Acce so re mo to a si n cr ó n ico : El acc eso re mo to e s o t ra tec no lo gía NET co n
cap ac id ad e s a s i ncr ó ni cas i nte g r ad a s. B u sq ue RP C as i ncró n ico en
Fra me wo r k SD K p ar a a p r end er a us ar d el e gad o s y e v e nto s co n p ro c ed i -
mi e nto s r e mo to s.
Ha y mu c h a s b ue na s r az o ne s p ar a e s t ud ia r lo s acce so s re mo to s, p ero a nt e s d e
e mp eza r, a se g úre s e d e es t ud iar la s c ap ac id ad e s d e lo s ser v ic io s W e b XM L v
ASP . NET p ara co n se g u ir co mu n ic ació n e n tr e p ro ce so s. P ued e a ho rr a rse mu c ho
tie mp o y e s fuer zo cr ea n d o la s ap li cac io ne s a n fi trio na s y mo d i fica nd o e l mo d o e n
q ue s u s cl ie nt es d i st a nci an o b j eto s.
37 c#
y seguridad
.NET
U na d e la s co sa s má s i mp o r ta n te s q ue d eb e re co rd ar c ua n d o s e tr as la d e a C# y
NET Fr a me wo r k es la se g ur id ad . D eb e a se g ur ars e d e q ue c u a nd o cre e ap li cac io -
ne s co n n - ni v ele s. la s eg ur id ad se a d e la má x i ma p r io rid ad p o rq ue la s p ro b ab il i -
d ad es d e q u e se p ro d u zca u n a b r ec h a e n u na ap l ica ció n d is tr ib uid a so n mu c h o
ma yo r e s q u e e n u na ap lic ació n i nd ep e nd ie n te. E s p o r e sto q ue NET Fr a me w o r k
se cr eo p e n s a nd o e n la s e g ur id ad , lo q ue s e r eflej a e n cad a asp ec to d el e nto r no .
NET Fr a me wo r k e s ca p az d e ej ec ut ar s e d e ma n era re mo ta, re al iza r d e sca r ga s
d in á mi ca s d e n ue vo s co mp o ne n te s e i ncl u so d e ej ec uc ió n d i n á mi ca Co n es te t ip o
d e e nto r no , si u n p ro g r a mad o r d eb e cr ear el m o d elo d e s e g urid ad , p ro b ab le me n te
tard e ma s e n co d i ficar lo q u e e n c r ear el p r o p io p ro gr a ma.
C ua nd o cr ee ap li cac io n es, e l mo d e lo d e s e g u n d ad s ue le b a sar se e n el ni v el d e
u s uar io o e l ni ve l d e g r up o . La ap lic ac ió n r ea liz ara c ier ta s a ccio n es o no . NET
Fra me wo r k p ro p o r cio n a a lo s p r o gr a mad o r e s med io s p ara seguridad basada en
funciones, q ue trab aj a d e u n a ma ne r a mu y p a recid a a l a s e g u nd ad d e n i ve l d e
u s uar io y d e ni ve l d e g r up o . La se g ur id ad b a s ad a e n fu nc io ne s s e p u ed e r es u mir
en p r i nc ip io s e id e nt id ad es , a u nq ue t a mb i é n p ro p o rcio na se g u nd ad d e n i vel d e
co d i go . a l q u e se h ac e r e fer e n cia g e ner a l me nt e co mo segundad de acceso a códi-
go o segundad basada en pruebas.
C ua nd o u n u s ua rio i nic i a u n a ap lic ac ió n q ue u s a se g u nd ad d e acc eso a co d i go .
p ued e t e ner acc eso a u n r ec ur so ( p o r ej e mp lo , u n a u n id ad d e red ) , p ero s i el
có d i go co n te n id o e n la a p lic ació n no e s f iab le, e l p ro gra ma no p u ed e a cc ed er a la
u ni d ad d e red . E s te t ip o d e se g u nd ad se b a s a e n co d i go mó vi l. Q u iza s n o q ui era
u sar u na ap l ica ció n mó v il y d ej ar a e sa ap l ica ció n q ue acc ed a a to d o s lo s rec ur so s
a lo s q u e se h a e n co m end ad o . La se g u nd ad b asad a e n fu nc io ne s e v it a q ue lo s
p r o gr a mad o re s ma li n te n cio nad o s e scr ib a n ap li ca cio ne s q ue p ued a n ej ec ut ars e co mo
si la s e st u v iéra mo s ej ec ut a nd o no so tr o s y r ea li z ar to d o t ip o d e a ccio n es en n ue s -
tr o eq u ip o lo cal o a tr a v és d e n ue str a r ed co r p o r ati v a.
La se g u nd ad d e NET Fr a me wo r k se co lo ca so b r e la s e g urid ad ya p res e nt e e n
s u s i ste ma o p er at i vo ( O S) . E st e s e g u nd o n i ve l d e se g ur id ad e s mu c ho m ás e x te n -
sib le q ue la s e g u nd ad OS. A mb o s t ip o s d e se g ur id ad . O S y N ET Fr a me wo r k .
p ued e n co mp le me n ta r s e en tr e s i.
Est e cap i t ulo l e a cer c a a var io s t e ma s r el acio n a d o s co n l a se g ur id ad , c o mo el
u so d e fu ncio n e s d e W i nd o ws p ar a d et er mi n ar p er mi so s. Ap re nd erá a s o lic it ar y
d en e gar p er mi so s d e ntr o d el co d i go mi e n tr a s re a liz a o p era cio ne s d e r e gi stro . P o r
ul ti mo , ap re nd erá a u sar p er mi so s b as ad o s e n at rib uto s p ara d e fi n ir lo s d erec ho s
d e s u co d i go e n t ie mp o d e ej ec uc ió n.
Seguridad de código
La se g u nd ad d e ac ce so a có d i go d et er mi n a s i se p er mi te a u n e n sa mb lad o
ej ecu tar se b as á nd o s e e n v a na s u n id ad e s d e p r ue b a, co mo la U R L d e l a q ue p ro ce -
d e el e n sa mb lad o y q uie n a u to r iz a el co n tr o l. Al i n sta lar NET Fra me wo rk. e s tá n
co n f i g urad o s lo s p er mi s o s p r ed et er mi n ad o s , lo q ue red uc e e no r me me n t e la s p o s i -
b ilid ad e s d e q u e u n co n t r o l q ue no e s d e co n f ia n za p ro ced e n te d e I nter n e t o d e u n a
in tr a n et lo ca l p ued a ej ec ut ar se e n s u eq u ip o . P u e d e h ab er vi s to e sto si h a in te n ta -
d o ej ec ut ar a l g u na s ap li cacio n e s o u sar al g u no s co n tro l es d e sd e u na u n i d ad d e r ed
q ue e xij a p ri v il e gio s d e se g ur id ad e sp e cia le s. E s to s p r i vi le g io s d e se g u n d ad esp e -
cia le s i nc l u ye n la e sc r it ur a e n u n ar c h i vo d e d i sco , leer o es crib ir e n y d esd e el
reg i str o , ad e má s d e o p e r acio n es d e r ed . No r ma l me n te r ecib irá u n a e xc e p ció n d e
se g u nd ad co mo l a s si g u i en te s c ua nd o i n te nt e h ac er e s ta s a cc io ne s si no c a mb ia l a
d ir ec ti v a d e se g u nd ad p ar a q ue p er mi ta e ste ti p o d e co mp o r ta mi e n to :
Unhandled Exception: System.Security.SecurityException : Request
for the permi.ssion of type
System.Secunty.Permissions.FileIOPermission
Permisos de código
El C LR . c u a nd o co nce d e p er mi s o s d e se g u nd ad , so lo co n ced e p er m iso s al
co d i go e n l a s o p er ac io n es q ue se l e p er mi te r ea liz ar. El C LR u sa o b j e t o s lla mad o s
permisos p ar a i mp le me n t ar es te tip o d e s e g ur id a d en co d i go ge s tio n ad o . Eo s p r i n -
cip a le s u so s d e lo s p er m iso s so n lo s s i g ui e nte s :
• E l có d i go p ued e so l ic itar lo s p er mi s o s q ue p rete nd e u sar o q u e p o s i b le me n -
te ne ce si te. .N ET Fr a m e wo r k ti e ne la tar ea d e d ete r mi n ar si e sta s p et icio -
ne s so n val id a s. La s p e t icio n es d e se g u nd ad se co n ced e n so lo si l as p r u eb as
r eco gid as d e l có d i go lo p er mi t e n. El co d i go n u nc a r ec ib e ma s p er mi so s d e
lo s p er mi tid o s p o r l a se g ur id ad a ct ua l. P o r o tra p ar te, e l co d i go p ued e
r ecib ir me no s p er mi s o q ue el e sp ec i fi cad o e n la p eti ció n.
• E1 C LR co nced e p er mi s o al co d i go b a sá nd o se e n var io s fac to re s : La id e n -
tid ad d e l co d i go ( co mo la U R L d e l a q u e fu e o b te nid o , q u ie n e scr ib ió el
co d i go y s i mi lar e s) , lo s p er mi so s q u e se so li ci t an y la ca nt id ad d e có d i g o
q ue e s d e co n fia n za, d e f in id a p o r l as d i fer e nt es d irec ti v as d e se g ur id ad .
• E1 co d i go p u ed e hac er u na p e ti ció n p ar a o b te n er u n d e ter mi n ad o p er mi so .
Si se rea li za u na p et ic ió n med ia nt e el co d i go . to d o el co d i go q ue s e ej ec ut e
en e l co nt e xto d e l a ap l i cació n d eb e te n er acc es o al p er mi so p ara q ue e s te
sea co nc ed id o .
E1 co d i go p u ed e r ecib ir tr e s c la se s d e p er mi so s, cad a u no d e lo s c ual es t i en e u n
p r o p o si to e sp e ci fico :
• Lo s p er mi s o s d e co d i go d e acce so r ep r es e nt a n el acce so a u n r ec ur so p ro -
te gid o o la a uto r id ad p ar a r ea li zar u na o p era ció n p ro te g id a.
• Lo s p er mi so s d e id e nt i d ad i nd ica n q ue el co d i go ti e ne cred e n ci ale s q ue
ad mit e n u n t ip o d e id e n tid ad p ar ti c ula r , co mo c o d i go q ue p ued e te ner u na
id e nt id ad " Ad mi n i str ad o r " y. p o r ta nto , ej ec ut ar se co n to d o s lo s p er mi s o s
q ue p ued a te ne r u n ad mi ni s tr ad o r .
• Lo s p er mi so s d e se g ur id ad b a sad a e n f u ncio n e s p ro p o rcio na n u n me ca n i s -
mo p ara d e sc ub r i r si u n u s uar io ( o el a ge n te q ue ac t úa e n no mb re d el
u s uar io ) t ie ne u n a id e nt id ad p ar ti c ular o es u n mi e mb ro d e u n car go es p e -
cí fi co . P ri n cip alP er mi s sio n e s el ú n ico p er mi so d e se g ur id ad b a -
sad a e n fu n cio ne s.
El ti e mp o d e ej ec u ció n p r o p o r cio n a cla s es d e p er mi so i nt e grad a s e n var io s
no mb r es d e e sp ac io s y p r o p o r cio na co mp a tib il i d ad p ara d i s e ñar e í mp l e me nt ar
cla se s d e p er mi so p er so na li z ad a s.
Seguridad de usuario
( as i to d o s lo s si st e ma s d e s e g ur id ad ac t ua le s i mp le me n ta n al go ll a mad o segu-
ridad de usuario. Esto s t ip o s d e s is te ma s d e se g u rid ad req u ier e n i n fo r ma ció n d e
lo s u s uar io s q ue so l ic it a n a cce so . P o r ej e mp lo , d eb e n s ab er q u ie ne s es a p erso n a y
a q u e el e me n to s tie n e ac ceso es e us u ar io . La se g ur id ad d e u s uar io d e se m p eñ a u n
p ap el mu y i mp o r ta nt e e n lo s s is te ma s co mp ut eri z ad o s p o rq u e, c ua nd o ej e cu ta u n a
ap li cac ió n e n s u eq uip o , la ap l ica ció n s u el e g uard ar l a id e n tid ad d e l a p e rs o na q ue
la e sta ej e c uta nd o . P o r tan to , s i ej ec u ta u na ap li cació n , es a ap l ica ció n t i en e to d o s
lo s d er e c ho s y p er mi so s e n s u eq uip o lo c al y a tra vé s d e la r ed q ue t e n d ría mo s
no so tr o s.
A d i fere n cia d e lo s ser v i cio s W i nd o ws , q u e l e p e r mit e n co n fi g urar q ui e n p are -
ce e sta r ej ec ut a nd o la a p lic ació n , u n a ap l ic ació n W i nd o ws no r ma l n u n ca hab í a
o to r gad o e s te tip o d e co nt r o l co n a n ter io r id ad . E st e hec ho ha fa ci li tad o l a p ro l i fe -
r ació n d e mu c ho s v ir u s y tr ó va no s a lo s q ue ti e ne n q ue e n fre nt ar se d iar ia me n te
lo s u s ua rio s y e mp r e s ar io s d e o r d e nad o r e s. Al p er mi tir le d e ter mi n ar el tip o d e
p er mi so q u e ti e ne n l as a p lic acio n e s e n s u eq uip o , se red uc e e no r me me n te la p o si -
b ilid ad d e u n at aq ue p o r p ar t e d e u n co d i go ma l ig n o . Op era cio n es, co m o le er e l
r eg i str o , so b r es crib ir ar ch i vo s d e si s te ma o r e co rrer s u l ib re ta d e d ir eccio n e s
p er so n al, no será n p o s i b le s. P ued e p r o b ar ráp i d a me nt e si s u s ap l ica ci o ne s se
ej ecu ta n s e g ú n e l us u ar i o q u e la s ej ec ut a p r o b a n d o el p ro gr a ma d el li s ta d o 3 7 .1 .
using System;
namespace SimpleSecurity
{
class Class1
{
[STAThread]
static void Main(string[] args)
{
Console.WriteLine ("I am currer.tly running a s : " ) ;
Console.WriteLine ("User : {0) " , Environment.UserName);
Console.WriteLine ("Doma in :
{0 }" , Environment.UserDomainName);
}
}
}
C ua nd o ej ec ut a e st e p r o gr a ma, d eb er í a ver el no mb r e q ue u sa p ara co nec tar se
a W i nd o ws, ad e má s d el no mb r e d e s u d o mi n io , c o mo mu e s tra la fi g ur a 3 7 .1 .
Figura 37.1. La clase Environment puede ser utilizada para tareas de seguridad
sencillas
3. Haga clic con el botón derecho del ratón en el cuadro a la derecha y selec -
cione Grupo nuevo. Llame a este grupo Developers. como muestra la
figura 37.3.
4. Tras crear este grupo, haga clic en el boton Agregar y agregue su cuent a
de usuario al grupo. Para este ej emplo, asegúrese de que no esta en el
grupo Administradores. Si es un administrador, quizas quiera probar la
siguiente aplicación con otra cuenta Windows.
Figura 37.3. Agregue un grupo Developers a Windows
Una vez que ha colocado el nuevo grupo, vamos a estudiar las clases
W i n d o w s P r i n c i p a 1 y W i n d o w s I d e n t i t y . Cuando se usan en conj unto,
estas dos clases pueden deter minar si el usuario actual de Windows pertenece a
algún grupo específico. Exami ne la aplicación de ej emplo del listado 37.2.
u s i n g S ys t e m ;
using System.Security.Principal;
c l a s s C l a s s1
{
static voíd Main ()
{
WindowsIdentity wi = WindowsIdentity.GetCurrent();
WindowsPrincipal wp = new WindowsPrincipal(wi);
usinq System;
usinq Microsoft.Win32;
usinq System.Security.Permissions;
class Class1
{
static void Main (string [] args )
{
try
{
ReqistryPermission regPermission = new
ReqistryPermission ( RegistryPermission Access.AllAccess ,
"HKEY _LOC AL MACHIME \\SOFTW ARE\\Microsoft:\\Windows
NT\\Current.Version") ;
reqPemission.Demand();
}
catch (Exception e){
Console.WriteLine(e.Message);
return;
}
RegistryKey myRegKey = Reqistry.LocalMachine;
myRegKey = myReqKey.OpenSubKey
(" SOFTWARE \\ Microsoft \\Windows NT\\CurrentVersion");
try
{
Object oValue = myReqKey.GetValue ("RegisteredOwner") ;
Console.WriteLine ("OS Registered Owner :
{0}", oValue.ToString());
catch (NullReferenceException)
{
}
}
}
No o l v id e q ue a u nq ue l a d ir ect i va d e s e g u nd ad NET p er mi t a a e s te c o d i go
ej ecu tar se , l a d ir ect i va d e s e g u nd ad d e l si s te ma o p erat i vo s ub yac e nt e t a mb ié n
debe concederle permiso para ejecutarse. Tras solicitar los permisos p ara la clave
del registro adecuada, sólo tiene que leer la clave RegistredOwner y mostrar
la información en la ventana de consola.
Denegación de permisos
Al igual que en el método Demand. también puede llamar al método Deny() .
que elimina los permisos para una operación. Por lo general, es aconsejable elimi -
nar antes de hacer la llamada cualquier permiso que sepa que no va a necesitar.
Puede solicitar permisos a medida que el código los vaya necesitando. Use el
método Deny( ) cuando haya completado una operación y sepa que ya no van a
ser necesarias mas operaciones.
La denegación de permisos tiene varias funciones. Por ejemplo, si esta usando
biblioteca de terceros, querrá asegurarse de que. tras manipular el registro, nin -
gún otro codigo pueda hacerlo. La denegación de permisos es un modo de conse-
guirlo.
El código del listado 37.4 usa una versión modificada del ejemplo anterior
para denegar en primer lugar un permiso de registro. Tras negar el permiso,
intenta leer la clave de registro, lo que da como r esultado una Security-
Exception. Si quiere deshacer una operación Deny en el codigo. solo tiene
que usar el método RevertDeny() para eliminar la denegación de permiso; y
cualquier intento posterior de leer la clave del registro solicitada se llevará a cab o
con éxito.
Listado 37.4. Denegación de permisos a los que no desea que se acceda
using System;
using Microsoft.Win32 ;
using System.Security.Permissions;
class Class1
{
static v oid Main (string[ ] a rgs)
{
try
{
RegistryPermission regPermission = n e w
RegistryPermission (RegistryPermission Access.AllAccess,
"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows
NT \ \ C ur rentV ers i on " ) ;
regPermission.Deny();
}
catch (Exception e)
{
Console.WriteLine (e.Hessaqe);
return;
RegistryKey myRegKey = Registry.LocalMachine;
myRegKey = myRegKey.OpenSubKey
("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion");
try
{
Object oValue=myRegKey.GetValue ("RegisteredOwner") ;
Console.WriteLine("OS Registered Owner:
{0} ",oValue.ToString());
}
catch (NullReferenceException)
{
}
}
}
Si e st á ej e c uta nd o e s te ej e mp lo e n Vi s ua l St ud i o . l a ap li cac ió n d eb erá d ete ne r -
se e n la s l í nea s d e ma ni p ul ac ió n d e l r e gi s tr o . A l ej ec u tar e st a ap l ica ció n d e sd e la
co n so la se g e ner a u na l ar g a l i st a d e er r o r e s d e e xcep ció n q u e i nd ica n cu al es el
p ro b le ma .
using System;
using Microsoft.Win32 ;
using System.Secunty.Permissions;
[RegístryPermissionAttribute(SecurityAction.Demand)]
class Class1
{
static void Main (string[] args)
{
RegistryKey myRegKey = Registry.LocalMachine;
myRegKey=myRegKey.OpenSubKey
("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersión");
try
{
Object oValue=myRegKey.GetValue ("RegisteredOwner");
Console.WriteLine ("OS Registercd Owner :
{0}", oValue.ToString());
}
catch (NullReferenceException)
{
}
}
}
Directiva de seguridad
Las d irec ti v as d e se g ur i d ad so n el co r azó n d e l a se g u nd ad b a sad a e n p ru eb as .
De sp ué s d e q u e se o b t ie ne u na p r ueb a d e u n e n s a mb lad o , e s e co d i go e s as i g nad o
a u n gr up o d e co d i go . E s te gr up o d e co d i go . a s u ve z. t ie ne u n co nj u nto d e p er mi -
so s q ue d e fi n e n lo q ue el co d i go p ued e y n o p u ed e hac er. No so lo p ued e m o d i fic ar
la d ir e ct i va d e s e g u nd ad p ar a q ue se aj us te a s us nec es id ad es, p ued e mo d i fic arl a
a v a n o s ni v ele s y p ued e cr ear gr up o s d e co d i go p erso n ali zad o s q u e co mp l e me nt e n
la s d ir ect i va s d e s e g ur id ad q ue ha d e fi n id o .
Figura 37.5. Los niveles de se solapan para determinar un nivel de seguridad final
Si cambia las directivas de su equipo para permitir ciertos tipos de opera cio-
nes de. por ejemplo, el código descargado de Internet, su administrador de red
puede aplicar una directiva de seguridad de empresa para prohibir esas operaciones.
Grupos de código
Todos los niveles de directivas de seguridad contienen grupos de codigo que. a
su vez contienen zonas para cada grupo de código. Este resultado es un ajuste de
configuración de segundad muy detallado a lo largo de todos los niveles de direc -
tivas y permite que hay a diferentes tipos de seguridad en cada nivel de directiva,
dependiendo de la zona del codigo en cuestión.
Inmediatamente dentro del grupo de código se sitúa un nodo A l l _ C o d e .
Como el propio nombre indica, estos conjuntos de permisos se aplican a todo el
codigo. Ademas de este nodo A l l _ C o d e . puede agregar más nodos para satisfa-
cer sus necesidades. Por ejemplo, puede crear nodos para el codigo que recibe de
los consultores o de cualquier otro tipo de fuente.
Cuando evalúe niveles de segundad, no olvide el modo en el que la directiva de
codigo se evalúa realmente. Los permisos para un ensamblado se unen a cada
nivel de directiva de segundad. Al unir todos estos permisos, debe trabajar con un
enorme conjunto de permisos. Cada uno de estos conjuntos de permisos se solapan
para que se pueda realizar una comparación y el valor mas restrictivo para cada
permiso se usa para el conjunto de permisos final.
Resumen
. NET Framewor k se basa en una inmensa cantidad de codi go de segundad que
vi gila cada aspecto de una aplicación o usua r io. Este entorno de segundad
per mi -
te a l p r o gr a mad o r y a l a d mi ni str ad o r d e e mp r es a co ntro lar lo q u e p er mi t e rea li zar
a u n a ap l ic ació n. He mo s e st ud iad o la s s e g ur id a d es d e id e n tid ad d e us u ario y d e
acce so a co d i go . Si se u sa n co nj u n ta me n te co n l a s e g urid ad d el s i ste ma o p erat i vo
s ub yac e nt e, p ued e cr e ar ap li cac io ne s ma s s e g ura s.
Parte VI
Apéndices
A Manual
de XML
A me no s q ue h a ya v i vid o en u na c u e va d ur a nte l o s úl ti mo s a ño s, ya h ab í a o íd o
hab lar d e XM L. S i n l u g ar a d ud a s. XM L ha r ec i b id o mu y b u e na s cri ti ca s, ma s d e
la s q ue mer ece . S m e mb ar go , a p e sar d e lo q ue p ued a hab er o id o e n a l g ú n l u stro so
fo lle to d e ma r ket i n g, n o es p r o b ab le q u e XM L. so l uc io ne el ha mb re d el mu n d o ,
tr ai g a l a p a z mu n d ial o c ur e to d a s la s e n f er me d ad es . T ras le er e s ta se cció n, d o -
mi n ar a la s b as e s d e XM L y s u s e st á nd ar e s a so c iad o s, co mo e sq ue ma s y esp ac io s
d e no mb re. E n p o ca s p a lab r a s. XM L e s u n d i ale cto SG M L s i mp l i fi cad o d is e ñad o
p ar a la mtero p erab il id a d y e st a co n s id er ad o e l AS CII d e l fu t uro . E n la u lt i ma
d écad a. AS CII ha s id o el es tá nd a r t r ad i cio n al p ara e l i nt erca mb io d e d ato s d e
te xto , p ero e s ta s ie nd o d esp la z ad o r áp id a me nt e p o r X M L. co mo el n ue vo es tá nd ar
A lo lar go d e e st a sec c ió n , ap r e nd er a a ap r eci ar la rara el e ga nc ia d e XM L: s u
co mb i na ció n ú ni ca d e p ur a se n ci lle z y p o te nc ia en b r uto . T a mb ié n ap re nd er a q u e
o tr o s e st á nd are s co mp l e me n ta n a XM L. La fa m ili a XM L d e e s tá nd ar e s co mp l e -
me n tar io s ha cre cid o mu cho e n lo s úl ti mo s a ño s , d e mo d o q u e. p ar a ab r e ví ar. so lo
le mo str are mo s lo s e st á n d ar e s ma s r ele v a nte s.
<html>
<head>
< ! --SITUE A Q U Í E L C O N T E N I D O D E L A C A B E C E R A
</head>
<body>
<!--SITUE AQUI EL CONTENIDO DEL CUERPO
</body>
</html>
HTML también consta de un mecanismo par a agregar mas infor mación a una
etiqueta, llamados atributos. Un atributo especifica una propiedad que pertenece
a una etiqueta, como el tamaño de una fuente Por ej emplo para que la palabra
"Meaning" aparezca con tamaño 4. debería escribir
<font size="4" >Meaning</font>
Como puede ver en el ej emplo anterior, los atr ibutos se escriben en la etiqueta
inicial v hay un espaci o de s eparación entre el nombre de la etiqueta y el nombre
del atributo. Estos toman la for ma
nombre_atributo=valor_cadena
En HTML. están predefinidos los atribut os que puede usar con cada etiqueta,
igual que las etiquetas. La etiqueta fuente, por ej emplo, tiene un atributo de tama -
ño. Los valores de atri buto deben estar entre comillas dobles o sencillas (no im -
porta cual de las dos use. mientras las comill as de apertura sean del mismo tipo
que las de cierre). Una etiqueta puede contener mas de un atributo Cada atributo
esta separado por un espacio. Por ej emplo, quizas quiera especificar el borde,
altura y anchura de una tabla, por ej emplo
<html>
<head>
<title>A Glossary in HTML>/title>
</head>
<body>
<h1> G1ossary</h1>
<div align="left">
<table border="1" width="359" height ="110">
<tr>
<td width="125" height="22">
<b><font size="4">Abbreviation</font></b>
</td>
<td width="234" height = "22">
<b><font size= "4">Meaning</font></b>
</td>
</tr>
<tr>
<td width="125" height = "22">
ADO
</td>
<td width="234" height="22 ">
<b>A</b>ctive <b>D</b>ata <b>O</b>bjects
</td>
</tr>
<tr>
<td width="125" height ="22”>
SOAP
</td>
<td width = "234" height ="22">
<b>s</b>imple <b>O</b>bject<b>A</b>ccess<b>P </
b>rotocol
</td>
</tr>
<tr>
<"td width="125" height = "22">
UDA
</td>
<td width=”234" height = "22">
<b>U</b>niversal<b>D</b>ata<b>A</b>ccess
</t d >
</tr>
<tr>
<td width="125" height = "22">
XML
</td>
<td width="234" height = "22">
e<b>x</b>tensible<b>M</b><rkup<b>L</b>anguage
</td>
</tr>
</table>
</div>
</body>
</ h t m l >
P r ó l o g o:
Declaracion XML (opcional)
<!--Situe aquí los comentarios -->
Document Type Declaration (optional)
<!-- Situe aquí los comentarios -->
C u e r po :
Document Element
<Document>
<!-- Situe aquí el documento>
</Document>
E p í l o g o:
<!-- Situe aquí los comentarios -- >
U n d o c u me n to X M L co mi e nz a co n u n p r ó lo go . Si e x cl u i mo s lo s co me nt ario s
o p cio na le s, el p ro lo go c o nt ie n e d o s el e me n to s p ri ncip al es (q ue t a mb i é n so n o p cio -
na le s) . La d e cl arac ió n XM L n ece s ita u n a tr ib uto , q ue s ir ve p a ra e sp eci ficar la
ver s ió n d e l a e sp ec i fi ca ció n X M L a la q ue se aj u st a el d o c u me n to . La d ecla rac ió n
XM L ta mb ié n t ie ne d o s atr ib u to s o p c io na le s: u n o p ara e sp ec i fi car la co d i fic ac ió n
d e car á ct er us ad a y o tr o p ar a e sp e ci f icar s i el d o cu me n to d ep e nd e d e u na d e fi n i -
ció n d e t ip o d e d o c u me n to ( DT D) . A co n ti n u ac i ó n t ie ne u n ej e mp lo d e u na d e cl a -
ració n XM L co mp le ta q ue u s a lo s tr e s a tr ib u to s.
. : • xm i VÍ- Í s i on " ! . u " enrod i ny-" UTF o" stancia 1 one - " yes"? •
: • 1
L i s t i d e l i r i o s s o b r e XMI. recomendados - - •
1: ■ ! < tomp i 1 <-i d o e i T’ d e M aro o del i 7, 3 0 3 0 por PGB
- - 7
4 : ■ XMI.Book.s ■
7 : • Book I 3 BU-”7 7 o ° 7 - 7 7 4 7 - c " " -
: ■ Ti t 1 e •XML By Exampl e* /Titiles-
'/ : • f.at eqory -Web Peve 1 opment-- /Cateqory •>
7: • A u t . h o r ' R p m i t Ha r c h a 1 / Au t h o r
B : -/Book
! 11 : - B o o k I BBM-" i - 8 7 K)i43-I 1-0 " >
11: - Tit lr-.-Pr o f e s s i on a 1 XML -s / T i t l e o
13: - (7 at.pqory - Internet'. /Category>
11: 'tiate g o r y ■ Internet P r o g r a nrm mg -i./Category >
1 4 : --oiat ego ryoy.MT.o /C!ategor\o
18: - Aut ho r • Pi chard Ande ns om/Author >
La l i nea ld e l l is tad o A. 3 co n ti e ne u na d ec lara c ió n co mp let a X M L q ue in cl u ye
lo s tr e s atr ib uto s. La s lí ne as 2 y 3 so n co me nt ario s u s ad o s e n es te caso p ara
ind ic ar la fu n ció n d e es t e d o c u me n to . A co nt i n u ació n e sta e l c u erp o d e l d o cu me n -
to X M L. e mp e za nd o p o r la li n ea 4 y t er mi n a nd o e n l a l i ne a 3 4 E s te d o c u me n to no
tie n e ep ilo go , co mo s ue le ser hab it ua l. El el e m en to d e d o c u me n to e sta e ntr e l a
etiq u eta <X M LB o o k s> ( la e tiq u eta d e i n ic io e st a en la lí n ea 4 . la et iq ue t a fi na l e n
la l i nea 3 4 ). El e le me n t o d e d o cu me nto ti e ne tr e s s ec u nd ar io s, cad a u no en cerrad o
en tr e u na e tiq ue ta <B o o k> El s ec u nd a r io 1 e mp i eza e n l a li ne a 5 y fi na liz a e n la
li ne a 4 . E l s ec u nd a rio 2 e mp ie za e n la li n ea 1 0 5 y fi na liz a e n la li n ea 2 7 . E l
sec u nd ario 3 e mp i eza e n l a l í nea 2 8 .5 y f i na li za e n l a l i nea 3 3 . C ad a ele me n to
B o o k - tie n e u n atr ib u to I SB N y u n a ca n tid ad d e se c u nd ar io s : u n o < Title >
u no o ma s < C ate go r y> y u no o ma s < Au t ho r> . Aq u í p u ed e ap re c iar se u na
ve n taj a s i g ni fica ti v a d e XM L so b r e lo s tr ad i cio n ale s arc h i vo s d e t e xto : XM L e st a
b ie n p r ep arad o p ara tr a t ar co n la s e str u ct ur as p r i mar io / sec u nd ario .
Co mo cread o r d el d o c u me n to , me r e c e la p e n a s eñ ala r q ue yo i n ve n té la s e ti -
q ue ta s y lo s at rib u to s us ad o s e n e st e d o c u me n to (XM LB o o k s. B o o k. ISB N. T it le.
Ca te go r y. Au t h o r). P o r ej e mp lo , o tr o a u to r p o d r a h ab er p r e fer id o u sar
< S h e l v i n g C a t e q o r y > en l u gar d e < C a t e g o r y > T a mb i én p ued e h acer lo
u st ed mi s mo s i e s ta e sp e ci fi cac ió n XM L ll e ga a alca n zar el o b j e ti vo n u m ero 6
(lo s d o c u me n to s X M L d eb e n ser le gib le s p ar a la s p er so na s y razo n ab le m en te
clar o s) .
Esquemas XML
El 2 d e Ma to d el 2 0 0 1 . el co n sej o e ncar g ad o d e co n tro l ar lo s e stá nd are s X M L
an u n cio q ue u n i mp o r t a nt e mi e mb r o d e la fa mi l ia X M L hab ía a lca n zad o el e st at u s
d e e st á nd ar ( u na reco m end ació n p r o p ue s ta, co mo www. w3 .o r g lo ll a ma ). E s te
es tá nd ar r ec ib e e l no mb r e d e E sq ue ma s XM L v es ta d e s ti nad o a re e mp laza r a l a
DT D co mo e l si s te ma p r ef er id o d e va lid ar d o c u me n to s X M L.
Esq ue ma s X M L o fr ece d o s ve n taj a s e v id e nt e s so b re la DT D:
• U n e sq ue ma X M L e s u n d o cu me n to XM L
• Lo s e sq ue ma s X M L p e r mi te e sp ec i fi car la s car acte rí st ica s d e d ato s ( c o mo
tip o , ta ma ñ o y p r e ci sió n ) d e lo s ele me n to s y atr i b ut o s
U n d o c u me n to d e e sq ue ma t ie ne e ste a sp ec to :
<?xml versión-"1.0" eneoding= "UTF-8 " ? >
</xsd : schema>
</xsd: element>