Nothing Special   »   [go: up one dir, main page]

Contenido de Un Vistazo

Descargar como docx, pdf o txt
Descargar como docx, pdf o txt
Está en la página 1de 510

Contenido de un vistazo

1 Fundamentos de marco . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2 .Entrada/salida
...... 1
. 67
3 (E/S) . . modificar
Buscar, . . . . . . . . .y. codificación
. . . . . . . . . . . de
. . texto.
. . . . . .. .. .. .. .. .. .. .. .. .. .. .. . . 14
4 ........
Colecciones 3
y genéricos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
5 ........
Serialización 5
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
6 .Gráficos
........ ....... ............ ............ ........ 7
31
7 .Rosca
. . . . .. .. .. .. .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
36
8 . . . .dominios
Los ... 7
de aplicación y servicios . . . . . . . . . . . . . . . . 43
9 . . . . . . . . . . y. .configuración
Instalación . de 5
47
10 aplicaciones . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. . . . . . . . . . . . . . . 7
La instrumentación. 55
11 .La
. . seguridad
. . . . . . . . .de
. la 1
62
aplicación
12 Y . . . .de
la seguridad . . los
. . . datos
. . . . .de
. . . . . . . . . . . . . . . . . . . . . . . . . 70
7
usuario
13 La . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
interoperación 1
14 .La
. . reflexión
. . . . . . . . .. .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
82
15 .Correo
. . . . . .. .. .. .. .. .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .9
88
......... ...
16 La 9
91
globalización . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
Tabla de contenido
Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
...

1   Fundamentos de
marco . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1
Antes de comenzar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
Lección 1: Uso de los tipos de valor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
Los tipos de valor integrados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
Cómo declarar los tipos de valor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
Cómo crear tipos definidos por el usuario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
Cómo crear enumeraciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
Laboratorio: Declarar y utilizar los tipos de valor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
Resumen de la lección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
Lección Revisión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
Lección 2: Uso de tipos comunes de referencia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
¿Qué es un tipo de referencia? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
Comparando el comportamiento de referencia y los tipos de valor . . . . . . . . . . . . . . . . . . 17
Los tipos de referencia incorporada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
Las cadenas y los constructores de cadena . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
Cómo crear y ordenar matrices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
Cómo utilizar Arroyos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
Cómo lanzar y capturar excepciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
Laboratorio: Trabajo con tipos de referencia. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
Resumen de la lección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
Lección Revisión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30

Lección 3: Crear clases. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32


¿Qué es la herencia? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
¿Qué es una interfaz? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
¿Cuáles son las clases parciales? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
¿Qué son los genéricos? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
Eventos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
¿Qué son los atributos?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
¿Qué es el reenvío de tipos? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
Práctica: Crear una clase derivada con los delegados. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
Resumen de la lección . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
Examen de la lección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
Lección 4: Conversión entre tipos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
La conversión en Visual Basic y C# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
Qué es el Boxing y Unboxing? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
Cómo implementar la conversión de tipos personalizados . . . . . . . . . . . . . . . . . . . . . . . . 56
Laboratorio: realizar conversiones de forma segura . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
Resumen de la lección . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
Examen de la lección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
Capítulo Revisión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
Resumen del capítulo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
Términos clave . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
Escenario de caso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
Caso práctico: diseño de una aplicación. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
Prácticas recomendadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
Administrar datos en una aplicación .NET Framework
Mediante el uso de .NET Framework 2.0 Tipos de sistema . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
Implementar interfaces de .NET Framework para causar que los componentes
Para cumplir con los contratos estándar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
Controlar las interacciones entre la aplicación de .NET Framework
Componentes mediante eventos y delegados . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
Tomar un Test de práctica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65

2   entrada/salida (E/S). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . 67
Antes de comenzar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
Lección 1: Explorar el sistema de archivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
¿Cuáles son las clases de sistema de archivo? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
 FileSystemInfo la  clase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
La   clase FileInfo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
Cómo obtener información acerca de un archivo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
Cómo copiar un archivo . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72

La   clase DirectoryInfo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73


Cómo enumerar los archivos de un directorio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
La   clase DriveInfo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
La   enumeración DriveType . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
Cómo enumerar las unidades . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
La   clase Path . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
Cómo cambiar una extensión de archivo en una ruta . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
La   clase FileSystemWatcher . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
Cómo controlar un directorio para los cambios. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
Laboratorio: enumerar archivos y observar cambios . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
Resumen de la lección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
Lección Revisión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
Lección 2: Lectura y escritura de archivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
Comprensión Arroyos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
Qué clases facilitan la lectura y la escritura de datos? . . . . . . . . . . . . . . . . . . . . . . 90
La   clase File . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
La   clase Directory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
La FileAccess  enumeración . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
La   enumeración FileMode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
La   clase FileStream. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
La   clase StreamReader . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
Cómo leer de un archivo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
La   clase StreamWriter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
Cómo escribir a un archivo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
Comprensión de lectores y escritores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
La   clase MemoryStream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
Cómo utilizar un MemoryStream  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
La   clase BufferedStream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
Cómo utilizar un BufferedStream  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
Laboratorio: Lectura y escritura de archivos. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
Resumen de la lección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
Lección Revisión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
Lección 3: la compresión de secuencias . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114
La introducción de las Secuencias de Compresión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114
La   clase GZipStream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115

La   clase DeflateStream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116


Cómo comprimir datos con una secuencia de compresión . . . . . . . . . . . . . . . . . . . . 118
Cómo descomprimir los datos con una secuencia de compresión . . . . . . . . . . . . . . . . . . 119
Laboratorio: Comprimir y descomprimir un archivo existente . . . . . . . . . . . . . . . . . . . . . . . 120
Resumen de la lección . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
Examen de la lección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
Lección 4: Trabajar con el almacenamiento aislado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
¿Qué es el almacenamiento aislado? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
La   clase IsolatedStorageFile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126
Cómo crear una tienda . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
La   clase IsolatedStorageFileStream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
Lectura y escritura de los datos en el almacenamiento aislado . . . . . . . . . . . . . . . . . . . . . . . . . 131
Cómo utilizar directorios de almacenamiento aislado . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
El IsolatedStorageFilePermission  Clase. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
Permitir el almacenamiento aislado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
Laboratorio: almacenar y recuperar los datos desde el almacenamiento aislado . . . . . . . . . . . . . . . . . . . .
135
Resumen de la lección . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
Examen de la lección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
Capítulo Revisión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
Resumen del capítulo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
Términos clave . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
Caso escenarios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
Caso práctico 1: Guardar la configuración de usuario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
Caso práctico 2: Monitorización de servidores antiguos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
Prácticas recomendadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
Crear un buscador de archivos de aplicación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
Crear una configuración sencilla de almacenamiento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
Tomar un Test de práctica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142

3   buscar, modificar y codificación de


texto . . . . . . . . . . . . . . . . . . . . . . . 143
Antes de comenzar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
Lección 1: Formar Expresiones regulares . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
Cómo utilizar expresiones regulares para la coincidencia de patrón . . . . . . . . . . . . . . . . . . 145
Cómo extraer datos pareados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
Cómo sustituir las subcadenas usando expresiones regulares . . . . . . . . . . . . . . . . . . 160
Cómo utilizar expresiones regulares para restringir la entrada de cadena . . . . . . . . . . . . . . 161
Laboratorio: crear un   evaluador de expresiones Regex . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
Resumen de la lección . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166
Examen de la lección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167

Lección 2: la codificación y la descodificación. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171


Comprensión de codificación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171
Utilizando la clase Encoding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173
Cómo examinar las páginas de códigos compatibles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174
Cómo especificar el tipo de codificación al escribir un archivo . . . . . . . . . . . . . . . . . 175
Cómo especificar el tipo de codificación al leer un archivo . . . . . . . . . . . . . . . . . 176
Laboratorio: Leer y escribir un archivo codificado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177
Resumen de la lección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177
Lección Revisión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178
Capítulo Revisión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
Resumen del capítulo. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
Términos clave . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
Escenarios de caso. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
Caso práctico 1: Validación de entrada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181
Caso práctico 2: Procesamiento de datos desde un equipo heredado . . . . . . . . . . . . . . 182
Prácticas recomendadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182
Mejorar las capacidades de manejo de texto de un archivo .NET Framework
Aplicación y buscar, modificar y controlar el texto dentro de un
Aplicación de .NET Framework usando expresiones regulares . . . . . . . . . . . . . . 182
Tomar un Test de práctica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183

4   colecciones y genéricos. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . 185
Antes de comenzar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186
Lección 1: recopilar elementos de datos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187
Tipos de colecciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187
Adición y eliminación de elementos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188
Iterar a través de elementos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191
Interfaces consistentes en colecciones. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194
Ordenar elementos. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195
Laboratorio: ordenar una tabla de cadenas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197
Resumen de la lección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199
Lección Revisión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199
Lección 2: Trabajar con las listas secuenciales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201
¿Qué son las listas secuenciales? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201
La   clase Queue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201
La   clase Stack . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203
Laboratorio: Construyendo FIFO y LIFO listas. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205
Resumen de la lección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207
Lección Revisión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 208

Lección 3: trabajar con diccionarios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209


Utilizando un diccionario  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209
La comprensión de la igualdad . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213
Utilizando la   interfaz IEqualityComparer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216
Utilizando la   clase SortedList . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218
Diccionarios especializados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 220
Práctica: Crear una tabla de consulta . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223
Resumen de la lección . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225
Examen de la lección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225
Lección 4: Uso de las colecciones especializadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226
Trabaja con bits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226
Las cadenas de recolección . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232
La   clase NameValueCollection. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
Laboratorio: una tabla de búsqueda, Localizable Case-Insensitive . . . . . . . . . . . . . . . . . . . . . . 238
Resumen de la lección . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239
Examen de la lección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239
Lección 5: colecciones genéricas. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241
¿Los medicamentos genéricos funcionan. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241
Mejorar la seguridad y rendimiento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 246
Estructura de la clase de colección genérica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255
Laboratorio: crear y utilizar una colección genérica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259
Resumen de la lección . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260
Examen de la lección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260
Capítulo Revisión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 262
Resumen del capítulo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 262
Términos clave . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 262
Caso escenarios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 262
Caso práctico 1: Usar un ArrayList  para almacenar códigos de estado. . . . . . . . . . . . . . . . . 263
Caso práctico 2: Seleccionar el tipo correcto de colección . . . . . . . . . . . . . . . . . . . . . . . . 263
Hipótesis 3: Reescribir para utilizar una colección Type-Safe . . . . . . . . . . . . . . . . . 264
Prácticas recomendadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 264
Utilice las colecciones genéricas. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 264
Compare   las clases de diccionario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 265
Tomar un Test de práctica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266

5   Serialización . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . 267
Antes de comenzar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 268
Lección 1: la serialización de objetos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269
¿Qué es la serialización? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269
Cómo serializar un objeto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 270
Cómo deserializar un objeto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 272
Cómo crear clases que se pueden serializar . . . . . . . . . . . . . . . . . . . . . . . . . . 274
Elegir un formato de serialización . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279
Cómo utilizar SoapFormatter. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279
Cómo controlar la serialización de jabón . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 280
Directrices para la serialización . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 281
Laboratorio: Serializar y deserializar objetos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 281
Resumen de la lección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 285
Lección Revisión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 286
Lección 2: serialización XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 288
¿Por qué utilizar la serialización XML? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 288
Cómo usar XML para serializar un objeto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 289
Cómo usar XML para deserializar un objeto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 290
Cómo crear clases que se pueden serializar
Mediante la serialización XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291
Cómo controlar la serialización XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291
Cómo se ajustan a un Esquema XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 295
Cómo serializar un objeto DataSet  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 296
Laboratorio: Uso de la serialización XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 297
Resumen de la lección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 300
Lección Revisión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 300
Lección 3: Serialización personalizada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 302
Cómo implementar la serialización personalizada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 302
Respondiendo a la serialización de eventos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 305
Cómo cambiar la serialización basándose en el contexto . . . . . . . . . . . . . . . . . . . . . . . . 308
Cómo crear un formateador personalizado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 309
Práctica: Implementar la serialización personalizada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 309
Resumen de la lección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 311
Lección Revisión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 311
Capítulo Revisión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 313
Resumen del capítulo. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 313
Términos clave . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 313
Escenarios de caso. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 314
Caso práctico 1: Eligiendo una serialización técnica . . . . . . . . . . . . . . . . . . . . 314
Preguntas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 314
Caso práctico 2: serialización entre versiones. . . . . . . . . . . . . . . . . . . . . . . . . . 315
Preguntas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 315

Prácticas recomendadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 315


Serializar o deserializar un objeto o un objeto gráfico mediante
Las técnicas de serialización en tiempo de ejecución . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 315
Controlar la serialización de objetos en formato XML mediante
El   espacio de nombres System.Xml.Serialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 316
Aplicar formato utilizando la serialización personalizada
El formateador de serialización Clases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 316
Tomar un Test de práctica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 317

6 Los   gráficos. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . 319
Antes de comenzar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 319
Lección 1: Dibujo de gráficos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 320
El   espacio de nombres System.Drawing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 320
Cómo especificar la ubicación y el tamaño de los controles . . . . . . . . . . . . . . . . . . . . . . 324
Cómo especificar el color de los controles. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 325
Cómo dibujar líneas y formas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 326
Cómo Personalizar bolígrafos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 330
Cómo rellenar formas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 332
Laboratorio: crear un método para dibujar un gráfico circular . . . . . . . . . . . . . . . . . . . . . . . . . . . . 334
Resumen de la lección . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 339
Examen de la lección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 340
Lección 2: Trabajar con imágenes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 342
Las      clases Image y Bitmap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 342
Cómo mostrar imágenes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 343
Cómo crear y guardar las imágenes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 343
Cómo utilizar los iconos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345
Laboratorio: Guardar un gráfico como una imagen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345
Resumen de la lección . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 346
Examen de la lección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 347
Lección 3: formato de texto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 349
Cómo agregar texto a un gráfico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 349
Cómo crear un   objeto Font . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 350
Cómo escribir texto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 350
Cómo controlar el formato del texto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 351
Laboratorio: Agregar texto a una imagen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 354
Resumen de la lección . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 360
Examen de la lección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 360
Capítulo Revisión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 362
Resumen del capítulo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 362

Términos clave . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 362


Escenarios de caso. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 362
Caso práctico 1: Elección de técnicas gráficas. . . . . . . . . . . . . . . . . . . . . . . . 363
Preguntas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 363
Caso práctico 2: Creación de gráficos simples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 364
Preguntas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 364
Prácticas recomendadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 364
Mejorar la interfaz de usuario de .NET Framework
Aplicación mediante pinceles, plumas, Colores y fuentes . . . . . . . . . . . . . . . . . . 365
Mejorar la interfaz de usuario de una aplicación de .NET Framework
Mediante el uso de gráficos, imágenes, mapas de bits e iconos . . . . . . . . . . . . . . . . . . . . . . . . 365
Mejorar la interfaz de usuario de una aplicación de .NET Framework
Mediante el uso de formas y tamaños . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 365
Tomar un Test de práctica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 366

7   Rosca . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . 367
Antes de comenzar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 368
Lección 1: Crear subprocesos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 369
Hilos simple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 369
Pasando datos a las roscas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 377
Detener subprocesos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 379
El contexto de ejecución . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 381
Laboratorio: usar la   clase Thread para demostrar el subprocesamiento múltiple . . . . . . . . . . . . . . . 383
Resumen de la lección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 385
Lección Revisión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 385
Lección 2: Intercambio de datos. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 387
Evitando colisiones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 387
Bloqueos de sincronización . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 391
Laboratorio: usar un mutex  para crear un Single Instance Application . . . . . . . . . . . . . . . 408
Resumen de la lección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 410
Lección Revisión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 410
Lección 3: El modelo de programación asincrónica . . . . . . . . . . . . . . . . . . . . . . . . . . . 412
Comprender la programación asincrónica . . . . . . . . . . . . . . . . . . . . . . . . . . 412
Utilizando el ThreadPool  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 420
Utilizando   objetos de temporizador . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 426
Laboratorio: utilizar el ThreadPool  a la cola de elementos de trabajo. . . . . . . . . . . . . . . . . . . . . . . . . 428
Resumen de la lección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 429
Lección Revisión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 429
Capítulo Revisión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 431
Resumen del capítulo. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 431

Términos clave . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 431


Caso escenarios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 432
Caso práctico 1: Mejorar el procesamiento del servidor . . . . . . . . . . . . . . . . . . . . . . . . . 432
Caso práctico 2: Múltiples Aplicaciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 432
Prácticas recomendadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 433
Crear una   aplicación ThreadPool. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 433
Tomar un Test de práctica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 434

8   dominios de aplicación y
servicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 435
Antes de comenzar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 436
Lección 1: crear dominios de aplicación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 437
¿Qué es un dominio de aplicación?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 437
La   clase AppDomain . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 439
Cómo crear un dominio de aplicación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 443
Cómo cargar ensamblados en un dominio de aplicación . . . . . . . . . . . . . . . . . . . . 443
Cómo descargar un dominio de aplicación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 444
Laboratorio: creación de dominios y cargar ensamblados . . . . . . . . . . . . . . . . . . . . . . . 444
Resumen de la lección . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 446
Examen de la lección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 446
Lección 2: Configuración de los dominios de aplicación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 448
Cómo utilizar un dominio de aplicación para lanzar
Conjuntos con privilegios limitados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 448
Cómo configurar las propiedades del dominio de aplicación . . . . . . . . . . . . . . . . . . . . . 451
Laboratorio: aplicación de control de privilegios de dominio . . . . . . . . . . . . . . . . . . . . . . . . . . . 453
Resumen de la lección . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 454
Examen de la lección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 455
Lección 3: Crear servicios de Windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 457
¿Qué es un servicio de Windows? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 457
Cómo crear un proyecto de servicio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 459
Cómo implementar un servicio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 460.
Cómo crear un proyecto de instalación de un servicio. . . . . . . . . . . . . . . . . . . . . . . . . . 461
Cómo gestionar y controlar un servicio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 463
Laboratorio: crear, instalar e iniciar un servicio para monitorizar un sitio Web . . . . . . . . . . . 465
Resumen de la lección . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 469
Examen de la lección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 470
Capítulo Revisión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 472
Resumen del capítulo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 472
Términos clave . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 472
Caso escenarios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 473
Caso práctico 1: Creación de una herramienta de pruebas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 473
Caso práctico 2: Supervisión de un archivo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 474
Prácticas recomendadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 475
Crear una unidad de aislamiento para Common Language Runtime
Dentro de una aplicación de .NET Framework mediante el uso de dominios de aplicación . . . . . 475
Implementar, instalar y controlar un servicio. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 476
Tomar un Test de práctica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 476

9   Instalación y configuración de
aplicaciones. . . . . . . . . . . . . . . . . . . . . . . . . 477
Antes de comenzar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 478
Lección 1: Opciones de configuración . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 480
Configuración de .NET Framework 2.0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 481
Ajustes comunes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 486
Configuración de la aplicación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 498
Laboratorio: Obtener la cadena de conexión de base de datos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 503
Resumen de la lección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 508
Lección Revisión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 509
Lección 2: Crear un instalador . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 511
Usando el instalador de base . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 511
Cometer una instalación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 515
La reversión de una instalación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 516
Laboratorio: establecer y revertir una clave de Registro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 517
Resumen de la lección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 518
Lección Revisión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 519
Lección 3: Uso de la herramienta Configuración de .NET Framework 2.0 . . . . . . . . . . . . . . . . . . . 520
Las configuraciones de navegación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 520
Cambiar una configuración . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 522
Restablecer una configuración . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 527
Laboratorio: cambio y restaurar la configuración de la aplicación . . . . . . . . . . . . . . . . . . . . . . . . . 528
Resumen de la lección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 529
Lección Revisión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 530
Lección 4: Gestión de la configuración . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 531
La obtención y almacenamiento de configuración . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 531
Implementar interfaces de configuración . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 533
Laboratorio: Leer y escribir la configuración . . . . . . . . . . . . . . . . . . . . . . . . . . . 543
Resumen de la lección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 545
Lección Revisión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 546
Capítulo Revisión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 547
Resumen del capítulo. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 547

Términos clave . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 548


Caso práctico: instalar y configurar una nueva aplicación . . . . . . . . . . . . . . . . . . 548
Prácticas recomendadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 549
Integrar la funcionalidad de gestión de la configuración en un archivo .NET
Marco de aplicación. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 549
Tomar un Test de práctica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 550

10
Instrumentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . 551
Antes de comenzar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 552
Lección 1: Registro de eventos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 553
Mediante el registro de eventos de Windows y de Microsoft . . . . . . . . . . . . . . . . . . . 553
Creación y eliminación de un registro de eventos. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 555
Para escribir en un registro de sucesos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 556
Lectura de un registro de eventos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 558
Laboratorio: crear y utilizar una aplicación de registro de eventos . . . . . . . . . . . . . . . . . . . . . . . . . 559
Resumen de la lección . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 560
Examen de la lección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 561
Lección 2: la depuración y el seguimiento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 563
Escribir la salida. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 563
Atributos Debug . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 572
Crear agentes de escucha de seguimiento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 581.
Oyente  objetos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 584
Laboratorio: crear y utilizar una aplicación de registro de eventos . . . . . . . . . . . . . . . . . . . . . . . . . 589
Resumen de la lección . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 590
Examen de la lección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 590
Lección 3: Supervisar el rendimiento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 592
Una visión general de los Procesos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 594
La   clase Process . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 594
Enumerar los procesos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 594
Utilizando contadores de rendimiento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 598
La   clase CounterCreationData. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 600
La   clase PerformanceCounterCategory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 601
La   clase PerformanceCounter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 603
Procesos de inicio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 604
El StackTrace   StackFrame y  clases. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 607
Laboratorio: aplicación de monitorización del rendimiento . . . . . . . . . . . . . . . . . . . . . . . . . . . . 609
Resumen de la lección . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 612
Examen de la lección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 612

Lección 4: Administración de la detección de eventos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 615


La enumeración de objetos de administración . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 615
Enumerar las unidades lógicas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 616
Enumerar adaptadores de red . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 617
Recuperar información acerca de los servicios que están en pausa . . . . . . . . . . . . . . . . . . . 619
Suscribirse a eventos de gestión utilizando
La   clase ManagementEventWatcher . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 619
Laboratorio: Escribir un caso de gestión a un registro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 620
Resumen de la lección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 622
Lección Revisión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 622
Capítulo Revisión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 624
Resumen del capítulo. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 624
Términos clave . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 624
Escenario de caso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 625
Caso práctico: Elegir dónde dirigir Outupt . . . . . . . . . . . . . . . . . . . . . . 625
Prácticas recomendadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 626
Incrustación de configuración, diagnóstico, gestión
Y las características de la instalación en una aplicación .NET Framework . . . . . . . . . . . . . 626
Tomar un Test de práctica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 626

11 La   seguridad de la aplicación . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . 627
Antes de comenzar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 629
Lección 1: Descripción de la seguridad de acceso a código. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 630
¿Cuál es la seguridad de acceso a código? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 630
Elementos de seguridad de acceso del código . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 631
¿Cuál es la política de seguridad? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 639
Cómo CAS trabaja con la seguridad del sistema operativo . . . . . . . . . . . . . . . . . . . . . . . 640
Cómo utilizar la herramienta Configuración de .NET Framework para configurar CA. . . . 641
Cómo utilizar la herramienta Directiva de seguridad de acceso a código . . . . . . . . . . . . . . . . . . . . . . 646
Laboratorio: configuración de seguridad de acceso a código. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 652
Resumen de la lección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 655
Lección Revisión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 656
Lección 2: Usar la seguridad declarativa para proteger las reuniones . . . . . . . . . . . . . . . . . . . . 658
Razones para utilizar el conjunto CAS declaraciones. . . . . . . . . . . . . . . . . . . . . . . . . . . . 658
Clases para permisos de CAS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 659
Tipos de permiso de conjunto de declaraciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . 662
Cómo crear General declaraciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 663
Directrices para el uso de declaraciones de montaje . . . . . . . . . . . . . . . . . . . . . . . . . . . 666
Laboratorio: Uso General solicitudes de permisos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 666

Resumen de la lección . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 667


Examen de la lección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 668
Lección 3: Usar la seguridad declarativa e imperativa para proteger métodos . . . . . . . . 672
Tipos de solicitudes de permiso de método . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 672
Directrices para el uso de solicitudes de permiso de método . . . . . . . . . . . . . . . . . . . . . 673
Técnicas para exigir permisos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 674
Técnicas para limitar los permisos. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 680
Cómo relajarse permisos y mejorar potencialmente el rendimiento . . . . . . . . . 682
Cómo llamar a código de confianza a partir de código de confianza parcial . . . . . . . . . . . . . . . . . . 686
Cómo utilizar conjuntos de permisos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 686
Laboratorio: Proteger los métodos con las demandas de seguridad de acceso a código . . . . . . . . . . .
687
Resumen de la lección . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 694
Examen de la lección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 694
Capítulo Revisión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 696
Resumen del capítulo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 696
Términos clave . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 696
Caso escenarios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 697
Caso práctico 1: Explicar la seguridad de acceso del código . . . . . . . . . . . . . . . . . . . . . . 697
Preguntas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 697
Caso práctico 2: Personalización de la seguridad de acceso del código . . . . . . . . . . . . . . . . . . . . 698
Preguntas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 698
Prácticas recomendadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 698
Implementar la seguridad de acceso a código para mejorar la seguridad
De una aplicación de .NET Framework . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 698
Controlar los permisos de los recursos utilizando el
System.Security.  Clases de permiso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 699
Privilegios de código de control utilizando System.Security.Policy  Clases . . . . . . . . . 699
Tomar un Test de práctica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 700

12   usuarios y la seguridad de los datos . . . . . . . . . . . . . . . . . . . . .


. . . . . . . . . 701
Antes de comenzar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 703
Lección 1: Autenticación y autorización de usuarios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 704
Autenticación y autorización Descripción . . . . . . . . . . . . . . . . . . . . . . . . . . 704
  La clase WindowsIdentity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 706
  Clase WindowsPrincipal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 708
  Clase PrincipalPermission . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 710
Cómo utilizar la seguridad basada en funciones declarativas demandas
Para restringir el acceso a los métodos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 711.

Cómo utilizar imperativa exige la seguridad basada en funciones.


Para crear aplicaciones que restringen el acceso a partes de su lógica . . . . . 713
Cómo implementar Usuarios y roles personalizados . . . . . . . . . . . . . . . . . . . . . . . . . . . 715
Manejo de excepciones de autenticación en los Arroyos . . . . . . . . . . . . . . . . . . . . . . . . 724
Laboratorio: Adición de seguridad basada en funciones a una aplicación . . . . . . . . . . . . . . . . . . . . . 725
Resumen de la lección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 729
Lección Revisión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 730
Lección 2: El uso de listas de control de acceso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 732
¿Qué es una lista de control de acceso discrecional? . . . . . . . . . . . . . . . . . . . . . . . . . . . 732
¿Qué es una lista de control de acceso de seguridad? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 735
Cómo ver y configurar ACL desde dentro de una Asamblea . . . . . . . . . . . . . 736
Laboratorio: Trabajo con las DACL y herencia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 739
Resumen de la lección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 740
Lección Revisión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 741
Lección 3: el cifrado y descifrado de datos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 743
Cifrado y descifrado de datos con claves simétricas . . . . . . . . . . . . . . . . . . 743
Cifrado y descifrado de datos con claves asimétricas . . . . . . . . . . . . . . . . . 753
La validación de la integridad de los datos con algoritmos hash . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
761.
Firma de archivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 766
Laboratorio: cifrar y descifrar archivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 770
Resumen de la lección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 774
Lección Revisión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 775
Capítulo Revisión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 777
Resumen del capítulo. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 777
Términos clave . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 777
Escenarios de caso. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 778
Caso práctico 1: Crear métodos de autenticación personalizado. . . . . . . . . . . . . . 778
Caso práctico 2: Protección de datos mediante Criptografía . . . . . . . . . . . . . . . 780
Prácticas recomendadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 780
Implementar un esquema de autenticación personalizado mediante
 System.Security.  Clases de autenticación. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 781
Acceder y modificar la información de identidad mediante
El System.Security.Principal   Clases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 781
Implementar el control de acceso mediante
 System.Security.AccessControl  Clases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 781
Cifrar, descifrar y hash de datos mediante
Las   clases System.Security.Cryptography . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 782
Tomar un Test de práctica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 782
    La interoperación entre 13 . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
783
Antes de comenzar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 785
Lección 1: Uso de objetos COM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 786
Importación de bibliotecas de tipos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 786
Con Visual Studio 2005 para importar un tipo . . . . . . . . . . . . . . . . . . . . . . . . . . . . 787
Mediante TlbImp.exe para importar un tipo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 788
Las herramientas utilizadas por la interoperabilidad
COM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 790
Utilizar objetos COM en el código . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 791
Manejo de excepciones en la Interoperabilidad COM . . . . . . .. . . . . . . . . . . . . . 792
Limitaciones de la interoperabilidad COM . . . . . . .. . . . . . . . . . . . . . . . . . . . . . 794
Laboratorio: utilizar una aplicación COM desde .NET . . . .. . . . . . . . . . . . . . . . . . . 794
Resumen de la lección . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 796
Examen de la lección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 796
Lección 2: Exponer los componentes .NET COM. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 798
Construcción de componentes .NET para su uso por parte de COM . .. . . . . . . . . 798
Ocultar público clases .NET desde COM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 800
Implementación de COM-ensamblados habilitados. . . . .. . . . . . . . . . . . . . . . . . 802
Laboratorio: construir un conjunto com-Enabled . . . . . . . .. . . . . . . . . . . . . . . . 803
Resumen de la lección . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 805
Examen de la lección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 806
Lección 3: Utilizar código no administrado . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . 807
Llamar a la invocación de la plataforma . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . 807
Encapsula funciones DLL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 809
Convertir tipos de datos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 809
El cálculo de referencias de estructuras . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . 811
Mediante una devolución de llamada con código no administrado .. . .. . . 817
Manejo de excepciones en el código administrado. . . . .. . . . . . . . . . . . . 819
Limitaciones del código no administrado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 822
Laboratorio: Llame a las funciones de la DLL de Windows . . . . .. . . . . . . .. . . . . . . 822
Resumen de la lección . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 823
Examen de la lección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 824
Capítulo Revisión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 825
Resumen del capítulo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 825
Términos clave . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 825
Caso práctico: la incorporación de código heredado en un proyecto .NET .. . . . . . . . 826
Entrevistas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 826
Prácticas recomendadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 828
La gestión práctica de la interoperación de código . . . . . . . . . . . . .. . . . . . . . . . . . 828
Tomar un Test de práctica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 828

14 La   reflexión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . 829
Antes de comenzar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 830
Lección 1: Descripción de la reflexión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 831
Descripción ensamblados y módulos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 831
Examinar un conjunto. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 833
Laboratorio: utilizar las herramientas .NET para examinar una Asamblea . .. .. . . . . . . 838
Resumen de la lección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 840
Lección Revisión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 840
Lección 2: atributos de ensamblado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 842
Atributos comunes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 842
Obtener atributos de ensamblado. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 847
Laboratorio: Establecer atributos de ensamblado y mostrarlos en tiempo de ejecución. . . . . . . . . . . . . .
848
Resumen de la lección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 850
Lección Revisión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 850
Lección 3: reflejo de Tipos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 852
Obtención de Tipos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 852
La enumeración de miembros de clase . . . .. . . . . . . . . . . . . . . . . . . . . . . . . 859
 BindingFlags utilizando el  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 863
Laboratorio: cargar un ensamblado, y volcar su información de tipo. .. . . . . . . 864
Resumen de la lección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 866
Lección Revisión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 867
Lección 4: Escribir el código dinámico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 868
Utilizando el código dinámico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 868
Laboratorio: invocar miembros mediante la reflexión . . . .. . . . . . . . . . . . 872
Resumen de la lección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 874
Lección Revisión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 874
Lección 5: Crear el código en tiempo de ejecución . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . 876
La construcción de su propio código . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . 876
Laboratorio: crear un ensamblado dinámico . . . . . . . . . . . . . .. . . . . . . . . . . . . 882
Resumen de la lección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 885
Lección Revisión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 885
Capítulo Revisión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 886
Resumen del capítulo. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . 886
Términos clave . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 886
Caso práctico: Crear una arquitectura de plugin . . . . . . . . . . .. . . . . . . . . . . . . . 887
Prácticas recomendadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 887
Escribir una aplicación de explorador de montaje . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 887
Tomar un Test de práctica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 888

15 de   Correo . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . 889
Antes de comenzar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 890
Lección 1: Crear un mensaje de correo electrónico . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . . 891
El proceso de creación y envío de un mensaje de correo electrónico . . .. . . . . . 891
Cómo crear un   objeto MailMessage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 892
Cómo adjuntar archivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 894
Cómo crear mensajes de correo electrónico HTML. . . . . . .. . . . . . . . . . . . . . . . . . . 895
Laboratorio: crear un   objeto MailMessage . . . . . . . . . . . . . .. . . . . . . . . . 897
Resumen de la lección . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 900
Examen de la lección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 900
Lección 2: Envío de correo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 902
Cómo enviar un mensaje . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 902
Cómo gestionar el correo Excepciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 903
Cómo configurar las credenciales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 904
Cómo configurar SSL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 905.
Cómo enviar un mensaje de forma asincrónica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 905.
Laboratorio: Enviar un mensaje de correo electrónico . . . . . .. .. . . . . . . . . . . . . . . . . . 907
Resumen de la lección . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 912
Examen de la lección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 913
Capítulo Revisión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 915
Resumen del capítulo . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . 915
Términos clave . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 915
Caso práctico: Agregar capacidades de correo electrónico a una aplicación existente ..
. . . . . . . . . . . 915
Prácticas recomendadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 916
Enviar correo electrónico a un servidor SMTP (Simple Mail Transfer Protocol)
Para la entrega de una aplicación de .NET Framework . . . . . . . . . . . . . . . . . . . . . . . 917
Tomar un Test de práctica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 917

16 de   la
globalización. . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . 919
Antes de comenzar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 919
Lección 1: Utilizando información de Cultura. . . . .. . . . . . . . . . . . . . . . . . . . . . . . . 920
  Clase CultureInfo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 920
CultureTypes  enumeración . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 925
RegionInfo  Clase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 926
DateTimeFormatInfo   NumberFormatInfo y  Clases . . . . . . . . . . . . . . . . . . . 927
Utilizando la   clase y   enumeración CompareOptions CompareInfo
Para las comparaciones de la Cultura . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 929
Laboratorio: Escribir código que se ajusta a la cultura . .. . . . . . . . . . . . . . . . . . . 932
Resumen de la lección . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 934
Examen de la lección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 935

Lección 2: Crear una cultura personalizada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 936


Laboratorio: crear su propia cultura . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 938
Resumen de la lección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 939
Lección Revisión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 940
Capítulo Revisión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 941
Resumen del capítulo. . . . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . 941
Términos clave . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 942
Escenario de caso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . 942
Caso práctico: instalar y configurar una nueva aplicación . . . . . . . . . . . . 942
Prácticas recomendadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 943
Utilizando información de Cultura . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 943
Crear una cultura personalizada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 943
Tomar un Test de práctica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 944

Respuestas. . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . . . . . 945

Glosario. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . 1007
Índice . . . . . . . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . . . . . . . 1015
Capítulo 1
Fundamentos marco

.NET Framework es un componente de Microsoft Windows integral diseñado para apoyar la


próxima generación de aplicaciones y servicios. Muchos de los fundamentos de .NET
Framework que resultará familiar a los desarrolladores que han trabajado en otros entornos de
desarrollo orientados a objetos; sin embargo, el .NET Framework también incluye muchos
elementos únicos que serán nuevas para incluso los desarrolladores más experimentados. Este
capítulo proporciona una descripción general de programación de .NET Framework,
incluyendo los conocimientos requeridos para cada capítulo de este libro.

Nota   .NET 2.0


Si usted ha trabajado con las versiones de .NET Framework versión 2.0 lanzada antes,
gran parte de esto estará familiarizado. Sin embargo, la versión 2.0 de .NET Framework
incluye varias características nuevas: los genéricos, las clases parciales y reenvío de tipo
(todos descritos en la lección 3, "Construcción de clases").

Objetivos del examen en este


capítulo:
■   Gestionar los datos en una aplicación .NET Framework mediante el uso de .NET
Framework
2.0 Tipos de sistemas. (Consultar el espacio de nombres System)
❑   los tipos de valor
❑ los   tipos de referencia
❑   atributos

❑   tipos genéricos
❑   clases de excepción
❑   Boxing y Unboxing
❑   TypeForwardedToAttribute Class

■   Implementar interfaces de .NET Framework para causar componentes para cumplir con
los contratos estándar. (Consultar el espacio de nombres System)
❑ El    interfaz IComparable
❑ El     interfaz IDisposable
❑     interfaz IConvertible
❑     interfaz ICloneable
❑     interfaz IEquatable
❑    interfaz IFormattable
■ para   controlar
las interacciones entre los componentes de la aplicación .NET
Framework mediante eventos y delegados. (Consultar el espacio de nombres System)
❑ El   delegado de  clase
❑     clase EventArgs
❑   EventHandler delegados

Las lecciones de este capítulo:


■ La   Lección 1: Uso de los tipos de valor . . . . . .. . . .. . . . . . . . . . . . . . . . . . . . . . . . . . 3
■ La   Lección 2: Uso de tipos comunes de referencia . . . . . . . .. . . . . . . . . . . . . . . . . . 17
■ La   Lección 3: Construcción de clases . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . 32
■ La   Lección 4: Conversión entre tipos. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54

Antes de empezar
Este libro asume que usted tiene por lo menos dos a tres años de experiencia en el desarrollo
basado en la Web, basado en Microsoft Windows o aplicaciones distribuidas mediante el uso
de .NET Framework 1.0, .NET Framework 1.1 y .NET Framework 2.0. Los candidatos
deberán tener conocimientos básicos de Microsoft Visual Studio 2005. Antes de comenzar,
debe estar familiarizado con Microsoft Visual Basic o C# y estar familiarizado con las
siguientes tareas:
■   Crearuna consola o una aplicación de Windows Forms en Visual Studio utilizando Visual
Basic o C#.
■   agregar espacios de nombres y las referencias al sistema de bibliotecas de clases para un
proyecto.
■   ejecutar un proyecto en Visual Studio, establecer puntos de interrupción, el paso a través
del código, y observar los valores de las variables.
Lección 1: Uso de los tipos de valor

El más simple de los tipos de .NET Framework, principalmente tipos numéricos y booleanos,
son tipos de valor. Los tipos de valor son variables que contienen sus datos directamente en
lugar de conteniendo una referencia a los datos almacenados en la memoria. Las instancias de
los tipos de valor se almacena en un área de memoria llamada la pila, donde el tiempo de
ejecución puede crear, leer, actualizar y eliminar de forma rápida con una sobrecarga mínima.

Más información  
Tipos de referencia
Para obtener más información acerca de los tipos de
referencia, consulte la Lección 2.

En general, existen tres tipos de valores:


■   tipos integrados
■   Los tipos definidos por el usuario
■   Enumeraciones

Cada uno de estos tipos se derivan de la clase System.ValueType tipo base. Las sec- ciones
siguientes muestran cómo utilizar estos tipos diferentes.

Después de esta lección, será capaz de:


■   elegir el más eficaz tipo de valor integrado
■   declarar los tipos de valor
■   Crear tus propios tipos
■   Utilizar enumeraciones
Lección Tiempo estimado: 30 minutos

Los tipos de valor integrados


Tipos integrados son tipos base proporcionado con el .NET Framework, con el cual otros tipos
son construidos. Incorporada en todos los tipos numéricos son tipos de valor. Usted elige un
tipo numérico basado en el tamaño de los valores que espera trabajar con y el grado de
precisión que
se requiere. La tabla 1-1 muestra una lista de los tipos numéricos más comunes por tamaño, de
menor a larg- est. Los primeros seis tipos se utilizan para los valores de número entero y los
tres últimos representan números reales con el fin de aumentar la precisión.
Tabla 1-1  los tipos de valor integrados
Tipo (Visual Bytes Gama Uso para
Basic/alias de C#)
System.SByte 1 -128 a 127 Firmaron los valores
(SByte/sbyte) de byte

System.Byte 1 0 a 255. Bytes sin signo


ByteByte ( /).
System.Int16 2 -32768 a 32767 La interoperación
(Short/short) Y otros especiales-
Izado usa
System.Int32 4 -2147483648 a 2147483647 Números enteros
(Integer/int) Y contadores
System.UInt32 4 De 0 a 4294967295 Conjunto positivo
(UInteger/uint) Números y
Contadores
System.Int64 8 -A 9223372036854775808 Todo grande
(Long/largo) 9223372036854775807 Números
System.Solo 4 -3,402823E+38 a Punto flotante
(Single/float) 3,402823E+38 Números
System.Double 8 -1,79769313486232E+308 a Precisas o grandes
(Doble/double) 1,79769313486232E+308 Punto flotante
Números
System.Decimal 16 -7922816251426433759354 Y FINANCIERA
Decimaldecimal A 3950335 La ciencia calcula-
( /). 792281625142643375935439 Nes que requieren
50335 Gran precisión

Las mejores prácticas  optimizando el rendimiento con tipos integrados


Optimiza el rendimiento del tiempo de ejecución de tipos enteros de 32 bits
(Int32  y UInt32), así que use los tipos de contadores y otras variables
integrales de acceso frecuente. Para operaciones de punto flotante, doble  es el
tipo más eficiente ya que esas operaciones son optimizadas por hardware.
Estos tipos numéricos se utilizan con tanta frecuencia que Visual Basic y C# definir alias
para ellos. Utilizando el alias es equivalente a utilizar el nombre completo del tipo, por lo
que la mayoría de los programadores utilizan el alias más cortos. Además de los tipos
numéricos, los tipos de datos no numéricos que se enumeran en la Tabla 1-2 también son
tipos de valor.
Tabla 1-2  Otros tipos de valor

Tipo (Visual Bytes Gama Uso para


Basic/alias de C#)
System.Char 2 N/A Solo caracteres
(Char/char) Unicode
System.Boolean 4 N/A  Valores
(Boolean/bool) VERDADERO/FALSO

System.IntPtr (ningun Dependient N/A Puntero a una


o) e de la dirección de memoria
plataforma
System.DateTime 8 1/1/0001 Momentos en el tiempo
(Date/fecha) 12:00:00 AM
12/31/9999
11:59:59 PM

Hay casi 300 tipos de valor en el marco, pero los tipos mostrados aquí cubren la mayoría de
necesidades. Cuando asigne entre las variables de tipo de valor, se copian los datos de una variable
a la otra y se almacenan en dos ubicaciones diferentes en la pila. Este comportamiento es diferente
del de los tipos de referencia, que se discute en la Lección 2.
Aunque los tipos de valor a menudo representan valores simples, todavía funcionan como objetos.
En otras palabras, puede llamar a métodos en ellos. De hecho, es común utilizar el  método
ToString al mostrar valores como texto.  Es invalidado ToString de la fundamental de  tipo
System.Object.

Nota   La   clase base Object


En .NET Framework, todos los tipos se derivan de System.Object. Esta relación ayuda a
establecer el sistema de tipos común usado en todo el marco.

Cómo declarar los tipos de valor


Para utilizar un tipo, primero debe declarar un símbolo como una instancia de ese tipo. Los tipos de
valor tiene un constructor implícito, a fin de declararlas crea el tipo automáticamente; no tienes que
incluir la  palabra clave New como lo hace con las clases. El constructor asigna un valor
predeterminado (normalmente un valor null o 0) a la nueva instancia, pero siempre se debe
inicializar explícitamente la variable dentro de la declaración, tal y como se muestra en el siguiente
bloque de código:
' VB
Dim b As Boolean = False

// C#
Bool b = false;

Nota   diferencias clave en Visual Basic y C#.


Una de las diferencias cosméticas entre Visual Basic y C# es que Visual Basic capitaliza
palabras clave, mientras que C# utiliza palabras clave en minúsculas. En el texto de este libro,
las palabras clave de Visual Basic siempre serán capitalizados para mejorar la legibilidad.
Ejemplos de código siempre incluirá ejemplos separados para Visual Basic y C#.

Nota   capitalizaciones Variable en Visual Basic y C#.


C# es sensible a las mayúsculas y minúsculas, pero Visual Basic no es sensible a mayúsculas y
minúsculas. Tradicionalmente, los nombres de variables comienzan con una letra minúscula en
C# y se capitalizan en Visual Basic. Para mantener la coherencia entre los distintos idiomas,
este libro le use minúsculas los nombres de variables para la mayoría de los ejemplos de Visual
Basic. Siéntase libre para aprovechar las variables de Visual Basic en su propio código, no
afecta al modo en que los procesos de ejecución de su código.

Declarar la variable como Nullable si desea ser capaz de determinar si un valor no ha sido


asignado. Por ejemplo, si está almacenando los datos de una pregunta de sí/no de un formulario y
el usuario no contesta a la pregunta, usted debe almacenar un  valor nulo. La fol- bramido del
código permite una variable booleana para ser True, Falseo Null:
' VB
Dim b como Nullable(Of Boolean) = nada

// C#
Nullable<bool> b = null;

// Notación abreviada, sólo para C#


Bool? B = null;

Declarar una variable como Nullable habilita el HasValue y  miembros de valor. Uso ha-


Valor para detectar si hay o no un valor ha sido establecido:
' VB
Si b.HasValue luego Console.WriteLine("b es {0}." b.Value) _ Else
Console.WriteLine("b no está establecido").

// C#
Si b.HasValue)Console.WriteLine("b es {0}." b.Value);
Else Console.WriteLine("b no está establecida.");
Cómo crear tipos definidos por el usuario
Los tipos definidos por el usuario se denominan también estructuras o simplemente las
estructuras, después de la palabra clave idioma utilizado para crearlos. Como con otros tipos de
valor, instancias de tipos definidos por el usuario se almacenan en la pila y contienen directamente
sus datos. En la mayoría de otras maneras, es- tructuras se comporte casi idénticas a las clases.
Las estructuras son un compuesto de otros tipos que hacen que sea más fácil trabajar con datos
relacionados. El ejemplo más simple de esto es System.Drawing.El punto, que contiene enteros X e
Y propiedades que definen las coordenadas horizontal y vertical de un punto. La  estructura Point
simplifica el trabajo con coordenadas de constructor y proporcionando a los países miem- bros
demostrado aquí:
' VB - Requiere la referencia a System.Drawing
' Crear punto
Dim p como nuevo System.Drawing.Punto(20, 30).

' Punto mover diagonalmente


p.Offset(-1, -1)
Console.WriteLine("punto X {0} y {1}", p.X, p.s)

// C# - Requiere la referencia a System.Drawing


// Crear punto
System.Drawing.El punto P = new System.Drawing.Punto(20, 30).

// Punto de mover diagonalmente


p.Offset(-1, -1);
Console.WriteLine("punto X {0} y {1}", p.X, p.s);

Puede definir sus propias estructuras mediante la estructura clave en Visual Basic o la  palabra
clave struct en C#. Por ejemplo, el código siguiente crea un tipo que recorre un conjunto de
números enteros comprendidos entre los valores mínimo y máximo establecidos por el constructor:

// C#
Ciclo de estruct.
{
// Campos privados
Int _val, _min, _max;

// Constructor
Ciclo público(int, int mín máx.).
{
_Val = min;
_Min = min;
_Max = max;
}
El valor int público
{
Get { return _val; }
Establecer
{
Si (valor > _max)
_Val = _min;
Otra cosa
{ Si (valor < _min)
_Val = _max;

Otra cosa
}
}
}
_Val = valor;
nulación pública string ToString()
{
Valor de retorno.ToString();
}

Public int ToInteger()


{
Valor de retorno;
}

// Operadores (nuevo en .NET 2.0)


Ciclo estático público operador +(ciclo int arg1, arg2)
{
Arg1.Value += arg2;
Volver arg1;
}

Ciclo estático público operador -(ciclo int arg1, arg2)


{
Arg1.Valor := arg2;
Volver arg1;
}
}

Puede utilizar esta estructura para representar los elementos que se repiten a lo largo de un
intervalo fijo, como grados de rotación o cuartos de un partido de fútbol, como se muestra aquí:
' VB
Dim grados como nuevo ciclo(0, 359), en los barrios como nuevo ciclo(1, 4) Para i as Integer = 0
a8
Grados += 90 : cuartos += 1
Console.WriteLine("grados = {0}, trimestres = {1}", grados, trimestres) Siguiente

// C#
Grados de ciclo = nuevo ciclo(0, 359); Ciclo
trimestres = nuevo ciclo(1, 4); en el caso de (int i
= 0; i <= 8; i++)
{
Grados += 90; cuartos += 1;
Console.WriteLine("grados = {0}, trimestres = {1}", grados, trimestres);
}

El ciclo muestra puede ser fácilmente convertida a y desde un tipo de valor a un tipo de referencia
cambiando la estructurastruct / palabras clave para la clase. Si realiza este cambio, las instancias
de la  clase de ciclo se asignan en el montón administrado, en lugar de como 12 bytes en la pila (4
bytes para cada campo entero privado) y asignación entre dos variables resultados en ambas
variables apuntando a la misma instancia.
Mientras que la funcionalidad es similar, las estructuras son generalmente más eficientes que las
clases. Debe definir una estructura, en lugar de una clase, si el tipo funcionará mejor como un tipo
de valor de un tipo de referencia. Concretamente, tipos de estructura debe cumplir todos estos
criterios:
■   lógicamente representa un solo valor
■   Tiene una instancia tiene un tamaño inferior a 16 bytes
■   no será modificado tras la creación
■   no será arrojado a un tipo de referencia

Cómo crear enumeraciones


Las enumeraciones son símbolos relacionados que tienen valores fijos. Utilizar enumeraciones
para propor- cionar una lista de opciones para los desarrolladores que usan la clase. Por ejemplo, la
enumeración siguiente contiene un conjunto de títulos:
/C#
Títulos enum : int { Sr., Sra., Sra., el Dr. };
Si se crea una instancia del  tipo de títulos, Visual Studio muestra una lista de los valores
disponibles cuando se asigna un valor a la variable. Aunque el valor de la variable es un número
entero, es fácil dar salida al nombre del símbolo en lugar de su valor, como se muestra aquí:

// C#
T = Títulos Títulos.dr;
Console.WriteLine("{0,}". t); // Muestra "Dra."

El propósito de las enumeraciones es para simplificar la codificación y mejorar la legibilidad del


código, permitiéndole utilizar símbolos descriptivos en lugar de valores numéricos sencillos.
Utilice enu- merations cuando los desarrolladores consume sus tipos deben elegir un conjunto
limitado de opciones de un valor.

Laboratorio: Declarar y utilizar los tipos de valor


Los ejercicios siguientes muestran cómo crear y usar una estructura y cómo crear una

enumeración. Si se produce un problema de completar un ejercicio, la com- pletó proyectos están


disponibles en el CD en la carpeta de código.
  Ejercicio 1: Crear una estructura
En este ejercicio vamos a crear una estructura simple con varios miembros del público.
1.  Con Visual Studio, cree un nuevo proyecto de aplicación de consola. Nombre del proyecto
CreateStruct.
2.  Crear una nueva estructura, denominada Persona, como se muestra en el código siguiente:
// C#
Struct Persona
{
}

3.  Dentro de la  estructura de la persona, define tres miembros del público:


❑   firstName (una cadena)

❑   lastName (una cadena)

❑ la   edad (un entero)
El siguiente código muestra esto:
// C#
Cadena pública FirstName,
LastName cadena pública; public
int edad;

4.  Cree un constructor que inicializa los tres variables miembro, como demuestra el código
siguiente:
// C#
Persona pública(string _FirstName, LastName, cadena _int _edad)
{
FirstName = _FirstName,
LastName = _lastName; edad =
_edad;
}

5.  Reemplazar el  método ToString para mostrar a la persona, el nombre, apellido y edad. El


siguiente código muestra esto:

// C#
Anulación pública string ToString()
{
Volver firstName lastName + + " " + " " + edad, edad;
}

6.  En el  método Main de la aplicación de consola, escribir código para crear una instancia de la
estructura y pase la instancia para el  método Console.WriteLine, como demuestra el código
siguiente:
// C#
Persona p = new persona("Tony", "Allen", 32); Console.WriteLine(p);

7.  Ejecute la aplicación de consola para comprobar que funciona correctamente.


    Ejercicio 2: Agregar una enumeración a una estructura
En este ejercicio, podrá extender la estructura creada en el ejercicio 1, añadiendo una enumeración.
1.  Abra el proyecto creado en el ejercicio 1.
2.  Declarar una enumeración en la nueva  estructura de la persona. El nombre de la
enumeración Gen- escala DERS, y especificar dos valores posibles: masculino y femenino. El
siguiente código sam- ple demuestra esto:
// C#
Enumeración pública géneros : int masculinos, femeninos { };

3.  Agregar un miembro público de tipo géneros, y modificar la persona constructor para aceptar


una instancia de Género. El siguiente código muestra esto:

// C#
Cadena pública FirstName, LastName
cadena pública; public int edad;
Géneros pública género;

Persona pública(string _FirstName, LastName, cadena _int _edad, sexos _sexo)


{
FirstName = _FirstName, LastName =
_lastName; edad = _edad;
Género Género = _;
}

4.  Modificar la persona.ToString para mostrar también el género, como demuestra el ejemplo de


código siguiente:

// C#
Anulación pública string ToString()
{
Volver firstName lastName + + " " + " (" + sexo + " " +), de edad edad;
}

5.  Modificar el  código principal para construir correctamente una instancia de la  estructura de la


persona, como demuestra el ejemplo de código siguiente:

// C#
Static void main(String[] args)
{
Persona p = new persona("Tony", "Allen", 32 personas.sexos.macho);
Console.WriteLine(p.ToString();
}

6.  Ejecute la aplicación de consola para comprobar que funciona correctamente.

Resumen de la lección
■   El.NET Framework incluye un gran número de tipos integrados que se pueden usar
directamente o utilizar para construir tus propios tipos.
■   Lostipos de valor contienen directamente sus datos, ofreciendo un excelente rendimiento. Sin
embargo, los tipos de valor están limitadas a tipos que almacene pedazos muy pequeños de datos.
En .NET Framework, todos los tipos de valor de 16 bytes o menos.
■   Puede crear tipos definidos por el usuario que almacenar varios valores y métodos.

En aplicaciones orientadas a objetos, una gran parte de la lógica de la aplicación se almacena en


los tipos definidos por el usuario.
■   Enumeraciones mejorar la legibilidad del código, proporcionando símbolos para un conjunto de
valores.

Examen de la lección
Puede utilizar las siguientes preguntas para poner a prueba su conocimiento de la información en la
lección 1, "Uso de tipos de valor." Las preguntas también están disponibles en el CD
complementario si prefiere revisarlos en forma electrónica.

Nota   Respuestas
Las respuestas a estas preguntas y explicaciones de por qué cada opción de
respuesta es correcta o no se encuentran en la sección "Respuestas" al final del
libro.

1.  Cuales de los siguientes son tipos de valor? (Seleccione todos los que
correspondan).
A.  Decimal
B.   Cadena
C.   System.Drawing.El punto
D.   Integer
2.  Se pasa una variable de tipo de valor en un procedimiento como argumento. El procedimiento
cambia la variable; sin embargo, cuando el procedimiento devuelve, la variable no ha cambiado.
¿Qué ha sucedido? (Elija uno).
A.  No se ha inicializado la variable antes de que fuera promulgada.
B.   Pasar de un tipo de valor en un procedimiento crea una copia de los datos.
C.   La variable fue declarando de nuevo en el nivel de procedimiento.
D.   El procedimiento manejó la variable como una referencia.
3.  Que es la declaración correcta para un entero nullable?
A.
// C#
Nullable(int) i = null;

B.
// C#
Nullable<int> i = null;
C.
// C#
Int i = null;

D.
// C#
Int<Nullable> i = null;

4.  Necesita crear una simple clase o estructura que contiene sólo los tipos de valor. Debe crear la
clase o estructura, para que se ejecute de forma tan eficiente como sea posible. Usted debe ser
capaz de pasar una instancia de la clase o estructura a un procedimiento sin la preocupación de que
el procedimiento se modifica. Cuál de los siguientes debería crear?
A.  Una clase de referencia
B.   Una estructura de referencia
C.   Una clase de valor
D.   Una estructura de valores
Lección 2: Uso de los tipos de referencia común.

La mayoría de los tipos de .NET Framework son tipos de referencia. Los tipos de referencia
proporcionan una gran flexibilidad y ofrecen un rendimiento excelente al pasar a métodos. En las
secciones siguientes se presentan los tipos de referencia común discutiendo clases incorporadas. La
lección 4, "Conversión entre tipos", abarca la creación de clases, inter- faces y delegados.

Después de esta lección, será capaz de:


■   Explicar la diferencia entre los tipos de valor y tipos de referencia.
■   Describir cómo los tipos de valor y tipos de referencia difieren al asignar valores.
■   El incorporado en la lista de tipos de referencia.
■   Describir cuándo se debe utilizar el   tipo de StringBuilder.
■   Crear y ordenar matrices.
■   abrir, leer, escribir y cerrar archivos.
■   detectar cuando se produzcan excepciones y responder a la excepción.
Lección Tiempo estimado: 40 minutos

¿Qué es un tipo de referencia?


Los tipos de referencia de almacenar la dirección de sus datos, también conocido como puntero, en
la pila. Los datos reales que se refiere a la dirección se almacena en un área de la memoria
denominada heap. El Runtime administra la memoria usada por el montón a través de un proceso
denominado recolección de basura. La recolección de basura recupera la memoria periódicamente
según lo requerido por la comercialización de los productos que ya no son referenciadas.

Las mejores prácticas de  recolección de basura


La recolección de basura ocurre sólo cuando sea necesario o cuando se desencadena por una
llamada a GC.Collect. La recolección de basura automática está optimizado para aplicaciones
donde la mayoría de los casos son de corta duración, salvo para aquellos asignados al inicio de
la aplicación. Siguiendo el patrón de diseño que tendrá como resultado el mejor rendimiento.

Comparando el comportamiento de referencia y los tipos


de valor
Debido a que los tipos de referencia de representar  la dirección de datos en lugar de los propios
datos, asignar a una variable de referencia a otro no copia los datos. En su lugar, la asignación de
una variable de referencia a otra instancia meramente crea una segunda copia de la referencia, que
se refiere a la misma ubicación de memoria en el montón como la variable original.
Considere la siguiente estructura simple declaración:
// C#
Los números de estruct.
{
Public int val;

Números públicos(int _val)


{ Val = _val; }

Anulación pública string ToString()


{ Return val.ToString(); }
}

Ahora considere el código siguiente, que crea una instancia de la  estructura de números, copias de
esa estructura a una segunda instancia, modifica ambos valores, y muestra los resultados:

// C#
Los números n1 = new números(0); los
números N2 = n1;
N1.val += 1;
N2.val += 2;
Console.WriteLine("n1 = {0}, n2 = {1}", n1, n2).

Este código podría mostrar "n1 = 1, n2 = 2" porque una estructura es un tipo de valor, y copiando
un tipo de valor se traduce en dos valores distintos. Sin embargo, si cambia el num- tancia
la declaración de tipo de una estructura para una clase, la misma aplicación podría mostrar "n1 = 3,
n2 = 3". Cambiar números de una estructura para una clase, hace que pueda ser un tipo de
referencia, en lugar de un tipo de valor. Cuando se modifica un tipo de referencia variable, debe
modificar todas las copias de dicho tipo de referencia variable.

Los tipos de referencia incorporada


Hay cerca de 2500 tipos de referencia incorporada en el .NET Framework. Lo que no se deriva
de System.ValueType es un tipo de referencia, incluidos estos 2500 o tan incorporada en los tipos
de referencia. En la tabla 1-3 se enumeran los tipos más utilizados, de las que muchos otros tipos
de referencia se derivan.
Tabla 1-3  Tipos de referencia común.

Tipo de uso para


System.Object El tipo de objeto es el tipo más general en el marco.
Puede convertir cualquier tipo de System.Object, y
usted puede confiar en cualquier tipo
habiendo ToString, GetType, y es igual a los miembros
heredados de este tipo.
System.String  datos de texto.
System.Text.StringBuilder  datos de texto dinámico.
System.Array  matrices de datos. Esta es la clase base para todas las
matrices.
Declaraciones de matriz utilizar la sintaxis de matrices
específicas del idioma.
System.IO.Stream  Buffer para el archivo, el dispositivo y la red I/O.
Esta es una clase base abstracta; la tarea específica
de clases se derivan de Stream.
System.Exception   Handling System y las excepciones definidas por la
aplicación.
Las excepciones específicas de tareas heredan de este
tipo.

Las cadenas y los constructores de cadena


Los tipos son algo más que simples contenedores de datos, también proporcionan los medios para
manipu- finales que los datos a través de sus miembros.  System.String proporciona un conjunto de
miembros para trabajar con texto. Por ejemplo, el siguiente código realiza una búsqueda rápida y
sustituir:
// C#
String s = "Esto es texto a buscar"; s =
s.Replace("Búsqueda", "sustituir"); Console.WriteLine(s);

Cadenas de tipo System.String son inmutables en .NET. Esto significa que cualquier cambio a una
cadena hace que el tiempo de ejecución para crear una nueva cadena y abandonar la antigua. Esto
sucede invisiblemente, y muchos programadores podrían sorprenderse al enterarse de que el
siguiente código asigna cuatro nuevas cadenas en memoria:
// C#
Cadena s;

S = "wombat"; // "wombat"
S += " kangaroo"; // "canguro" wombat
S += " wallaby"; // "wombat canguro wallaby"
S += " Koala"; // "wombat canguro wallaby koala" Console.WriteLine(s);

Sólo la última cadena tiene una referencia; los otros tres serán eliminadas durante la recolección de
la basura. Evitar estos tipos de cadenas temporales ayuda a evitar la innecesaria la recolección de
basura, lo que mejora el rendimiento. Hay varias maneras de evitar cadenas temporales:
■   Utilice la  clase String's concat, Joino  métodos de formato para unir varios
elementos en una sola declaración.
■   Utilice la  clase StringBuilder para crear dinámicas (mutable) cadenas.
StringBuilder es la solución más flexible porque puede abarcar varias declaraciones. El constructor
predeterminado crea un búfer de 16 bytes de longitud, que crece a medida que sea necesario. Puede
especificar un tamaño inicial y Tamaño máximo si te gusta. El siguiente código- dem onstrates
utilizar StringBuilder:
// C#
System.Text.StringBuilder sb = new System.Text.StringBuilder(30).
Sb.append("wombat"); // Build String. sb.append("
KANGAROO");
Sb.append(" wallaby");
Sb.append(" koala");
String s = sb.ToString(); // copia el resultado en una cadena. Console.WriteLine(s);

Otra característica sutiles pero importantes de la  clase String es que anula los operadores
de System.Object. La tabla 1-4 muestra los operadores la  clase String de reemplazos.
Tabla 1-4  Operadores de cadena

Operador Visual Basic C# Acción en System.String


Además +o& + Une dos cadenas para crear una nueva
Cadena.
Igualdad = == Devuelve True si dos cadenas tienen
Mismo contenido; False si están
Diferente.
La desigualdad <> != El inverso del operador de igualdad.
Asignación = = Copia el contenido de una cadena
En una nueva. Esto provoca que las
cadenas
A comportarse como los tipos de
valor,
A pesarincluso
de que se implementan como
Los tipos de referencia.

Cómo crear y ordenar matrices


Las matrices son declarados usando paréntesis (en Visual Basic) o con corchetes (en C#) como
parte de la declaración.  Como con el  tipo string, System.Array ofrece miembros para trabajar con
sus datos contenidos. El siguiente código declara una matriz con algunas ini- cial los datos y, a
continuación, ordena la matriz:

// C#
// Declarar e inicializar una matriz. int[] ar = { 3, 1,
2 };

// Llame a un método compartido/matriz estática.


Array.sort(AR);
// Mostrar el resultado.
Console.WriteLine("{0}, {1}, {2}", ar[0], ar, ar[1][2]);

Cómo utilizar arroyos


Las secuencias son otro tipo muy común porque son los medios para la lectura y la escritura en el
disco y comunicar a través de la red. System.IO.Stream type es el tipo base para todos los tipos de
secuencias de tareas específicas. La tabla 1-5 muestra algunos de los tipos de flujo más
comúnmente utilizados. Además , secuencias de red se encuentran en el  espacio de nombres, y
System.Network.Sockets encr ypted arroyos se encuentran en el  espacio de nombres
System.Security.Cryptography.
Tabla 1-5  Tipos de secuencias comunes

 Utilización de tipo System.IO para


   Crear una base de FileStream flujo usado para escribir o leer desde
un archivo
MemoryStream  crea una corriente de base utilizado para escribir o
leer desde la memoria
StreamReader  lee datos desde la secuencia
StreamWriter  escribe datos a la secuencia

La más simple de las clases Stream son StreamReader y StreamWriter, que le permiten leer y


escribir archivos de texto. Puede pasar un nombre de archivo como parte del constructor, que le
permite abrir un archivo con una sola línea de código. Una vez que haya procesado un archivo,
llame al  método Close para que el archivo no permanecen bloqueados. El siguiente código, que
exige que el  espacio de nombres System.IO, demuestra cómo escribir y leer desde un archivo de
texto:

// C#
// Crear y escribir en un archivo de texto
StreamWriter sw = new StreamWriter("text.txt");
Sw.WriteLine("Hello, World!");
Sw.Close();

// Leer y mostrar un archivo de texto


StreamReader sr = new StreamReader("text.txt");
Console.WriteLine(sr.ReadToEnd();
Sr.Close();

Más info  arroyos

Para obtener más información acerca de las secuencias, consulte el


Capítulo 2, "de entrada/salida (E/S)".
Cómo lanzar y capturar excepciones
Las excepciones son eventos inesperados que interrumpir la ejecución normal de un conjunto. Por
ejemplo, si su conjunto es leer un archivo de texto grande de un disco extraíble y el usuario quita el
disco, el tiempo de ejecución se producirá una excepción. Esto tiene sentido porque no hay manera
la Asamblea pueda seguir funcionando.
Excepciones nunca debe causar en su conjunto a fallar completamente. En su lugar, usted debe
planear para las excepciones ocurren, detenerlos, y responder al evento. En el ejemplo anterior, se
podría notificar al usuario que el archivo no estaba disponible y, a continuación, esperar más
instrucciones del usuario. El siguiente código simplificado, que requiere el  espacio de nombres
System.IO, demuestra esto:
// C#
Pruebe
{
StreamReader sr = new StreamReader(@"C:\boot.ini");
Console.WriteLine(sr.ReadToEnd();
}
Catch (Exception ex)
{
// Si hay problemas para leer el archivo, muestra un mensaje de error
Console.WriteLine("Error al leer el archivo: " + ex.message);
}

En el ejemplo anterior, si se produce cualquier tipo de error, incluyendo un error de archivo no


encontrado, error de insuficiencia de privilegios, o un error durante la lectura del archivo de
procesamiento de con- sigue existiendo dentro del  bloque Catch. Si no se producen problemas, el
tiempo de ejecución se salta el  bloque Catch.
La base de la  clase de excepciones es muy útil y contiene un mensaje de error y otras apli- cación
de datos. Además de la base de la  clase de excepciones, el marco define cientos de clases de
excepción para describir  diferentes tipos de eventos, todas derivan d
desde System.SystemException. Además, puede definir sus propias excepciones  cuando sea
necesario para describir un evento con más detalle que los permitidos por las clases de excepción
estándar derivado de System.ApplicationException.
Tener varias clases de excepción le permite responder de manera diferente a los distintos tipos
de errores. El tiempo de ejecución sólo se ejecutará el primer  bloque catch coincidente con una
excep- ción tipo, sin embargo, por lo tanto, pedir a los bloques Catch desde los más específicos a
los menos específicos. El siguiente ejemplo de código muestra distintos mensajes de error para un
error de archivo no encontrado, un error de insuficiencia de privilegios, y cualquier otro tipo de
error que puede ocurrir.
// C#
Pruebe
{
StreamReader sr = new StreamReader("text.txt");
Console.WriteLine(sr.ReadToEnd);
}
Capturas (System.IO.FileNotFoundException ex)
{
Console.WriteLine("No se pudo encontrar el archivo.");
}
Capturas (System.UnauthorizedAccessException ex)
{
Console.WriteLine("no tiene suficientes permisos.");
}
Catch (Exception ex)
{
Console.WriteLine("Error al leer el archivo: " + ex.message);
}

Este proceso se denomina a veces filtrar excepciones. Manejo de excepciones también admite


un  bloque Finally. El  bloque Finally se ejecuta después del  bloque Try y los  bloques Catch ha
finalizado la ejecución, o si no se ha producido una excepción. Por lo tanto, usted debe utilizar
un  bloque finally para cerrar cualquier limpieza de arroyos o cualquier otro objeto que pudiera
quedar abierta si  se produce una excepción. El código de ejemplo siguiente cierra el  objeto
StreamReader si o no se produce una excepción:
// C#
StreamReader sr = new StreamReader("text.txt");
Pruebe
{
Console.WriteLine(sr.ReadToEnd();
}
Catch (Exception ex)
{
// Si hay problemas para leer el archivo, muestra un mensaje de error
Console.WriteLine("Error al leer el archivo: " + ex.message);
}
Finalmente
{
// Cerrar la StreamReader, o si no se ha producido una excepción sr.Close();
}

Observe que la  declaración StreamReader  se trasladó fuera del  bloque try en la pre- cede el
ejemplo. Esto es necesario porque el  bloque finally no puede tener acceso a las variables que se
declaran dentro del  bloque Try. Esto tiene sentido porque dependiendo del lugar donde se ha
producido una excepción, declaraciones de variables dentro del  bloque Try puede no haber sido
ejecutados. Para capturar las excepciones que se producen durante y después de la  declaración de
StreamReader, utilizar try anidadocatch // bloques Finally.
Normalmente, todos los códigos excepto por simples declaraciones de variables deben ocurrir
dentro de  bloques try. Error sólido manejo mejora la experiencia del usuario  cuando se producen
problemas y simplifica enormemente los problemas de depuración. Sin embargo, el manejo de
excepciones no incurrir

Una pequeña penalización de rendimiento. Para ahorrar espacio y centrarse en temas específicos, el
código de muestra en este libro normalmente no incluirá la gestión de excepciones.
Laboratorio: Trabajo con tipos de referencia
Los siguientes ejercicios reforzar el conocimiento de los tipos de referencia, cadenas y excep-
ciones. Si se produce un problema de completar un ejercicio, los proyectos terminados están
disponibles en el CD en la carpeta de código.
    Ejercicio 1: Identificar los tipos de valor o referencia
En este ejercicio, va a escribir una aplicación de consola que muestra si los objetos son tipos de
valor o de referencia.
1.  Con Visual Studio, cree un nuevo proyecto de aplicación de consola. Nombre del proyecto
Tipos List-Value.
2.  Crear instancias de las clases siguientes:
❑   SByte

❑ El   Byte

❑   Int16

❑   Int32

❑   Int64

❑   cadena

Excepción
El siguiente código muestra esto:
// C#
SByte un Byte = 0, b
= 0; Int16 c = 0;
Int32 d = 0; Int64 e
= 0; string s = "";
Exception ex = nueva excepción();
3.  Agregue  cada uno  de  los casos a un nuevo objeto Array, como demuestra el código siguiente:
// C#
Tipos object[] = {a, b, c, d, e, s, ex };

4.  Dentro de un  bucle foreach, verificar el Object.GetType().IsValueType propiedad para deter-


minar si el tipo es un tipo de valor. Mostrar el nombre de cada tipo y si es un tipo de valor o un tipo
de referencia, tal y como demuestra el código siguiente:
// C#
Foreach ( objeto o en tipos )
{
Tipo de cadena.
Si (o.GetType().IsValueType)
Type = "Valor tipo";
Otra cosa
Type = "Tipo de referencia".
Console.WriteLine("{0}: {1}", oh.GetType(), escriba );
}
5.  Ejecute la aplicación de consola, y verifique que cada tipo coincide con su com- prensión.
   
Ejercicio 2: Trabajar con cadenas y
matrices.
En este ejercicio, va a escribir una función para ordenar una cadena.
1.  Con Visual Studio, cree un nuevo proyecto de aplicación de consola. Nombre del proyecto
SortString.
2.  Definir una cadena. A continuación, utilice el  método String.Split para separar la cadena en
una matriz de palabras. El siguiente código muestra esto:
// C#
String s = "Microsoft .NET Framework 2.0 Application Development Foundation".
String[] sa = s.split(" ");

3.  Llamar al  método Array.Sort para ordenar el conjunto de palabras, como demuestra el código
siguiente:

// C# Array.sort(SA);

4.  Llamar al  método String.Join para convertir la matriz de palabras en una sola cadena y, a
continuación, escriba la cadena a la consola. El código de ejemplo siguiente demonio demuestra
esto:

// C#
S = String.Join(" ", sa);
Console.WriteLine(s);

5.  Ejecute la aplicación de consola, y comprobar que funciona correctamente.


   

Ejercicio 3: Trabajar con arroyos y excepciones


Piense en un escenario en el que un compañero escribió una aplicación sencilla de Windows
Forms para ver archivos de texto. Sin embargo, los usuarios se quejan de que es muy
temperamental. Si el usuario se equivoca al escribir el nombre de archivo o si el archivo no está
disponible por cualquier motivo, la aplicación falla con un error de excepción no controlada. Debe
agregar el control de excepciones para la aplica- ción para mostrar mensajes de error descriptivos a
los usuarios si el archivo no está disponible.
1.  Copiar el capítulo01\lección2-ViewFile carpeta desde el CD a tu disco duro y abrir la versión
en C# o Visual Basic .NET versión del proyecto ViewFile.
2.  Las excepciones se producen cuando un usuario intenta ver un archivo. Por lo tanto, modificar
el código que se ejecuta para la showButton.Haga clic en evento. Agregar código para capturar
cualquier tipo de excepción que se produce, y mostrar el error en un cuadro de diálogo al usuario.
Si se produce una excepción después de la TextReader objeto es inicializado, debe cerrarlo si o no
se produce una excepción. Necesitará dos  bloques Try anidadas: uno para atrapar excepciones
durante la  inicialización de TextReader, y un segundo para capturar excepciones cuando se lee el
archivo. En el ejemplo de código siguiente se muestra esto:
// C#
Pruebe
{
TextReader tr = new StreamReader(locationTextBox.Text);
Pruebe
{ DisplayTextBox.Text = tr.ReadToEnd(); }
Catch (Exception ex)
{ MessageBox.Show(ex.message); }
Finalmente
{ Tr.Close(); }
}
Catch (Exception ex)
{ MessageBox.Show(ex.message); }

3.  Ejecute su aplicación. Primero verifique que pueda mostrar correctamente un archivo de texto.
A continuación, proporcione un nombre de archivo no válido, y compruebe que aparece un cuadro
de mensaje cuando se proporciona un nombre de archivo no válido.
4.  Siguiente Agregar sobrecargado de excepciones para la captura
de System.IO.FileNotFoundExcep- ción y System.UnauthorizedAccessException. El código de
ejemplo siguiente demonio demuestra esto:

// C#
Pruebe
{
TextReader tr = new StreamReader(locationTextBox.Text);
Pruebe
{ DisplayTextBox.Text = tr.ReadToEnd(); }
Catch (Exception ex)
{ MessageBox.Show(ex.message); }
Finalmente
{ Tr.Close(); }
}
Capturas (System.IO.FileNotFoundException ex)
{ MessageBox.Show("Lo sentimos, el archivo no existe."); }
Capturas (System.UnauthorizedAccessException ex)
{ MessageBox.Show("Lo sentimos, pero no tienes suficientes privilegios."); }
Catch (Exception ex)
{ MessageBox.Show(ex.message); }

5.  Ejecutar la aplicación de nuevo, y comprobar que ofrece su nuevo mensaje de error si se


proporciona un nombre de archivo no válido.

Resumen de la lección
■ Los   tipos de referencia contienen la dirección de datos en lugar de los datos reales.
■   Cuando copia un tipo de valor, una segunda copia del valor es creado. Cuando copia un tipo de
referencia, sólo el puntero es copiado. Por lo tanto, si copia una referencia- encia tipo y, a
continuación, modifique la copia, la copia y el original se cambian las variables.
■   El .NET Framework incluye un gran número de tipos de referencia incorporada que puede
utilizar directamente o utilizar para construir tus propios tipos.
■   Lascadenas son inmutables; utilizar la  clase StringBuilder para crear una cadena de forma
dinámica.
■   Utilice secuencias de lectura y escritura a los archivos, la memoria y la red.
■   Utilice la  cláusula catch dentro de  bloques try para filtrar excepciones por tipo. Cierre y dis-
plantean de nonmemory recursos en la  cláusula final de un  bloque Try.

Examen de la lección
Puede utilizar las siguientes preguntas para poner a prueba tus conocimientos de la información
Les- hijo 2, "Uso de los tipos de referencia común." Las preguntas también están disponibles en el
CD complementario si prefiere revisarlos en forma electrónica.

Nota   Respuestas
Las respuestas a estas preguntas y explicaciones de por qué cada opción de respuesta es
correcta o no se encuentran en la sección "Respuestas" al final del libro.

1.  Cuales de los siguientes son tipos de referencia? (Seleccione todos los que correspondan).
A.  Los tipos declarados Nullable
B.   Cadena
C.   Excepción
D.   Todos los tipos derivados de System.Object
2.  ¿Cuál es el orden correcto de  cláusulas Catch cuando el manejo de diferentes tipos de
excepción?
A.  Orden de más general a más específico.
B.   Orden de más probable a menos probable de ocurrir.
C.   Ordenar desde el más específico al más general.
D.   Orden de menos probable que con mayor probabilidad de ocurrir.
3.  Cuándo se debe utilizar la  clase StringBuilder en lugar de la  clase String?
A.  Cuando se construye una cadena de cadenas más cortas.
B.   Cuando se trabaja con datos de texto de más de 256 bytes.
C.   Cuando desea buscar y reemplazar el contenido de una cadena.
D.   Cuando una cadena es un tipo de valor.
4.  ¿Por qué debe cerrar y disponer de recursos en un  bloque finally en lugar de un
 Bloque catch?
A.  Se le evita tener que repetir la operación en cada captura.
B.    Ejecutar bloques finally si o no se produce una excepción.
C.   El compilador genera un error si los recursos no están dispuestos  en el finalmente
Bloque.
D.   No puede disponer de los recursos en un  bloque Catch.
Lección 3: Crear clases
En los lenguajes orientados a objetos, el grueso del trabajo deberá realizarse dentro de los objetos.
Pero todas las aplicaciones más sencillas requieren construir uno o más clases personalizadas, cada
una con varias propiedades y métodos que se utilizan para realizar tareas relacionadas con ese
objeto. Esta lección explica cómo crear clases personalizadas.

Después de esta lección, será capaz de:


■   Describir y utilizar la herencia.
■   Describir y utilizar interfaces.
■   Describir y utilizar clases parciales.
■   Crear un tipo genérico, y utilizar la función de tipos genéricos.
■   Responder y generar eventos.
■   Agregar atributos para describir conjuntos y métodos.
■   Pasar un tipo de una biblioteca de clases a otro mediante el reenvío de tipos.

¿Qué es la herencia?
El .NET Framework tiene miles de clases y cada clase tiene muchos métodos y propiedades
diferentes. El seguimiento de todas estas clases y miembros sería imposible si el .NET Framework
no se ejecutaron muy coherente. Por ejemplo, cada clase tiene un  método ToString que realiza
exactamente la misma tarea--verting una instancia de la clase en una cadena. Asimismo, muchas
clases admiten los mismos operadores, tales como la comparación de dos instancias de una clase
para la igualdad.
Esta coherencia es posible a causa de la herencia y las interfaces (descrito en la siguiente
sección). Utilizar la herencia para crear nuevas clases a partir de otras existentes. Por ejemplo,
aprenderá en el Capítulo 6, "Gráficos", que la  clase Bitmap hereda de la  clase Image y se
extiende por la adición de funcionalidad. Por lo tanto, se puede utilizar una instancia de la  clase
Bitmap de la misma manera que puede utilizar una instancia de la  clase Image. De cualquier
modo, la  clase Bitmap proporciona métodos adicionales que le permiten hacer más con imágenes.
Puede crear fácilmente una excepción personalizada heredando de la clase System.Application-
excepción, como se muestra aquí:
// C#
La clase System.ApplicationException DerivedException :
{
Anulación pública string mensaje
{
Get { return "se produjo un error en la aplicación."; }
}
}
Puede lanzar y agarrar la nueva excepción porque la clase personalizada hereda esa conducta de su
clase base, como se muestra aquí:
// C#
Pruebe
{
Nueva DerivedException throw();
}
Capturas (DerivedException ex)
{
Console.WriteLine("Fuente: {0}, error: {1}", ex.Source, ex.message);
}

Observe que la excepción personalizada no sólo apoya la throw/catch comportamiento, pero


también incluye un  miembro de origen (así como otros) hereda de System.Application- excepción.
Otro beneficio de la herencia es la capacidad de usar las clases derivadas indistintamente. Por
ejemplo, existen cinco clases que heredan de System.Drawing.Brush clase
base: HatchBrush LinearGradientBrush ,, , PathGradientBrush SolidBrush  TextureBrush , y.
Los gráficos. método DrawRectangle requiere un  objeto Brush como uno de sus parámetros; sin
embargo, usted nunca tendrá que pasar un objeto de la  clase Brush base
a Graphics.DrawRectangle. En su lugar, se le pasa un objeto de una de las clases derivadas.
Porque cada uno de ellos es derivado de la  clase Brush, el  método Graphics.DrawRectangle
puede aceptar cualquiera de ellos. Del mismo modo, si se va a crear una clase derivada de la  clase
Brush, también puede pasar un objeto de la clase Graphics.DrawRectangle.
¿Qué es una interfaz?
Interfaces, también conocido como contratos, definir un conjunto común de miembros que todas
las clases que implementan la interfaz debe proporcionar. Por ejemplo, la  interfaz IComparable
define el  método CompareTo, que permite que dos instancias de una clase para ser comparado por
la igualdad. Todas las clases que implementan la  interfaz IComparable, sea personalizada-cre-
ados o construido en .NET Framework, puede ser comparado por la igualdad.
IDisposable es una interfaz que proporciona un único método dispose, para habilitar los
ensamblados que crea una instancia de la clase para liberar los recursos que la instancia ha pro-
sumed. Para crear una clase que implementa la  interfaz IDisposable utilizando Visual Studio
2005, siga estos pasos:

1.  Crear la declaración de clase. Por ejemplo:


// C#
Clase BigClass
{
}

2.  Agregar la declaración de interfaz. Por ejemplo:


// C#
Clase : BigClass IDisposable
{
}
3.  Si está utilizando Visual Basic, Visual Studio debe generar automáticamente declaraciones de
método para cada uno de los métodos necesarios. Si no es así, eliminar la implementa el comando
y vuelva a intentarlo; Visual Studio aún se puede arrancar. Si está utilizando C#, haga clic con el
botón derecho en la declaración de la interfaz, haga clic en Implementar interfaz y, a continuación,
haga clic en Implementar interfaz nuevamente, como se muestra en la figura 1-1.

Figura 1-1  Visual Studio simplifica la implementación de una interfaz

4.  Escribir código para cada uno de los métodos de la interfaz. En este ejemplo, puede escribir
código en el  método Dispose para liberar los recursos que tenía asignados.
La tabla 1-6 muestra las interfaces utilizadas más comúnmente en el .NET Framework.
Tabla 1-6  interfaces de uso común

Clase   Descripción
IComparable  implementado por tipos cuyos valores pueden ser ordenado;
por ejemplo, las clases numéricas y de
cadena. IComparable es necesaria para la ordenación.
IDisposable Demultas métodos para eliminar manualmente de un objeto. Esta
interfaz es importante para grandes objetos que consumen
recursos, o los objetos que bloquean el acceso a recursos tales
como bases de datos.
IConvertible  permite una clase que se va a convertir en un tipo base,
como Boolean,
Byte, Doble, o String.
ICloneable   admite copiar un objeto.
IEquatable  permite comparar a instancias de una clase para la igualdad. Por
ejemplo, si implementa esta interfaz, usted podría decir "si
(A == b)".
IFormattable  le permite convertir el valor de un objeto en una cadena con
formato especial. Esto ofrece una mayor flexibilidad que la base
del  método ToString.
Puede crear sus propias interfaces, demasiado. Esto es útil si necesita crear varias clases
personalizadas que se comportan de forma similar y se pueden utilizar indistintamente. Por
ejemplo, el código siguiente define una interfaz que contiene tres miembros:

// C#
Interfaz IMessage
{
// Enviar el mensaje. Devuelve True es un éxito, FALSE en caso contrario. bool
Send();
// El mensaje a enviar mensaje de
cadena { GET; set; }
// La Dirección Enviar a. cadena
dirección { GET; set; }
}

Si implementar esa interfaz en una clase nueva, Visual Studio genera la siguiente plantilla
para los miembros de la interfaz:

// C#
Clase IMessage EmailMessage :
{
Public bool Send()
{
Nueva excepción throw("La operación o método no está implementado.");
}

Mensaje de cadena pública


{
Obtener
{
Nueva excepción throw("La operación o método no está implementado.");
}
Establecer
{
Nueva excepción throw("La operación o método no está implementado.");
}
}

Dirección de la cadena pública


{
Obtener
{
Nueva excepción throw("La operación o método no está implementado.");
}
Establecer
{
Nueva excepción throw("La operación o método no está implementado.");
}
}
}

Si crea una clase personalizada y, posteriormente, decide que sería útil disponer de varias
clases con los mismos miembros, Visual Studio tiene un atajo para extraer una interfaz de una
clase personalizada. Simplemente siga estos pasos:
1.  Haga clic con el botón secundario en la clase en Visual Studio 2005.
2.  Haga clic en Refactorizar y, a continuación, haga clic en Extraer interfaz.
3.  Especifique el nombre de la interfaz, seleccione los miembros públicos que deben
formar la inter- cara y, a continuación, haga clic en Aceptar.
Las clases pueden implementar varias interfaces. Por lo tanto, una clase puede implementar
la    interfaz IComparable y IDisposable.

¿Cuáles son las clases parciales?


Las clases parciales permiten dividir la definición de una clase en varios archivos de código
fuente. La ventaja de este enfoque es que oculta los detalles de la definición de clase para que
las clases derivadas pueden centrarse en más porciones significativas.
La clase de formulario Windows Forms es un ejemplo de una clase parcial incorporada. En
Visual Studio 2003 y anteriores, formularios clases incluidas en el código generado por el
diseñador del formulario. Ahora que el código está oculto en una clase parcial
Form .Designer.vb o Form.Designer.cs.
En Visual Basic, debe seleccionar Mostrar todos los archivos en el Explorador de soluciones
para ver los archivos de clase de par- cial. En C#, que está activado de forma predeterminada.
Las clases parciales que no son parte de los objetivos del examen, pero usted necesita saber
acerca de ellos, para que pueda encontrar el diseñador de formularios de código al crear un
nuevo formulario de Windows.

¿Qué son los genéricos?


Los medicamentos genéricos son parte de .NET Framework el tipo de sistema que permite
definir un tipo dejando algunos detalles sin especificar. En lugar de especificar los tipos de
param- eters o clases de miembros, puede permitir que el código que usa el tipo que se
especifique . Esto permite que el código de consumo para adaptar su tipo a sus propias
necesidades específicas.

Punta del examen   tipos genéricos son nuevos en .NET 2.0, y probablemente verá
un número inusualmente grande de preguntas acerca de los genéricos en el examen.

La versión de .NET Framework 2.0 incluye varias clases genéricas en el sistema.Colectores-


ciones.Generic , incluido el diccionario de nombres, Cola SortedDictionary,
y SortedList. Estas clases funcionan de manera similar a sus homólogos no genéricos
en System.Collections, pero ellos ofrecen mayor rendimiento y seguridad de tipos.

Más info  colecciones genéricas.


La versión 2.0 de .NET Framework incluye el   espacio de nombres
System.Collections.Generic, que proporciona de forma integrada en colecciones que ofrecen
un rendimiento mejorado a través del estándar de las colecciones. Para obtener más
infor- mación, consulte el Capítulo 4, "Colecciones y genéricos".

¿Por qué el uso de genéricos?


Las versiones 1.0 y 1.1 de .NET Framework no admiten genéricos. En su lugar, desa- opers
utiliza la  clase de objeto para los parámetros y los miembros y arrojaría otras clases y de
la  clase de objeto. Los genéricos ofrecen dos ventajas significativas en comparación con
la  clase de objeto:
■   reduce los errores en tiempo de ejecución,  el compilador no puede
detectar el tipo de errores cuando emitidos y de la  clase de objeto. Por ejemplo, si
convierte una cadena a una  clase de objeto y, a continuación, intentar convertir
ese objeto a un entero, el compilador no se captura el

Error. En su lugar, el tiempo de ejecución se producirá una excepción. El uso de genéricos


permite al compilador para detectar este tipo de errores antes de que su programa se ejecuta.
Además, puede especificar restricciones para limitar las clases usadas en un genérico,
permitiendo que los com- el elevador para detectar un tipo incompatible.

■   rendimiento mejorado  Casting requiere boxing y unboxing (explicada más


adelante en la lección 4, "Conversión entre tipos"), en la que se roba el tiempo de
procesador y ralentiza el rendimiento. El uso de genéricos no requiere la fundición
o el boxeo, que
Mejora el rendimiento en tiempo
de ejecución.
Mundo Real
Tony Northrup
No he sido capaz de reproducir las ventajas de rendimiento de los genéricos; sin
embargo, de acuerdo a Microsoft, los genéricos son más rápidos que el casting. En
la práctica, la fundición resultó ser varias veces más rápido que mediante un
genérico. Sin embargo, probablemente no notará las diferencias de rendimiento en
sus aplicaciones. (Mis pruebas durante 100.000 iteraciones toma sólo unos pocos
segundos.) Así que aún debe utilizar genéricos porque son de tipo seguro.

Cómo crear un tipo genérico


En primer lugar, examinar las siguientes clases. Clases Obj y Gen realizar exactamente las
mismas tareas, pero  utiliza el objeto obj clase para habilitar cualquier tipo para ser aprobado,
mientras que el gen utiliza genéricos:
// C#
Clase Obj
{
Objeto público t;
Objeto público u;

Público(object obj _t, objeto _u)


{
T = _t;
U = _u;
}
}

Clase Gen<T, U>


{
T t; público
U público u;

Gen pública(T _T, U _u)


{
T = _t;
U = _u;
}
}

Como puede ver, la  clase Obj tiene dos miembros del tipo Object. La  clase gen tiene dos
miembros de tipo T y U. El código de consumo determinará los tipos para T y U.
Dependiendo de cómo consumir el código utiliza la  clase gen, T y U puede ser una cadena,
un int, una clase personalizada, o cualquier combinación de los mismos.
Hay una gran limitación para la creación de una clase genérica: código genérico sólo es válido
si va a compilar para cada posible instancia construida del genérico, ya sea un  Int, una cadena,
o de cualquier otra clase. Básicamente, usted está limitado a las capacidades de la base
de  clase de objeto al escribir código genérico. Por lo tanto, usted puede llamar al  método
ToString o
 Método GetHashCode dentro de su clase, pero no puede utilizar el signo + o el operador >.
Estas mismas restricciones no se aplican a los consumidores porque el código código de
consumo ha declarado un tipo para el genérico.

Cómo consumir un tipo genérico


Cuando usted consume un tipo genérico, debe especificar los tipos para cualquier genéricos
utilizados. Considere la siguiente aplicación de consola de código, que utiliza
el gen y obj clases:
// C#
// Agregar dos cadenas utilizando la clase Obj oa Obj = new
Obj("Hello, ", "Mundo!"); Console.WriteLine((string)oa.t +
(string)oa.u);

// Agregar dos cadenas mediante la clase gen


Gen<string, string> ga = new Gen<string, string>("Hello, ", "Mundo!"); Console.WriteLine(ga.t + ga.u);

// Agregar un doble y un int utilizando la clase Obj Obj ob = new


Obj(10.125, 2005); Console.WriteLine((La doble)ob.t + (int)ob.u);

// Agregar un doble y un int utilizando la clase gen


Gen<double, int> gb = new Gen<double, int>(10.125, 2005);
Console.WriteLine(gb.t + gb.u);

Si ejecuta ese código en una aplicación de consola, el Obj y Gen clases de producir


exactamente los mismos resultados. Sin embargo, el código que utiliza la  clase gen realmente
funciona más rápido porque no requieren conversión boxing y unboxing y de la  clase de
objeto. Adi- cionalmente, los desarrolladores tendrán mucho más fácil utilizando la  clase gen.
En primer lugar, desa- Opers no habría fundido manualmente desde la  clase de objeto para los
tipos apropiados.
Segundo, los errores de tipo serían capturados en tiempo de compilación en lugar de en tiempo
de ejecución. A- onstrate dem que benefician, considere el siguiente código, que contiene un
error:
// C#
// Agregar un doble y un int utilizando la clase gen
Gen<double, int> gc = new Gen<double, int>(10.125, 2005);
Console.WriteLine(gc.t + gc.u);

// Agregar un doble y un int utilizando la clase Obj oc Obj =


new Obj(10.125, 2005); Console.WriteLine((int)oc.t +
(int)oc.u);

La última línea de código de ejemplo que contiene un error-el oc.El valor t es arrojado a


un int en lugar de un doble. Lamentablemente, el compilador no se captura el error. En su
lugar, en C#, se produce una excepción en tiempo de ejecución cuando el tiempo de ejecución
intenta convertir un doble a un  valor int. En Visual Basic, que permite conversiones de
restricción por defecto, el resultado es aún peor, se produce un error de cálculo. Es mucho más
fácil corregir un error que el compilador las capturas y mucho más difíciles de detectar y
corregir un error en tiempo de ejecución, por lo que la clase genérica pro- porciona un claro
beneficio.

Cómo utilizar restricciones


Los genéricos sería muy limitado si sólo podía escribir código que permita compilar para
cualquier clase, porque se estaría limitado a las capacidades de la base de  clase de
objeto. Para superar esta limitación, utilice las restricciones para colocar los requisitos acerca
de los tipos de código de consumo que puede sustituir a su genérico.
Los genéricos admiten cuatro tipos de restricciones:
■   interfaz  permiten sólo los tipos que implementan interfaces específicas
para utilizar el genérico.
■   clase Base  Permitir sólo tipos que coincidan o heredar de una clase base
específica para utilizar el genérico.
■   Constructor  requieren tipos que use su genérico para implementar un
constructor sin parámetros.
■   referencia o tipo de valor  requieren tipos que use su genéricos para ser
una referencia- encia o tipo de valor.
Utilizar la  cláusula As en Visual Basic o en la  cláusula where en C# para aplicar una
restricción a un genérico. Por ejemplo, la siguiente clase genérica sólo podía ser utilizada por
los tipos que implementan  la  interfaz IComparable:
// C#
Clase CompGen<T>
Where T : IComparable
{
Público t1; T
Public T t2;

CompGen pública(T _t1, T _t2)


{
T1 = _t1;
T2 = _t2;
}

Public T max()
{
Si (t2.CompareTo(t1) < 0)

La clase anterior compilará correctamente. Sin embargo, si quita la  cláusula where, el


compilador devolverá un error indicando que el tipo genérico T no contiene una definición
de CompareTo. Limitando el genérico para las clases que implementan IComparable, usted
garantiza que el  método CompareTo estará siempre disponible.
Eventos
La mayoría de los proyectos no son lineales. En aplicaciones de Windows Forms, usted podría
tener que esperar para que un usuario haga clic en un botón o presione una tecla y, a
continuación, responder a dicho evento. En las aplicaciones de servidor, es posible que tenga
que esperar una solicitud recibida en la red. Estos capabil- dades son proporcionados por los
eventos de .NET Framework, como se describe  en las secciones siguientes.

¿Qué es un evento?
Un evento es un mensaje enviado por un objeto para indicar la ocurrencia de una acción. La
acción puede ser causada por la interacción del usuario, como hacer clic con el ratón, o podría
ser reaccionará por algún otro programa de lógica. El objeto que genera el evento se conoce
como remitente del evento. El objeto que captura el evento y responde a ella se llama
el receptor de eventos.
En caso de que la comunicación, el remitente del evento no sabe qué objeto o método recibirá
los eventos que provoca. Lo que se necesita es un intermediario (o puntero-como el
mecanismo) entre la fuente y el receptor. El marco de trabajo .NET- define un tipo especial
(delegado) que proporciona la funcionalidad de un puntero a función.

¿Qué es un delegado?
Un delegado  es una clase que puede contener una referencia a un método. A diferencia de
otras clases, una clase del delegado tiene una firma, y puede contener referencias a los
métodos que coinciden con su firma. Un delegado es equivalente a un puntero a función con
seguridad o una devolución de llamada. Mientras los delegados tienen otros usos, la discusión
se centra en la funcionalidad de gestión de eventos de delegados. Una declaración de delegado
es suficiente para definir una clase de delegado. La declaración proporciona la firma del
delegado, y common language runtime proporciona la aplicación. En el ejemplo siguiente se
muestra una declaración de delegado de evento:
// C#
Public void AlarmEventHandler delegado(object sender, EventArgs e);

La firma estándar de un delegado del controlador de eventos define un método que no


devuelve un valor, cuyo primer parámetro es de tipo Object y hace referencia a la instancia
que genera el evento, y cuyo segundo parámetro se deriva de tipo EventArgs y contiene los
datos del evento. Si el evento no se generan datos de evento, el segundo parámetro es
simplemente una instancia de EventArgs. De lo contrario, el segundo parámetro es un tipo
personalizado derivada de EventArgs y suministros los campos o propiedades necesarias para
mantener los datos del evento.
EventHandler es un delegado predefinidos que específicamente representa un método de
control de eventos para un evento que no genere datos. Si su evento no generan datos, usted
debe suministrar su propio tipo de datos de evento y, o bien crear un delegado donde el tipo
del segundo  parámetro es el tipo personalizado, o debe utilizar el
genérico EventHandler delegado de clase y sustituye el  tipo personalizado para el parámetro
de tipo genérico.
Para asociar el evento con el método que controlará el evento, agrega una instancia del
delegado al evento. El controlador de eventos se llama cuando se produce el evento, a menos
que se extraiga el delegado.

Cómo responder a un
evento
Usted debe hacer dos cosas para responder a un evento:
■   Crear un método para responder al evento. El método debe coincidir con la  firma del
delegado. Normalmente, esto significa que debe devolver void y aceptar dos parámetros:
un objeto EventArgs y  (o una clase derivada). El siguiente código muestra esto:
// C#
Private void button1_Click(object sender, EventArgs e)
{
// Código de Método
}

■   agregarel controlador de eventos para indicar qué método debe recibir eventos, como
demuestra el código siguiente:
// C#
Este.button1.Click += new System.EventHandler(this.button1_Click);

Nota   .NET 2.0

El .NET Framework 2.0 incluye una nueva versión genérica del   tipo EventHandler.

Cuando se produce el evento, el método especificado se ejecutará.

Cómo generar un evento


Como mínimo, debe hacer tres cosas para provocar un evento:
■   Crear un delegado:
// C#
Delegado público void MyEventHandler(object sender, EventArgs e);

■   Crear un miembro de evento:


// C#
Evento público MyEventHandler MyEvent;
■   invocaral delegado dentro de un método cuando necesite subir el evento, como FOL-
bramido código demuestra:

// C#
Si (MyEvent != null)
MyEvent(este, EventArgs.Empty); e =

EventArgs EventArgs nuevo();

Si (controlador != null)
{
// Invoca los delegados.
controlador(esto, e);
}
// Nota que C# comprobaciones para determinar si el controlador es nulo.
// Esto no es necesario en Visual Basic

Además, se puede derivar una clase personalizada de EventArgs si necesita pasar infor-
mación al controlador de eventos.

NOTE   Las diferencias en la crianza de los eventos en Visual Basic y C#.


Visual Basic y C# difieren al desencadenamiento de eventos. En C#, se debe
verificar si el evento es null antes de hacer la llamada. En Visual Basic, puede
omitir esta comprobación.

¿Qué son los atributos?


Los atributos describen un tipo, método o propiedad de forma que se pueden consultar
mediante programación utilizando una técnica denominada reflexión. Algunos usos comunes
para los atributos son
■   Especificar qué privilegios de seguridad requiere una clase
■   especificar privilegios de seguridad para negarse a reducir el riesgo de seguridad
■   declarar funciones, como, por ejemplo, apoyando la serialización
■   describir el conjunto proporcionando un título, una descripción y un aviso de copyright
Los tipos de atributos se derivan de la  clase base System.Attribute y se especifican utilizando
<> o [] La notación. En el ejemplo de código siguiente se muestra cómo agregar atributos de
ensamblado:
// C# - AssemblyInfo.cs
[Asamblea: AssemblyTitle("ch01CS")]
[Asamblea: AssemblyDescription("Capítulo 1 muestras")] [asamblea:
AssemblyConfiguration("")]
[Asamblea: AssemblyCompany("Microsoft Learning")]
[asamblea: AssemblyProduct("ch01CS")]
[Asamblea: AssemblyCopyright("Copyright © 2006")] [asamblea:
AssemblyTrademark("")]
Visual Studio crea automáticamente algunos atributos estándar para su montaje cuando se crea
un proyecto, incluyendo un título, una descripción, la compañía, guía y versión. Usted debe
modificar estos atributos para cada proyecto se crea porque los valores predeterminados no
incluir información importante como la descripción.
Atributos hacen más que describir un conjunto de otros desarrolladores, también pueden
declarar los requisitos o capacidades. Por ejemplo, para activar una clase que se va a serializar,
debe agregar el  atributo Serializable, como demuestra el código siguiente:
// C#
[serializable]
Clase ShoppingCartItem
{
}

Sin el  atributo Serializable, una clase no es serializable. Asimismo, el código siguiente utiliza
atributos para declarar que necesita leer el archivo C:\boot.ini. A causa de este atributo, el
tiempo de ejecución generará una excepción antes de la ejecución si carece el speci- zos
privilegio:

// C#
Utilizando
System;
Utilizando System.Security.Permissions.

[General:FileIOPermissionAttribute(SecurityAction.RequestMinimum, Read=@"C:\boot.ini")]
DeclarativeExample namespace
{
La clase Class1
{
[STAThread]
Static void main(String[] args)
{
Console.WriteLine("Hello, World!");
}
}
}

¿Qué es el reenvío de tipos?


Reenvío de tipo es un atributo (implementado en TypeForwardedTo) que le permite pasar un
tipo de un ensamblado (ensamblado A) en otro ensamblado (ensamblado B), y hacerlo de tal
manera que no es necesario recompilar los clientes que consumen general A. Después de un
componente (conjunto) de buques y está siendo utilizado por la solici- tudes de cliente, puede
utilizar el reenvío de tipos para pasar un tipo del componente (general) en otro ensamblado y
enviar el componente actualizado (y cualquier otro pe- blies requerido), y el cliente las
aplicaciones seguirán funcionando sin ser recompilado. Reenvío de tipo sólo funciona para los
componentes a los que hace referencia a  las aplicaciones existentes. Cuando reconstruye una
aplicación, debe haber ensamblado adecuado para cualquier tipo de referencias utilizadas en la
aplicación.
Para mover un tipo de clase de una biblioteca a otra, siga estos pasos:

Nota   .NET 2.0

El reenvío de tipos es una


característica nueva en .NET 2.0.

1.  Agregar un  atributo TypeForwardedTo al origen ensamblado de biblioteca de


clases.
2.  Cortar la definición del tipo de origen de la biblioteca de clases.
3.  Pegar la definición de tipo en los destinos de la biblioteca de clases.
4.  Reconstruir ambas bibliotecas.
El código siguiente muestra la declaración del atributo utilizado para mover TypeA al DestLib
Biblioteca de clase:
// C#
Utilizando System.Runtime.CompilerServices;
[general:TypeForwardedTo(typeof(DestLib.Escribaa)]

Práctica: Crear una clase derivada con delegados


Los siguientes ejercicios demuestran la herencia y eventos. Si se produce un problema de
completar un ejercicio, los proyectos terminados están disponibles en el CD en la carpeta de
código.
    Ejercicio 1: derivar una clase nueva a partir de una clase existente
En este ejercicio, va a derivar una clase nueva a partir de la  clase de persona que creó en
Lección 1.
1.  Copiar el capítulo01\lección3-Persona carpeta desde el CD a tu disco duro y abrir la
versión en C# o Visual Basic versión del cre- ateStruct proyecto.
2.  Cambiar la  estructura de la persona a una clase.
3.  Crear la definición de una nueva clase denominada Manager que hereda de la persona
base
Clase.
// C#
Gestor de clases : Persona
{
}

4.  La adición de dos nuevos miembros públicos como cadenas: phoneNumber y


OffceLocation.
5.  Invalidar el constructor para que acepte un número de teléfono y la ubicación de la oficina,
para definir los nuevos miembros. Usted tendrá que llamar al constructor de la clase base,
como se muestra en el siguiente ejemplo de código:

// C#
Administrador público(string _FirstName, LastName, cadena _int _edad, sexos _el
género, la cadena _phoneNumber, cadena _officeLocation)
: Base (_FirstName, LastName, __edad, _sexo)
{
PhoneNumber = _phoneNumber;
OfficeLocation = _officeLocation;
}

6.  Reemplazar el  método ToString para agregar el número de teléfono y la ubicación de la


oficina, como se muestra en el siguiente ejemplo:

// C#
Anulación pública string ToString()
{
Volver base.ToString() + " " + phoneNumber + " " + officeLocation;
}

7.  Modificar el  método principal para crear un  objeto Manager en lugar de un objeto person.
Vuelva a ejecutar la aplicación para comprobar que funciona correctamente.

Ejercicio 2: Responder a un evento

En este ejercicio vamos a crear una clase que responde a un evento de temporizador.
1.  Con Visual Studio, cree un nuevo proyecto de aplicación de formularios Windows Forms.
Nombre del proyecto TimerEvents.
2.  Agregar un  control ProgressBar al formulario, como se muestra en la figura 1-2.

Figura 1-2  controlará esta barra de progreso por responder a los


eventos del temporizador
3.  Dentro de la clase de formulario de declaración, declarar una instancia de System.Windows
Forms. objeto de temporizador.  Los objetos de temporizador se puede utilizar para lanzar los
eventos después de un número especificado de milisegundos. En el ejemplo de código
siguiente se muestra cómo declarar un  objeto Timer:
// C# System.Windows.Forms.Timer t;

4.  En el diseñador, ver las propiedades del formulario. A continuación, ver la lista de eventos.
Haga doble clic en el  evento Load para crear automáticamente un controlador de eventos que
se ejecutan la primera vez que el formulario es inicializado. Dentro del método, inicializar
el  objeto de temporizador, establezca el intervalo de un segundo, crear un controlador de
eventos para el  evento Tick, e iniciar el temporizador. En el ejemplo de código siguiente se
muestra esto:

// C#
Private void Form1_Load(object sender, EventArgs e)
{
T = new System.Windows.Forms.Timer();
T.Intervalo = 1000;
T.Tick += new EventHandler(t_Tick);
T.Start();
}

5.  Aplicar el método que responda al temporizador. evento Tick. Cuando se produce el


evento, agregar 10 al ProgressBar. atributo value. A continuación, detenga el temporizador si
el componente ProgressBar. atributo Value ha llegado a 100. El código de ejemplo siguiente
demonio demuestra esto:

// C#
Void t_Tick(object sender, EventArgs e)
{
ProgressBar.Value += 10;

Si ProgressBar.valor >= 100)


T.stop();
}

6.  Ejecutar la aplicación para comprobar que responde al evento timer cada segundo.

Resumen de la lección
■   Utilizar la herencia para crear nuevos tipos sobre la base de las ya existentes.
■   Utilizar interfaces para definir un conjunto común de miembros que deben ser aplicadas
por los tipos relacionados.
■   clases parciales dividir una definición de clase en varios archivos de código fuente.
■   Eventos le permiten ejecutar un método especificado cuando algo ocurre en una dife-
rentes sección de código.
■   utilizar atributos para describir ensamblados, tipos y miembros.
■   Utilice el   atributo TypeForwardedTo  para pasar un tipo de clase de una biblioteca a otra.
Examen de la lección
Puede utilizar las siguientes preguntas para poner a prueba su conocimiento de la información
en la lección 3, "Construyendo clases." Las preguntas también están disponibles en el CD
complementario si prefiere revisarlos en forma electrónica.

Nota   Respuestas
Las respuestas a estas preguntas y explicaciones de por qué cada opción de
respuesta es correcta o no se encuentran en la sección "Respuestas" al final
del libro.

1.  Cuál de las siguientes afirmaciones es verdadera? (Seleccione todos los que correspondan).
A.  Define un contrato de herencia entre tipos.
B.   Interfaces definen un contrato entre tipos.
C.   La herencia se deriva de un tipo de un tipo base.
D.   Interfaces derivar un tipo de un tipo base.
2.  Cuales de los siguientes son ejemplos de tipos genéricos incorporada? (Seleccione todos
los que correspondan).
A.  Nullable
B.   Valor booleano
C.   EventHandler
D.   System.Drawing.El punto
3.  Se está creando una clase genérica y necesita deshacerse de los objetos genéricos.
¿Cómo se puede hacer esto?
A.  Llame al objeto. método Dispose.
B.   Implementar la  interfaz IDisposable.
C.   Derive de la clase genérica de la  clase IDisposable.
D.   Utilizar restricciones para exigir el tipo genérico para implementar
la IDisposable
Interfaz.
4.  Se ha implementado un delegado de eventos de una clase, pero cuando intenta fijar un
procedimiento de evento obtendrá un error de compilador que no hay sobrecarga que coincida
con el delegado. ¿Qué ha sucedido?
A.  La firma del procedimiento de evento no coincide con la definida por el
delegado.
B.   El procedimiento de evento es declarado compartida/estática, pero debe ser
un miembro de la instancia.
C.   Escribió incorrectamente el nombre de procedimiento de evento cuando se
conecta al delegado.
D.   La clase fue creada en un idioma diferente.

Lección 4: Conversión entre tipos


A menudo, usted necesita para convertir entre dos tipos diferentes. Por ejemplo, usted puede
ser que necesite para determinar si un entero es mayor o menor que un doble. Usted puede ser
que necesite para pasar un doble a un método que requiere un entero como un parámetro. O
puede que necesite mostrar un número como texto.
Esta lección describe cómo convertir entre tipos en Visual Basic y C#. Conversión de tipos es
una de las pocas esferas en Visual Basic y C# difieren considerablemente.

Después de esta lección, será capaz de:


■   convertir entre tipos.
■   Explicar boxeo y la razón por la que debe ser evitada.
■   aplicar los operadores de conversión.
Lección Tiempo estimado: 20 minutos

La conversión en Visual Basic y C#.


De forma predeterminada, Visual Basic permite conversiones implícitas entre tipos, mientras
que C# prohíbe las conversiones implícitas que pierda precisión. Para desactivar las
conversiones implícitas en Visual Basic, agregue Option Strict On al comienzo de cada
archivo de código, o (en Visual Studio) seleccione Proyecto, elija Propiedades, seleccione
Compilar y seleccionar Option Strict On para todo el proyecto.
Visual Basic y C# permite la conversión implícita si el tipo de destino puede aloja- modate
todos los valores posibles del tipo de origen. Eso se llama una conversión de ampliación, y es
ilustrada por el siguiente ejemplo:

// C#
Int i =
1;
Double d = 1.0001;
D = i; // Conversión permitida.

Si el intervalo o la precisión del tipo de fuente supera al del tipo de destino, la operación se


denomina conversión de restricción, que normalmente requiere una conversión explícita. La
tabla 1-7 muestra las maneras de realizar conversiones explícitas.
Tabla 1-7  métodos para la conversión explícita

El tipo de sistema Visual Basic C# Convierte


System.Convert Entre los tipos que imple-
El ment System.IConvertible
Interfaz.
CType (Tipo) Entre los tipos que definen
emitidos
Operador Los operadores de conversión.
Escriba.ToString, Entre la cadena y la base
Escriba.Parse Tipos; lanza una excepción si
el
No es posible la conversión.
Escriba.TryParse, Desde la cadena a un tipo base.
Escriba.TryParseEx Devuelve false si la conver-
act Sion no es posible.
CBool, CInt, Entre la base Visual Basic
CStr, etc. Tipos; compilado para inline
Mejor rendimiento. (Visual
Sólo básico).
DirectCast, Entre los tipos. DirectCast
TryCast Produce una excepción si el
Los tipos no están relacionados
aLatravés de o si no
herencia
Comparten una interfaz común.
TryCast devuelve  Nothing en
Esas situaciones. (Visual
Sólo básico).

Nota   .NET 2.0


TryParse, TryParseExact, y TryCast  son nuevos en .NET 2.0. Anteriormente, había que
intentar un análisis  o
Conversión  y luego coger la excepción si ha
fallado.

Las conversiones de restricción puede devolver un resultado incorrecto si el valor de origen


supera el rango del tipo de destino. Si una conversión entre tipos no está definida, usted
recibirá un error en tiempo de compilación.

Qué es el Boxing y Unboxing?


El boxeo se convierte un tipo de valor a un tipo de referencia, y unboxing convierte un tipo de
referencia a un tipo de valor. El siguiente ejemplo demuestra el boxeo por la conversión de
un entero (un tipo de valor) a un objeto (un tipo de referencia):

// C#
Int i = 123;
O = object (Objeto) i;

Unboxing ocurre si asigna a un objeto de referencia a un tipo de valor. El siguiente ejem- plo
demuestra unboxing:

// C#
Objeto o = 123;
Int i = (int) o;

Las mejores prácticas  boxing y unboxing


Boxing y Unboxing incurrir en gastos generales, por lo que se debe evitar cuando
intensamente la programación de tareas repetitivas. El boxeo también se produce cuando se
llama a los métodos virtuales que una estructura hereda de System.Object, como ToString.
Siga estos consejos para evitar la innecesaria boxeo:
■   implementar versiones específicas de cada tipo (sobrecargas) para procedimientos que
aceptan varios tipos de valor.

Es mejor crear varios procedimientos sobrecargados de uno que acepta un objeto Argumento.
■   Utilizar genéricos siempre que sea posible en lugar de aceptar los   argumentos del objeto.
■   El override ToString, Igual a, y GetHash  miembros virtuales al definir estructuras.

Cómo implementar la conversión de tipos personalizados.


Puede definir conversiones para sus propios tipos de varias maneras. La técnica que usted elija
dependerá del tipo de conversión que desea realizar:
■   Definir los operadores de conversión para simplificar el estrechamiento y ensanchamiento de
las conversiones entre tipos numéricos.
■   override ToString para proporcionar la conversión a cadenas y override Parse para
proporcionar la conversión de cadenas.
■   implementar System.IConvertible para activar la conversión a través de System.Convert.
Utilice esta técnica para habilitar la cultura específica de las conversiones.
■   Implementar una  clase TypeConverter para habilitar la conversión en tiempo de diseño para
utilizar en la ventana Propiedades de Visual Studio. Conversión en tiempo de diseño está fuera del
alcance del examen y la  clase TypeConverter no está cubierto en este libro.

Más info La  conversión en tiempo de diseño.


Para obtener más información acerca de la conversión en tiempo de diseño, lea "Ampliar
compatibilidad en tiempo de diseño" en http://msdn2.microsoft.com/en-
us/library/37899azC(en-us,VS.80).aspx.

Nota   .NET 2.0

Los operadores de conversión son nuevos en .NET 2.0.

Definir los operadores de conversión le permite asignar directamente desde un tipo de valor a su
tipo personalizado. Utilice la ampliación/ Palabra clave implícito para las conversiones que no
pierda preci- sión; utilice el estrechamiento/ Palabra clave para las conversiones explícitas que
podrían perder precisión. Por ejemplo, la siguiente estructura define que permiten a los operadores
de asignación y de valores enteros:
// C#
Struct TypeA
{
El valor int pública;

// Permite la conversión implícita de un entero. public static operador


implícito TypeA(int arg)
{
TypeA res = new TypeA();
Res.Value = arg;
Volver res;
}

// Permite la conversión explícita a un entero public static


int(operador explícito TypeA arg)
{
Volver arg.Value;
}

// Proporciona conversión de cadenas (evita el boxeo). public


override string ToString()
{
Volver this.Value.ToString();
}
}

El tipo anterior también invalida ToString para realizar la conversión a cadena sin boxeo. Ahora
puede asignar números enteros al tipo directamente, como se muestra aquí:

// C#
TypeA a; int i;
// Conversión de ampliación está bien implícito. a =
42; // en lugar de a.Value = 42
// Conversión de restricción debe ser explícita. i = (int); // en
lugar de i = valor.
Console.WriteLine("a = {0}, i = {0}",.ToString(), i.ToString());

Para implementar  la  interfaz System.IConvertible, agregue la  interfaz IConvertible a la


definición de tipo. A continuación, utilice Visual Studio para implementar la interfaz
automáticamente. Visual Studio inserta las declaraciones de miembro para 17 métodos, incluyendo
GetTypeCode, ChangeType, y escribir métodos para cada tipo de base. Usted no tiene que aplicar
cada método, y algunos, como ToDateTime-probablemente será inválida. No válido para

Métodos simplemente arrojar una excepción-Visual Studio agrega automáticamente código para
lanzar una excepción para cualquier métodos de conversión que no se implementen.
Después de implementar  IConvertible, el tipo personalizado puede ser convertido con la apre-
ciación de la  clase System.Convert como se muestra aquí:

// C#
TypeA a; b; bool
A = 42;
// Convertir utilizando ToBoolean. b =
Convert.ToBoolean(a);
Console.WriteLine("a = {0}, b = {1}",.ToString(), b.ToString());

Laboratorio: realizar
conversiones de forma segura
Los siguientes ejercicios muestran cómo evitar problemas con las conversiones implícitas para que
los programas funcionen de manera predecible. Si usted encuentra un problema completando una
exer- cise, los proyectos terminados están disponibles en el CD en la carpeta de código.
Ejercicio 1: Examinar la Conversión implícita

En este ejercicio, examinará la conversión para determinar qué número de tipos permiten la
conversión implícita.
1.  Crear una nueva aplicación de consola en Visual Studio.
2.  Declarar instancias de tres tipos de valores: Int16, Int32, y  doble. En el ejemplo de código
siguiente se muestra esto:
// C#
Int16 i16 = 1; Int32
i32 = 1; doble db =
1;

3.  Intento de asignar a cada variable para todos los demás, como demuestra el ejemplo de código
siguiente.
// C#
I16 = i32.
I16 = db;

I32 = i16;
I32 = db;

Db = i16.
Db = i32.
4.  Intento de construir su proyecto. Que las conversiones implícitas no permitir que el
compilador, y por qué?
Ejercicio 2: Activar la opción Estricta (sólo Visual Basic)

En este ejercicio, que es sólo para los desarrolladores que utilizan Visual Basic, podrá modificar
las opciones del compilador y, a continuación, reconstruir el proyecto creado en el ejercicio 1.

1.  En Visual Studio, abra el proyecto creado en el ejercicio 1.


2.  Haga clic en el menú Proyecto y, a continuación, haga clic en  Propiedades
denombredesolución.
3.  Haga clic en la ficha Compilar. Para la conversión implícita, cambiar el  tipo de notificación
para Error.
4.  Intento de construir su proyecto. Que las conversiones implícitas no permitir que el
compilador, y por qué?

Resumen de la lección
■   El .NET Framework se puede convertir automáticamente entre tipos integrados. Ampliando las
conversiones ocurren implícitamente en Visual Basic y C#. Estrechamiento conver- siones
requieren conversión explícita en C#, mientras que Visual Basic permite conversiones de
restricción por defecto.
■ El   boxeo permite cualquier tipo a ser tratada como un tipo de referencia.
■   Debe aplicar específicamente los operadores de conversión para activar la conversión de tipos
personalizados.

Examen de la lección
Puede utilizar las siguientes preguntas para poner a prueba tus conocimientos de la información
Les- hijo 4, "Conversión entre tipos." Las preguntas también están disponibles en el CD de iones
complementaria si prefiere revisarlos en forma electrónica.

Nota   Respuestas
Las respuestas a estas preguntas y explicaciones de por qué cada opción de
respuesta es correcta o no se encuentran en la sección "Respuestas" al final del
libro.

1.  ¿Por qué debería evitarse el boxeo? (Elija uno).


A.  Añade sobrecarga.
B.   Los usuarios deben tener privilegios administrativos para ejecutar la aplicación.
C.   Hace código menos legible.
2.  Las estructuras heredan  ToString de System.Object. ¿Por qué haría alguien anular ese método
dentro de una estructura? (Seleccionar tantas respuestas correctas como aplicar).
A.  Para evitar el boxeo.
B.   Devolver algo distinto del nombre de tipo.
C.   El compilador requiere de estructuras para reemplazar el  método ToString.
D.   Para evitar errores en tiempo de ejecución causado por las conversiones de
cadena no válido.
3.  Si no hay conversión válida entre dos tipos, lo que debe hacer cuando se implementa
la  interfaz IConvertible?
A.  Eliminar el  miembro ToType que realiza la conversión.
B.   Lanzar una excepción InvalidCastException.
C.   Lanzar una nueva excepción personalizada informando del error.
D.   Dejar el organismo miembro vacía.
4.  Con conversiones estricto activado, cual de las siguientes opciones permiten una conversión
implícita? (Seleccione todos los que correspondan).
A.  Int16 a INT32
B.   Int32 a INT16
C.   Int16 a doble
D.   Doble a Int16

Repaso del cap tulo


A nuevas prácticas y reforzar las habilidades aprendidas en este capítulo, puede per- en las
siguientes tareas:
■   Revisar el resumen del capítulo.
■   Revisar la lista de términos clave que se introdujo en este capítulo.
■   Complete los casos. Estos escenarios configurar situaciones del mundo real
involv- ción de los temas de este capítulo, y le pedimos a usted para crear una
solución.
■   completar las prácticas sugeridas.
■   tomar un examen de práctica.

Resumen del capítulo


■   Los tipos de valor son pequeñas, las variables que almacenan los datos directamente, en lugar
de almacenar un puntero a un segundo memor y ubicación que contiene los datos. Asignación
entre los tipos de valor, copia los datos de una variable a otra, la creación de una instancia
independiente de los datos. Puede hacer que los tipos de valor nullable utilizando el  Nul- dere tipo
genérico, y puede crear estructuras que combinan varios tipos de valor.
■ Los   tipos de referencia contienen la dirección de datos en lugar de los datos reales. El .NET
Framework incluye miles de tipos de referencia para realizar prácticamente cualquier tarea que
pudiera requerir. El tipo de referencia más comúnmente utilizado es la  clase String. Dado que
la  clase String es inmutable, se comporta de forma diferente a otros tipos de referencia. Cuando
copia la mayoría de las clases de referencia, sólo el puntero es copiado, lo que significa que los
cambios realizados en una instancia también se refleja en la otra instancia. Cuando se produce un
evento inesperado, el .NET Framework genera una excepción. Puede controlar estas excepciones
creando  bloques Try/Catch en el código.
■   Clases en lenguajes de .NET Framework son tipos personalizados que pueden incluir los tipos
de valor, los tipos de referencia, Métodos, atributos y propiedades. Para habilitar la consis- tency
entre clases, se puede utilizar la herencia (donde puede derivar una clase nueva a partir de una
clase existente) o una interfaz (donde se necesita para implementar interfaces especificadas). Los
genéricos permiten crear una clase o método que trabaja con una variedad de tipos. Para habilitar
aplicaciones que respondan a eventos planificados, puede plantear y responder a los eventos.

■ La   conversión le permite comparar y copiar los valores entre dos tipos diferentes. La
conversión implícita ocurre automáticamente y se comporta de manera diferente en Visual Basic y
C#. C# permite la conversión implícita sólo para las conversiones de restricción, donde no hay
información podría perderse. Visual Basic permite la conversión implícita tanto para el
estrechamiento y conversiones de ampliación. Cuando los valores se convierten  en un tipo de
valor a un tipo de referencia, se considera el boxeo. Unboxing ocurre si asigna a un objeto de
referencia a un tipo de valor.

Términos clave
¿ Sabes lo que significan estos términos clave? Puede verificar sus respuestas mediante la
búsqueda de los términos en el glosario al final del libro.
■ El   boxeo

■   emitidos

■   restricción

■   contrato

■   excepción

■   excepciones de filtrado
■   La recolección de basura
■   tipo genérico
■   heap

■   interface

■   estrechamiento

■   tipo Nullable
■ La   firma

■   stack

■   estructura

■   unboxing

■   ampliación

Escenario de caso
En el caso siguiente escenario, tendrá que aplicar lo que ha aprendido acerca de los tipos. Usted
puede encontrar las respuestas a estas preguntas en la sección de "respuestas" al final de este libro.
Caso práctico: diseño de una aplicación
 Recientemente usted ha aceptado un trabajo como desarrollador de aplicaciones internas en el
departamento de tecnología de la infor- mación de una empresa empresa del sector sanitario. Su
primera tarea consiste en diseñar una aplicación interna que los empleados vayan a utilizar para
gestionar la información sobre clientes (a quien todo el mundo llama "abonados"), sus planes
actuales, medicamentos y médicos elegidos. Contestar las preguntas de su jefe acerca de sus
opciones de diseño.
1.  Necesitamos gestionar información sobre ambos abonados y médicos. ¿Cómo se puede hacer
esto? Tendrá usted una clase para ambos, dos clases separadas, o qué?
2.  Nuestros empleados necesitan para buscar grupos de abonados o médicos. Por ejemplo, si un
médico se jubila, necesitamos contactar con todos los abonados de ese doctor y ayudarlos a
encontrar un nuevo médico. Asimismo, estamos en contacto con los médicos anualmente para
renovar sus contratos. ¿Cómo se puede almacenar un grupo de suscriptores o médicos en su
aplicación?
3.  Una de las tareas que su aplicación va a realizar es la generación de etiquetas de correo para
grupos de abonados o médicos. ¿Hay alguna manera de que usted puede escribir un único método
que controlará las direcciones para ambos abonados y los médicos? Cómo se va a implementar
esto?
4.  La confidencialidad de nuestra información es extremadamente importante para nosotros.
Nuestra base de datos devel- oper va a restringir los permisos en la base de datos para evitar que
usuarios no autorizados obtengan acceso. Si los privilegios de un usuario son rechazadas, me
gustaría que indique al usuario que se ponga en contacto con su administrador para obtener acceso.
Cómo va a manejar si una consulta de base de datos es rechazado por falta de privilegios?

Prácticas recomendadas
Que le ayudarán a dominar los objetivos del examen se presenta en este capítulo, se com- pleta las
siguientes tareas.
Administrar datos en una aplicación de .NET
Framework usando .NET Framework 2.0 Tipos de
sistema
Para esta tarea, se deben completar un mínimo de prácticas 1 y 2. Si desea una mejor comprensión
de cómo los genéricos realizar en el mundo real, la práctica completa 3.
■   prácticas 1  Abra el último proyecto que ha creado, y añadir excepciones a su código. A
menos que el rendimiento es una prioridad mayor que la fiabilidad, todo el código fuera- lado de
declaraciones de tipo de valor debe estar en un  bloque Try.

■ La   Práctica 2  Crear una lista vinculada clase genérica que permite crear una cadena de
diferentes tipos de objetos.
■ La   Práctica 3  crear dos clases con funciones idénticas. Use genéricos para la primera clase,
y el reparto de la segunda clase para  los tipos de objeto. Crear un  bucle que utiliza la clase a lo
largo de miles de iteraciones. Momento de la actuación de ambos el genérico
La clase y la clase base Object -para determinar cuál funciona mejor. Puede utilizer
DateTime.Now.garrapatas para medir el tiempo.

Implementar interfaces de .NET Framework para causar


componentes a Cumplir con los contratos estándar
Para esta tarea, se deben completar las tres prácticas para adquirir experiencia de implementar
interfaces comunes con clases del mundo real y esquema.
■ La   Práctica 1  Crear una clase personalizada que implementa las interfaces necesarias para
permitir una variedad de la clase que se va a ordenar.
■ La   Práctica 2  Crear una clase personalizada que puede ser convertido a tipos de valor
común.
■ La   Práctica 3  Crear una clase personalizada que puede eliminarse mediante el IDisposable
. Método Dispose.

Controlar las interacciones entre la aplicación de .NET


Framework Componentes mediante eventos y delegados
Para esta tarea, debe completar ambas prácticas 1 y 2.
■ La   Práctica 1  Abra la última aplicación de Windows Forms que creó, y examinar el código
Visual Studio genera automáticamente para responder a eventos de la interfaz de usuario.
■ La   Práctica 2  Crear una clase que genera un evento y deriva una clase personalizada basada
en EventArgs. A continuación, crear un conjunto que responda al evento.

Tomar un Test de práctica


Los tests de práctica en este libro's Companion CD ofrecen muchas opciones. Por ejemplo, puede
hacerse la prueba en un solo examen objetivo, o puede probar usted mismo en todos los contenido
del examen de certificación 70-536. Puede configurar la prueba para que simula cuidadosamente 66
Capítulo 1 Comentario
La experiencia de tomar un examen de certificación, o puede configurarlo en modo de estudio, de
modo que usted puede mirar las respuestas correctas y explicaciones después de responder a cada
pregunta.

Capítulo 2
Entrada/Salida (I/O)
En este capítulo, aprenderá a trabajar con la entrada/salida (E/S) en el Microsoft .NET Framework.
Los elementos básicos del sistema de E/S incluyen el acceso a archivos y carpetas en el sistema de
archivos, trabajar con secuencias de lectura y escritura, utilizando compresión de arroyos, y usar el
almacenamiento aislado.

Objetivos del examen en este


capítulo:
■   Acceso de archivos y carpetas utilizando el sistema de archivos de clases. (Consulte la
sección System.IO
Espacio de nombres)
❑ El   archivo class y la  clase FileInfo
❑     clase Directory y DirectoryInfo class
❑   DriveInfo  clase y  enumeración DriveType
❑   FileSystemInfo clase y  clase FileSystemWatcher
❑    clase Path
❑   ErrorEventArgs  clase y  delegado ErrorEventHandler
❑   RenamedEventArgs clase y  delegado RenamedEventHandler
■   Gestionar secuencias de bytes utilizando  clases Stream. (Consulte el  espacio de nombres
System.IO)
❑     clase FileStream
❑    clase Stream (no  lector y  escritor clases porque son un objetivo separado)
❑    clase MemoryStream
❑    clase BufferedStream
■   Gestionar la aplicación de .NET Framework datos mediante lector y escritor
Las clases. (Consulte el  espacio de nombres System.IO)
❑   StringReader clase y  clase StringWriter
❑   TextReader clase y  clase TextWriter
❑    clase StreamReader y  clase StreamWriter
❑    clase  clase BinaryReader y BinaryWriter
■   comprimir o descomprimir transmitir información en una aplicación de .NET
Framework (consulte  namespace System.IO.Compression)  y mejorar la
seguridad de aplica- ción de datos mediante el almacenamiento aislado.
(Consulte el  espacio de nombres System.IO.IsolatedStorage)
❑     clase IsolatedStorageFile
❑   IsolatedStorageFileStream class

❑    clase DeflateStream
❑   GZipStream class

Las lecciones de este capítulo:


■ La   Lección 1: Explorar el sistema de archivos . . .. . . . . . . . . . . . . . . . . . . . . . .
69
■ La   Lección 2: Lectura y escritura de archivos . . . . . .. . . . . . . . . . . . . . . . . . .
88
■ La   Lección 3: Comprimir secuencias . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . 114
■   Lección 4: Trabajar con el almacenamiento aislado . . . . .. . . . . . . . . . . 125

Antes de empezar
Para completar las lecciones de este capítulo, debe estar familiarizado con Microsoft Visual

Basic o en C# y se sienten cómodos con las siguientes tareas:


■   Crear una aplicación de consola en Microsoft Visual Studio, usando Visual Basic o C#.
■   Sistema de agregar referencias a bibliotecas de clases para un proyecto.
■   Crear archivos de texto.

Mundo Real
Shawn Wildermuth
He escrito cientos, si no miles de líneas de código y siempre vuelvo al sistema
de E/S para mucho . La sección de E/S de .NET Framework proporciona
ayuda para realizar una multitud de tareas. Mucho de lo que hay en el sistema
de E/S es la base para otras partes del Marco, así como para los productos de
terceros. Aprender los conceptos básicos de cómo  funcionan los archivos y
arroyos ha hecho haciendo mi trabajo mucho más fácil.
Lección 1: Explorar el sistema de archivos
En el trabajo diario de los desarrolladores, una de las tareas más comunes es la de trabajar con el
sistema de archivos. Esta tarea incluye la navegación y recopilar información acerca de las
unidades, carpetas y archivos, así como esperando cambios que ocurren en el sistema de archivos.

Después de esta lección, será capaz de:


■   Escribir código que utiliza el archivo  y   clases FileInfo.
■   Escribir código que utilice las      clases Directory y DirectoryInfo.
■   Utilice el DriveInfo   DriveType y  clases.
■   Enumerar los archivos, directorios y unidades utilizando el FileSystemInfo  clases
derivadas.
■   Utilice la   clase Path para manipular las rutas del sistema de archivos.
■   observar cambios en el sistema de archivos utilizando la   clase FileSystemWatcher.
Lección Tiempo estimado: 20 minutos

¿Cuáles son las clases de sistema de archivo?


Dentro del  espacio de nombres System.IO  son un conjunto de clases que se utiliza para
desplazarse y manipular archivos, directorios y unidades. El sistema de archivos, las clases se
dividen en dos tipos de clases: Informativo y utilidad.
La mayoría de los informativos FileSystemInfo clases se derivan de la clase base. Estas clases
exponen toda la información del sistema acerca de los objetos del sistema de archivos,
específicamente, archivos, directorios y unidades. Estas clases se
denominan FileInfo y DirectoryInfo.
Además, la  clase DriveInfo representa una unidad en el sistema de archivos, pero aunque todavía
es un informativo de clase, que no se derivan de la  clase FileSystemInfo porque no comparten el
mismo tipo de comportamiento (por ejemplo, usted puede eliminar archivos y carpetas, pero no las
unidades).
Las clases de utilidad proporcionar métodos estáticos (o compartidos en Visual Basic) para realizar
determinadas operaciones sobre los objetos del sistema de archivos, como archivos, directorios y
rutas de acceso del sistema de archivos. Estas clases de utilidad incluir el archivo, Directorio, y
la ruta de clases.

La   clase FileSystemInfo

La  clase FileSystemInfo proporciona la funcionalidad básica para todos archivo informativo Sys-
tem clases. La tabla 2-1 muestra las  propiedades FileSystemInfo más importantes.
Tabla 2-1  FileSystemInfo  Propiedades

Nombre Descripción
Obtiene o establece FileAttributes atributos del archivo o directorio actual.
CreationTime  obtiene o establece el tiempo que el archivo o
directorio actual fue creado.
   Determina si existe el archivo o directorio.
Extensión  obtiene una representación de cadena de la extensión del
archivo o directorio.
FullName  obtiene la ruta de acceso completa del archivo o directorio.
LastAccessTime obtiene o establece el momento en que el fichero o directorio
fue visitada. LastWriteTime  obtiene o establece el momento en que el
fichero o directorio fue el último escrito. Name  Obtiene el
nombre simple para el archivo o directorio. Para un archivo, este
Es el nombre del directorio. Para un directorio, este es el
Último nombre de directorio en la jerarquía de directorios.

La tabla 2-2 muestra los  métodos FileSystemInfo más importantes.


Tabla 2-2  FileSystemInfo  métodos

Nombre Descripción
Eliminar:   elimina el archivo o directorio del sistema de archivos
Actualizar  Actualiza los datos en la clase con la informa- ción
más actual desde el sistema de archivos

La   clase FileInfo


La  clase FileInfo proporciona la funcionalidad básica para el acceso y la manipulación de un
solo archivo en el sistema de archivos.
La tabla 2-3 muestra las  propiedades FileInfo más importantes.
Tabla 2-3    propiedades FileInfo

Nombre Descripción
Directory  obtiene el  objeto DirectoryInfo que representa el directorio que
este archivo se almacena dentro de
Nombredirectorio  Obtiene el nombre del directorio que este archivo se almacena dentro de
IsReadOnly  obtiene o establece el indicador que determina si el archivo
puede ser modificado o eliminado
Length   obtiene la longitud del archivo

La tabla 2-4 muestra los  métodos FileInfo más importantes.


Tabla 2-4    métodos FileInfo

Nombre Descripción
AppendText  crea un nuevo StreamWriter que permitirá Anexar al archivo de
texto. Consulte la lección 2 para obtener más información.
CopyTo  hace una copia del archivo en una nueva ubicación.
Crear  crea un archivo basado en la información del archivo actual.
   Crea un nuevo CreateText StreamWriter y un nuevo archivo para escribir el texto.
Ver Lección 2 para obtener más información.
Descifrar  descifra un archivo cifrado por el usuario actual.
Cifrar  cifra un archivo, de modo que sólo el usuario actual puede descifrar
la información dentro del archivo.
MoveTo  mueve el archivo a una nueva ubicación.
Abrir  abre el archivo con privilegios específicos (lectura, lectura y escritura, y
así sucesivamente).
OpenRead  abre el archivo con acceso de sólo lectura.
OpenText  abre el archivo y devuelve un StreamReader para permitir la lectura
de texto dentro del archivo.
OpenWrite  abre el archivo con acceso de sólo escritura.
Replace   reemplaza un archivo con el que se describe en la FileInfo actual
Objeto.
Cómo obtener información acerca de un archivo
Para obtener información acerca de un archivo específico, siga este procedimiento:
■   Crear un nuevo  objeto FileInfo utilizando la ruta de acceso al archivo.
■   acceder a las  propiedades del objeto FileInfo.
Por ejemplo, puede comprobar si un archivo existe llamando al  objeto FileInfo existe
La propiedad, tal como se muestra en el siguiente código:
// C#
OurFile = new FileInfo FileInfo(@"c:\boot.ini ");

Si (ourFile.Existe).
{
Console.WriteLine("Filename : {0}", ourFile.Nombre); Console.WriteLine("ruta
: {0}", ourFile.FullName);
}

Utilizando el  objeto FileInfo de esta forma permite que el código para interrogar la información
acerca de un archivo en el sistema de archivos.

Cómo copiar un archivo


Además de tener acceso a los datos sobre un archivo, el  objeto FileInfo permite operaciones a
realizar en el archivo. De nuevo, una vez que un  objeto FileInfo válida se obtiene, todo lo que
tienes que hacer es llamar al  método CopyTo para realizar una copia de su archivo, como muestra
el ejemplo de código siguiente:

// C#
OurFile = new FileInfo FileInfo(@"c:\boot.ini");

OurFile.CopyTo(@"c:\boot.bak");

El mismo procedimiento se utiliza para mover y crear archivos. Una vez que tenga una validez
 Objeto FileInfo, puede acceder a todas sus propiedades y llamar a sus métodos.

La   clase DirectoryInfo


La  clase DirectoryInfo proporciona la funcionalidad básica para acceder y manipular un pecado-
gle directorio en el sistema de archivos. La tabla 2-5 muestra las más importantes  propiedades
DirectoryInfo.

Tabla 2-5  DirectoryInfo  Propiedades

Nombre Descripción
   Obtiene el principal  objeto DirectoryInfo al directorio padre del
directorio actual en la jerarquía del directorio
   Obtiene la raíz raíz parte de la ruta del directorio como una cadena

La tabla 2-6 muestra los más importantes  métodos DirectoryInfo.


Tabla 2-6  DirectoryInfo  métodos

Nombre Descripción
Crear  crea el directorio descrito en el actual DirectoryInfo Objeto
CreateSubdirectory  crea un nuevo directorio como un niño directorio del
directorio actual en la jerarquía del directorio
GetDirectories   DirectoryInfo recupera una matriz de objetos que
representan los subdirectorios del directorio actual.
GetFiles Reintentoves una matriz de  objetos FileInfo que representa
todos los archivos en el directorio actual.
GetFileSystemInfos   FileSystemInfo recupera una matriz de objetos que
representan los archivos y subdirectorios del directorio
actual
MoveTo  mueve el directorio actual a una nueva ubicación

Cómo enumerar los archivos de un directorio


Para acceder a los archivos en un directorio es muy similar a tener acceso a información de
archivo. Los siguientes pasos muestran cómo enumerar los archivos en un directorio:
1.  Crear un  objeto DirectoryInfo válido utilizando la ruta de acceso al directorio.
2.  Llamar al  método GetFiles para enumerar los archivos en el directorio. En el
ejemplo de código siguiente se muestra cómo llevar a cabo esta tarea:

// C#
DirectoryInfo ourDir = new DirectoryInfo(@"c:\windows");

Console.WriteLine("Directorio: {0}", ourDir.FullName);

(Foreach en archivo ourDir FileInfo.GetFiles())


{
Console.WriteLine("file: {0}", archivo.Nombre);
}

Utilizando el  método GetFiles del  objeto DirectoryInfo, eres capaz de enumerar  los archivos


dentro de un directorio.
La   clase DriveInfo

Los  modelos de clase DriveInfo una unidad y proporciona métodos y propiedades para consultar
la información de la unidad. Utilice DriveInfo para determinar qué unidades están disponibles y
qué tipo de unidades son. También puede realizar consultas para determinar la capacidad y el
espacio libre disponible en la unidad. La tabla 2-7 muestra las  propiedades DriveInfo más
importantes.
Tabla 2-7  DriveInfo  Propiedades

Nombre Descripción
AvailableFreeSpace  obtiene la cantidad de espacio disponible en la unidad. La
cantidad puede ser diferente de la cantidad devuelta
por TotalFreeSpace (descrita más adelante en esta tabla), en
función de las cuotas de disco.
DriveFormat  obtiene el formato de la unidad, como NTFS o FAT32.

Tabla 2-7  DriveInfo  Propiedades

Nombre Descripción
DriveType  Obtiene el tipo de unidad en la forma de la DriveType enumer-
ción (que se describe en la siguiente sección).
IsReady Obtiene el estado de la unidad, indicando si está preparada
para ser visitada.
Name  Obtiene el nombre de la unidad.
RootDirectory  obtiene un  objeto DirectoryInfo que representa el directorio
raíz de la unidad.
TotalFreeSpace  obtiene la cantidad total de espacio libre en el disco.
TotalSize  obtiene el tamaño total de la unidad.
VolumeLabel  obtiene o establece la etiqueta de la unidad. Podría establecerse
sólo en unidades que no sean de sólo lectura.

La tabla 2-8 muestra el  método DriveInfo más importantes.


Tabla 2-8  DriveInfo  método

Nombre Descripción
GetDrives  un método estático (o compartido en Visual Basic) que
devuelve todas las unidades en el sistema actual.
La   enumeración DriveType
La  enumeración DriveType proporciona los posibles tipos de unidades que pueden ser
representados por un  objeto DriveInfo. La tabla 2-9 muestra los miembros de la  enumeración
DriveType.
Tabla 2-9  DriveType   miembros

Nombre Descripción
   Una unidad óptica de CD-ROM. Puede ser un CD-ROM, DVD, y así
sucesivamente.
Arreglado   un disco fijo.
Red  una unidad asignada de red.

Tabla 2-9  DriveType   miembros

Nombre Descripción
NoRootDirectory  una unidad que no tiene un directorio raíz.
Ram   una unidad R AM.
   Una unidad extraíble que tiene medios extraíbles.
Desconocido  El tipo de unidad no pudo ser determinada.

Cómo enumerar las unidades


Siga este procedimiento para enumerar todos los discos de un sistema:
1.  Llamar al  método GetDrives estático (o compartido en Visual Basic) de la DriveInfo Clase.
2.  Bucle a través de la matriz de  objetos devueltos por GetDrives DriveInfo. En el ejemplo de
código siguiente se ilustra este proceso:

// C#
DriveInfo[] unidades = DriveInfo.GetDrives();

(Foreach DriveInfo duro en unidades).


{
Console.WriteLine("Unidad: {0}", unidad.Nombre);
Console.WriteLine("Tipo: {0}", unidad.DriveType);
}

Como puede ver, el  método GetDrives del  objeto DriveInfo simplifica enormemente el


pro- ceso de enumerar las unidades en un sistema.
Nota   unidades ópticas

Todos los tipos de unidades ópticas (CD, CD/R, DVD, DVD/R, etcétera) están
marcados como DriveInfo.CDRom.

La   clase Path


La  clase Path proporciona métodos para manipular una ruta del sistema de archivos. La
tabla 2-10 muestra el camino más importante de métodos.
Tabla 2-10     Métodos de ruta estática

Nombre Descripción
   Toma una ruta ChangeExtension y devuelve una nueva ruta con la
extensión del nombre de archivo cambiado. (Tenga en
cuenta que sólo la cadena de la ruta de los cambios, no la
extensión de nombre de archivo).
Cosechadora  Combina dos cadenas de ruta de acceso
compatibles. GetDirectoryName  devuelve el nombre del directorio en la ruta
especificada. GetExtension   devuelve el nombre de la extensión del
archivo especificado Ruta.

GetFileName  devuelve el nombre del archivo en la ruta especificada.


Extensión - GetFileNameWithout Devuelve el nombre de archivo sin la extensión
en la ruta especificada.

GetFullPath  devuelve una ruta completa de acceso a la ruta


especificada. Este método puede ser utilizado para
resolver rutas relativas.
GetPathRoot devuelve el nombre del directorio raíz en la ruta especificada.
GetRandomFileName  genera un nombre de archivo aleatorio.
GetTempFileName  genera un archivo temporal en el sistema de archivos
y devuelve la ruta completa al archivo.
GetTempPath  devuelve la ruta del directorio de archivos
temporales para el usuario actual o el sistema.
HasExtension  indica si la ruta especificada tiene una extensión
de nombre de archivo.
IsPathRooted  indica si la ruta especificada incluye un directorio
raíz.

Cómo cambiar una extensión de archivo en una ruta


La  clase Path permite interrogar y analizar los componentes individuales de una ruta del
sistema de archivos. En lugar de escribir su propio código de análisis de cadena, la  clase Path
permite contestar a las preguntas más comunes que tendría sobre una ruta del sistema de
archivos. Por ejem- plo, si desea obtener y cambiar la extensión de un archivo, puede hacerlo
con la  clase Path, como se muestra en el siguiente fragmento de código:
// C#
Cadena ourPath = @"c:\boot.ini";
Console.WriteLine(ourPath);

Console.WriteLine("EXT: {0}", ruta.GetExtension(ourPath);

Console.WriteLine("Cambiar ruta: {0}".


Ruta.ChangeExtension(ourPath, "bak");

Utilizando el  método GetExtension de la  clase Path, puede obtener la extensión actual de


una ruta del sistema de archivos. Sin embargo, debido a que la meta original era cambiar la
ruta de acceso, se utiliza el  método ChangeExtension de la  clase Path.

La   clase FileSystemWatcher


La  clase FileSystemWatcher proporciona métodos para el monitoreo de los directorios del
sistema de archivos para los cambios. La tabla 2-11 muestra las más importantes  propiedades
FileSystemWatcher.

Tabla 2-11      propiedades


FileSystemWatcher
Nombre Descripción
EnableRaisingEvents  obtiene o establece si el objeto observador
debería generar eventos. Normalmente, se
utiliza para activar y desactivar la
visualización de un directorio y/o archivos.
Filtrar Obtiene o establece el filtro de archivo que se
usan para determinar qué archivo cambios en
el monitor. Un filtro en blanco indi- cates
"todos los archivos".

Tabla 2-11      propiedades FileSystemWatcher

Nombre Descripción
IncludeSubdirectories  obtiene o establece un indicador de si la
observación de un directorio es incluir
subdirectorios o sólo en el directorio especificado
en la  propiedad Path.
NotifyFilter  obtiene o establece el tipo de cambios que
observar. Por defecto, todos los cambios (crear,
suprimir, renombrar archivos y modificaciones)
son notificados.
Ruta  obtiene o establece la ruta de acceso al directorio en
el monitor.

La tabla 2-12 muestra el más importante  método FileSystemWatcher.


Tabla 2-12      Método FileSystemWatcher

Nombre Descripción
WaitForChanged  método sincrónico para ver un directorio para los
cambios y para devolver una estructura  que con-
mantiene todos los cambios

La tabla 2-13 muestra los más importantes  eventos FileSystemWatcher.


Tabla 2-13      eventos FileSystemWatcher

Nombre Descripción
   Se produce cuando cambia un archivo o directorio ha
cambiado en el directorio observados
   Se produce cuando se crea un archivo o directorio ha sido
creado en el directorio observados
Eliminado Se produce cuando un archivo o directorio ha sido
eliminado en el directorio observados
Renombrado Ohccurs cuando un archivo o directorio ha sido
cambiado en el directorio observados

Cómo controlar un directorio para cambios


Para supervisar un directorio para cambios, siga este procedimiento:
1.  Crear un nuevo   objeto FileSystemWatcher, especificando el directorio en la
ruta La propiedad.
2.  Registrarse para crear y borrar eventos.
3.  Activar eventos estableciendo EnableRaisingEvents en true. El siguiente
fragmento de código ilustra este proceso:
// C#
Watcher FileSystemWatcher = new FileSystemWatcher();
Watcher.Path = @"c:\".

// Registrarse para
eventos watcher.Creada
+=
Nueva FileSystemEventHandler(watcher_changed);
Watcher.Deleted +=
Nueva FileSystemEventHandler(watcher_changed);

// Inicio viendo
watcher.EnableRaisingEvents = true;

// Controlador de eventos
Static void watcher_cambiado(object sender,
FileSystemEventArgs e)
{
Console.WriteLine("directorio cambiado({0}): {1}", e.ChangeType,
E.FullPath);
}

El controlador de eventos informa simplemente cada cambio encontrado en el  objeto


FileSystemEventArgs que se envía al controlador de eventos.
Además de utilizar el Creado, Eliminado, y  eventos modificados, puede supervisar el sistema para
renombrar los archivos. Para supervisar un directorio para renombrar archivos, puede seguir este
procedimiento:
1.  Crear un nuevo   objeto FileSystemWatcher,  especificando  el directorio en la  ruta La
propiedad.
2.  Registrarse para cambiar el nombre del evento.
3.  Activar eventos estableciendo EnableRaisingEvents en true. El siguiente fragmento de
código ilustra este proceso:
// C#
Watcher FileSystemWatcher = new FileSystemWatcher();
Watcher.Path = @"c:\".

// Registrarse para eventos


watcher.renombrado +=
Nueva RenamedEventHandler(watcher_Nombre);

// Inicio viendo watcher.EnableRaisingEvents = true;


// Controlador de eventos
Static void watcher_renombrado(object sender,
RenamedEventArgs e)
{
Console.WriteLine("nombre cambia de {0} {1}", e.OldFullPath,
E.FullPath);
}

Al ver el sistema de archivos, puede obtener más cambios que FileSystemWatcher puede manejar.


Cuando hay demasiados eventos ocurren, FileSystemWatcher lanza el  evento Error. Para capturar
el  evento de error, siga estos pasos:
1.  Crear un nuevo   objeto FileSystemWatcher,  especificando  el directorio en la  ruta La
propiedad.
2.  Registrarse para el  evento Error.
3.  Activar eventos estableciendo EnableRaisingEvents en true. El siguiente fragmento de código
ilustra este proceso:
// C#
Watcher FileSystemWatcher = new FileSystemWatcher();
Watcher.Path = @"c:\".
// Registrarse para eventos
watcher.Error +=
Nueva ErrorEventHandler(watcher_error).

// Inicio viendo watcher.EnableRaisingEvents =


true;
// Controlador de eventos
Static void watcher_Error(object sender, ErrorEventArgs e)
{
Console.WriteLine("Error: {0}",
e.GetException();
}

Laboratorio: enumerar archivos y observar cambios


En este laboratorio, debe enumerar los archivos de una carpeta y, a continuación, mira a ver si
alguno de los archivos han cambiado. Si se produce un problema de completar un ejercicio, los
proyectos terminados están disponibles en el CD en la carpeta de código.
   Ejercicio 1: a enumerar los archivos en una carpeta
En este ejercicio vamos a enumerar todos los archivos en una carpeta en particular.
1.  Crear una nueva aplicación de consola denominada ShowFilesDemo.
2.  Agregar una de las importaciones (o de una  instrucción using en C#) para el  espacio de
nombres System.IO en el nuevo proyecto.
3.  Agregar un nuevo método que toma un  objeto DirectoryInfo denominado ShowDirectory.
4.  Dentro de su nuevo método, iterar a través de cada uno de los archivos en su directorio y
mostrarlos en la consola de a uno por vez. El código podría tener este aspecto:
// C#
Static void ShowDirectory(DirectoryInfo dir)
{
// Mostrar cada archivo
(Foreach FileInfo en archivo dir.GetFiles())
{
Console.WriteLine("file: {0}", archivo.FullName);
}
}
5.  Dentro del  método ShowDirectory, iterar a través de cada subdirectorio y llamar al  método
ShowDirectory. Esto llamará al ShowDirectory recursivamente para encontrar todos los archivos
de cada directorio. Este código podría ser algo como esto:

// C#
// Ir a través de los subdirectorios
// Recursivamente
(Foreach DirectoryInfo subDir en dir.GetDirectories())
{
ShowDirectory(subDir);
}

6.  En el  método Main, escribir código para crear una nueva instancia de la  clase
DirectoryInfo para el directorio del sistema y utilizarlo para llamar al  método ShowDirectory
nuevo. Por ejemplo, el código siguiente:
// C#
DirectoryInfo dir = new DirectoryInfo(Entorno.SystemDirectory); ShowDirectory(dir);

7.  Generar el proyecto y resolver cualquier error. Compruebe que la aplicación de consola suc-
logre enumera todos los archivos en el directorio del sistema (Environment.SystemDirectory).
    Ejercicio 2: observar cambios en el sistema de archivos
En este ejercicio, podrá observar los cambios en el sistema de archivos para todos los archivos que
terminan con la La extensión .ini.
1.  Crear una nueva aplicación de consola denominada FileWatchingDemo.
2.  Importar el  espacio de nombres System.IO  en el nuevo archivo.
3.  Crear una nueva instancia de la  clase FileSystemWatcher, especificando el sistema di-
rectorio. Por ejemplo, podría usar el siguiente código:

// C#
Watcher FileSystemWatcher =
Nueva(FileSystemWatcher entorno.SystemDirectory);

4.  Modificar las propiedades del vigilante del sistema de archivos para buscar sólo para
archivos .ini, buscar a través de todos los subdirectorios, y aceptar los cambios sólo si los atributos
del archivo cambia o si el tamaño del archivo cambia. El código podría tener este aspecto:

// C#
Watcher.Filter = "*.ini"; watcher.IncludeSubdirectories =
true; watcher.NotifyFilter =
| NotifyFilters NotifyFilters.Attributes.Tamaño;

5.  Para ver los cambios, agregue un controlador de eventos para el  evento Changed de su objeto
Watcher.
Por ejemplo, podría usar el siguiente código:

// C#
Watcher.Cambio +=
Nueva FileSystemEventHandler(watcher_changed);

6.  Necesitará el siguiente método que el  evento Changed se va a llamar. Dentro de este método,
escribir en la consola el nombre del archivo cambiado. El código podría tener este aspecto:

// C#
Static void watcher_cambiado(object sender, FileSystemEventArgs
e)
{
Console.WriteLine("cambiado: {0}", e.FullPath);
}

7.  Establezca la propiedad EnableRaisingEvents en  true para indicarle al objeto watcher para


comenzar a lanzar eventos.

8.  Generar el proyecto y resolver cualquier error. Compruebe que la aplicación de consola suc-
logre informes cuando los atributos de cualquier archivo .ini en el directorio del sistema cambie o
cuando cambia el tamaño del archivo.
Resumen de la lección
■   El FileInfo, DirectoryInfo DriveInfo , y las clases se pueden usar para enumerar y
revisar las propiedades de los objetos del sistema de archivos.
■   La  clase Path puede ser utilizada para interrogar una ruta del sistema de archivos
y debe ser utilizado en lugar de analizar la cadena manualmente.
■   La  clase FileSystemWatcher puede utilizarse para supervisar  el sistema de
archivos para los cambios como adiciones, eliminaciones y renamings.

Examen de la lección
Puede utilizar las siguientes preguntas para poner a prueba tus conocimientos de la información
Les- hijo 1, "Cómo navegar en el sistema de archivos." Las preguntas también están disponibles en
el CD de iones complementaria si prefiere revisarlos en forma electrónica.

Nota   Respuestas
Las respuestas a estas preguntas y explicaciones de por qué cada opción de
respuesta es correcta o no se encuentran en la sección "Respuestas" al final del
libro.

1.  Que son medios aceptables para abrir un archivo de escritura? (Seleccione todos los que
correspondan).
A.
// C#
File.Open("somefile.txt", FileMode.Create);

B.
// C#
File.Open("somefile.txt", FileMode.Create, FileAccess.Write);
C.
// C#
File.Open("somefile.txt", FileMode.Create, FileAccess.Read);

D.
// C#
Archivo = new FileInfo FileInfo("archivo.txt");
File.Open(FileMode.Create);

2.  Cuál de los siguientes tipos de cambios que puede ser detectado por
el archivo- SystemWatcher? (Seleccione todos los que correspondan).
A.  Nuevos archivos
B.   Los nuevos directorios
C.  Archivos modificados D.  Archivos cambiados E.   Ninguno
3.  El código siguiente cambia la extensión del archivo. (True o False).

// C#
Cadena ourPath = @"c:\boot.ini";
Ruta.ChangeExtension(ourPath, "bak");

A.  True
B.   False
Lección 2: Lectura y escritura de archivos
Lectura y escritura de archivos son dos de las tareas más comunes en el mundo del de-
sarrollo. Como un desarrollador de .NET, usted necesita saber cómo leer y escribir
archivos. .NET Framework facilita la realización de estas tareas.

Después de esta lección, será capaz de:


■   abrir un archivo y leer su contenido.
■   Crear una corriente de memoria.
■   escribir y cerrar un archivo.
Lección Tiempo estimado: 20 minutos

Comprensión arroyos
Las secuencias son una forma común de tratar ambos secuencial y acceso aleatorio a los datos
dentro de .NET Framework. Las secuencias son utilizadas en diferentes partes del marco.
Comienzan con una clase abstracta (por medio de la  palabra clave MustInherit en Visual
Basic) que proporciona la interfaz básica y aplicación para todos los flujos en el marco.
Algunas de las propiedades y métodos de la  clase Stream se muestran en la tabla 2-14 y la
tabla 2-15, respectivamente.
Tabla 2-14    propiedades de secuencia

Nombre Descripción
CanRead  determina si el stream es compatible con la
lectura. CanSeek  determina si la secuencia admite la
función de búsqueda. CanTimeout  determina si la secuencia
puede agotar el tiempo de espera. CanWrite  determina si
la secuencia puede ser escrito. Length   obtiene la
longitud (en bytes) de la secuencia.
   Obtiene o establece la posición del cursor virtual para determinar
dónde en el arroyo la posición actual. El valor
de posición no puede ser mayor que el valor de
la longitud de la secuencia.
ReadTimeout   obtiene o establece la secuencia de tiempo de espera para
las operaciones de lectura.
WriteTimeout  obtiene o establece la secuencia de tiempo de espera para
las operaciones de escritura.

Tabla 2-15    Métodos Stream

Nombre Descripción
Cerrar  Cierra la secuencia y libera todos los recursos asociados con ella.
A ras Borra los buffers en el stream y obliga a cambios a escribir en
el sistema subyacente o dispositivo.
Leer Performs una lectura secuencial de un número especificado
de bytes desde la posición actual y actualiza  la posición al final
de la lectura tras la finalización de la operación.
ReadByte Performs la lectura de un solo byte y actualiza  la posición
moviendo por uno. Idéntico al llamar a Read para leer de un solo
byte.
Seek   establece la posición dentro de la secuencia.
SetLength Specifies la longitud de la secuencia. Truncará la corriente si la
nueva longitud es menor que la longitud anterior y expandirá la
corriente si la verdad es lo contrario.
Escribir  escribe la información en la secuencia como un número
de bytes y actualiza  la posición actual para reflejar la nueva
posición de escritura.
WriteByte  escribe un byte a la secuencia y actualiza la posición.
Idéntico al llamar a escribir con un solo
byte.

Todas las demás clases stream en el .NET Framework se derivan de la  clase Stream.
Estas clases derivadas incluyen los siguientes:
■   FileStream  (System.IO)
■   MemoryStream (System.IO)

■   CryptoStream (System.Security)

■   NetworkStream System.Net ()
■   GZipStream (System.Compression)

La razón por la que estas corrientes tienen una clase base común es que trabajar con
datos como un flujo es una manera común de trabajar con datos. Por aprender a trabajar
con una corriente en general, puede aplicar este conocimiento a cualquier tipo de flujo.
Por ejemplo, podría escribir un método simple para volcar el contenido de una
secuencia a la consola de la siguiente manera:

// C#
Static void DumpStream(Stream theStream)
{
// Mover la posición de la secuencia al principio
theStream.posición = 0;

// Ir a través de toda la secuencia y mostrar el contenido mientras


(theStream.La posición != theStream.Length)
{
Console.WriteLine("{0:X2}", theStream.ReadByte();
}
}

Este código no importa qué clase de stream es enviado; puede tratar con cualquier secuencia
de la misma manera. Asimismo, anexar información a un arroyo puede hacerse en una com-
mon, como se muestra en este ejemplo:
/
/

C
#
Static void AppendToStream(Stream theStream, byte[]
datos)
{
// Mover la posición hasta el final
theStream.Posición = theStream.Length;

// Agregar algunos bytes theStream.Write(data, 0


data.Length);
}

Qué clases facilitan la lectura y la escritura de datos?


Un número de clases tomen parte en el proceso de lectura y escritura de archivos. La mayoría
opera- ciones comienzan con la  clase File. Esta clase expone los métodos estáticos (o
métodos compartidos en Visual Basic) que permiten abrir y crear archivos. La  clase File
puede realizar varios tipos de operaciones:
■   Las operaciones atómicas para leer o escribir todo el contenido de un archivo

■   Operaciones para abrir archivos para lectura


■   Operaciones para crear o abrir archivos de escritura
■   simplesoperaciones de archivos (File.Exists, File.Delete, y así
sucesivamente)
Cuando un archivo se abre o se crea, la  clase File puede devolver varios tipos de objetos. La
más rudimentaria de estos es el  objeto FileStream. Esta es una simple clase stream, sino que
representa un archivo en el sistema de archivos.
Además , el archivo de clase también tiene métodos que
devuelven StreamReaders y StreamWriters. Estas clases se ajustan a un FileStream para
soporte de lectura y escritura secuencial de un arroyo.

Las sencillas operaciones de archivo que el archivo clase admite son idénticas a las de la clase
FileInfo explicado en la Lección 1.
Además de estas clases, la  clase MemoryStream es un especializado para manip ulating
stream- los datos en la memoria. Esta clase se utiliza a menudo para crear flujo de objetos en
memoria para optimizaciones.

La   clase File


La  clase File proporciona la funcionalidad básica para abrir secuencias de archivo para
lectura y escritura. La tabla 2-16 muestra el más importante archivo estático/métodos
compartidos.
Tabla 2-16  Archivo  Estático/Métodos compartidos

Nombre Descripción
AppendAllText  anexa una cadena especificada en un archivo existente,
también crea el archivo si no existe.
AppendText  abre un archivo o crea un archivo nuevo si no existe
uno) y devuelve un StreamWriter que está dispuesta a
permitir que el texto se anexa al archivo.
Copy  copia un archivo a un nuevo archivo. El nuevo archivo no
debe existir para
Copia sea exitosa.
Crear  crea un nuevo archivo y devuelve un  objeto FileStream.
CreateText  crea o abre un archivo y devuelve un  objeto
StreamWriter que está listo para tener el texto escrito
en ella.
Move  mueve un archivo de un lugar a otro.

Tabla 2-16  Archivo  Estático/Métodos compartidos

Nombre Descripción
Abrir  Abre un archivo existente y devuelve un  objeto
FileStream.
OpenRead  abre un archivo existente y devuelve un FileStream de sólo
lectura Objeto.
OpenText  abre un archivo existente y devuelve un  objeto
StreamReader.
OpenWrite  abre un archivo existente para escribir y devuelve
un StreamWriter Objeto.
ReadAllBytes  abre un archivo, lee el contenido de ésta en una matriz
de bytes, y cierra el archivo en una operación atómica.
ReadAllLines   abre un archivo, lee el contenido del mismo en una matriz
de
cadenas (Uno por línea) y cierra el archivo en una operación atómica.
ReadAllText  abre un archivo, lee el contenido de ésta en una cadena,
y cierra el archivo en una operación atómica.
WriteAllBytes  abre un archivo, escribe el contenido de una matriz de
bytes en ella (más escribir cualquier dato existente) y
cierra el archivo en una operación atómica.
WriteAllLines  abre un archivo, escribe el contenido de una matriz de
cadenas en ella (sobrescribiendo los datos existentes),
y cierra el archivo en una operación atómica.
WriteAllText  abre un archivo, escribe el contenido de una cadena en
ella (overwrit- ing cualquier dato existente) y cierra el
archivo en una operación atómica.

La   clase Directory


Como ocurre con la  clase File, .NET Framework admite la  clase Directory, que presenta una
interfaz compartida/estático para manipular y crear directorios del sistema de archivos.
La  clase Directory proporciona la funcionalidad básica para crear, gestionar, analizar y
eliminar directorios. La tabla 2-17 muestra el directorio más importante estática/métodos
compartidos.

Tabla 2-17  Directorio  estático/Métodos compartidos

Nombre Descripción
CreateDirectory  crea todos los directorios en un trazado suministrado
Eliminar   Elimina un directorio especificado.
Existe  determina si existe un directorio en el sistema de
archivos GetCreationTime  devuelve la fecha y hora de creación de un
directorio GetCurrentDirectory  devuelve un  objeto DirectoryInfo para el
trabajo actual
Directorio de la aplicación
GetDirectories  obtiene una lista de los nombres de los subdirectorios
en un directorio especificado.
GetDirectoryRoot  devuelve el volumen de información y/o la raíz de un
directorio especificado.
GetFiles   devuelve los nombres de los archivos de un directorio
GetFileSystemEntries  devuelve una lista de subdirectorios y archivos en el
directorio especificado.
GetLastAccessTime  devuelve el tiempo que un directorio especificado del último
acceso GetLastWriteTime  devuelve el tiempo que un directorio especificado se
escribió último GetLogicalDrives  obtiene una lista de unidades en el sistema
actual como cadenas con el
Patrón de "C:\"
GetParent  obtiene el directorio padre del directorio especificado.
Move  mueve un archivo o directorio (y su contenido) a un lugar
determinado
   Establece el tiempo SetCreationTime un directorio específico fue creado
SetCurrentDirectory  Establece el directorio especificado para el directorio
de trabajo actual para una aplicación.
SetLastAccessTime  establece la última vez que se accedió a un directorio
SetLastWriteTime  establece la última vez fue escrito para un directorio

La   enumeración FileAccess


La  enumeración FileAccess proporciona a los miembros que se utilizan para determinar los
derechos requeridos cuando se abre un archivo. La tabla 2-18 muestra
el FileAccess miembros.
Tabla 2-18  FileAccess  miembros de enumeración

Nombre Descripción
Leer Specifies que el archivo debe abrirse con acceso de
sólo lectura.
Escribir  Especifica que el archivo debe abrirse para ser
escrito. No se puede leer el archivo, sólo se pueden
agregar.
ReadWrite  especifica acceso completo al archivo para lectura o
escritura. Equiv alent- para combinar    los valores de
lectura y escritura.
La   enumeración FileMode
La  enumeración FileMode proporciona a los miembros que especifican la forma en que un
archivo se ha abierto o creado. La tabla 2-19 muestra la mayoría de las FileMode miembros.
Tabla 2-19  FileMode  miembros de enumeración

Nombre Descripción
Append  abre un archivo y mueve el puntero en la clase
FileStream al final del archivo. Sólo puede utilizarse
con FileAccess.Write.
Crear  crea un archivo nuevo. Si el archivo ya existe, se
sobrescribe.
CreateNew  crea un archivo nuevo. Si el archivo ya existe, se
produce una excepción.
Abrir  Abre un archivo existente. Si el archivo no existe, se
genera una excepción.
OpenOrCreate  abre un archivo existente. Si el archivo no existe, se crea
un archivo nuevo.
Truncar  abre un archivo existente pero vacía el archivo existente
de manera que su longitud es cero bytes.

La   clase FileStream


La  clase FileStream proporciona la funcionalidad básica para abrir secuencias de archivo para
lectura y escritura. La tabla 2-20 y la tabla 2-21 muestran las más importantes   propiedades y
métodos FileStream, respectivamente.
Tabla 2-20
propiedades FileStream
Nombre Descripción
CanRead  determina si el stream es compatible con la lectura.
(Heredado de la  clase Stream).
CanSeek  determina si la secuencia admite la función de búsqueda.
(Heredado de la  clase Stream).
CanTimeout  determina si la secuencia puede agotar el tiempo de
espera. (Heredado de la  clase Stream).
CanWrite  determina si la secuencia puede ser escrito. (Heredado de
la  clase Stream).
Manejar  obtiene la secuencia subyacente de identificador de
archivo.
Length   obtiene la longitud (en bytes) de la secuencia. (Heredado de
la
 Clase
Stream).
Name  Obtiene el nombre del archivo.
   Obtiene o establece la posición del cursor virtual para determinar
dónde en el arroyo la posición actual. El valor
de posición no puede ser mayor que la longitud de la
secuencia. (Heredado de la  clase Stream).
ReadTimeout Obtiene o establece la secuencia de tiempo de
espera para las operaciones de lectura. (Heredado
de la  clase Stream).
WriteTimeout  obtiene o establece la secuencia de tiempo de
espera para las operaciones de escritura. (Heredado
de la  clase Stream).

Tabla 2-21    métodos FileStream


Nombre Descripción
Cerrar  Cierra la secuencia y libera todos los recursos
asociados con ella. (Heredado de la  clase Stream).
A ras Borra los buffers en el stream y obliga a cambios a
escribir en el sistema subyacente o dispositivo. (Heredado
de la  clase Stream).
El bloqueo  impide que otros procesos puedan cambiar todo o
parte del archivo.
Leer Performs una lectura secuencial de un número
especificado de bytes desde la posición actual y
actualiza  la posición al final de la lectura tras la
finalización de la operación. (Inher- ited de la  clase
Stream).
ReadByte Performs la lectura de un solo byte y actualiza la posición
moviendo por uno. Idéntico al llamar a Read para leer de
un solo byte. (Heredado de la  clase Stream).
Seek   establece la posición dentro de la secuencia. (Heredado de
la
 Clase
Stream).
SetLength Specifies la longitud de la secuencia. Truncará la corriente
si la nueva longitud es menor que la longitud anterior y
expandirá la corriente si la verdad es lo contrario.
(Heredado de la  clase Stream).
   Permite desbloquear otros procesos para cambiar una parte o la
totalidad de los respaldan- ing archivo.
Escribir  escribe la información en la secuencia como un
número de bytes y actualiza  la posición actual para
reflejar la nueva escritura de posi- ción. (Heredado de
la  clase Stream).
WriteByte  escribe un byte a la secuencia y actualiza la posición.
Idéntico al llamar a escribir con un solo byte. (Heredado de la  clase Stream).

La   clase StreamReader


La  clase StreamReader proporciona la funcionalidad básica para escribir datos
desde una secuencia clase derivada. La tabla 2-22 y la tabla 2-23 muestran la más
importante  prop- erties StreamReader y métodos, respectivamente.
Tabla 2-22      propiedades StreamReader

Nombre Descripción
BaseStream  obtiene la secuencia subyacente de que el lector está
escrito en CurrentEncoding  obtiene la actual codificación usada para la
secuencia subyacente EndOfStream  determina si el lector ha
encontrado el final de
La
secuen
cia

Tabla 2-23      métodos StreamReader

Nombre Descripción
Cerrar  Cierra el lector y la secuencia subyacente
Peek  devuelve el siguiente carácter en el arroyo sin mover
la posición actual del flujo
Read   Lee el siguiente conjunto de caracteres en el
flujo ReadBlock  lee el siguiente bloque de caracteres en
el flujo ReadLine   lee la siguiente línea de caracteres en
el flujo
ReadToEnd  lee todos los caracteres hasta el final de la secuencia
Cómo leer de un archivo
Abrir un archivo es bastante común. En su forma más simple, la apertura de un archivo
consiste en pedir la  clase File para abrir una secuencia especificando la ruta de acceso al
archivo. Cuando se abre un archivo para leer su contenido, puede utilizar el   miembro de
enumeración FileMode.Open para especificar un archivo existente, así
como FileAccess.Read para obtener acceso de sólo lectura a los archivos, como se ve en este
ejemplo de código:

// C#
= File FileStream
File.Open(@"C:\boot.ini", FileMode.Open, FileAccess.Read);

El  método File.Open devuelve un  objeto FileStream. Una secuencia de archivo, sólo es un


flujo, así que usted puede ver el contenido llamando al leer o ReadByte métodos de la  clase
Stream. Pero para facilitar la lectura del archivo, el sistema de E/S
admite StreamReader y Arroyo- Writer clases, que hacen más fácil la lectura y la escritura.
Para leer el archivo, puede simplemente crear un nuevo StreamReader  FileStream que
envuelve la, como se muestra aquí:
// C#
Rdr StreamReader = new StreamReader(Archivo); Console.Write(rdr.ReadToEnd();
Rdr.Close();
File.Close();

La  clase StreamReader se destina a leer una secuencia como una cadena, no como una serie
de bytes. De esta manera, la StreamReader's métodos para devolver datos devuelven cadenas
o matrices de cadenas.
La  clase File admite algunos métodos adicionales para hacer más simple para abrir un
archivo para lectura. En el ejemplo anterior, se ha creado un objeto FileStream y luego creó
un nuevo StreamReader. La  clase File apoya la creación de un StreamReader directamente
con el  método de texto abierto, como se ve en este fragmento de código:
/
/

C
#
Rdr StreamReader = File.OpenText(@"C:\boot.ini");
Console.Write(rdr.ReadToEnd();
Rdr.Close
();

Si todo lo que usted necesita hacer es leer todo el archivo, el archivo clase admite la lectura
del archivo en una única llamada al método, ocultar todos los detalles de la secuencia y reader
aplicación llamando a su  método ReadAllText:

// C# Console.WriteLine(File.ReadAllText(@"C:\boot.ini");

Por lo tanto, si usted puede conseguir todo lo que necesite con el archivo de la clase  método
ReadAllText, ¿por qué habría de utilizar estos otros métodos? La razón habitual es que usted
no necesita el archivo de texto completo. Este enfoque es especialmente útil si usted está
buscando un determinado fragmento de texto. Por ejemplo, en este fragmento de código,
puede extraer los datos, línea por línea y prueba para una cadena que coincide, y si la
encuentra, no necesitas cargar toda la cadena en la memoria:
// C#
Rdr StreamReader = File.OpenText(@"C:\boot.ini");

// Buscar a través de la secuencia hasta que lleguemos al final mientras (!


rdr.EndOfStream)
{
Cadena = línea rdr.ReadLine();
Si (línea.Contains("Inicio")
{
// Si encontramos la palabra boot, notificaremos
// El usuario y dejar de leer el archivo.
Console.WriteLine("encontrado boot:");
Console.WriteLine(Línea);
Break;
}

// Limpiar
rdr.Close();

Análisis de archivos con este método es especialmente útil cuando se busca a través de
archivos muy grandes.

La   clase StreamWriter


La  clase StreamWriter proporciona la funcionalidad básica para leer datos de un arroyo clase
derivada. La tabla 2-24 y la tabla 2-25 muestran la más importante  prop- erties StreamWriter
y métodos, respectivamente.
Tabla 2-24      propiedades StreamWriter

Nombre Descripción
AutoFlush  obtiene o establece un indicador que muestra si cada
llamada al  método Write deben enjuagar los cambios
en la secuencia subyacente.
BaseStream  obtiene la secuencia subyacente de que el lector está
leyendo.    Obtiene la actual codificación codificación usada para la
secuencia subyacente. NewLine  obtiene o establece una cadena
que contiene la cadena de terminador de línea.
Normalmente sólo se utiliza si es necesario cambiar la
cadena que ter-
Minates una línea
individual.

Tabla 2-25      métodos StreamWriter

Nombre Descripción
Cerrar  Cierra el escritor y la secuencia subyacente
Escribir  escribe al arroyo
WriteLine  escribe datos a la secuencia seguida por la cadena que
termi- coordenadas una línea individual

Cómo escribir en un archivo


Antes de poder escribir en un archivo, tienes que abrir el archivo para escritura.
Este proceso es sim- ilar al abrir un archivo para lectura. Por ejemplo, puede abrir
un archivo para escribir como se muestra a continuación:

/
/

C
#
FileStream = file archivo.Create(@"c:\somefile.txt");

A diferencia del código para abrir un archivo para lectura, este código crea un archivo nuevo
con un  objeto FileStream listo para ser escrito. Con el  objeto FileStream en la mano, usted
puede escribir a la secuencia directamente, si así lo desea. Más a menudo, sin embargo, usted
deseará utilizar un  objeto StreamWriter para escribir datos en el archivo nuevo, como se
muestra en este código:
// C#
Escritor StreamWriter = new StreamWriter(Archivo);
Writer.WriteLine("Hello");
writer.Close(), File.Close();

Puede utilizar StreamWriter para escribir el texto directamente en el nuevo archivo. Este


patrón es similar al patrón de lectura de un archivo. También, como se ha demostrado
anteriormente para la lectura, la  clase de archivo permite crear un  objeto StreamWriter
directamente con el  método CreateText:

// C#
Escritor StreamWriter = File.CreateText(@"c:\somefile.txt");
Writer.WriteLine("Hello");
Writer.Close();

La  clase File también soporta el  método WriteAllText que escribe una cadena en un
nuevo archivo, como se muestra aquí:

// C#
Archivo.WriteAllText(@"c:\somefile.txt", "Hola");

Este proceso es sencillo, pero hay veces cuando necesite escribir en un archivo existen- tes.
Para escribir en un archivo existente es similar excepto en la forma de realmente abrir el
archivo. Para abrir un archivo para escritura, puede utilizar el    método Open de la clase de
archivo pero especifica que desea escribir en la secuencia que se devolverá, como se muestra
en este ejemplo:
/
/

C
#
Archivo FileStream = null;
= file archivo.Open(@"c:\somefile.txt",
FileMode.Open,
FileAccess.Write);

El archivo de clase tiene el  método OpenWrite, que es un atajo para lograr esto y simplifica
la apertura de archivos existentes para escribir. En lugar de llamar al  método Open de
la  clase File y especificando que desea abrirlo para escritura, puede simplemente usar un
código como este:
// C#
= file archivo.OpenWrite(@"c:\somefile.txt");

Estos fragmentos de código funciona sólo si el archivo existe. En muchas ocasiones, deseará
abrir un archivo existente o crear uno nuevo. Lamentablemente, el  método OpenWrite abrirá
sólo un archivo existente. Podría escribir código para comprobar la existencia del archivo y
crear el archivo si no existe, sin embargo, afortunadamente, puede utilizar el  método Open de
la  clase File para especificar que desea abrir o crear un archivo, como se muestra aquí:

// C#
= file archivo.Open(@"c:\somefile.txt", FileMode.OpenOrCreate, FileAccess.Write);

El  valor de enumeración FileMode.OpenOrCreate permite evitar escribir código proce- dural


para tratar la cuestión de si se trata de un archivo nuevo o existente.
Comprensión de los lectores y escritores
Como se muestra en las secciones anteriores, StreamReader y StreamWriter son clases que
facilitan la escritura y la lectura de secuencias de texto. Ese es el propósito de las clases del
escritor y el lector. La  clase StreamReader se deriva de la  clase TextReader abstracta (por
medio de la  palabra clave MustInherit en Visual Basic). StreamWriter, no es de extrañar que
se deriva de la  clase TextWriter abstracta. Estas clases abstractas representan la interfaz
básica basada en texto para todos los lectores y escritores.

Por ejemplo, hay un lector y escritor de texto adicional llamado


par StringReader y StringWriter. La finalidad de estas clases es para escribir y leer en cadenas
en memoria. Por ejemplo, para leer desde una cadena mediante StringReader, utilice un
código como el siguiente:

// C#
String s = @"Hola a todos esta
es una cadena de texto de
múltiples líneas".

StringReader rdr = new StringReader(s);

// Ver si existen más caracteres mientras


(RDR.Peek() != -1)
{
Cadena = línea rdr.ReadLine();
Console.WriteLine(Línea);
}

Por el contrario, escribe una cadena en una forma eficiente


mediante StringWriter. StringWriter utiliza un StringBuilder, por lo que es muy eficiente en la
creación de cadenas cada vez mayores. Se utiliza de la siguiente manera:
// C#
Escritor StringWriter = new StringWriter(); escritor.WriteLine("Hola a todos"); escritor.WriteLine("Este es un multi-
línea"); escritor.WriteLine("cadena de texto");

Console.WriteLine(escritor.ToString();

Pero porque no necesita siempre de sólo lectura o escritura de datos textuales, el marco de
trabajo .NET-  namespace BinaryReader también soporta dos clases para la lectura o escritura
de archivo binario

Los datos. Las    clases BinaryReader y BinaryWriter puede usarse para manejar obteniendo
los datos binarios y de los arroyos. Por ejemplo, si desea crear un nuevo archivo para
almacenar datos binarios, puede utilizar la  clase BinaryWriter para escribir distintos tipos de
datos en una secuencia de la siguiente manera:

// C#
FileStream = File newFile.Create(@"c:\somefile.bin");

BinaryWriter escritor = new BinaryWriter(newFile);

Número largo = 100;


Bytes byte[] = new byte[] { 10, 20, 50, 100 };
String s = "hambre".

Escritor.Write(número); escritor.Write(bytes); escritor.Write(s);

Escritor.Close();
Si usted ha escrito los datos con la clase BinaryWriter, puede utilizar la clase
BinaryReader para obtener los datos en el mismo orden. Para cada clase
BinaryWriter.Write o  llamada WriteLine, necesitará llamar a la clase BinaryReader.Leer
el método apropiado. Por ejemplo, el código siguiente leerá los datos escritos por el código
mostrado:
// C#
NewFile FileStream = File.Open(@"c:\somefile.bin", FileMode.Open);

BinaryReader reader = new BinaryReader(newFile);

Número largo = reader.ReadInt64(); Byte[]


bytes = reader.ReadBytes(4); string s
= reader.ReadString();
Reader.Close();

Console.WriteLine(número);
Foreach (byte b en bytes)
{
Console.Write("[{0}]", b);
}
Console.WriteLine();
Console.WriteLine(s);

La     clase MemoryStream


La  clase MemoryStream proporciona la funcionalidad básica para crear secuencias en
memoria. La tabla 2-26 y la tabla 2-27 muestran la más importante MemoryStream
con métodos y propiedades, respectivamente.
Tabla 2-26  MemoryStream
Propiedades
Nombre Descripción
CanRead  determina si el stream es compatible con la lectura.
(Heredado de la  clase Stream).
CanSeek  determina si la secuencia admite la función de búsqueda.
(Heredado de la  clase Stream).
CanTimeout  determina si la secuencia puede agotar el tiempo de espera.
(Heredado de la
 Clase Stream).
CanWrite  determina si la secuencia puede ser escrito. (Heredado de
la  clase Stream).
Capacidad  obtiene o establece el número de bytes asignados para la
secuencia.

Tabla 2-26  MemoryStream   Propiedades

Nombre Descripción
Length   obtiene la longitud (en bytes) de la secuencia. (Heredado de la
 Clase Stream).
   Obtiene o establece la posición del cursor virtual para determinar
dónde en el arroyo la posición actual. El valor de posición no
puede ser mayor que la longitud de la secuencia. (Heredado de
la  clase Stream).
ReadTimeout Obtiene o establece la secuencia de tiempo de espera para las
operaciones de lectura. (Heredado de la  clase Stream).
WriteTimeout  obtiene o establece la secuencia de tiempo de espera para las
operaciones de escritura. (Heredado de la  clase Stream).

Tabla 2-27      métodos MemoryStream

Nombre Descripción
Cerrar  Cierra la secuencia y libera todos los recursos asociados con
ella. (Heredado de la  clase Stream).
A ras Borra los buffers en el stream y obliga a cambios a escribir en
el sistema subyacente o dispositivo. (Heredado de la  clase
Stream).
GetBuffer  recupera la matriz de bytes sin signo que se utilizaron para crear
la secuencia.
Leer Performs una lectura secuencial de un número especificado
de bytes desde la posición actual y actualiza  la posición al
final de la lectura tras la finalización de la operación.
(Heredado de la  clase Stream).
ReadByte Performs la lectura de un solo byte y actualiza  la posición
moviendo por uno. Idéntico al llamar a Read para leer de un
solo byte. (Heredado de la  clase Stream).
Seek   establece la posición dentro de la secuencia. (Heredado de
la  clase Stream).
SetLength Especifica la longitud de la secuencia. Este método truncará la
corriente si la nueva longitud es menor que la longitud anterior
y expandirá la corriente si la verdad es lo contrario. (Heredado
de la  clase Stream).

Tabla 2-27      métodos MemoryStream

Nombre Descripción
ToArray  escribe toda la secuencia en una matriz de bytes.
Escribir  escribe la información en la secuencia como un número
de bytes y actualiza  la posición actual para reflejar la
nueva posición de escritura. (Heredado de la  clase
Stream).
WriteByte  escribe un byte a la secuencia y actualiza la posición. Este
método es idéntico a llamar escribir con un solo byte.
(Heredado de la  clase Stream).
WriteTo  escribe el MemoryStream a otra secuencia.

Cómo utilizar un MemoryStream


Como hemos visto, el trabajo con secuencias de datos es una habilidad muy importante para
cualquier desarrollador. Por desgracia, a menudo necesitará crear una secuencia antes de que
usted realmente necesita para guardarla en un lugar (como en un archivo). La  clase
MemoryStream tiene la tarea de ayudar a crear secuencias en memoria. Crear una secuencia
de memoria es tan sencillo como crear una nueva instancia de la  clase MemoryStream:
// C#
MemoryStream memStrm = new MemoryStream();

Puede utilizar StreamWriter exactamente como se utiliza la  clase FileStream anteriormente


para escribir datos a su nuevo MemoryStream:
// C#
Escritor StreamWriter = new StreamWriter(memStrm);
escritor.WriteLine("Hello"); escritor.WriteLine("Adiós");

Ahora que tienes los datos en su  objeto MemoryStream, qué hacemos con ella? Los
diseñadores de la  clase MemoryStream comprender que almacenar la secuencia en la
memoria suele ser una situación temporal. De modo que la clase admite la escritura de la
secuencia directamente a otra secuencia o copiar los datos a otros dispositivos de
almacenamiento. Un uso común de una memoria- Stream es limitar el tiempo se abre un
archivo para escritura (debido a que bloquea el archivo). Para Seguir este ejemplo de
ejecución, puede indicarle el MemoryStream para escribir un FileStreamcomo se muestra a
continuación:
// C#
// Forzar al escritor a cargar los datos en el
// La secuencia subyacente
Writer.flush();

// Crea una secuencia de archivos


FileStream = file archivo.Create(@"c:\inmemory.txt");

// Escriba toda la secuencia de memoria al archivo


MemStrm.WriteTo(Archivo);

// Limpiar writer.Close(), File.Close(); memStrm.Close();

Como puede ver, el código realiza estas tareas:


1.  Dice el escritor StreamWriter para enjuagar sus cambios en la secuencia subyacente (en
este caso, el MemoryStream).
2.  Se crea el nuevo archivo.
3.  Indica que el  objeto MemoryStream para escribir a sí mismo con el  objeto FileStream.
Este proceso le permite hacer tiempo de trabajo intenso en el MemoryStream y luego abra el
archivo, vaciar los datos y cerrar el archivo rápidamente.
La   clase BufferedStream
La  clase BufferedStream proporciona la funcionalidad básica para envolver arroyos para
mejorar el rendimiento mediante el almacenamiento temporal lee y escribe a través de la
secuencia. La tabla 2-28 y la tabla 2-29 muestran la más importante BufferedStream métodos
y propiedades, respectivamente.

Tabla 2-28  BufferedStream


Propiedades
Nombre Descripción
CanRead  determina si el stream es compatible con la lectura. (Inher-
ited de la  clase Stream).
CanSeek  determina si la secuencia admite la función de búsqueda.
(Inher- ited de la  clase Stream).
CanTimeout  determina si la secuencia puede agotar el tiempo de
espera. (Heredado de la  clase Stream).
CanWrite  determina si la secuencia puede ser escrito. (Heredado de
la  clase Stream).
Length   obtiene la longitud (en bytes) de la secuencia. (Heredado de la
 Clase Stream).
   Obtiene o establece la posición del cursor virtual para determinar
dónde en el arroyo la posición actual. El valor
de posición no puede ser mayor que la longitud de la
secuencia. (Heredado de la  clase Stream).
ReadTimeout Obtiene o establece la secuencia de tiempo de espera para las
operaciones de lectura. (Inher- ited de la  clase Stream).
WriteTimeout  obtiene o establece la secuencia de tiempo de espera
para las operaciones de escritura. (Heredado de
la  clase Stream).

Tabla 2-29  BufferedStream


métodos
Nombre Descripción
Cerrar  Cierra la secuencia y libera todos los recursos asociados
con ella. (Heredado de la  clase Stream).
A ras Borra los buffers en el stream y obliga a cambios a escribir
en el sistema subyacente o dispositivo. (Heredado de
la  clase Stream).
Leer Performs una lectura secuencial de un número especificado
de bytes desde la posición actual y actualiza  la posición al
final de la lectura tras la finalización de la operación.
(Inher- ited de la  clase Stream).
Tabla 2-29  BufferedStream   métodos

Nombre Descripción
ReadByte Performs la lectura de un solo byte, y actualiza la
posición moviendo por uno. Idéntico al llamar
a Read para leer un pecado- gle byte. (Heredado de
la  clase Stream).
Seek   establece la posición dentro de la secuencia. (Heredado de
la
 Clase
Stream).
SetLength Specifies la longitud de la secuencia. Este método
complir- cate la corriente si la nueva longitud es menor
que la longitud anterior y expandirá la corriente si la
verdad es lo contrario. (Heredado de la  clase Stream).
Escribir  escribe la información en la secuencia como un
número de bytes y actualiza la posición actual para
reflejar la nueva escritura de posi- ción. (Heredado de
la  clase Stream).
WriteByte  escribe un byte a la secuencia y actualiza la posición.
Este método es idéntico a llamar escribir con un solo
byte. (Heredado de la  clase Stream).

Cómo utilizar un BufferedStream


A veces, desea la conveniencia de utilizar una secuencia de comandos, pero escribir datos en
una secuencia directamente no funciona muy bien. Esta situación es donde puedes usar
un buffer- clase Stream. Las  envolturas BufferedStream otro objeto Stream para permitir
escribe para suceder a un buffer, y sólo cuando el buffer se vacía ¿los datos realmente se
empuja a la secuencia subyacente. Para utilizar un BufferedStream, siga estos pasos:
1.  Crear un nuevo  objeto FileStream utilizando el archivo class para especificar un archivo
nuevo.
2.  Crear una nueva corriente de búfer, especificando la secuencia de archivo como la
secuencia subyacente.
3.  Utilice StreamWriter para escribir datos en la memoria tampón de
stream. El siguiente fragmento de código ilustra este proceso:
// C#
FileStream = File newFile.Create(@"c:\test.txt"); BufferedStream

buffer = new BufferedStream(newFile); escritor StreamWriter = new

StreamWriter(buffered); escritor.WriteLine("algunos datos");

Writer.Close();

Laboratorio: Lectura y escritura de archivos


En este laboratorio, se creará un nuevo archivo, escribir algunos datos, y cerrar el archivo.
Podrá volver a abrir el archivo, leer los datos, y mostrar los datos en la consola. Si encoun- ter
un problema de completar un ejercicio, los proyectos terminados están disponibles en el CD
en la carpeta de código.
    
Ejercicio 1: Escribir a un nuevo archivo.
En este ejercicio, creará un nuevo archivo e insertar algún texto en él.
1.  Crear una nueva aplicación de consola denominada FileDemo.
2.  Agregar una de las importaciones (o de una  instrucción using en C#) para
el  espacio de nombres System.IO en el nuevo proyecto.
3.  En el  método Main, crear un nuevo StreamWriter callling CreateText por
el método de la  clase File.
4.  Escribir algunas líneas para el redactor de flujo mediante el  método
WriteLine.
5.  Cerrar StreamWriter. El código podría tener este aspecto cuando haya
terminado:
// C#
Static void main(String[] args)
{
Escritor StreamWriter = File.CreateText(@"c:\Newfile.txt");
escritor.WriteLine("Este es mi nuevo archivo"); escritor.WriteLine("le gusta
su formato?"); writer.Close();
}
6.  Generar el proyecto y resolver cualquier error. Compruebe que la aplicación
de consola cre- ates el archivo comprobando manualmente el archivo en el
sistema.
 Ejercicio 2: Lectura de un archivo
En este ejercicio, va a abrir el archivo creado en el ejercicio 1 y mostrar el contenido en la
consola.
1.  Abra el proyecto FileDemo creado en el ejercicio 1.
2.  En el  método Main después del  objeto StreamWriter está cerrada, abra el
archivo utilizando el  método OpenText de la  clase File para crear un
nuevo  objeto StreamReader.
3.  Crear una nueva cadena denominada  contenido y  llamar al   método
ReadToEnd del
 Clase StreamReader para obtener todo el contenido del archivo.
4.  Cerrar el  objeto StreamReader.
5.  Escriba la cadena a la consola. El código podría tener este aspecto:

// C#
Lector StreamReader = File.OpenText(@"c:\Newfile.txt");
Contenido de cadena = reader.ReadToEnd();
lector.Close(); Console.WriteLine(índice);

6.  Genere el proyecto y resolver cualquier error. Compruebe que la aplicación de


consola suc- logre muestra el contenido del archivo en la ventana de la
consola.

Resumen de la lección
■   La  claseFile puede ser usada para abrir archivos, crear nuevos archivos, leer
archivos completos atomi- camente, e incluso escribir archivos.
■   La  claseFileStream representa un archivo en el sistema de archivos y
permite la lectura y escritura (dependiendo de cómo se haya creado).
■   El StreamReader y StreamWriter clases se utilizan para simplificar la
escritura de cadenas a los arroyos.

■   El MemoryStream es un stream especializados para la creación de contenido


en la memoria y admite guardar el stream a otros arroyos.

Examen de la lección
Puede utilizar las siguientes preguntas para poner a prueba tus conocimientos de la
información Les- hijo 2, "la lectura y escritura de archivos." Las preguntas también
están disponibles en el CD de iones complementaria si prefiere revisarlos en forma
electrónica.

Nota   Respuestas
Las respuestas a estas preguntas y explicaciones de por qué cada opción de
respuesta es correcta o no se encuentran en la sección "Respuestas" al final
del libro.

1.  Los métodos de la  clase FileStream afectan la  propiedad Posición?


(Seleccione todos los que correspondan).
A. Leer B.  Bloquear C. Escribir D.   Buscar
2.  ¿Cómo se puede forzar cambios en StreamWriter para ser enviado a la
secuencia que va a grabar? (Seleccione todos los que correspondan).
A.  Cerrar StreamWriter.
B.   Llamar al  método Flush de StreamWriter.
C.   Establezca la  propiedad AutoFlush de StreamWriter en true.
D.   Cerrar la secuencia.
3.  Cuales de las siguientes cree un FileStream para escribir cuando desea abrir
un archivo existente o crear uno nuevo si no existe? (Seleccione todos los que
correspondan).
A.  Crear una nueva instancia de la  clase FileStream, con la  opción de
FileMode
OpenOrCreate.
B.   Llamada File.Create para crear la clase FileStream.
C.   Llamada File.Open con la  opción de FileMode OpenOrCreate.
D.   Llamada File.Open con el FileMode opción de Abrir.

Lección 3: Comprimir secuencias


Ahora que conoce los fundamentos de cómo trabajar con arroyos, estás listo para aprender
acerca de un nuevo tipo de secuencia que será importante para ciertos tipos de proyectos. A
menudo en proyectos reales, le resultará útil para ahorrar espacio o ancho de banda mediante
la compresión de datos. El .NET Framework admite dos nuevas clases stream que pueden
comprimir los datos.

Después de esta lección, será capaz de:


■   comprimir secuencias con las      clases GZipStream y DeflateStream.
■   descomprimir secuencias con las      clases GZipStream y DeflateStream.
Lección Tiempo estimado: 10 minutos

La introducción de las Secuencias de Compresión


En el sistema de E/S dentro de .NET Framework, existen dos métodos de compresión de
datos: gzip y deflate. Ambos de estos métodos de compresión son algoritmos de compresión
estándar de la industria que también son libres de la protección de patentes. Por lo tanto, eres
libre de utilizar cualquiera de estos métodos de compresión en tus propias aplicaciones sin
ningún tipo de los derechos de propiedad intelectual.

Tenga en cuenta   las


limitaciones de tamaño de
compresión
Ambos métodos de compresión están limitadas a la compresión de datos
sin comprimir de hasta 4 GB.

Estos métodos de compresión son expuestos por el .NET Framework  como dos


tipos de secuencias que admiten la compresión y descompresión. Estos flujos son
apli- carse en las    clases GZipStream y DeflateStream.

Nota   debo usar gzip o DEFLATE?


Tanto las      clases GZipStream y DeflateStream utiliza el mismo algoritmo de
compresión de datos. La única diferencia es que la especificación GZIP 1  permite
encabezados que incluyen información adicional que pudiera ser útil para
descomprimir un archivo con la   herramienta gzip ampliamente utilizado. Si la
compresión de datos para uso exclusivo dentro de su propio sistema, el archivo
se escribe usando DeflateStream  es ligeramente menor debido a la falta de
información de encabezado, pero si usted tiene la intención de distribuir los
archivos a ser decom- pulsado a través de gzip, utilice GZipStream  en su lugar.

1   http://www.ietf.org/rfc/rfc1952.txt?number=1952

La   clase GZipStream


GZipStream es una clase que permite la compresión y descompresión de datos a través de
stream mediante el método de compresión gzip. La tabla 2-30 y la tabla 2-31 muestran la más
importante GZipStream métodos y propiedades, respectivamente.
Tabla 2-30
GZipStream  Propiedade
s
Nombre Descripción
BaseStream  obtiene la secuencia subyacente.
CanRead  determina si el stream es compatible con la lectura.
(Inher- ited de la  clase Stream).
CanSeek  determina si la secuencia admite la función de
búsqueda. (Inher- ited de la  clase Stream).
CanTimeout  determina si la secuencia puede agotar el tiempo de
espera. (Heredado de la  clase Stream).
CanWrite  determina si la secuencia puede ser escrito. (Heredado
de la  clase Stream).
Longitud No utilice. Generará NotSupportedException.
(Heredado de la  clase Stream).
   No utilice la posición. Generará NotSupportedException.
(Heredado de la  clase Stream).
ReadTimeout Obtiene o establece la secuencia de tiempo de espera
para las operaciones de lectura. (Inher- ited de la  clase
Stream).
WriteTimeout  obtiene o establece la secuencia de tiempo de
espera para las operaciones de escritura. (Heredado
de la  clase Stream).

Tabla 2-31
GZipStream  métodos
Nombre Descripción
Cerrar  Cierra la secuencia y libera todos los recursos
asociados con ella. (Heredado de la  clase Stream).
A ras Borra los buffers en el stream y obliga a cambios a
escribir en el sistema subyacente o dispositivo.
(Heredado de la  clase Stream).

Tabla 2-31  GZipStream  métodos

Nombre Descripción
Leer Performs una lectura secuencial de un número
especificado de bytes desde la posición actual y
actualiza  la posición al final de la lectura tras la
finalización de la operación. (Inher- ited de la  clase
Stream).
ReadByte Performs la lectura de un solo byte y actualiza la
posición moviendo por uno. Este método es idéntico a
llamar a Read para leer de un solo byte. (Heredado de
la  clase Stream).
Buscar No utilice. Generará NotSupportedException.
(Heredado de la  clase Stream).
SetLength No utilice. Generará NotSupportedException.
(Heredado de la  clase Stream).
Escribir  escribe la información en la secuencia como un
número de bytes y actualiza  la posición actual para
reflejar la nueva escritura de posi- ción. (Heredado de
la  clase Stream).
WriteByte  escribe un byte a la secuencia y actualiza la posición.
Este método es idéntico a llamar escribir con un solo
byte. (Heredado de la  clase Stream).

La   clase DeflateStream


DeflateStream es una clase que permite la compresión de datos a través de stream
mediante el método de compresión Deflate. La tabla 2-32 y la tabla Administración
del show el más importante DeflateStream métodos y propiedades,
respectivamente.
Tabla 2-32  DeflateStream
Propiedades
Nombre Descripción
BaseStream  obtiene la secuencia subyacente.
CanRead  determina si el stream es compatible con la
lectura. (Heredado de la  clase Stream).
CanSeek  determina si la secuencia admite la función de
búsqueda. (Heredado de la  clase Stream).

Tabla 2-32  DeflateStream


Propiedades
Nombre Descripción
CanTimeout  determina si la secuencia puede agotar el tiempo de
espera. (Heredado de la  clase Stream).
CanWrite  determina si la secuencia puede ser escrito. (Heredado de
la  clase Stream).
Longitud No utilice. Generará NotSupportedException. (Heredado de
la  clase Stream).
   No utilice la posición. Generará NotSupportedException.
(Heredado de la  clase Stream).
ReadTimeout Obtiene o establece la secuencia de tiempo de espera
para las operaciones de lectura. (Heredado de la  clase
Stream).
WriteTimeout  obtiene o establece la secuencia de tiempo de espera
para las operaciones de escritura. (Heredado de
la  clase Stream).

Administración del cuadro      métodos DeflateStream

Nombre Descripción
Cerrar  Cierra la secuencia y libera todos los recursos asociados
con ella. (Heredado de la  clase Stream).
A ras Borra los buffers en el stream y obliga a cambios a escribir
en el sistema subyacente o dispositivo. (Heredado de
la  clase Stream).
Leer Performs una lectura secuencial de un número especificado
de bytes desde la posición actual y actualiza  la posición al
final de la lectura tras la finalización de la operación.
(Inher- ited de la  clase Stream).
ReadByte Performs la lectura de un solo byte y actualiza la posición
moviendo por uno. Este método es idéntico a llamar
a Read para leer de un solo byte. (Heredado de la  clase
Stream).
Buscar No utilice. Generará NotSupportedException. (Heredado de
la  clase Stream).

Administración del cuadro      métodos DeflateStream

Nombre Descripción
SetLength No utilice. Generará NotSupportedException.
(Heredado de la  clase Stream).
Escribir  escribe la información en la secuencia como un
número de bytes y actualiza  la posición actual para
reflejar la nueva escritura de posi- ción. (Heredado de
la  clase Stream).
WriteByte  escribe un byte a la secuencia y actualiza la posición.
Este método es idéntico a llamar escribir con un solo
byte. (Heredado de la  clase Stream).

Cómo comprimir datos con una secuencia de


compresión
Secuencias de Compresión son un poco diferentes a las secuencias mostradas en la anterior
les- hijos. En lugar de la secuencia escrito a un recurso (por ejemplo, un archivo de
un FileStream o memoria para un MemoryStream), escribe en otra secuencia. La secuencia de
compresión se utiliza para tomar datos como cualquier secuencia, pero cuando escribe datos,
lo empuja a otra secuencia en el fichero comprimido (o descomprimido) de formato.
Los párrafos siguientes proporcionan un ejemplo típico: la lectura de un archivo existente en
el sistema de archivos y escribir una nueva versión del archivo comprimido. En primer lugar
usted necesita para abrir el archivo y el archivo comprimido que va a escribir a:
// C#
= Archivo sourceFile FileStream.OpenRead(inFilename); =
Archivo destFile FileStream.Create(outFilename);

Comprimir un stream requiere que la secuencia de compresión envolver el saliente (o destino)


Arroyo. Esta tarea se realiza en el constructor de la secuencia de compresión, tal y como se
muestra aquí:
// C#
GZipStream compStream =NuevodestFile CompressionMode.Compress GZipStream( ,);

Esta declaración indica la secuencia de compresión para comprimir datos y empujarla hacia la
des- tination stream. La constr CompressionMode uctor toma un valor que especifica si la
secuencia va a ser usado para comprimir o descomprimir. En este caso, desea comprimir la
secuencia, por lo que utilizar CompressionMode.Compress. Una vez que haya creado la
secuencia de compresión, es simplemente una cuestión de lectura de datos desde la secuencia
de origen e introduciéndolo en la secuencia de compresión, tal y como se muestra aquí:
// C#
Int = theByte sourceFile.ReadByte();
Mientras (theByte !=
-1)
{
CompStream.WriteByte((byte)theByte);
TheByte = .sourceFile ReadByte();
}

Este código transfiere datos de byte en byte desde el archivo de origen () en la sourceFile
com-compStream pression stream (). Observe que usted no escribe directamente en el archivo
de destino en todo (destFile). Porque estás escribiendo en la secuencia de compresión
sexyskadi- ción, el arroyo se llena con la versión comprimida de los datos desde el archivo de
origen.
Las secuencias de código recién mostrado no es específico para el método de compresión
gzip. Si cambiamos la construcción de la secuencia a utilizar  en su lugar el DeflateStream, el
resto del código no cambia en absoluto. Todo lo que debe hacer es crear un  lugar
DeflateStream, tal como se muestra aquí (observe la firma del constructor es el mismo que el
de la GZipStream):

// C#
DeflateStream compStream =
Nuevo (destFile DeflateStream, CompressionMode.Compress);

Cómo descomprimir los datos con una secuencia de compresión


Descompresión utiliza el mismo diseño de los programas de compresión, excepto
que los arroyos se tratan de forma ligeramente diferente. Por ejemplo, puede crear
sus archivos de origen y de destino como se hacía antes:

// C#
= Archivo sourceFile FileStream.OpenRead(inFilename); =
Archivo destFile FileStream.Create(outFilename);

En este caso, el archivo de origen es un archivo comprimido y el archivo de destino va a ser


escrito como un fichero descomprimido. Cuando se crea la secuencia de compresión, puede
cambiar de dos maneras: puede ajustar el archivo de origen, porque es ahí donde los datos
comprimidos existe, y debe especificar el CompressionMode.Descomprimir para especificar
que se están descomprimiendo el envuelto stream:
// C#
GZipStream compStream
=
Nueva GZipStream(, sourceFile CompressionMode.Descomprimir);

Además, necesita cambiar la forma en que procesa el archivo para leer la secuencia de
compresión en lugar de desde el archivo de origen y escribir en el archivo directamente, en
lugar de a través de la secuencia de compresión:
// C#
Int = compStream theByte.ReadByte();
Mientras (theByte !=
-1)
{
DestFile.WriteByte((byte)theByte);
= compStream theByte.ReadByte();
}

En ambos casos (compresión o descompresión), la secuencia de compresión está diseñado


para envolver el stream que contiene o contendrá datos comprimidos). Si se leen o escriben
datos comprimidos es totalmente dependiente de si se comprime o descomprime.

Laboratorio: Comprimir y descomprimir un archivo existente


En esta práctica, podrá crear una aplicación de consola simple que leerá un archivo del
sistema de archivos y comprimirlos en un archivo nuevo. Si se produce un problema de
completar un ejercicio, los proyectos terminados están disponibles en el CD en la carpeta de
código.

    Ejercicio 1: Comprimir un archivo existente


En este ejercicio, va a comprimir un archivo existente en un nuevo archivo comprimido.
1.  Crear una nueva aplicación de consola denominada CompressionDemo.
2.  Agregar una de las importaciones (o de una  instrucción using en C#)
para System.IO y Espacios de nombres System.IO.Compression en el nuevo proyecto.
3.  Crear un nuevo método estático (o compartido en Visual Basic)
llamado comprimir- Archivo que toma dos cadenas: inFilename y outFilename. La firma del
método debe parecerse a esto:
// C#
Static void CompressFile(String, String) outFilename inFilename
{
}

4.  Dentro de este método Open de un  objeto FileStream (denominada  SourceFile)


abriendo el archivo especificado en el inFilename.
5.  Crear un nuevo  objeto FileStream (denominado destFile) creando un nuevo
archivo especificado en el outFilename.
6.  Crear un nuevo  objeto (denominado  compStream GZipStream), especificando
la destFile como el arroyo para escribir los datos comprimidos. Especificar
también que este será un com- pression stream. El código podría tener este
aspecto:

// C#
GZipStream compStream =
Nueva GZipStream(destFile, CompressionMode.Compress);

7.  Transmitir los datos en el archivo de código fuente en la secuencia de compresión


un byte a la vez.
El código podría tener este aspecto:
// C#
Int = theByte sourceFile.ReadByte();
Mientras (theByte != -1)
{
CompStream.WriteByte((byte)theByte);
= theByte sourceFile.ReadByte();
}

8.  Cerrar todos los arroyos antes de salir del método.


9.  En el  método Main del nuevo proyecto de consola, llame al  método
CompressFile con un archivo existente y un nuevo nombre de archivo
(normalmente termina con el archivo de origen
.Gz). La llamada podría tener este aspecto:

// C#
CompressFile(@"c:\boot.ini", @"c:\boot.ini.gz");

Nota  utilizando un archivo pequeño puede resultar en el archivo


comprimido sea más grande que el original debido a la sobrecarga de
compresión. Si utiliza un archivo de mayor tamaño, el tamaño del tejado
no causará tal palidez en el tamaño del archivo resultante

10.  Generar el proyecto y resolver cualquier error. Compruebe que la aplicación


de consola cre- ados el nuevo archivo comprimido comprobando
manualmente el archivo comprimido en el sistema de archivos. El archivo
debe leerse como un galimatías en un editor de texto (como el Bloc de notas).

Ejercicio 2: Descomprimiendo el archivo Nuevo

En este ejercicio, va a abrir el archivo creado en el ejercicio 1 y descomprimir el archivo en


un archivo nuevo.
1.  Abra el proyecto CompressionDemo creado en el ejercicio 1.
2.  Crear un nuevo método estático (o compartido en Visual Basic)
denominada descomprimir- Archivo que toma dos
cadenas: inFileName y outFileName. La firma del método debe parecerse a
esto:
// C#
Static void DecompressFile(String, String) outFilename
inFilename
{
}

3.  Dentro de este método Open de un  objeto FileStream (denominada


SourceFile) abriendo el archivo especificado en el inFilename, que será el
archivo comprimido que usted escribió en el ejercicio 1.
4.  Crear un nuevo  objeto FileStream (denominado destFile) creando un nuevo
archivo especificado en el outFilename.
5.  Crear un nuevo  objeto (denominado compStream GZipStream),
especificando la sourceFile como el arroyo para leer los datos comprimidos. Se
especifica también que este será un torrente de descompresión. El código podría
tener este aspecto:
// C#
GZipStream compStream =
Nueva GZipStream(, sourceFile CompressionMode.descomprimir);

6.  Transmitir los datos de la compresión de archivos en el archivo de destino un


byte a la vez. El código podría tener este aspecto:

// C#
Int = compStream theByte.ReadByte();
Mientras (theByte != -1)
{
DestFile.WriteByte((byte)theByte);
= compStream theByte.ReadByte();
}

7.  Cerrar todos los arroyos antes de salir del método.


8.  En el  método Main del nuevo proyecto de consola, llame al  método
UncompressFile y pasarle el nombre de archivo del archivo comprimido creado en
el ejercicio 1 y el nombre de un archivo que recibirá los datos descomprimidos. La
llamada podría ser algo como esto:

// C#
DecompressFile(@"c:\boot.ini.gz", @"c:\boot.ini.test");

9.  Generar el proyecto y resolver cualquier error. Compruebe que la aplicación de


consola cre- ates el nuevo archivo sin comprimir por abrirlo con el Bloc de notas.
Comparar el contenido del archivo en el archivo original para ver si son idénticos.

Resumen de la lección
■   La secuencia de compresión ( las clasesGZipStream y DeflateStream) puede
ser usada para comprimir o descomprimir los datos de hasta 4 GB.
■   La secuencia de compresión clases se utilizan para envolver otra secuencia en
la que se guardarán los datos comprimidos.

Examen de la lección
Puede utilizar las siguientes preguntas para poner a prueba tus conocimientos de la
información Les- hijo 3, "comprimir secuencias." Las preguntas también están
disponibles en el CD complementario si prefiere revisarlos en forma electrónica.
Nota   Respuestas
Las respuestas a estas preguntas y explicaciones de por qué cada opción de
respuesta es correcta o no se encuentran en la sección "Respuestas" al final
del libro.

1.  Cuando la  compresión de datos con la   clase DeflateStream, ¿cómo


especificar una secuencia en la que escribir datos comprimidos?
A.  Establezca la  propiedad BaseStream con la secuencia de destino, y
establezca la   propiedad pressionMode com- a la Compresión.
B.   Especificar la secuencia en la que escribir en el momento en que  se crea
el objeto DeflateStream
(Por ejemplo, en el constructor).
C.   Utilice el  método Write de la  clase DeflateStream.
D.   Registrarse para el  evento BaseStream de la  clase DeflateStream.
2.  ¿Qué tipos de datos pueden un GZipStream comprimir? (Seleccione todos los
que correspondan).
A.  Cualquier archivo
B.   Cualquier dato
C.   Cualquier dato menos de 4 GB de tamaño
D.   Cualquier archivo que no superan los 4 GB de tamaño
Lección 4: Trabajar con el almacenamiento aislado
Como nos estamos volviendo más y más conscientes de, programas de acceso irrestricto a un
ordenador no es una gran idea. La aparición de spyware, malware y virus nos dice que trabaja
en el recinto de seguridad limitada es un mundo mejor para la mayoría de los usuarios. Unfor-
lamentablemente, muchos programas todavía necesita guardar algún tipo de estado datos
sobre sí mismas. La manera de hacer esto puede ser algo tan inocuo como almacenar datos en
la memoria caché. Para colmar las necesidades de aplicaciones para guardar datos y el deseo
de los administradores y los usuarios para utilizar la configuración de seguridad más limitado,
el .NET Framework admite el concepto de almacenamiento aislado.

Después de esta lección, será capaz de:


■   Tener acceso al almacenamiento aislado para el almacenamiento de datos
utilizando el programa IsolatedStorageFile
Clase.
■   Crear archivos y carpetas en el almacenamiento aislado mediante
el IsolatedStorageFileStream
Clase.
■   Acceso diferentes tiendas dentro del almacenamiento aislado por usuario y
por equipo utilizando la   clase IsolatedStorageFile.
Lección Tiempo estimado: 15 minutos.

¿Qué es el almacenamiento aislado?


Ejecución de código con privilegios limitados tiene muchas ventajas, dada la presencia de
preda- tores que endosar los virus y spyware en sus usuarios. .NET Framework tiene varios
mecanismos para tratar la ejecución como usuarios con menos privilegios. Porque la mayoría
de las aplicaciones tienen que lidiar con almacenar algunos de su estado de forma persistente
(sin recurrir a bases de datos u otros medios), sería bueno tener un lugar para almacenar infor-
mación que era seguro para usar sin tener que comprobar si la aplicación tiene suficientes
derechos para guardar datos en el disco duro. Esa solución es lo que el almacenamiento
aislado está diseñado para ofrecer.
Mediante el almacenamiento aislado para guardar sus datos, usted tendrá acceso a
un lugar seguro para almacenar información sin tener que recurrir a que los
usuarios tengan que conceder acceso a determinados archivos o carpetas en el
sistema de archivos. La ventaja principal de usar el almacenamiento aislado es que
su apli- cación se ejecutará independientemente de si se está ejecutando bajo,
limitado, parcial o plena confianza.

Nota   .NET 2.0


En .NET 2.0, existen nuevos tipos de aplicaciones que están diseñadas para ser
desplegadas y se instala desde páginas Web denominado "aplicaciones Click-
Once" o, a veces, de "aplicaciones de cliente inteligente." Estos nuevos tipos de
aplicaciones están pensadas para resolver el despliegue de aplicaciones a través
de una compañía o empresa.

Más info  Click-Once


aplicaciones
Para obtener más información sobre aplicaciones, consulte Click-
Once http://msdn2.microsoft.com/en-us/
Biblioteca/14
2dbbz4.aspx.
La     clase IsolatedStorageFile
La  clase IsolatedStorageFile proporciona la funcionalidad básica para crear archivos y
carpetas de almacenamiento aislado. La tabla 2-34 muestra el más
importante IsolatedStorageFile static/ métodos compartidos.
Tabla 2-34  IsolatedStorageFile   estática/Métodos compartidos

Nombre Descripción
GetMachineStoreForApplication  recupera una máquina-nivel de
almacenamiento para el clic- una
aplicación que llama
GetMachineStoreForAssembly  recupera un almacén a nivel de la máquina
para la asamblea que llamó
GetMachineStoreForDomain  recupera una máquina-nivel de almacenamiento
para el AppDomain
Dentro de la corriente general que llama
GetStore  recupera almacena cuyo ámbito de aplicación
se basa en el
 Enumerador IsolatedStorageScope
GetUserStoreForApplication  recupera un almacén a nivel de usuario
para la aplicación que llama Click-Once
GetUserStoreForAssembly  recupera un almacén a nivel de usuario para
la asamblea que llamó
GetUserStoreForDomain  recupera un almacén a nivel de usuario
para AppDomain
Dentro de la corriente general que llama
La tabla 2-35 muestra las más importantes  propiedades de IsolatedStorageFile.
Tabla 2-35  IsolatedStorageFile
Propiedades
Nombre Descripción
   La Click-Once ApplicationIdentity la identidad de la aplicación que
orienta el almacenamiento aislado
   La identidad del ensamblado AssemblyIdentity que orienta el
almacenamiento aislado

Tabla 2-35
IsolatedStorageFile   Propiedades
Nombre Descripción
CurrentSize  el tamaño actual de los datos almacenados en este
almacenamiento aislado
DomainIdentity L aidentidad del AppDomain que orienta el
almacenamiento aislado
Tamañomáximo  el tamaño máximo de almacenamiento para este
almacenamiento aislado
Alcance  el  valor de la enumeración
IsolatedStorageScope que describe el alcance
de este almacenamiento aislado

La tabla 2-36 muestra los  métodos más importantes de IsolatedStorageFile.


Tabla 2-36      métodos
IsolatedStorageFile
Nombre Descripción
Cerrar  Cierra una instancia de una
tienda CreateDirectory  crea un directorio dentro
de la tienda DeleteDirectory  elimina un
directorio dentro de la tienda DeleteFile
elimina un archivo dentro de la tienda.
GetDirectoryNames  obtiene una lista de nombres de directorios dentro de la
tienda que coincidan con una máscara de directorio
GetFileNames  obtiene una lista de los nombres de archivo dentro de la
tienda que coincidan con una máscara de archivo
Quitar  Quita todo el almacén del sistema actual

Cómo crear una tienda


Antes de poder guardar los datos en el almacenamiento aislado, debe determinar cómo el
alcance de los datos que desee en su tienda. Para la mayoría de aplicaciones, tendrás que
elegir uno de los dos métodos siguientes:
■   General/máquina,  este método crea un almacén para guardar
información que es- pecífico al ensamblado que realiza la llamada y el equipo
local. Este método es útil para cre- explotación de datos a nivel de aplicación.
■   General/Usuario  Este método crea un almacén para guardar información
que es específica para el ensamblado que realiza la llamada y el usuario
actual. Este método es útil para crear datos de nivel de usuario.

La creación de una asamblea a nivel de máquina/store se logra llamando a la  clase ageFile


IsolatedStor- GetMachineStoreForAssembly del método, como se muestra aquí:
// C#
IsolatedStorageFile IsolatedStorageFile.GetMachineStoreForAssembly
machineStorage =();

Este almacén de almacenamiento aislado es específico a la Asamblea que está llamando, si


ese es el ejecutable principal en un proyecto de Microsoft Windows Forms o una biblioteca de
vínculos dinámicos (DLL) que es parte de un proyecto mayor.
La creación de una asamblea/almacén de nivel de usuario es similar, pero el método es
llamado  - StoreForAssembly GetUser, como se muestra en este ejemplo:
// C#
IsolatedStorageFile userStorage =
IsolatedStorageFile.GetUserStoreForAssembly();

La tienda es en este caso en el ámbito del usuario específico que está ejecutando la asamblea.
Si necesita especificar el usuario para el almacén, usted necesitará usar la suplantación (que se
describe en el capítulo 12).

Nota   tienda de nivel


de aplicación
Para las aplicaciones implementadas, Click-Once almacenamiento aislado
también admite un almacén de nivel de aplicación que soporta tanto a nivel de
la máquina y una tienda tienda a nivel de usuario. Nivel de aplicación almacena
sólo funcionan dentro de Click-Once aplicaciones porque la ejecución tiene su
propio conjunto de pruebas que podrían o no ser válidos para aplicaciones
locales.

Más info El  almacenamiento aislado

Para obtener más información acerca del almacenamiento aislado, consulte


las siguientes fuentes:
■   "Introducción al almacenamiento aislado" por Microsoft Corporation
(disponible en línea en http://msdn.microsoft
.Com/library/default.asp?URL=/library/en-
us/cpguide/html/cpconintroductiontoisolatedstorage.asp ).
■ La programación de Windows Forms en C# o VB.NET,  por Chris vende
(Addison-Wesley, 2004). Lea el capítulo 11, páginas 426-430,
"Configuración de la aplicación," para una explicación de cómo los
diferentes tipos de tiendas afectan a usuarios y usuarios móviles.

La   clase IsolatedStorageFileStream


La IsolatedStorageFileStream clase encapsula una corriente que se utiliza para crear archivos
de almacenamiento aislado. Esta clase se deriva de la  clase FileStream, para su uso después
de la creación es casi idéntica a la  clase FileStream. La tabla 2-37 muestra el más
importante IsolatedStorageFileStream propiedades.
Tabla 2-37
IsolatedStorageFileStream  Propieda
des
Nombre Descripción
CanRead  determina si el stream es compatible con la lectura.
(Heredado de la  clase Stream).
CanSeek  determina si la secuencia admite la función de búsqueda.
(Heredado de la  clase Stream).
CanTimeout  determina si la secuencia puede agotar el tiempo de
espera. (Heredado de la  clase Stream).
CanWrite  determina si la secuencia puede ser escrito. (Heredado
de la  clase Stream).
Manejar  obtiene la secuencia subyacente de identificador de
archivo. (Heredado de la  clase FileStream.)
Length   obtiene la longitud (en bytes) de la secuencia. (Heredado
de la
 Clase
Stream).
Name  Obtiene el nombre del archivo. (Heredado de la clase
FileStream
C
la
se
).
   Obtiene o establece la posición del cursor virtual para determinar
dónde en el arroyo la posición actual. El valor
de posición no puede ser mayor que la longitud de la
secuencia. (Heredado de la  clase Stream).
ReadTimeout Obtiene o establece la secuencia de tiempo de espera
para las operaciones de lectura. (Inher- ited de la  clase
Stream).
WriteTimeout  obtiene o establece la secuencia de tiempo de
espera para las operaciones de escritura. (Heredado
de la  clase Stream)

La tabla 2-38 muestra el más importante IsolatedStorageFileStream métodos.


Tabla 2-38  IsolatedStorageFileStream  métodos

Nombre Descripción
Cerrar  Cierra la secuencia y libera todos los recursos asociados con
ella. (Heredado de la  clase Stream).
A ras Clears cualquier buffers en el stream y obliga a cambios a
escribir en el sistema subyacente o dispositivo. (Heredado de
la  clase Stream).
El bloqueo  impide que otros procesos puedan cambiar todo o
parte del archivo. (Heredado de la  clase FileStream.)
Leer Performs una lectura secuencial de un número especificado
de bytes desde la posición actual y actualiza la posición al
final de la lectura tras la finalización de la operación.
(Heredado de la  clase Stream).
ReadByte Performs la lectura de un solo byte y actualiza la posición
moviendo por uno. Este método es idéntico a llamar
a Read para leer de un solo byte. (Heredado de la  clase
Stream).
Seek   establece la posición dentro de la secuencia. (Heredado de
la secuencia
Cla
se).
SetLength Especifica la longitud de la secuencia. Truncará la corriente si
la nueva longitud es menor que la longitud anterior y
expandirá la corriente si la verdad es lo contrario. (Heredado
de la  clase Stream).
   Permite desbloquear otros procesos para cambiar una parte o la
totalidad del fichero subyacente. (Heredado de la  clase
FileStream.)
Escribir  escribe la información en la secuencia como un número de
bytes y actualiza  la posición actual para reflejar la nueva
posición de escritura. (Heredado de la  clase Stream).
WriteByte  escribe un byte a la secuencia y actualiza la posición. Este
método es idéntico a llamar escribir con un solo byte.
(Heredado de la  clase Stream).

Lectura y escritura de los datos en el almacenamiento


aislado
Crear datos en el almacenamiento aislado es parecido a escribir cualquier otro tipo de datos en
el archivo Sys- tem, con la excepción de que debe utilizar la  clase
IsolatedStorageFileStream. Usted cre- se comió un nuevo IsolatedStorageFileStream objeto
creando una nueva instancia de la clase, especificando un nombre de archivo relativo e
incluyendo un objeto Store para especificar que tienda a incluir dentro. El siguiente fragmento
de código proporciona un ejemplo:
// C#
IsolatedStorageFile userStore =
IsolatedStorageFile.GetUserStoreForAssembly();

IsolatedStorageFileStream userStream =
Nueva IsolatedStorageFileStream("UserSettings.set", FileMode.Create, userStore);

Después de crear una tienda, puede crear una secuencia de archivos especificando el nombre
de archivo, el FileMode utilizar en abrir o crear el archivo y almacenar el objeto que ha
creado. Una vez que tenga una instancia de la  clase IsolatedStorageFileStream, trabajar con
ella es idéntica a trabajar con cualquier secuencia de archivos (como hicimos en la lección 2).
Esto es porque IsolatedStorageFileStream deriva de FileStream. El siguiente fragmento de
código proporciona un ejemplo:
// C#
StreamWriter userWriter = new StreamWriter(userStream);
UserWriter.WriteLine("User Prefs");
UserWriter.Close();

En este ejemplo, se utiliza un  objeto StreamWriter estándar para escribir datos en la


secuencia. De nuevo, una vez que tenga el  objeto userStream, trabajar con ella es idéntica a
trabajar con cualquier archivo en el sistema de archivos.

La preparación para leer los datos es tan simple como crear un objeto Stream abriendo el
archivo en lugar de crear, como se muestra aquí:

// C#
IsolatedStorageFileStream userStream =
Nueva IsolatedStorageFileStream("UserSettings.set", FileMode.Open, userStore);

Simplemente cambiando la FileMode  Abrir, puede abrir el archivo, en lugar de crear uno


nuevo. A diferencia de la interfaz de programación de aplicaciones (API) para archivos
almacenados arbi- trarily en el sistema de archivos, la API de archivos en el almacenamiento
aislado no admite la comprobación de la existencia de un archivo directamente
como archivo. no existe. En su lugar, se debe pedir a la tienda para obtener una lista de los
archivos que coinciden con una máscara de archivo. Si la encuentra , puede abrir el archivo,
como se muestra en este ejemplo:
// C#
String[] files = userStore.GetFileNames("UserSettings.set");
Si (archivos.Length ==
0)
{
Console.WriteLine("No se guardan datos para este usuario");
}
Otra cosa
{
// ...
}

Puede utilizar el  método GetFileNames de la  clase IsolatedStorageFile para obtener una lista
de los archivos que coincidan con el nombre de archivo (u otras máscaras de archivo
como *.set). Esta sustitución es suficiente para probar la existencia del archivo antes de tr
ying para leer, eliminar o reemplazar el archivo.

Cómo utilizar directorios de almacenamiento aislado


Usted no se limita a almacenar los datos como un conjunto de archivos en el almacenamiento
aislado; en su lugar, también puede crear directorios para almacenar datos dentro. Antes de
poder crear archivos en un directorio, debe llamar al método CreateDirectory de la  clase
IsolatedStorageFile, tal y como se muestra aquí:
// C#
UserStore.CreateDirectory("SomeDir");

IsolatedStorageFileStream userStream = new


IsolatedStorageFileStream(@"SomeDir\UserSettings.set",
FileMode.Create,
UserStor
e);

En este ejemplo, se crea el directorio antes de intentar crear un nuevo archivo en ese
directorio. Si no crea el directorio en primer lugar, obtendrá una excepción de análisis de ruta.
Los directorios son tratados como archivos que para probar su existencia, debe utilizar un
método que devuelve una matriz de cadenas que coincidan con una máscara.
La GetDirectoryNames método de la  clase IsolatedStorageFile le permite encontrar un
directorio existente antes de intentar crear:
// C#
String[] = directorios
UserStore.GetDirectoryNames("SomeDir");

Si (directorios.Length == 0)
{
UserStore.CreateDirectory("SomeDir");
}

Obteniendo los nombres de directorio que coincida con su nombre, usted puede hacer una
prueba para ver si existe el directorio y crear sólo si no estaba ya creado.

La   clase IsolatedStorageFilePermission


El IsolatedStorageFilePermission clase encapsula el permiso puede concederse a código para
permitir el acceso a almacenamiento aislado. La tabla 2-39 muestra
el IsolatedStorageFilePermission propiedades más importantes.
Tabla 2-39  IsolatedStorageFilePermission   Propiedades

Nombre Descripción
UsageAllowed  obtiene o establece los tipos de uso permitido.
Opciones UserQuota  obtiene o establece el tamaño total de
almacenamiento permitido por usuario

Permitir el almacenamiento aislado


Ante una asamblea (o aplicación) pueden hacer uso del almacenamiento aislado, debe tener
permiso para hacerlo. Para asegurarse de que todo el código con el que está trabajando tiene
los permisos suficientes, será necesario solicitar ese permiso. Esta tarea se puede llevar a cabo
mediante la anotación de tu clase o método con IsolatedStorageFilePermission, tal como se
muestra a continuación:
// C# [IsolatedStorageFilePermission(SecurityAction.La demanda)]
Clase Programa
{
// ...
}

IsolatedStorageFilePermission se utiliza para garantizar que cualquier llamada a trabajar con


stor aislados- edad dentro de esta clase tendrá éxito. Si su código no tiene los permisos
necesarios para tener acceso al almacenamiento aislado, incluido este atributo permitirá a los
administradores a comprender mejor lo que necesita este permiso conjuntos y les permiten
agregar el permiso si es necesario.

Este permiso también admite varias propiedades  que se pueden utilizar para modificar la
forma de iso- lacionadas con el almacenamiento se utiliza, como se muestra en este ejemplo:
// C# [IsolatedStorageFilePermission(SecurityAction.La demanda,
Opciones UserQuota=1024,
UsageAllowed=IsolatedStorageContainment.AssemblyIsolationByUser)]
Programa de clase
{
// ...
}

Añadiendo opciones userquota y UsageAllowed describe el sistema de seguridad cómo este


código tiene la intención de utilizar el almacenamiento aislado.

Laboratorio: almacenar y recuperar los datos desde el


almacenamiento aislado
En este laboratorio, se creará un nuevo archivo en el almacenamiento aislado y, a
continuación, lea el archivo de almacenamiento aislado. Si se produce un problema de
completar un ejercicio, la com- pletó proyectos están disponibles en el CD en la carpeta de
código.
    Ejercicio 1: Crear un archivo de almacenamiento aislado
En este ejercicio, creará un nuevo archivo de almacenamiento
aislado.
1.  Crear una nueva aplicación de consola denominada IsolatedStorageDemo.
2.  Agregar una de  las importaciones  (o de una    instrucción using en C#) para
System.IO  y
System.IO.IsolatedStorage espacios de nombres  en el nuevo proyecto.
3.  En el  método Main del nuevo proyecto, cree una nueva instancia de la  clase
denominada ageFile IsolatedStor-  userStore que tiene el ámbito del usuario
actual y general. El código podría tener este aspecto:
// C#
IsolatedStorageFile userStore =
IsolatedStorageFile.GetUserStoreForAssembly();

4.  Crear una nueva instancia del  objeto IsolatedStorageFileStream, pasando el


nombre
UserSettings.set y la nueva tienda, como se muestra en este ejemplo:
// C#
IsolatedStorageFileStream userStream = new
IsolatedStorageFileStream("UserSettings.set", FileMode.Create, userStore);

5.  Utilice StreamWriter para escribir algunos datos en la nueva secuencia, y


cerrar el escritor cuando termine. El código podría tener este aspecto:
// C#
StreamWriter userWriter = new StreamWriter(userStream);
UserWriter.WriteLine("User Prefs");
UserWriter.Close();

6.  Genere el proyecto y resolver cualquier error. Compruebe que la aplicación de


consola cre- ados el nuevo archivo comprobando el archivo en el sistema. El
archivo permanecerá en un directorio en C:\Documents and
Settings\<usuario>\Local Settings\Application Data\IsolatedStorage. Este
directorio es un directorio de caché, así que usted encontrará algunos nombres
de directorios generados por la máquina que puede parecer un galimatías.
Usted debe encontrar el archivo si profundizar en el directorio llamado
AssemFiles.
    Ejercicio 2: Lectura de un archivo de almacenamiento aislado
En este ejercicio, usted leerá el archivo creado en el ejercicio
1.
1.  Abra el proyecto creado en el ejercicio 1 (IsolatedStorageDemo).
2.  Después de que el código del ejercicio 1, agregue el código para comprobar si
el archivo existe en la tienda y mostrar un mensaje de la consola si no se
encontraron archivos. El código podría tener este aspecto:
' VB
Archivos dim() As String = userStore.GetFileNames("UserSettings.set") si los
archivos.Length = 0 Then
Console.WriteLine("No se guardan datos para este usuario")
End If
// C#
String[] files = userStore.GetFileNames("UserSettings.set");
Si (archivos.Length == 0)
{
Console.WriteLine("No se guardan datos para este usuario.");
}

3.  Si el archivo se encuentra, se crea un nuevo  objeto IsolatedStorageFileStream


que abre el archivo creado en el ejercicio 1. Crear un StreamReader para leer
todo el texto del archivo en una variable de cadena local nuevo. El código
podría tener este aspecto:

// C#
UserStream = new
IsolatedStorageFileStream("UserSettings.set", FileMode.Open,
userStore);
StreamReader userReader = new StreamReader(userStream);
Contenido de cadena = userReader.ReadToEnd();

4.  Agregue una línea a la consola que muestra la cadena que ha creado  desde


el arroyo- Reader.
5.  Generar el proyecto y resolver cualquier error. Compruebe que la aplicación
de consola muestra el contenido del archivo en la línea de comandos.

Resumen de la lección
■   La  clase IsolatedStorageFile puede utilizarse para acceder a zonas seguras
para almacenar datos para las asambleas y los usuarios.
■   El IsolatedStorageFileStream clase puede usarse para leer y escribir datos en
estos almacenes seguros.
■   El IsolatedStorageFileStream clasederiva de la  clase FileStream, así que
alguno de los archivos de la clase crea puede usarse como cualquier otro
archivo en el sistema de archivos.
■   El IsolatedStorageFilePermission clase puede utilizarse para asegurarse de
que el código tiene los permisos adecuados para actuar sobre el
almacenamiento aislado.

Examen de la lección
Puede utilizar las siguientes preguntas para poner a prueba su conocimiento de la información
en la lección 4, "Trabajar con el almacenamiento aislado." Las preguntas también están
disponibles en el CD complementario si prefiere revisarlos en forma electrónica.

Nota   Respuestas
Las respuestas a estas preguntas y explicaciones de por qué cada opción de
respuesta es correcta o no se encuentran en la sección "Respuestas" al final
del libro.

1.  ¿Qué métodos se pueden utilizar para crear un nuevo  objeto


IsolatedStorageFile? (Seleccione todos los que correspondan).
A.  IsolatedStorageFile.GetStore
B.   IsolatedStorageFile.GetMachineStoreForAssembly
C.   IsolatedStorageFile.GetUserStoreForAssembly
D.   IsolatedStorageFile constructor
2.  Un  objeto IsolatedStorageFileStream puede usarse como cualquier
otro  objeto FileStream.
A.  True
B.   False

Repaso del capitulo


A nuevas prácticas y reforzar las habilidades aprendidas en este capítulo, puede
per- en las siguientes tareas:
■   Revisar el resumen del capítulo.
■   Revisar la lista de términos clave que se introdujo en este capítulo.
■   Complete los casos. Estos escenarios configurar situaciones del mundo real
involv- ción de los temas de este capítulo, y le pedimos a usted para crear una
solución.
■   completar las prácticas sugeridas.
■   tomar un examen de práctica.

Resumen del capítulo


■   El FileSystemInfo clases (FileInfo y DirectoryInfo) se puede utilizar para
navegar por el sistema de archivos y obtener información acerca de los
archivos y directorios, incluidos el tamaño, sellos de tiempo, nombres,
atributos, y así sucesivamente.
■   La  clase File proporciona un punto de partida para crear o abrir archivos en
el sistema de archivos.
■   La  clase FileStream puede utilizarse para transmitir datos dentro o fuera del
sistema de archivos.
■   Las     clases StreamReader y StreamWriter son instrumentales en relación
con el movimiento de datos dentro y fuera de corrientes,
incluidas FileStreams, s, y MemoryStream IsolatedStorageFileStreams.
■   El .NET Framework admite dos clases para la compresión de datos:  GZip-
la  clase Stream y la  clase DeflateStream.
■ El     almacenamiento aislado proporciona un lugar seguro para guardar datos
acerca de un ensamblado específico, usuario o aplicación. Puesto que el
almacenamiento aislado requiere tan pocos derechos de seguridad, es la mejor
manera de almacenar los datos sin tener que conceder una aplicación con
derechos permisivos para el sistema de un usuario.

Términos clave
¿ Sabes lo que significan estos términos clave? Puede verificar sus respuestas
mediante la búsqueda de los términos en el glosario al final del libro.
■   desinflar

■   Sistema de archivo
■ El   almacenamiento aislado
■   gzip

Casos
En los siguientes casos, podrá aplicar lo que ha aprendido acerca de los archivos y directorios.
Usted puede encontrar las respuestas a estas preguntas en la sección de "respuestas" al final de
este libro.

Caso práctico 1: Guardar los ajustes de usuario


Usted es un desarrollador recién contratado por una gran compañía de seguros. Su jefe le pide
que agregue un cuadro de diálogo de preferencias de usuario para una aplicación de Windows
Forms. Su Manager también le informa de que los usuarios no se ejecutan como
administradores de sus propias máquinas, pero como usuarios limitados. El diálogo se añade a
un jefe de ventas, aplicaciones y usuarios suelen compartir ordenadores. Es muy importante
que el personal de ventas puede ver cada una de las derivaciones del otro.

Preguntas
Responda las siguientes preguntas para el administrador.
1.  En un nivel alto, describir cómo se podría almacenar la información para los
usuarios.
2.  Cómo sería que si cambio de diseño necesarios para comprimir los datos de
preferencia para ayudar a limitar el tamaño de los datos?

Caso práctico 2: Monitorización de servidores antiguos


Usted es un desarrollador que trabaja para una gran organización de TI. Tiene un gran número
de sistemas que están supuestos a ser llevados sin conexión en el próximo mes. Su jefe ha
escuchado que los servidores de archivos siguen siendo utilizados por algunos usuarios, pero
está teniendo dificultades para encontrar quién está guardando los datos a los servidores. Ella
le pide que escriba una pequeña aplicación que se puede instalar en los servidores que se le
envíe un mensaje de correo electrónico cada vez que se guarda un archivo nuevo o creados en
el servidor.

Entrevistas
La siguiente es una lista de personal de la empresa entrevistados y sus
declaraciones:
■   IT Manager  "Estoy seguro de que algunos usuarios siguen usando los
servidores sin saberlo. Si pudiéramos controlar los servidores por una semana,
así que puedo migrar los usuarios a nuestros nuevos servidores de ficheros,
podríamos ahorrar un montón de dinero en los gastos de mantenimiento".

■   Desarrollo Manager  "Uno de nuestros desarrolladores intentó escribir en


este archivo-monitoring app hace varias semanas, pero finalmente se rindió.
Él era mantener una lista de todos los archivos del sistema y análisis del
sistema cada cinco minutos, y que está tomando demasiado largo".

Preguntas
Responda las siguientes preguntas para el administrador.
1.  ¿Qué tipo de aplicación se puede crear para atender la necesidad del
departamento de TI?
2.  ¿Cómo va a supervisar los servidores de archivos?
3.  ¿Cómo va a lidiar con el gerente de desarrollo está preocupado con el
rendimiento?

Prácticas recomendadas
Que le ayudarán a dominar los objetivos contemplados en el presente capítulo, complete las
siguientes tareas.
Crear un archivo de aplicación Buscador
Para esta tarea, se deben completar un mínimo de prácticas 1 y 2. Para más expe- riencia con
las secuencias de compresión, por favor complete la Práctica 3.
■ La   Práctica 1  Crear una aplicación que se va a buscar una unidad para un
archivo en particular.
■ La   Práctica 2  Agregar código para ver el archivo utilizando la  clase
FileStream para mostrar el archivo en una ventana de texto.
■   Práctica 3.  Por último, añadir una función para comprimir un archivo
cuando se encuentra.

Crear una configuración simple Storage


Para esta tarea, se deben completar un mínimo de prácticas 1 y 2. Para comprender cómo el
usuario y datos a nivel de máquina difieren en el almacenamiento aislado, completar la
Práctica 3.
■ La   Práctica 1  Crear una aplicación de Windows Forms, que permite a los
usuarios guardar y almacenar los datos en el almacenamiento aislado.
■ La   Práctica 2  Probar la aplicación de Windows Forms se ejecuta en
distintas cuentas de usuario.
■ La   Práctica 3  Modificar la aplicación  para almacenar algunos datos a
nivel de la máquina para ver si ese dato es el mismo para todos los usuarios.

Tomar un Test de práctica


Los tests de práctica en este libro's Companion CD ofrecen muchas opciones. Por ejemplo,
puede hacerse la prueba en un solo examen objetivo, o puede probar usted mismo en todos los
exámenes de certificación 70-536 contenido. Puede configurar la prueba para que simula
cuidadosamente la expe- riencia de tomar un examen de certificación, o puede configurarlo en
modo de estudio, de modo que usted puede mirar las respuestas correctas y explicaciones
después de responder a cada pregunta.

Más info  práctica de pruebas


Para obtener más información acerca de la prueba de la práctica todas las
opciones disponibles, consulte la sección "Cómo usar los tests de práctica"
La sección en este manual de introducción.
Capítulo 3
Buscar, modificar y Codificación de texto

El procesamiento de texto es una de las tareas de programación más comunes. Entrada de usuario
es habitualmente en formato de texto, y podría tener que ser validados, esterilizado, y reformatear.
A menudo, los desarrolladores necesitan para procesar archivos de texto generado a partir de un
sistema heredado para extraer datos importantes. Estos sistemas heredados a menudo utilizan
técnicas de codificación no estándar. Además, los desarrolladores pueden necesitar para la salida
de los archivos de texto en formatos específicos para introducir datos en un sistema heredado.
En este capítulo se describe cómo utilizar expresiones regulares para validar los datos de entrada,
cambiar el formato del texto, y extraer datos. Además, en este capítulo se describen los diferentes
tipos de codificación utilizado por archivos de texto.

Objetivos del examen en este capítulo:

■   Mejorar las capacidades de manejo de texto de una aplicación de .NET Framework


(consulte
 El espacio de nombres System.Text),  y buscar, modificar y controlar el texto en
un marco de trabajo .NET Application usando expresiones regulares. (Consultar
el  espacio de nombres System.RegularExpressions.)
❑    clase StringBuilder
❑    clase Regex
❑   coinciden con clase y  clase MatchCollection
❑ El   Grupo clase y  clase GroupCollection
❑   Codificar texto mediante  clases de codificación
❑   decodificar el texto mediante  clases de descodificación
❑   Capturar clase y  clase CaptureCollection

Las lecciones de este capítulo:

■ La   Lección 1: Formar Expresiones regulares . .. . . . . . . . . . . . 145


■ La   Lección 2: la codificación y la descodificación. . . . . .. . . . . . . . . . . . . . . . . 171

143
Antes de empezar
Para completar las lecciones de este capítulo, debe estar familiarizado con Microsoft
Visual
Basic o en C# y se sienten cómodos realizando las siguientes tareas:
■   Crear una aplicación de consola en Microsoft Visual Studio, usando Visual Basic o
C#.
■   Sistema de agregar referencias a bibliotecas de clases para un proyecto.
■   leer y escribir archivos y arroyos.

Lección 1: formar expresiones regulares.


Los desarrolladores a menudo necesitan para procesar texto. Por ejemplo, usted puede ser que
necesite para procesar la entrada de un usuario para quitar o reemplazar caracteres especiales. O
puede que necesite el pro- ceso de texto que ha sido resultado de una aplicación heredada a integrar
la aplicación con un sistema existente. Durante décadas, UNIX y Perl, los desarrolladores han
utilizado una técnica compleja pero eficiente para procesar texto: expresiones regulares.
Una expresión regular es un conjunto de caracteres que puede ser comparado con una cadena para
deter- minar si la cadena cumple con los requisitos de formato especificado. También puede
utilizar la reglamenta- ción es lar las expresiones para extraer porciones de texto o para reemplazar
el texto. Para tomar decisiones basadas en texto, puede crear expresiones regulares que coinciden
con cadenas compuesto enteramente de números enteros, cadenas que contengan sólo letras
minúsculas, o cadenas que coincidan con entrada hexadecimal. También puede extraer porciones
clave de un bloque de texto, que se puede utilizar para extraer el estado de una imagen o la
dirección del usuario enlaces de una página HTML. Por último, puede actualizar el texto utilizando
expresiones regulares para cambiar el formato del texto o eliminar caracteres no válidos.
Después de esta lección, será capaz de:
■   Utilizar expresiones regulares para determinar si una cadena coincide con un patrón
específico.
■   Utilizar expresiones regulares para extraer datos desde un archivo de texto.
■   Utilizar expresiones regulares para reformatear datos de texto.
Lección Tiempo estimado: 45 minutos

Cómo utilizar expresiones regulares para la coincidencia de patrón


Para permitir a usted para probar expresiones regulares, cree una aplicación de consola
denominada TestRegExp que acepta dos cadenas como entrada y determina si la primera cadena
(una expresión regular) coincide con la segunda cadena. La siguiente aplicación de consola, que
utiliza el  espacio de nombres System.Text.RegularExpressions, realiza esta comprobación
utilizando la estática System.Text.RegularExpressions.Regex. método IsMatch y muestra los
resultados en la consola:

// C#
Utilizando System.Text.RegularExpressions;

TestRegExp namespace
{
La clase Class1
{
[STAThread]
Static void main(String[] args)
{
Si (Regex.IsMatch(args[1], args[0])) Console.WriteLine("Input coincide con la expresión regular.");
Otra cosaConsole.WriteLine("Input no coincide con la expresión regular.");

A continuación, ejecute la aplicación determinando si la expresión regular "^\d{5}$" coincide con


la cadena "12345" o "1234". La expresión regular no tiene sentido ahora, pero al final de la
lección. El resultado  debe parecerse al siguiente:
C:\>TestRegExp ^\d{5}$ 1234
La entrada no coincide con la expresión regular.

C:\>TestRegExp ^\d{5}$ 12345


Entrada coincide con la expresión regular.

Como demuestra este código, el regex. método IsMatch compara una expresión regular a una
cadena y devuelve VERDADERO si la cadena coincide con la expresión regular. En este ejem- plo,
"^\d{5}$" significa que la cadena debe tener exactamente 5 dígitos numéricos. Como se muestra en
la Figura 3-1, el carat ("^") representa el inicio de la cadena "\d" significa dígitos numéricos, "{5}"
indica cinco dígitos numéricos secuenciales, y "$" representa el final de la cadena.

Coincidir con principio de entrada


Coincidir sólo dígitos numéricos
Coincidir exactamente 5 caracteres.
Coincidencia al final de la entrada

^\d{5}$
Figura 3-1 El  análisis de una expresión regular

Si quita el primer carácter de la expresión regular, puede cambiar drásticamente el sentido de


la trama. La expresión regular "\d{5}$" siempre coinciden con los números de cinco dígitos
válidos, tales como "12345". Sin embargo, también coinciden con la cadena de entrada
"abcd12345" o "DROP TABLE Customers -- 12345". De hecho, la modificación de la
expresión regular coincide con cualquier cadena de entrada que termina en cualquier número
de cinco dígitos.

Importantes  incluyen las principales carat


Cuando se valida la entrada, olvidando los principales carat puede exponer una
vulnerabilidad de seguridad. Use peer las revisiones de código para limitar el riesgo de
error humano.
Cuando se valida la entrada, comenzar siempre expresiones regulares con un carácter
"^" y terminan con "$". Este sistema garantiza que el input coincide exactamente con la
expresión regular especificada y no contienen simplemente una  entrada
correspondiente.

Las expresiones regulares pueden ser utilizadas para hacer coincidir los patrones de entrada
complejos, demasiado. La siguiente expresión regular coincide con las direcciones de correo
electrónico:
^([\w-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w]+\).+) ([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$

Las expresiones regulares son una manera muy eficaz para comprobar la entrada del usuario;
sin embargo, el uso de expresiones regulares tiene las siguientes limitaciones:
■   Las expresiones regulares son difíciles de crear, a menos que esté muy
familiarizado con el formato. Si tiene años de experiencia en programación en
Perl, no tendrás ningún problema usando expresiones regulares en código C#. Sin
embargo, si usted tiene una copia-
Terreno en Visual Basic Scripting, el formato críptico de expresiones regulares que
inicialmente parecen completamente ilógico.
■   Creación de expresiones regulares puede  ser confuso, pero la lectura de las
expresiones regulares es definitivamente.  Hay una buena probabilidad de que
otros programadores se dan errores en expresiones regulares al realizar una revisión de
código entre pares. Más
Complejo la expresión regular, mayor será la probabilidad de que la estructura de la
expresión contiene un error que será ignorada.
Las secciones que siguen describen estos y otros aspectos de la expresión regular pat- tern
matching en más detalle. A medida que lea estas secciones, experimentar con- dif erentes
tipos de expresiones regulares usando la aplicación TestRegExp.
Más info  expresiones
regulares.
Libros enteros han sido escritos sobre las expresiones regulares, y esta lección
sólo pueden rayar la superficie. La información proporcionada en esta lección
debería ser suficiente para el examen. Sin embargo, si usted desea aprender
más acerca de las características avanzadas de las expresiones regulares, lea la
sección "Elementos del lenguaje de expresiones regulares" en la Referencia
general de .NET Framework en http://msdn.microsoft.com/library/  en-
us/cpgenref/html/cpconRegularExpressionsLanguageElements.asp .

Cómo utilizar texto simple coincidencia


El uso más sencillo de las expresiones regulares es determinar si una cadena
coincide con un patrón. Por ejemplo, la expresión regular "abc" coincide con la
cadena "abc", "abcde", o "yzabc" porque cada uno de los Strings contiene la
expresión regular. Los comodines no son necesarias.

Cómo hacer coincidir el texto en ubicaciones específicas


Si usted desea hacer coincidir el texto que comienza en el primer carácter de una
cadena, inicie la expresión regular con un símbolo "^". Por ejemplo, la expresión
regular "^abc" coincide con la cadena "abc" y "abcde", pero no coincide con
"yzabc". Hacer coincidir el texto que termina en el último carácter de una cadena,
coloque un símbolo "$" al final de la expresión regular. Por ejemplo, la expresión
regular "abc$" coincide con "abc" y "yzabc", pero no coincide con "abcde". Para
coincidir exactamente con una cadena, incluyen tanto "^" y "$". Por ejemplo,
"^abc$" sólo coincide con "abc" y no coincide con "abcde" o "yzabc".
Cuando la búsqueda de palabras, el uso de "\b" para coincidir con un límite de
palabra. Por ejemplo, "coche\b" coincide con "coche" o "tocar" pero no
"carburador". Asimismo, "\b" coincide con un límite nonword y pueden utilizarse
para garantizar que un personaje aparece en el medio de una palabra. Por ejemplo,
"coche\b" coincide con "el carburador" pero no "tocar".

Nota   confundida?
Si las expresiones regulares parece críptica, y eso es porque ellos son. A
diferencia de casi todo lo demás en el
.NET Framework, las expresiones regulares se basan en gran medida en
caracteres especiales con significados que no
Ser humano nunca pudo descifrar en su propio. La razón de esto es simple:
Regular expres-
Sion" se originó en el mundo UNIX durante una época en que la memoria y el
almacenamiento son extremadamente limitadas
Y los desarrolladores tenían que hacer cada recuento de caracteres. Por esta
razón, deberá siempre com-
El ment expresiones regulares. Tan duro como pueden ser la creación, lectura
regular del otro desarrollador
Expresiones es casi imposible.

La tabla 3-1 muestra los caracteres que puede utilizar para provocar la expresión
regular para que coincida con una ubicación- pecífico en una cadena. De éstos, el más
importante saber son "^" y "$".
Tabla 3-1  caracteres que coinciden con la ubicación en cadenas

    Descripción del personaje


^  Especifica que la coincidencia debe comenzar en el primer
carácter de la cadena o el primer carácter de la línea. Si está
analizando la entrada multilínea, ^ coincide con el comienzo de
cualquier línea.
$  Especifica que el partido debe finalizar en el último carácter de
la cadena, el último caracter antes \n al final de la cadena, o el
último carácter al final de la línea. Si está analizando la entrada
multilínea, $ coincide con el final de una linea.
\A  Especifica que la coincidencia debe comenzar en el primer
carácter de la cadena (y omite varias líneas).
\Z Especifica que el partido debe finalizar en el último carácter de la cadena o el
último carácter antes \n al final de la cadena (y omite varias
líneas).
\Z Especifica que el partido debe finalizar en el último carácter
de la cadena (y omite varias líneas).
\G Especifica que la coincidencia debe producirse en el punto en el partido
anterior terminó. Cuando se utiliza con Match.NextMatch, este
acuerdo asegura que los combates son contiguos.
\B   especifica que la coincidencia debe producirse en un límite entre
\W (alfanumérico) y \W (no alfanumérico) caracteres. La
coincidencia debe producirse en los límites de las palabras, que
son los primeros o últimos caracteres de palabras separadas por
cualquier carácter no alfanumérico.
\B   especifica que el partido no debe ocurrir en un \b límite.
Observe que las expresiones regulares son sensibles a mayúsculas y minúsculas,
incluso en Visual Basic. A menudo, la capital- izado caracteres tienen el significado
opuesto de caracteres en minúsculas.
Muchos códigos de expresión regular comienzan con una barra invertida. Al
desarrollar en C#, se debe comenzar cada expresión regular con un @, de modo que
las barras diagonales inversas se trata Literalmente. Haga esto incluso si la expresión
regular no contiene una barra invertida, porque reducirá el riesgo de adición de un
muy difícil encontrar bug si edita la expresión regular más tarde. Por ejemplo:
// C#
Regex.IsMatch("patrón", @"\Apattern\Z").

 Punta del examen   No trates de memorizar cada código de expresiones


regulares. Seguro que impresionarían a la multitud de UNIX en la oficina, pero
el examen sólo necesita saber los códigos más comúnmente utilizado, que este
libro se llama en ejemplos.

Cómo hacer coincidir


los caracteres especiales
Puede hacer coincidir los caracteres especiales de las expresiones regulares. Por
ejemplo, \t representa una ficha, y \n representa un salto de línea. Los caracteres
especiales que se muestran en la Tabla 3-2 podría no aparecer en la entrada de
usuario o el promedio del archivo de texto; sin embargo, pueden aparecer si el
pro- ceso de salida de un sistema UNIX o heredados.
Tabla 3-2  caracteres de escape utilizado
en expresiones regulares.
    Descripción del personaje
\A   coincide con una campana (alarma) \u0007.
\B   en una expresión regular, \b denota un límite de palabra (entre
\W y \w caracteres) excepto dentro de una clase de caracteres
[], donde
\B se refiere al carácter de retroceso. En un patrón de
sustitución, \b siempre denota un retroceso.
\T   coincide con una tabulación \u0009.
\R   coincide con un retorno de carro \u000D.
\V   coincide con una tabulación vertical \u000B.
\F   coincide con un form feed \u000C.
\N   coincide con una nueva línea \u000A.
\E   coincide con un escape \u001B.
\040 Coincide con un carácter ASCII octal (hasta 3 dígitos);
números sin cero son referencias inversas si tienen tan sólo
un dígito o si corresponden a un grupo de captura número.
Por ejemplo, el personaje de \040 representa un espacio.

Tabla 3-2  caracteres de escape utilizado en expresiones regulares.

    Descripción del personaje


\X20   coincide con un carácter ASCII mediante representación hexadecimal
(Exactamente dos dígitos).
\CC  Coincide con un carácter de control ASCII, por ejemplo,
\cC es control-C.
\U0020   coincide con un carácter Unicode usando la representación
hexadecimal
(Exactamente cuatro dígitos).
\ Cuando va seguido por un carácter que no es reconocido como un
carácter de escape, coincide con ese carácter. Por ejemplo, \*
representa un asterisco (en lugar de repetir caracteres
coincidentes), y \\ representa una sola barra invertida.

Cómo hacer coincidir el


texto con caracteres comodín
También puede utilizar expresiones regulares para que coincidan con caracteres
repetidos. El símbolo "*" coincide con el  carácter anterior cero o más veces. Por
ejemplo, "A*n" coincide con "ton", "tooon" o "TN". El símbolo "+" funciona de forma
similar, pero debe coincidir con una o más veces. Por ejemplo, "a+n" coincide con
"ton" o "tooon", pero no "TN".
Para que coincida con un número específico de  caracteres repetidos, el uso de "{}" n
donde n es un dígito. Por ejemplo, "a{3}n" coincide con "tooon" pero no "ton" o
"TN". Para que coincida con un rango de caracteres repetidos, el uso de "{} min ,
max". Por ejemplo, "a{1,3} n" coincide con "ton" o "tooon" pero no "TN" o "toooon".
Para especificar sólo un mínimo,  deje el segundo número en blanco. Por ejemplo,
"a{3,} n" requiere tres o más caracteres consecutivos "o".
Hacer un carácter opcional, utilice el símbolo "?". Por ejemplo, "a?n" coincide con
"ton" o "TN", pero no "tooon". A coincide con cualquier carácter individual, el uso de
".". Por ejemplo, "a.n" coincide con "totn" o "tojn" pero no "ton" o "TN".
Para que coincida con uno de varios caracteres, utilice paréntesis. Por ejemplo, "[a]n
ro" coincide con "toon" o "rasgado" pero no "ton" o "toron". También puede coincidir
con un rango de caracteres. Por ejemplo, "a-r[o]n" coincide con "toon", "topn", "toqn"
o "rasgado" pero no "coincidencia" o "toyn toan".
La tabla 3-3 resume los caracteres de expresión regular se utiliza para que coincida
con múltiples acters char- o un rango de caracteres.

Tabla 3-3  comodín y rangos de caracteres utilizados en expresiones


regulares.
    Descripción del personaje

* Coincide con el carácter anterior o subexpresión cero o


más veces. Por ejemplo, "zo*" coincide con "z" y con "zoo".
El carácter "*" es equivalente a "{0,}".
+ Coincide con el carácter anterior o subexpresión una o más
veces. Por ejemplo, "zo+" coincide con "zo" y con "zoo",
pero no "z". El carácter "+" es equivalente a "{1,}".
? Coincide con el carácter anterior o subexpresión cero o una
vez. Por ejemplo, "hacer(es)?" coincide con "hacer" en
"hacer" o "no". El ? Carácter es equivalente a "{0,1}".
{N} La n es un entero no negativo. Coincide exactamente n veces.
Por ejemplo, "o{2,}" no coincide con la "o" de "Bob" pero
coincide con las dos "o" en "alimentos".
{N,} La n es un entero no negativo. Coincide al menos n veces. Por
ejemplo, "o{2,}" no coincide con la "o" de "Bob" y coincide
con todas las "o" en "foooood". La secuencia "o{1,}" equivale
a "o+". La secuencia "o{0,}" equivale a "o"*.
{N,m} La m y n son enteros no negativos, donde "n <= m". Coincide como
mínimo n y como máximo m veces. Por ejemplo, "o{1,3}"
coincide con las tres primeras "o"s en "fooooood". "O{0,1}"
equivale a "o?". Tenga en cuenta que no se puede poner
un espacio entre la coma y los números.
? Cuando este personaje sigue inmediatamente a cualquiera de
los demás quan- tifiers (*, +, ?, {n}, {n,}, {n,m}), el patrón es
nongreedy coincidentes. Un nongreedy patrón coincide con
tan poco de la cadena de búsqueda como sea posible, mientras
que el codicioso predeterminado patrón coincide con la mayor
parte posible de la cadena buscada. Por ejemplo, en la cadena
"oooo", "oh+?" coincide con una sola "o", mientras que "o+"
coincide con todas las "o".
. Coincide con cualquier carácter individual excepto "\n". Para
que coincida con cualquier carácter incluyendo el "\n", utilice
un modelo como "[\s\S]".
X|y   coincide con x o y. Por ejemplo, "z|food" coincide con "z" o
"Comida". "(Z|f)ood" coincide con "zood" o "comida".

Tabla 3-3  comodín y rangos de caracteres utilizados en expresiones


regulares.
    Descripción del personaje
[Xyz] Un conjunto de caracteres. Coincide con cualquiera de los
caracteres encerrados. Por ejemplo, "[abc]" coincide con la "a"
de "plano".
[A-Z] Un rango de caracteres. Coincide con cualquier carácter en el
rango especificado. Por ejemplo, "[a-z]" coincide con
cualquier carácter alfabético en el rango "A" a la "z".

Expresiones regulares también proporcionan caracteres especiales para representar


rangos de caracteres comunes. Puede utilizar "[0-9]" para que coincida con cualquier
dígito numérico, o puede usar "\d". Simi- mente, "\D" no coincide con cualquier dígito
numérico. Usar "\s" para que coincida con cualquier espacio en blanco que caract.- ter,
y usar "\S" no coincide con cualquier carácter de espacio en blanco. La tabla 3-4
resume estos caracteres.
Tabla 3-4  caracteres usados en expresiones regulares.

    Descripción del personaje


\D   Coincide con un dígito. Equivalente a "[0-9]".
\D   Coincide con un carácter que no sea un dígito. Equivalente a "[^0-
9]".
\S Coincide con cualquier carácter de espacio en blanco,
incluidos espacios, tabulaciones y saltos de página. Equivale a
"[ \f\n\r\t\v]".
\S   no coincide con cualquier carácter de espacio en blanco.
Equivalente a
"[^ \f\n\r\t\v]".
\W   coincide con cualquier carácter de palabra, incluido el subrayado.
Equivalente a"[A-Za-z0-9_]".
\W   coincide con cualquier carácter que no sea una palabra. Equivalente
a [^A-Za-z0-9_]".

Para identificar a un grupo de personajes, rodean los caracteres entre paréntesis. Por
ejemplo, "foo(100){1,3} hoo" coincide con "fooloohoo" y "fooloolooloohoo" pero no
"foo-hoo" o "foololohoo". Asimismo, "foo(loo|roo|)hoo" podría coincidir con
"fooloohoo" o "fooroohoo". Puede aplicar cualquier carácter comodín o de otro
carácter especial a un grupo de caracteres.
También puede nombrar grupos para referirse a la coincidencia de datos más tarde.
Para asignar un nombre a un grupo, utilice t él fo r m a "(?< nam e > cháchara n )".
Fo r ejemplo, t h e re gular expresión 

"Foo(?<mediados>loo|roo)hoo" coincide con "fooloohoo". Más adelante, se podría hacer


referencia al grupo de los "media" para recuperar "Loo". Si ha utilizado la misma expresión
regular coincide con "fooroohoo", "media" podría contener "roo".

Cómo hacer coincidir con las referencias inversas


Referencias inversas utiliza grupos con nombre para permitir a usted para buscar otras
instancias de char- acters que coincidan con un comodín. Las referencias inversas
proporcionan una forma cómoda de buscar repetir- ción de grupos de caracteres. Se pueden
considerar como una instrucción de taquigrafía para coincidir con la misma cadena de
nuevo.
Por ejemplo, la expresión regular (?<char>\w)\k<char>, utilizando grupos con nombres y
referencias inversas, busca pares de caracteres contiguos. Cuando se aplica a la cadena
"Tomaré un poco de café", que busca coincidencias en las palabras "yo", "pequeño" y
"café". El metacarácter \w encuentra cualquier carácter de palabra único. La construcción de
agrupamiento (?<char> ) encierra el metacarácter para forzar el motor de expresiones
regulares para recordar un sub- coincidencia de expresión (que, en este caso, será un único
carácter) y guárdelo con el nombre de "char". La construcción de referencia inversa
\k<char> causas el motor para comparar el carácter actual del carácter coincidente anterior
almacenado bajo "char". Toda la expresión regular logra encontrar una coincidencia donde
un solo personaje es el mismo que el caracter precedente.
Para buscar palabras enteras repetidas, puede modificar la agrupación subexpresión para
buscar cualquier grupo de caracteres precedidos por un espacio, en lugar de simplemente
buscar cualquier carácter individual. Puede sustituir la subexpresión \w+, que coincide con
cualquier grupo de caracteres para el metacarácter \w y usar el metacarácter \s para hacer
coincidir un espacio anterior a t h e g roup caracteres. Esto produce t h e re gular expresión
(?<char>\s\w+)\k<char>, que encuentra cualquier toda repetición de palabras tales como
"el", pero también coincide con otras repeticiones de la cadena especificada, como en la
frase "la teoría".
Para verificar que el segundo partido está en un límite de palabra, agregue el metacarácter \b
después de repetir la coincidencia. La expresión regular resultante, (?
<char>\s\w+)\k<char>\b, sólo busca palabras enteras repetidas que están precedidos por un
espacio en blanco.
Una referencia inversa se refiere a la definición más reciente de un grupo (la definición más
inmediatamente a la izquierda en la coincidencia de izquierda a derecha). En concreto,
cuando un grupo realiza varias capturas, una referencia inversa se refiere a la captura más
reciente. Por ejemplo, (?<1>a)(?<1>\1b)* encuentra aababb como coincidencia del patrón de
captura (a)(ab)(abb). Lazo- ing cuantificadores no borrar las definiciones de grupo.
Si un grupo no ha captado cualquier subcadena, una referencia inversa a ese grupo es
indefinido y nunca coincide. Por ejemplo, la expresión \1() nunca coincide con nada, pero la
expresión ()\1 coincide con la cadena vacía.
La tabla 3-5 muestra los parámetros opcionales que agregar modificadores de referencia
inversa a una expresión regular.
Tabla 3-5  parámetros de referencia inversa

Construccion de
referecia imversa

\Número referencia inversa. Por ejemplo, (\w)\1 encuentra duplicado caracteres de


palabra.
\K<nombre> Nombre de referencia inversa. Por ejemplo, (?<char>\w)\k<char>
encuentra duplicado caracteres de palabra. La expresión (?
<43>\w)\43 hace lo mismo. Puede usar comillas simples en
lugar de corchetes angulares, por ejemplo, \k'char'.

Cómo especificar opciones de expresiones regulares


Puede modificar un patrón de expresión regular con opciones que afectan conductas
coincidentes- ior. Opciones de expresiones regulares puede fijarse en una de dos maneras
básicas: pueden ser spec- ified en el  parámetro options en el patrón
Regex(opciones), constructor, donde options es un bitwise o combinación
de RegexOptions valores enumerados, o se pueden establecer dentro del patrón de la
expresión regular con la versión en línea (?imnsx-imnsx:-) agrupar struct o (?imnsx-imnsx)
varios construir.
Opción en línea construye, un signo menos (-) antes de una opción o un conjunto de
opciones desactiva aquellas opciones. Por ejemplo, la construcción en línea (?ix-ms)
enciende el IgnoreCase y IgnorePatternWhitespace opciones y desactiva
la Multiline  Singleline y opciones. Todas las opciones de expresiones regulares están
desactivadas de forma predeterminada.
La tabla 3-6 muestra los miembros de la  enumeración y el equivalente RegexOptions
caracteres de opción en línea. Tenga en cuenta que las opciones RightToLeft y compilado se
aplican sólo a una expresión como un todo y no están permitidos en línea. (Ellos sólo puede
especificarse en las opciones de  parámetro al  constructor Regex.) las opciones
Ninguno y ECMAScript no están permitidos en línea.

Tabla 3-6  Opciones de expresiones regulares

RegexOptio  Carácte Descripción


n r en
Estados línea
Ninguno  N/A   Especifica que no hay opciones.
IgnoreCase   i   especifica la coincidencia sin distinción entre
mayúsculas y minúsculas.
Multiline M   Especifica el modo de varias líneas. Cambia
el significado de ^ y $ para que realicen las
coincidencias al principio y al final,
respectivamente, de una línea, no sólo al
principio y al final de toda la cadena.
ExplicitCapture  n   especifica que la única captura válida se
explic- itly nombrado o grupos numerados
del formulario (?<nombre>…). Esto permite
que los paréntesis para actuar como grupos
que no capturan sin la torpeza sintáctica de
(?:…).
Compilado  N/A Especifica que la expresión regular se compilan en un
ensamblado. Genera el lenguaje intermedio
de Microsoft (MSIL) Código de la
reglamenta- ción es lar de
expresión; proporciona mayor rapidez en la
ejecución a expensas del tiempo de inicio.
Singleline  s   especifica el modo de una sola línea. Cambia
la media- ción del carácter de punto (.) para
que coincida con cada personaje (en lugar de
cada personaje excepto \n).
- X  Especifica que el espacio en blanco sin
IgnorePatter escapar está excluido de la trama, y permite
n com- ciones tras un signo de número (#).
Espacio en Tenga en cuenta que nunca se elimina el
blanco espacio en blanco dentro de una clase de
caracteres.
Tabla 3-6  Opciones de expresiones regulares

RegexOption  Carácter Descripción


Estados en línea
   N/A RightToLeft   Especifica que la búsqueda se mueve de derecha a
izquierda en lugar de izquierda a derecha. Una
expresión regular con esta opción se mueve a la
izquierda de la posición inicial, en lugar de a la
derecha. (Por lo tanto, la posición de partida debe ser
especificado como el final de la cadena, en lugar de al
principio.) Esta opción puede especificarse en la
mitad, lo cual es una limitación diseñada para evitar
la posibilidad de crear expresiones regulares con
bucles infinitos. Sin embargo, la búsqueda tardía (?<)
construye proporcionan algo similar que puede ser
utilizado como una subexpresión.
RightToLeft sólo cambia la dirección de la búsqueda.
No puede revertir la subcadena que se busca. Las
anticipaciones y las aserciones hacia atrás no
cambian: lookahead mira hacia la derecha; tardías se
ve a la izquierda.
   N/A ECMAScript   Especifica que el comportamiento compatible con
ECMAScript está habilitado para la expresión. Esta
opción sólo se puede usar en conjunción con
la IgnoreCase  Multiline y banderas. Uso
de ECMAScript con cualquier otras banderas, se
genera una excepción.
CultureInvariant  N/A   Especifica que las diferencias culturales en el lenguaje
son ignoradas.

Considere las siguientes tres líneas del archivo de texto:


Abc def ghi

Si este archivo de texto se copian a la  cadena s, el siguiente método devuelve  false porque


"Def" no es tanto al principio como al final de la cadena:
Regex.IsMatch(s, "^Def$")
Pero el siguiente método devuelve  true porque el RegexOptions. opción Multiline habilita el
símbolo "^" para que coincida con el inicio de una línea (en lugar de toda la cadena), y
permite que el símbolo "$" para que coincida con el final de una línea:
Regex.IsMatch(s, "^Def$", RegexOptions.Multiline)

Cómo extraer datos pareados


Además de simplemente en determinar si una cadena coincide con un patrón, puede extraer
infor- mación de una cadena. Por ejemplo, si edita un archivo de texto que contiene "Com-
pany Nombre: Contoso, Inc.", puede extraer solamente el nombre de la empresa utilizando
una expresión regular.
Para que coincida con un patrón y capturar el partido, siga estos
pasos:
1.  Crear una expresión regular, y lo encierre entre paréntesis en el patrón que
debe coincidir.
2.  Crear una instancia de System.Text.RegularExpressions.Match  clase
utilizando el  método estático Regex.Match.
3.  Recuperar los datos pareados por tener acceso a los elementos de los Grupos
coinciden. Array. Por ejemplo, el siguiente código muestra extrae el nombre de
compañía de la entrada Y lo muestra en la consola:

// C#
Entrada de cadena = "Nombre de la empresa: Contoso,
Ltd.";
M = Match Regex.Match(entrada, @"Nombre de la empresa: (.*$");
Console.WriteLine(m.Grupos[1]);

Ejecuta esta aplicación de consola (que requiere el  espacio de nombres


System.Text.RegularExpressions) muestra "Contoso, Inc.". Este ejemplo demuestra que con
muy poco código, puede realizar complejos de extracción de texto usando expresiones
regulares. Observe que en este ejemplo se utiliza grupos anónimos, que el tiempo de ejecución
se numeran automáticamente a partir de 1.
En el siguiente ejemplo se busca una cadena de entrada e imprime todos los href="…" los
valores y sus posiciones en la cadena. Esto se hace creando un  objeto Regex compilado y
luego utilizando un  objeto Match para iterar a través de todas las coincidencias de la cadena.
En este ejemplo, el metacarácter \s coincide con cualquier carácter de espacio y \S Coincide
con cualquier carácter de espacio de no-
// C#
Void inputString DumpHrefs(String)
{
R; Regex Match m;

R = new Regex("href\\s*=\\s*(?:\"(?<1\">[^]*)\"|(?<1>\\S+)", RegexOptions.IgnoreCase|RegexOptions.Compiled);


(M = r.Match(inputString); m.éxito; m = m.NextMatch())
{
Console.WriteLine("encontrado href " + m.Grupos[1] + " en "
+ m.Grupos[1].Index);
}
}

También puede llamar al  método Match.Resultado para reformatear extraen subcadenas. En


el siguiente ejemplo de código se utiliza coinciden.Resultado para extraer un protocolo y
número de puerto desde una dirección URL. Por
ejemplo, “http://www.contoso.com:8080/letters/readme.html" devolvería "http:8080".
// C#
String(String url) de extensión
{
R = new Regex Regex(@"^(?<proto>\w+)://[^/]+?(?<puerto>:\d+)?/", RegexOptions.Compiled);
Retorno r.Match(url).Resultado("${proto}${puerto}");
}

Cómo sustituir las subcadenas usando expresiones regulares.


También puede utilizar expresiones regulares para realizar sustituciones mucho más
complejos de lo que es posible con el  método String.Replace. En el siguiente ejemplo de
código se utiliza el  método estático Regex.Replace para reemplazar fechas en
el mm/dd/aa con las fechas en formato dd-mm-yy formato:

// C#
String(String MDYToDMY entrada)
{
Volver Regex.Replace(entrada, "\\b(?<mes>\\d{1,2})/(?<día>\\d{1,2})/(?<año>\\d{2,4})\\b", "${day}-${month}-${año}");
}

Este ejemplo demuestra el uso de referencias inversas con nombre dentro del modelo de
reemplazo de Regex.Sustituir. Aquí, la sustitución de la expresión ${day} inserta la sub-
cadena capturada por el grupo (?<día>…).
En el siguiente ejemplo de código se utiliza el  método estático Regex.Replace para quitar los
caracteres no válidos- acters desde una cadena. Puede utilizar el  método CleanInput definido
aquí strip poten- considerablemente los caracteres nocivos que se han introducido en un
campo de texto que acepta datos del usuario.  CleanInput devuelve una cadena después de la
eliminación de todos los caracteres no alfanumérico, excepto @, - (guión), y . (Un punto).
// C#
Cadena CleanInput(string cadena)
{
// Reemplazar caracteres no válidos con cadenas vacías. volver Regex.Replace(cadena, @"[^\w\.@-]", "");
}

Caracteres de escape y las sustituciones son las únicas construcciones especiales reconocidas
en un patrón de sustitución. Todas las construcciones sintácticas descritas en las siguientes
secciones se permiten solamente en expresiones regulares; no son reconocidos en sustitución
patrones. Por ejemplo, el patrón de sustitución*${txt}b Inserta la cadena "a*" fol- lowed por
la subcadena coincidente txt por el grupo de captura, si cualquiera, seguido por la cadena "b".
El carácter * no se reconoce como un metacarácter dentro de un patrón de sustitución.
Asimismo, $ patrones no son reconocidos dentro de patrones de expresiones regulares. Dentro
de expresiones regulares, $ designa al final de la cadena.
La tabla 3-7 muestra cómo definir patrones de sustitución nombradas y
numeradas.
Tabla 3-7  caracteres de escape utilizado en las sustituciones

    Descripción del personaje


$number   sustituye la última subcadena igualado por número de grupo 
(Deci
mal).
${name} sustituye la última subcadena acompañado por un (?<nombre> ) grupo.
$$   sustituye  un único literal "$".
$&   sustituye  una copia de todo el match.
$'   sustituye  todo el texto de la cadena de entrada antes del partido.
$'   sustituye  todo el texto de la cadena de entrada después del
partido.
$+   sustituye  el último grupo capturado.
$_   sustituye  toda la cadena de entrada.

Cómo utilizar expresiones regulares para restringir la entrada de


cadena
Cuando la consolidación de la seguridad en su aplicación, las expresiones regulares son la
manera más efi- cientes para validar la entrada del usuario. Si crea una aplicación que acepta
un número de cinco dígitos de un usuario, puede utilizar una expresión regular para
asegurarse que la entrada es exactamente cinco caracteres de longitud y que cada carácter es
un número de 0 a 9. Asimismo, cuando se lo solicite un usuario por su nombre y apellido,
puede comprobar su entrada con una expresión regular y producir una excepción cuando la
entrada contiene números, delimitadores, o cualquier otro tipo de caracteres no alfabéticos.
Lamentablemente, no toda entrada es tan fácil de describir como números de teléfono y
direcciones de correo electrónico. Nombres y direcciones son particularmente difíciles de
validar porque pueden con- servar una amplia variedad de caracteres de alfabetos
internacionales desconocido para usted. Por ejemplo, O'Dell, Varkey Chudukatil, Skjønaa,
Craciun, y McAskill-White son legítimos todos los apellidos. Filtrado mediante programación
estos ejemplos de entrada válida desde Entrada malintencionada como "1" -- Productos DROP
TABLE" (un ataque de inyección SQL) es difícil.
Un método común es el de instruir a los usuarios reemplazar caracteres en sus propios
nombres. Por ejemplo, usuarios que normalmente entran a un apóstrofo o un guión en su
nombre podría omitir esos caracteres. Los usuarios con letras que no forman parte del estándar
del alfabeto romano podría reemplazar letras con los caracteres romanos similar más cercano.
Aunque este sistema permite validar la entrada más rigurosamente, se obliga a los usuarios a
sacrificar la ortografía correcta de sus nombres, algo que mucha gente tome muy
personalmente.
Como alternativa, puede realizar el filtrado tanto como sea posible en la entrada y, a
continuación, limpiar la entrada de cualquier contenido potencialmente malicioso. La mayoría
de validación de entrada debe ser pesimista y permitir sólo la entrada que consta únicamente
de caracteres aprobados. De cualquier modo, la validación de datos de entrada de nombres
reales que necesite ser optimista y causar un error sólo cuando existen caracteres
específicamente denegados. Por ejemplo, puede rechazar el nombre de un usuario si contiene
uno de los siguientes caracteres: !, @, #, $, %, ^, * (), < >. Todos estos caracteres son poco
probable que aparezca un nombre pero que pueda utilizarse en un ataque. Microsoft Visual
Studio .NET proporciona la siguiente expresión regular para hacer coincidir los nombres
válidos: "[a-zA-Z''-Â'\s]{1,40}".

Mundo Real
Tony Northrup
A menudo me obstinada a un fallo. Durante muchos años, yo simplemente se negó a
aprender expresiones regulares. Las expresiones regulares son la forma en la que UNIX de
hacer las cosas, y yo era un chico de Windows.
Recientemente, he revisado algunos código que escribí hace ya varios años cuando yo
todavía era terco. Yo había escrito decenas de líneas de código para comprobar la validez
de los datos de texto que podría haber sido escrito con una singular expresión regular. Que
no me molesta en sí mismo, porque a veces escribir más código mejora la legibilidad. Sin
embargo, en este caso, la comprobación de texto había quedado tan complejo que contenía
errores.
Me reescribió el código utilizando expresiones regulares, y no sólo arregló los bugs, pero
simplifica el código. Así, por su propio bien, no ignore las expresiones regulares sólo
porque les parece demasiado complejo. En buceo, pasar unas horas trabajando con ellos, y
no te arrepentirás.

Laboratorio: crear un   patrón de expresión Regex.


En este laboratorio, procesar un array de cadenas válidas para distinguir los números de
teléfono y códigos postales. Entonces usted va a formatear los números de teléfono. Si usted
encuentra un problema com- pleting un ejercicio, los proyectos terminados están disponibles
en el CD en la carpeta de código.
    Ejercicio 1: Distinguir entre un número de teléfono y un código postal
En este ejercicio, va a escribir código para distinguir entre un número de teléfono,
un código postal y los datos no válidos.
1.  Copia el código de Visual Basic o C# versión del Capítulo03\LECCION1-
ejercicio1 carpeta desde el CD al disco duro y abrir el archivo de solución.
2.  Utilizando una línea de código, completar el  método IsPhone, de modo que
devuelve true si cualquiera de los siguientes formatos:
❑   (555)555-1212

❑ El   (555) 555-1212
❑   555-555-1212

❑   5555551212

Aunque muchos diferentes expresiones regulares, el  método IsPhone escribir


podría tener este aspecto:
// C#
Static bool IsPhone(string s)
{
Volver Regex.IsMatch(S, @"^\(?\d{3}\)?[\s\-]?\d{3}\-?\d{4}$");
}

Cada componente de esta expresión regular coincide con una parte requerida
u opcional de un número de teléfono:
■   ^  coincide con el principio de la cadena.
■   \(?  Opcionalmente coincide con un paréntesis de apertura. El
paréntesis es pre- cedido con una barra invertida, ya que el paréntesis es
un carácter especial en las expresiones regulares. La siguiente
interrogante hace el paréntesis
Opcional.
■   \d{3,}  coincide exactamente tres dígitos numéricos.

■   \)?  Opcionalmente coincide con un paréntesis de cierre. El paréntesis


va precedido de una barra invertida porque el paréntesis es un carácter
especial en las expresiones regulares. La siguiente interrogante hace el
paréntesis opcional.
■   [\s\-]?   Coincide con un espacio ("\s") o un guión ("\") que separa el
código de área del resto del número de teléfono. La siguiente
interrogante hace del espacio o guión opcional.
■   \d{3,}  coincide exactamente tres dígitos numéricos.
■   \-?  Opcionalmente coincide con un guión.
■   \d{4}$  requiere que la cadena termina con cuatro dígitos numéricos.
3.  Utilizando una línea de código, completar el  método IsZip para que
devuelva true si cualquiera de los siguientes formatos:
❑   01111

❑   01111-1111

Aunque muchos diferentes expresiones regulares, el  método IsZip escribir


podría tener este aspecto:

// C#
Static bool IsZip(string s)
{
Volver Regex.IsMatch(S, @"^\d{5}(\\d{4})?$");
}

Cada componente de esta expresión regular coincide con una parte requerida
u opcional de un código postal:
■   ^  coincide con el principio de la cadena.
■   \d{5}  coincide exactamente cinco dígitos numéricos.
■   (\\d{4})?  Opcionalmente coincide con un guión seguido por
exactamente cuatro dígitos numéricos. Porque la expresión está rodeado
de paréntesis y seguido por un signo de interrogación, la expresión se
consideran opcionales.
■   $  coincide con el final de la cadena.

4.  Generar y ejecutar el proyecto. La salida debe coincidir con lo siguiente:


(555)555-1212 es un número de teléfono
(555) 555-1212 es un número de teléfono
555-555-1212 es un número de teléfono
Es un número de teléfono 5555551212
01111 es un código postal
Es un código postal 01111-1111
47 es desconocido
111-11-1111 es desconocido

Si el resultado que obtiene no coincide con el resultado mostrado, ajustar sus expresiones
regulares según sea necesario.
    Ejercicio 2: Formatear una cadena
En este ejercicio, debe volver a formatear números de teléfono en un estándar (###) ###-####
Formato.
1.  Abra el proyecto creado en el ejercicio 1.
2.  Agregue un método denominado  ReformatPhone que devuelve un valor de tipo
string y acepta una cadena como argumento. El uso de expresiones regulares,
aceptar datos de número de teléfono proporcionado en uno de los formatos que se
usan en el ejercicio 1, y el formato de los datos en el (###) ###-#### de formato.
Aunque muchos diferentes expresiones regulares, el  método IsZip escribir podría
tener este aspecto:

// C#
Cadena estática ReformatPhone(string s)
{
M = Match Regex.Match(S, @"^\(?(\d{3})?[\)\s\-]?(\d{3})\-?(\d{4}$");
Devuelve String.Format("({0}) {1}-{2,}", m.Grupos[1], m.Grupos[2], m.Grupos[3]);
}

Observe que esta expresión regular coincide casi exactamente con que se utiliza
en el  método IsPhone. La única diferencia es que cada uno de losn \d{}
expresiones está cercada por paréntesis. Esto coloca a cada uno de los conjuntos
de números en un grupo separado que puede ser fácilmente formateado
utilizando String.Format.
3.  Cambiar el  método principal de modo que escribe ReformatPhone(s) en el  bucle
foreach en lugar de simplemente s. Por ejemplo, el  bucle foreach debe tener este
aspecto:
// C#
Foreach (cadena de entrada)
{
Si (IsPhone(s) Console.WriteLine(ReformatPhone(s) + " es un número de teléfono");
Else If (IsZip(s) Console.WriteLine(s + " no es un código postal");
Else Console.WriteLine(s + " es desconocido");
}

4.  Generar y ejecutar el proyecto. La salida debe coincidir con lo siguiente:


(555) 555-1212 es un número de
teléfono (555) 555-1212 es un número
de teléfono (555) 555-1212 es un
número de teléfono (555) 555-1212 es
un número de teléfono
01111 es un código
postal
Es un código postal 01111-
1111
47 es
desconocid
o
111-11-1111 es
desconocido

Observe que cada uno de los números de teléfono ha sido reordenada aunque fueron
inicialmente en cuatro formatos diferentes. Si el resultado no coincide con el de salida sólo se
muestra, ajuste las expresiones regulares según sea necesario.

Resumen de la lección
■   Las expresiones regulares permiten determinar si el texto coincide con casi cualquier tipo
de formato. Soporte para expresiones regulares docenas  de operadores y caracteres
especiales. Los más comúnmente utilizados son "^" para coincidir con el inicio de la cadena,
"$" para que coincida con el final de una cadena, "?" para hacer un carácter facultativo, "."
para que coincida con cualquier carácter, y "*" para hacer coincidir un carácter repetido.
■   para hacer coincidir los datos mediante una expresión regular, crear un patrón utilizando
grupos a spec- ify los datos que usted necesita para extraer, llamada Regex.Match para crear
un  objeto Match y, a continuación, examine cada uno de los elementos de los Grupos
coinciden. Array.
■   Para cambiar el formato del texto de datos mediante una expresión regular, llame al
estático Regex.Sustituir Método.

Examen de la lección
Puede utilizar las siguientes preguntas para poner a prueba su conocimiento de la información
en la lección 1, "formar expresiones regulares." Las preguntas también están disponibles en el
CD complementario si prefiere revisarlos en forma electrónica.

Nota   Respuestas
Las respuestas a estas preguntas y explicaciones de por qué cada opción de
respuesta es correcta o no se encuentran en la sección "Respuestas" al final
del libro.

1.  Escribir una aplicación para actualizar hipervínculos absolutos en archivos


HTML. Si se han cargado el archivo HTML en una cadena denominada s.
Cuál de los siguientes ejemplos de código mejor reemplaza "http://" con
"https://"?
A
.
// C#
S = Regex.Replace(s, "http://", "https://");

B.
// C#
S = Regex.Replace(s, "https://" "http://");

C.
// C#
S = Regex.Replace(s, "http://", "https://" RegexOptions.IgnoreCase);

D.
// C#
S = Regex.Replace(s, "https://" "http://", RegexOptions.IgnoreCase);

2.  Escribir una aplicación para procesar los datos contenidos en un formato de


texto. Cada archivo contiene información acerca de un único cliente. El
siguiente es un ejemplo de formulario:
Nombre: Tom
Apellido: Perham
Dirección: 1 Pine St. Ciudad: Springfield Estado: MA
Código postal: 01332

Se han leído los datos del formulario en la  variable de cadena s. Cuál de los
siguientes ejemplos de código almacena correctamente la parte de datos del
formulario en el fullName, dirección, ciudad, estado, y zip variables?
A.
// C#
Cadena p = @"Nombre: (?<firstName>.*$)\n" +
@"Apellidos: (?<Apellido>.*$)\n" +
@"Dirección: (?<dirección>.*$)\n" +
@"Ciudad: (?<ciudad>.*$)\n" +
@"Estado: (?<estado>.*$)\n" +
@"Zip: (?<zip>.*$)".
M = Match Regex.Match(s, p, RegexOptions.Multiline);
Cadena FullName = m.Groups["nombre"] + " " + m.Groups["LastName"];
Cadena dirección = m.Groups["Dirección"].ToString();
Ciudad de cadena = m.Groups["Ciudad"].ToString(); string estado = m.Groups["Estado"].ToString(); string zip =
m.Groups["zip"].ToString();

B.

// C#
Cadena p = @"Nombre: (?<firstName>.*$)\n" +
@"Apellidos: (?<Apellido>.*$)\n" +
@"Dirección: (?<dirección>.*$)\n" +
@"Ciudad: (?<ciudad>.*$)\n" +
@"Estado: (?<estado>.*$)\n" +
@"Zip: (?<zip>.*$)"; m = Match Regex.Match(s, p);
Cadena FullName = m.Groups["nombre"] + " " + m.Groups["LastName"];
Cadena dirección = m.Groups["Dirección"].ToString();
Ciudad de cadena = m.Groups["Ciudad"].ToString(); string estado = m.Groups["Estado"].ToString(); string zip =
m.Groups["zip"].ToString();

C.
// C#
Cadena p = @"Nombre: (?<firstName>.*$)\n" +
@"Apellidos: (?<Apellido>.*$)\n" +
@"Dirección: (?<dirección>.*$)\n" +
@"Ciudad: (?<ciudad>.*$)\n" +
@"Estado: (?<estado>.*$)\n" +
@"Zip: (?<zip>.*$)".
M = Match Regex.Match(s, p, RegexOptions.Multiline);
Cadena FullName = m.Groups["<nombre>"] + " " + m.Groups["<Apellido>"];
Cadena dirección = m.Groups[" <dirección>"].ToString();
Ciudad de cadena = m.Groups["<ciudad>"].ToString(); string estado = m.Groups["<ESTADO>"].ToString(); string zip =
m.Groups["<zip>"].ToString();

D.
// C#
Cadena p = @"Nombre: (?<firstName>.*$)\n" +
@"Apellidos: (?<Apellido>.*$)\n" +
@"Dirección: (?<dirección>.*$)\n" +
@"Ciudad: (?<ciudad>.*$)\n" +
@"Estado: (?<estado>.*$)\n" +
@"Zip: (?<zip>.*$)"; m = Match Regex.Match(s, p);
Cadena FullName = m.Groups["<nombre>"] + " " + m.Groups["<Apellido>"];
Cadena dirección = m.Groups[" <dirección>"].ToString();
Ciudad de cadena = m.Groups["<ciudad>"].ToString(); string estado = m.Groups["<ESTADO>"].ToString();
string zip = m.Groups["<zip>"].ToString();

3.  Cuál de las siguientes expresiones regulares coincide con las cadenas "zoot" y
"Zot"?
A.  Z(oo)+t
B.
Zo*t$ 
C.
$zo*t 
D.
^(zo)+t
4.  Cuál  de las siguientes cadenas de caracteres coincide con la expresión regular
"^A(MO)+t.*z$"? (Seleccione todos los que correspondan).
A.  Amotz
B.   Amomtrewz
C.   Amotmoz
D.   Atrewz
E.   Amomomottothez
Lección 2: la codificación y descodificación
Cada cadena y archivo de texto está codificado usando uno de los muchos estándares de
codificación distintos. La mayor parte del tiempo, el .NET Framework gestiona la codificación
para usted automáticamente. Sin embargo, hay veces en que usted podría necesitar para controlar
manualmente la codificación y decodificación, como cuando:
■   interoperar con sistemas UNIX o heredados
■ la   lectura o escritura de archivos de texto en otros idiomas.
■   Creación de páginas HTML
■   generar manualmente los mensajes de correo electrónico
Esta lección describe técnicas de codificación común y le muestra cómo usarlos en aplicaciones
.NET Framework.

Después de esta lección, será capaz de:


■   Describir la importancia de la codificación y lista de estándares de codificación
común.
■   Utilice la   clase Encoding para especificar formatos de codificación, y convertir
entre todos los estándares de codificación.
■   determinar mediante programación qué páginas de código .NET Framework admite.
■   Crear archivos con un formato de codificación específico.
■   leer archivos con formatos de codificación inusuales.
Lección Tiempo estimado: 30 minutos

Comprender la codificación
Aunque no fue el primer tipo de codificación, Código estándar americano para el intercambio de
información (ASCII) sigue siendo la base para los tipos de codificación. Asignados a caracteres
ASCII de 7 bits de bytes utilizando los números de 0 a 127. Estos caracteres incluyen Inglés letras
en mayúsculas y minúsculas, números, signos de puntuación y algunos caracteres de control
especiales. Por ejemplo, 0x21 es "!", 0x31 es "1", 0x43 es "C", 0x63 es "c", y 0x7D es "}".
Mientras ASCII fue suficiente para la mayoría de las comunicaciones en idioma inglés, ASCII no
incluir caracteres usados en alfabetos no inglés. Para permitir que los equipos que se utilizan en los
no-lugares de habla inglesa, los fabricantes han hecho uso de los valores restantes-128 y 255-en un
byte de 8 bits. A lo largo del tiempo, distintos lugares asignados caracteres únicos para valores
mayores de 127. Porque diferentes ubicaciones pueden tener diferentes caracteres asignados a un
único valor, la transferencia de documentos entre diferentes idiomas creado problemas.
Para ayudar a reducir estos problemas, American National Standards Institute (ANSI) define
el código estandarizado páginas que había valores ASCII estándar de 0 a 127, y la lan- meden-
valores específicos para 128 y 255. Una página de códigos es una lista de algunos códigos de
caracteres (los caracteres representados como código de puntos) en un orden determinado. Las
páginas de código se define generalmente en apoyo de idiomas específicos o grupos de idiomas
que comparten recurso- ing sistemas. Las páginas de código de Windows contienen 256 puntos de
código y se basan en cero.
Si has recibido un mensaje de correo electrónico o ver una página Web que parecía tener caracteres
de cuadro o signos de interrogación donde debería aparecer cartas, que han experimentado un
problema de codificación. Porque la gente cree páginas Web y correos electrónicos en muchos
idiomas diferentes, cada uno de ellos debe estar marcado con un tipo de codificación. Por ejemplo,
un mensaje de correo electrónico podría incluir uno de los siguientes encabezados:
Content-Type: text/plain; charset=iso-8859-1
Content-Type: text/plain; charset="Windows-1251"

"ISO-8859-1" corresponde a la página de código 28591, "Europeo Occidental (ISO)". Si se ha


especificado "ISO-8859-7", podría haber contenido caracteres desde el "Griego (ISO)" de la página
de código, número 28597. Asimismo, las páginas Web HTML suelen incluir una etiqueta meta
como uno de los siguientes:
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">

Más y más, ASCII y tipos de codificación ISO 8859 están siendo sustituidas por Unicode. Unicode
es una gran página de códigos con decenas de miles de caracteres que admiten la mayoría de los
idiomas y alfabetos, incluyendo latín, griego, cirílico, árabe, hebreo, chino y japonés (y muchos
otros scripts).
Unicode en sí no especifica un tipo de codificación; sin embargo, hay varias normas para la
codificación Unicode. El .NET Framework utiliza Unicode UTF-16 (Unicode trans- formación
Format, Formato de codificación de 16 bits) para representar los caracteres. En algunos casos, el
.NET Framework utiliza UTF-8 internamente. El  espacio de nombres System.Text proporciona
clases que permiten codificar y decodificar caracteres.  System.Text encoding support incluye los
siguientes tipos de codificaciones:
■ La   codificación Unicode UTF-32  Unicode UTF-32 representa la codificación
Unicode char- acters como secuencias de números enteros de 32 bits. Puede
utilizar la  clase de codificación UTF32para convertir los caracteres a y desde la
codificación UTF-32.
■ La   codificación Unicode UTF-16 la  codificación Unicode UTF-16
representa los caracteres Unicode- acters como secuencias de enteros de 16 bits.
Puede utilizar la  clase UnicodeEncoding para convertir los caracteres a y desde la
codificación UTF-16.
■ La   codificación Unicode UTF-8  Unicode UTF-8 utiliza 8 bits, 16 bits, 24 bits
y 48 bits de codificación. Los valores del 0 al 127, use 8-bit ASCII encoding y
coinciden exactamente con val- ores, proporcionando algún grado de
interoperabilidad. Los valores de 128 a 2047 utilice
 Codificación de 16 bits y prestar apoyo para el latín, griego, cirílico, hebreo y
árabe alfabetos. Valores de 2048 a 65.535 el uso de 24 bits de codificación para
chino, japonés, coreano y otros idiomas que requieren un gran número de valores.
Puede utilizar la  clase UTF8Encoding para convertir los caracteres a y desde la
codificación UTF-8.
■ la   codificación ASCII  CODIFICACIÓN ASCII codifica el alfabeto latino como
solo caracteres ASCII de 7 bits. Porque esta codificación sólo admite valores de
caracteres U+0000 a U+007F, en la mayoría de los casos es insuficiente para
internacionalizarse aplica-
Ema. Puede utilizar la  clase ASCIIEncoding para convertir los caracteres  a y
desde La codificación ASCII.
■   ANSI/ISO codificaciones   System.Text.Encoding clase proporciona soporte
para una amplia variedad de codificaciones de ANSI/ISO.

Más info  Unicode

Para obtener más información sobre Unicode, consulte el Estándar Unicode


en http://www.unicode.org.

Utilizando la clase Encoding


Puede usar System.Text.Encoding.GetEncoding método para devolver un objeto de codificación
para una codificación especificada. Puede utilizar la codificación. método GetBytes para convertir
una cadena Unicode para su representación en bytes en una codificación especificada. En el
ejemplo de código siguiente se utiliza el  método GetEncoding Encoding.Para crear un objeto de
codificación de destino para la página de códigos coreana. El código llama al  método.GetBytes de
codificación para convertir una uni- cadena de código para su representación en bytes en la
codificación coreana. A continuación, el código muestra el byte representaciones de las cadenas en
la página código de Corea.

// C#
// Obtener la codificación coreana
E = codificación Encoding.GetEncoding("Korean");

// Convertir bytes ASCII a coreano codificación codifica; byte[]


Codificado = e.GetBytes("Hello, world!");

// Mostrar los códigos de bytes


(Int i = 0; i < codificados.Length; i++) Console.WriteLine("Byte: {0} {1}", i, codificado[i]);
En este ejemplo de código se muestra cómo convertir texto en una página de códigos diferente; sin
embargo, normalmente no convertir una frase en inglés  en una página de códigos diferente. En la
mayoría de las páginas de códigos, los puntos de código 0 a 127 representan los mismos caracteres
ASCII. Esto permite la continuidad y código heredado. Los puntos de código 128 mediante 255
difieren significativamente entre las páginas de códigos. Porque el código de ejemplo se traduce la
expresión ASCII, "Hello, world!" (que consta enteramente de bytes ASCII comprendidos en el
rango de puntos de código de 0 a 127), la traducción de bytes en el Coreano coincidan exactamente
con la página de códigos ASCII original de bytes.

Más Info  Código páginas


Para obtener una lista de todas las páginas de códigos admitidas, vea el tema
"Codificación" de Clase A http://msdn2.microsoft.com/
En-us/library/system.text.encoding
(VS.80).aspx.

Cómo examinar las páginas de códigos compatibles


Para examinar todas las páginas de códigos admitidas en el .NET Framework,
llamada Encoding.GetEncod- turas. Este método devuelve una matriz de  objetos EncodingInfo. El
siguiente ejemplo de código muestra el número, el nombre oficial y el nombre descriptivo de las
bases de código .NET Framework:

// C#
Ei EncodingInfo[] = Encoding.GetEncodings();
(Foreach EncodingInfo e en ei)
Console.WriteLine("{0}: {1}, {2}", e.CodePage, e.Nombre, e.DisplayName);

Cómo especificar el tipo de codificación al escribir un archivo


Para especificar el tipo de codificación al escribir un archivo, utilice una
sobrecarga Stream construc- tor que acepta un  objeto de codificación. Por ejemplo, el siguiente
ejemplo de código crea varios archivos con diferentes tipos de codificación:

// C#
StreamWriter swUtf7 = new StreamWriter("utf7.txt", falso, la codificación UTF7);
SwUtf7.WriteLine("Hello, World!");
SwUtf7.Close();

StreamWriter swUtf8 = new StreamWriter("utf8.txt", falso, la codificación UTF8).


SwUtf8.WriteLine("Hello, World!");
SwUtf8.Close();

StreamWriter swUtf16 = new StreamWriter("utf16.txt", falso, Codificación.Unicode);


SwUtf16.WriteLine("Hello, World!");
SwUtf16.Close();

StreamWriter swUtf32 = new StreamWriter("utf32.txt", falso.Codificación UTF32);


SwUtf32.WriteLine("Hello, World!");
SwUtf32.Close();

Si ejecuta el ejemplo de código anterior, notará que los cuatro archivos diferentes, cada uno de
ellos tiene diferentes tamaños de archivo: el archivo UTF-7 es de 19 bytes, el archivo UTF-8 es de
18 bytes, el archivo UTF-16 es de 32 bytes y el archivo UTF-32 es de 64 bytes. Si abre cada uno de
los archivos en la nota- pad, UTF-8 y UTF-16 Presentación de archivos correctamente. Sin
embargo, el formato UTF-7 y UTF-32 archivos se muestran incorrectamente. Todos los archivos se
han codificado correctamente; sin embargo, el Bloc de notas no es capaz de leer correctamente en
UTF-7 y UTF-32 archivos.

Nota   Elegir un tipo de


codificación
Si no está seguro de qué tipo de codificación que se utiliza al crear un archivo,
simplemente, acepte el valor predeterminado si no se especifica un tipo de
codificación. .NET Framework se elige UTF-16.

Cómo especificar el tipo de codificación al leer un archivo


Normalmente, no es necesario especificar un tipo de codificación al leer un archivo. .NET
Framework descodifica automáticamente los tipos de codificación más comunes. Sin embargo,
puede especificar un tipo de codificación mediante una  secuencia sobrecargado constructor, como
muestra el ejemplo siguiente:
// C#
Cadena fn = "archivo.txt";
StreamWriter sw = new StreamWriter(fn, false Encoding.UTF7);
Sw.WriteLine("Hello, World!");
Sw.Close();

StreamReader sr = new StreamReader(fn, Codificación.UTF7); Console.WriteLine(sr.ReadToEnd();


Sr.Close();

A diferencia de la mayoría de los tipos de codificación de Unicode, la inusual tipo de codificación


UTF-7 en el previ- ou muestra de código requiere que declare explícitamente que al leer un
archivo. Si se ejecuta el siguiente código, que no especifica el tipo de codificación UTF-7 cuando
lea- ing el archivo, será leído incorrectamente y mostrará el resultado equivocado:
// C#
Cadena fn = "archivo.txt";
StreamWriter sw = new StreamWriter(fn, false Encoding.UTF7);
Sw.WriteLine("Hello, World!");
Sw.Close();

StreamReader sr = new StreamReader(fn); Console.WriteLine(sr.ReadToEnd()); el sr.Close();

Laboratorio: Leer y escribir un archivo codificado


En esta pr ctica, usted podrá convertir un archivo de texto de uno a otro tipo de codificación. Si se
produce un problema de completar un ejercicio, los proyectos terminados están disponibles en el
CD en la carpeta de código.
    Ejercicio: Convertir un archivo de texto a un tipo diferente de codificación
En este ejercicio, puede convertir un archivo de texto en formato UTF-7.
1.  Usar Visual Studio 2005 para crear una aplicación de consola en blanco.
2.  Escribir código para leer el archivo C:\boot.ini y, a continuación, escribir en un
archivo llamado boot- utf7.txt mediante la codificación UTF-7. Por ejemplo, el
código siguiente (que requiere el  espacio de nombres System.IO)  trabajaría:

// C#
StreamReader sr = new StreamReader(@"C:\boot.ini");
StreamWriter sw = new StreamWriter("boot-utf7.txt", falso, la codificación UTF7);
Sw.WriteLine(sr.ReadToEnd();
Sw.Close();
Sr.Close();

3.  Ejecute la aplicación y abrir el maletero-utf7.txt en el Bloc de notas. Si el archivo


se ha traducido cor rectamente, Bloc de notas mostrará con algunos caracteres no
válidos porque el Bloc de notas no admite el tipo de codificación UTF-7.
Resumen de la lección
■ Los   estándares de codificación de caracteres de los valores de byte de mapa. ASCII
es una de las más antiguas, la mayoría de los estándares de codificación
generalizada; sin embargo, ofrece un apoyo muy limitado para idiomas distintos
del inglés. Hoy en día, diversas normas de codificación Unicode proporcionan
compatibilidad multilingüe.
■   El System.Text.Encoding clase proporciona métodos estáticos para la codificación
y decod- ing texto.
■   Llame Encoding.GetEncodings para recuperar una lista de páginas de códigos
compatibles.
■   Para especificar el tipo de codificación al escribir un archivo, utilice una
sobrecarga  con- secuencia structor que acepta un  objeto de codificación.
■   normalmente no tiene que especificar un tipo de codificación al leer un archivo.
De cualquier modo, puede especificar un tipo de codificación mediante
una secuencia sobrecargado constructor que acepte un  objeto de codificación.

Examen de la lección
Puede utilizar las siguientes preguntas para poner a prueba su conocimiento de la información en la
lección 2, "la codificación y la descodificación." Las preguntas también están disponibles en el CD
de iones complementaria si prefiere revisarlos en forma electrónica.
Nota   Respuestas
Las respuestas a estas preguntas y explicaciones de por qué cada opción de
respuesta es correcta o no se encuentran en la sección "Respuestas" al final del
libro.

1.  Cuál de los siguientes tipos de codificación que produciría el mayor tamaño de


archivo?
A.  UTF-32
B.   UTF-16
C.   UTF-8
D.   ASCII
2.  Cuál  de los siguientes tipos de codificación compatible con el chino? (Seleccione
todos los que correspondan).
A.  UTF-32
B.   UTF-16
C.   UTF-8
D.   ASCII
3.  Necesita decodificar un archivo codificado en ASCII. Cuál de los siguientes tipos de
descodificación produciría resultados correctos? (Seleccione todos los que
correspondan).
A.  Codificación UTF32.
B.   Codificación UTF16.
C.   La codificación UTF8.
D.   La codificación UTF7.
4.  Usted está escribiendo una aplicación que genera los informes de resumen nocturnos.
Estos informes serán vistos por los ejecutivos en su oficina de Corea y debe contener
caracteres coreanos. Cuál de los siguientes tipos de codificación que se debe utilizar?
A.  Iso-2022-KR
B.   X-EBCDIC-KoreanExtended
C.   X-mac-coreano
D.   UTF-16.
Repaso del cap tulo
A nuevas prácticas y reforzar las habilidades aprendidas en este capítulo, puede
per- en las siguientes tareas:
■   Revisar el resumen del capítulo.
■   Revisar la lista de términos clave que se introdujo en este capítulo.
■   Complete los casos. Estos escenarios configurar situaciones del mundo real
involv- ción de los temas de este capítulo, y le pedimos a usted para crear una
solución.
■   completar las prácticas sugeridas.
■   tomar un examen de práctica.

Resumen del capítulo


■   Las expresiones regulares tienen raíces en Unix y Perl, y pueden parecer
compli- localizado y antinatural para los desarrolladores de .NET Framework.
Sin embargo, ordinario de expre- siones son extremadamente eficiente y útil
para la validación de la entrada de texto, la extracción de datos de texto, y
formatear los datos.
■   En el pasado decenio, el estándar de codificación más comúnmente usados
para los archivos de texto se ha desplazado gradualmente de ASCII a
Unicode. Sí Unicode admite varios estándares de codificación de dife- rentes.
Aunque .NET Framework utiliza la codificación UTF-16 estándar por
defecto, puede especificar otros estándares de codificación para satisfacer
requisitos erability interop-.

Términos clave
¿ Sabes lo que significan estos términos clave? Puede verificar sus respuestas mediante la
búsqueda de los términos en el glosario al final del libro.
■   página de código
■   expresión regular
■   Unicode

Casos
En los siguientes casos, podrá aplicar lo que ha aprendido sobre cómo val- idate entrada
utilizando expresiones regulares y cómo procesar archivos de texto con diferentes tipos de
codificación. Usted puede encontrar las respuestas a estas preguntas en la sección de
"respuestas" al final de este libro.
Caso práctico 1: Validación de las entradas
Su organización, Neptuno, está creando una aplicación basada en Web para permitir a los clientes
para introducir su propia información de contacto en su base de datos. Como un nuevo empleado,
se le asignará una tarea sencilla: crear la interfaz de front-end y preparar la entrada del usuario para
ser almacenados en una base de datos. Usted debe comenzar por varias entrevistas con el personal
de la empresa y revisar los requisitos técnicos.

Entrevistas
La siguiente es una lista de personal de la empresa entrevistados y sus declaraciones:
■   IT Manager  "Esta es la primera asignación, así que estoy comenzando que fuera
fácil. Bofetada juntos una página Web que toma la entrada del usuario. Que deben
tomar, qué, cinco minutos?".
■   desarrollador de base de datos "simplemente abandona la entrada en cadenas
denominado "" CompanyName, ContactName "" y "" phoneNumber. Es entrar en
una base de datos back-end de SQL, pero voy a escribir ese código después de que
haya terminado. Oh, el "companyName" no puede tener más
A 40 caracteres, "contactName" está limitada a 30 caracteres, y "phoneNum- ber"
está limitada a 24 caracteres."
■   Jefe de Seguridad "Esta no es una tarea tan fácil como parece. Esta página va a
estar disponible al público en Internet, sucias, y hay un montón de sombreros
negros. Hemos obtenido cierta atención negativa en la prensa
Recientemente para nuestras prácticas de comercio internacional. Concretamente,
hemos irritado a cou- ple de grupos con estrechos lazos con organizaciones de
hackers. Acaba de hacer su mejor para limpiar la entrada, porque vas a ver algunos
malintencionados basura arrojada contra ustedes".

Requisitos técnicos
Crear una aplicación ASP.NET que acepta los siguientes elementos de información de los usuarios
y la valida rigurosamente:
■ El   nombre de la empresa
■   Nombre de contacto
■   Número de teléfono

Preguntas
Responda las siguientes preguntas para el administrador:
1.  ¿Cómo se puede restringir la entrada antes de escribir cualquier tipo de código?
2.  ¿Cómo se puede limitar aún más la entrada escribiendo código?
Caso práctico 2: Procesamiento de datos desde un equipo
heredado
Usted es un desarrollador de aplicaciones de trabajo Seguros inmensos. Recientemente, ges-
tión decidió iniciar el proceso de migración de un sistema heredado (apodado "mainframe")
personalizado a las aplicaciones .NET Framework. Como parte de la reunión inicial para el
proyecto de migración, su jefe le pregunta acerca de cómo va a manejar varios desafíos.

Preguntas
Responda las siguientes preguntas para el administrador:
1.  Mainframe almacena sus datos en una base de datos; sin embargo, los datos en
bruto no es en sí accesibles a nosotros a menos que podamos encontrar un
programador que sabe cómo escribir código para ese sistema. Podemos
generar los datos que necesitamos en informes basados en texto, sin embargo.
Es posible analizar el texto informes para extraer sólo los datos sin formato y
las etiquetas? Cómo se puede hacer eso, y qué clases y métodos que debe
utilizar?
2.  Informes del mainframe están en formato ASCII. Puede manejar que ASCII?
Si es así, ¿cómo?

Prácticas recomendadas
Que le ayudarán a dominar los objetivos del examen se presenta en este capítulo, complete las
siguientes tareas.

Mejorar las capacidades de manejo de texto de un archivo .NET


Framework Aplicación y buscar, modificar y controlar el texto
dentro de un Aplicación de .NET Framework usando xpresiones
regulares.
Para esta tarea, se deben completar un mínimo de prácticas 1 a 4. Si desea una mejor
comprensión de cómo especificar tipos de codificación, la práctica completa 5.
■   prácticas 1  Escribir una aplicación de consola que lee el archivo
C:\boot.ini y dis- se juega exactamente el tiempo de espera.
■ La   Práctica 2  Escribir una aplicación de consola que procesa el archivo
%windir%\Windows Update.log y muestra la hora, la fecha y el código de
salida de las filas que muestran un código de salida.
■ La   Práctica 3  Escribir una aplicación de Windows Forms que acepta un
nombre, dirección y número de teléfono de un usuario. Agregar un botón
Enviar que usa regular expre- siones para validar la entrada.
■ La   práctica 4:  Escribir una aplicación de consola que lee el archivo %windir
%\Windows Update.log, cambia el formato de fecha dd-mm-aa, y escribe el
resultado en un segundo archivo.
■   Práctica 5  Write una aplicación de consola de ingenio h cumplido hodth en
re anuncios t h e %Windir%\Windows Update.log y escribe el resultado en un
segundo archivo utilizando Un tipo de codificación en un parámetro. Compare
los tamaños de archivo de cada encod- ing tipo.

Tomar un Test de práctica


Los tests de práctica en este libro's Companion CD ofrecen muchas opciones. Por
ejemplo, puede hacerse la prueba en un solo examen objetivo, o puede probar usted
mismo en todos los exámenes de certificación 70-536 contenido. Puede configurar la
prueba para que simula cuidadosamente la expe- riencia de tomar un examen de
certificación, o puede configurarlo en modo de estudio, de modo que usted puede mirar
las respuestas correctas y explicaciones después de responder a cada pregunta.

Más info  práctica de pruebas


Para obtener más información acerca de la prueba de la práctica todas las opciones
disponibles, consulte la sección "Cómo usar los tests de práctica"
La sección en este manual de introducción.
Capítulo 4

Colecciones y genéricos

Colecciones de clases que se utiliza para agrupar y gestionar objetos relacionados que le permiten
iterar sobre esos objetos son una de las herramientas más básicas en cualquier Developer's
toolchest. Permiten almacenar, buscar, e iterar sobre colecciones de objetos. Colecciones toman
más en Matrices Matrices. son útiles, pero sin la riqueza de las colecciones la mayoría de las
aplicaciones nunca consigue despegar.

Objetivos del examen en este capítulo:


■   Gestionar un grupo de datos relacionados en una aplicación .NET Framework
utilizando
Colecciones. (Consulte el  espacio de nombres System.Collections)
❑    clase ArrayList
❑   interfaces de colección
❑   iteradores

❑    clase Hashtable
❑   CollectionBase  clase y  clase ReadOnlyCollectionBase
❑   DictionaryBase clase y  clase DictionaryEntry
❑   Comparer  class
❑    clase Queue
❑     clase SortedList
❑   BitArray  class
❑    clase Stack
■   Gestionar los datos en una aplicación .NET Framework mediante colecciones
especializadas. (Consulte la sección System.Collections.Specialized )
❑   especializado  clases String
❑    diccionario especializado
❑    clase NameValueCollection
❑   CollectionsUtil

❑   BitVector32  Estructura y BitVector32.la sección estructura

185
■   Mejorar la seguridad de tipos y el rendimiento de la aplicación en un archivo
.NET Framework aplica- ción mediante las colecciones genéricas. (Consulte
el  espacio de nombres System.Collections.Generic)
❑   Colección.interfaces genéricas
❑    diccionario genérico
❑    Comparer genérico clase y  clase EqualityComparer genérico
❑     estructura KeyValuePair genérico
❑ Una     lista
genérica de clase, la lista Genérica.estructura de enumerador
y ordenar- edList clase genérica
❑    cola genérica de clase y cola genérica.estructura de enumerador
❑    SortedDictionary clase genérica
❑    LinkedList genérico
❑ una   clase de pila genérica y Generic Stack.estructura de enumerador

Las lecciones de este capítulo:


■ La   Lección 1: recopilar elementos de datos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . 187
■ La   Lección 2: Trabajar con las listas secuenciales . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . 201
■ La   Lección 3: trabajar con diccionarios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
209
■ La   Lección 4: Utilización de las colecciones especializadas . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . 226
■   Lección 5: Colecciones genéricas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. 241

Antes de empezar
Para completar las lecciones de este capítulo, debe estar familiarizado con Microsoft Visual
Basic o C# y cómodo con las siguientes tareas:
■   Crear una aplicación de consola en Microsoft Visual Studio, usando Visual Basic
o C#.
■   Sistema de agregar referencias a bibliotecas de clases
para un proyecto.

Mundo Real
Shawn Wildermuth
Las colecciones son el fundamento de la mayor parte de las aplicaciones se escribirá como
desarrollador. Prácticamente todas las aplicaciones que he desarrollado nunca ha utilizado
colecciones ampliar- clusivamente. Por ejemplo, he utilizado colecciones para almacenar listas
de mensajes de correo electrónico que estoy listo para procesar en un sistema de correo
electrónico que escribí hace algunos años.

Lección 1: recopilar elementos de datos


Los equipos son naturalmente buenos en el manejo de grandes cantidades de datos. En su trabajo
diario como desarrollador, encontrará que es necesario almacenar los datos en una forma ordenada.
El .NET Framework admite tratar los datos de esta forma, ofreciendo una amplia gama de
colecciones para almacenar sus datos. Para cada trabajo de colección, el .NET Framework sup-
capas de una solución.

Después de esta lección, será capaz de:


■   Crear una colección.
■   Agregar y quitar elementos de una colección.
■   iterar sobre los elementos de una colección
Lección Tiempo estimado: 15 minutos.

Tipos de colecciones
.NET Framework el  espacio de nombres System.Collections admite varios tipos de colectores-
ciones. Estas colecciones son clases que admiten la obtención de información en forma ordenada.
Su reto será discernir la colección para utilizarla en una instancia específica. La tabla 4-1 muestra
las colecciones más frecuentemente utilizado en el sistema.Colectores- nes de espacio de
nombres  y para qué se utilizan.
Tabla 4-1  Tipos de colecciones

Nombre Descripción
   Un simple resizeable ArrayList, basado en índices, colección de
objetos SortedList  una recopilación ordenada de pares
nombre/valor de los objetos cola   un primero en entrar,
primero en salir colección de objetos.
   Una pila de último en entrar, primero en salir colección de objetos.
Hashtable  una colección de pares nombre/valor de objetos que
permite la recuperación por nombre o índice
BitArray  una colección compacta de  valores
booleanos StringCollection  una simple colección de cadenas
resizeable StringDictionary  una colección de pares nombre/valor de
cadenas que permite
Por nombre o índice de recuperación

Tabla 4-1  Tipos de colecciones

Nombre Descripción
ListDictionary  una recaudación eficaz para almacenar pequeñas listas de
objetos
   Una colección que utiliza HybridDictionary ListDictionary para
almacenamiento cuando el número de elementos de la
colección es pequeña, y después migra los elementos a
una Hashtable para grandes colecciones
NameValueCollection  una colección de pares nombre/valor de cadenas que le
permite recuperar por nombre o índice

Todas estas colecciones se utilizan en una variedad de situaciones. Las primeras cuatro lecciones
de este capítulo le explicará cómo usar estas colecciones, así como cuándo utilizar que recogió-
ción. Para el resto de la lección 1, utilizaremos la  colección ArrayList para almacenar y recuperar
objetos. ArrayList es la más básica de todas las colecciones.

Adición y eliminación de elementos


La  clase ArrayList es un simple contenedor, desordenada de los objetos de cualquier tipo. Agregar
y quitar elementos de la colección es muy sencillo.
ArrayList admite dos métodos para agregar elementos a la colección: Agregar y AddRange.
El  método Add permite añadir un solo objeto a la colección. Puede utilizar el  método Add para
almacenar cualquier objeto en .NET. Aquí están algunos ejemplos de código que agrega objetos de
distintos tipos a un ArrayList:
// C#
ArrayList coll = new ArrayList();

// Agregar elementos individuales a la colección string s =


"Hola";
Coll.Add(s); coll.Add("Hola");
coll.Add(50); coll.Add(new
Object();
Observe que puede agregar objetos que existen como variables o se crean en línea con el   método
Add. Incluso puede agregar tipos de valores (tales como el número '50' En el ejemplo anterior).
Los tipos de valor pueden ser almacenados en las colecciones, pero primero necesitan ser envuelto
en una referencia de objeto, un proceso conocido como el boxeo.

Más info El  boxeo


Para comprender mejor el boxeo, ver Eric Gunnerson's " Cuadro de Niza. Lo que
está en él?" El artículo de MSDN
Online: http://msdn.microsoft.com/library/default.asp?URL=/library/en-
us/dncscol/html/ csharp02152001.asp

Además de apoyar el  método Add, el ArrayList admite el  método AddRange para agregar una
gama de elementos, generalmente a partir de una matriz o de otra  colección. El código siguiente
proporciona un ejemplo:

// C#
String[] anArray =
New string[] { "más", "o", "menos" };
Coll.AddRange(anArray);

Object[] = anotherArray
New Object[] { new Object(), new ArrayList(); }
Coll.AddRange(anotherArray);

El  método AddRange admite la adición de un rango de elementos desde cualquier objeto que
admita la  interfaz ICollection (que incluye todos los arrays, ArrayList de objetos, y la mayoría de
los colectores- nes tratadas en este capítulo).
Los    métodos Add y AddRange agregar elementos al final de la colección. Porque Array- listas
son colecciones dinámicas, también permiten insertar objetos en ellos en posiciones específicas.
Para llevar a cabo su tarea, t un ArrayList también admite t él   InsertRange inserta y métodos. El
código siguiente proporciona un ejemplo:
// C#
Coll.Insert(3, "Hola a todos");

String[] moreStrings =
New string[] { "Goodnight", "ya" };
Coll.moreStrings InsertRange(4);

Además de insertar y agregar métodos, usted puede también utilizar el indizador para establecer


un objeto- pecífico de la colección, como se muestra en el siguiente código:

// C#
Coll[3] = "Hola a todos".
Tenga en cuenta que utilizando el indizador no es lo mismo que usar el  método Insert, como lo
establece el elemento en esa ubicación concreta en la colección sobrescribiendo el objeto antiguo
en esa posición en lugar de insertar un objeto.
Por último, el ArrayList apoya la eliminación de elementos de la colección. Tres métodos de
eliminación de elementos de apoyo: Quitar,  RemoveRange RemoveAt , y. El  método Remove
eliminará un objeto específico de la colección. No hay ninguna indicación de si el  retiro no se
pudo encontrar el elemento que desea eliminar. En otras palabras, si no se encuentra el elemento en
la colección, quitar volverá sin lanzar una excepción. El  método Remove es demostrado en el
ejemplo siguiente:
// C# coll.Add("Hello"); collQuitar("Hello");

En contraste, el  método RemoveAt quita un elemento en un índice en particular dentro de la col-


lection. Además, el  método RemoveRange permite eliminar una amplia gama de índices de la
colección de una vez por todas. Ambos métodos se muestran aquí:

// C#
// Elimina el primer elemento de ArrayList coll.RemoveAt(0).
// Quita los primeros cuatro artículos en ArrayList coll.RemoveRange(0, 4).

La  clase ArrayList admite también algunos otros métodos que son útiles para agregar objetos y
eliminación de objetos de la colección:
■   El  método Clear se usa para vaciar una colección de todos sus elementos.
■   El  método IndexOf es utilizado para determinar el índice de un determinado
elemento de la colección.
■   El  método Contains se utiliza para comprobar si un determinado objeto existe en
la colección.
Al utilizar estos métodos, puede realizar más compleja adición y eliminación de elementos dentro
de la colección, como se muestra en este ejemplo:
// C#
Cadena myString = "Mi cadena".
Si (collcontiene(Micadena)
{
Int index = collIndexOf(Micadena);
Coll.RemoveAt(index);
}
Otra cosa
{
Coll.Clear();
}

Ahora puede manipular los objetos de la colección, pero ¿cómo puede sacarlos de la colección?
Iterar a través de elementos
Una colección no es muy útil a menos que usted puede caminar a través de los elementos que
contiene. Afortunadamente, el ArrayList (como la mayoría de las colecciones en este capítulo)
admite varias maneras para iterar sobre su contenido. El ArrayList admite un indizador numérico
que permite escribir código sencillo, como la siguiente, para mostrar los elementos en orden (de
manera muy similar a como se haría con una matriz):
// C#
(Int x = 0; x < collcontar; ++x)
{
Console.WriteLine(coll[x]);
}

Mediante el ArrayList's  propiedad Count y el indizador, puede simplemente caminar a través de la


colección. El ArrayList también admite la  interfaz IEnumerable para permitir el uso de
un enumerador para acceder a la lista. La  interfaz IEnumerable dicta que la clase sup- puertos
el  método GetEnumerator que devuelve una  interfaz IEnumerator. A su vez, la  interfaz
IEnumerator proporciona un mecanismo sencillo para desplazase en dirección de avance. Los
detalles de la  interfaz IEnumerator puede verse en la Tabla 4-2 (que muestra las propiedades) y 4-
3 (que muestra métodos).
Tabla 4-2  IEnumerator  Propiedades

Nombre Descripción
Corriente  obtiene el elemento actual de la colección que se enumera

Tabla 4-3  IEnumerator  métodos

Nombre Descripción
MoveNext  mueve al siguiente elemento de la colección. El valor
devuelto por el método utilizado para determinar si el
enumerador ha llegado al final de la colección.
Restablecer Establece el empadronador antes del primer elemento de la
colección para permitir MoveNext para ser llamados a
obtener el primer elemento de la colección.

Utilizando la  interfaz IEnumerator le permite caminar a través de la lista de objetos en forma


ordenada, como se ve en este ejemplo:
// C#
 Enumerador IEnumerator = collGetEnumerator();
Mientras (enumerador.MoveNext()).
{
Console.WriteLine(enumerador.Actual);
}

En este ejemplo se muestra el modelo simple de obtener un enumerador de la colección y el uso de


la  llamada a MoveNext para caminar a través de la lista. Acceso a la  propiedad actual del
enumerador devuelve el elemento actual de la lista.
Visual Basic y C# admite un nivel de lenguaje construir para hacer esto mismo enu- meration en
una forma más simplificada: foreach. Mediante la  construcción de Foreach, puede enumerar una
lista completa, como se muestra en el siguiente ejemplo:
// C#
(Objeto elemento foreach en coll)
{
Console.WriteLine(elemento);
}

La  construcción foreach especifica que está enumerando el coll de objetos y crear un  objeto de
elemento para cada elemento de la colección. Esta construcción se basa en la  interfaz
IEnumerable. Puede ser utilizado en cualquier colección que admite la  interfaz IEnumerable. Una
de las ventajas de este esquema de iteración es que si usted tiene una colección de algunos tipos
conocidos, puede especificar el tipo de la  construcción foreach para ahorrar tiempo en la fundición
de objetos, como se puede ver en este fragmento de código:
' VB
Dim newColl como nuevo newColl
ArrayList().Add("Hello") newColl.Add("Adiós").

Para cada elemento como cadena en newColl


Console.WriteLine(item) Siguiente tema

// C#
ArrayList newColl = new ArrayList();
newColl.Add("Hello"); newColl.Add("Adiós");

(String elemento foreach en newColl)


{
Console.WriteLine(elemento);
}

Porque usted sabe que todos los elementos de la colección son cadenas, puede especificar
la cadena como el tipo de elementos para iterar. Si la colección había un elemento que no era una
cadena, el .NET Framework arrojaría un casting de excepción.
Interfaces consistentes en colecciones
Como se mencionó en la sección anterior, la  interfaz IEnumerable se utiliza para
proporcionar un modo común para iterar en una colección. Además, .NET Framework admite
otra interfaz importante que debe ser implementada en el programa de aplicación- Ming de
interfaz (API) de una clase de colección. Esta interfaz se llama la  interfaz ICollection y
deriva de la  interfaz IEnumerable. Esto significa que cada colección que admita la  interfaz
ICollection también debe admitir la  interfaz IEnumerable.
El objetivo de esta interfaz es asegurar que cada colección admite una forma común de
obtener los elementos de una colección, así como una forma de copiar la colección a
un  objeto Array. La  interfaz ICollection más importante de propiedades y métodos se
muestran en la Tabla 4-4 y Tabla 4-5, respectivamente.
Tabla 4-4  ICollection  Propiedades

Nombre Descripción
Conde  obtiene el número de elementos de la colección
actualmente IsSynchronized  obtiene un indicador de si la colección es
seguro para los subprocesos SyncRoot  obtiene un objeto que
puede utilizarse para sincronizar el
Colec
ción

Tabla 4-5    métodos ICollection

Nombre Descripción
CopyTo  copia el contenido de una colección en una matriz.

Para simple lista de colecciones (como ArrayList), el .NET Framework admite una interfaz
que se utiliza para exponer las listas de elementos. Esta interfaz se llama la  interfaz IList y
deriva directamente de la  interfaz ICollection. Si una clase admite la  interfaz IList, debe
apoyar también la interfaz ICollection e  interfaces IEnumerable. Esta
coherencia  de interfaces simplifica la forma en que trabajamos con colecciones en general.
La mayoría de la  interfaz IList debe resultarle familiar a usted en este punto. En la sección
"Agregar y Quitar elementos" anteriormente en este capítulo, la mayoría de estas propiedades
y métodos fueron cubiertos como parte de la  clase ArrayList. La  interfaz IList es más
importante prop- erties y métodos se muestran en la Tabla 4-6 y Tabla 4-7, respectivamente.
Tabla 4-6  IList  Propiedades

Nombre Descripción
IsFixedSize   obtiene un indicador de si esta colección puede ser
redimensionado IsReadOnly  obtiene un indicador de si una colección
puede ser cambiado Item   obtiene o establece el elemento de un
índice específico de la colección

Tabla 4-7    métodos IList

Nombre Descripción
Add  agrega un elemento a la colección
Clear  borra la colección de todos los elementos
Contiene  tests si un elemento está contenido en la colección
IndexOf Finds un elemento en la colección, y devuelve el índice del
elemento
Inserte   agrega un elemento a un índice específico de la colección
Quitar  Quita la primera aparición del objeto especificado en la
colección
RemoveAt  quita un elemento en un índice específico de la colección

Ordenar elementos
El ArrayList admite un método para ordenar los elementos de una colección. Para ordenar los
elementos dentro de un
ArrayList, basta con llamar al  método Sort de ArrayList así:
// C#
Coll.Sort();

El  método Sort funciona utilizando el comparador clase para hacer la comparación.


Los com- parer clase es una implementación predeterminada que admite la  interfaz IComparer.
Esta interfaz indica que implementar un método llamado comparar que toma dos objetos (por
ejemplo, a y b) y devuelve un entero que representa el resultado de la comparación. El resultado es
interpretado como se muestra en la Tabla 4-8.
Tabla 4-8  comparar  resultados

Condición de valor
Menor que cero el objeto izquierdo es menor que el objeto de derecho. Los
objetos son iguales a cero.
Más de cero a la izquierda es el objeto más que el objeto de derecho.

El  método Sort permite especificar un  objeto IComparer para utilizar en lugar  de la


predeterminada. Por ejemplo, en lugar de utilizar la  clase Comparer podría especificar un caso-
mediante la comparación insensible CaseInsensitiveComparer así:
// C#
Coll.Sort(nueva CaseInsensitiveComparer();

Escribir su propio comparer es relativamente sencillo, ya que sólo requiere que usted para
implantar el  método Compare de la  interfaz IComparer. Por ejemplo, si quería hacer la
comparación en sentido inverso (resultando en una colección clasificada en orden descendente),
puede escribir una clase comparador rápido así:

// C#
Public Class DescendingComparer : IComparer
{
CaseInsensitiveComparer _comparer = new CaseInsensitiveComparer();

Público objeto int Comparar (x, y) del objeto


{
// Invertir la comparación objetos a
// Obtener comparaciones descendente volver _comparer.Compare(y, x);
}
}

Esta clase implementa la  interfaz IComparer. En nuestro  método Compare, simplemente estamos


invirtiendo la derecha e izquierda para obtener comparaciones de la comparación en el lado
opuesto (o descendente).
Entonces podemos usar este nuevo objeto de comparación en ordenar nuestra colección en orden
descendente:

// C#
Coll.Sort(nueva DescendingComparer();

Laboratorio: ordenar una tabla de cadenas


En esta práctica, va a crear una colección de cadenas y ordenarlas. Si se produce un problema de
completar un ejercicio, los proyectos terminados están disponibles en el CD de iones
complementaria en la carpeta de código.
Ejercicio 1: Crear una colección de cadenas y ordenarlas
En este ejercicio vamos a crear una aplicación de consola nueva que crea un sencillo recogió- ción,
agrega varias cadenas, y los muestra en la ventana de la consola. Entonces usted podrá ordenar la
colección y mostrar los elementos de la colección en la ventana de la consola en el nuevo orden.
1.  Crear una nueva aplicación de consola denominada BasicCollection.
2.  En el archivo de código principal, incluir (o importación para Visual Basic)
el System.Collections
Espacio de nombres.
3.  En el  método Main del proyecto, crear una nueva instancia de la  clase ArrayList.
4.  Agregar cuatro cuerdas a la nueva colección "primerasegunda ", "", "", y "
tercercuarto".
5.  Iterar sobre la colección y mostrar cada elemento de la ventana de la consola en
una línea separada.
6.  Llame al  método Sort siguiente en la colección para ordenar sus elementos.
7.  Iterar sobre la colección de nuevo y mostrar cada elemento de la ventana de la
consola para confirmar que la orden es diferente ahora. El código resultante podría
ser algo como esto:

// C#
Utilizando System.Collections.

Programa de clase
{
Static void main(String[] args)
{
ArrayList myList = new ArrayList();
myList.Add("First"); myList.Add("Segundo");
myList.Add("tercera"); myList.Add("Cuarta");

(Cadena elemento foreach en myList)


{
Console.WriteLine("Sin clasificar: {0}", tema);
}

// Ordenar utilizando el comparador estándar


myList.Sort();

(Cadena elemento foreach en myList)


{
Console.WriteLine(" ordena: {0}", tema);
}
}
}

8.  Genere el proyecto y resolver cualquier error. Compruebe que la aplicación de


consola éxito- demuestra plenamente los elementos en la ventana de la consola,
ambos sin clasificar en primera y, a continuación, se ordenan alfabéticamente .
Resumen de la lección
■   El .NET Framework es compatible con una variedad de clases de colección que
puede utilizarse en diferentes circunstancias.
■   El ArrayList es una simple recopilación de elementos desordenados.
■   Los     métodos Add y AddRange de ArrayList se utilizan para agregar elementos
a un
ArrayList.
■   El inserto y  métodos InsertRange de ArrayList se utilizan para insertar elementos
en lugares específicos en una colección.
■   El Quitar, RemoveAty RemoveRange métodos de ArrayList son utilizados para
eliminar elementos de una colección.
■   El indizador de ArrayList se puede utilizar para iterar sobre una colección.
■   Las    interfacesIEnumerable e IEnumerator puede utilizarse para enumerar una
colección tan bien.
■   La  construcción foreach en Visual Basic y C# utilizan la  interfaz IEnumerable
para iterar en una colección de forma concisa.

Examen de la lección
Puede utilizar las siguientes preguntas para poner a prueba su conocimiento de la información en la
lección 1, "Recopilación de datos Artículos." Las preguntas también están disponibles en el CD
complementario si prefiere revisarlos en forma electrónica.

Nota   Respuestas
Las respuestas a estas preguntas y explicaciones de por qué cada opción de
respuesta es correcta o no se encuentran en la sección "Respuestas" al final del
libro.

1.  Cuál de los siguientes  métodos de ArrayList puede utilizarse para determinar si


existe un elemento en la colección? (Seleccione todos los que correspondan).
A.
Quitar B.
Contiene C.
IndexOf D.
Contar
2.  ¿Cuál es la  clase Comparer utiliza? (Seleccione todos los que
correspondan).
A.  Para comparar dos objetos, normalmente para ordenar
B.   Para comprobar si dos variables son referencias al mismo objeto
C.   Para ordenar un ArrayList en el ArrayList.El método Sort
D.   Para proporcionar una implementación predeterminada de la  interfaz
IComparer
Lección 2: Trabajar con las listas secuenciales
No todas las colecciones son creadas iguales. A veces, tiene más sentido para lidiar con una lista de
objetos como una lista secuencial de elementos, en lugar de acceder a ellos de forma individual.

Después de esta lección, será capaz de:


■   Crear y utilizar un primero en entrar, primero en salir (FIFO) colección
■   Crear y utilizar un último en entrar, primero en salir (LIFO) colección.
Lección Tiempo estimado: 10 minutos

¿Qué son las listas secuenciales?


A menudo las colecciones son simplemente un conjunto de objetos que necesitan ser tratadas en
forma ordenada. Por ejemplo, puede crear una clase que acepta objetos a realizar algún tipo de
trabajo en contra. Dependiendo de los requisitos específicos, es probable que usted necesita para
tener acceso a los objetos de uno en uno y procesarlos. Tener acceso a elementos en el medio de la
lista tiene muy poco uso para su clase.
En lugar de usar una colección como ArrayList, puede utilizar otras dos clases expuesta por .NET
Framework, cuya tarea es la de almacenar datos como una lista y simplemente permitir el acceso a
dichos objetos como sean necesarias. La cola y apilar las clases están diseñadas para ser usadas
para almacenar datos en una base secuencial.

La   clase Queue


La  clase Queue es una colección para tratar primero en entrar, primero en salir (FIFO)
manipulación de objetos secuenciales. La interfaz para la  clase Queue es muy simple: se admite
poner elementos en la cola y tirando de ellas.
La  clase Queue funciona bastante diferente que el ArrayList se muestra en la Lección 1.
Específica- ically, acceso y eliminación de elementos de la colección fueron dos operaciones
diferentes en el ArrayList. La cola combina estas operaciones en el  método Dequeue. Las
operaciones son combinadas, principalmente porque la  clase Queue está concebido para ser
utilizado con cre- comió listas de objetos para trabajar con el objetivo de alguna manera. Como su
nombre sugiere, la cola trata los objetos como clientes en línea en el banco. Cuida sólo acerca de
quién es el próximo y donde agregar personas al final de la línea.
Las propiedades más importantes y los métodos de la  clase Queue se muestran en la Tabla 4-9 y la
tabla 4-10, respectivamente.
       Propiedades de la cola de la tabla 4-9

Nombre Descripción
Conde  obtiene el número de elementos en la cola.

La tabla 4-10    Métodos de cola

Nombre Descripción
Dequeue  recupera un elemento desde la parte delantera de la cola,
eliminando al mismo tiempo
Enqueue  agrega un elemento al final de la cola.
Peek  recupera el primer elemento de la cola sin quitarlo
realmente

Trabajar con la  clase Queue es muy sencillo. Una vez que tenga una instancia de la clase,
puede utilizar el  método Enqueue para agregar elementos a la cola y el  método Dequeue
para quitar elementos de la lista, como se demuestra en este breve ejemplo:
// C#
La cola q = nueva cola();
q.Enqueue("Un tema");
Console.WriteLine(q.Dequeue();

La  clase Queue permite agregar elementos duplicados y valores nulos, por lo que no se puede
comprobar el resultado de la cola o el  método Peek para ver si la cola está vacía. Para ello,
puede comprobar la  propiedad Count para ver si la colección está vacía. Por ejemplo, si
agrega elementos a la cola y desea eliminarlos y mostrarlas en la consola, se puede escribir un
código como el siguiente:
// C#
La cola q = nueva cola();

Q.Enqueue("First");
q.Enqueue("Segundo"
);
q.Enqueue("tercera");
q.Enqueue("Cuarta");

Mientras (q.Count > 0)


{
Console.WriteLine(q.Dequeue();
}

Porque la cola es una colección de FIFO, en el ejemplo anterior se producirá la fol- bramido
orden de visualización en la ventana de la consola:
Primer Segundo Tercer cuarto
Hay momentos en el que ser capaz de mirar al siguiente elemento sin quitar realmente es una
buena idea. Imagínese si usted tenía algún código que podría trabajar con determinados tipos
de objetos. Si usted fuera a Dequeue y luego descubre que alguien tenía que manejar, usted
podría poner de nuevo en la cola, pero perdería su lugar en la línea. Ahí es donde  entra en
juego el método Peek, como se muestra en este ejemplo:
// C#
Si (q.Peek() es una cadena)
{
Console.WriteLine(q.Dequeue();
}

A veces, la colección de tipo secuencial que necesita no es el primero en entrar, primero en


salir, pero en el último en entrar, primero en salir. Ahí es donde  entra en juego la clase Stack.

La   clase Stack


En contraste con la  clase Queue, la  clase Stack es el último en entrar, primero en salir
(LIFO) colección. La interfaz de la  clase Stack es también muy simple: soporta empujando
elementos en la pila y chasquidos.
Como usted probablemente puede adivinar por su nombre, la  clase Stack más estrechamente
ejemplifica una pila de cartas. Como usted toma cartas de la pila, puede tirar una tarjeta fuera
de la parte superior, pero nov Cavar hacia abajo en la pila para obtener una tarjeta. Las
propiedades más importantes y los métodos de la  clase Stack se muestran en la tabla 4-11 y la
tabla 4-12, respectivamente.
Tabla 4-11    propiedades Pila

Nombre Descripción
Conde  obtiene el número de elementos de la pila

La tabla 4-12    Métodos de pila

Nombre Descripción
Pop  recupera un elemento desde la parte superior de la pila,
eliminando al mismo tiempo
Push  agrega un elemento a la parte superior de la pila
Peek  recupera el elemento superior de la pila sin extraerlo

Trabajar con la  clase Stack es similar a trabajar con la  clase Queue, pero en lugar de
enqueuing y cola, que están empujando en y saltando fuera de la pila. Una vez que tenga una
instancia de la clase, se utiliza el  método Push para agregar elementos a la pila y el  método
Pop para eliminar elementos de la pila, como se muestra en este breve ejemplo:
// C#
Pila s = new Stack(); s.("push");
un elemento
Console.WriteLine(s.Pop();

Como con la  clase Queue, puede agregar duplicados y valores nulos, por lo que no se puede
comprobar el resultado del pop o  método Peek para ver si la pila está vacía. Por ejemplo, si
agrega elementos a la pila y desea eliminarlos y mostrarlas en la consola, se puede escribir un
código como el siguiente:
// C#
Pila s = new Stack();

S.push("First"); s.push("Segundo"); s.push("tercera");


s.push("Cuarta");

Mientras (s.Count > 0)


{
Console.WriteLine(s.Pop();
}

Debido a que la pila es una colección LIFO, el orden de los resultados de este código están
invertidas, por lo que vimos anteriormente en la  clase Queue ejemplo:
Cuarto Segundo tercio
Primera

 Punta del examen   exigente de una cola  de una pila  es simple si se piensa


en ellas como sus equivalentes en el mundo real. Las colas son líneas al cine; las
pilas son papeles en su escritorio. Nunca tienes que pasar primero cuando otros
están en línea antes que en una sala de cine, y es probable que siempre agarra
la hoja en la parte superior de una pila de papeles.

Laboratorio: Construyendo FIFO y LIFO listas


En este laboratorio, debe crear una cola y una pila y mostrar sus datos a la consola. Si se
produce un problema de completar un ejercicio, los proyectos terminados están disponibles en
el CD en la carpeta de código.
   Ejercicio 1: Crear y utilizar una cola.
En este ejercicio, creará una cola, se le agregan elementos y vaciar la cola de la ventana de la
consola.
1.  Crear una nueva aplicación de consola denominada SequentialCollections.
2.  En el archivo de código principal, incluir (o importación para Visual Basic)
el System.Collections Espacio de nombres.
3.  En el  método Main del proyecto, crear una nueva instancia de la  clase Queue.
4.  Agregar cuatro cuerdas a la nueva colección:  "primero", "segundo", "tercer"
y "cuarto".
5.  Vacíe la cola, un elemento a la vez, se utiliza la  propiedad Count para probar
si la colección está vacía. El código resultante podría tener este aspecto:

// C#
Utilizando System.Collections.

Programa de clase
{
Static void main(String[] args)
{
Cola = nueva cola();

La cola.Enqueue("First"); la
cola.Enqueue("Segundo");
la cola.Enqueue("tercera");
la cola.Enqueue("Cuarta");

Mientras (cola.Count > 0)


{
Object OBJ = queue.Dequeue();
Console.WriteLine("de cola: {0}", obj);
}
}
}

6.  Genere el proyecto y resolver cualquier error. Compruebe que la aplicación de


consola suc- logre ejecuta y muestra los elementos de la cola, en primero en
entrar, primero en salir.

    Ejercicio 2: Crear y utilizar una pila


En este ejercicio, creará una pila, agregar elementos, y vacía la pila a la ventana de la consola.
1.  Abra la aplicación de consola que creó en el ejercicio 1, llamado- secuencial
de las colecciones.
2.  Después de la cola , el código crea una nueva instancia de la  clase Stack.
3.  Agregar cuatro cuerdas a la pila: "primero", "segundo", "tercer"y "cuarto".
4.  Vacíe la cola, un elemento a la vez, se utiliza la  propiedad Count para probar
si la colección está vacía. El código resultante podría tener este aspecto:

// C#
Stack pila = new Stack();

Stack.push("First");
stack.push("Segundo");
stack.push("tercera");
stack.push("Cuarta");
Mientras (pila.Count > 0)
{
Object OBJ = stack.Pop(); Console.WriteLine("de la
pila: {0}", obj);
}

5.  Genere el proyecto y resolver cualquier error. Compruebe que la aplicación de


consola suc- logre ejecuta y muestra que los elementos de la pila están
invirtiendo el orden de los elementos de la cola (es decir, en el último en
entrar, primero en salir).

Resumen de la lección
■   El .NET Framework admite la cola y pila para ofrecer clases de colectores-
ciones que representan las listas secuenciales de elementos.
■   La cola es el primero en entrar, primero en salir (FIFO) colección.

■   La  clase Queue apoya la enqueue and dequeue   métodos para agregar y


quitar elementos de la colección.
■   La pila es el último en entrar, primero en salir (LIFO) colección.
■   La  claseStack Push y Pop admite los métodos para agregar y eliminar
elementos, respectivamente, de la colección.
■   ambos secuencial de las clases de colección apoyo Peek para ver el siguiente
elemento de la colección sin extraerlo.

Examen de la lección
Puede utilizar las siguientes preguntas para poner a prueba su conocimiento de la
información en la lección 2, "Trabajar con las listas secuenciales." Las preguntas
también están disponibles en el CD complementario si prefiere revisarlos en forma
electrónica.
Nota   Respuestas
Las respuestas a estas preguntas y explicaciones de por qué cada opción de
respuesta es correcta o no se encuentran en la sección "Respuestas" al final
del libro.

1.  Lo que hace el  método Dequeue de la  clase Queue? (Seleccione todos los que
correspondan).
A.  Recupera un elemento desde la parte frontal de la colección
B.   Agrega un elemento a la colección
C.   Elimina el primer elemento de la colección
D.   Borra la colección
2.  ¿En qué orden hace una pila recuperar elementos que use su  método pop?
A.  Orden aleatorio 
B.   Primero en entrar, primero en salir 
C.   Último en entrar, primero en salir 
D.   El último, último-out
Lección 3: trabajar con diccionarios
En el otro extremo del espectro de las listas secuenciales son diccionarios. Los
diccionarios son colecciones que están diseñados para almacenar listas de pares
clave/valor para permitir la búsqueda de valores basados en una clave.

Después de esta lección, será capaz de:


■   Utilice una Hashtable  para crear una simple lista de elementos exclusivos.
■   Utilice SortedList  para ordenar una lista de objetos.
■   Trabajar con   objetos DictionaryEntry para almacenar pares nombre/valor.
■   Enumerar diccionarios y saber cómo utilizar DictionaryEntry.
■   entender la   interfaz IEqualityComparison proporcionar originalidad a
Tablas hash.
■   Utilice el HybridDictionary  para almacenar pares de nombre/valor de una forma muy
eficiente.
■   Utilice el OrderedDictionary  para almacenar pares de nombre/valor de una
forma que conserva el orden de agregarlos a la colección.
Lección Tiempo estimado: 30 minutos

Utilizando un diccionario.
El diccionario clases compatibles con .NET Framework se utiliza para asignar una clave a un
valor. Básicamente, éstas existen para permitir crear tablas de búsqueda que se pueden
correlacionar arbi- claves arbitrarias a valores arbitrarios. En el caso más básico, la  clase
Hashtable se utiliza para realizar esta asignación de pares clave/valor. Por ejemplo, supongamos
que usted necesita para asignar direcciones de correo electrónico para el nombre completo del
usuario. Usted podría utilizar una tabla hash para almacenar este mapa- ping, como se ve en este
fragmento de código:
// C#
= new emailLookup Hashtable Hashtable();

// Agregar método toma una clave (primer parámetro)


// Y un valor (segundo parámetro)
EmailLookup.Añadir("sbishop@contoso.com", "el obispo, Scott");

// El indizador es funcionalmente equivalente a agregar


emailLookup["sbishop@contoso.com"] = "Obispo, Scott".

A diferencia de los anteriores tipos de colecciones, diccionarios siempre esperar dos pedazos de
información para agregarlos a la colección: una clave y un valor. Este ejemplo nos muestra dos
maneras de agregar elementos a nuestra colección. En primer lugar, el  método Add nos permite
añadir un elemento especificando el par clave/valor. Además, puede usar el indizador para
especificar un par clave/valor especificando la clave en el indizador y asignar el valor que desee
para que apunte a la clave.
Recuperar objetos de un diccionario también es sencillo. Para acceder a los datos una vez que se ha
agregado al diccionario (aquí, un Hashtable), simplemente llame al indizador con la clave que está
buscando:

// C# Console.WriteLine(emailLookup["sbishop@contoso.com"]);
Diccionarios porque están hechas para buscar pares clave/valor, no es una sorpresa que la iteración
a través de los objetos en un diccionario no es sencillo. Por ejem- plo, suponga que ha creado
una Hashtable y desea iterar sobre todos los valores. El código para llevar a cabo esta tarea podría
tener este aspecto:
// C#
= new emailLookup Hashtable Hashtable();

EmailLookup["sbishop@contoso.com"] = "Obispo, Scott";


emailLookup["chess@contoso.c"] = "om Hess, Cristiana";
emailLookup["djump@contoso.com"] = "Salto, Dan".

Nombre de objeto (foreach en emailLookup)


{
Console.WriteLine(nombre);
}

Usted podría esperar que esto le mostrará los nombres de cada persona en la  vari- emailLookup
capaz. Lo que realmente se escriben en la consola es esta:
System.Collections.DictionaryEntry
System.Collections.DictionaryEntry
System.Collections.DictionaryEntry

¿Por qué sucede esto? Sucede porque en realidad se está repasando las entradas del  objeto
Dictionary, no las claves o los valores. Si desea que este código para escribir los nombres de los
usuarios, se podría cambiar el iterador para trabajar con estos  objetos DictionaryEntry, así:
' VB
Para cada entrada como en emailLookup DictionaryEntry
Console.WriteLine(entry.Value) Siguiente entrada

// C#
Foreach (Entrada de DictionaryEntry emailLookup)
{
Console.WriteLine(entrada.Value);
}

Un  objeto DictionaryEntry es simplemente un contenedor que contiene una clave y un valor. Por


lo que obtener los elementos es tan simple como hacer la iteración, pero debe recuperar
el valor o clave necesarios para sus necesidades.
Diccionario de todas clases (incluida la Hashtable) admiten la  interfaz IDictionary. La  interfaz
IDictionary deriva de la  interfaz ICollection. La  interfaz IDictionary más importante de
propiedades y métodos se explican en la tabla 4-13 y la tabla 4-14, respectivamente.
Tabla 4-13
IDictionary  Propiedades
Nombre Descripción
IsFixedSize   obtiene un indicador de si esta colección puede ser
redimensionado IsReadOnly  obtiene un indicador de si una colección puede
ser cambiado Item   obtiene o establece el elemento en un elemento
específico de la colección
Tabla 4-13  IDictionary  Propiedades

Nombre Descripción
Teclas   ICollection obtiene un objeto que contiene una lista de las
claves de la colección
   Obtiene valores ICollection  object que contiene una lista de los
valores de la colección

La tabla 4-14    métodos IDictionary

Nombre Descripción
Add  agrega un par clave/valor a la colección.
Borrar  Elimina todos los elementos de la colección.
Contiene  pruebas si una tecla específica contenida en la colección.
GetEnumerator  devuelve un  objeto IDictionaryEnumerator para la colección.
Este método es diferente del método con el mismo nombre en
la  interfaz IEnumerable que devuelve una  interfaz IEnumer-
ador.
Eliminar  Elimina el elemento en la colección que corresponde a
una clave específica.

La  interfaz IDictionary es algo parecido a la  interfaz IList en la lección 1, pero no permite


acceder a los elementos por index, sólo por clave. Esta interfaz permite acceder a la lista de claves
y valores directamente como colecciones de objetos. Este diseño es útil si necesita iterar sobre
cualquiera de estas listas por separado.
Anteriormente en esta sección, usted vio cómo iterar sobre los nombres en una lista de correo
electrónico utilizando el  objeto DictionaryEntry que es devuelto por el iterador. Usted también
podría iterar sobre esos valores por la iteración a través de la  propiedad en lugar de valores, como
en este ejemplo:
// C#
Foreach (nombre de objeto emailLookup.Valores)
{
Console.WriteLine(nombre);
}

Además de la  interfaz IDictionary, la Hashtable admite dos métodos que permiten probar la


existencia de claves y valores. Estos métodos se muestran en la tabla 4-15.
La tabla 4-15    Métodos Hashtable

Nombre Descripción
ContainsKey  determina si la colección contiene una clave específica
ContainsValue  determina si la colección contiene un valor específico

La comprensión de la igualdad
La  clase Hashtable es un tipo específico de la clase de diccionario que utiliza un valor entero
(llamado un hash) para ayudar en el almacenamiento de sus claves. La  clase Hashtable utiliza el
hash para acelerar la búsqueda de una clave específica en la colección. Cada uno de los objetos
de .NET se deriva de la  clase Object. Esta clase admite el  método GetHash, que devuelve un
número entero que identifica el objeto.
¿Por qué el hecho de que la  clase Hashtable es almacenar un valor hash importa el desarrollador?
La Hashtable permite hash únicos de valores, no valores únicos. Si intenta almacenar la misma
tecla dos veces, la segunda llamada que sustituye a la primera llamada, tal como se muestra en este
ejemplo:

// C#
Duplicados = new Hashtable Hashtable();

Duplicados["Primer"] = "1st";
Duplicados["Primer"] = "la primera";

Console.WriteLine(duplicados.Count); // 1

La  colección duplicados sólo se almacena un elemento en este ejemplo porque el hash de


"Primero" es el mismo que el de "primera". La  clase String invalida el  método GetHashCode
de objeto para obtener este comportamiento. Se espera que dos cadenas con el mismo texto para
ser iguales aunque sean diferentes instancias. Esto es cómo la  clase Hashtable pruebas para la
igualdad, probando el código hash de los objetos. Este método es probablemente lo que quiere (Y
SE ESPERA) en la mayoría de los casos.
El .NET Framework  no siempre entienden la igualdad como lo hacemos, sin embargo. Por
ejemplo, imagine que ha creado una clase simple denominada pescado que contiene el nombre de
los peces de la siguiente manera:

// C#
Clase pública de pescado
{
Nombre de cadena.
Peces pública(string) theName
{
Nombre = el nombre;
}
}

Ahora si queremos crear dos instancias de la  clase de peces con el mismo nombre, la Hashtable
Los trata como objetos diferentes, como se muestra en el siguiente código:
// C#
Duplicados = new Hashtable Hashtable();
Pescar peces nuevos key1 =(""); arenque
pescado key2 = new pescararenque("");

Duplicados[clave1] = "Hola";
Duplicados[clave2] = "Hola";

Console.WriteLine(duplicados.Count); // 2

¿Por qué hay dos elementos en la colección que tienen el mismo nombre? La  colección duplicados
almacena dos elementos en este ejemplo porque la  clase Object la implementación
de GetHashCode crea un hash que es probable que sea único para cada instancia de una clase.
Usted podría invalidar el GetHashCode en la  clase de peces a tratar y deje la Hashtable sepa que
son iguales, de la siguiente manera:
// C#
Anulación pública int GetHashCode()
{
Volver nombre.GetHashCode();
}

Si devuelve el valor hash del nombre del pez, las dos instancias de los peces tendrán el mismo
código hash. Pero eso es suficiente para la Hashtable para determinar son objetos idénticos?
Desafortunadamente, no. Si la Hashtable encuentra dos objetos con el mismo hash, se llama a
su  método Equals para ver si los dos objetos son en realidad iguales. De nuevo, la implementación
predeterminada del objeto.Equals devolverá  false si los dos objetos son dos instancias diferentes
de la misma clase. Por lo tanto necesitamos agregar también un reemplazo del  método Equals para
nuestra  clase de pescado:
// C#
Anulación pública bool Equals(object obj)
{
Fish otherFish = obj como peces;
Si (otherFish == null) return false;
Volver otherFish.name == nombre;
}

Aquí podemos probar para ver si el otro objeto es también un pescado y, en caso afirmativo,
comparar el nombre para probar si los dos objetos son iguales. Sólo entonces la   clase Hashtable
pueda determinar si dos teclas son idénticos.
Utilizando la   interfaz IEqualityComparer
Además de poder cambiar sus clases para proporcionar igualdad, usted podría encontrar que es
necesario proporcionar la igualdad fuera de la clase. Por ejemplo, supongamos que desea
almacenar las claves en la Hashtable como cadenas, pero la necesidad de ignorar el caso de la
cadena. Cambiar la clase String para admitir esto o crear su propia  clase String que se distingue
entre mayúsculas y minúsculas sería una solución dolorosa. Esta situación es donde la Hashtable's
capacidad de usar una clase que calcula la igualdad entra en juego.
La  clase Hashtable admite un constructor que puede aceptar una instancia de la  interfaz
IEquality- Comparer como argumento. Muy parecida a la  interfaz IComparer mostrado en la
lección 1 que te permitía ordenar colecciones, la  interfaz IEqualityComparer admite dos métodos:
GetHashCode y Equals. Estos métodos permiten que el comparador clase para manejar la igualdad
para los objetos en lugar de depender de los objetos para proveerlos. Por ejemplo, el código
siguiente crea un sencillo comparador insensible a mayúsculas y minúsculas, de modo que usted
puede hacer sus claves de cadena no distingue entre mayúsculas y minúsculas:

// C#
Public Class InsensitiveComparer : IEqualityComparer
{
CaseInsensitiveComparer _comparer = new CaseInsensitiveComparer();

Public int GetHashCode(object obj)


{
Volver obj.ToString().ToLowerInvariant().GetHashCode();
}
Público El nuevo bool Equals(Object x, objeto y)
{
Si _comparer.Compare(x, (y) == 0)
{
Devuelve true;
}
Otra cosa
{
Return false;
}
}
}
En esta nueva clase, se están implementando la  interfaz IEqualityComparer para proporcionar
códigos hash y las comparaciones de igualdad. Nota t hat t su clase utiliza t
él CaseInsensitiveComparer incorporada para hacer la  comparación Igual real. Además,
el GetHash- Código toma el objeto pasado y convierte a minúsculas antes de obtener el hashcode.
Este proceso es lo que tarda el caso sensibilidad fuera del código hash cre- ación. Ahora, cuando se
crea una tabla hash, puede decírselo a utilizar esta clase para hacer las comparaciones:
' VB
Como dehash Dim = New Hashtable Hashtable(New InsensitiveComparer()).

Dehash("Primero") = "1st"
dehash("Segundo") = "2nd"
dehash("tercera") = "3rd"
dehash("Cuarta") = "4th" dehash("cuarta")
= "4th"

Console.WriteLine(dehash.Count) ' 4

// C#
= new dehash Hashtable Hashtable(new InsensitiveComparer());

Dehash["Primer"] = "1st";
dehash["Segundo"] = "2nd";
dehash["Tercera"] = "3rd";
dehash["Cuarta"] = "4th"; dehash["cuarta"]
= "4th";

Console.WriteLine(dehash.Count); // 4

Porque usted está utilizando este case-insensitive igualdad en la creación del objeto Hashtable se
termina con sólo cuatro elementos en la colección. Se trata de "cuarto" y "cuarto" como idénticos.
La Hashtable es una gran clase para crear tablas de búsqueda, pero hay veces cuando lo que
realmente necesita es ordenar un conjunto de elementos por algún valor de clave. Cuando se
procesa una iteración

A través de la  clase Hashtable, devuelve los elementos en el orden de su valor hash. Ese orden no
es práctico para la mayoría de las situaciones. La SortedList es una clase de diccionario que admite
la ordenación.

Utilizando la   clase SortedList


Aunque la  clase SortedList es definitivamente una clase de diccionario, comparte algunos de sus
comportamientos con cómo funcionan las listas simples. Esto significa que usted puede (y
probablemente) tener acceso a los elementos almacenados en la SortedList en orden. Por ejemplo,
puede utilizar un SortedList para ordenar una lista de elementos como:
// C#
SortedList ordenar = new SortedList();
ordenar["Primer"] = "1st"; ordenar["Segundo"] =
"2nd"; ordenar["Tercera"] = "3rd"; sort["Cuarta"] =
"4th"; sort["cuarta"] = "4th";

Foreach DictionaryEntry en sort (entrada)


{
Console.WriteLine("{0} = {1}", entrada.Key Entry.Value);
}

Este código genera una ordenación simple de nuestros objetos:


Primera = 1 =
4cuarto cuarto =
4Segundo = 2ª
tercera = 3er

Se puede ver desde el  iterador foreach en el fragmento de código anterior que


el SortedList todavía es una clase de diccionario (como es evidenciado por DictionaryEntry).
Además de tener la misma interfaz que todos tienen clases de diccionarios, la  clase SortedList
admite propiedades adicionales para permitir el acceso de las claves y valores por número de
índice. Tabla 4-16 y La tabla 4-17 muestra las propiedades y métodos, respectivamente,
de SortedList (no incluye la  interfaz IDictionary miembros).
Tabla 4-16  SortedList  Propiedades

Nombre Descripción
Capacidad  obtiene o establece el número de elementos asignados
actualmente para la colección. Este es el número total de franjas
horarias asignadas actualmente para los elementos, no el número
de elementos de la colección. (Conde le dará el número de
elementos de la colección).

La tabla 4-17  SortedList  métodos

Nombre Descripción
ContainsKey  determina si la colección contiene una clave
específica ContainsValue  determina si la colección contiene un valor
específico GetByIndex   recupera el valor a un índice específico de la
colección GetKey  recupera la clave en un índice específico de la
colección GetKeyList  recupera una lista ordenada de las teclas.
GetValueList  recupera una lista de valores.
IndexOfKey  obtiene el índice de una clave de la colección
IndexOfValue  obtiene el índice de la primera ocurrencia del valor especificado
en la colección
RemoveAt  quita un valor específico en la colección por
index SetByIndex  sustituye un valor a un índice específico de la
colección TrimToSize  utiliza para liberar la capacidad no utilizada en
la colección

Como se puede observar en estas tablas, SortedList agrega una serie de métodos de acceso a los
datos por el número de índice. La clase es compatible con la recuperación de las claves y los
valores de índice, y también admite mirando para recuperar su índice. Porque esta clase está
ordenada, el índice de un elemento puede cambiar a medida que se agregan o eliminan elementos.
Puede utilizar el mismo proceso que aprendió en la lección 1 para ordenar una
colección SortedList. En lugar de requerir que usted llame a Ordenar para ordenar
elementos, SortedList realiza cuando se agregan elementos de ordenación. Con esto en mente, se
puede especificar un IComparer al crear el SortedList, de modo que puede controlar la forma en
que ocurre la ordenación. Si usted pide prestada la  clase DescendingComparer que fue explicado
en la lección 1, puede modificar el código para incluir la  clase DescendingComparer, así:

// C#
SortedList ordenar = new SortedList(new DescendingComparer());
Ordenar["Primer"] = "1st";
ordenar["Segundo"] = "2nd";
ordenar["Tercera"] = "3rd";
sort["Cuarta"] = "4th";

Foreach DictionaryEntry en sort (entrada)


{
Console.WriteLine("{0} = {1}", entrada.Key Entry.Value);
}

Ahora la clasificación está en orden descendente (recuerde que el orden es el orden alfabético por
el nombre, no el número):
Tercera = 3er
segundo = 2º
Cuarto = 4A
Primera = 1

Diccionarios especializados
Hay momentos en los diccionarios estándar (  ) y Hashtable SortedList limita- ciones, ni las
limitaciones funcionales o de rendimiento relacionados. Para cerrar esa brecha, el .NET
Framework admite otros tres diccionarios: ListDictionary HybridDictionary OrderedDictionary, ,
y

ListDictionary
La  clase Hashtable es una colección muy eficiente en general. El único problema con la  clase
Hashtable es que requiere un poco de sobrecarga, y para colecciones pequeñas (menos de 10
elementos)  los gastos generales pueden obstaculizar el desempeño. Ahí es donde la lista- viene en
el diccionario. Está implementado como una simple matriz de elementos debajo del Capó, así que
es muy eficiente para pequeñas colecciones de elementos. La ListDictionary clase tiene la misma
interfaz que la  clase Hashtable, así que puede ser utilizado como reemplazo. Para demostrar, aquí
está el ejemplo usado Hashtable con anterioridad. Esta vez, sin embargo, estamos
utilizando ListDictionary. Tenga en cuenta que ninguno de los código es diferente excepto para los
con- struction del objeto:
// C#

ListDictionary emailLookup = new ListDictionary ();

EmailLookup["sbishop@contoso.com"] = "Obispo, Scott";


emailLookup["chess@contoso.c"] = "om Hess, Cristiana";
emailLookup["djump@contoso.com"] = "Salto, Dan".

Foreach DictionaryEntry emailLookup (entrada)


{
Console.WriteLine(entry.Value);
}

HybridDictionary

Como vimos en el debate de ListDictionary, hay algunas ineficiencias en la Hashtable para


colecciones pequeñas. Sin embargo, si utiliza ListDictionary para grandes listas no es eficaz en
todo. En general, esto significa que si usted sabe que su colección es pequeña, utilice una  lista-
Diccionario; si tu colección es grande, utilice una Hashtable. Pero, ¿y si simplemente no saben
cuán grande es su colección? Ahí es donde  entra en juego la HybridDictionary. Es apli- carse
como ListDictionary y sólo cuando la lista se vuelve demasiado grande ¿se transformará en
una Hashtable internamente. La HybridDictionary se usa mejor en situaciones donde algunas
listas son pequeñas y otras son muy grandes.
Como con el ListDictionary, la interfaz es idéntica a la Hashtable, así que es una sustitución,
como se muestra en los siguientes fragmentos de código:
// C#
Nueva HybridDictionary HybridDictionary emailLookup = ();

EmailLookup["sbishop@contoso.com"] = "Obispo, Scott";


emailLookup["chess@contoso.c"] = "om Hess, Cristiana";
emailLookup["djump@contoso.com"] = "Salto, Dan".
Foreach DictionaryEntry emailLookup (entrada)
{
Console.WriteLine(entry.Value);
}

OrderedDictionary
Hay veces cuando desee disponer de la funcionalidad de Hashtable pero usted necesidad de con-
trol el orden de los elementos en la colección. Cuando se agregan elementos a una Hashtable, dos
cosas son ciertas: no hay forma de acceder a los elementos por index; y si utiliza un enumerador
para superar esta limitación, los elementos están ordenados por su valor hash. Usted podría utilizar
un SortedList, pero que asume que desea los elementos se ordenan en el orden proporcionado por
las teclas. Quizás sólo hay un ordinal orden?
Para dar cabida a usted cuando usted necesita un diccionario rápido sino también a la necesidad de
mantener los elementos en forma ordenada, el .NET Framework admite la OrderedDictionary.
Un OrderedDictionary es mucho como una Hashtable excepto que tiene métodos adicionales y
prop- erties-como se ve en la tabla 4-18 y la tabla 4-19, respectivamente, para permitir el acceso a
los elementos de índice.
La tabla 4-18
OrderedDictionary  propiedades Extra
Nombre Descripción
Tema   sobrecargado para respaldar el acceso por índice

La tabla 4-19
OrderedDictionary  Métodos
adicionales
Nombre Descripción
Insert   inserta un par clave/valor a un índice específico de la colección
RemoveAt  quita un par clave/valor a un índice específico de la
colecció

Estas adiciones a la interfaz de la clase le permiten tratar con la colección como si la clase eran una
mezcla de ArrayList  y Hashtable.

Práctica: Crear una tabla de búsqueda


En este laboratorio, se crea una tabla de consulta para sustituir los números con su cadena repre-
ciones. Si se produce un problema de completar un ejercicio, los proyectos terminados están
disponibles en el CD en la carpeta de código.
   Ejercicio 1: Crear una tabla de búsqueda
En este ejercicio vamos a crear una tabla de consulta para una serie de números, analizar a través
de los dígitos de una cadena, y mostrar los números en la consola.
1.  Crear una nueva aplicación de consola denominada DictionaryCollections.
2.  En el archivo de código principal, incluir (o importación para Visual Basic)
el System.Collections
Espacio de nombres.
3.  En el  método Main del proyecto, crear una nueva instancia de la  clase Hashtable.
4.  Agregar elementos a la nueva instancia de la  clase Hashtable donde la clave es
una cadena que contiene los números del cero al nueve, y el valor es el nombre
definido de los números del cero al nueve.
5.  Siguiente Crear una variable de cadena con una serie de números.
6.  Ir a través de la cadena, un carácter a la vez utilizando una  construcción foreach.
7.  Dentro del foreach, crear una nueva cadena de caracteres de la variable creada en
el  bucle foreach.
8.  Compruebe si la Hashtable contiene la clave de la cadena de un solo carácter.
9.  Si es así, obtenga el valor de la clave de la Hashtable y mostrarlos en la consola.
El código podría tener este aspecto:
// C#
Utilizando System.Collections.

Programa de clase
{
Static void main(String[] args)
{
Consulta de Hashtable = new Hashtable();

Lookup["0"] = "Cero";
lookup["1"] = "uno";
lookup["2"] = "dos";
lookup["3"] = "tres";
lookup["4"] = "cuatro";
lookup["5"] = "5"; lookup["6"] =
"6"; lookup["7"] = "7";
lookup["8"] = "8"; lookup["9"] =
"9";

Cadena ourNumber = "888-555-1212".

Foreach (char c en ourNumber)


{
Dígito = cadena c.ToString();
Si (Lookup.ContainsKey(dígito)
{
Console.WriteLine(lookup[dígito]);
}
}
}
}

10.  Genere el proyecto y resolver cualquier error. Compruebe que la aplicación de


consola suc- logre enumera todos los dígitos del número
especificado.Resumen de la lección
■   La  interfazIDictionary proporciona la convención de llamada básica para
todos diccionario
Colecciones.
■   La  clase Hashtable puede utilizarse para crear tablas de búsqueda.
■   Puede utilizar un  objeto DictionaryEntry para obtener la clave y el valor de un
objeto en un
 Colección de diccionario.
■   El SortedList puedeser usado para crear la lista de elementos que pueden ser
ordenados por una clave.
■   El IEqualityComparer puede utilizarse para generar valores de hash y comparar
dos objetos arbitrarios para la igualdad.

Examen de la lección
Puede utilizar las siguientes preguntas para poner a prueba su conocimiento de la información en la
lección 3, "trabajar con diccionarios." Las preguntas también están disponibles en los com- panion
CD si usted prefiere revisarlos en forma electrónica.

Nota   Respuestas
Las respuestas a estas preguntas y explicaciones de por qué cada opción de
respuesta es correcta o no se encuentran en la sección "Respuestas" al final del
libro.

1.  Cuando se agrega una clave para una Hashtable, lo que se puede llamar a métodos
en la clave para determinar si la clave es única? (Seleccione todos los que
correspondan).
A.  GetType
B.   GetHashCode
C.   ToString
D.   Es igual a
2.  Cuál de las siguientes afirmaciones es verdadera?
A.  Puede pasar una instancia de una clase que admite la  interfaz
IEqualityComparer cuando construya una Hashtable para cambiar la forma
de claves se eval- uated de unicidad.
B.   Puede asignar un IEqualityComparer objeto Hashtable existente.
C.   No puede utilizar un IEqualityComparer con una Hashtable.
D.   Una Hashtable  implementa  IEqualityComparer.
Lección 4: Uso de las Colecciones Especializadas
Las tres primeras lecciones en este capítulo presenta una serie de colecciones que se pueden
utilizar para almacenar cualquier objeto en .NET. Aunque estas son herramientas valiosas,
utilizando a menudo puede llevar a tener que convertir objetos cuando se recupere de las
colecciones. El .NET Framework admite un nuevo espacio de nombres
llamado System.Collections.Specialized que incluye colecciones que están diseñados para
trabajar con tipos específicos de datos.

Después de esta lección, será capaz de:


■   Utilice el BitArray  y BitVector32  clases para tratar con conjuntos de   valores
booleanos.
■   Utilice el StringCollection  y StringDictionary  clases para almacenar
colecciones de cadenas.
■   Utilice el NameValueCollection  para almacenar pares de nombre/valor de un
modo seguro.
Lección Tiempo estimado: 30 minutos

Trabaja con bits


En muchos casos, usted tendrá que tratar con datos en conjuntos de  expresiones booleanas.
Una de las necesidades más comunes es tener una lista de bits que puede ser activado o
desactivado. Dos clases de .NET Framework simplifican el trabajo con colecciones de
bits: BitArray y BitVector32.
La  clase resizeable BitArray es una colección que puede almacenar  valores booleanos.
Además de ser resizeable, apoya las operaciones a nivel de bits comunes tales como y, o, no y
exclusivo oXor ().
La  estructura BitVector32 es ligeramente diferente de una bestia BitArray. El propósito de
la  estructura BitVector32 es para ayudar en la manipulación de bits en un entero de 32 bits.
La BitVector32 no es una colección resizeable en absoluto , sino que está fijado en 32 bits, de
modo que pueden manipular los bits individuales de un entero de 32 bits.

Cómo utilizar un BitArray


La BitArray es una colección resizeable tradicionalmente, pero no un ajuste dinámico del
tamaño de uno. Cuando se crea una nueva instancia de la  clase BitArray, debe especificar el
tamaño de la colección. Una vez que la nueva instancia se ha creado, puede cambiar el tamaño
por chang- ción de la  propiedad Length. A diferencia de otras colecciones, BitArray no
admite agregar o quitar. Esta falta de apoyo porque cada valor en un BitArray sólo puede
ser true o false, así que la idea de agregar o quitar no es realmente aplicable.
Una vez que crea una instancia de la  clase BitArray, tendrá una colección de Boolean Valores con
el valor predeterminado de false. Para establecer bits individuales, utilice el indizador así:
// C#
BitArray bits = nuevo BitArray(3).
Bits[0] = false; bits[1] = true;
los bits[2] = false;

El verdadero poder de la BitArray está en su capacidad para realizar  operaciones booleanas sobre


dos BitArray objetos (del mismo tamaño). BitArray para crear dos objetos y realizar una
operación OR exclusiva de ellas, siga estos pasos:
1.  Crear una instancia de la  clase BitArray, especificando el tamaño que usted necesita.
2.  Establecer algunos valores de los bits individuales.
3.  Repita los pasos 1 y el paso 2 para crear un segundo BitArray que tiene el mismo
tamaño que el primero.
4.  Llamar al  método Xor en el primer BitArray, suministrando el segundo BitArray. Esto
producirá un nuevo BitArray con los resultados de la  operación Xor. El código debe
tener este aspecto:

// C#
BitArray bits = nuevo BitArray(3).
Bits[0] = false; bits[1] = true;
los bits[2] = false;
BitArray moreBits = new BitArray(3).
Bits[0] = true; los
bits[1] = true; los
bits[2] = false;

BitArray xorBits = bits.Xor(moreBits);

(Bool foreach en bits xorBits)


{
Console.WriteLine(bits);
}

Cómo utilizar un BitVector32  para máscaras de bits


La  estructura BitVector32 es muy útil para la gestión de bits individuales en un mayor núme- ro.
La BitVector32 almacena todos sus datos como un entero de 32 bits. Todas las operaciones en
el BitVector32 cambiar realmente el valor del entero dentro de la estructura. En cualquier
momento, puede recuperar el número entero almacenado llamando a la estructura de la  propiedad
de los datos.
La  estructura BitVector32 permite crear máscaras de bits en orden secuencial llamando a su
estático (o compartido en Visual Basic) método  CreateMask. Llamar al  método CreateMask sin
parámetros se crea una máscara para el primer bit de la estructura. Llame- ing posteriormente,
suministrando la última máscara creada, creará la siguiente máscara de bits. Estas máscaras se
pueden utilizar con la  estructura del indizador BitVector32 para establecer u obtener valores en
ese bit específico.
Un ejemplo ayudará a aclarar el proceso. Supongamos que necesita para establecer el valor de los
tres primeros bits en algunos entero de 32 bits. Utilice estos pasos para crear máscaras de bits,
establecer valores y obtener los valores de los bits:
1.  Crear una instancia de la  estructura BitVector32, especificando un valor cero para
el valor inicial para asegurarse de que todos los bits son claras.
2.  Crear una máscara para el primer bit llamando al  método CreateMask
BitVector32.sin ningún parámetro.
3.  Crear la siguiente máscara de bits llamando al   método BitVector32.CreateMask,
pero incluyen la última máscara de bits como un parámetro para indicarle que cree
la siguiente máscara.
4.  Repita los pasos 1 a 3 hasta que haya tres máscaras de bits. El código debe tener
este aspecto:
// C#
BitVector32 = new vector BitVector32(0).

Int = firstBit BitVector32.CreateMask();


Int = secondBit BitVector32.CreateMask(firstBit);
Int = thirdBit BitVector32.CreateMask(secondBit);

5.  Ahora el primer y el segundo conjunto de bits a true usando el indizador así:


// C#
Vector[firstBit] = true;
Vector[secondBit] = true;

6.  Escribir la   propiedad Data 's BitVector32 en la ventana de la consola para confirmar


que el valor es ahora 3 (1 para el primer bit plus 2 para el segundo bit es igual a 3).

// C#
Console.WriteLine("{0} debe ser de 3", vector.data).

7.  Si usted escribe toda la estructura (no los datos ) de la propiedad en la ventana de la


consola, que le mostrará qué bits están encendidos:
// C# Console.WriteLine(vector);
BitVector32 //{00000000000000000000000000000011}

8.  A continuación cree un nuevo  objeto BitVector32 y establezca su valor inicial 4 (que


debe activar el bit 3 y apagar los bits 1 y 2).
9.  A continuación, obtenga cada uno de los tres primeros bits como  valores booleanos
usando el indizador del BitVector32. Puede utilizar las máscaras creadas por los
primeros tres bits; no son específicos de una instancia de un BitVector32 porque la
estructura siempre almacena 32 bits. El código debe tener este aspecto:
// C#
BitVector32 = new newVector BitVector32(4);
Bool bit1 = newVectorfirstBit[]; bool bit2 =
newVectorsecondBit[]; bool bit3 =
newVectorthirdBit[];

// Bit1 = FALSO, bit2 = false, bit3 = true

La comprensión matemática binaria


La  estructura BitVector32 está diseñada para simplificar el uso de la Matemática
binaria para utilizar indi- viduo bits de información dentro de un número mayor.
Para comprender cómo  funciona la estructura BitVector32, sin embargo, tendrá
que comprender cómo funciona la matemática binaria.
Dentro de la memoria del ordenador, cada fragmento de datos está almacenado
como una serie de interruptores que pueden ser activados o desactivados. Para
almacenar números como un conjunto de conmutadores, cada número requiere una
cierta cantidad de estos interruptores. Estos interruptores se conocen como bits.
Por ejemplo, un byte sin signo es un número de 8 bits y puede almacenar números
de cero a 255. Este proceso funciona porque cada bit del número rep- se resiente el
2 elevado a la potencia de sus dígitos (a partir de cero; los bits se numeran de
derecha a izquierda). Por lo tanto, si el primer dígito, es 2^0, o 1. El segundo dígito
es 2^1, o 2, y así sucesivamente. Por lo tanto, si un byte sin signo ha rellenado
todos los 8 bits, puede ser representada por esta ecuación: 1 + 2 + 4 + 8 + 16 + 32
+ 64 + 128 = 255. Por ejemplo, para almacenar el número de 5 dígitos, 0 y 2 están
encendidos (00000101), el cual puede ser expresado como (2^0) + (2^2) = 1 + 4 =
5.
Números con 8 bits no son lo suficientemente grandes como para
preocuparse. BitVector32 la estruc- tura se utiliza porque como los números
crecen el arreglo de bits se vuelve más y más confusa. Por ejemplo, el último
dígito en un entero de 32 bits sin signo es bastante grande (2.147.483.648).
Complicar las cosas aún más es el hecho de que la  realidad BitVector32 funciona
con un entero sin signo, por lo que el último dígito es realmente
-(2^31), que trata con el rango negativo de un entero de 32 bits firmado. El pur-
plantean un BitVector32 es para ocultar los números detrás de las escenas para que
pueda tratar los bits como índices del número.

Cómo utilizar un   embalaje para bit BitVector32


Aunque BitVector32 es una estructura muy útil para tratar con bits individuales, también soporta
poco embalaje. Embalaje de bits puede ser definido como teniendo varios números más pequeños
Y de su embalaje en un gran número. Embalaje de bits a menudo se realiza para reducir el
almacenamiento de números especialmente pequeñas.
Por ejemplo, usted podría tener tres números para almacenar que son bastante pequeñas. El primer
número podría tener un valor máximo de 10, la segunda un valor máximo de 50, y el tercero de un
valor máximo de 500. Usted podría almacenar estos tres Int16s pero tendrías que estar malgastando
espacio. En su lugar, debe utilizar un BitVector32 para almacenar los tres valores en un solo
Número de 32 bits.

BitVector32 le permite crear secciones de la estructura que se utilizará para almacenar los números
de ciertos tamaños. Así que antes de empezar, necesitará crear las secciones. Esto se hace en
mucho la misma manera como usted crear máscaras, pero necesitará especificar el número más
grande que la sección puede almacenar. Por lo tanto, si usted utiliza el ejemplo anterior de 10, 50 y
500, tendría que crear las secciones así:
// C#
BitVector32.Sección = firstSection
BitVector32.CreateSection(10);
BitVector32.Sección secondSection =
BitVector32.CreateSection(50); firstSection
BitVector32.Sección thirdSection =
BitVector32.CreateSection secondSection(500).

Al igual que el , el CreateSection CreateMask utiliza la última sección para determinar dónde
"Pack" el nuevo número.
Una vez que tienes las secciones, puede establecer y obtener mediante el indizador y la nueva
sección variables, como se muestra aquí:
// C#
BitVector32 = new packedBits BitVector32(0).

PackedBits[firstSection] =
10; packedBits[secondSection] =
1; packedBits[thirdSection] = 192;

Console.WriteLine(packedBits[firstSection]);
Console.WriteLine([secondSection packedBits]);
Console.WriteLine([thirdSection packedBits]);

Después de haber trabajado con las secciones, puede obtener la   propiedad Data 'sitVector32 para
mantener el número subyacente que contiene los tres números empaquetados en:
// C# Console.WriteLine(packedBits.data).
// 98314

Console.WriteLine(packedBits);
BitVector32 //{00000000000000011000000000001010}

Usted podría hacer los cálculos para averiguar que el número 98314 puede almacenar 10, 1 y 192,
pero el BitVector32 puede hacerlo por usted con mucho menos trabajo.
Las cadenas de recolección
Probablemente el  tipo más común de objeto que necesita almacenar en una colección son cadenas.
Para satisfacer  esta necesidad, el .NET Framework admite dos col especializados- ciones que son
fuertemente tipados para almacenar cadenas: StringCollection y StringDictionary.

La   clase StringCollection


La StringCollection es una simple colección de tamaño dinámicamente (como ArrayList) que sólo
puede almacenar cadenas. StringCollection pero todavía es sólo una colección como los demás
hombres- cionado en este capítulo, por lo que trabajar con él es prácticamente idéntica a usar
un ArrayList, como se ve en este ejemplo:

// C#
StringCollection coll = new StringCollection();

Coll.Add("Primero");
coll.Add("Segundo");
coll.Add("tercera");
coll.Add("Cuarta");
coll.Add("cuarta");
// Coll.Add(50); <- no compila...no una cadena

Cadena = theString coll[3];


// Ya No necesitan
// Cadena theString = (string) coll[3];

Las líneas que agregue las cadenas a la colección tiene el mismo aspecto que los anteriores
ejemplos utilizando el ArrayList. La única diferencia es que la adición de un nonstring genera un
error de compilación. (Véase la línea comentada.) Además, cuando se recupera la cadena, usted ya
no está trabajando con objetos sino con cadenas. Esto reduce la necesidad de conversión al
recuperar los elementos.

La   clase StringDictionary


La StringDictionary es una versión con establecimiento inflexible de tipos de las colecciones de
diccionarios se muestra en la Lección 3. Esto significa que usted puede usar como una Hashtable,
excepto que tanto las claves y los valores deben ser cadenas:

// C#
StringDictionary dict = new StringDictionary();
Dict["Primer"] = "1st";
dict["Segundo"] = "2nd";
dict["Tercera"] = "3rd";
dict["Cuarta"] = "4th";
dict["cuarta"] = "cuarto".
// Dict[50] = "51"; <- no se compilará...no una cadena

Cadena convertida = dict["Segundo"];


// No necesita de fundición

Es importante entender que las claves son sensibles a mayúsculas y minúsculas por omisión
para la cadena- objetos de diccionario, por lo que las teclas "cuarto" y "cuarto" son
equivalentes.

Colecciones Case-Insensitive
Como vimos anteriormente en este capítulo, puede controlar la comparación o la igualdad
mediante la    interfaz IComparer y IEqualityComparer. Uno de los usos más comunes de
estas interfaces es crear case-insensitive colecciones de diccionarios. Como éste es un uso
común, el .NET Framework tiene una  clase CollectionsUtil que admite la creación
de Hashtable  SortedList y objetos que son insensibles a mayúsculas y minúsculas. Su uso es
tan sencillo como llamar  o
CreateCaseInsensitiveHashtable CreateCaseInsensitiveSortedList. El siguiente fragmento de
código proporciona un ejemplo:
// C#
Tabla Hashtable =
CollectionsUtil.CreateCaseInsensitiveHashtable();
Tabla["hello"] = "Hola"; la tabla["Hola"] =
"Heya"; Console.WriteLine(Tabla.Count); // 1

SortedList inList =
CollectionsUtil.CreateCaseInsensitiveSortedList();
InList["hello"] = "Hola"; inList["Hola"] =
"Heya"; Console.WriteLine(inList.Count); // 1

Colecciones Culture-Invariant
El comportamiento predeterminado de las colecciones es utilizar la cultura actual del
subproceso. Esto significa que las comparaciones son dependientes en las normas de la cultura
actual. Cuando se utilizan comparaciones, asegurándose de que los objetos son únicos ingenio
hin colecciones así como al ordenar elementos en colecciones ordenadas (o al insertar
elementos en ordenados colectores- ciones como SortedList).
Dependiendo de sus necesidades específicas, podría ser importante para hacer las
comparaciones en una cultura-invariante, sin tener en cuenta la cultura actual o de cualquier
cultura. Esta situación surge con las aplicaciones Web y las aplicaciones que necesitan
almacenar infor- mación a través de las culturas. ¿Qué piensa que sucedería si usted almacena
una lista que tenía el inglés, español, hebreo y persa llaves? ¿Cómo se puede esperar que la
serie se producen. En la mayoría de estos casos, usted debe crear colecciones que no son
afectados por el (invariantes) con respecto a la cultura actual. A diferencia de case-insensitive
colecciones,  métodos CollectionsUtil no se puede utilizar para crear sus colecciones. En su
lugar, debe especificar la nueva colección con nuevas instancias  de un  objeto
StringComparer que per- para ms una cadena que distingue entre mayúsculas y minúsculas
comparación utilizando t él palabra R eglas comparación invariantes de la cultura. Por
ejemplo, es posible que desee crear una nueva  y Hashtable SortedList con ambos casos la
insensibilidad y la invariante de la cultura. El código debe tener este aspecto:
/
/

C
#
Hashtable Hashtable = nuevo
hash( StringComparer.InvariantCulture);
SortedList list = new
SortedList( StringComparer.InvariantCul
ture);

La   clase NameValueCollection


Por último, hay un tipo especializado de clase llamada NameValueCollection. A primera vista,
parece que esta clase y StringDictionary son similares porque ambas permiten agregar claves
y valores que son cadenas. Sin embargo, existen algunos difieren procedido: permite varios
valores por clave y los valores pueden ser recuperados por index así como clave.

Con la  clase NameValueCollection, puede almacenar varios valores por clave. Puede hacerlo
con el  método Add. Para recuperar todos los valores de una clave determinada, puede utilizar
el  método GetValues así:
/
/

C
#
NameValueCollection nv = new NameValueCollection();

Nv.Añadir("Key", "Texto");
Nv.Añadir("Key", "Más texto");

(Foreach string s en nv.GetValues("Key")


{
Console.WriteLine(s);
}
// Algo de
texto
// Más
texto
Al agregar claves idénticas con el  método Add, entonces puede acceder a los valores
llamando GetValues y proporcionando la clave. Cuando una lista de todos los valores, se
mostrará ambos elementos se agregan con la misma clave.
Curiosamente, el  método Add y el indizador tienen comportamientos diferentes. Por ejem-
plo, si agrega dos elementos con el indizador y utilizan la misma clave, se conservan sólo el
último valor agregado. Este comportamiento es el mismo que con los otros diccionarios
explicado en la Lección 3. Por ejemplo, si agrega valores con el indizador y con el   método
Add, usted puede ver la diferencia en el comportamiento:

// C#
Nv["Primer"] = "1st";
Nv["Primer"] = "primera".

Nv.Add("Segundo", "2nd");
Nv.Add("Segundo", "Segundo");

Console.WriteLine(nv.GetValues("Primero").Length);
// 1

Console.WriteLine(nv.GetValues("Segundo").Length);
// 2

Por último, la otra diferencia entre usar el NameValueCollection y la cadena- Diccionario es


que puede recuperar los elementos por el índice de clave. Así que cuando le pregunte
el nombre- ValueCollection para devolver un determinado valor del índice, se devuelve el
valor de esa clave. Si la clave tiene más de un valor, se devuelve como una lista delimitada
por comas:

// C#
NameValueCollection nv = new NameValueCollection();

Nv.Add("primero", "1st");
nv.Add("Segundo", "2nd");
nv.Add("Segundo", no "Primera");

(Int x = 0; x < nv.Count; ++x)


{
Console.WriteLine(nv[x]);
}
// 1
// 2nd,no primero

Laboratorio: una tabla precalculada Case-Insensitive,


Localizable
En esta pr ctica, usted creará una tabla precalculada localizable. Si usted encuentra un
problema com- pleting un ejercicio, los proyectos terminados están disponibles en el CD en la
carpeta de código.
  Ejercicio 1: Crear una ListCollection
En este ejercicio vamos a crear una tabla de búsqueda de nombres de países. Las claves
estarán en Español.
1.  Crear una nueva aplicación de consola denominada LookupCollections.
2.  En el archivo de código principal, incluir (o importación para Visual Basic)
el System.Collections,
System.Collections.Specialized, y los  espacios de nombres
System.Globalization.
3.  En el  método Main del proyecto, crear una nueva instancia de
la ListCollection
Clase, especificando que no distingue entre mayúsculas y minúsculas y cultura
invariante.
4.  Agregar tres búsquedas en la colección, especificando "Estados Unidos" por
"Naciones
Los Estados", "Canadá" para "Canadá", y "España" por "España.
5.  Escribir en la consola los valores de las versiones en español de España y
Canadá.
El código debe tener este aspecto:
// C#
Utilizando System.Globalization.
Utilizando System.Collections.
Utilizando System.Collections.Specialized.

Programa de clase
{
Static void main(String[] args)
{

// Hacer que el diccionario no distingue entre mayúsculas y minúsculas


ListDictionary list = new ListDictionary(
Nueva CaseInsensitiveComparer(CultureInfo.InvariantCulture);

// Agregar algunos elementos


Lista["Estados Unidos"] = "Estados Unidos de América".
Lista["Canadá"] = "Canadá".
Lista["España"] = "España";

// Mostrar los resultados


Console.WriteLine(list["españa"]);
Console.WriteLine(list["CANADÁ"]);

Console.Read();
}
}

6.  Genere el proyecto y resolver cualquier error. Compruebe que la aplicación de


consola muestra correctamente tanto en España como en Canadá.
Resumen de la lección
■   El BitArray clase BitVector32 y la estructura puede  ser utilizado tanto para
realizar operaciones a nivel de bits en una serie de  valores booleanos.
■   El StringCollection y StringDictionary clases son clases de tipo seguro para
almacenar las cadenas.
■   Puede crear case-insensitive versiones de Hashtable  SortedList y objetos
utilizando la  clase CollectionsUtil.
■   NameValueCollection es una clase útil para almacenar más de un valor por
clave en una colección de nombre/valor.

Examen de la lección
Puede utilizar las siguientes preguntas para poner a prueba su conocimiento de la información
en la lección 4, "Uso de colecciones especializadas." Las preguntas también están disponibles
en el CD complementario si prefiere revisarlos en forma electrónica.

Nota   Respuestas
Las respuestas a estas preguntas y explicaciones de por qué cada opción de
respuesta es correcta o no se encuentran en la sección "Respuestas" al final
del libro.

1.  ¿Qué tipos de colecciones pueden ser hechas desde la  clase CollectionsUtil?


(Seleccione todos los que correspondan).
A.  Case-insensitive StringDictionary
B.   Cultura-
invariante Hashtable 
C.   Case-insensitive Hashtable 
D.   Case-insensitive SortedList
2.  ¿Qué tipos de objetos se almacena como un valor en StringDictionary?
A.  Cadenas
B.   Objetos
C.   Matrices de cadenas
D.   Cualquier tipos .NET
Lección 5: colecciones genéricas.
Antes de la lección 4, sólo las colecciones que trabajaron con las instancias de la  clase Object
fueron discutidos. Si deseara recuperar un tipo específico de objeto, se necesita para convertir
dicho objeto en su tipo real. En la lección 4, vimos algunas colecciones especializadas para trabajar
con tipos conocidos como cadenas. Pero añadiendo un par de col especializados- ciones no
resuelve la mayoría de los problemas con la seguridad de tipos y colecciones. Ahí es donde vienen
en colecciones genéricas.

Después de esta lección, será capaz de:


■   Crear y trabajar con listas de tipo seguro
■   Crear y trabajar con colas de tipo seguro
■   Crear y trabajar con pilas de tipo seguro
■   Crear y trabajar con diccionarios de tipo seguro
■   Crear y trabajar con tipos seguros colecciones listas vinculadas
Lección Tiempo estimado: 20 minutos

¿Los medicamentos genéricos funcionan


La programación es acerca de cómo resolver problemas. A veces, la necesidad de resolver un
problema particular es común a muchas de las situaciones. Por ejemplo, la necesidad de recopilar
una lista ordenada de elementos es un problema muy común. Dentro de .NET Framework, la  clase
ArrayList intenta resolver este problema. ArrayList porque no sabe qué clase de objetos que los
usuarios podrían querer almacenar, simplemente almacena las instancias de la  clase Object. Cada
cosa en .NET puede ser representado como un objeto; por lo tanto, un ArrayList puede almacenar
cualquier tipo de objeto. Problema resuelto, ¿verdad? Aunque las colecciones de objetos soluciona
este problema, introduce otros nuevos. Por ejemplo, si desea almacenar una colección de números
enteros, se puede escribir un código como el siguiente:
// C#
ArrayList myInts = new ArrayList();

MyInts.Add(1);
myInts.Add(2);
myInts.Add(3).

Foreach (Objeto i en myInts)


{
Int número = (int)i;
}

Todo es bueno; puede crear una colección y agregar números enteros. Usted puede conseguir su
enteros de la colección lanzando desde el objeto que devuelve su colección. Pero lo que si se añade
una línea como la siguiente:

// C#
MyInts.Add("4");

Este compilará bien, pero en el  bucle foreach se producirá una excepción porque el 4 es una
cadena y no un entero. Tratar con tales excepciones menores es problemático. Sería mejor si usted
podría tratar con la colección de modo que sólo puede almacenar inte- Gers. Usted podría escribir
una nueva clase que tiene este comportamiento, como se muestra en el siguiente fragmento de
código:
// C#
Public Class IntList : ICollection, IEnumerable
{
ArrayList privado _innerList = new ArrayList();

Public void Add(int número)


{
_InnerList.Add(número);
}

Público  este int[index] int.


{
Obtener
{
Retorno (int)_innerList[index];
}
}

Los miembros ICollection #region


// Nota: ICollection miembros no se muestran aquí por razones de brevedad.
//   Usted necesitará implementar ICollection sobre sus propias colecciones
#Endregion

Los miembros IEnumerable #region


// Nota: IEnumerable miembros no se muestran aquí por razones de brevedad.
//   Necesitará implementar IEnumerable en sus propias colecciones
#Endregion
}

Para resumir, se crea una nueva colección que apoya la colección básica inter- faces
(ICollection e IEnumerable). Se utiliza un ArrayList para realmente hacer la recolección de
elementos. Por último, haces un  método Add y un indizador que son fuertemente tipados en
enteros. Ahora puede utilizar esta clase de la siguiente manera:

'// C#
 Nueva IntList IntList myIntegers =();

MyIntegers.Add(1); myIntegers.Add(2); myIntegers.Add(3).


// MyIntegers.Add("4"); no compila!

Foreach (Objeto i en myIntegers)


{
Int número = (int)i; // nunca se arroja una excepción.
}
Esto funciona muy bien. Obtendrá un error de compilación si alguien intenta añadir algo que no es
un número entero para su clase. Su  código de foreach nunca será una excepción porque nunca le
deje nada excepto para los números enteros en la colección. Problema resuelto, ¿verdad? Es una
gran solución, pero tomó un montón de trabajo. Sería estupendo si pudiera escribir una clase de
lection col- y sólo tiene que especificar en la clase qué tipo que desea utilizar. Afortunadamente,
usted puede con tipos genéricos.
Los tipos genéricos son tipos que tengan otros nombres de tipo para definirlos como un tipo. En
lugar de crear una colección con establecimiento inflexible de tipos en un tipo específico, vamos a
escribir una colección rápida que puede utilizar cualquier tipo:
// C#
Public Class MyList<T> : ICollection, IEnumerable
{
ArrayList privado _innerList = new ArrayList();

Public void Add(T val)


{
_InnerList.Add(val);
}

Esta clase es idéntica a la colección creada anteriormente en el capítulo, pero en lugar de hacer una
colección de enteros, usaremos un parámetro de tipo genérico T. En cada lugar que habíamos
enteros, vamos a poner ahora el parámetro  T. Esta T es reemplazado con el nombre de tipo
durante la compilación. Por lo tanto, podemos usar esta clase para crear colecciones con
establecimiento inflexible de tipos que son válidos para cualquier tipo de .NET, como se muestra
en el ejemplo siguiente:

// C#
MyList<int> myIntList = new MyList<int>();
MyIntList.Add(1).
// MyIntList.Add("4"); no compila!

MyList<String> myStringList = new MyList<String>();
MyStringList.Add("1");
// MyStringList.Add(2); // no compila!

Cuando se usa esta clase genérica, usted simplemente tiene que incluir el parámetro genérico (el
nombre de un tipo) en la creación de la instancia. En el primer ejemplo se crea el inte- ger
colección queríamos, pero la misma clase genérica puede también crear una cadena colección o
una colección de cualquier tipo en .NET, incluso su propio.
Los medicamentos genéricos son utilizados en diferentes lugares dentro de .NET Framework, pero
los medicamentos genéricos, verá con más frecuencia son las clases de colecciones genéricas.
Observe que no necesitará a cre- comió su propia lista genérica collection-las clases de colección
genéricas en el marco ya tienen uno…y muchos más.
Mejora de la seguridad y el rendimiento
En .NET Framework, los equivalentes genéricos existen para la mayoría de las clases ya
examinadas en este capítulo. Además, existen varias nuevas colecciones que están disponibles sólo
como tipos genéricos. En esta sección se proporcionan ejemplos de cómo utilizar cada uno de estos
tipos. Tabla 4-20 enumera los tipos discutidos, junto con una asignación a sus equivalentes de tipo
genérico.
Tabla 4-20  tipos genéricos
equivalentes
Tipo tipo genérico
Lista de ArrayList<> Cola<> pila
Stack<><> Diccionario Hashtable
SortedList SortedList<> ListDictionary
Dictionary<><> OrderedDictionary
HybridDictionary Diccionario Dictionary<>
SortedDictionary SortedDictionary<>
NameValueCollection DictionaryEntry
KeyValuePair Dictionary<><>

Tabla 4-20  tipos genéricos equivalentes

 StringCollection tipo genérico de tipo List<String>


StringDictionary Dictionary<string>
N/A   LinkedList<>

Como se puede ver en la tabla 4-20, la mayoría de las clases que han aprendido a utilizar en este
capítulo tienen un equivalente genérico. El único tipo de colección que es nuevo es la   clase
LinkedList, que será cubierto en breve.

La   clase genérica List


La  clase List genérico se utiliza para crear tipo simple-safe listas ordenadas de objetos. Por
ejemplo, si usted quiere tener una lista de números enteros, tendría que crear un  objeto List speci-
fying el tipo de entero para el parámetro genérico. Una vez que crea una instancia de la  clase de
lista genérica, puede realizar las siguientes acciones:
■   Puede usar Agregar para agregar elementos a la lista, pero los elementos debe
coincidir con el tipo especificado en el parámetro de tipo genérico de la lista.
■   también puede utilizar la sintaxis de indizador para recuperar elementos de
la lista Tipo.
■   también puede utilizar la  sintaxis de Foreach para iterar a través de la lista. En este
ejemplo se almacena enteros en la lista:
// C#
List<int> intList = new List<int>();

IntList.Add(1).
IntList.Add(2).
IntList.Add(3).
Int número = intList[0];

Foreach en intList (int i)


{
Console.WriteLine(i);
}

La  clase de lista genérica es tan fácil de usar como el ArrayList, pero de tipo seguro basado en el
parámetro de tipo genérico. Como vimos la lección 1, podemos ordenar una lista llamando
al  método Sort. No es diferente para la  clase de lista genérica, pero hay una nueva sobrecarga que
vale la pena mencionar. El  método de clasificación en la lista genérica clase admite un delegado
genérico.
¿Qué son los delegados genéricos? Son simplemente como estructuras o clases genéricas, pero
parámetros genéricos sólo se utilizan para definir la convención de llamada del delegado. Por
ejem- plo, el  método Sort de la  clase genérica List tiene una comparación genérica delegado.
La comparación genérica delegado se define de la siguiente manera:

// C#
Delegado público int comparación><T ( T x,
Ty 
)

Suponga que desea ordenar la lista en orden inverso. Usted podría escribir todo
un com- parer clase para hacer esto. O usted puede hacer las cosas más fáciles y acaba de escribir
un método que coincide con el delegado genérico, como se muestra aquí:
// C#
Static int ReverseIntComparison(int x, int y)
{
Volver y - x;
}

Observe que este método no es genérico, pero coincide con el genérico Delegado de comparación.
(La lista se compone de números enteros, así que tu comparación debe utilizer Los números
enteros para los dos parámetros.) Esta coherencia le permite llamar a la función sort con su método
para llamar a cada comparación:
// C#
ReverseIntComparison intList.Sort();

Este enfoque es mucho más fácil que escribir toda una  clase de comparación para comparaciones
poco utilizados.

 Cola genérica  y   clases de pila


Estas clases genéricas son de tipo-versiones seguras de la cola y pila clases tratadas en la Lección
2. Para utilizar estas colecciones, basta con crear nuevas instancias de suministrar el parámetro de
tipo genérico del tipo que espera en la cola o pila. Para utilizar un  tipo de cola genérica, puede
crear una instancia de la  clase Queue y realice una de las siguientes acciones: ING
■   Puede utilizar Enqueue para agregar elementos en la cola, pero los elementos debe
coincidir con el tipo especificado en el parámetro de tipo genérico de la cola.
■   también puede utilizar cola para recuperar elementos del  tipo de cola. Este ejemplo
almacena cadenas de la cola:

// C#
Cadena Queue<> q = nueva cola<> String();
Q.Enqueue("Hello");
Cadena = cola cola.Dequeue();

Un  tipo de pila genérico es igual de fácil de usar. Puede crear una instancia de la  pila Clase y
realice una de las siguientes acciones:
■   Puede utilizar Push para agregar elementos a la pila, pero los elementos debe
coincidir con el tipo especificado en el parámetro de tipo genérico de la pila.
■   también puede usar Pop para recuperar elementos del  tipo de pila. Por ejemplo,
esta pila Almacena enteros:
// C#
Stack<int> series = new Stack<> int();
Serials.Push(1).
SerialNumber = int serials.Pop();

  Clase Dictionary genérica


La  clase Dictionary genérica que más se parezca a
la Hashtable ListDictionary HybridDictionary , y clases. La  clase Dictionary genérica se
diferencia de la lista genérica, Stack, y  clases de cola que se utiliza para almacenar un par
clave/valor de una colección. Para permitir esto, necesitará especificar dos parámetros de tipo
genérico cuando se crea una instancia de la  clase Dictionary genérica. Para utilizar un diccionario
genérico tipo, puede seguir estos pasos:
1.  Cree una instancia de la  clase Dictionary genérica, especificando el tipo de la
clave y el tipo de valores que se almacenan en el diccionario.
2.  Puede utilizar la sintaxis de indizador para agregar o recuperar elementos
del diccionario, pero los tipos de estos elementos claves y valores deben coincidir
con los tipos especificados en los parámetros de tipo genérico del diccionario. En
este ejemplo se almacena enteros como las claves y las cadenas como los valores
del diccionario:

// C#
Dictionary<int, string> dict = new Dictionary<int, string>();
Dict[3] = "tres"; dict[4] =
"cuatro"; dict[1] = "uno";
dict[2] = "dos"; String str =
dict[3];

En este ejemplo se muestra cómo utilizar un entero para la clave del diccionario y cómo utilizar
una cadena para el contenido. Una diferencia importante entre la  clase Dictionary genérica y sus
homólogos no genéricos es que no utilice un  objeto DictionaryEntry para mantener el par
clave/valor. Así que cuando usted recupera objetos individuales o iterar sobre la colección,
necesitará trabajar con un nuevo tipo genérico llamado KeyValuePair.
La  clase KeyValuePair genérico tiene dos tipos al igual que la  clase Dictionary genérica.
Normalmente, no será crear instancias de este tipo; en su lugar, volverá de  clases genéricas del
diccionario. Por ejemplo, si iterar sobre un  objeto Dictionary, la devuelve un
enumerador KeyValuePair atado al nombre clave y tipos de valor especificado en El  tipo de
diccionario. Puede iterar sobre los elementos de una  clase Dictionary genérica- mente por siga
estos pasos:
1.  Cree un  bucle foreach, especificando una  clase KeyValuePair genérico como el tipo
de objeto que se devuelve en cada iteración. Los tipos especificados en
el KeyValuePair deben coincidir con los tipos usados en el diccionario original.
2.  En el interior del  bloque foreach, puede utilizar el KeyValuePair para recuperar las
claves y valores con propiedades llamado clave y valor, respectivamente. Este ejemplo
contin- ues el diccionario ejemplo mostrado anteriormente:
// C#
ForeachKeyValuePair (<int, string> i en dict)
{
Console.WriteLine("{0} = {1}", i.Key, i.Value);
}

La  clase Dictionary genérica conserva el orden en que los elementos se agregan a la lista.

 SortedList  y   clases genéricas SortedDictionary


La genérica SortedList y SortedDictionary clases son como la  clase Dictionary genérica, con la
excepción de que mantiene sus elementos ordenados por la clave de la colección. Para utilizar
un SortedList, siga estos pasos:
1.  Crear una instancia de SortedList, especificando la clave y el valor de los parámetros de
tipo genérico.
2.  Puede utilizar la sintaxis de indizador para agregar o recuperar elementos de
la SortedList, pero los tipos de estos elementos claves y valores deben coincidir con los
tipos especificados en los parámetros de tipo genérico de la SortedList.
3.  Cree un  bucle foreach, especificando una  clase KeyValuePair genérico como el tipo
de objeto que se devuelve en cada iteración. Los tipos especificados en
el KeyValuePair deben coincidir con los tipos usados en el original SortedList.
4.  En el interior del  bloque foreach, puede utilizar el KeyValuePair para recuperar las
claves y valores con propiedades llamado clave y valor, respectivamente. En este
ejemplo se almacena enteros como las claves y los valores de las cadenas como
el SortedList:
// C#
SortedList<string, int> sortList = new SortedList<> string, int();
SortList["Uno"] = 1;
sortList["2"] = 2; sortList["3"] =
3;

(KeyValuePair foreach<string, int> i en sortList)


{
Console.WriteLine(i);
}

El uso de la clase SortedDictionary es idéntico. Usar SortedDictionary, siga estos pasos:


1.  Crear una instancia de SortedDictionary, especificando la clave y el valor de los
parámetros de tipo genérico.
2.  Puede utilizar la sintaxis de indizador para agregar o recuperar elementos en
el SortedDictionary, pero los tipos de estos elementos claves y valores deben
coincidir con los tipos especificados en los parámetros de tipo genérico
de SortedDictionary.
3.  Cree un  bucle foreach, especificando una  clase KeyValuePair genérico como el
tipo de objeto que se devuelve en cada iteración. Los tipos especificados en
el KeyValuePair deben coincidir con los tipos usados en el
original SortedDictionary.
4.  En el interior del  bloque foreach, puede utilizar el KeyValuePair para recuperar
las claves y valores con propiedades llamado clave y valor, respectivamente. En
este ejemplo se almacena enteros como las claves y los valores de las cadenas
como el SortedDictionary:
// C#
SortedDictionary<string, int> sortedDict =
Nueva SortedDictionary<string, int>();
SortedDict["Uno"] = 1;
sortedDict["2"] = 2; sortedDict["3"]
= 3;
(KeyValuePair foreach<string, int> i en sortedDict)
{
Console.WriteLine(i);
}

 LinkedList  clase genérica


La  clase LinkedList genérico es un tipo de colección que es nueva en .NET, aunque los con- la
CEPT es bien desgastadas y probado. De hecho, recuerdo haber escrito un LinkedList en la
universidad. La idea detrás de una lista enlazada es un conjunto de elementos que están vinculados
entre sí. En cada elemento, puede desplazarse al elemento siguiente o anterior sin tener que tener
acceso al col- lection sí. Esto es muy útil cuando se pasan objetos alrededor que necesita saber
acerca de sus hermanos.
La tabla 4-21 y la tabla 4-22 muestran la interfaz para la  clase LinkedList
genérico:
La tabla 4-21
LinkedList  Propiedades
Nombre Descripción
Conde  obtiene el número de nodos en el LinkedList
Primero  se obtiene el primer nodo en el LinkedList
Última  obtiene el último nodo LinkedList

La tabla 4-22
LinkedList  métodos
Nombre Descripción
   Agrega un nuevo nodo AddAfter después un nodo existente
en LinkedList AddBefore  agrega un nuevo nodo antes de un nodo
existente en LinkedList AddFirst  agrega un nuevo nodo al comienzo
de LinkedList AddLast  agrega un nuevo nodo al final de LinkedList
Borrar  Elimina todos los nodos desde LinkedList
Contiene  pruebas para ver si un valor está contenida dentro de LinkedList
CopyTo  copia todo el LinkedList a una matriz
Buscar   Localiza el primer nodo que contenga el valor especificado
FindLast  busca el último nodo que contenga el valor especificado
La tabla 4-22  LinkedList  métodos

Nombre Descripción
Quitar  Quita la primera aparición de un valor o un nodo de
LinkedList
RemoveFirst  elimina el primer elemento de LinkedList
RemoveLast  elimina el último elemento de LinkedList

LinkedList  LinkedListNode contiene una colección de objetos. Cuando se trabaja con


un LinkedList, usted será principalmente obteniendo y caminar por los nodos. Las propiedades de
la  clase LinkedListNode genérico se detallan en la tabla 4-23.
Tabla 4-23  LinkedListNode  Propiedades

Nombre Descripción
Lista  obtiene el LinkedList que el nodo pertenece a
   Obtiene el siguiente nodo siguiente en
el LinkedList anterior  Obtiene el nodo anterior en
el LinkedList valor  obtiene el valor contenido en
el nodo.

Una peculiaridad de la  clase LinkedList genérico es que la aplicación de la enu-


ILinkedListEnumerator merator () permite la enumeración de los valores de la lista con-
utilizando LinkedListNode objetos. Este comportamiento es diferente del  tipo de diccionario
genérico, donde el enumerador devuelve un  objeto KeyValuePair genérico. La diferencia existe
porque LinkedListNode objetos pueden ser utilizados para recorrer la lista de elementos, pero sólo
un fragmento de datos en cada nodo. Por lo tanto, no es necesario devolver los nodos durante la
enumeración.
Utilizar LinkedList, puede crear una instancia de la  clase LinkedList, especificando el tipo para ser
almacenados como valores en la lista y, a continuación, puede realizar cualquiera de las siguientes
acciones:
■   Puede utilizar los    métodos AddLast AddFirst y para agregar elementos al
principio y al final de la lista, respectivamente. Los     jefes de delegación se
reunieron AddLast AddFirst y retur n un LinkedListNode si son simplemente
especificando el valor de estos métodos.
■   también puede utilizar los    métodos AddBefore y AddAfter para agregar valores
en el medio de la lista. Para usar estos métodos, debe tener acceso a
un LinkedListNode al que desea agregar valores antes o después.

■   también puede usar la  construcción foreach para iterar sobre los valores en
el LinkedList.
Tenga en cuenta que el tipo puede enumerar son los valores, no los nodos de la
lista. Este ejemplo almacena cadenas en el LinkedList:
// C#
LinkedList<> Enlaces cadena = new LinkedList<string>(); LinkedListNode<string> Primera =
links.AddLast("First"); LinkedListNode<cadena> última = links.AddFirst("Último");
LinkedListNode<string> segundo = links.AddBefore(último, "Segundo");
links.AddAfter(segundo, "Terceros");

(Foreach string s en enlaces)


{
Console.WriteLine(s);
}

Estructura de la clase de colección genérica


Muy similar a las colecciones no genéricas, existen diferentes partes de la forma en que funcionan
las colecciones genéricas, que son comunes a través de diferentes colecciones genéricas. Estos
com- monalities incluyen el uso de interfaces de colección genérica, Generic enumeradores,
comparaciones y genéricos.

Interfaces de colección genérica


En las colecciones no genéricas, un conjunto de interfaces definen una interfaz coherente a través
de col- ciones. Estas interfaces incluyen IEnumerable, ICollection, IList, y así sucesivamente.
Aunque las colecciones genéricas implementar estas interfaces, también admiten las versiones
genéricas de estos mismos interfaces, como se muestra en el ejemplo siguiente:
// C#
List<String> stringList = new List<String>();

// ...

La secciónlista IList = (IList)stringList;


Objeto firstItem = La secciónlista[0];

La   interfaz IList no genéricos es apoyada por la lista genérica colección. Pero en las operacio-
nes, hay una  interfaz IList genérico que puede utilizarse para obtener datos de la interfaz de un
modo seguro, así:
// C#
IList<String> typeSafeList = (IListstringList<String> );
Cadena = typeSafeList firstString[0];
Este es el mismo para la interfaz ICollection, IDictionary, IEnumerable, y así sucesivamente. En
general, si se trabaja con colecciones genéricas, pero también desea trabajar con las interfaces en
lugar de la clase específica, debe utilizar la versión genérica de la interfaz compatible con
seguridad de tipos.

Los enumeradores colección genérica


Las colecciones genéricas que se muestran en esta lección todo apoyo itera sobre los valores de la
colección. Para facilitar la iteración, cada colección genérica admite su propia  estructura de
enumerador anidados. Esta estructura de enumerador es especializada para el mismo tipo de clase
principal. Si necesita utilizar el enumerador real en lugar de la de- cada construcción, puede
obtener el enumerador llamando al  método GetEnumerator así:

// C#
List<string> stringList = new List<string>();

// ...

List<string>.enumerador e = stringList.GetEnumerator();

Mientras (e.MoveNext())
{
// Typesafe acceso al elemento actual
String s = e.Current;
}

Mediante la  estructura de enumerador, puede obtener el elemento actual de la recogió genérico-


ción de un modo seguro. Todas las colecciones genéricas admiten esta  estructura de enumerador.

Las comparaciones genéricas


En las lecciones anteriores, vimos que podíamos usar la    interfaz IComparer y
IEqualityComparer para proporcionar operaciones de comparación para acciones de ordenación y
comparación en nuestras colecciones. Para las colecciones genéricas, existen versiones genéricas
de estas inter- faces. En los casos en que es necesario escribir su propia implementación
de IComparer y IEqualityComparer interfaces, clases base genéricas pueden hacer gran parte del
trabajo para usted. Estas clases son la  clase Comparer genérico EqualityComparer genérico
y clase. Si necesita implementar su propia lógica de comparación, debe heredar de estas clases
base, implementar cualquier métodos abstractos y anular cualquier comportamiento
predeterminado que necesita, como se muestra en el ejemplo siguiente:

// C#
Clase MyComparer<T> : Comparer<T>
{
Anulación pública int Compare(T , T y x)
{
Volver x.GetHashCode() - y.GetHashCode();
}
}
Escribir sus propias colecciones
Las interfaces de la colección que se mencionan en este capítulo (por ejemplo, IList e
ICollection) se puede usar para implementar sus propias colecciones. Puede empezar
de cero y escribir sus propias colecciones que implementan estas interfaces, y el resto
de .NET Framework reconocerá tus clases como colecciones.
Gran parte de la labor necesaria para escribir sus colecciones es común a muchos
colectores- ciones. .NET Framework expone varias clases base para concluir este
comportamiento común:
■   CollectionBase

■   ReadOnlyCollectionBase

■   DictionaryBase

Estas clases base puede ser utilizado como la base de su propia


colección. CollectionBase clase admite la interfaz IList, IEnumerable, y  las
interfaces ICollection. Hereda de CollectionBase le permitirá disponer de una
colección que ya admite estas interfaces. Usted podría utilizar la  clase
CollectionBase cuando necesite una simple recopilación de elementos con algún
comportamiento específico que usted no encontrará en la incorporada en las
colecciones.
Como CollectionBase clase, el ReadOnlyCollectionBase admite la interfaz
IList, IEnumerable, y  las interfaces ICollection. La gran diferencia en la base
ReadOnlyCollection es que no permite cambiar la colección desde fuera de la clase.
Esta clase es ideal cuando se necesita su propia colección, que es de sólo lectura para
los usuarios de la colección.
A diferencia de CollectionBase y ReadOnlyCollectionBase clases,
la DictionaryBase implementa el IDictionary, IEnumerable, y  las interfaces
ICollection. El Diccionario- clase base se utilizaría si necesita implementar su propia
colección con clave.
Antes de .NET 2.0, sería común a crear sus propias colecciones usando estas
interfaces para crear colecciones de tipo seguro. Ahora que los genéricos disponibles,
es preferible utilizar las colecciones genéricas si el único requisito es un tipo-
colección segura.
Laboratorio: crear y utilizar una colección genérica
En este laboratorio, puede crear un diccionario genérico para contener códigos de país llamando y
el nombre completo del país. Si se produce un problema de completar un ejercicio, la com- pletó
proyectos están disponibles en el CD en la carpeta de código.
Ejercicio 1: Crear una colección genérica para almacenar los datos de estado

En este ejercicio, puede crear un diccionario genérico para mantener los códigos de llamada con
sus nombres completos.
1.  Crear una nueva aplicación de consola denominada GenericCollections.
2.  En el  método Main del proyecto, crear una nueva instancia del diccionario
genérico
Clase, especificando la clave para ser un entero y el valor a una cadena.
3.  Agregar elementos a la colección mediante códigos de país para las claves y los
nombres de países, como los valores.
4.  Intente agregar cadenas para las claves de los códigos de país para asegurarse de
que el diccionario
Es de tipo seguro. Si no compila, elimínelos en código o comentarlos.
5.  Escribir en la consola de uno de los códigos de país utilizando el indizador sintaxis
del diccionario.
6.  Iterar sobre la colección, y escribir el código del país y el nombre del país para
cada KeyValuePair en el diccionario. El código podría tener este aspecto:

// C#
Programa de clase
{
Static void main(String[] args)
{
Dictionary<int, String> countryLookup =
Nuevo diccionario<int, String>();

CountryLookup[44] = "Reino Unido";


countryLookup[33] = "Francia"; countryLookup[31] =
"Holanda"; countryLookup[55] = "Brasil".
//CountryLookup["64"] = "Nueva Zelanda";

Console.WriteLine("El 33 Código: {0}", countryLookup[33]);


(KeyValuePair foreach<int, String> elemento countryLookup)
{
Código int = item.clave;
Cadena país = item.Value;
Console.WriteLine("código {0} = {1}", código país);
}

Console.Read();
}
}

7.  Genere el proyecto y resolver cualquier error. Compruebe que la aplicación de


consola suc- logre muestra todos los países añadidos.

Resumen de la lección
■   lascolecciones genéricas pueden utilizarse para crear más tipos seguros y
potencialmente las versiones más rápidas de sus homólogos no genéricos.
■   La lista genérica, Diccionario, Cola, Stack, SortedList SortedDictionary , y las
clases son de tipo versiones seguras de las colecciones que fueron discutidas en las
Clases 1 a 3.
■   La nueva  clase genérica LinkedList es una colección para almacenar elementos
que conocen su propia relación en la lista, y permite la iteración sin tener acceso a
la colección en sí misma.

Examen de la lección
Puede utilizar las siguientes preguntas para poner a prueba su conocimiento de la información en
la lección 5, "colecciones genéricas." Las preguntas también están disponibles en el CD
complementario si prefiere revisarlos en forma electrónica.

Nota   Respuestas
Las respuestas a estas preguntas y explicaciones de por qué cada opción de respuesta
es correcta o no se encuentran en la sección "Respuestas" al final del libro.

1.  ¿Qué tipo de objeto  enumerador diccionario genérico volver?


A.  Objeto
B.   Los objetos de la clase genérica KeyValuePair
C.   Key
D.   Valor
2.  Donde se pueden agregar elementos a un LinkedList? (Seleccione todos los que
correspondan).
A.  Al comienzo de la LinkedList
B.   Antes de cualquier nodo específico
C.   Después de cualquier nodo específico
D.   Al final de la LinkedList
E.   En cualquier índice numérico en el LinkedList
Repaso del cap tulo
A nuevas prácticas y reforzar las habilidades aprendidas en este capítulo, puede per- en las
siguientes tareas:
■   Revisar el resumen del capítulo.
■   Revisar la lista de términos clave que se introdujo en este capítulo.
■   Complete los casos. Estos escenarios configurar situaciones del mundo real
involv- ción de los temas de este capítulo, y le pedimos a usted para crear una
solución.
■   completar las prácticas sugeridas.
■   tomar un examen de práctica.

Resumen del capítulo


■   un ArrayList es la más básica de las colecciones, lo que le permite mantener
una lista ordenada de objetos.
■   La cola y pila colecciones le permiten almacenar listas de elementos
secuenciales.
■   Los Diccionarios puede ser usada para guardar pares clave/valor de elementos
para agilizar las búsquedas.
■   haycolecciones especializadas que le permiten recopilar las cadenas
y Boolean val- ues y crear tablas de búsqueda sólo de cadena.
■   lascolecciones genéricas son un mecanismo para crear colecciones de tipo
seguro sin escribir todas sus propias clases de colección.

Términos clave
¿ Sabes lo que significan estos términos clave? Puede verificar sus respuestas mediante la
búsqueda de los términos en el glosario al final del libro.
■   colección

■   tipo genérico
■   iteración

Casos
En los siguientes casos, podrá aplicar lo que ha aprendido acerca de cómo utilizar las
colecciones. Usted puede encontrar las respuestas a estas preguntas en la sección de
"respuestas" al final de este libro.
Caso práctico 1: Usar un ArrayList  para almacenar códigos de
estado
Usted es un desarrollador para un departamento de TI en una empresa grande. Usted escribir
pequeñas apli- caciones que ayudan a los usuarios a ver los pedidos en el sistema. Su jefe le dice
que usted necesita para agregar un campo de código de estado a una aplicación existente. Ella dice
que los códigos de estado son estáticos y no van a ser al menos cinco, aunque ese número podría
cambiar más adelante.

Preguntas
Responda las siguientes preguntas para el administrador.
1.  ¿Cómo vas a almacenar los códigos de estado para utilizar en el formulario?
2.  Si los códigos de estado deben ser ordenados de forma distinta para diferentes
usuarios, ¿es eso un problema?

Caso práctico 2: Seleccione la correcta recaudación


Usted es un desarrollador que trabaja para un pequeño negocio inmobiliario. Usted escribió una
pequeña aplicación que permite a la empresa mantener un seguimiento de los anuncios actuales.
Usted está informado por su jefe que la empresa acaba de ser comprada por una empresa de mayor
tamaño. Tu jefe quiere que la aplicación actual para ser modificado para mantener una lista de
todas las personas de ventas actual.

Entrevistas
La siguiente es una lista de personal de la empresa entrevistados y sus declaraciones:
■   Su Manager  "no estamos seguros de cuántos nuevos vendedores  que tenemos,
pero tenemos que ser capaces de utilizar un código de ventas y el nombre completo
de la persona de ventas en dif erentes- partes de la aplicación".
■      "la persona de ventas en la actualidad, la aplicación no muestra el agente de
lista respon- sable. Pero si la adición de este comportamiento va a ralentizar la
aplicación, podemos vivir sin él".

Preguntas
Responda las siguientes preguntas para el administrador:
1.  La colección que se utilizará, a sabiendas de que el tamaño de la lista cambiará y
puede ser pequeño o grande?
2.  Cómo mostrar el agente de lista afecta al rendimiento de la aplicación?
Hipótesis 3: Reescribir para utilizar una colección Type-Safe
Usted es un desarrollador principal en una gran entidad bancaria. El departamento de TI
trabaja para tiene un montón de desarrolladores junior. Uno de los programadores junior
escribió una colección para mantener una lista de toda la actividad de la cuenta bancaria para
un año. Varios desarrolladores están usando y teniendo algunos problemas con los errores en
tiempo de ejecución porque un ArrayList se utiliza para almacenar toda la actividad.

Entrevistas
La siguiente es una lista de personal de la empresa entrevistados y sus
declaraciones:
■   Su Manager  "Tenemos que cambiar la colecta para ayudar a nuestro
personal a desarrollar sus aplicaciones más rápido y las aplicaciones deben ser
más fiable".
■   Programador Junior  "cuando utilizo la colección, intento agregar
actividad y algunas veces me accidentalmente agregar el tipo equivocado de
objeto en la colección. Desde que compila bien, no nos enteramos de
un problema hasta que uno de los escrutadores tiene la apli-
Catión crash".

Preguntas
Responda las siguientes preguntas para el administrador.
1.  ¿Cómo van a volver a implementar esta colección para resolver estas
cuestiones?
2.   La nueva colección va a ser más lenta que la actual?

Prácticas recomendadas
Que le ayudarán a dominar los objetivos contemplados en el presente capítulo, complete las
siguientes tareas.

Utilice las colecciones genéricas.


Para esta tarea, se deben completar un mínimo de prácticas 1 y 2. Para una comprensión más
profunda de las colecciones genéricas, debe completar también la práctica 3.

Práctica 1
■   Crear un nuevo  objeto ArrayList.
■   añadir algunos objetos de diferentes tipos.
Práctica 2
■   Crear una lista genérica de un tipo específico.
■   añadir algunos objetos del tipo correcto e incorrecto.
■   ver cómo funciona de manera diferente en cada situación.

Práctica 3.
■   Crear un diccionario genérico Object.
■   agregar varios elementos.
■   iterar sobre los elementos y ver cómo la  clase KeyValuePair genérico
funciona.

Compare   las clases de diccionario


Para esta tarea, usted debe completar al menos Práctica 1. Si desea ver cómo grandes
colecciones trabajo, también debe completar las prácticas 2 y 3.

Práctica 1
■   Crear Hashtable ListDictionary HybridDictionary , y objetos.
■   Almacenar cinco objetos en cada diccionario.
■   prueba la velocidad de búsquedas con los distintos diccionarios y ver cómo
difieren en función del tamaño de la lista de almacenes.

Práctica 2
■   cambiar los objetos que ha creado en la práctica 1 para almacenar 100
objetos, y ver si los resultados son similares.

Práctica 3.
■   cambiar los objetos que ha creado en la práctica 1 para almacenar 10.000
objetos, y ver si los resultados son similares.
Tomar un Test de práctica
Los tests de práctica en este libro's Companion CD ofrecen muchas opciones. Por ejemplo,
puede hacerse la prueba en un solo examen objetivo, o puede probar usted mismo en todos los
Contenido del examen de certificación 70-536. Puede configurar la prueba tan estrechamente
que simula la experiencia de tomar un examen de certificación, o puede configurarlo en modo
de estudio, de modo que usted puede mirar las respuestas correctas y explicaciones después de
responder a cada pregunta.

Más info  práctica de pruebas


Para obtener más información acerca de la prueba de la práctica todas las
opciones disponibles, consulte la sección "Cómo usar los tests de práctica"
La sección en este manual de introducción.
Capítulo 5
La serialización
Muchas aplicaciones necesitan almacenar o transferir datos almacenados en objetos. Para realizar
estas tareas tan sencillas como sea posible, el .NET Framework incluye varias técnicas de
serialización. Estas técnicas convertir objetos en binario, Simple Object Access Protocol (SOAP), o
documentos XML que se pueden almacenar fácilmente, transferir y recuperar. Este capítulo
describe cómo implementar la serialización mediante las herramientas incorporadas en el marco
de .NET y cómo implementar la serialización para satisfacer los requisitos personalizados.

Objetivos del examen en este capítulo:

■   serializaro deserializar un objeto o un objeto gráfico mediante runtime serializa-


Ción técnicas. (Consulte el  espacio de nombres System.Runtime.Serialization).
❑ la   serialización de las interfaces.
❑   atributos de serialización.
❑   SerializationEntry estructura y SerializationInfo clase.
❑   ObjectManager clase.

❑ El   formateador  , FormatterConverter clase clase y  clase FormatterServices.


❑   StreamingContext estructura.

■   controlar la serialización de objetos en formato XML mediante System.Xml


 Espacio de nombres de .la serialización.
❑   serializar y deserializar objetos en formato XML usando XmlSerializer
Clase.
❑   controlar la serialización mediante atributos de serialización.
❑   Implementar interfaces de serialización XML para proporcionar un formato
personalizado La serialización XML.
❑   Delegados y controladores de eventos son proporcionados por
el System.Xml.Serialization Espacio de nombres.

■   Aplicar formato de serialización personalizada mediante la serialización clases


Formatter.
❑    clase
SoapFormatter.  (Consulte la sección
System.Runtime.Serialization.formateadores.Soap
Espacio de nombres).
❑    claseBinaryFormatter
(Consulte System.Runtime.Serialization.formateadores.Binary
Espacio de nombres).

Las lecciones de este capítulo:

■ La   Lección 1: serialización de objetos . . . . . . . . . . . . . . . . . . . . . . . . . . . 269


    Lección 2: ■ La serialización XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . 288
■ La   Lección 3: Serialización personalizada . . . . . .. . . . . . . . . . . . . . . . . . . . 302

Antes de empezar
Para completar las lecciones de este capítulo, debe estar familiarizado con Microsoft Visual Basic
o en C# y se sienten cómodos con las siguientes tareas:
■   Crear una aplicación de consola en Microsoft Visual Studio, usando Visual Basic o
C#.
■   Sistema de agregar referencias a bibliotecas de clases para un proyecto.
■   escribir en archivos y objetos stream.

Lección 1: la serialización de objetos


Cuando se crea un objeto en una aplicación de .NET Framework, usted probablemente nunca
pensar acerca de cómo se almacenan los datos en MEMOR Y. Usted no debería tener a .NET
Framework se encarga de eso por usted. No obstante, si desea guardar el contenido de un objeto en
un archivo, enviar un objeto a otro proceso o transmitirla a través de la red, tienes que pensar en
cómo el objeto es representado porque tendrá que convertirlo a un formato diferente. Esta
conversión se denomina serialización.

Después de esta lección, será capaz de:


■   elegir entre binario, SOAP, XML, y la serialización personalizada.
■   serializar y deserializar objetos utilizando las bibliotecas estándar.
■   Crear clases que se pueden serializar y deserializar.
■   Cambiar el comportamiento estándar del proceso de serialización y deserialización.
■   aplicar la serialización personalizada para tomar el control total del
proceso de serialización.
Lección Tiempo estimado: 45 minutos

¿Qué es la serialización?
Serialización, tal como se implementa en el  espacio de nombres System.Runtime.Serialization, es
el proceso de serializar y deserializar los objetos de modo que puedan almacenarse o trasladarse y
posteriormente re-creado. La serialización es el proceso de convertir un objeto en una secuencia
lineal de bytes que pueden ser almacenados o transferidos. Deserialización es el proceso de
convertir una secuencia de bytes serializada previamente en un objeto.

Mundo Real
Tony Northrup
La serialización puede ahorrar un montón de tiempo de desarrollo. Antes de que se
dispusiera de serialización, tuve que escribir código personalizado sólo para
almacenar o transferir información. Por supuesto, este código tiende a romperse
cuando he realizado cambios en otras partes de la aplicación. Hoy en día, con .NET
Framework, puedo almacenar y transferir datos con sólo un par de líneas de
código. De hecho, pocas veces me he encontrado la necesidad de modificar el
comportamiento predeterminado alization seri--simplemente funciona.

Básicamente, si usted desea almacenar un objeto (o varios objetos) en un archivo para su posterior
recuperación, puede almacenar el resultado de la serialización. La próxima vez que desee leer los
objetos, puede llamar a los métodos de deserialización, y su objeto es re-creado exactamente como
lo había sido anteriormente. De manera similar, si desea enviar un objeto a una aplicación que se
ejecuta en otro equipo, puede establecer una conexión de red, serializar el objeto a la secuencia y, a
continuación, deserializar el objeto en la aplicación remota. La teletransportación en la ciencia
ficción de ence es un buen ejemplo de serialización (aunque la teletransportación no es actualmente
compatible con .NET Framework).

Nota   detrás de las escenas de


serialización
Windows se basa en la serialización para muchas tareas importantes, incluyendo
servicios Web, remoting, y copiar elementos en el portapapeles.

Cómo serializar un objeto


En un nivel alto, los pasos para serializar un objeto son como sigue:
1.  Crear un objeto Stream para sujetar la salida serializados.
2.  Crear un  objeto BinaryFormatter (ubicado en 
System.Runtime.Serialization.formateadores.Binary).
3.  Llamar al  método.Serialize BinaryFormatter para serializar el objeto, y enviar el
resultado a la secuencia.
En el plano del desarrollo, la serialización puede ser aplicado con muy poco código. La fol- bajar la
consola que requiere la aplicacion System.IO, System.Runtime.Serialization y
System.Runtime.Serialization.formateadores. Binary-demuestra esto:
// C#
Datos de cadena = "Esto debe ser almacenada en un archivo.".

// Crear un archivo para guardar los datos en


FileStream fs = new FileStream("SerializedString.Data", FileMode.Create);

// Crear un objeto BinaryFormatter para realizar la serialización


BinaryFormatter bf = new BinaryFormatter();

// Usar el objeto BinaryFormatter para serializar los datos para el archivo bf.Serialize(fs, datos);

// Cerrar el archivo fs.Close();

Si ejecuta la aplicación y abrir el archivo de datos SerializedString.En el Bloc de notas, verá el


contenido de la cadena que almacena rodeado de información binaria (que aparece como basura en
el Bloc de notas), como se muestra en la figura 5-1. .NET Framework almacena la cadena como
texto ASCII y luego se agrega un poco más de bytes binarios antes y después del texto para
describir los datos para el deserializer.

Figura 5-1  Serializar objetos pueden guardarse como archivos pero no son archivos de texto.

Si sólo es necesario para almacenar una sola cadena en un archivo, no sería necesario utilizar
serializa- ción-simplemente puede escribir la cadena directamente a un archivo de texto. La
serialización resulta útil al almacenar información más compleja, como la fecha y la hora actuales.
Como demuestra el ejemplo de código siguiente, la serialización de objetos complejos es tan
simple como serializar una cadena:
// C#
// Crear un archivo para guardar los datos en
FileStream fs = new FileStream("SerializedDate.Data", FileMode.Create);

// Crear un objeto BinaryFormatter para realizar la serialización


BinaryFormatter bf = new BinaryFormatter();

// Usar el objeto BinaryFormatter para serializar los datos para el archivo bf.Serialize(fs,
System.DateTime.Now);

// Cerrar el archivo
fs.Close();
Cómo deserializar un objeto
Deserializar un objeto le permite crear un nuevo objeto basado en los datos almacenados. Essen-
considerablemente, deserializar restaura un objeto guardado. En un nivel alto, los pasos para
deserializar un objeto son como sigue:
1.  Crear un objeto Stream para leer la salida serializados.
2.  Crear un  objeto BinaryFormatter.
3.  Crear un nuevo objeto para almacenar los datos deserializado.
4.  Llamar al  método BinaryFormatter.deserializar para deserializar el objeto, y lo
echó al tipo correcto.
En el nivel de código, los pasos para deserializar un objeto son fáciles de implementar. La fol-
bajar la consola que requiere la aplicación System.IO, System.Runtime.Serialization y
System.Runtime.Serialization.formateadores.Binary namespaces demuestra cómo leer y mostrar la
cadena serializada datos guardados en un ejemplo anterior:

// C#
// Abrir un archivo para leer los datos desde
FileStream fs = new FileStream("SerializedString.Data", FileMode.Open);

// Crear un objeto BinaryFormatter para realizar la deserialización


BinaryFormatter bf = new BinaryFormatter();

// Crear el objeto para almacenar los datos de la cadena de datos deserializados =


"";

// Usar el objeto BinaryFormatter para deserializar los datos desde el archivo de datos = (string)
bf.deserializar(fs);
// Cerrar el archivo fs.Close();

// Mostrar la cadena deserializado


Console.WriteLine(data).

Deserializar un objeto más complejo, como DateTime, funciona exactamente de la misma forma.


El siguiente ejemplo de código muestra el día de la semana y la hora almacenada por un ejemplo de
código anterior:
// C#
// Abrir un archivo para leer los datos desde
FileStream fs = new FileStream("SerializedDate.Data", FileMode.Open);

// Crear un objeto BinaryFormatter para realizar la deserialización


BinaryFormatter bf = new BinaryFormatter();

// Crear el objeto para almacenar los datos deserializados


DateTime DateTime = new previousTime();

// Usar el objeto BinaryFormatter para deserializar los datos desde el archivo previousTime = (DateTime)
bf.deserializar(fs);
// Cerrar el archivo
fs.Close();

// Mostrar el tiempo deserializado


Console.WriteLine("Día: " + previousTime.DayOfWeek + ", _ Hora: " +
previousTime.TimeOfDay.ToString());

Como demuestran estos ejemplos de código, almacenar y recuperar objetos requiere


sólo unas pocas líneas de código, no importa cuán complejo es el objeto.

Nota   El funcionamiento interno de la


deserialización
En el tiempo de ejecución, la deserialización puede ser un proceso complejo. El
tiempo de ejecución avanza por el proceso de deserialización secuencialmente,
empezando al principio y trabajar su camino hasta el final. El proceso se complica si
un objeto serializado en la secuencia hace referencia a otro objeto.
Si un objeto hace referencia a otro objeto, el formateador  (discutido en más detalle
en la lección 3) Consulta ObjectManager  para determinar si el objeto al que se hace
referencia ya ha sido deserializar (una referencia hacia atrás), o si no ha sido aún
deserializar (una referencia adelantada). Si es una referencia hacia atrás,
el formateador  finaliza inmediatamente la referencia. Sin embargo, si es una
referencia adelantada, el formateador  registra una reparación  con
el ObjectManager. Una reparación está ultimando una referencia de objeto después
del objeto al que se hace referencia ha sido deserializar. Una vez que el objeto al que
se hace referencia se deserializa ObjectManager,  completa la referencia.

Cómo crear clases que se pueden serializar


Puede serializar y deserializar clases personalizadas agregando el  atributo Serializable a la clase.
Esto es importante para que usted, u otros desarrolladores usando tu clase, puede fácilmente
almacenar o transferir las instancias de la clase. Incluso si usted no necesita seri- alization
inmediatamente, es una buena práctica para habilitarla para su uso en el futuro.
Si está satisfecho con el manejo de la serialización predeterminado, ningún otro código además
el  atributo Serializable es necesaria. Cuando su clase es serializado, el runtime seri- alisios todos
los miembros, incluidos los miembros privados.

Tenga en cuenta   las preocupaciones en materia de seguridad con la serialización


La serialización puede permitir que otro código para ver o modificar los datos de una
instancia de objeto que de otra manera serían inaccesibles. Por lo tanto, realizar la
serialización de código requiere el   atributo SecurityPermission (desde
el System.Security.Permissions  namespace) con el SerializationFormatter  indicador
especificado. Bajo la política predeterminada, este permiso no es dado a descargar de
Internet o intranet código; sólo el código
En el equipo local se concede este permiso. El   método GetObjectData deberían estar
explícitamente
Protegidas por el exigente   con el atributo
SecurityPermission SerializationFormatter  bandera
Especifica, como se ilustra en el código de ejemplo en la lección 3, o exigiendo que otros
permisos
Ayudar específicamente a proteger los datos privados. Para obtener más información
acerca de la seguridad del código, consulte el capítulo 12,
"El usuario y seguridad de datos".

También puede controlar la serialización de sus clases para mejorar la eficiencia de su clase o para
satisfacer los requisitos personalizados. Las secciones siguientes describen cómo personalizar
cómo se comporta su clase durante la serialización.

Cómo deshabilitar la serialización de miembros específicos


Algunos miembros de la clase, como temporales o valores calculados, podrían no necesitar ser
almacenados. Por ejemplo, considere la siguiente clase, ShoppingCartItem:

// C# [serializable]
Clase ShoppingCartItem
{
Int productId público; público precio
decimal; public int cantidad decimal;
pública total.
ShoppingCartItem pública(int _productID, decimal, int __precio cantidad)
{
ProductId = _productID;
Precio = _precio; cantidad =
_cantidad; total = precio * cantidad;
}
}

La ShoppingCartItem incluye tres miembros que deberán ser facilitados por la aplica- ción cuando
se crea el objeto. El cuarto miembro, total, se calcula dinámicamente multiplicando el precio y la
cantidad. Si esta clase se serializa como está, el total sería almacenada con el objeto serializado,
perder una pequeña cantidad de espacio de almacenamiento. Para reducir el tamaño del objeto
serializado (y por lo tanto reducir los requisitos de almacenamiento al escribir la seri- alized objeto
a un disco, y los requisitos de ancho de banda al transmitir el objeto serializado a través de la red),
agregue el  atributo NonSerialized al total del miembro:
// C#
[NonSerialized] decimal público total.

Ahora, cuando el objeto está serializado, el total de los estados serán omitidos. Asimismo, el total
de los Estados no se inicializa cuando se deserializa el objeto. Sin embargo, el valor
de total todavía debe calcularse antes el objeto deserializado es utilizado.
Para activar tu clase para inicializar automáticamente un miembro nonserialized, utilice el
IDeserializationCallback interfaz y, a continuación, implementar  IDeserializationCallback
.OnDeserialization. Cada vez que tu clase se deserializa el runtime, llamará al  método
OnDeserialization IDeserializationCallback.Después de la deserialización es completa. El siguiente
ejemplo muestra la  clase modificada ShoppingCartItem no serializar el  valor total, y para calcular
automáticamente el valor en la deserialización:

// C# [serializable]
Clase IDeserializationCallback ShoppingCartItem : {
Int productId público; público precio
decimal; public int cantidad;
[NonSerialized] decimal público total.
ShoppingCartItem pública(int _productID, decimal, int __precio cantidad)
{
ProductId = _productID;
Precio = _precio; cantidad = _cantidad; total
= precio * cantidad;
}
Void IDeserializationCallback.OnDeserialization(object sender)
{
// Después de la deserialización, calcular el total total = precio *
cantidad;
}
}

Con OnDeserialization implementado, el total de los estados ahora está correctamente definido y


disponible para las aplicaciones después de la clase se guardan.

Cómo proporcionar compatibilidad de versiones


Puede haber problemas de compatibilidad entre versiones si alguna vez intenta deserializar un
objeto que ha sido serializado por una versión anterior de la aplicación. Específicamente, si se
agrega un miembro de una clase personalizada e intentar deserializar un objeto que carece de ese
miembro, el tiempo de ejecución se producirá una excepción. En otras palabras, si agrega un- tados
a una clase en la versión 3.1 de su aplicación,  no será capaz de deserializar un objeto creado por la
versión 3.0 de su aplicación.
Para superar esta limitación, usted tiene dos opciones:
■   aplicar la serialización personalizada, tal como se describe en la lección 3, que es
capaz de importar objetos serializados anterior.
■   aplicar el   atributo OptionalField a miembros recién añadidos que podrían causar los
problemas de compatibilidad entre versiones.

El  atributo OptionalField  no afecta el proceso de serialización. Durante deserial- lización, si los


estados no fue serializado, el runtime dejará el valor del miembro, como null en lugar de lanzar una
excepción. En el ejemplo siguiente se muestra cómo utilizar el  atributo OptionalField:

// C# [serializable]
Clase IDeserializationCallback ShoppingCartItem :
{
Int productId público; público
precio decimal; public int
cantidad;
[NonSerialized] decimal público total.
[Público] OptionalField bool tributable;

Si usted necesita para inicializar los miembros opcional, implementan la   interfaz IDeserialization-
la devolución de llamada como se describe en la sección "Cómo deshabilitar la serialización de
determinados países miem- bros" anteriormente en esta lección, o responder a los eventos de
serialización, como se describe en la Lección 3.

Nota   .NET 2.0


El .NET Framework 2.0 es capaz de deserializar objetos que han usado los miembros,
por lo que aún puede tener la capacidad para deserializar un objeto si tiene un
miembro que ha sido extraído desde serial- ización. Este comportamiento es distinto
de .NET Framework 1.0 y 1.1, que lanzó una excepción si información adicional se
encuentra en el objeto serializado.

Mejores prácticas para la compatibilidad de la versión


Para asegurar el correcto comportamiento de las versiones, siga estas reglas cuando se modifica
una clase personalizada de una versión a otra:
■   Nunca quitar un campo serializado.
■   Nunca aplique NonSerializedAttribute atributo para un campo si el atributo no se
aplicó al campo en una versión anterior.
■   nunca cambie el nombre o el tipo de un campo serializado.
■   Cuando se agrega un nuevo campo serializado, aplicar el  atributo
OptionalFieldAttribute.
■   Al quitar un  atributo NonSerializedAttribute desde un campo que no estaba seri-
alizable en una versión anterior, aplicar el  atributo OptionalFieldAttribute.
■   Todos los campos opcionales, establecer valores predeterminados mediante la
serialización callbacks salvo 0 o nulo como predeterminados son aceptables.

Elegir un formato de serialización


El .NET Framework incluye dos métodos para dar formato a los datos serializados en el Sistema
.Runtime.Serialization namespace,  ambas de las cuales implementar los IRemotingFormatter
Interfaz:
■ BinaryFormatter  situado en
el System.Runtime.Serialization.formateadores.Binary namespace, este
formateador es la manera más eficiente para serializar objetos que serán leídos por
sólo de aplicaciones basadas en .NET Framework.
■  SoapFormatter  situado en
el System.Runtime.Serialization.formateadores.Soap namespace, este formateador
basado en XML es la forma más confiable para serializar objetos que serán
transmitidos a través de una red o leer por no-.NET Framework apli-
Los cationes.  Es más probable que el SoapFormatter exitosamente atraviesen los
firewalls de BinaryFormatter.
En resumen, usted debe elegir BinaryFormatter sólo cuando sepa que todos los clientes los datos
serializados de apertura serán las aplicaciones de .NET Framework. Por lo tanto, si usted está
escribiendo objetos en el disco para ser leído posteriormente por su aplicación,  es per- fect
BinaryFormatter. Utilice SoapFormatter en otras aplicaciones pueden leer sus datos serializados y
cuando se envían datos a través de una red.  También SoapFormatter funciona de forma fiable en
situaciones donde usted podría elegir BinaryFormatter, pero el objeto serializado puede consumir
de tres a cuatro veces más espacio.
Mientras SoapFormatter se basa en XML, está destinado principalmente a ser utilizado por
servicios Web SOAP. Si su objetivo es almacenar objetos en forma abierta, basada en estándares,
documento que puede ser consumido por las aplicaciones que se ejecutan en otras plataformas, la
forma más flexible para realizar la serialización es elegir la serialización XML. Lección 2 En este
capítulo discute la serialización XML en longitud.

Cómo utilizar SoapFormatter
Utilizar SoapFormatter, agregar una referencia a System.Runtime.Serialization.formateadores
.Soap.dll general a su proyecto. (A diferencia de BinaryFormatter, no se incluyen por defecto.) a
continuación, se escribe el código exactamente como lo haría para utilizar BinaryFormatter, pero
sustituyendo la  clase SoapFormatter para la  clase BinaryFormatter.
Al escribir código para BinaryFormatter  SoapFormatter y es muy similar, la serie- izado de
datos es muy diferente. El ejemplo siguiente es un objeto serializado de tres miembros
con SoapFormatter que ha sido ligeramente editada:
<SOAP-ENV:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SOAP-
ENV:Body>
<A1:ShoppingCartItem id="ref-1">
<productId>100</productId>
<precio>10,25</precio>
<cantidad>2</cantidad>
</a1:ShoppingCartItem>
</SOAP-
ENV:Body>
</SOAP-
ENV:Envelope>

Nota   .NET 2.0


  No admite la serialización SoapFormatter compatibilidad entre versiones de
.NET Marco. La serialización entre las versiones 1.1 y 2.0 tipos en el marco a
veces fracasa.   ¿BinaryFormatter proporciona compatibilidad entre versiones.

Cómo controlar la serialización de jabón


La serialización binaria se destina para uso exclusivo por parte de aplicaciones basadas en
.NET Framework. Por lo tanto, rara vez hay una necesidad de modificar el formato estándar.
Sin embargo, la serialización de jabón está destinado a ser leído por una gran variedad de
plataformas. Además, usted puede ser que necesite para serializar un objeto para satisfacer
necesidades específicas, tales como  jabón predefinidos nombres de elemento y atributo.
Puede controlar el formato de un documento serializada SOAP mediante los atributos que
aparecen en la Tabla 5-1.
Tabla 5-1  atributos de serialización XML

Atributo Se aplica a Especifica


SoapAttribute Campo, propiedad pública, El miembro de clase será
parámetro o valor de serializada como un
retorno atributo XML.
SoapElement Campo, propiedad pública, La clase será serializada
parámetro o valor de como un elemento XML.
retorno
SoapEnum Un campo público que El nombre de elemento
es un identificador de de un miembro de la
enumeración enumeración.
Tabla 5-1  atributos de serialización XML

Atributo Se aplica a Especifica


SoapIgnore Propiedades públicas y campos La propiedad o el campo
debería
Ser ignorados cuando el
contienen-
Clase de ing está serializado.
SoapInclude Declaración de clase derivada El tipo debe ser incluido
del público
Nes y métodos públicos para Cuando la generación de
Descripción de servicios Web esquemas
(Para ser reconocido cuando
Language (WSDL) Serializados).
Documentos

Atributos de serialización SOAP funcionan de manera similar a los atributos de serialización


XML. Para obtener más información acerca de los atributos de serialización XML, consulte la
sección "Cómo controlar la serialización XML" en la Lección 2 de este capítulo.

Directrices para la serialización


Tenga en cuenta las siguientes pautas al utilizar la serialización:
■   En caso de duda, marcar una clase como serializable. Incluso si no necesita
serializar ahora, usted podría necesitar la serialización posterior. O podría necesitar
otro desarrollador para serializar una clase derivada.
■   Marca calculado o miembros temporales como NonSerialized. Por ejemplo, si la
vía el ID del hilo actual en una variable miembro, el ID del hilo, es probable que
no sea válida en la deserialización. Por lo tanto, no se deben almacenar.
■   Utilice SoapFormatter cuando necesita portabilidad.
Utilice BinaryFormatter para mayor eficacia.

Laboratorio: Serializar y deserializar objetos.


En este laboratorio, modificar una clase para habilitar la serialización eficiente y, a continuación,
actualizar una aplicación para realizar la serialización y deserialización de esa clase. Si se produce
un problema de completar un ejercicio, los proyectos terminados están disponibles en los com-
panion CD en la carpeta de código.
    Ejercicio 1: Crear una clase Serializable
En este ejercicio, va a modificar una clase personalizada para que los desarrolladores puedan
almacenar fácilmente en el disco para su posterior recuperación o transferirlo a través de una red a
otra aplicación .NET Framework.
1.  Copiar el capítulo05\LECCION1-Serialize-People carpeta desde el CD a tu disco
duro y abrir la versión en C# o Visual Basic versión del proyecto Serialize-People.
2.  Examine la  clase Person. ¿Qué cambios necesita hacer para que la persona
La clase sea serializable?
Debe agregar el  atributo Serializable.
3.  Añadir el  espacio de nombres System.Runtime.Serialization  a la clase.
4.  Agregar el  atributo Serializable a la persona a la clase y, a continuación, generar
el proyecto para asegurarse de que se compila correctamente.
    Ejercicio 2: serializar un objeto
En este ejercicio, va a escribir código para almacenar un objeto en el disco mediante el método más
eficiente posible.
1.  Abra el proyecto Serialize-People se modifica en el ejercicio 1.
2.  Agregue el System.IO, System.Runtime.Serialization y System.Runtime.Serialization
 Espacios de nombres de .los formateadores.binario para el archivo que contiene el
principal.
3.  Agregue código al  método Serialize sp para serializar el objeto a un archivo en el
directorio actual llamado Person.dat. El código podría tener el siguiente aspecto:

// C#
Private static void serialize(Persona sp)
{
// Crear un archivo para guardar los datos en
FileStream fs = new FileStream("Persona.dat", FileMode.Create);

// Crear un objeto BinaryFormatter para realizar la serialización


BinaryFormatter bf = new BinaryFormatter();

// Usar el objeto BinaryFormatter para serializar los datos para el archivo bf.Serialize(fs, sp);

// Cerrar el archivo fs.Close();


}

4.  Genere el proyecto y resolver cualquier error.


5.  Abra un símbolo del sistema en el directorio Build y, a continuación, probar la
aplicación ejecutando  el siguiente comando:
Tony Serialize-People 1923 4 22

6.  Examine los datos serializados abriendo el archivo producido su aplicación para


comprobar que el nombre que ha introducido con éxito fue capturado. La fecha y la
edad son la información contenida en los datos serializados por igual; sin embargo, son
menos fáciles de interpretar en el Bloc de notas.
    Ejercicio 3: deserializar un objeto
En este ejercicio, usted debe leer un objeto desde el disco que ha sido serializado
utilizando BinaryFormatter.
1.  Abra el proyecto Serialize-People modificó en los ejercicios 1 y 2.
2.  Agregue código al deserializar el método en el programa principal para deserializar
el  objeto dsp desde un archivo en el directorio predeterminado
denominado Persona.dat. El código podría tener el siguiente aspecto:
// C#
Persona estática privada deserialize()
{
Persona dsp = new persona();
// Abrir un archivo para leer los datos desde
FileStream fs = new FileStream("Persona.dat", FileMode.Open);

// Crear un objeto BinaryFormatter para realizar la deserialización


BinaryFormatter bf = new BinaryFormatter();

// Usar el objeto BinaryFormatter para deserializar los datos desde el archivo dsp =
(Persona)bf.deserializar(fs);

// Cerrar el archivo
fs.Close();

Volver dsp;
}

3.  Genere el proyecto y resolver cualquier error.


4.  Abra un símbolo del sistema en el directorio Build y, a continuación,  ejecute el
siguiente comando sin parámetros de línea de comandos:
Serialize-People

Tenga en cuenta que el  comando Serialize-People muestra el nombre, fecha de


nacimiento y edad de la persona objeto serializado previamente.
    Ejercicio 4: Optimizar una clase para la
deserialización
En este ejercicio, modificar una clase para mejorar la eficiencia de la serialización.
1.  Abra el proyecto Serialize-People modificó en ejercicios 1, 2 y 3.
2.  Modifique la  clase Person para prevenir la edad estados de ser serializado. Para
ello, agregue el  atributo NonSerialized para los estados, como se muestra en el
código siguiente:

// C#
[NonSerialized] public int edad;

3.  Crear y ejecutar el proyecto sin parámetros de línea de comandos. Tenga en cuenta


que el  comando Serialize-People muestra el nombre y la fecha de nacimiento de
la persona objeto serializado previamente. Sin embargo, la edad se muestra como
cero.
4.  Modifique la  clase Person para implementar la  interfaz IDeserializationCallback,
como muestra el fragmento de código siguiente:

// C#
Serialize_namespace personas
{
[Serializable]
Clase de persona : IDeserializationCallback

5.  Agregar el  método OnDeserialization IDeserializationCallback.a la  clase Person.


El código podría tener el siguiente aspecto:
' VB
Sub_IDeserializationCallback OnDeserialization(ByVal sender As Object) _ implementa
IDeserializationCallback.OnDeserialization
' Después de la deserialización, calcular la edad
CalculateAge() End
Sub

// C#
Void IDeserializationCallback.OnDeserialization(object sender)
{
// Después de la deserialización, calcular la edad
CalculateAge();
}

6.  Crear y ejecutar el proyecto sin parámetros de línea de comandos. Tenga en cuenta


que el  comando Serialize-People muestra el nombre, fecha de nacimiento y edad
de la previ- amente persona objeto serializado. La edad se muestra correctamente
este tiempo porque se calcula inmediatamente después de deserialización.

Resumen de la lección
■   La serialización es el proceso de convertir la información en una secuencia de
bytes que pueden ser almacenados o transferidos.
■   para serializar un objeto, primero debe crear un objeto Stream. A continuación,
cree un  objeto BinaryFormatter y llamar al  método.Serialize BinaryFormatter.
Para deserializar un objeto, siga los mismos pasos pero llame al  método
BinaryFormatter.deserializar.
■   Para crear una clase que se puede serializar, agregue el  atributo Serializable.
También puede utilizar atributos para desactivar la serialización de miembros
específicos.
■   SoapFormatter proporciona una menos eficientes pero más interoperables,
alternativa a la
 Clase BinaryFormatter.
■   utilizar SoapFormatter,siga el mismo proceso que para BinaryFormatter, pero
usar System.Runtime.Serialization.formateadores.Soap. clase SoapFormatter.
■   Puede controlar la   serialización SoapFormatter mediante atributos para
especificar los nombres de elementos serializados y especificar si un miembro se
serializa como un elemento o un atributo.
■   Es una buena práctica para hacer todas las clases serializables incluso si no
inmediata- ately requieren la serialización. Debe deshabilitar la serialización de
calculada y miembros temporales.

Examen de la lección
Puede utilizar las siguientes preguntas para poner a prueba su conocimiento de la información en la
lección 1, "Serialización de Objetos." Las preguntas también están disponibles en el CD
complementario si prefiere revisarlos en forma electrónica.

Nota   Respuestas
Las respuestas a estas preguntas y explicaciones de por qué cada opción de
respuesta es correcta o no se encuentran en la sección "Respuestas" al final del
libro.

1.  Cuáles  de las siguientes son necesarios para serializar un objeto? (Seleccione


todos los que correspondan).
A.  Una instancia de BinaryFormatter
B.   Permisos de archivo para crear archivos temporales
C.   Servicios de Internet Information Server
D.   Un objeto Stream
2.  Cuál de los siguientes atributos debe agregar a una clase para poder serializarse?
A.   B.
ISerializable
Serializable C.
SoapInclude
D.   OnDeserialization
3.  Cuál de los siguientes atributos se debe agregar a un miembro para impedir que se
serializaron BinaryFormatter?
A.  NonSerialized
B.   Serializable
C.   SerializationException
D.   SoapIgnore
4.  Cuál de las siguientes interfaces debe implementar para permitirle ejecutar un método
después de una instancia de la clase se serializa?
A.  IFormatter
B.   ISerializable
C.   IDeserializationCallback
D.   IObjectReference
Lección 2: serialización XML
XML es un sistema normalizado de documentos basados en texto, formato para almacenar la
información legible de la aplicación. Al igual que HTML proporciona un estándar basado en
texto para el formato de derechos- documentos legibles, XML proporciona un estándar que
puede ser fácilmente procesado por com- puters. XML puede ser utilizado para almacenar
cualquier tipo de datos, incluidos los documentos (la última versión de Microsoft Office
almacena documentos utilizando XML), imágenes, música, archivos binarios, y la
información de la base de datos.
El .NET Framework incluye varias bibliotecas para la lectura y escritura de archivos XML,
incluido el  espacio de nombres System.Xml.Serialization.
System.Xml.Serialization proporciona métodos para convertir objetos, incluyendo aquellas
basadas en clases personalizadas, a y desde archivos XML. Con la serialización XML, puede
escribir casi cualquier objeto en un archivo de texto para su posterior recuperación con sólo
unas pocas líneas de código. Asimismo, puede utilizar la serialización XML para transmitir
objetos entre equipos a través de servicios Web, incluso si el equipo remoto no está
utilizando .NET Framework.

Después de esta lección, será capaz de:


■   serializar y deserializar objetos utilizando la serialización XML.
■   Personalizar el comportamiento de la serialización de clases
personalizadas para satisfacer necesidades específicas, tales como un
esquema XML.
■   serializar un objeto dataset.
Lección Tiempo estimado: 40 minutos

¿Por qué utilizar la serialización XML?


Utilizar la serialización XML cuando necesite intercambiar un objeto con una aplicación que
puede no estar basado en .NET Framework, y no necesita serializar cualquier miembros
privados. La serialización XML proporciona las siguientes ventajas sobre serialización
estándar:
■   mayor interoperabilidad  XML  es un archivo basado en texto estándar,
y todos los modernos entornos de desa- rrollo son bibliotecas para procesar
archivos XML. Por lo tanto, un objeto que se serializaron utilizando XML
puede ser fácilmente procesado por una aplicación
Escrito para un sistema operativo diferente en un entorno de desarrollo
distinto.
■  Más amigable administrador  los objetos serializados utilizando XML
pueden verse y editarse con cualquier editor de texto, incluido el Bloc de
notas. Si va a almacenar objetos en archivos, esto da a los administradores la
oportunidad de ver y editar el archivo XML. Este
Puede ser útil para personalizar la aplicación, solución de problemas, y desarrollar
nuevas aplicaciones que interactúan con la aplicación existente.
■   Mejor compatibilidad hacia adelante  los objetos serializados utilizando
XML son autodescriptivos y fácil de procesar. Por lo tanto, cuando llegue el
momento de sustituir su aplica- ción, la nueva aplicación será más fácil procesar tu
serializa
Si utiliza objetos XML.
Además, debe usar la serialización XML en cualquier momento si necesita ajustarse a un esquema
XML- pecífico o controlan la forma en que un objeto está codificado. No se puede utilizar XML
para cada situación, sin embargo. Específicamente, la serialización XML tiene las siguientes
limitaciones:
■ la   serialización XML sólo puede serializar datos públicos. No se puede serializar
datos privados.
■   No se puede serializar gráficos de objetos; puede utilizar la serialización XML sólo
en objetos.

Cómo usar XML para serializar un objeto


En un nivel alto, los pasos para serializar un objeto son como sigue:
1.  Crear una secuencia, TextWriter, o XmlWriter para albergar la salida serializados.
2.  Crear un  objeto XmlSerializer (en el  espacio de nombres
System.Xml.Serialization)  pasándole el tipo de objeto va a serializar.
3.  Llamar al   método.Serialize XmlSerializer para serializar el objeto y la salida de
los resultados de la secuencia.
En el nivel de código, estos pasos son similares a los de serialización estándar. El siguiente pro-
solicitud única que requiere el uso de System.IO y  System.Xml.Serialization namespaces
demuestra la simplicidad:
// C#
// Crear un archivo para guardar los datos en
FileStream fs = new FileStream("SerializedDate.XML", FileMode.Create);
// Crear un objeto XmlSerializer para realizar la serialización
XmlSerializer xs = nuevo XmlSerializer(typeof(DateTime);

// Usar el objeto XmlSerializer para serializar los datos al archivo xs.Serialize(fs,


System.DateTime.Now);

// Cerrar el archivo
fs.Close();

Cuando se ejecuta, la aplicación genera un archivo de texto similar a la siguiente:


<?xml version="1.0" ?>
<dateTime>2005-12-05T16:28:11.0533408-05:00</dateTime>
En comparación con el  objeto DateTime serializado creó en la lección 1, la serialización XML
produce una muy legible, fácilmente archivo editado.

Cómo usar XML para deserializar un objeto


Para deserializar un objeto, siga estos pasos:
1.  Crear una secuencia, TextReader, o el  objeto XmlReader para leer la entrada
serializado.
2.  Crear un  objeto XmlSerializer (en el  espacio de nombres
System.Xml.Serialization) pasándole el tipo de objeto que plan para deserializar.
3.  Llamar al  método Deserialize.XmlSerializer para deserializar el objeto, y lo echó
al tipo correcto.
En el siguiente ejemplo de código se deserializa un archivo XML que contiene un  objeto
DateTime y muestra ese objeto, el día de la semana y la hora.
// C#
// Abrir un archivo para leer los datos desde
FileStream fs = new FileStream("SerializedDate.XML", FileMode.Open);

// Crear un objeto XmlSerializer para realizar la deserialización


XmlSerializer xs = nuevo XmlSerializer(typeof(DateTime);

// Usar el objeto XmlSerializer para deserializar los datos desde el archivo


DateTime = previousTime (DateTime)xs.deserializar(fs);

// Cerrar el archivo
fs.Close();

// Mostrar el tiempo deserializado


Console.WriteLine("Día: " + previousTime.DayOfWeek + ", tiempo: " +
previousTime.TimeOfDay.ToString());

Cómo crear clases que se pueden serializar utilizando la


serialización XML
Para crear una clase que se pueda serializar mediante serialización XML, debe realizar las
siguientes tareas:
■   especificar la clase como público.
■   especificar todos los miembros que deben ser serializados como public.
■   Crear un constructor sin parámetros.
A diferencia de clases procesadas con serialización estándar, las clases no tienen el   atributo
Serializable para ser procesados con la serialización XML. Si hay miembros private o protected,
serán omitidos durante la serialización.
Cómo controlar la serialización XML
Si serializar una clase que cumpla con los requisitos para la serialización XML, pero no tiene
atributos de serialización XML, el motor de tiempo de ejecución utiliza la configuración
predeterminada que satisfacen las necesidades de muchas personas. Los nombres de los elementos
XML se basan en la clase y los nombres de los miembros, y cada miembro se serializa como un
elemento XML independiente. Por ejem- plo, considere la siguiente clase simple:
// C#
Public Class ShoppingCartItem
{
ProductId Int32 público; público
precio decimal; public Int32
cantidad;
Decimal público total.

ShoppingCartItem público()
{
}
}

Serializar una instancia de esta clase con valores de ejemplo crea el siguiente XML (que ha sido
ligeramente simplificado para mejorar la legibilidad):
<?xml version="1.0" ?>
<ShoppingCartItem>
<productId>100</productId>
<precio>10,25</precio>
<total>20,50</total>
</ShoppingCartItem>

Si se define el esquema XML, esto podría ser suficiente. Sin embargo, si usted necesita para crear
documentos XML que se ajustan a normas específicas, usted puede ser que necesite para controlar
cómo está estructurada la serialización. Puede hacerlo utilizando los atributos que aparecen en la
Tabla 5-2.
Tabla 5-2  atributos de serialización XML

Atributo Se aplica a Especifica


XmlAnyAttribute Campo, propiedad pública Al deserializar el
Parámetro o valor de retorno Array será rellenado con
Que devuelve una matriz de  Objetos que XmlAttribute
 Objetos XmlAttribute Representan todos los atributos
XML
Desconocida para el esquema.
XmlAnyElement Campo, propiedad pública Cuando deserializar,
Parámetro o valor de retorno La matriz se rellena con
Que devuelve una matriz de XmlElement objetos que
 Objetos XmlElement Representan todos los
elementos XML
Desconocida para el esquema.

Tabla 5-2  atributos de serialización XML

Atributo Se aplica a Especifica


XmlArray Campo, propiedad pública Los miembros de la matriz.
Parámetro o valor de retorno Se generarán como mem-
Que devuelve una matriz de Bers de una matriz de XML.
Objetos complejos
XmlArrayItem Campo, propiedad pública Los tipos derivados que puede
Parámetro o valor de retorno Puede insertarse en una matriz.
Que devuelve una matriz de Generalmente aplicado en
Objetos complejos conjunc-
Ción con XmlArrayAttribute.
XmlAttribute Campo, propiedad pública Los estados serán
Parámetro o valor de retorno Serializada como un XML
Atributo.
XmlChoice- Campo, propiedad pública Los estados pueden seguir
Identificador Parámetro o valor de retorno Eliminar la ambigüedad
mediante un
Enumeración.
XmlElement Campo, propiedad pública El campo o propiedad
Parámetro o valor de retorno Se serializa como un XML
Elemento.
XmlEnum Ese es un campo público El nombre del elemento de la
Identificador de enumeración enu-
Miembro meration.
XmlIgnore Propiedades públicas La propiedad o el campo
Y los campos debería
Ser ignorada cuando los con-
Tención class está serializado.
Esto funciona de forma similar
aLa NonSerialized standard
Atributo de serialización.
XmlInclude Declaración de clase derivada La clase debe ser incluido
del
Nespúblico
y valores de retorno de Cuando la generación de
Métodos públicos para Web esquemas
(Para ser reconocido cuando
Descripción de servicios Serializados).
Language (WSDL)
Documentos
Tabla 5-2  atributos de serialización XML

Atributo Se aplica a Especifica


XmlRoot Declaraciones de clase pública Controla la serialización XML
El atributo de destino como un
El elemento raíz del XML.
Utilice
Atributoelpara especificar más
El espacio de nombres y el
elemento
Nombre.
XmlText Propiedades públicas y La propiedad o el campo
campos debería
Se serializa como texto XML.
XmlType Declaraciones de clase pública El nombre y el espacio de
nombres
El de
tipo XML.

Puede utilizar estos atributos  para hacer una clase serializada se ajustan a los requisitos
XML específico. Por ejemplo, considere los atributos requeridos para realizar las
siguientes tres cambios en el documento XML serializado:
■   Cambiar el  nombre al elemento ShoppingCartItem CartItem.
■   Hacer  un atributo de productId CartItem, en lugar de un elemento separado.

Nota   Los atributos y elementos


XML
En XML, un elemento puede contener otros elementos, al igual que un objeto
puede tener miembros. Ele- mentos también pueden tener atributos que
describen el elemento, simplemente como una propiedad puede describir un
objeto de .NET Framework. Al examinar un documento XML, puede reconocer
atributos porque aparecen dentro de un elemento <> soportes. Examinar las
diferencias entre los dos ejemplos en esta sección para comprender la distinción.

■   No incluyen el total del documento serializado.


Para realizar estos cambios, modificar la clase con atributos, como se muestra en la figura:
// C#
[XmlRoot ("CartItem")]
Public Class ShoppingCartItem
{
[XmlAttribute] público productId Int32.
Precio decimal pública;
Public Int32 cantidad;
[XmlIgnore] decimal público total.

ShoppingCartItem público()
{
}
}

Esto daría como resultado el siguiente archivo XML, que cumple con los requisitos especificados:
<?xml version="1.0" ?>
<CartItem productId="100">
<precio>10,25</precio>
<cantidad>2</cantidad>
</CartItem>

Aunque los atributos le permitirá satisfacer la mayoría de requisitos de serialización XML, puede
tomar el control completo sobre la serialización XML implementando la  interfaz IXmlSerializ-
poder en su clase. Por ejemplo, puede separar los datos en bytes en lugar de búfer de grandes
conjuntos de datos, y también evitar la inflación que se produce cuando los datos se codifican
utilizando la codificación Base64. Controlar la serialización, implementar el ReadXml  WriteXml
y métodos para el control de las    clases XmlReader y XmlWriter utilizado para leer y escribir los
datos XML.

Cómo se ajustan a un esquema XML.


Normalmente, cuando dos aplicaciones diferentes que van a intercambiar archivos XML, la desa-
opers trabajan juntos para crear un archivo de esquema XML. Un esquema XML define la estruc-
tura de un documento XML. Muchos tipos de esquema XML ya existentes y, siempre que sea
posible, debe aprovechar un esquema XML existente.

Más info  esquemas XML

Para obtener más información acerca de los esquemas XML,


visite http://www.w3.org/XML/Schem.

Si usted tiene un esquema XML, puede ejecutar la herramienta de definición de esquema XML
(XSD.exe) para producir un conjunto de clases con establecimiento inflexible de tipos en el
esquema y se anota con atributos. Cuando una instancia de esa clase es serializado, el XML
generado se adhiere al esquema XML. Esta es una alternativa más simple con el uso de otras clases
en el marco de .NET, como las    clases XmlReader y XmlWriter, analizar y escribir una secuencia
XML.
Para generar una clase basada en un esquema, siga estos pasos:
1.  Crear o descargar el  archivo .xsd esquema XML en su equipo.
2.  Abra un símbolo del sistema de Visual Studio 2005.
3.  Desde el símbolo del sistema de Visual Studio 2005, ejecutar el esquema
Xsd.exe.xsd /Clases/Language:[CS | VB]. Por ejemplo, para crear una nueva clase
basada en un archivo de esquema denominado C:\library\schema.xsd, debe
ejecutar el siguiente comando:
// C#
Xsd C:\library\schema.xsd /Clases /language:CS

4.  Abra el archivo recién creado (denominado esquema Esquema .cs o.vb) y


agregar la clase a su aplicación.
Cuando se serializa la clase recién creada, se adaptará automáticamente el esquema XML.
Esto hace que sea sencillo para crear aplicaciones que interactúan con los servicios Web
basados en estándares.

Más info  conforme al esquema XML


Para obtener más información acerca de conforme al esquema XML, leer "XML
Schema Part 0: Primer" http://www.w3.org/TR/2001/REC-xmlschema-0-
20010502/  y "mediante el esquema y la serialización para aprovechar la
lógica empresarial" por Eric Schmidt
en http://msdn.microsoft.com/library/en-us/dnexxml/html/  xml04162001.as
p.

Cómo serializar un objeto DataSet


Además de serializar una instancia de una clase pública, una instancia de un  objeto DataSet
puede también ser serializado, como se muestra en el ejemplo siguiente:
// C#
Private void SerializeDataSet(string filename){ XmlSerializer ser = nuevo
XmlSerializer(typeof(DataSet));

// Crea un DataSet, se agrega una tabla, columna y 10 filas. DataSet ds = new


DataSet("myDataSet");
DataTable t = new DataTable("tabla1"); c =
DataColumn nueva DataColumn("cosa");
t.Columns.Add(C);
Ds.Tables.Add(t);
DataRow r;
Para(int i = 0; i<10;i++){
R=
t.NewRow();
R[0] = "Cosa " + i;
T.Rows.Add(r).
}
= nuevo escritor TextWriter StreamWriter(Filename);
Ser.Serialize(escritor, ds);
Writer.Close();
}

De forma similar, se puede serializar matrices, colecciones y las instancias  de una    clase


XmlNode o XmlElement. Aunque esto es útil, no ofrecen el mismo nivel de control que
tendría si los datos se almacenan en clases personalizadas. Como alternativa, puede utilizar
el DataSet.WriteXml, DataSet.ReadXML, y DataSet.GetXml métodos.

Laboratorio: Uso de la serialización XML


En esta pr ctica, usted podrá actualizar una aplicación que utiliza actualmente la  serie
BinaryFormatter- lización para utilizar la serialización XML. Si se produce un problema
de  completar un ejercicio, los proyectos terminados están disponibles en el CD en la carpeta
de código.
Ejercicio: Sustitución de la serialización binaria con la serialización XML
En este ejercicio, que actualiza un proyecto de apoyo a almacenar datos mediante estándares
abiertos- basa la serialización XML.
1.  Copiar el capítulo05\lección2-Serialize-People carpeta desde el CD a tu disco
duro y abrir la versión en C# o Visual Basic versión del proyecto Serialize-
People.
2.  Añadir el  espacio de nombres System.Xml.Serialization  al programa
principal.
3.  Reescribir el   método de serialización para utilizar la serialización XML en
lugar de serialización binaria. El nombre del archivo temporal persona.xml. El
código podría tener el siguiente aspecto:
// C#
Private static void serialize(Persona sp)
{
// Crear un archivo para guardar los datos en
FileStream fs = new FileStream("Persona.XML", FileMode.Create);

// Crear un objeto XmlSerializer para realizar la serialización


XmlSerializer xs = nuevo XmlSerializer(typeof(Persona);

// Usar el objeto XmlSerializer para serializar los datos al archivo xs.Serialize(fs, sp);

// Cerrar el
archivo
fs.Close();
}

4.  Reescribir la deserialización método usar XML en lugar de datos binarios de


deserialización deserialización. El código podría tener el siguiente aspecto:
// C#
Persona estática privada deserialize()
{
Persona dsp = new persona();
// Crear un archivo para guardar los datos en
FileStream fs = new FileStream("Persona.XML", FileMode.Open);
// Crear un objeto XmlSerializer para realizar la deserialización
XmlSerializer xs = nuevo XmlSerializer(typeof(Persona);

// Usar el objeto XmlSerializer para deserializar los datos para el archivo dsp =
(Persona)xs.deserializar(fs);

// Cerrar el archivo
fs.Close();
Volver dsp;
}

5.  Genere el proyecto y resolver cualquier error.


6.  Abra un símbolo del sistema en el directorio Build y, a continuación,  ejecute el
comando siguiente:
Tony Serialize-People 1923 4 22

¿Qué mensaje de excepción ¿recibe, y por qué?


Verá el mensaje "Invalid parámetros. Serialize_People.persona es inaccesible
debido a su nivel de protección. Sólo pueden procesarse los tipos públicos ." El
error se produce porque la persona de clase no está marcado como público.
7.  Editar la clase Person y marcarlo como público. A continuación, vuelva a generar
el proyecto y ejecutar el siguiente comando:
Tony Serialize-People 1923 4 22

8.  Examine los datos serializados para verificar que la información que haya
proporcionado en la línea de comandos se ha capturado. ¿Por qué la edad aparecen
en la serie- izado archivo aunque la edad NonSerialized miembro tiene el atributo?
La NonSerialized atributo se aplica a la serialización binaria, pero no afecta a
La serialización
XML.
9.  Ahora ejecute el comando sin parámetros para comprobar que la deserialización
funciona correctamente.
Resumen de la lección
■   la serialización XML proporciona  la interoperabilidad para comunicarse con
diferentes plataformas y flexibilidad para ajustarse a un esquema XML.
■     No se puede utilizar la serialización XML para serializar los datos privados o
gráficos de objetos.
■   para
serializar un objeto, primero debe crear una
secuencia, TextWriter XmlWriter , o. A continuación, cree un  objeto
XmlSerializer y llamar al  método.Serialize XmlSerializer. Para deserializar
un objeto, siga los mismos pasos pero llame el XmlSerializer.deserializar
el método.
■   Paracrear una clase que se puede serializar, especificar la clase y todos los
miembros como públi- ca, y crear un constructor sin parámetros.
■   Puede controlar la serialización XML mediante atributos. Los atributos
pueden cambiar los nombres de los elementos, serializar miembros como
atributos en lugar de elementos, y excluir a los miembros de la serialización.
■   Utilicela herramienta Xsd.exe para crear una clase que se adaptará
automáticamente a un XML Esquema cuando se serializa.
■   Datasets,
matrices, colecciones y las instancias de una    clase XmlNode o
XmlElement pueden todos ser serializadas con XmlSerializer.

Examen de la lección
Puede utilizar las siguientes preguntas para poner a prueba tus conocimientos de la
información Les- hijo 2, "serialización XML." Las preguntas también están disponibles en el
CD complementario si prefiere revisarlos en forma electrónica.

Nota   Respuestas
Las respuestas a estas preguntas y explicaciones de por qué cada opción de
respuesta es correcta o no se encuentran en la sección "Respuestas" al final
del libro.

1.  Cuál de los siguientes son los requisitos para una clase que se va a serializar
con XML
La serialización? (Seleccione todos los que correspondan).
A.  La clase debe ser pública.
B.   La clase debe ser privado.
C.   La clase debe tener un constructor sin parámetros.
D.   La clase debe tener un constructor que acepta un  parámetro
SerializationInfo.
2.  Cuál de los siguientes atributos que usaría para causar un miembro a ser serie- izado
como un atributo, en lugar de un elemento?
A.  XmlAnyAttribute
B.   XMLType
C.   XMLElement
D.   XMLAttribute
3.  La herramienta que se debe utilizar para ayudarle a crear una clase que, a la hora de
serializar, produciría un documento XML que se ajustaban a un esquema XML?
A.  Xsd.exe.
B. Xdcmake.exe C. X
Padsi90.exe D.
Xcacls.exe.
4.  Cuál de los siguientes atributos se debe agregar a un miembro para impedir que se
serializaron serialización XML?
A.  XMLType
B.   XMLIgnore C.
XMLElement D.
XMLAttribute
Lección 3: Serialización personalizada
Serialización personalizada es el proceso de controlar la serialización y deserialización de un
tipo. Al controlar la serialización, es posible garantizar la serialización compatibil- lidad, que
es la capacidad de serializar y deserializar entre versiones de un tipo sin romper la
funcionalidad básica del tipo. Por ejemplo, en la primera versión de un tipo, es posible que
sólo haya dos campos. En la próxima versión de un tipo, varios campos son añadidos. Pero la
segunda versión de la aplicación debe ser capaz para serializar y deserial- ize ambos tipos.
Esta lección describe cómo controlar la serialización implementando sus propias clases de
serialización.

Después de esta lección, será capaz de:


■   Implementar la   interfaz ISerializable para tomar control sobre la forma en que
una clase está serializado.
■   Responder a eventos de serialización para ejecutar código en diferentes
etapas del proceso de serialización.
■   Escribir código que ajusta la serialización y deserialización de acuerdo al
contexto.
■   describir el papel de IFormatter.
Lección Tiempo estimado: 30 minutos

Cómo implementar la serialización personalizada


Serialización en .NET Framework es muy flexible y puede ser personalizado para satisfacer la
mayoría de necesidades de desarrollo. En algunas circunstancias, es posible que necesite un
control completo sobre el proceso de serialización.
Puede anular la serialización integrada en .NET Framework implementando la  interfaz
ISerializable y aplicando el  atributo Serializable a la clase. Esto es particularmente útil en los
casos en que el valor de una variable miembro es válido después de dese- rialization, pero
necesita proporcionar la variable con un valor para reconstruir el estado completo del objeto.
Además, usted no debe utilizar la serialización por defecto en una clase que está marcada con
el  atributo Serializable y declarativa o imperativas de seguridad en el nivel de clase o a sus
constructores. En su lugar, estas clases siempre debe implementar la  interfaz ISerializable.
Aplicación ISerializable implica implementar el  método GetObjectData y un constructor
especial que se utiliza cuando se deserializa el objeto. El tiempo de ejecución
llama GetObjectData durante la serialización y el constructor de serialización durante La
deserialización. El compilador avisará si usted se olvida de aplicar GetObjectData, pero si
usted se olvida de aplicar el constructor especial, usted no notará un problema hasta el tiempo
de ejecución cuando usted recibe una excepción de serialización.
Cuando el tiempo de ejecución llama GetObjectData durante la serialización, usted es
responsable de rellenar SerializationInfo objeto proporcionado con la llamada al método.
Simplemente añada las variables a serializar como pares nombre/valor utilizando el  método
AddValue, que inter- nalmente crea SerializationEntry estructuras para almacenar la
información. Cualquier texto puede ser utilizado como el nombre. Usted tiene la libertad de
decidir qué variables miembro se agregan a la SerializationInfo objeto, siempre que se
serializa los datos suficientes para restaurar el objeto durante la deserialización. Cuando el
tiempo de ejecución llama a su constructor de serialización, simplemente recuperar los valores
de las variables de SerializationInfo utilizando los nombres utilizados durante la serialización.
En el siguiente ejemplo de código, que usa el System.Runtime.Serialization y System
Espacios de nombres de .Security.Permisos, muestra cómo
implementar ISerializable serializa- ción , el constructor y el  método GetObjectData:
// C# [serializable]
Clase : ShoppingCartItem ISerializable
{
ProductId Int32 público; público
precio decimal; public Int32
cantidad; [NonSerialized]
Decimal público total.

// La serialización estándar no constructor


ShoppingCartItem pública(int _productID, decimal, int __precio cantidad)
{
ProductId = _productID;
Precio = _precio; cantidad =
_cantidad; total = precio * cantidad;
}

// El siguiente constructor es de deserialización ShoppingCartItem


protegido(SerializationInfo, info.
Contexto StreamingContext)
{
ProductId = info.GetInt32("Product ID"); precio =
info.GetDecimal("Precio"); cantidad =
info.GetInt32("Cantidad"); total = precio * cantidad;
}

// El siguiente método es llamado durante la serialización


[SecurityPermissionAttribute(SecurityAction.La demanda,
SerializationFormatter=true)]
Virtual public void GetObjectData(SerializationInfo info, StreamingContext
contexto)
{
Info.AddValue("Product ID", Idproducto);
info.AddValue("Precio", precio);
info.AddValue("Cantidad", cantidad);
}

Anulación pública string ToString()


{
Volver productId + " " + precio + " x " + cantidad + " " + = total;
}
}

En este ejemplo, SerializationInfo hace mucho del trabajo de serialización y deserial- ización.


La construcción de un SerializationInfo objeto requiere un objeto cuyo tipo implementa
la  interfaz IFormatterConverter.    Siempre SoapFormatter BinaryFormatter y construir una
instancia de System.Runtime.Serialization.FormatterConverter tipo, sin darle la oportunidad
de usar un diferente  tipo IFormatterConverter. FormatterConverter incluye métodos para
convertir valores entre tipos básicos, como el de convertir un número decimal en una doble, o
un entero firmado a un entero sin signo.

Importante  reducción de los riesgos de seguridad


mediante el uso de la validación de datos
Se debe realizar la validación de datos en el constructor de serialización y
arrojar una SerializationException  si se proporcionan datos no válidos. Se corre
el riesgo de que un atacante podría usar tu clase, sino proporcionar información
falsa- ización de serie en un intento de explotar una debilidad. Usted debe
asumir que las llamadas realizadas a su constructor de serialización son iniciados
por un atacante, y permitir la construcción sólo si todos los datos
proporcionada es válida y realista. Para obtener más información acerca de la
seguridad del código, consulte el capítulo 12, "el usuario y seguridad de datos".

Responder a eventos de serialización


El .NET Framework 2.0 admite la serialización binaria de eventos cuando se utiliza el código
binario- clase Formatter. Estos eventos llame a métodos en la clase cuando la serialización y
deserialización. Hay cuatro eventos de serialización:
■ la   serialización de   este evento se produce justo antes de que tenga lugar la
serialización. Aplicar el Atributo OnSerializing al método que debe ejecutarse
durante este evento.
■   serializa   este evento se produce justo después de que tenga lugar la
serialización. Aplicar el Atributo OnSerialized al método que debe ejecutarse
durante este evento.
■   deserializar   Este evento se produce justo antes de la deserialización
elimina. Aplicar el  atributo OnDeserialized al método  que debe ejecutarse
durante este Evento.

■   Deserializan   Este evento se produce justo después de la deserialización se


lleva a cabo y después IDeserializationCallback.OnDeserialization ha sido
llamado. Usted debe usar  en su lugar OnDeserialization
IDeserializationCallback.Cuando los formateadores  distinto Asunto
BinaryFor podrían ser utilizados. Aplicar el  atributo OnDeserializing al
método que debe ejecutarse durante este evento.
La secuencia de estos eventos se ilustra en la figura 5-2.
Comienza la La deserialización comienza
serialización

[OnSerializin [OnDeserializing]
g]

Se produce la La deserialización ocurre


serialización

IDeserializationCallb
[OnSerialize
ack,
d] OnDeserializatio
n

Completada la [OnDeserialized]
serialización

La deserialización completado

La figura 5-2  puede utilizar la serialización eventos para ejecutar métodos durante las diferentes
fases de la serializa- ción y proceso de deserialización.

Utilizando estos eventos es la mejor y más fácil manera de controlar el proceso de


serialización. Los métodos no acceder al stream de serialización sino que permiten alterar el
objeto antes y después de la serialización, o antes y después de la deserialización. Los
atributos se pueden aplicar a todos los niveles de la jerarquía de herencia de tipo, y cada
método es llamado en la jerarquía desde la base hasta la más derivada. Este mecanismo evita
la complejidad y los problemas resultantes de la aplicación de la  interfaz ISerializable dando
la responsabili- bilidad para la serialización y deserialización de la aplicación más derivada.
Para un método para responder a uno de estos eventos, debe cumplir estos requisitos:
■   acepta un  objeto como parámetro StreamingContext
■   devolver void
■   tener el atributo que coincide con el evento que desea interceptar
El siguiente ejemplo muestra cómo crear un objeto que responde a serie- ización de eventos.
En su propio código, puede responder a tantos o tan pocos eventos como desee. Además,
puede aplicar el mismo evento de serialización a múltiples métodos, o aplicar varios eventos
en un solo método.

// C# [serializable]
Clase ShoppingCartItem
{
ProductId Int32 público; público
precio decimal; public Int32; la
cantidad total de decimales;

[OnSerializing]
Void StreamingContext CalculateTotal(SC)
{
Total = precio * cantidad;
}

[OnDeserialized]
Void StreamingContext CheckTotal(SC)
{
Si (total == 0) { CalculateTotal(SC); }
}
}

Los eventos sólo se admiten para la  serialización BinaryFormatter.


Para SoapFormatter o serialización personalizada, usted está limitado a usar el  interfaz
IDeserializationCallback, tal como se explica en la Lección 1 de este capítulo.

Cómo cambiar la serialización en función del contexto


Normalmente, cuando se serializa un objeto, el destino no importa. En algunas circunstan-
cias, sin embargo, es posible que desee para serializar y deserializar un objeto diferente,
dependiendo del destino. Por ejemplo, normalmente no debe serializar los países miem- bros
que contienen información sobre el proceso en curso, ya que la información podría no ser
válido cuando se deserializa el objeto. Sin embargo, esa información sería útil si el objeto va a
ser deserializados por el mismo proceso. Alternativamente, si el objeto deserializado es útil
sólo si el mismo proceso, puede elegir una excepción si usted sabía que el destino era un
proceso diferente.
La  estructura StreamingContext pueden proporcionar información acerca del destino de un
objeto serializado a las clases que implementan la  interfaz
ISerializable. StreamingContext pasa a ambos GetObjectData y un objeto de serialización del
constructor. El Streaming- contexto estructura tiene dos propiedades:
■   contexto  una referencia a un objeto t hat cont ains cualquier información
de contexto deseado por el usuario.
■   Estado  un conjunto de indicadores de bit para indicar el origen o el destino
de los objetos serializados/deserializar. Las banderas son:
❑   CrossProcess  el origen o destino es un proceso diferente en la misma
máquina.
❑   CrossMachine     el origen o el destino está en una máquina diferente.
❑         El
origen o el destino del archivo es un archivo. No asuma que el
mismo proceso se deserializar los datos.
❑   Persistencia   el origen o destino es una tienda, como una base de
datos, archivo u otro. No asuma que el mismo proceso se deserializar
los datos.
❑   Remoting   el origen o destino es remoting a un lugar desconocido.
La ubicación podría estar en la misma máquina, pero también podría
estar en otro
La máquina.
❑   Otros   el origen o destino es desconocido.
❑   cerrar  elobjeto gráfico se está clonada. El código de serialización
podría suponer que el mismo proceso se deserializar los datos y, por lo
tanto, es seguro que Access administra u otros recursos no
administrados.
❑   CrossAppDomain   el origen o destino es un AppDomain diferentes.

❑   todo   el origen o destino puede ser cualquiera de los anteriores


contextos. Este es el contexto predeterminado.
Para hacer decisiones de contexto durante la serialización y deserialización, implementan
la  interfaz ISerialization en su clase. Para la serialización, inspeccione la  estructura
StreamingContext pasa a su  método GetObjectData del objeto. Para la deserialización,
inspeccione la  estructura StreamingContext pasa a su constructor de serialización del objeto.
Si serializar o deserializar un objeto y desea proporcionar contexto infor- mación, modifique
la  propiedad StreamingContext IFormatter.Contexto antes de llamar el formateador
de serializar o deserializar los métodos. Esta propiedad se implementa por tanto
el BinaryFormatter  SoapFormatter y clases. Cuando se construye un formateador, el
formateador se establece automáticamente la  propiedad de contexto nulo y la  propiedad
estatal a todos.

Cómo crear un formateador personalizado


Crear un formateador personalizado, implemente la    interfaz IGenericFormatter IFormatter
o. Ambos BinaryFormatter y SoapFormatter implementar la  interfaz IFormatter. El Para-
matterServices proporciona métodos estáticos de la clase (incluyendo GetObjectData) para
ayudar con la implementación de un formateador.

Nota   .NET 2.0


Aunque  IFormatter  estaba disponible comenzando con .NET
1.1, IGenericFormatter  es nuevo en .NET 2.0.

Más info  formateadores


personalizados
Muy pocas personas tendrán que implementar los formateadores
personalizados. Por consiguiente, este libro cubre a un nivel muy alto. Para
obtener información detallada acerca de los formateadores personalizados, lea
"formatear tu camino al éxito con las versiones de .NET Framework 1.1 y
2.0." http://msdn.microsoft.com/msdnmag/issues/
04/10/AdvancedSerializatio
n/default.aspx.

Práctica: Implementar la serialización personalizada


En este laboratorio, debe modificar una clase para anular la serialización predeterminada y
tomar con- trol sobre los cuales los miembros son serializados. Si se produce un problema de
completar un ejercicio, los proyectos terminados están disponibles en el CD en la carpeta de
código.
Ejercicio: Actualización de una clase para utilizar la Serialización
personalizada
En este ejercicio, va a actualizar una clase para mejorar la eficiencia de la serialización,
manteniendo al mismo tiempo un control total sobre el modo en que los datos se almacenan y
recuperan.
1.  Copiar el capítulo05\lección3-Serialize-People carpeta desde el CD a tu disco
duro y abrir la versión en C# o Visual Basic versión del proyecto Serialize-
People.
2.  Añadir el  espacio de nombres System.Runtime.Serialization a la  clase
Person.
3.  Agregar el  atributo Serializable a la persona a la clase y, a continuación,
generar el proyecto para asegurarse de que se compila correctamente.
4.  Modifique la  clase Person para que implemente  ISerializable.
5.  Agregar el  método GetObjectData SerializationInfo, que acepta un objeto y
un  objeto StreamingContext y añade elementos a
serializarse SerializationInfo objeto. Agregar
el nombre y fechadenacimiento  SerializationInfo variables al objeto, pero
no agrega la variable edad. El código podría tener el siguiente aspecto:
// C#
Virtual public void GetObjectData(SerializationInfo info, StreamingContext
contexto)
{
Info.AddValue("name", nombre);
Info.AddValue("DOB", Fechadenacimiento);
}

6.  Agregar el constructor de serialización, que acepta un SerializationInfo objeto


y un  objeto y, a continuación, StreamingContext inicializa las variables
miembro utilizando el contenido de SerializationInfo objeto. Utilice los
mismos nombres de elementos que utilizó en el paso anterior. Después de que
usted haya deserializa todas las variables, llame al  método CalculateAge
para inicializar las variables de edad. El código podría tener el siguiente
aspecto:

// C#
Persona pública(SerializationInfo info, StreamingContext contexto)
{
Nombre = info.GetString("Nombre");
Fechadenacimiento = info.GetDateTime
("DOB"); CalculateAge();
}

7.  Genere el proyecto y resolver cualquier error.


8.  Abra un símbolo del sistema en el directorio Build y, a continuación,  ejecute
el comando siguiente:
Tony Serialize-People 1923 4 22

9.  Ahora ejecute el comando sin parámetros para comprobar que la


deserialización funciona correctamente.

Resumen de la lección
■   Puede implementar  ISerialization para realizar la serialización personalizada.
■   BinaryFormatter proporciona cuatro eventos que se pueden utilizar para el
control de partes de la Seri- alization OnSerializing OnSerialized proceso: ,, ,
y OnDeserialized OnDeserializing.
■   El StreamingContext , una instancia de la clase que se ofrece a los métodos
llamados durante la serialización de eventos, le da información sobre el
origen o el destino previsto del proceso de serialización. El método de
realizar la serialización debe especificar esta información para que sea útil.
■   aunque pocos desarrolladores requerirá un control total sobre la
serialización, usted puede implantar el   IGenericFormatter IFormatter
o interfaces para crear formateadores personalizados.

Examen de la lección
Puede utilizar las siguientes preguntas para poner a prueba su conocimiento de la
información en la lección 3, "Serialización personalizada." Las preguntas también
están disponibles en el CD complementario si prefiere revisarlos en forma
electrónica.

Nota   Respuestas
Las respuestas a estas preguntas y explicaciones de por qué cada opción de
respuesta es correcta o no se encuentran en la sección "Respuestas" al final
del libro.

1.  Los parámetros que deben aceptar un constructor si la clase implementa


ISerializ- capaz? (Seleccione todos los que correspondan).
A.  SerializationInfo
B.   Formateador
C.   StreamingContext
D.   ObjectManager
2.  Los eventos que se utiliza para ejecutar un método inmediatamente antes de la
deserialización produce?
A.  OnSerializing
B.   OnDeserializing
C.   OnSerialized
D.   OnDeserialized
3.  Los  eventos que se utiliza para ejecutar un método inmediatamente después
de la serialización se produce?
A.  OnSerializing
B.   OnDeserializing
C.   OnSerialized
D.   OnDeserialized
4.  Cuál de los siguientes son los requisitos para que un método es llamado en
respuesta a un evento de serialización? (Seleccione todos los que
correspondan).
A.   StreamingContext aceptar un objeto como parámetro. 

B.    SerializationInfo aceptar un objeto como parámetro. 


C.   Devolver void.
D.   Devolver un  objeto StreamingContext.

Repaso del cap tulo


Para practicar y reforzar los conocimientos aprendidos en este capítulo, puede
realizar cualquiera de las siguientes tareas:
■   Revisar el resumen del capítulo.
■   Revisar la lista de términos clave que se introdujo en este capítulo.
■   Complete los casos. Estos escenarios configurar situaciones del mundo real
involv- ción de los temas de este capítulo, y le pedimos a usted para crear una
solución.
■   completar las prácticas sugeridas.
■   tomar un examen de práctica.

Resumen del capítulo


■ la   serialización genera un objeto como una serie de bytes, mientras que la
deserialización lee un objeto serializado y define el valor de un objeto. La
mayoría de clases personalizadas se pueden serializar simplemente añadiendo
el  atributo Serializable. En algunos casos, puede ser capaz de mejorar la
eficiencia o prever cambios en la estructura de clases mediante la
modificación de su clase para cambiar el comportamiento de la serialización
por defecto.
■    la serialización XML proporciona una manera de almacenar y transferir
objetos utilizando normas abiertas. La serialización XML se pueden
personalizar para adaptarse a las necesidades exactas de un esquema XML,
haciendo sencillo para convertir objetos en documentos XML y en objetos.
■ la   serialización personalizada es necesaria en situaciones donde las clases
contienen información compleja, se producen cambios importantes a la str
ucture rojo de una clase entre diferentes versiones, y donde se necesita un
control completo sobre la forma en que se almacena la información. Puede
realizar la serialización personalizada implementando la  interfaz
ISerializable o por responder a los eventos de serialización.

Términos clave
¿ Sabes lo que significan estos términos clave? Puede verificar sus respuestas mediante la
búsqueda de los términos en el glosario al final del libro.
■   BinaryFormatter

■ La   deserialización

■ La   serialización
■   SoapFormatter

■   XML  (eXtensible Markup Language).


Casos
En los siguientes casos, podrá aplicar lo que ha aprendido acerca de cómo implementar y
aplicar la serialización, así como cómo actualizar las aplicaciones que hacen uso de la
serialización. Usted puede encontrar las respuestas a estas preguntas en la sección de
"respuestas" al final de este libro.
Caso práctico 1: la elección de una técnica de serialización
Usted es un desarrollador de aplicaciones para la ciudad Power & Light. Durante el último
año, usted y su equipo han estado creando una solución .NET Framework distribuidas para
sustituir el anticuado sistema que representa actualmente el uso de la electricidad y distribuye
las facturas a los clientes. Se han creado órganos para vigilar el uso de la electricidad, y que
están en la etapa de desarrollo cuando usted necesita para transmitir información de uso para
el proyecto de ley- ing sistema. Su jefe le pide a entrevistar a personas clave y luego llegar a
su oficina para responder a sus preguntas acerca de sus opciones de diseño.

Entrevistas
La siguiente es una lista de personal de la empresa entrevistados y sus
declaraciones:
■   Sistema de Facturación Development Manager  "Ya tengo mi chico
que trabaja sobre esto, y él ha construido métodos con .NET que aceptan
el uso de clases de objeto y agregar la información de facturación para la
base de datos. Tan solo tenemos que crear esos
Objetos y enviarlos a través de la red interna a nosotros".
■   Network Manager  "Toda la contabilidad y la facturación de los servidores
están en la misma sub-red, así no tienes que preocuparte por el tráfico de la
red va a través de los firewalls. Me gustaría que para intentar minimizar el
ancho de banda utilizado, tenemos mil-
Los leones de cuentas, y que está a punto de subred ya saturada".

Preguntas
Responda las siguientes preguntas para el administrador:
1.  Método de serialización que usas?
2.  ¿Qué cambios se necesitan para hacer de tu clase para habilitar la
serialización?
3.  Acerca de cuántas líneas de código se necesita escribir para realizar la
serialización?

Caso práctico 2: serialización entre versiones


Usted es un desarrollador de aplicaciones de trabajo Seguros inmensos. Recientemente, se ha
lanzado la versión 1.0 del incidente, una aplicación basada en .NET 1.1 que rastrea los
eventos seguros a lo largo de su ciclo de vida.
Con el éxito del lanzamiento de la versión 1.0 del incidente, ha comenzado el desarrollo de
incidencia 2.0. Incidente 2.0 se basa en .NET 2.0. Durante una reunión de planificación, su
jefe le pregunta acerca de cómo incidente 2.0 manejará el proceso de actualización durante la
implementación.

Preguntas
Responda las siguientes preguntas para el administrador:
1.  En Incident 1.0, sé que usted guardar algunas de las preferencias del usuario,
como la ventana de posi- ción, a un archivo mediante la serialización
tus preferencias objeto utilizando BinaryFormatter. Puede deserializar los
ajustes directamente en incidente 2.0 si no realiza ningún cambio en
las preferencias de clase?
2.  Tenemos algunas solicitudes de funciones que requieren que usted agregue
más preferencias. Si desea agregar más miembros a la  clase de preferencias,
que todavía pueda deserializar directamente esos ajustes en incidente 2.0? En
caso afirmativo, ¿qué adaptaciones especiales tendrá que hacer?
3.  El departamento de TI ha solicitado podemos pasar a utilizar archivos de
configuración basado en XML para que puedan editarlos más fácilmente.
¿Cómo podría usted deserializar la existen- tes en formato binario objeto,
mientras que serializar un objeto XML?

Prácticas recomendadas
Que le ayudarán a dominar la "Implementación de la serialización y la funcionalidad de
entrada/salida en una aplicación .NET Framework" examen objetivo, completar el
seguimiento de las tareas.

Serializar o deserializar un objeto o un objeto gráfico mediante


Las técnicas de serialización en tiempo de ejecución
Para esta tarea, se deben completar un mínimo de prácticas 1 y 2. Si desea un mejor
entendimiento de cómo la serialización puede ser utilizado en el mundo real y usted tiene los
recursos necesarios para realizar la práctica 3, completarla.

■ La   Práctica 1  usando la última clase personalizada que se creó como parte


de su trabajo, modificarlo de manera que se pueda serializar. A continuación,
escribir una aplicación para serializar y deserializar
utilizando BinaryFormatter. Examine los datos serializados. A continuación,
modifique la aplica-
Ción a utilizar SoapFormatter. Examine los datos serializados.
■ La   Práctica 2  Examine la clase utilizada en la práctica 1 y, si es posible,
identificar a un miembro que no necesita ser serializadas. Modifique la clase
de tal manera que el MEM- ber no será serializado, pero se define
automáticamente en la deserialización.
■ La   Práctica 3  Escribir una aplicación cliente/servidor para transferir un
objeto entre dos equipos en red utilizando la serialización y deserialización.

Controlar la serialización de objetos en formato XML usando


el   espacio de nombres System.Xml.Serialization
Para esta tarea, se deben completar las tres prácticas para adquirir experiencia con XML La
serialización de clases del mundo real y esquema.
■   prácticas 1  Escribir una aplicación que utiliza la serialización XML para
serializar y deserializar la última clase creada como parte de su trabajo.
■ La   Práctica 2  Examine la clase utilizada en la práctica 1 y, si es posible,
identificar a un miembro que no necesita ser serializadas. Utilizar un atributo
para modificar la clase de tal manera que los Estados no será serializado.
■ La   Práctica 3  Encontrar un esquema XML en Internet y crear una clase
que, a la hora de serializar, conforme al esquema XML. Crear la clase
mediante dos técnicas distintas: manualmente y con Xsd.exe.

Aplicar el formato de serialización personalizada mediante el


Serialización clases Formatter
Para esta tarea, se deben completar un mínimo de prácticas 1 y 2. Si desea un conocimiento
en profundidad del proceso de serialización, completar la Práctica 3.
■ La   Práctica 1  usando la última clase personalizada que se creó como parte
de su trabajo, modificarlo para que se implemente   con éxito ISerialization y
pueden ser serializados y dese- rialized. Examinar las clases de miembros
para determinar si puede optimizer La serialización, omitiendo los valores
calculados.
■ La   Práctica 2  Crear una clase que proporciona métodos para los
cuatro  eventos alization BinaryFormatter seri-.
■   Práctica 3.  Implementar  la  interfaz IFormatter para crear un
formateador personalizado. Utilizarlo durante la serialización y
deserialización de entender el papel del formateador de serialización.

Tomar un Test de práctica


Los tests de práctica en este libro's Companion CD ofrecen muchas opciones. Por ejemplo,
puede hacerse la prueba en un solo examen objetivo, o puede probar usted mismo en todos los
exámenes de certificación 70-536 contenido. Puede configurar la prueba para que simula
cuidadosamente la expe- riencia de tomar un examen de certificación, o puede configurarlo en
modo de estudio, de modo que usted puede mirar las respuestas correctas y explicaciones
después de responder a cada pregunta.

Más info  práctica de pruebas


Para obtener más información acerca de la prueba de la práctica todas las
opciones disponibles, consulte la sección "Cómo usar los tests de práctica", sec-
ción en la introducción de este libro.
Capítulo 6
Los gráficos
Los gráficos se pueden utilizar para mejorar la interfaz de usuario de sus aplicaciones, generar
gráficos e informes, así como editar o crear imágenes. El .NET Framework incluye
herramientas que permiten dibujar líneas y formas, tramas y texto. Este capítulo describe
cómo crear gráficos e imágenes utilizando las clases del  espacio de nombres
System.Drawing.

Objetivos del examen en este capítulo:


■   Mejorar la interfaz de usuario de una aplicación de .NET Framework
utilizando el sistema
 Espacio de nombres .Dibujo.
❑   mejorar  lainterfaz de usuario de una aplicación de .NET Framework
mediante el uso de pinceles, lápices, colores y fuentes.
❑   mejorar  la
interfaz de usuario de una aplicación de .NET Framework
mediante gráficos, imágenes, mapas de bits e iconos.
❑   mejorar  la interfaz de usuario de una aplicación de .NET Framework
utilizando formas y tamaños.
Las lecciones de este capítulo:
■ La   Lección 1: Dibujo de gráficos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
320
■ La   Lección 2: Trabajar con imágenes . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . 342
■ La   Lección 3: formato de texto . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . .
349

Antes de empezar
Para completar las lecciones de este capítulo, debe estar familiarizado con Microsoft Visual
Basic o en C# y se sienten cómodos con las siguientes tareas:
■   Crear una aplicación de Windows Forms en Microsoft Visual Studio
utilizando Visual Basic o C#.
■   escribir archivos y arroyos.

Lección 1: Dibujar gráficos


Puede utilizar .NET Framework para mejorar la interfaz de usuario mediante el dibujo de
líneas, cir- Cles, y otras formas. Con sólo un par de líneas de código, puede mostrar estos
gráficos en un formulario u otro control de formularios Windows Forms.
Después de esta lección, será capaz de:
■   describir los miembros del   espacio de nombres System.Drawing.
■   controlar la ubicación, el tamaño y el color de los controles.
■   dibujar líneas y formas vacías, y formas sólidas.
■   Personalizar los lápices y pinceles para mejorar los gráficos.
Lección Tiempo estimado: 60 minutos.

El   espacio de nombres System.Drawing


El .NET Framework incluye el  espacio de nombres System.Drawing para permitirle crear g
raphics desde cero o modificar las imágenes existentes. Wit h e l  espacio de nombres
System.Drawing, puede hacer lo siguiente:
■   Añadir círculos, líneas y otras formas a la interfaz de usuario de forma
dinámica.
■   Crear gráficos desde cero.
■   editar y cambiar el tamaño de las imágenes.
■   Cambiar la relación de compresión de imágenes guardadas en el disco.
■   Recortar o ampliar las imágenes.
■   Añadir copyright logotipos o texto a imágenes.
Esta lección se centrará en la elaboración de gráficos. Lección 2 abarca el trabajo con
imágenes, y La lección 3 describe cómo dar formato al texto.
La tabla 6-1 muestra las clases más importantes en el  espacio de nombres System.Drawing,
que puede utilizar para crear objetos utilizados para la creación y edición de imágenes.
Tabla 6-1    clases System.Drawing

Clase   Descripción
   Encapsula  un mapa de bits GDI+ Bitmap, que consta de los datos
de píxeles de una imagen gráfica y sus atributos. Un  objeto
Bitmap es un objeto que se utiliza para trabajar con
imágenes definidas por los datos de los píxeles. Esta es la
clase que se utilizará cuando necesite cargar o guardar
imágenes.
Tabla 6-1    clases System.Drawing

Clase   Descripción
Cepillo  clases derivadas de esta clase base abstracta, descritos en la
sección "Cómo rellenar formas", definir los objetos utilizados
para rellenar el inte- riors de formas gráficas tales como
rectángulos, elipses, tartas, poli- gons y trazados.
Pinceles  Pinceles para todos los colores estándar. Esta clase puede ser
inher- ited. Utilice esta clase para evitar la creación de una
instancia de una  clase Brush.
ColorConverter  convierte los colores de un tipo de datos a otro. Tener acceso a
esta clase mediante el TypeDescriptor.
   Traduce ColorTranslator colores en y desde  las estructuras de color GDI+.
Esta clase no se puede heredar.
Font  define un formato concreto para el texto, incluida la fuente,
el tamaño y los atributos de estilo. Esta clase no se puede
heredar.
FontConverter  convierte  los objetos Font de un tipo de datos a otro. Acceder a
la
 Clase FontConverter mediante el  objeto TypeDescriptor.
FontFamily  define un grupo de tipo se enfrenta con un diseño básico
similar y ciertas variaciones en los estilos. Esta clase no se
puede heredar.
Graphics  encapsula  una superficie de dibujo de GDI+. Esta clase no se
puede heredar. Podrá utilizar esta clase en cualquier momento
si necesita dibujar líneas, dibujar formas o añadir texto a un
control gráfico o imagen.
Icono Represiente un icono de Microsoft Windows, que es una
pequeña imagen de mapa de bits que se usa para representar un
objeto. Los iconos pueden ser pensadas como trans- padre
bitmaps, aunque su tamaño es determinado por el sistema.
IconConverter  convierte un  objeto Icon de un tipo de datos a otro. Tener
acceso a esta clase mediante el  objeto TypeDescriptor.
Imagen de  una clase base abstracta que proporciona la funcionalidad para
el mapa de bits
Y Metafile clases descendientes.
ImageAnimator   anima una imagen que tiene tramas basadas en tiempo.
ImageConverter ImageConverter es una clase que se puede utilizar para
convertir  objetos de imagen de un tipo de datos a otro. Tener
acceso a esta clase mediante el  objeto TypeDescriptor.

Tabla 6-1    clases System.Drawing

Clase   Descripción
ImageForm ImageFormatConverter es una clase que puede ser usado para
at- convertir col- ors de un tipo de datos a otro. Tener acceso a
Converter esta clase mediante el  objeto TypeDescriptor.
Pen  define un objeto que se utiliza para dibujar líneas, curvas y
flechas. Esta clase no se puede heredar.
Plumas  Plumas de todos los colores estándar. Esta clase no se
puede heredar.
Utilice esta clase para evitar la creación de una instancia de
la  clase Pen.
PointConverter  convierte un  objeto de punto de un tipo de datos a otro.
Tener acceso a esta clase mediante el  objeto
TypeDescriptor.
Rectáng Convierte los rectángulos de un tipo de datos a otro. Tener
ulo- acceso a esta clase mediante el  objeto TypeDescriptor.
Convert
er
Región Describes el interior de una forma gráfica compuesta de rectan-
gles y rutas. Esta clase no se puede heredar.
    SizeConverter SizeConverter la clase se utiliza para convertir de un tipo de
datos a otro. Tener acceso a esta clase mediante el  objeto
TypeDescriptor.
SolidBrush  define un pincel de un solo color. Los pinceles se usan para rellenar
el gráfico- ics formas, como rectángulos, elipses, tartas, polígonos
y rutas. Esta clase no se puede heredar.
StringFormat  encapsula la  información de diseño de texto (tales como la
alineación y el espaciado entre líneas), mostrar las manipulaciones
(como la inserción de puntos suspensivos y sustitución de cifras
nacionales), y las funciones OpenType. Esta clase no se puede
heredar.
SystemBrushes  cada propiedad de la  clase SystemBrushes es un  objeto
SolidBrush que es el color de un elemento de pantalla de
Windows.
SystemColors  cada propiedad de la  clase SystemColors es una  estructura de
color que es el color de un elemento de pantalla de Windows.
SystemFonts  especifica las fuentes que se utilizan para mostrar texto en los
elementos de pantalla de Windows.

Tabla 6-1    clases System.Drawing

Clase   Descripción
SystemIcons  cada propiedad de la  clase SystemIcons es un  objeto Icon para
Iconos de todo el sistema Windows. Esta clase no se puede heredar.
SystemPens  cada propiedad de la  clase SystemPens es un  objeto Pen que es el
color de un elemento de la pantalla en Windows y que tiene una
anchura de 1.
TextureBrush  cada propiedad de la  clase TextureBrush es un  objeto Brush que
utiliza una imagen para rellenar el interior de una forma. Esta clase
no se puede heredar.
- Atributo ToolboxBitmap
Puede aplicar un ToolboxBitmapAttribute a un control de manera que los contenedores, como
diseñador de formularios de Visual Studio de Microsoft, puede recuperar un icono que representa el
control. El mapa de bits para el icono puede estar en un archivo por sí mismo o incrustado en el
ensamblado que contiene el control.
El tamaño del mapa de bits que desea incrustar en el ensamblado del control (o almacenar en un
archivo independiente) debe ser de 16 por 16. El  método GetImage de un  objeto
ToolboxBitmapAttribute puede devolver el pequeño
16-por-16 o una gran imagen de 32-por-32 que crea la imagen mediante el escalado de la
imagen pequeña.

De estas clases, se utilizarán  los gráficos más a menudo porque proporciona métodos para dibujar
en la pantalla del dispositivo. La  clase Pen se utiliza para dibujar líneas y curvas, mientras que las
clases derivadas de la clase abstracta Brush se utiliza para rellenar el interior de formas. Además,
debe estar familiarizado con la  clase PictureBox, que puede utilizar en aplicaciones de Windows
Forms para mostrar una imagen como parte de la interfaz de usuario. El  espacio de nombres
System.Drawing  incluye las estructuras que se muestran en la Tabla 6-2.
Tabla 6-2
System.Drawing  estructuras
Clase   Descripción
CharacterRange  especifica un rango de posiciones de caracteres dentro de una cadena.
Color  representa un color.
   Representa el punto entero de un par ordenado de coordenadas x e y que
define un punto en un plano bidimensional.

Tabla 6-2  System.Drawing  estructuras

Clase   Descripción
PointF  representa un par ordenado de punto flotante de coordenadas x e
y que define un punto en un plano bidimensional.
Rectangle  almacena un conjunto de cuatro números enteros que representan
la ubicación y el tamaño de un rectángulo. Para las funciones
más avanzadas de la región, utilizar un  objeto de región.
RectangleF  almacena un conjunto de cuatro números de punto flotante que
representan la ubicación y el tamaño de un rectángulo. Para las
funciones más avanzadas de la región, utilizar un  objeto de
región.
Tamaño  almacena un par ordenado de números enteros,
normalmente el ancho y el alto de un rectángulo.
SizeF  almacena un par ordenado de números de punto flotante,
normalmente el ancho y el alto de un rectángulo.

La más importante de estas estructuras y las estructuras que se utilizan con mayor frecuencia son
Color, punto, Rectánguloy Tamaño.

Cómo especificar la ubicación y el tamaño de los controles


Uno de los usos más simple y más común para el  espacio de nombres System.Drawing es la
especificación de la ubicación de los controles en una aplicación Windows Forms. Este proceso
puede ser útil para crear formularios que ajustar dinámicamente en función de la entrada del
usuario.
Para especificar la ubicación de un control, crear una nueva  estructura Point especificando las
coordenadas con respecto a la esquina superior izquierda del formulario, y utilizar el punto para
establecer el control loca- ción de bienes. La  estructura PointF relacionados acepta las
coordenadas como puntos flotantes, en lugar de números enteros, pero PointF puede utilizarse para
especificar la ubicación de los controles de la GUI. Por ejemplo, para mover un botón en la esquina
superior izquierda del formulario, exactamente 10 píxeles desde la parte superior y a la izquierda,
se puede usar el siguiente código:
// C#
Button1.Location = new Point(10, 10).

Nota   muestras gráficas requieren una aplicación de


Windows Forms
La mayor parte de este libro se basa en las aplicaciones de consola para muestras.
Sin embargo, este capítulo utiliza Windows
Las aplicaciones de formularios para
mostrar fácilmente gráficos.

Como alternativa al uso de Punto, puede realizar la misma función con


el izquierdo y superior  derecho e inferior o las propiedades de un control. Sin embargo, esto
requiere dos líneas de código, como se muestra en el siguiente ejemplo:
// C#
Button1.Izquierda = 10;
Button1.arriba = 10;

Puede especificar el tamaño de un control tan simplemente como especificar la ubicación. El


código siguiente muestra cómo especificar el tamaño mediante la  clase de tamaño:
// C#
Button1.Size = nuevo tamaño(30, 30).
Cómo especificar el color de los controles
Puede especificar un color del control mediante la  estructura de color. La forma más simple de
especificar un color es utilizar una de las propiedades predefinidas ubicadas dentro
de System.Drawing.Color, como muestra el ejemplo siguiente:

Button1.ForeColor = Color.Red);
Button1.BackColor = Color.azul;

Si necesita especificar un color personalizado, utilice el  método estático de Color.FromArgb. El


met hod tiene varias sobrecargas, así que usted puede especificar el color con un solo byte,
especificando el rojo, el verde y el azul, o mediante otro tipo de información.

El ejemplo siguiente ilustra cómo especificar color proporcionando tres enteros para rojo, verde y
azul:
// C#
Button1.ForeColor = Color.FromArgb(10, 200, 200);
Button1.BackColor = Color.FromArgb(200, 5, 5).

Cómo dibujar líneas y formas


Para dibujar en un formulario o un control, siga estos pasos de alto
nivel:
1. Crear un  objeto Graphics  llamando a  System.Windows.Forms.Control.Crear- 
Método gráfico.
2.  Crear un  objeto Pen.
3.  Llame a un miembro de la  clase Graphics para dibujar sobre el control mediante
el Lápiz. El dibujo empieza con la  clase System.Drawing.Graphics. Para crear una
instancia de esta Clase, normalmente se llama un  método CreateGraphics del control.
Alternativamente, como se discutió En la lección 2, se puede crear un  objeto Graphics
basado en un  objeto de imagen si desea ser capaz de guardar la imagen como un
archivo. Una vez creado el objeto graphics, tienes muchos métodos que usted puede
llamar para realizar el dibujo:

■   Clear     Borra toda la superficie de dibujo, y se rellena con un color especificado.


■   DrawEllipse,   dibuja una elipse o un círculo definido por un rectángulo
delimitador especificado por un par de coordenadas, una altura y una anchura. La
elipse se toquen los bordes del rectángulo delimitador.
■      DrawIconUnstretched DrawIcon y   dibuja la imagen representada por el
icono speci- zos en las coordenadas especificadas, con o sin escalar el icono.
■   , DrawImage          dibuja
DrawImageUnscaledAndClipped
DrawImageUnscaled y e l  objeto de imagen especificada en la ubicación
especificada, con o sin escalar o cosecha- ping en la imagen.
■   DrawLine     dibuja una línea que conecta los dos puntos especificados por los
pares de coordenadas.
■   DrawLines     dibuja una serie de segmentos de línea que conectan un rayo
de punto ar Las estructuras.

■   DrawPath   dibuja una serie de líneas y curvas conectadas.


■   DrawPie     dibuja una forma circular definida por una elipse especificada por un par
de coordenadas, un ancho, un alto y dos líneas radiales. Observe que las coordenadas
que suministre con DrawPie especificar la esquina superior izquierda de un rectángulo
imaginario que formarían
Los límites de la tarta; las coordenadas no especifica el pastel del centro.
■   DrawPolygon   dibuja una forma con tres o más lados definida por una matriz de
Punto de estructuras.
■   DrawRectangle   dibuja un rectángulo o un cuadrado especificado por un par de
coordenadas, una anchura y una altura.
■   DrawRectangles   dibuja una serie de rectángulos o cuadrados por el rectángulo
especificado
Las estructuras.
■   DrawString     dibuja la cadena de texto especificada en la ubicación especificada
con el cepillo y  objetos de fuente especificada.
Para utilizar cualquiera de estos métodos, debe proporcionar una instancia de la  clase Pen.
Normalmente, debe especificar la  clase Pen de color y ancho en pixeles con el constructor. Por
ejem- plo, el siguiente código dibuja un 7 píxel de ancho línea roja desde la esquina superior
izquierda (1, 1) a un punto próximo a la mitad del formulario (100, 100), como se muestra en la
figura 6-1. Para ejecutar este código, cree una aplicación de formularios Windows Forms y
agregue el código a un método run durante el  evento Paint del formulario:
// C#
// Crear un objeto Graphics a partir del formulario
Graphics g = this.CreateGraphics();

// Crea un objeto Pen para dibujar


Bolígrafo p = new Pen(Color.Red, 7);

// Dibujar la línea
G.DrawLine(p, 1, 1, 100, 100);
Figura 6-1  utilice Graphics.DrawLine  para crear líneas rectas

Asimismo, el siguiente código dibuja un azul pastel con un ángulo de 60 grados, como se muestra
en la Figura 6-2:

// C#
Graphics g = this.CreateGraphics(); p = new Pen
Pen(Color.Blue, 3).

G.DrawPie(p, 1, 1, 100, 100, 30, 60).

Figura 6-2 La  utilización de gráficos.DrawPie  para crear gráficos circulares

Graphics.DrawLines, gráficos, DrawPolygon. Graphics.DrawRectangles y aceptar las


matrices como parámetros para permitir crear formas más complejas. Por ejemplo, el
siguiente código dibuja un púrpura, polígono de cinco lados, como se muestra en la Figura
6-3:
' VB
Dim g como gráficos = Me.CreateGraphics
Dim p como pluma = nueva pluma(Color.MediumPurple, 2)

' Crear una matriz de puntos


Dim puntos como punto() = Nuevo Punto() {Nuevo Punto(10, 10), _ Nuevo Punto(10, 100),
...
Nuevo punto(50, 65), _ Nuevo
Punto(100, 100), _ Nuevo Punto(85,
40).}

' Dibujar una forma definida por la matriz de puntos g.DrawPolygon(p,


puntos)

// C#
Graphics g = this.CreateGraphics();
Bolígrafo p = new Pen(Color.MediumPurple, 2).

// Crea una matriz de puntos


Punto[] Puntos = new punto[]
{Nuevo Punto(10, 10)
Nuevo punto (10, 100), el nuevo
punto(50, 65), el nuevo punto(100,
100), el nuevo punto(85, 40)};

// Dibujar una forma definida por la matriz de puntos g.DrawPolygon(p,


puntos).

Figura 6-3 La  utilización de gráficos.DrawPolygon  para crear formas hechas


de varias líneas

Nota   horizontal y a continuación vertical


Cuando se pasa de coordenadas a cualquier método de .NET Framework, pasará la
horizontal (X)-coor- dinate primero, y luego la coordenada vertical (Y) segundo. En un
100 por 100 píxeles  , la imagen 0,0 es la esquina superior izquierda; 100,0 es la
esquina superior derecha; 0, 100 se encuentra en la esquina inferior izquierda; y
100.100 es la esquina inferior derecha.

Cómo Personalizar bolígrafos


Además de controlar el color y el tamaño de un lápiz, que se especifican en la pluma con- structor,
también puede controlar el patrón y endcaps. Las tapas del extremo son los extremos de la línea, y
puede utilizarlas para crear flechas y otros efectos especiales.
Por defecto, las plumas trazan líneas sólidas. Para dibujar una línea punteada, cree una instancia de
la  clase Pen y, a continuación, establezca la  propiedad DashStyle Pen.a uno de estos
valores: DashStyle.Dash, DashStyle.DashDot, DashStyle.DashDotDot, DashStyle.dot, o .DashStyl
e sólida. La fol- bajando el código, que requiere que el System.Drawing.Drawing2D , demonio
namespace demuestra cada uno de estos estilos de pluma y crea el resultado mostrado en la Figura
6-4:
// C#
Graphics g = this.CreateGraphics(); p = new Pen
Pen(Color.Red, 7);

P.DashStyle = DashStyle.dot;
G.DrawLine(p, 50, 25, 400, 25).

P.DashStyle = DashStyle.Dash;
G.DrawLine(p, 50, 50, 400, 50);

P.DashStyle = DashStyle.DashDot;
G.DrawLine(p, 50, 75, 400, 75);

P.DashStyle = DashStyle.DashDotDot;
G.DrawLine(p, 50, 100, 400, 100);

P.DashStyle = DashStyle.sólido;
G.DrawLine(p, 50, 125, 400, 125);

Figura 6-4  La   clase Pen proporciona varios estilos de guión

También puede utilizar el lápiz.  Pen.DashPattern DashOffset y propiedades para definir un


modelo de guión personalizado.

Para controlar los endcaps y crear flechas o de llamadas, modificar


la pluma.StartCap y Pen.EndCap  LineCap propiedades mediante la enumeración. El siguiente
código demon- demuestra la mayoría de los pen cap estilos y crea el resultado mostrado en la
Figura 6-5:
// C#
Graphics g = this.CreateGraphics(); p = new Pen
Pen(Color.Red, 10);

P.StartCap =.ArrowAnchor LineCap; p.EndCap


=.DiamondAnchor LineCap; g.DrawLine(p, 50, 25,
400, 25).

P.StartCap =.SquareAnchor LineCap; p.EndCap =


LineCap.Triángulo; g.DrawLine(p, 50, 50, 400, 50);
P.StartCap = LineCap.plana; p.EndCap =
LineCap.Ronda; g.DrawLine(p, 50, 75, 400,
75);

P.StartCap =.RoundAnchor LineCap; p.EndCap


= LineCap.Square; g.DrawLine(p, 50, 100, 400,
100);

Figura 6-5  La   clase Pen proporciona opciones para startcaps y endcaps

Cómo rellenar formas


Para la mayoría de los  métodos Draw, la  clase Graphics también tiene  métodos de relleno que
dibujar una forma y rellenar el contenido. Estos métodos funcionan exactamente igual que
los  métodos Draw, con la salvedad de que se requiere una instancia de la  clase Brush en lugar de
la  clase Pen. La  clase Brush es abstracto, por lo que debe crear una instancia de una de las clases
secundarias:
■   System.Drawing.Drawing2D.HatchBrush   define un pincel rectangular con
un estilo hatch, un color de primer plano y un color de fondo
■   System.Drawing.Drawing2D.LinearGradientBrush   encapsula un pincel
con un lin- ear degradado que proporciona una atractiva visualmente, relleno de
aspecto profesional
■  System.Drawing.Drawing2D.PathGradientBrush   proporciona una
funcionalidad similar a LinearGradientBrush; sin embargo, puede definir una
compleja trama de relleno que se desvanece entre múltiples puntos
■   System.Drawing.SolidBrush   define un pincel de un solo color
■   System.Drawing.TextureBrush   define un pincel a partir de una imagen que
se puede colocar en mosaico a través de una forma, como un diseño de papel tapiz
Por ejemplo, el siguiente código dibuja un sólido granate, polígono de cinco lados, como se
muestra en la Figura 6-6:
// C#
Graphics g = this.CreateGraphics(); cepillo b = new
SolidBrush(Color.Granate); Punto[] Puntos = new punto[]
{Nuevo Punto(10, 10)
Nuevo punto (10, 100), el nuevo
punto(50, 65), el nuevo punto(100,
100), el nuevo punto(85, 40)};
G.FillPolygon(B, puntos).

Figura 6-6  Utilice el cepillo  con la clase Graphics.diversos  métodos de relleno para dibujar objetos
sólidos.

Puede dibujar objetos rellenos con un esquema por llamar primero a la  clase Graphics  método de
relleno y, a continuación, llamar al    método draw de la clase Graphics. Por ejemplo, el siguiente
código dibuja un polígono con un esquema y un patrón de relleno, como se muestra en la Figura 6-
7:

// C#
Graphics g = this.CreateGraphics(); p = new Pen
Pen(Color.granate, 2).
B = nuevo pincel LinearGradientBrush(nuevo punto(1,1), el nuevo punto(100,100), Color.Blanco,
Color.Red);
Punto[] Puntos = new punto[]
{Nuevo Punto(10, 10)
Nuevo punto (10, 100), el nuevo
punto(50, 65),

Nuevo punto (100, 100), el


nuevo punto(85, 40)};

G.FillPolygon(B, puntos).
G.DrawPolygon(p, puntos).

Figura 6-7  Combinar gráficos.Fill  con gráficos.  métodos Draw para crear objetos sólidos con
contornos

Puede utilizar t él mismo técnicas para dibujar sobre controles, como botones o las instancias de
la  clase PictureBox. Si usted necesita para llenar todo un  objeto Graphics con un pecado- gle
color, llame al  método Graphics.Clear.

Laboratorio: crear un método para dibujar un gráfico de tarta


En este laboratorio, debe crear un método para dibujar un gráfico de tarta, y luego mejorar ese
método para hacer el gráfico de tarta más atractiva visualmente. Si se produce un problema de
completar un ejercicio, los proyectos terminados están disponibles en el CD en la carpeta de
código.
    Ejercicio 1: Dibujar un gráfico de tarta
En este ejercicio, escribir un método que dibuja un gráfico de tarta dada una matriz de datos y
el tamaño de la estructura. En este punto, simples líneas negras será suficiente.
1.  Copiar el capítulo06\LECCION1-ejercicio1-PieChart carpeta desde el CD a tu
disco duro y abrir la versión en C# o Visual Basic versión del PieChart proyecto.
2.  Examine el formulario. El formulario tiene un control PictureBox llamado gráfico
que está enlazado a los cuatro lados del formulario. Observe que el  evento Paint
llama al  método Draw.
3.  Examinar el   método Draw. Este método incluye datos de ejemplo  que se pasan
como parámetros al  método drawPieChart usted completará. Observe que
el  método drawPieChart devuelve un  objeto Image, que se utiliza para definir la
tabla control PictureBox.
4.  Examine la  clase PieChartElement. Esta clase contiene información para describir
una sola sección del gráfico circular.
5.  Examinar el  método drawPieChart. Recibe dos parámetros: un objeto
ArrayList conteniendo sólo PieChartElement objetos, y una  estructura de tamaño.
6.  Completar el  método drawPieChart. En primer lugar, definir un  objeto de mapa de
bits que se van a devolver, crear un  objeto Graphics a partir del  objeto de mapa de
bits y, a continuación, devolver  el  objeto de mapa de bits. Por ejemplo, el código
siguiente:
// C#
Bm = new Bitmap Bitmap(s.s.Width, Height); Gráficos g =
Graphics.FromImage(BM);

// TODO: dibujar un gráfico de tarta en g volver


bm;

7.  En este punto, el proyecto se compila, pero no se dibuja el gráfico de tarta. Antes de
poder crear un gráfico de tarta de las PieChartElement objetos en el ArrayList, debe
determinar cuántos grados utiliza cada elemento. Para ello, debe calcular el total de
todos los PieChartElement. objetos de valor. Por ejemplo, el código siguiente:
// C#
// Calcular el valor total de todas las filas total flotación = 0.

(Foreach PieChartElement e en elementos)


{
If (e.value < 0)
{
Lanzar nuevos ArgumentException("Todos los elementos deben tener valores positivos");
}
Total += e.value;
}

8.  Ahora debe definir el rectángulo que consumirá el gráfico basado en la  estructura


de tamaño que se pasa al método como parámetro. El siguiente código funcionaría,
y proporciona un búfer suficiente en todos los lados de la imagen:
// C#
// Defina el rectángulo que va a utilizar el gráfico de tarta
Rectangle rect = new Rectangle(1, 1, s.Width - 2, s.altura - 2);

9.  A continuación, defina un  objeto Pen para dibujar el gráfico de tarta. Esto puede
ser un proceso sencillo, negro, 1 píxel de pluma:

// C#
Bolígrafo p = new Pen(Color.Black, 1).

10.  Por último, cree un  bucle foreach que calcula los grados para cada sección del
gráfico circular, y dibuja los gráficos de tarta. Hay muchas maneras de hacerlo,
como en el siguiente código:
// C#
// Dibujar la primera sección a 0 grados float startAngle
= 0.

// Dibujar cada uno de los gráficos circulares


(Foreach PieChartElement e en elementos)
{
// Calcular los grados que esta sección consumirá,
// Según el porcentaje del total de float sweepAngle = (e.value /
total) * 360.

// Dibujar la forma circular


G.DrawPie(p, rect, sweepAngle startAngle);

// Calcular el ángulo de la siguiente forma circular agregando


// La forma actual de grados al total anterior. startAngle += sweepAngle;
}

11.  Ejecute la aplicación y corregir cualquier error. Cambiar el tamaño del formulario y
observe que el gráfico circular se redimensiona automáticamente; el  evento Paint
llama al  método draw cuando cambia el tamaño del formulario.
    Ejercicio 2: Mejorar la apariencia del gráfico circular
En este ejercicio, mejorar el proyecto creado en el ejercicio 1 para realizar el gráfico de tarta más
atractiva visualmente. Específicamente, usted va a llenar en cada sección con un color diferente y
habilitar el anti-aliasing para suavizar las líneas.
1.  Copiar el capítulo06\LECCION1-ejercicio2-PieChart carpeta desde el CD a tu disco
duro y abrir la versión en C# o Visual Basic versión del PieChart proyecto.
Alternativamente, usted puede continuar trabajando en el proyecto creado en el
ejercicio 1.
2.  En primer lugar, en el   método drawPieChart, crear una matriz que contenga los
colores que desea utilizar en el gráfico circular. Podrá asignar los colores de forma
secuencial, por lo que no coloque colores similares, uno detrás del otro. En aras de la
simplicidad, lanzar una excep- ción si el gráfico tiene más elementos de los que tiene
los colores de su matriz. Por ejemplo:

// C#
Colores Color[] = { Color.Red, Color.Naranja, Amarillo, Color.Color.Verde, Color.Blue, Color.añil,
Color.Violeta, Color.DarkRed, Color.DarkOrange, Color.DarkSalmon, Color.Verdeoscuro,
Color.DarkBlue, Color.color lavanda,.LightBlue, Color.Coral };

Si (elementos.Count > colores.Length)


{
Lanzar nuevos ArgumentException("gráfico debe tener " +
Colores.Longitud.ToString() + " o menos elementos");
}

Nota   mantenerlo
simple
En aras de mantener el ejercicio centrado, algunos aspectos de este proyecto no
son exactamente como lo haría el diseño en el mundo real. Por ejemplo,
normalmente se desea dar a la aplicación que llama la opción de especificar
colores para las diferentes secciones, que podría llevarse a cabo mediante la
adición de un   objeto Color a la   clase PieChartElement. Además, elementos
como la captura de las excepciones, la validación de las entradas, y afirmando
se omiten en los ejemplos.

3.  Usted tendrá que seguir el color en uso. Antes de que el  bucle foreach, inicialice
una inte- ger a cero para actuar como un contador:

// C#
Int colorNum = 0.

4.  Dentro del  bucle foreach, agregue dos líneas: una para crear un nuevo  objeto
Brush, y un segundo para llamar al  método Graphics.FillPie. Llame
a Graphics.FillPie inmediatamente antes de llamar a Graphics.DrawPie de modo
que el contorno se dibuja sobre el sector relleno. En el siguiente ejemplo de código
se utiliza la  clase LinearGradientBrush, que requiere la adición de
la System.Drawing.Drawing2D namespace  para el proyecto:

// C#
// Dibujar la primera sección a 0 grados float startAngle
= 0.
Int colorNum = 0.

// Dibujar cada uno de los gráficos circulares


(Foreach PieChartElement e en elementos)
{
// Crear un pincel con un gradiente de Niza
B = nuevo pincel LinearGradientBrush(rect, colores[colorNum++], Color.Blanco, (float)45
);

// Calcular los grados que esta sección consumirá,


// Según el porcentaje del total de float sweepAngle = (e.value /
total) * 360.

// Dibujar el relleno de formas circulares g.FillPie(b, rect,


sweepAngle startAngle);

// Dibujar los contornos de formas circulares


G.DrawPie(p, rect, sweepAngle startAngle);

// Calcular el ángulo de la siguiente forma circular agregando


// La forma actual de grados al total anterior. startAngle += sweepAngle;
}

5.  Ahora, ejecute la aplicación. Experimente con los diferentes tipos de pincel para
encontrar uno que es más atractivo. Observe que las líneas aparecen un poco
irregulares; puede hacer que las líneas aparezcan más suaves,
estableciendo Graphics.SmoothingMode, como demuestra la siguiente línea:
' VB
G.SmoothingMode = SmoothingMode.HighQuality

// C#
G.SmoothingMode = SmoothingMode.HighQuality;

Resumen de la lección
■   El  espacio de nombres System.Drawing proporciona herramientas para dibujar
gráficos y edición de imágenes existentes. Las clases son más útiles , Imageny
gráficos de mapa de bits.
■   Utilice el punto y  clases de tamaño para especificar la ubicación y el tamaño de
los controles.
■   El System.Drawing.Color estructura proporciona propiedades predefinidas de
colores comunes.
■   Para dibujar líneas y formas, cree una instancia de la  clase Graphics, crear
un  objeto Pen y, a continuación, llame a uno de los  métodos miembro de gráficos
para dibujar una línea o una forma de usar la pluma de instancia.
■   Las plumas pueden personalizarse añadiendo endcaps o cambiando el patrón de
línea diferentes combinaciones de puntos y rayas.
■   para dibujar formas sólidas, cree una instancia de la  clase Graphics, cree
un  objeto Brush y, a continuación, llame a uno de los  métodos miembro de
gráficos para dibujar la forma utilizando el pincel instancia.

Examen de la lección
Puede utilizar las siguientes preguntas para poner a prueba su conocimiento de la información en la
lección 1, "Dibujar gráficos." Las preguntas también están disponibles en el CD complementario si
prefiere revisarlos en forma electrónica.

Nota   Respuestas
Las respuestas a estas preguntas y explicaciones de por qué cada opción de
respuesta es correcta o no se encuentran en la sección "Respuestas" al final del
libro.

1.  Cuál de los siguientes métodos se puede utilizar para dibujar un cuadrado con un
color sólido?
A.  Graphics.DrawLines
B.   Graphics.DrawRectangle 
C.   DrawPolygon Graphics. 
D.   Graphics.DrawEllipse
E.   Graphics.FillRectangle
F.  Graphics.FillPolygon
G.   Graphics.FillEllipse
2.  Cuál de los siguientes métodos se puede utilizar para dibujar un triángulo vacío?
A.  Graphics.DrawLines
B.   Graphics.DrawRectangle
C.   DrawPolygon Graphics.
D.   Graphics.DrawEllipse
E.   Graphics.FillRectangle
F.  Graphics.FillPolygon
G.   Graphics.FillEllipse

3.  Cuál de las siguientes clases es necesario para dibujar un círculo vacío?


(Seleccione todos los que correspondan).
A.  System.Drawing.Graphics
B.   System.Drawing.Pen
C.   System.Drawing.Brush
D.   System.Drawing.Bitmap
4.  Cuál de las siguientes clases de pincel se puede utilizar para crear un rectángulo sólido
que es rojo en la parte superior y poco a poco se desvanece a blanco hacia la parte
inferior?
A.  System.Drawing.Drawing2D.HatchBrush
B.   System.Drawing.Drawing2D.LinearGradientBrush
C.   System.Drawing.Drawing2D.PathGradientBrush
D.   System.Drawing.SolidBrush
E.   System.Drawing.TextureBrush
5.  ¿Qué tipo de línea sería el siguiente código de ejemplo dibujar?
// C#
Graphics g = this.CreateGraphics(); p = new Pen
Pen(Color.Red, 10);

P.StartCap = LineCap.plana; p.EndCap


=.ArrowAnchor LineCap; g.DrawLine(p, 50, 50,
400, 50);

A.  Una flecha que apunta hacia arriba.


B.   Una flecha apuntando hacia abajo
C.   Una flecha hacia la izquierda
D.   Una flecha orientada hacia la derecha
Lección 2: Trabajar con imágenes
A menudo los desarrolladores necesitan para mostrar, crear o modificar imágenes. .NET
Framework provee herramientas para trabajar con una variedad de formatos de imagen, lo que
le permite realizar muchas tareas comunes de edición de imágenes.

Después de esta lección, será capaz de:


■   describir el propósito de las      clases Image y Bitmap.
■   mostrar imágenes en formularios o controles PictureBox  objetos.
■   Crear una imagen nueva, agregar líneas y formas a la imagen y guardarla como
un archivo.
Lección Tiempo estimado: 30 minutos

Las      clases Image y Bitmap


 System.Drawing.Image clase abstracta le da la capacidad de crear, cargar,
modificar y guardar imágenes como archivos .bmp, .jpg y .tif. Algunas cosas útiles
que usted puede hacer con la  clase Image incluyen:
■   Crear un dibujo o gráfico, y guardar los resultados como un archivo de
imagen.
■   Utilice
el texto (como se describe en la lección 3) para añadir información de
copyright o marca de agua a una imagen.
■   Redimensionar imágenes JPEG de modo que consumen menos espacio y se
pueden descargar más rápido.
La  clase Image es abstracta, pero puede crear instancias  de la calificación con
el Image.FromFile (que acepta una ruta a un archivo de imagen como parámetro) y Image.de
-- Stream (que acepta un System.IO.Stream Object como parámetro) Métodos. También puede
utilizar dos clases que heredan de imagen: System.Drawing.Bitmap para imágenes fijas,
y Sys- tem.Drawing.Imaging.Metafile para imágenes animadas.
Mapa de bits es la clase más comúnmente utilizado para trabajar con imágenes nuevas o
existentes. Los diferentes constructores le permiten crear un mapa de bits a partir de
un archivo de imagen existente, o una secuencia, o para crear un mapa de bits en blanco de
una determinada altura y anchura. Bits contiene dos particularmente útiles los métodos
que  carece de imagen:
■   GetPixel   devuelve un  objeto Color describiendo un determinado  píxel en
la imagen. Un píxel es un único punto de color en la imagen, compuesta de un
rojo, verde y azul.
■   SetPixel   establece un píxel con el color especificado.
Sin embargo, la edición de imagen más complejos requiere que usted cree un  objeto Graphics
por call- ing Graphics.FromImage.

Cómo mostrar imágenes


Para mostrar una imagen guardada en el disco en un formulario, cargar
con Image.FromFile y crear un  control PictureBox, y luego usar la imagen para
definir controles PictureBox.Background-image. El siguiente código de ejemplo (que requiere
un formulario con una instancia del control PictureBox llamado pictureBox1) demuestra este
proceso:
/
/

C
#
I = Imagen Image.FromFile(@"C:\windows\Gone Fishing.bmp");
PictureBox1.BackgroundImage = i;

Asimismo, el siguiente código realiza la misma cosa usando la  clase Bitmap:


/
/

C
#
B = new Bitmap Bitmap(@"C:\windows\Gone Fishing.bmp");
PictureBox1.BackgroundImage = b;

Como alternativa, puede mostrar una imagen como fondo de un formulario o control mediante
el  método Graphics.DrawImage. Este método tiene 30 sobrecargas, así que tiene una amplia
variedad de opciones para especificar la ubicación y el tamaño de la imagen. El código
siguiente utiliza este método para establecer una imagen como fondo de un formulario,
independientemente de las dimensiones del formulario son:

/
/

C
#
Bitmap bm = new Bitmap(@"C:\WINDOWS\web\wallpaper\Azul.jpg"); Gráficos g
= this.CreateGraphics();
G.DrawImage(BM, 1, 1, this.Width, este.de altura);

Cómo crear y guardar imágenes


Para crear una imagen nueva en blanco, crear una instancia de la  clase Bitmap con uno de los
constructores que no requieren una imagen existente. A continuación, puede editarlo mediante
El  método Bitmap.SetPixel, o puede llamar a Graphics.FromImage y editar la imagen
utilizando los  métodos de dibujo de gráficos.
Para guardar una imagen, llamar Bitmap.Save. Este método tiene varias fáciles de entender
sobre- carga. Dos de las sobrecargas acepta un parámetro de
tipo System.Drawing.Imaging.Image- Formato, por lo que usted debe proporcionar una de las
siguientes propiedades para describir el tipo de archivo: BMP, EMF, Exif, GIF, JPEG,
MemoryBmp iconos, PNG, TIFF o WMF. Jpeg es el formato más común para fotografías y
GIF es el formato más común para los gráficos, capturas de pantalla y los dibujos.
Por ejemplo, el código siguiente crea un relleno de mapa de bits de 600-por-600, se crea
un  objeto Graphics basado en el mapa de bits, utiliza
los gráficos.FillPolygon y gráficos.DrawPolygon métodos para dibujar una forma en el mapa
de bits y, a continuación, se guarda en un archivo llamado bm.jpg en el directorio actual. Este
código puede ejecutarse como una aplicación de consola, y requiere que
el System.Drawing.Drawing2D y System.Drawing.Imaging espacios de nombres.
/
/

C
#
Bm = new Bitmap Bitmap (600, 600);
Gráficos g = Graphics.FromImage(BM);

B = nuevo pincel LinearGradientBrush(nuevo punto(1, 1), el nuevo punto (600, 600),


Color.Blanco, Color.Red);
Punto[] Puntos = new punto[]
{Nuevo Punto(10,
10)
Nuevo punto(77, 500),
el nuevo punto(590,
100), el nuevo punto
(250, 590), el nuevo
punto(300, 410)};

G.FillPolygon(B, puntos).
Bm.Save("bm.jpg", ImageFormat.jpeg);

Para editar una imagen existente, simplemente cambie el  constructor Bitmap en el ejemplo


anterior para cargar una imagen.

Cómo usar iconos


Los iconos son mapas de bits transparentes de tamaños específicos que son utilizados por
Windows para transmitir el estado. .NET Framework proporciona el estándar de 40-por-40
Sistema de iconos como propiedades de la  clase SystemIcons, incluyendo los iconos de
exclamación, información, y a la pregunta.
La forma más sencilla de agregar un icono a una forma o imagen es llamar a
los gráficos.  Graphics.DrawIconUnstretched DrawIcon o métodos. El código siguiente
produce el resultado mostrado en la Figura 6-8:
/
/

C
#
Graphics g = this.CreateGraphics();
G.DrawIcon(SystemIcons.Pregunta, 40, 40).

Figura 6-8  SystemIcons  proporciona acceso a iconos comunes que puede usar


para transmitir el estado
También puede editar los iconos de sistema o cargar iconos guardados utilizando los
constructores integrados en el icono de clase. Una vez que crea una instancia de la  clase,
llame al icono icon.ToBitmap al cre- ate un  objeto de mapa de bits que se pueden editar.

Laboratorio: Guardar un gráfico como una imagen


En este laboratorio, escribir código para guardar un  objeto de mapa de bits en el disco como
un archivo JPEG. Si se produce un problema de completar un ejercicio, los proyectos
terminados están disponibles en el CD en la carpeta de código.
    Ejercicio: Guardar un gráfico como una imagen
En este ejercicio, va a agregar código para guardar una imagen del gráfico circular
en el disco como un archivo.
1.  Copiar el capítulo06\lección2-ejercicio1-PieChart carpeta desde el CD a tu
disco duro y abrir la versión en C# o Visual Basic versión del PieChart
proyecto.
2.  Agregue código al  método saveButton_Click para pedir al usuario un nombre
de archivo, y el gráfico de tarta de escritura en el disco. Por simplicidad,
siempre guardar la imagen como un archivo JPEG. El siguiente código, que
exige que el  espacio de nombres System.Drawing.Imaging, es un ejemplo de
cómo hacerlo:
' VB
' Mostrar el cuadro de diálogo Guardar
Como saveDialog Dim = New SaveFileDialog saveDialog
SaveFileDialog.DefaultExt = ".jpg".
SaveDialog.Filter = "archivos JPEG (*.jpg)|*.jpg;*.jpeg|Todos los archivos (*.*)|*.*"

Si no (saveDialog.ShowDialog DialogResult.Cancel =)
' Guardar la imagen en el archivo especificado en formato JPEG
chart.Image.Save(saveDialog.FileName, ImageFormat.jpeg)
End If

// C#
// Mostrar el cuadro de diálogo Guardar
SaveDialog SaveFileDialog = new SaveFileDialog();
SaveDialog.DefaultExt = ".jpg".
SaveDialog.Filter = "archivos JPEG (*.jpg)|*.jpg;*.jpeg|Todos los archivos (*.*)|*.*".

Si (saveDialog.ShowDialog() != DialogResult.Cancel)
{
// Guardar la imagen en el archivo especificado en formato JPEG
chart.Image.Save(saveDialog.FileName, ImageFormat.jpeg);
}

3.  Ejecutar y probar la aplicación para verificar que funciona correctamente y


que puede ver el archivo guardado.

Resumen de la lección
■   las    clases Image y Bitmap le permiten editar o crear imágenes, y guardar
los resultados como un archivo.
■   Para mostrar una imagen en un conjunto de formularios Windows Forms,
cargar la imagen en una instancia de la    clase de imagen o mapa de bits,
crear una instancia del  control PictureBox, y luego utilizar la imagen o
el  objeto de mapa de bits para definir los controles PictureBox.Background-
image la propiedad.
■   para crear y guardar una imagen, crear un  objeto Bitmap, modificarla
utilizando un  objeto Graphics y, a continuación, llamar al  método Save.de
mapa de bits.
■   Paramostrar un icono, llame al
método Graphics.DrawIcon o Graphics.DrawIconUnstretched
Métodos utilizando una de las propiedades de la  clase SystemIcons.

Examen de la lección
Puede utilizar las siguientes preguntas para poner a prueba tus conocimientos de la
información Les- hijo 2 "Trabajo con imágenes." Las preguntas también están disponibles en
el CD complementario si prefiere revisarlos en forma electrónica.

Nota   Respuestas
Las respuestas a estas preguntas y explicaciones de por qué cada opción de
respuesta es correcta o no se encuentran en la sección "Respuestas" al final
del libro.

1.  Cuál de las siguientes clases se puede utilizar para mostrar una imagen JPEG a
partir de un archivo existente en un formulario? (Seleccione todos los que
correspondan).
A.  System.Drawing.Image
B.   System.Drawing.Bitmap
C.   System.Drawing.Imaging.metaarchivo
D.   System.Windows.Forms.Control PictureBox
2.  ¿Cómo se puede dibujar un borde negro alrededor de la imagen JPEG que
hayas guardado en el disco y, a continuación, guardar la imagen actualizada
de vuelta al disco?
A.  Crear un  objeto Graphics cargando la imagen JPEG desde el disco.
Dibujar el borde llamando Graphics.DrawRectangle. Por último, guarde
la imagen actualizada llamando Graphics.Guardar.
B.   Crear un  objeto Bitmap al cargar la imagen JPEG desde el disco.
Dibujar la- bor der llamando Bitmap.DrawRectangle. Por último, guarde
la imagen actualizada llamando Bitmap.Guardar.
C.   Crear un  objeto Bitmap  al cargar la imagen JPEG desde el disco. Crear
un  objeto Graphics llamando Graphics.FromImage. Dibujar el borde
llamando Graphics.DrawRectangle. Por último, guarde la imagen
actualizada llamando - mapa de bits.Save.
D.   Crear un  objeto Bitmap  al cargar la imagen JPEG desde el disco. Crear
un  objeto Graphics llamando Bitmap.CreateGraphics. Dibujar el borde
por call- ing Graphics.DrawRectangle. Por último, guarde la imagen
actualizada llamando - mapa de bits.Save.
3.  Qué formato debe elegir guardar una fotografía que podría ser inaugurada por
una amplia variedad de aplicaciones?
A.
ImageFormat.bm
p B.
ImageFormat.gif 
C.
ImageFormat.JP
EG D.
ImageFormat.pn
g
4.  Qué formato debe elegir guardar un gráfico circular que podría ser inaugurada
por una amplia variedad de aplicaciones?
A.
ImageFormat.bm
p B.
ImageFormat.gif 
C.
ImageFormat.JP
EG D.
ImageFormat.pn
g
Lección 3: formato de texto
A menudo los desarrolladores agregar texto a las imágenes para etiquetar objetos o crear
informes.  Esta lección describe cómo agregar texto con formato a las imágenes.

Después de esta lección, será capaz de:


■   Describir el proceso de creación de los objetos necesarios para agregar texto a
las imágenes.
■   Crear   objetos Font para satisfacer sus requisitos de tipo, tamaño y estilo.
■   Utilice Graphics.DrawString  para hacer anotaciones en imágenes con texto.
■   controlar el formato del texto.
Lección Tiempo estimado: 30 minutos

Cómo agregar texto a un gráfico


Puede agregar texto a las imágenes, mediante la creación de una instancia de la  clase
Graphics, de la misma forma que agrega objetos sólidos. A un alto nivel, siga estos pasos:
1.  Crear un  objeto Graphics, tal como se describe en las lecciones 1 y 2.
2.  Crear un  objeto Font.
3.  Opcionalmente, cree un  objeto Brush.
4.  Llame a Graphics.DrawString y especifique la ubicación del texto.

Mundo Real
Tony Northrup
Cuando no estoy codificación, estoy tomando fotos. Puedo vender las fotos en
la Web para cubrir el costo indignante de mi equipo de la cámara.
Lamentablemente, si bien tienen DRM (digital rights management) para
música y vídeo, nadie ha averiguado DRM para imágenes. Así, hasta que
alguien desarrolla una buena imagen sistema DRM, su mejor apuesta es
agregar entrometido y filigranas visibles copyright notificaciones a las
imágenes publicadas en la Web. Esto no va a detener a alguien de copiar su
pic- tures y violar los derechos de autor, pero el texto del copyright hace que
las imágenes más difíciles de usar.
Cómo crear un   objeto Font
La  clase Font ofrece 13 diferentes constructores. La forma más sencilla de crear un  objeto
Font es pasar el nombre de la familia de fuentes (como una cadena), el tamaño de la fuente
(como un integer o float) y estilo de fuente ( System.Drawing. propiedad FontStyle). Por
ejemplo, el siguiente constructor crea una Arial de 12 puntos en negrita:
/
/

C
#
Fuente f = new Font("Arial", 12, FontStyle.Bold);

También puede crear un nuevo  objeto Font utilizando FontFamily, como se muestra en el


siguiente código:
/
/

C
#
FontFamily ff = new FontFamily("Arial"); font f =
Nueva Fuente(ff, 12);

Si necesita leer el tipo de fuente a partir de una cadena, puede usar la   clase
FontConverter. Este no es el método preferido, sin embargo, porque utiliza una cadena para
describir un font es menos confiable. (Es menos confiable porque el compilador no puede
detectar errores o errores tipográficos). Por lo tanto, no se descubre un error en el nombre de
la fuente hasta un argumento en tiempo de ejecución- se produce la excepción. En el siguiente
ejemplo se crea una fuente Arial de 12 puntos:

/
/

C
#
Convertidor FontConverter = new FontConverter();
Fuente f = (Fuente)converter.ConvertFromString("Arial, 12 pt");

Cómo escribir texto


Después de crear un  objeto Font, necesita crear un  objeto Brush (como se describe en la
lección 1) para definir cómo el texto se llenará. Alternativamente, puede simplemente dar
un System.Drawing.Pinceles propiedad para evitar crear un  objeto Brush. Para terminar,
añadir el texto a la imagen, llamada Graphics.DrawString. El siguiente código dibuja texto del
cur- alquiler forma y produce el resultado que se muestra en la Figura 6-9:
'

/
/

C
#
Graphics g = this.CreateGraphics();
Fuente f = new Font("Arial", 40, FontStyle.Bold);
G.DrawString("Hello, World!", f, Pinceles.azul, 10, 10).
Figura 6-9  llamada Graphics.DrawString  para agregar texto a un   objeto Graphics.

Por supuesto, es mucho más fácil de añadir texto a un formulario utilizando  objetos Label.
Sin embargo, el gráfico- ics.DrawString también le permite agregar texto a las
imágenes y mapas de bits. Esto resulta útil para añadir información de copyright visible a una
imagen, añadir marcas de tiempo para las imágenes, gráficos y anotación.
Cómo controlar el formato del texto
.NET Framework permite controlar la alineación y la dirección del texto, utilizando la  clase
StringFormat. Después de crear y configurar un  objeto StringFormat, puede proporcionar
el  método DrawString de Graphics.para controlar la forma en que el texto está formateado.
Los miembros más importantes de la  clase StringFormat son:
■ La   alineación   obtiene o establece la alineación horizontal del texto. Las
opciones posibles son:
❑   StringAlignment.Center   centra el texto horizontalmente
❑   StringAlignment.Cerca de   Alinea el texto a la izquierda
❑   StringAlignment.Ahora   alinea el texto a la derecha
■   FormatFlags   obtiene o establece una  enumeración StringFormatFlags
que contiene la información de formato- ting. Las posibles opciones  son
StringFormatFlags
❑   DirectionRightToLeft   se muestra el texto de derecha a izquierda.
❑   DirectionVertical   texto alineado verticalmente.
❑   DisplayFormatControl   caracteres de control como la marca de
izquierda a derecha se muestran en la salida con un representante de los
glifos.
❑   FitBlackBox   partes de caracteres permitidos para la cadena del
diseño voladizo del rectángulo. Por defecto, los caracteres se reubica
para evitar cualquier pendiente.
❑   LineLimit   sólo líneas enteras están establecidos en el formato del
rectángulo. Por defecto, el diseño sigue hasta el final del texto o hasta
que no haya más líneas son visibles como resultado de recortes, lo que
ocurra primero. Observe que la opción predeterminada

Permitir la configuración de la última línea para ser parcialmente oscurecida por un rectángulo
de formato que no es un múltiplo entero de la altura de la línea. Para garantizar que sólo se
observan líneas enteras, especificar este valor y tener cuidado para proporcionar un formato de
ángulo rect- al menos tan alto como la altura de una línea.
❑   MeasureTrailingSpaces   incluye el espacio final al final de cada
línea.Por defecto, el rectángulo delimitador devuelto por el  método
MeasureString Excluye el espacio al final de cada línea. Establecer este
indicador para incluir ese espacio en la medición.
❑   NoClip   sobresaliendo por partes de pictogramas y texto sin envolver
llegando- lado el rectángulo de formato están autorizados a mostrar. De
forma predeterminada, todo el texto y el glifo partes alcanzar fuera del
rectángulo de formato se recortan.
❑   NoFontFallback   Suplencia a fuentes alternas para caracteres no
admitidos en la fuente solicitada está desactivada. Cualquier faltan
caracteres  se muestran con las fuentes que faltan en el glifo,
normalmente una plaza abierta.
❑   NoWrap  entre líneas de ajuste de texto dentro de un rectángulo de
formato cuando está desactivado. Esta bandera está implícita cuando se
pasa de un punto en lugar de un rectan- gle, o cuando el rectángulo
especificado tiene una longitud de línea cero.
■   LineAlignment   obtiene o establece la alineación de texto vertical. Las
opciones posibles son:
❑   StringAlignment.Center   centra el texto verticalmente
❑   StringAlignment.Cerca de   Alinea el texto a la parte superior
❑   StringAlignment.Ahora   alinea el texto a la parte inferior
■   Recorte   obtiene o establece la  enumeración StringTrimming para
este StringFormat
Objeto. Las opciones posibles son:
❑   carácter     especifica que el texto se ajusta al carácter más cercano.
❑   EllipsisCharacter   Especifica que el texto está cortado con el
carácter más cercano, y los puntos suspensivos se inserta al final de una
línea recortada.
❑   EllipsisPath   El centro se retira de líneas recortadas y sustituido por
puntos suspensivos. El algoritmo mantiene como mucho del último
segmento delimitado por barras de la línea como sea posible.
❑   EllipsisWord   Especifica que el texto se recorta a la palabra más
cercana, y los puntos suspensivos se inserta al final de una línea
recortada.
❑   No  especifica ningún recorte.
❑   Palabra   Especifica que el texto se recorta más cercano a la palabra.
El código siguiente muestra el uso de la  clase StringFormat y produce el resultado que se
muestra en la Figura 6-10:
// C#
Graphics g = this.CreateGraphics();

// Crear un nuevo rectángulo.


Rectángulo R = new Rectangle(nuevo punto(40, 40), nuevo tamaño(80, 80).

// Construir 2 nuevos objetos StringFormat


StringFormat f1 = new StringFormat(StringFormatFlags.NoClip); StringFormat f2 = new
StringFormat(F1).

// Establecer las propiedades de alineación y LineAlignment


// Ambos objetos StringFormat para diferentes valores.
f1.LineAlignment = StringAlignment.Cerca; f1.Alineación =
StringAlignment.Centro; f2.LineAlignment = StringAlignment.Centro;
f2.Alineación = StringAlignment.Lejos.
F2.FormatFlags = StringFormatFlags.DirectionVertical;

// Dibujar el rectángulo delimitador y una cadena para cada


// Objeto StringFormat.
g.DrawRectangle(Pens.negro, r).
G.DrawString("Format1", este.Font, Brushes.Red, (RectangleF)r, f1).
G.DrawString("Format2", este.Font, Brushes.Red, (RectangleF)r, f2).

Figura 6-10  Utilice StringFormat  para controlar la alineación y dirección del texto

Laboratorio: Agregar texto a una imagen


En este laboratorio, agregar un logotipo de copyright a una imagen antes de grabarla en el
disco, y actualizar un gráfico para mostrar una leyenda. Si usted encuentra un problema
completando una exer- cise, los proyectos terminados están disponibles en el CD en la carpeta
de código.
    Ejercicio 1: Agregar un aviso de copyright en una imagen
1.  Copiar el capítulo06\lección3-ejercicio1-PieChart carpeta desde el CD a tu
disco duro y abrir la versión en C# o Visual Basic versión del PieChart
proyecto. Alternativamente, usted puede continuar trabajando en el proyecto
que creó en la Lección 2.
2.  Sin modificar el gráfico  PictureBox, agregar un aviso de copyright de la
imagen guardada. El aviso deberá decir "Copyright 2006, Contoso, Inc." y
aparecerá en la esquina superior izquierda. El código siguiente podría sustituir
el contenido anterior de la  instrucción if:

// C#
Si (saveDialog.ShowDialog() != DialogResult.Cancel)
{
// Definir el mapa de bits, gráficos, Font y Brush para derechos de autor LOGO
Bitmap (Mapa de bits bm =)chart.imagen;
Graphics g = Graphics.FromImage(BM); font f =
new Font("Arial", 12).
Cepillo b = new SolidBrush(Color.Blanco);

// Agregar el texto de copyright


G.DrawString("Copyright 2006, Contoso, Inc.", f, b, 5, 5).

// Guardar la imagen en el archivo especificado en formato JPEG


BM.Save(saveDialog.FileName, ImageFormat.jpeg);
}

3.  Ejecute la aplicación y guardar una imagen. Observe que el aviso de copyright son
difíciles de leer cuando se superpone a la imagen. Una manera de resolver esto es
dibujar el texto con un color contrastante detrás de la cadena, y el desplazamiento
en un píxel en cada di- ción. Por ejemplo, el siguiente código añade un fondo
negro al blanco, texto de copyright:
// C#
// Definir el mapa de bits, gráficos, Font y Brush para derechos de autor LOGO
Bitmap (Mapa de bits bm =)chart.Imagen gráfica; g
= Graphics.FromImage(BM); font f = new
Font("Arial", 12).

// Crear el texto en primer plano el cepillo


Cepillo b = new SolidBrush(Color.Blanco);
// Crear el pincel de texto procedencia
Cepillo bb = new SolidBrush(Color.Black);

// Agregar el fondo de texto de copyright


Cadena ct = "Copyright 2006, Contoso, Ltd.";
g.DrawString(ct, f, bb, 4, 4); g.DrawString(ct, f, bb, 4, 6);
g.DrawString(ct, f, bb, 6, 4); g.DrawString(ct, f, bb, 6, 6).

// Agregar el texto de copyright primer plano


g.DrawString(ct, f, b, 5, 5).

// Guardar la imagen en el archivo especificado en formato JPEG


BM.Save(saveDialog.FileName, ImageFormat.jpeg);

4.  Vuelva a ejecutar la aplicación y guardar la imagen de nuevo. Observe que


cuando el ejemplar- derecho texto se superpone el gráfico de tarta, el texto
tiene un fondo negro, lo que hace que sea fácil de leer.
    Ejercicio 2: Agregar una leyenda al gráfico de tarta
En este ejercicio, va a modificar el  método drawPieChart creado en ejercicios anteriores para
dividir la imagen en dos partes. La mitad izquierda se mostrará el gráfico de tarta, y la mitad
derecha mostrará una leyenda que muestra el color de cada segmento del gráfico circular, el
nombre de ese segmento y el valor.
1.  Copiar el capítulo06\lección3-ejercicio2-PieChart carpeta desde el CD a tu
disco duro y abrir la versión en C# o Visual Basic versión del PieChart
proyecto. Alternativamente, usted puede continuar trabajando en el proyecto
creado en el ejercicio 1.
2.  En primer lugar, modificar el  método drawPieChart de modo que el gráfico
circular sólo consume la mitad izquierda de la imagen. La siguiente
modificación logra esto:
// C#
// Defina el rectángulo que va a utilizar el gráfico de tarta
// Usar sólo la mitad del ancho para dejar espacio para la leyenda
Rectangle rect = new Rectangle(1, 1, (s.Width/2) - 2, s.altura - 2);

3.  A continuación, en la mitad derecha de la imagen, dibuje un cuadro negro con


un fondo blanco.
El código siguiente muestra una forma de hacerlo:
// C#
// Defina el rectángulo que utilizará la leyenda
Punto lRectCorner = nuevo punto((s.Width / 2) + 2, 1).
Tamaño lRectSize = nuevo tamaño(s.Width - (s.Width / 2 - 4, s.altura - 2); Rectángulo lRect = new
Rectangle(lRectCorner, lRectSize);

// Dibujar un cuadro negro con un fondo blanco para la leyenda. Cepillo lb = new
SolidBrush(Color.Blanco);
Lp = pluma Pluma nueva(Color.Black, 1);
g.FillRectangle(lb, lRect); g.DrawRectangle(lp,
lRect);

4.  Calcular los valores necesarios para dibujar cada elemento de


leyenda, incluyendo:
❑ El   número de píxeles verticales para cada elemento de leyenda
❑ El   ancho del cuadro de leyenda
❑ la   altura del cuadro de leyenda
❑ El   espacio intermedio entre elementos de leyenda
❑ El   borde izquierdo del texto de la leyenda
❑ El   ancho del texto de la leyenda
El código siguiente muestra una forma de hacerlo:
// C#
// Determinan el número de píxeles verticales para cada elemento de leyenda int = (vert
lRect.Height - 10) / elements.Count.

// Calcular la anchura del cuadro de leyenda, ya que el 20% del total de ancho de leyenda int = lRect
legendWidth.Width / 5.
// Calcular la altura de la caja de la leyenda como el 75% de la altura de elemento de leyenda
legendHeight int = (int) (vert * 0,75);

// Calcular un espacio intermedio entre elementos int


buffer = (int)(vert - legendHeight) / 2.

// Calcular el borde izquierdo del texto de la leyenda


Int = lRectCorner textX.X + + legendWidth buffer * 2.

// Calcular el ancho del texto de la leyenda


Int = lRect textWidth.Width - (lRect.Width / 5) - (2); * buffer

5.  Por último, a través del bucle PieChartElements objetos y dibujar cada ele-


mento de leyenda. El siguiente ejemplo muestra un  bucle foreach separadas
por simplicidad; de cualquier modo, para una mayor eficiencia, esto debe
combinarse con el  bucle foreach existente:
// C#
// Inicio La leyenda de cinco píxeles desde la parte superior del rectángulo int currentVert
= 5;
Int legendColor = 0.

(Foreach PieChartElement e en elementos)


{
// Crear un pincel con un gradiente de Niza
Rectángulo thisRect = new Rectangle(lRectCorner.X + buffer, buffer, legendWidth
currentVert +, legendHeight);
B = nuevo pincel LinearGradientBrush(thisRect, colores[legendColor++], Color.Blanco,
(float)45).

// Dibujar el cuadro de leyenda relleno y el borde


g.FillRectangle thisRect(b); g.DrawRectangle(lp,
thisRect);

// Defina el rectángulo de texto


RectangleF textRect = new Rectangle(textX, currentVert + buffer, textWidth, legendHeight);

// Defina la fuente para el texto


Font tf = new Font("Arial", 12).

// Crear el texto en primer plano el cepillo


Cepillo tb = new SolidBrush(Color.Black);

// Definir la alineación vertical y horizontal para el texto


StringFormat sf = new StringFormat(); sf.Alineación =
StringAlignment.Cerca; sf.LineAlignment =
StringAlignment.Centro;

// Dibujar el texto
G.DrawString(e.name + " " + e.value, TF, TB, textRect, sf);

// Incrementar la actual ubicación vertical currentVert += vert;


}

6.  Ejecutar la aplicación para comprobar que funciona. Con el añadido de leyenda,


más del texto de copyright ahora se superpone con el gráfico de sectores, lo que
demuestra la eficacia del fondo negro.
Resumen de la lección
■   Para agregar texto a un gráfico, crear un  objeto Graphics, cree un  objeto
Font, opcionalmente crear un  objeto Brush, y luego llamar al  método
Graphics.DrawString.
■   Para crear un  objeto Font, pase el nombre de la familia de fuentes, tamaño de
fuente y estilo de fuente.
■   Escribir texto llamando al  método Graphics.DrawString. El  método
DrawString requiere un  objeto Font, un  objeto Brush que especifica el color
del texto y la ubicación para dibujar el texto.
■   Utilice la  clase StringFormat para controlar el formato del texto. Puede
utilizar esta clase para cambiar la dirección del texto, o para cambiar la
alineación del texto.

Examen de la lección
Puede utilizar las siguientes preguntas para poner a prueba su conocimiento de la información
en la lección 3, "Formateo de texto." Las preguntas también están disponibles en el CD
complementario si prefiere revisarlos en forma electrónica.

Nota   Respuestas
Las respuestas a estas preguntas y explicaciones de por qué cada opción de
respuesta es correcta o no se encuentran en la sección "Respuestas" al final
del libro.

1.  ¿Cuáles son los pasos para agregar texto a una imagen?


A.  Crear un  objeto Graphics y un  objeto String. A continuación, llame
a string.Draw.
B.   Crear un  objeto Graphics, un  objeto Font, y un  objeto Brush. A
continuación, llame a gráficos
.
Cordón.
C.   Crear un  objeto Graphics, un  objeto Font, y un  objeto Pen. A
continuación, llame a gráficos
.
Cordón.
D.   Crear un  objeto de mapa de bits, un  objeto Font, y un  objeto Brush. A
continuación, llamar bitmap
.
Cordón.
2.  Cuál de las siguientes es una clase que se necesita para crear una instancia para
especificar que una cadena debe estar centrado cuando dibujan?
A.  StringFormat
B.   StringAlignment
C.   FormatFlags
D.   LineAlignment
3.  Cuál de los siguientes comandos provocaría una cadena a ser alineado a la
izquierda?
A.  StringFormat.LineAlignment = cerca de 
B.   StringFormat.LineAlignment = lejos 
C.   StringFormat.Alineación = cerca
D.   StringFormat.Alineación = lejos

Repaso del capitulo


A nuevas prácticas y reforzar las habilidades aprendidas en este capítulo, puede
com- pleta las siguientes tareas:
■   Revisar el resumen del capítulo.
■   Revisar la lista de términos clave que se introdujo en este capítulo.
■   Complete los casos. Estos escenarios configurar situaciones del mundo real
involv- ción de los temas de este capítulo y preguntarle a crear soluciones.
■   completar las prácticas sugeridas.
■   tomar un examen de práctica.
Resumen del capítulo
■   para dibujar gráficos, crear dos objetos: un  objeto Pen y un  objeto Graphics.
Utilice el  objeto Pen para definir el color y el ancho del dibujo. El  objeto
Graphics proporciona métodos para dibujar líneas y formas. Para llenar
formas, utilice un  objeto Brush con un  objeto Graphics.
■   Paratrabajar con imágenes, utilice las    clases Image y Bitmap. Para editar
imágenes, crear un
 Objeto Graphics basado en la imagen o el  objeto de mapa de bits.
■   Para agregar texto a una imagen o gráfico, crear     los objetos Brush y Font.
Luego  llame
Graphics.DrawString. Para dar formato al texto, utilice la  clase StringFormat.

Términos clave
¿ Sabes lo que significan estos términos clave? Puede verificar sus respuestas mediante la
búsqueda de los términos en el glosario al final del libro.
■   Bitmap

■   Cepillo

■   Graphics

■   Pen

Casos
En los siguientes casos, podrá aplicar lo que ha aprendido en este capítulo. Usted puede
encontrar las respuestas a estas preguntas en la sección de "respuestas" al final de este libro.
Caso práctico 1: Elección de técnicas gráficas
Usted es un desarrollador de aplicaciones internas de Contoso, Inc. Usted y su equipo son
responsables de una aplicación interna Contoso usa para seguimiento de pedidos y gestión de
inventario. Recientemente, una persona en el grupo de relaciones públicas pidió a su director
para discutir los cambios que son necesarios para la apariencia y el funcionamiento de sus
aplicaciones internas. Su administrador delegado la discusión para usted.

Entrevistas
La siguiente es una lista de personal de la empresa entrevistados y sus
declaraciones:
■   Representante de Relaciones Públicas  "Estamos en el proceso de re-
inventar nuestro corpo- tipo imagen. Hay muchos elementos a nuestra nueva
imagen, incluidas las imágenes, el uso de determinados colores y fuentes y
logotipos. Para la gestión de marcas coherente tanto inter-
Nally y externamente, todo lo que producimos  debe ajustarse a estas normas.
Concretamente, en nuestra aplicación interna, me gustaría mostrar una
pantalla de bienvenida que muestra una foto  de nuestra sede corporativa con
nuestro logo en la esquina superior izquierda. Cuando el programa se carga,
me gustaría que el logotipo aparezca en la parte superior izquierda cor- ner.
Además, todas las fuentes de la aplicación debe ser Arial 12. Oh, legal todavía
está en el proceso de aprobar nuestro logotipo, así que es probable que
cambie. Puedo proporcionarle el logotipo actual y la imagen de nuestra sede
en formato .JPEG".
■   IT Manager  "Estos PR chicos cambiará el logotipo y la imagen alrededor
de una docena de veces antes de decidir lo que desea utilizar, por lo que le
sugiero almacena las imágenes como archivos en el directorio de instalación y
cargarlos dinámicamente con
La aplicación. No quiero dar nada por descontado, incluido el tamaño de las
imágenes".
Preguntas
Responda las siguientes preguntas para el administrador:
1.  La foto de la Sede es de 6 megapíxeles, y es demasiado grande para la pantalla
de bienvenida. ¿Cómo se puede cambiar su tamaño?
2.  ¿Cómo se puede mostrar el logotipo de la empresa a través de la fotografía de
la sede corporativa?
3.  Si el tamaño de los cambios en el logotipo corporativo, ¿cómo se puede
garantizar que no cubra la totalidad de la foto de la sede y sólo consume la
esquina superior izquierda de la imagen?
4.  ¿Cómo puede cambiar las fuentes utilizadas en la aplicación?

Caso práctico 2: Creación de gráficos simples


Usted es un desarrollador en el departamento de TI de Fabrikam, Inc. recientemente, ha
lanzado la versión 1.0 de ventas, una aplicación interna utilizada por el equipo de ventas para
realizar un seguimiento de los pedidos. Las ventas reemplazaron sus anteriores métodos de
seguimiento basados en papel, y todos en el equipo de ventas ha sido satisfecho con la
aplicación.
Ahora el Vicepresidente de ventas desea discutir las peticiones para la próxima ver- sión de su
aplicación.

Entrevistas
La siguiente es una lista de personal de la empresa entrevistados y sus
declaraciones:
■   Vicepresidente de Ventas  "La aplicación de seguimiento de ventas que
usted escribió es excelente, realmente ha mejorado la eficiencia de nuestra
organización de ventas. Ahora que tenemos datos almacenados en una base de
datos, me gustaría la posibilidad de acceso para proporcionar más
Una visión de la forma en que diferentes equipos de ventas se realizan a lo
largo del tiempo. Quiero ser capaz de ver un gráfico de línea o un gráfico de
barras que muestra el rendimiento de ventas para cada quar- ter del año, con el
rendimiento del equipo en cada una de un color diferente. También necesito
ser capaz de guardar el gráfico para poder agregarlo a una presentación que
tengo que hacer antes de que la junta de directores".

Preguntas
Responda las siguientes preguntas para el administrador:
1.  ¿Qué tipo de control se puede utilizar para mostrar un gráfico en un formulario
Windows Forms de apli- cación?
2.  ¿Qué método se utiliza para dibujar un gráfico de línea?
3.  ¿Qué método se utiliza para dibujar un gráfico de barras?
4.  ¿Cómo guardar el gráfico a un archivo?

Prácticas recomendadas
Que le ayudarán a dominar la "Mejora de la interfaz de usuario de una aplicación de .NET
Framework usando el  espacio de nombres System.Drawing" objetivo del examen, complete
las siguientes tareas.
Mejorar la interfaz de usuario de una aplicación de .NET
Framework El uso de pinceles, lápices, colores y fuentes
Para esta tarea, deberá completar las prácticas de al menos 1 a 3 para ganar experiencia en el
uso de pinceles, colores y fuentes. Si desea un conocimiento en profundidad del uso de
plumas, com- pleta Práctica 4.
■ La   Práctica 1  Cree una aplicación de formularios Windows Forms para
demostrar los diferentes gráficos.SmoothingMode técnicas disponibles.
Dibuje un círculo, y mostrar el nombre de la actual  SmoothingMode. Cada
cinco segundos, vuelva a dibujar el círculo, y
Mostrar el nuevo SmoothingMode. Examine los bordes del círculo con las
dife- rentes SmoothingMode ajustes.
■ La   Práctica 2  dibujar un círculo sólido en un formulario y cambiar el color
cada dos segundos mientras se muestra el nombre del color en la parte inferior
del formulario.
■ La   Práctica 3  dibujar un círculo sólido en un formulario y cambiar el
cepillo cada 5 seg.- productos de limpieza abrasivos de ningún tipo mientras
se muestra el nombre de la brocha en la parte inferior del formulario.
■ La   Práctica 4  Crear una aplicación que utiliza
la pluma.DashOffset y Pen.DashPat- tern propiedades para definir un modelo
de guión personalizado.

Mejorar la interfaz de usuario de una aplicación de .NET


Framework Uso de gráficos, imágenes, mapas de bits e iconos
Para esta tarea, se deben completar las tres prácticas para adquirir experiencia trabajando con
imágenes en escenarios del mundo real.
■ La   Práctica 1  Crear una aplicación de Windows Forms que le permita
navegar por las fotos guardadas en el ordenador.
■ La   Práctica 2  Crear una aplicación que crea 80-por-80-pixel miniaturas de
todas las imágenes guardadas en una carpeta.
■ La   Práctica 3  Crear una aplicación que lee las imágenes de una carpeta, se
agrega una copia- Logotipo de derecho a la imagen, y guarda las imágenes en
una segunda carpeta.

Mejorar la interfaz de usuario de una aplicación de .NET


Framework Utilizar las formas y tamaños.
Para esta tarea, se deben completar un mínimo de prácticas 1 y 2. Si desea un conocimiento en
profundidad de las formas de dibujo, completar la Práctica 3.
■ La   Práctica 1  Crear una aplicación que le permite dibujar polígonos
haciendo clic en un formulario. Cada vez que el usuario hace clic en el
formulario, agregue un nuevo punto para el polígono en la ubicación en que se
hace clic.
■ La   Práctica 2  Agregar una serie de rectángulos generados aleatoriamente
en una matriz y, a continuación, llamar a Graphics.DrawRectangles para
mostrarlos.
■   Práctica 3.  Crear un método para dibujar un gráfico de barras que es
similar en función a la
DrawPieChart método utilizado en este capítulo.

Tomar un Test de práctica


Los tests de práctica en este libro's Companion CD ofrecen muchas opciones. Por ejemplo,
puede hacerse la prueba en un solo examen objetivo, o puede probar usted mismo en todos los
exámenes de certificación 70-536 contenido. Puede configurar la prueba para que simula
cuidadosamente la expe- riencia de tomar un examen de certificación, o puede configurarlo en
modo de estudio, de modo que usted puede mirar las respuestas correctas y explicaciones
después de responder a cada pregunta.

Más info  práctica de pruebas


Para obtener más información acerca de la prueba de la práctica todas las
opciones disponibles, consulte la sección "Cómo usar los tests de práctica", sec-
ción en la introducción de este libro.

Capítulo 7

Rosca
Rosca es un concepto importante en el desarrollo de software. La idea básica detrás de
subprocesos es para realizar varias operaciones simultáneamente. Cada una de estas oper-
nizaciones puede ser pensado como un subproceso independiente de la lógica. La mayoría de
las operaciones tienen tiempos de inactividad, cuando una operación está a la espera de que
ocurra algo (por ejemplo, a la espera de una respuesta de un servidor Web o esperando un
recurso a ser disponible). Con rosca, usted puede tener el procesador  o procesadores de una
máquina con- tinuar a hacer otras tareas durante ese tiempo.
Es cada vez más común para ejecutar código en las máquinas que tengan múltiples
procesadores. Cuando se escribe una aplicación que no utilice los múltiples subprocesos, su
aplicación es wast- ing estos procesadores adicionales. Mediante el sistema de enhebrado en el
.NET Framework, puede crear aplicaciones multiproceso robusto y fiable.

Objetivos del examen en este capítulo:


■   Desarrollar
aplicaciones .NET Framework multiproceso. (Consulte la
sección System.Threading
Espacio de nombres)
❑    clase Thread
❑     clase ThreadPool
❑ El    delegado ThreadStart y  delegado ParameterizedThreadStart
❑   Timeout clase,  clase
Timer,  delegado TimerCallback, función
WaitCallback delegado,
WaitHandle clase y  delegado
WaitOrTimerCallback
❑   ThreadExceptionEventArgs clase y  clase
ThreadExceptionEventHandler
❑   ThreadState y enumeración  enumeración ThreadPriority
❑   ReaderWriterLock class

❑    clase AutoResetEvent y ManualResetEvent class


❑     interfaz IAsyncResult (consultar el espacio de nombres System)
❑   EventWaitHandle clase,  clase, SendOrPostCallback
RegisteredWaitHandle del delegado y  delegado IOCompletionCallback
❑   enclavarse
 clase,  estructura NativeOverlapped y  clase superpuesta
❑   ExecutionContext clase,  clase, HostExecutionContext
HostExecutionContext- clase Manager y  delegado ContextCallback
❑   LockCookie  estructura, la   clase Monitor,  clase Mutex, y  clase
Semaphore
Mundo Real
Shawn Wildermuth
He utilizado el sistema de enhebrado en .NET en muchos proyectos que he
trabajado. Si yo estaba usando para un trabajo de la cola con
el ThreadPool para acelerar un proceso de larga duración o para ejecutar un
subproceso en segundo plano en una aplicación Microsoft Windows Forms
para permitir una mayor facilidad de uso para una aplicación de subprocesos,
ha sido una herramienta fundamental en mi caja de herramientas.

Las lecciones de este capítulo:


■   Lección 1: Crear subprocesos . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . 369
■ La   Lección 2: Intercambio de datos. . . . . . . . . .. . . . . . . . . . . . . . . . . 387
■ La   Lección 3: El modelo de programación asíncrona. . . . . . . . . . . . 412

Antes de empezar
Para completar las lecciones de este capítulo, debe estar familiarizado con
Microsoft Visual Basic o en C# y se sienten cómodos con las siguientes tareas:
■   Crear una aplicación de consola en Microsoft Visual Studio, usando Visual
Basic o C#.
■   Sistema de agregar referencias a bibliotecas de clases para un proyecto.
■   Crear archivos de texto.
■   Agregar eventos en el registro de eventos.
Lección 1: Crear subprocesos
Los subprocesos son la base de aplicaciones de alto rendimiento. En .NET Framework, el  espacio
de nombres System.Threading contiene los tipos que se utilizan para crear y gestionar múltiples
subprocesos en una aplicación.

Después de esta lección, será capaz de:


■   Crear subprocesos para hacer trabajar al mismo tiempo.
■   Iniciar y participar en las roscas.
■   anular los subprocesos.
■   Utilice las regiones críticas.
Lección Tiempo estimado: 20 minutos

Hilos sencillos
Para empezar a trabajar con hilos, debe familiarizarse con la  clase Thread. Esta clase ejemplifica
un único subproceso. La  clase Thread se utiliza para crear e iniciar subprocesos. La  clase Thread
más importante de propiedades y métodos se describen en la Tabla 7-1 y la Tabla 7-2,
respectivamente.
Tabla 7-1  Thread  Propiedades

Nombre Descripción
IsAlive  obtiene un valor que indica que el subproceso actual se está
ejecutando actualmente.
IsBackground  obtiene o establece si el subproceso se ejecuta como un
subproceso en segundo plano.
IsThreadPoolThread  obtiene si este es un subproceso del grupo de subprocesos.
ManagedThreadId  obtiene un número para identificar el subproceso actual. No
es el mismo que el ID del hilo del sistema operativo.
Nombre  obtiene o establece un nombre asociado con la
rosca. Prioridad  obtiene o establece la prioridad del
subproceso.     ThreadState ThreadState obtiene el valor de la
rosca.
Tabla 7-2    Métodos de rosca

Nombre Descripción
Anular  plantea una excepción ThreadAbortException en la
rosca para indicar que la rosca debe ser anulada.
   Plantea una interrupción ThreadInterruptedException cuando
un subproceso se encuentra en un estado de bloqueo
(ThreadState.WaitSleepJoin). Si el subproceso nunca
se bloquea, la interrupción no ocurre nunca.
Unirse   bloquea el subproceso de llamada hasta que el
subproceso termina.
Reanudar la   obsoleta. No utilice.
Inicio  establece un hilo a programarse para su ejecución.
Suspender  deprecated. No utilice.

Además de las propiedades y métodos de una instancia de una  clase, el hilo de rosca también


tiene propiedades y métodos estáticos. Estos se detallan en la Tabla 7-3 y la Tabla 7-4.
Tabla 7-3   subproceso estático  Propiedades

Nombre Descripción
   Obtiene la actual CurrentContext ThreadContext objeto relacionado con
el subproceso actual
CurrentPrincipal  Obtiene y establece el usuario asociado con el subproceso
actual
CurrentThread  obtiene el hilo de ejecución actual

Tabla 7-4   subproceso estático  métodos

Nombre Descripción
BeginCriticalRegion  utilizada para notificar al host que la ejecución de código
no puede ser anulada de forma segura. Anulando una
rosca entre un gion y EndCriticalRegion
BeginCriticalRe- podría dejar un AppDomain en un
estado inseguro.
EndCriticalRegion  utilizada para notificar al host que ha llegado al final de
una región crítica.
Tabla 7-4   subproceso estático  métodos

Nombre Descripción
GetDomain Obtiene el AppDomain asociado con el subproceso de ejecución
actual.
GetDomainID Obtiene un identificador único para el AppDomain asociado con
el subproceso en ejecución actualmente.
ResetAbort   cancela una  solicitud de anulación para el subproceso en ejecución
actualmente.
Dormir  bloquea el subproceso actual para una determinada cantidad de
tiempo.
Renuncia a la ejecución a otros subprocesos para permitirles
trabajar.
SpinWait  bloquea el subproceso actual durante un cierto número de
iteraciones.
No renuncie a la ejecución a otros subprocesos.
VolatileRead  lee la última versión de un valor de campo independientemente
del procesador en un entorno multiprocesador escribió el valor.
VolatileWrite  escribe un valor de campo inmediatamente para asegurarse de
que está disponible para todos los procesadores.

Además de la  clase Thread, la  enumeración ThreadState es crucial en la comprensión de cómo


trabajar con  objetos Thread. La Tabla 7-5 detalla los valores de la  enumeración ThreadState.
Tabla 7-5    enumeración ThreadState

Nombre Descripción
Anulada  la rosca se ha detenido.
AbortRequested  el hilo ha sido solicitada para abortar, pero la interrupción aún
está pendiente y no ha recibido la excepción
ThreadAbortException.
Antecedentes  El subproceso se ejecuta como un subproceso en segundo plano.
Ejecuta   el subproceso ha comenzado.
Detenido  el subproceso se ha detenido.
   La rosca StopRequested está siendo solicitada para detener. Sólo para uso interno.
Tabla 7-5    enumeración ThreadState

Nombre Descripción
Suspende  el subproceso ha sido suspendida. Apoyado por
compatibilidad con versiones anteriores, pero
debido suspender/reanudar no debería ser utilizado,
este estado no debería usarse.
   La rosca SuspendedRequested ha solicitado que se suspenda. Apoyado por
compatibilidad con versiones anteriores, pero
debido suspender/reanudar no debería ser utilizado,
este estado no debería ser utilizado Tampoco.

No comenzadas  el hilo ha sido creado, pero el  método


Thread.Start no ha sido llamado.
   Se bloquea el subproceso WaitSleepJoin como llamar
a Monitor.Wait, Thread.Sleep, o Thread.Join.

Crear un subproceso
Para crear e iniciar un nuevo subproceso, siga estos pasos:
1.  Crear un método que  no toma argumentos y no devuelve ningún dato (por
ejemplo, utilice el  tipo de valor devuelto void en C#). Este método debería
tener un aspecto parecido a este:
' VB
Shared Sub SimpleWork()
Console.WriteLine("Subproceso: {0}", Thread.CurrentThread.ManagedThreadId) End Sub

// C#
Static void SimpleWork()
{
Console.WriteLine("Subproceso: {0}", Thread.CurrentThread.ManagedThreadId);
}

2.  Crear un nuevo  delegado ThreadStart, y especifique el método creado en el


paso 1.
3.   Crear un nuevo  objeto Thread, especificando el  objeto ThreadStart
creado en el paso 2.
4.  Llame Thread.Start para iniciar la ejecución de la nueva rosca. El código se
terminan buscando algo como esto:
// C#
ThreadStart operación = nuevo ThreadStart(SimpleWork);

// Crea, pero no se inicia un nuevo subproceso


Rosca theThread = new Thread(operación);
// Comienza el trabajo en un nuevo subproceso
theThread.Start();

Cuando  se llama al método Start, el  método se llama SomeWork en un nuevo subproceso y


el subproceso ejecuta hasta que termine el método. En este ejemplo, nuestro  método
SimpleWork escribe la frase "hilo" y muestra la  propiedad ManagedThreadId. Esta propiedad
es un valor numérico que se asigna a cada subproceso. Posteriormente, se utilizará este valor
para ver lo que se está llevando a cabo el trabajo en qué hilo.

Mediante múltiples subprocesos


Un escenario más probable de este caso simple es aquel en el que usted querrá crear varios
subprocesos para hacer el trabajo. Por ejemplo, podemos cambiar el ejemplo mostrado
anteriormente para crear varios subprocesos y empezar a todos en el mismo trabajo:

// C#
ThreadStart operación = nuevo ThreadStart(SimpleWork);

(Int x = 1; x <= 5; ++x)


{
// Crea, pero no se inicia un nuevo subproceso
Rosca theThread = new Thread(operación);

// Comienza el trabajo en un nuevo subproceso


theThread.Start();
}
Esto ejecuta el trabajo en cinco subprocesos separados, como al mismo tiempo como su
máquina en particular es capaz de hacer. Si aplicamos este cambio, debemos obtener cinco
subprocesos independientes todos escribir su propio thread id a la ventana de la consola:
Rosca: 3
Rosca: 4
Rosca: 5
Rosca: 6
Rosca: 7

Vemos rosca consecutivos números porque el trabajo que estamos haciendo


en SimpleWork es muy rápido. Vamos a cambiar nuestro trabajo a algo un poco más extenso
para que podamos ver las roscas trabajando simultáneamente:

// C#
Static void SimpleWork()
{
(Int x = 1; x <= 10; ++x)
{
Console.WriteLine("Subproceso: {0}",
Thread.CurrentThread.ManagedThreadId);

// Ralentizar la rosca y deje que otros subprocesos de trabajo


Thread.Sleep(10).
}
}
En esta nueva versión de SimpleWork, escribimos nuestro identificador del subproceso 10
veces. Además, estamos usando Thread.Sleep para frenar nuestra ejecución. El  método
Thread.Sleep nos permite especificar un tiempo en milisegundos para permitir que otros
subprocesos realizan el trabajo. En cada subproceso, estamos permitiendo que 10
milisegundos para los otros subprocesos para escribir sus propios datos a la consola. Para ver
cómo funciona esto, podemos cambiar nuestro  método SimpleWork para escribir la iteración
está trabajando en:
Rosca: 3
Rosca: 4
Rosca: 5
Rosca: 6
Rosca: 7
Rosca: 3
Rosca: 4
Rosca: 5
Rosca: 6
Rosca: 7

Esto nos permite realizar operaciones como simultáneamente como nuestro hardware lo
permita.

Nota   Threading y
procesadores
Creación de aplicaciones multiproceso solía ser el dominio del desarrollador del
lado del servidor. Esto es
Ya no es el caso. Con la inclusión de hyperthreading y procesadores de doble núcleo
en el escritorio
Y en el portátil, en la mayoría de las aplicaciones de
subprocesos encontrarán útil.

Como nuestro trabajo aumenta y el tiempo que se tarda en completar cada subproceso pasa
más tiempo, tendremos que determinar cómo hacer nuestro hilo principal (el que la rosca cre-
ación existe código) espere hasta que todo el trabajo esté completo. Aquí es donde el  método
Thread.Join entra en juego.

Usando Thread.Join
Más a menudo que no, necesitará la aplicación espere para un subproceso para finalizar su
ejecución. Para lograrlo, la  clase Thread apoya el  método join:
// C#
TheThread.join();

El  método Join indica al sistema para que la aplicación espere hasta que el hilo se ha
completado. Por supuesto, en este caso simple realmente no necesita un segundo subproceso
porque está esperando a que finalice de todos modos. Un mejor ejemplo para nosotros es tener
cinco subprocesos que todos hacer algunos trabajos y para esperar de ellos. Cuando estamos
trabajando con varios subprocesos, nuestra tarea de programación es un poco más
complicado, ya que tenemos que esperar para todos nuestros hilos. Podemos hacer esto
manteniendo una referencia a todos nuestros hilos y llamar a sumarse en cada uno de los
subprocesos a la espera de las roscas para completar, una a la vez, como se muestra en el
siguiente código:
// C#
ThreadStart operación = nuevo ThreadStart(SomeWork);
Tema[] = new Thread theThreads[5];

(Int x = 0; x < 5; ++x)


{
// Crea, pero no se inicia un nuevo subproceso
TheThreads[x] = new Thread(operación);

// Comienza el trabajo en un nuevo subproceso


TheThreads[x].Start();
}

// Esperar cada subproceso complete


Foreach (Rosca t en theThreads)
{
T.join();
}

Almacenando las roscas en una matriz, podemos esperar a que cada uno de los subprocesos
de a uno por vez. Como cada subproceso finaliza, el  método Join devolverá y podemos
continuar.

La prioridad del subproceso


La  clase Thread admite el ajuste o la obtención de la prioridad de un subproceso  mediante
la  enumeración ThreadPriority. La  enumeración ThreadPriority consta de los valores
indicados en la Tabla 7-6.
Tabla 7-6  ThreadPriority    valores de enumeración

Nombre Descripción
   La más alta prioridad AboveNormal  prioridad superior a
la Normal Normal  La prioridad predeterminada BelowNormal
inferior a la normal.
   La prioridad más baja
Las roscas están programadas sobre la base de esta enumeración. En la mayoría de los casos,
querrá usar el valor predeterminado (Normal). Decidir utilizar subprocesos que tienen menor
prioridad subproceso puede causar que el sistema de operación para privar a un hilo más de lo que
cabría esperar, o si utiliza prioridades mayores (especialmente mayor), puede agotar el sistema.
Aunque es nec esario--Normal no utilice las prioridades de subprocesos a veces, tomar esta
decisión con mucha cautela. Aumenta el rendimiento de un sistema simplemente aumentando la
prioridad del subproceso no es probable para ayudar en el largo plazo, otros subprocesos hambre
tienden a retroceder y provocar consecuencias inesperadas.

Pasando datos a las roscas


En cada uno de los ejemplos anteriores, se utiliza el  delegado ThreadStart, que no toma ningún
parámetro. En la mayoría de uso en el mundo real de la creación de subprocesos, necesitará pasar
información a cada uno de los hilos. Para ello, es necesario utilizar un nuevo parámetro llamado
delegado- izedThreadStart. Este delegado especifica la firma de un método con un parámetro de
tipo Object y no devuelve nada. El siguiente fragmento de código proporciona un ejemplo:

// C#
Static void WorkWithParameter(object o)
{
Información = cadena (string) o;
(Int x = 0; x < 10; ++x)
{
Console.WriteLine("{0}: {1}", info,
Thread.CurrentThread.ManagedThreadId);

// Ralentizar la rosca y deje que otros subprocesos de trabajo


Thread.Sleep(10).
}
}

Este es un método que toma un solo  parámetro Object (y por lo tanto puede ser una referencia a
cualquier objeto). Para utilizar esto como punto de partida de un subproceso de llamada, puede
crear un  delegado eterizedThreadStart Param- para que apunte a este nuevo método y utilizar
el Thread.Start Sobrecarga del método que toma un único parámetro del objeto. El siguiente
fragmento de código proporciona un ejemplo:
// C#
ParameterizedThreadStart operación =
Nueva ParameterizedThreadStart(WorkWithParameter);

// Crea, pero no se inicia un nuevo subproceso


Rosca theThread = new Thread(operación);

// Comienza el trabajo en un nuevo subproceso


TheThread.Start("Hello");

// Un segundo subproceso con un parámetro diferente newThread


Rosca = new Thread(operación); newThread.Start("Adiós");
Ser conscientes de que, debido a que el  método WorkWithParameter toma un
objeto Thread.Start podría ser llamado con cualquier objeto en lugar de la cadena que espera. Ser
cuidadoso en choos- ing su método de arranque para un subproceso para tratar tipos desconocidos
es crucial para un buen código de subprocesos. En lugar de la fundición ciegamente el parámetro
del método en nuestra cadena, es una mejor práctica para probar el tipo de objeto, como se muestra
en el ejemplo siguiente:
// C#
Info string = o como cadena.

Si (info == null)
{
Tirar InvalidProgramException("parámetro para el subproceso debe ser una cadena");
}

Detener subprocesos
Control de subprocesos en sus aplicaciones a menudo requiere que usted sea capaz de detener
subprocesos. El principal mecanismo para detener subprocesos es utilizar el  método
Thread.Abort. Cuando el  método Thread.Abort se denomina enhebrado, el sistema se prepara para
lanzar un hilo- AbortException en la rosca. Si la excepción es capturado o no, el subproceso está
detenido después de que éste se produce. El siguiente fragmento de código proporciona un
ejemplo:
// C#
Rosca newThread = new Thread(nuevo ThreadStart(AbortThisThread);

NewThread.Start();
NewThread.Abort();

Static void AbortThisThread()
{
// Datos de configuración
SomeClass.IsValid = true;
SomeClass.Hacompletado = true;

// Escriba el objeto a la consola


.SomeClass WriteToConsole();
}

Porque el  método AbortThisThread  nunca coge el ThreadAbortException, este hilo se detiene en


la línea actualmente en ejecución cuando el subproceso principal llama Abort para matar el hilo.
Esto es un problema porque el sistema de rosca no sabe donde puede matar el hilo. Anulando el
hilo en el lugar equivocado en el código podría dejar los datos en un estado inconsistente. Por
ejemplo, si la excepción ThreadAbortException se produce entre el valor de las    propiedades y
IsValid IsComplete, el  objeto SomeClass podrían quedar en un estado inconsistente. Si
el ThreadAbortException se produce después de que el código escribe las propiedades pero antes
el código llama al  método WriteToConsole, nuestro objeto será coherente. Sólo nunca escriba su
propia salida a la consola.
Para resolver el problema de dejar objetos o AppDomain en un estado incoherente, la  clase
Thread tiene dos importantes: métodos estáticos  BeginCriticalRegion y EndCriti- calRegion.
Podemos añadir las llamadas a estos métodos para indicarle al sistema de subprocesos que
pueden anular este hilo, no sólo en esta región crítica. El siguiente fragmento de código pro-
porciona un ejemplo:
// C#
Static void AbortThisThread()
{
// Establecer
datos Thread.BeginCriticalRegio
n(); SomeClass.IsValid = true;
SomeClass.Hacompletado = true;
Thread.EndCriticalRegion();

// Escriba el objeto a la consola


.SomeClass WriteToConsole();
}

La idea detrás de una región crítica es proporcionar una región de código que debe ejecutarse
como si se tratara de una única instrucción. Cualquier intento de abortar un hilo mientras se
está dentro de una región crítica tendrá que esperar hasta después de que la región crítica está
completa. En ese punto, el hilo se abortará, lanzando la excepción ThreadAbortException. La
diferencia entre un subproceso con y sin una región crítica se ilustra en la figura 7-1.

Nota
.NET 2.0
Un cambio importante en los subprocesos en la versión 2.0 de .NET Framework
es que la rosca.Suspender  y Reanudar Thread.  métodos han sido retirados (y
marcados como obsoletos). Suspender y reanudar threads, debe utilizar métodos
de sincronización de subprocesos, como se describe en la Lección 2.
Thread Start trabajan más de trabajo Incluso
Más trabajo

Anulación de subproceso

Abortado

Región crítica

Inicio de rosca funcionan más trabajo aún más el trabajo Hech

Anulación de subproceso Abortado

Figura 7-1  regiones críticas y anulación de


subprocesos

El contexto de ejecución
Cada subproceso tiene datos asociados a ella, y que los datos que normalmente se propaga a
nuevas roscas. Estos datos incluyen información de seguridad (la  rosca y IPrincipal identidad), los
ajustes de localización (la cultura el subproceso se ejecuta en) y trans- acción información
de System.Transactions. Para acceder a la ejecución actual con- texto, el ExecutionContext clase
proporciona métodos estáticos para controlar el flujo de información de contexto.
Por defecto, el contexto de ejecución fluye a los subprocesos de ayuda, pero lo hace a un costo. Si
desea detener el flujo de la información de contexto (para aumentar el rendimiento, pero perder la
situación actual de la seguridad, la cultura y la información en el contexto de la transacción), puede
utilizar la  clase ExecutionContext. Para suprimir el contexto, se
llama ExecutionContext.SuppressFlow;

Para restaurar el contexto, se llama ExecutionContext.RestoreFlow-como se muestra en el código


de seguimiento
/
/

C
#
= Flujo AsyncFlowControl ExecutionContext.SuppressFlow();

// Nuevo subproceso no obtiene el contexto de ejecución


Hilo Hilo = new Thread(nuevo ThreadStart(SomeWork);
Thread.Start();
Thread.join()
;

// Restablecer el
flujo
ExecutionContext.RestoreFlow();
// Flujo.También podría
deshacer();

También puede llamar al  método Run estática de ExecutionContext, que permite a


exe- cute código arbitrario en el subproceso actual y especificar la información de
contexto para su uso. Para usar el  método Run, usted necesita obtener una copia de
un contexto de ejecución. Puede utilizar ExecutionContext.Capture para recuperar
el contexto actual. A continuación, puede llamar a ejecutar, spec-ifying el contexto
de uso y un ContextCallback delegado con un método para ejecutar dentro del
contexto especificado. El siguiente fragmento de código proporciona un ejemplo:
'

V
B

' Obtiene el contexto actual


Dim As ctx ExecutionContext ExecutionContext =.Capture()

' Ejecutar el método ContextCalled


ExecutionContext.Run(ctx, Nueva ContextCallback(ContextCalled), nada)

/
/

C
#

// Obtener el contexto actual


Ctx = ExecutionContext ExecutionContext.Capture();

// Ejecutar el método ContextCalled


ExecutionContext.Run(ctx, nuevo), ContextCalled ContextCallback(null);
Nota   Host-contexto específico
Si la   clase AppDomainManager tiene una referencia a un   objeto
HostExecutionContextManager válida (en la   clase
AppDomainManager HostExecutionContextManager  propiedad), entonces el
tiempo de ejecución también fluirá un contexto de ejecución independiente,
denominado HostExecutionContext,  con cada subproceso. Mucho como
con ExecutionContext, usted puede capturar  o restaurar  datos en
el HostExecutionContext, pero sólo el anfitrión (el código que crea
el AppDomain) puede determinar si necesita este contexto de ejecución
adicionales.

Laboratorio: usar la   clase Thread para demostrar el


subprocesamiento múltiple
En este laboratorio, crear una sencilla aplicación que cuenta de 1 a 10 y muestra cada
iteración en la ventana de la consola. Si se produce un problema de completar un
ejercicio, los proyectos terminados están disponibles en el CD en la carpeta de código.
    Ejercicio: Creación de múltiples subprocesos
En este ejercicio, va a crear una aplicación de consola simple y comenzar dos subprocesos
simul- taneously.
1.  Crear una nueva aplicación de consola, y llamarlo SimpleThreadingDemo.
2.  Crear un nuevo método estático denominado Contar.
3.  En la nueva clase, agregue una  instrucción using (o la  instrucción
Imports en Visual
Basic) para el  espacio de nombres System.Threading.
4.  Con el nuevo método, cree un  bucle for que cuenta de 1 a 10.
5.  Dentro del  bucle for nuevo, escriba el número actual y
el ManagedThreadId
Para el subproceso actual.
6.  Después de escribir en la consola, el sueño del subproceso actual durante
10 milisegundos.
7.  Volver al  método Main, y crear un nuevo  delegado ThreadStart que
apunta al  método de conteo.
8.  Ahora crea dos subprocesos, cada uno apuntando hacia el  método de
conteo.
9.  Iniciar ambos subprocesos.
10.  Unir ambos subprocesos para  garantizar que la aplicación no se completa
hasta que las roscas son de hecho. El código debe tener este aspecto:
// C#
Utilizando System;
Utilizando System.Collections.Generic.
Utilizando System.Text;

Using System.Threading;

Programa de clase
{
Static void main(String[] args)
{
ThreadStart starter = new ThreadStart(contando); primera rosca
= new Thread(starter);
Enroscar la segunda = new Thread(starter);

Primera.Start();
Segundo.Start();

Primera.join();
Segundo.join();

Console.Read();
}

Static void contando()


{
(Int i = 1; i <= 10; i++)
{
Console.WriteLine("count: {0} - Rosca: {1}", i,
Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(10).
}
}
}

11.  Genere el proyecto y resolver cualquier error. Compruebe que la


aplicación de consola suc- logre muestra los subprocesos ejecutándose
simultáneamente. Usted puede determinar  esto comprobando si cada
subproceso de la cuenta están entremezclados con los de otros
subprocesos. La naturaleza exacta de la mezcla depende del tipo de hard-
ware. Un solo procesador máquina será muy ordenado, pero un multipro-
cessor (o multicore) máquina será algo aleatorio.

Resumen de la lección
■   para realizar trabajos simultáneamente, utilice la  clase Thread.
■   Para iniciar la ejecución de los subprocesos, utilice el    método Start de
la clase Thread.
■   para esperar en las roscas para completar, utilizar el    método Join de la
clase Thread.
■   Para cancelar la ejecución de un subproceso, utilice la  clase Thread
el  método Abort.
■   paracompartir datos a través de los subprocesos, utilice la  clase
ExecutionContext.

Examen de la lección
Puede utilizar las siguientes preguntas para poner a prueba tus conocimientos de la
información Les- hijo 1, "Creación de hilos." Las preguntas también están disponibles en
el CD complementario si prefiere revisarlos en forma electrónica.

Nota   Respuestas
Las respuestas a estas preguntas y explicaciones de por qué cada opción
de respuesta es correcta o no se encuentran en la sección "Respuestas" al
final del libro.

1.  ¿Qué tipo de objeto es obligatorio cuando se inicia un subproceso que


requiere un único parámetro?
A.   Delegado ThreadStart
B.    Delegado ParameterizedThreadStart
C.    Clase SynchronizationContext
D.    Clase ExecutionContext
2.  ¿Qué método deja un subproceso en ejecución?
A. Thread.Suspender 
B. Thread.Reanudar 
C.   Thread.Abort
D.   Thread.Join
Lección 2: Intercambio de datos
La parte más difícil del trabajo con subprocesos es compartir datos entre varios subprocesos. Una
vez que comience a trabajar con múltiples subprocesos en una aplicación, usted se convierte en
responsable de la protección de los datos compartidos desde la que se puede acceder desde varios
subprocesos. Por suerte para nosotros que el .NET Framework que hace muy sencilla.

Después de esta lección, será capaz de:


■   Utilice la   clase Interlocked para realizar operaciones atómicas.
■   Utilice el bloqueo de C#  o Visual Basic SyncLock  sintaxis para bloquear los datos.
■   Utilice la   clase Monitor para bloquear los datos.
■   Utilizar un ReaderWriterLock  para bloquear los datos.
■   Utilice un mutex  para sincronizar subprocesos.
■   Utilice un semáforo  para roscas del acelerador.
■   Utilizar un evento  para señalar las roscas.
Lección Tiempo estimado: 40 minutos

Evitando colisiones
Antes de enhebrar estuvo disponible, usted podría esperar que cualquier acceso de datos fue
realizada por un objeto a la vez. Ahora que hemos entrado en el mundo multiproceso, tenemos que
afrontar el hecho de que varios subprocesos puedan estar interrogando a nuestros objetos
simultáneamente. Esto causa problemas en algunas maneras engañosas. Por ejemplo, considere la
siguiente clase:

// C#
Contador de clase pública
{
Public static int Count;
}

El contador clase contiene un campo estático llamado recuento que permite acceder directamente


a un número de elementos de la colección. Podríamos escribir un método simple que añade que
cuentan con un gran número de veces, así:

// C#
Static void UpdateCount()
{
(Int x = 1; x <= 10000; ++x)
{
Counter.Count = Contador.Count + 1;
}
}
No importa cuántas veces corremos este UpdateCount, siempre deberíamos añadir 10.000 al
número en el recuento. Parece lógico entonces que si usamos subprocesos para ejecutar este
método, deberíamos obtener 10.000 multiplicado por el número de subprocesos en nuestra cuenta.
Por ejemplo, este código enhebrado podría ejecutar 10 subprocesos para actualizar la cuenta:
// C#
ThreadStart starter = new ThreadStart(UpdateCount);

Subproceso subprocesos[] = new Thread[10];


(Int x = 0; x < 10; ++x)
{
Roscas[x] = new Thread(starter);
Roscas[x].Start();
}

// Espere al finalizar para (int x = 0; x <


10; ++x)
{
Roscas[x].join();
}
// Mostrar en la consola el conteo total
// Debe ser 10 * 10.000 = 100.000
Console.WriteLine("Total: {0}", Counter.Count).

Si está trabajando con un único procesador sin compatibilidad con Hyper-Threading, este código
será probablemente siempre funcionan como se espera. Pero si se ejecuta este código varias veces
en un multiprocesador o Hyper-Threaded máquina, verá que a veces el resultado- ing número es
inferior a los 100.000 esperados. ¿Por qué?
Esto sucede debido a la manera en que la actualización del  campo count trabaja en el
proces- sor. En la mayoría de los equipos,  este código se resuelve en tres pasos:
1.  Cargar el valor en un registro dentro del procesador.
2.  Aumentar (o disminuir) el valor en el registro.
3.  Copiar el valor del registro a la memoria.
El problema es que en el código de subprocesos todos estos tres pasos son tratadas como
atomic oper- nizaciones. Como se muestra en la Figura 7-2, dos subprocesos cada uno
pudiera leer los valores de la memoria y al mismo tiempo actualizar con el mismo valor
actualizado. Esta es la razón por la que el código mostrado pierde algunos de los cargos.
Recuento de la carga en el registro (Count Recuento de la carga en el registro (Count

es 10) incrementan el valor de Registro SomeClass es 10) incrementan el valor de Registro


Count=10
(Count es todavía 10) (Count es todavía 10)
Almacenar valor incrementado (Count es Almacenar valor incrementado (Count es
ahora 11) ahora 11)
Figura 7-2  Incrementar valores en varios
subprocesos

Lo que si se utiliza una sintaxis diferente? Podríamos formatear nuestro código


como sigue:
' VB
Counter.Count = Contador.Count + 1
'O
Counter.Count += 1

// C#.Count Contador+
+;
// O
Counter.Count += 1;

Nuestro cambio no importa porque el código compilado todavía necesita realizar las tres
operaciones indicadas anteriormente. ¿Cómo se puede solucionar este problema? Puede
utilizar la  clase Interlocked para realizar la operación de incremento. La  clase
Interlocked está detallado en la Tabla 7-7.

La tabla 7-7  enclavarse  métodos estáticos

Nombre Descripción
Agregar  Agrega dos enteros como una operación atómica.
Puede ser utilizado para la sustracción agregando un
número entero negativo.
   Resta un decremento de un valor como una operación atómica.
Exchange  Intercambia dos valores como una operación atómica.
   Añade un incremento al valor como una operación atómica.
Read   Lee un número de 64 bits como una operación atómica.
Necesario para
Los sistemas de 32 bits porque los números de 64 bits
están representados como dos piezas de información.

Podemos cambiar nuestro UpdateCount método para utilizar la  clase Interlocked para


resolver la cuestión de subprocesos como:
// C#
Static void UpdateCount()
{
(Int x = 1; x <= 10000; ++x)
{
//.Count Contador = Contador.Count + 1;
Interlocked.Increment(ref Counter.Count).
}
}

Nota  Los datos volátiles


La   clase Thread (así como el compilador de C#) admite volátil de lectura y
escritura en los campos. La idea detrás de lecturas y escrituras volátil es evitar el
almacenamiento en caché de datos dentro de una CPU de caus- ing
incoherencias de datos en sus aplicaciones de subprocesos. En lugar de utilizar
el hilo's    VolatileWrite VolatileRead y  métodos (o la palabra clave volatile en
C#), utilice la   clase Interlocked u otros métodos de sincronización de alto nivel
discutidos en esta lección. Con estas estrategias se asegurará de que usted tiene
el comportamiento correcto independientemente de qué modelo de CPU o de
memoria a la que se ejecuta el software.

Más info la  sincronización


de subprocesos
Sincronización de subprocesos es un tema complejo que tiene muchas
interacciones sutiles en diferentes environ- ciones. Consulte CLR via C#,
segunda edición, por Jeffrey Richter (Microsoft Press,  2006), en concreto, en
las páginas 622-629, comprender los matices de sincronización de subprocesos.

Utilizando el  método de incremento Interlocked.y que pasa una referencia al valor que desea
incrementar, se garantiza que la operación de incremento es atómico, de modo que el
problema desaparece. El principal problema con la  clase Interlocked es que funciona con un
pequeño conjunto de tipos de .NET. ¿Qué puede hacer si necesita sincronizar el acceso a sus
propias clases o tratar los trozos más grandes de código como atomic? ¿Cómo se puede
asegurar que otros tipos de código están protegidos contra el acceso del subproceso múltiple?
Bloqueos de sincronización abordar todas estas cuestiones.

Bloqueos de sincronización
El propósito de bloqueos de sincronización le permite sincronizar el acceso a los objetos de
.NET. Si cambiamos nuestro  ejemplo de clase de contador para calcular dos cargos diferentes
(un simple recuento, y un recuento de cuando nuestro contador es un número par), nuestro
código tendrá este aspecto:

// C#
Contador de clase pública
{
Int _Count = 0.
Int _evenCount = 0.

Public int Count


{
Get { return _count; }
}

Público EvenCount int.


{
_EvenCount get { return; }
}

Public void UpdateCount()


{
Interlocked.Increment(ref _count);
Si (recuento % 2 == 0) // un número par
{
Interlocked.Increment(ref _evenCount);
}
}
}

Ahora podemos usar esta clase en una versión modificada de nuestro anterior código:
// C#
Recuento del contador = Contador nuevo();

ParameterizedThreadStart starter = new


ParameterizedThreadStart(UpdateCount); la rosca[]
hilos = nuevo subproceso[10];

(Int x = 0; x < 10; ++x)


{
Roscas[x] = new Thread(starter);
Roscas[x].Start(count);
}

// Espere al finalizar para (int x = 0; x <


10; ++x)
{
Roscas[x].join();
}

// Mostrar en la consola el conteo total y accesos


Console.WriteLine("Total: {0} - EvenCount: {1}", count.Count,
count.EvenCount);

Static void UpdateCount(object param)


{
Recuento del contador (contador =)param;

(Int x = 1; x <= 10000; ++x)


{
// Agregar dos al conde
count.UpdateCount();
}
}

Como antes, creamos 10 subprocesos para agregar cuenta para nosotros, pero en este caso
estamos compartiendo una instancia de la  clase de contador. Cuando se ejecuta este código,
nuestro recuento sencillo siempre tiene razón, pero ocasionalmente nuestro recuento de los
valores es incorrecto. Esto es debido a que dos subprocesos puede venir y actualizar la cuenta
simple derecho uno después de otro, de manera que la línea que comprueba si el recuento es
par o impar puede perder incluso un recuento. Aunque cada una de las operaciones de
incremento es thread-safe, como una unidad, las dos opera- ciones no son.

Para resolver este problema, puede utilizar la sincronización de cerraduras. En C#, debe
utilizar la  palabra clave de bloqueo, y en Visual Basic es la  palabra clave SyncLock.
Podemos cambiar nuestra  clase Counter para utilizar un bloqueo de sincronización para
resolver este problema de la siguiente manera:
/
/

C
#
Public void UpdateCount()
{
Bloqueo (este)
{
_Count = _conde + 1;
Si (recuento % 2 == 0) // un número par
{
_EvenCount = _evenCount + 1;
}
}
}

Reemplazando el  código de clase entrelazadas con un bloqueo de sincronización, podemos


garantizar que sólo un subproceso en un momento puede entrar en el bloqueo de la sección de
código. El bloqueo requiere que se especifique un objeto para utilizarlo como identificador del
bloqueo. Para que el código manipula varios pedazos de datos dentro de una clase, puede
utilizar la instancia actual de la clase (esta en C# y Me en Visual Basic).
El bloqueo de sincronización funciona bloqueando otros subprocesos del acceso al código
mientras un subproceso está dentro de la cerradura. Por ejemplo, el primer subproceso
provocará este trozo de código para ser bloqueado basado en la instancia de la clase. El
segundo subproceso esperará hasta que el primer subproceso sale el bloqueo antes de entrar en
el bloqueo, y así sucesivamente para subprocesos adicionales.
Aunque utilizando las palabras clave en Visual Basic y C# son a menudo las formas más
fáciles de crear bloqueos de sincronización, es posible que necesite tener más control sobre la
forma en que usted está creando un bloqueo. Debajo de las cubiertas, los bloqueos de
sincronización son sólo utilizando la  clase Monitor para realizar la sincronización. La tabla 7-
8 muestra una lista de los más importantes métodos estáticos de la  clase Monitor.
Tabla 7-8  supervisar    métodos estáticos

Nombre Descripción
Entrar   crea un bloqueo exclusivo sobre un objeto especificado
Salir  libera un bloqueo exclusivo sobre un objeto especificado
TryEnter  intenta crear un bloqueo exclusivo sobre un determinado objeto.
Opcionalmente admite un valor de tiempo de espera en la
adquisición del bloqueo
Espere  libera un bloqueo exclusivo, y bloquea el subproceso
actual hasta que pueda volver a adquirir el bloqueo

Podemos obtener el mismo comportamiento del monitor cambiando nuestro código a este


aspecto:

// C#
Public void UpdateCount()
{
Monitor.Enter(this);
Pruebe
{
_Count = _conde + 1;
Si (recuento % 2 == 0) // un número par
{
_EvenCount = _evenCount + 1;
}
}
Finalmente
{
Monitor.Exit(this);
}
}

A pesar de bloqueos de sincronización va a resolver muchos de los problemas de


sincronización de subprocesos, pueden presentar un problema conocido como interbloqueos.
Comprensión de los interbloqueos
Un interbloqueo es un caso donde dos trozos de código están intentando acceder a los mismos
objetos pero están bloqueando mutuamente desde la obtención de los recursos. El siguiente
fragmento de código pro- porciona un ejemplo:

/
/

C
#
Clase
Deadlocker
{
ResourceA objeto = new Object();
ResourceB objeto = new Object();
Public void First()
{
Bloqueo (ResourceA)
{
Bloqueo (ResourceB)
{
Console.WriteLine("First");
}
}
}

Public void Segundo()


{
Bloqueo (ResourceB)
{
Bloqueo (ResourceA)
{
Console.WriteLine("Segundo");
}
}
}
}

Si llamamos a esta clase de la siguiente manera:


// C#
Interbloqueo Deadlocker = new Deadlocker();

ThreadStart firstStart = new ThreadStart(estancamiento.Primero); ThreadStart


secondStart = new ThreadStart(estancamiento.Segundo);

Enroscar la primera = new Thread(firstStart); segunda


rosca = new Thread(secondStart);

Primera.Start();
Segundo.Start();

Primera.join();
Segundo.join();

El bloqueo ocurre como esta:


1.  Primer subproceso se inicia y se bloquea ResourceA.
2.  Segundo subproceso se inicia y se bloquea ResourceB.
3.  Primer subproceso bloquea ResourceB esperando para ser liberados.
4.  Segundo subproceso bloquea ResourceA esperando para ser liberados.
5.  La aplicación se detiene en sus pistas.
En este punto de la explicación, sería estupendo poder decirles que hay alguna clase de magia
que le ayudarán a evitar los interbloqueos. No hay. Los deadlocks son un asunto de desarrollo
cuidadoso y la detección. Aparte de ser muy cuidadoso en el bloqueo de objetos Con la  clase
Monitor, también puede utilizar los siguientes métodos para tratar con algunos objetos
supercondenación:
■   Utilizar Monitor.TryEnter con tiempos de espera para permitir la
supercondenación situaciones para recuperar o volver a intentar, en lugar de
quedar atascado.
■   Reduce la cantidad de código que tiene bloqueado para reducir el tiempo que
un recurso está bloqueado.
Las mejores prácticas de
esperar para siempre
En lugar de usar excesivamente largos tiempos de espera en milisegundos para
tiempos de espera infinito (donde están dispuestos a esperar tanto como tarda),
debería utilizar el tiempo de espera   infinito estáticos de la clase de  bienes.

Otros métodos de sincronización


Aunque la  clase Monitor es útil (y ligero) herramienta para desarrollar software con
subprocesos, otros mecanismos de sincronización tienen sus propios usos. En esta sección
obtendrá información sobre cada uno de estos objetos de sincronización, incluyendo las
siguientes:
■   ReaderWriterLock class

■ la   sincronización con los objetos de kernel de Windows


❑    clase Mutex
❑    clase Semaphore
❑    clase AutoResetEvent
❑    clase ManualResetEvent
R  clase eaderWriterLock   el propósito de t él ReaderWriterLock es diferenciar entre las
dos clases de código que puede utilizar determinados recursos. Mediante
el lector- WriterLock, puede bloquear el acceso a los lectores y escritores por separado.
La ReaderWriter-Bloquear permite múltiples lectores para acceder a los datos al mismo
tiempo, pero sólo un único escritor puede obtener un bloqueo de los datos. Todos los lectores
deben liberar sus bloqueos antes de un escritor puede obtener un bloqueo de los datos.
La ReaderWriterLock propiedades y métodos de la clase están detalladas en la Tabla 7-9 y la
tabla 7-10, respectivamente.
Tabla 7-9  ReaderWriterLock  Propiedades

Nombre Descripción
IsReaderLockHeld   obtiene un indicador que muestra si el lector tiene un
bloqueo
IsWriterLockHeld  obtiene un indicador que muestra si un escritor tiene un
bloqueo

Tabla 7-10  ReaderWriterLock  métodos

Nombre Descripción
AcquireReaderLock  obtiene un bloqueo de lectura dentro de un tiempo
especificado. Si un bloqueo no puede concederse
dentro del período de tiempo de espera, se lanza una
excepción de aplicación.
AcquireWriterLock  obtiene un bloqueo de escritura dentro de un tiempo
especificado. Si un bloqueo no puede concederse
dentro del período de tiempo de espera, se lanza una
excepción de aplicación.
DowngradeFromWriterLock  convierte un bloqueo de escritura a un bloqueo de
lectura.
ReleaseReaderLock  libera un bloqueo de lectura.
ReleaseWriterLock  libera un bloqueo de escritura.
   Actualizaciones UpgradeToWriterLock un bloqueo de lectura a un bloqueo
de escritura.

Para adquirir un bloqueo de lectura, siga estos pasos:


1.  Crear una instancia de la  clase ReaderWriterLock para ser compartidos a través de
los subprocesos.
2.  Cree un  bloque try/catch (capturar un ApplicationException). Este  bloque
try/catch se utiliza para capturar la excepción si la adquisición del bloqueo de la
lectora alcanza el tiempo de espera.
3.  Dentro del    bloque try/catch, adquirir el lector bloqueo llamando a
ReaderWriter- Lock.AcquireReaderLock.
4.  Después de adquirir el bloqueo de lectura, cree un  bloque try/finally para contener
cualquier código de lectura.
5.  Hacer cualquier trabajo que necesite, pero sólo leer cualquier thread-safe de datos
dentro de la  parte try del  bloque try/finally.
6.  En la  parte final del  bloque try/finally, suelte el bloqueo de la lectora
con ReaderWrit- erLock.ReleaseReaderLock.
Por ejemplo, si desea leer un valor a la consola, puede usar un bloqueo de lectura para acceder
a ella, por ejemplo:
// C#
ReaderWriterLock rwLock = new ReaderWriterLock();
Int contador = 0;
Pruebe
{
RwLock.AcquireReaderLock(100).

Pruebe
{
Console.WriteLine(contador);
}
Finalmente
{
RwLock.ReleaseReaderLock();
}
}
Capturas (ApplicationException)
{
Console.WriteLine("Error al obtener un bloqueo de lectura");
}

A veces cuando se necesita cambiar datos, necesitará un bloqueo de escritura. Para adquirir un
bloqueo de escritura, siga estos pasos:
1.  Crear una instancia de la  clase ReaderWriterLock para ser compartidos a
través de los subprocesos.
2.  Cree un  bloque try/catch (capturar un ApplicationException). Este  bloque
try/catch se utiliza para capturar la excepción si la adquisición del bloqueo del
escritor alcanza el tiempo de espera.
3.  Dentro del  bloque try/catch, adquirir el bloqueo de escritura
llamando ReaderWriterLock
.AcquireWriterLock.
4.  Después de adquirir el bloqueo de escritura, cree un  bloque try/finally para
contener cualquier código de escritura.
5.  Hacer cualquier trabajo que usted necesita en la  parte try del  bloque
try/finally.
6.  En la  parte final del  bloque try/finally, suelte el bloqueo de escritura
con ReaderWriter- Lock.ReleaseWriterLock.

Por ejemplo, si desea que los cambios de valores, usted podría utilizar un bloqueo de escritura
para permitir cambiar de forma segura:
// C#
ReaderWriterLock rwLock = new ReaderWriterLock();
Int contador = 0;
Pruebe
{
RwLock.AcquireWriterLock(1000);
La ReaderWriterLock está diseñado para trabajar con dos clases de cerraduras, lectores y
auto- res. Para facilitar aún más la utilidad de estos bloqueos, ReaderWriterLock tam- puerto
actualizar un bloqueo de lectura a un bloqueo de escritura y regresar a un bloqueo de lectura.
Realiza estas dos tareas con el UpgradeToWriterLock y DowngradeFromWriter- Métodos de
bloqueo, respectivamente. Estos métodos deben utilizarse en tándem, tanto
como Monitor.Enter y Monitor.Exit métodos. El  método LockCookie UpgradeToWriterLock
devuelve un objeto. Este LockCookie es una estructura que la ReaderWriterLock utiliza para
Permitir el bloqueo de escritura que se degradarán cuando haya terminado de escribir a los
datos bloqueados. El siguiente fragmento de código proporciona un ejemplo:
ejemplo:

// C#
Pruebe
{
Cookie LockCookie = rwLock.UpgradeToWriterLock(1000);
Contador++;
RwLock.DowngradeFromWriterLock(ref cookie);
}
Capturas (ApplicationException)
{
// No pudo obtener un bloqueo, por lo que ignorar la parte de escritura
}

Observe que la  llamada UpgradeToWriterLock requiere un  valor de tiempo de espera y es


posible que no puedan adquirir el bloqueo de escritura en el plazo de tiempo especificado
(como cualquier adquisición de un bloqueo de escritura). Por el contrario, el  método no
requiere ningún tiempo de espera DowngradeFromWriterLock porque es liberar el bloqueo de
escritor y lector de reinstaurar la cerradura que fue adquirida antes de la cerradura se
actualizó.
Lasincronización con los objetos de kernel de Windows en el sistema operativo,
hay tres objetos de kernel-Mutex, semáforo, y el evento-cuyo trabajo es le permiten realizar la
sincronización de subprocesos. Aunque estos objetos de kernel proporcionan potentes syn-
Instalaciones chronization, son objetos pesados. Por ejemplo, usando
un mutex para sincronizar los subprocesos en lugar de un monitor es aproximadamente 33
veces más lento (como señaló Jeffrey Richter en su libro CLR via C#, mencionado
anteriormente en este capítulo). Aunque estos objetos de kernel vienen con una sobrecarga
adicional, le permiten per- las tareas de sincronización de forma que son imposibles con
el monitor y ReaderWriterLock clases:
■   Un  mutex permite la sincronización (como un bloqueo) a través de
AppDomain y  los límites del proceso.
■   un semáforo se
utiliza para acelerar el acceso a un recurso para un
determinado número de subprocesos.
■   Un evento proporcionauna forma de notificar a los subprocesos múltiples (a
través de AppDomain y los límites del proceso) que se ha producido algún
evento.
Cada uno de estos objetos de sincronización está representada por las clases de .NET Marco
(Mutex semáforo ,, , AutoResetEvent y ManualResetEvent). Todas estas clases se derivan de
una clase común llamado WaitHandle. La  mayoría de la clase WaitHandle obligacio- nes de
propiedades y métodos se detallan en la tabla 7-11 y la tabla 7-12, respectivamente.
Tabla 7-11    propiedad WaitHandle

Nombre Descripción
Manejar  obtiene o establece el objeto de kernel del sistema nativo del
mango

Tabla 7-12  WaitHandle  métodos

Nombre Descripción
Cierre  libera todos los recursos utilizados por el actual objeto de kernel
WaitOne  bloquea el subproceso actual hasta el objeto de kernel se señaliza

 Clase mutex   el propósito de la  clase Mutex es proporcionar un mecanismo de bloqueo, y


funciona de la misma manera que la  clase Monitor. La principal diferencia es que la  clase
Mutex puede bloquear los datos a través de AppDomain y los límites del proceso. Para usar
un mutex, siga estos pasos:
1.  Crear una instancia de la  clase Mutex para ser compartidos a través de los subprocesos:
' VB
Dim m como nuevo mutex()

// C#
Mutex m = new mutex();

2.  Dentro de un nuevo subproceso, cree una  instrucción if llamando a la  clase


Mutex's WaitOne
Método para esperar hasta que el bloqueo está disponible:
' VB
Si m.WaitOne(1000, false) entonces
End If

// C#
Si (m.WaitOne(1000, falso)) // espere 1 segundo para bloquear
{
}

3.  Crear un  bloque try/finally dentro del  bloque if.


4.  Dentro de la  sección try del  bloque try/finally, hacer el trabajo que hay que hacer al
tener acceso exclusivo al  objeto de exclusión.
5.  En la    parte final del     bloque try/finally,  suelte el  mutex  llamando
Mutex.ReleaseMutex:
// C#
Pruebe
{
// Algunos trabajos
}
Finalmente
{
M.ReleaseMutex();
}

6.  Opcionalmente, crear un  bloque else  después de la   instrucción if bloque a


tratar con no obtener el bloqueo:
'

V
B

E
l
s
e
' Reaccionar a no obtener el recurso (por ejemplo, reintentando
' O notificar al usuario). End
If

// C#
}
Otra cosa
{
// Reaccionan a no obtener el recurso (por ejemplo, reintentando
// O notificar al usuario).
}

En la mayoría de los casos, deseará crear la exclusión mutua con un nombre


conocido, de modo que usted pueda conseguir la exclusión mutua a través
de AppDomain y/o límites de proceso. Si usted cree que con un nombre, puede
utilizar el   método OpenExisting Mutex 's estático para obtener un mutex que ya
ha sido creado. El siguiente fragmento de código proporciona un ejemplo:
'

V
B
Dim theMutex como mutex = nada
Pruebe
Mutex.OpenExisting theMutex =("MYMUTEX")
Ex como WaitHandleCannotBeOpenedException capturas
' No se puede abrir el mutex porque no existe.
Intente final
' Se crea si no existe.
Si no es nada theMutex luego
TheMutex = Nuevo Mutex(false, "MYMUTEX")
End If

// C#
Mutex theMutex = null;

Pruebe // intentar abrir el mutex


{
Mutex.OpenExisting theMutex =("MYMUTEX");
}
Capturas (WaitHandleCannotBeOpenedException)
{
// No se puede abrir el mutex porque no existe.
}

// Crearlo si no existe si (theMutex == null)


{
TheMutex = nuevo Mutex(false, "MYMUTEX");
}

Usted necesita usar un  bloque try/catch al intentar abrir un mutex existente porque en lugar
de devolver un valor null, una WaitHandleCannotBeOpenedException es lanzado en su
lugar. Utilizando una  exclusión mutua con nombre común es una forma de sincronizar los
datos a través de procesos enlazados- aries. El primer proceso para intentar abrir
el mutex acabarán creando, y todos los demás procesos simplemente obtendrá la creada por
el primer proceso.
 Clase Semaphore   La  clase Semaphore se utiliza para acelerar el uso de algún recurso.
Específicamente, un semáforo crea un objeto de kernel que soporta un número determinado
de ranuras válido a la vez. Cuando las ranuras están llenas, el código restante se bloqueará
hasta que esté disponible una ranura por otro subproceso liberar la ranura.

Nota   .NET 2.0

La   clase Semaphore es nueva en .NET Framework


2.0.

Crear una nueva instancia de la  clase Semaphore le permite especificar el número de


ranuras usadas y el número máximo de ranuras. El siguiente fragmento de código
proporciona un ejemplo:
' VB
Dim theSemaphore como nueva Semaphore(0, 10)

// C#
Semáforo theSemaphore = nueva Semaphore(0, 10).
La mayoría de las operaciones tras la creación del semáforo son exactamente como
las operaciones indicadas oreja- lier para la  clase Mutex. La principal diferencia es
que cuando se suelte el sema- phore puede especificar cuántas ranuras desea
liberar, como se muestra en el ejemplo siguiente:
'

V
B
TheSemaphore.Suelte(5).

/
/

C
#
TheSemaphore.Liberación(
5).

Además, como con la  clase Mutex, puede especificar un nombre que puede
utilizarse para crear y abrir compartidos a través de los semáforos  y los límites del
proceso de AppDomain. El seguimiento fragmento de código proporciona un
ejemplo:
'

V
B
Dim theSemaphore como semáforo = nada

Intente ' intentar abrir el semáforo


Semáforo.OpenExisting theSemaphore =("THESEMAPHORE")
Ex como WaitHandleCannotBeOpenedException capturas
' No se puede abrir el semáforo porque no existe.
Int
en
te
fin
al

' Se crea si no existe.


Si no es nada theSemaphore luego
TheSemaphore = nueva Semaphore(0, 10, "THESEMAPHORE")
E
n
d
I
f

/
/

C
#
Semáforo theSemaphore = null;

Pruebe // intentar abrir el semáforo


{
Semáforo.OpenExisting theSemaphore =("THESEMAPHORE");
}
Capturas (WaitHandleCannotBeOpenedException)
{
// No se puede abrir el semáforo porque no existe.
}

// Crearlo si no existe si (theSemaphore


== null)
{
TheSemaphore = nueva Semaphore(0, 10, "THESEMAPHORE");
}

 Clase de eventos   Los eventos son un tipo de objeto de kernel que tiene dos
estados, on y off. Estos estados permiten threads en una aplicación espere hasta que
un evento se señaliza a hacer algo específico. Hay dos tipos de eventos: reinicio
automático y reinicio manual. Cuando un
Evento de restablecimiento automático está señalizado, el primer objeto que espera para el
evento se vuelve a un no- estado señalado. Este comportamiento es similar al de un mutex. A
la inversa, un evento de restablecimiento manual permite todos los subprocesos que están
esperando a ser desbloqueados hasta algo restablece manualmente el evento a un estado
nonsignaled.
Estos eventos son representados como el AutoResetEvent y ManualResetEvent clases de
.NET Framework. Ambas clases heredan de una   clase EventWaitHandle común (que
hereda de la  clase WaitHandle).

Nota   .NET 2.0

EventWaitHandle, AutoResetEventy ManualResetEvent ,   las clases son nuevos en .NET


Framework 2.0.

Crear una nueva instancia de una clase de evento permite especificar el estado de la señal del
evento, como se muestra en el ejemplo siguiente:
' VB
Dim autoEvent como nuevo AutoResetEvent(true)
Dim manualEvent como nuevo ManualResetEvent(false)

// C#
AutoResetEvent autoEvent = new AutoResetEvent(true); ManualResetEvent manualEvent =
new ManualResetEvent(false);

La EventWaitHandle clase admite dos nuevos métodos que son específicas para trabajar con
eventos: Set y Reset. Estos métodos se utilizan para cambiar el evento de encendido y
apagado, como se muestra en el ejemplo siguiente:
' AutoEvent VB.set()
manualEvent.reset()

// C# autoEvent.set();
manualEvent.reset();

Al igual que los otros objetos del kernel, eventos le permiten especificar un nombre que
puede utilizarse para crear y abrirlos a través de AppDomain y los límites del proceso. El
apoyo a la denominada eventos está en el EventWaitHandle nivel. Al crear o abrir un evento
con nombre, usted necesitará tratar EventWaitHandle en lugar de las    clases
AutoResetEvent y ManualResetEvent. Cuando se crea un nuevo  objeto EventWaitHandle,
no sólo especifica el estado de la señal, sino también el tipo de evento es necesario. Por
ejemplo, puede utilizar el siguiente código para crear o abrir un evento con nombre:
' VB
Dim casode como EventWaitHandle = nada
Pruebe
Casode = EventWaitHandle.OpenExisting("CASODE")
Ex como WaitHandleCannotBeOpenedException capturas
' No se puede abrir el AutoResetEvent porque no existe.
Intente final

' Se crea si no existe.


Si casode no es nada luego
Casode = New EventWaitHandle(false, _
EventResetMode.AutoReset, "CASODE")
End If

// C#
EventWaitHandle casode = null;

Pruebe // intente abrir el evento


{
Casode = EventWaitHandle.OpenExisting("CASODE");
}
Capturas (WaitHandleCannotBeOpenedException)
{
// No se puede abrir el AutoResetEvent porque no existe.
}

// Crearlo si no existe si (casode == null)


{
Casode = new EventWaitHandle(false,
EventResetMode.AutoReset, "CASODE");
}

Laboratorio: usar un mutex  para crear un


Single Instance Application
En este laboratorio, crear una sencilla aplicación de consola en la que utilizará
un mutex para asegurarse de que sólo hay una instancia de la aplicación que se
ejecuta en cualquier punto. Si se produce un problema de completar un ejercicio,
los proyectos terminados están disponibles en el CD en la carpeta de código.
1.  Crear una nueva aplicación de consola denominada SingleInstance.
2.  En el archivo de código principal, incluir (o importación para Visual
Basic) System.Threading.
3.  En el  método Main de la aplicación de consola, crear un mutex local variable
y asignarle un valor null (o Nothing en Visual Basic).
4.  Crear una cadena constante para mantener el nombre de shared Mutex. Hacer
que el valor
"RUNMEONCE".
5.  Cree un  bloque try/catch.
6.  Dentro de la  sección try del  bloque try/catch, llame
el mutex.OpenExisting método, usando la cadena de constantes definidas en el
paso 4 como el nombre del Mutex. A continuación, asignar el resultado a
la  variable Mutex creados en el paso 2.
7.  Para la  sección de capturas del  bloque try/catch, coger
un WaitHandleCannotBeOpene- dException para determinar que la exclusión mutua
con nombre no existe.
8.  A continuación, pruebe el mutex variable creada en el paso 2 para null (o Nothing en
Visual
Basic) para ver si el mutex podría ser encontrado.
9.  Si el mutex no fue encontrada, crear la exclusión mutua con la cadena constante del
paso
4.
10.  Si el Mutex , fue encontrado cerca de la  variable Mutex y salir de la aplicación. El
código final podría tener este aspecto:
' VB
Imports System.Threading

Programa de clase

Sobrecargas Public Shared Sub Main() Dim oneMutex


como mutex = nada
Const MutexName As String = "RUNMEONLYONCE"

Intente ' intentar abrir el mutex


Mutex.OpenExisting Catch ex como
WaitHandleCannotBeOpenedException
' No se puede abrir el mutex porque no existe.
Intente final

' Se crea si no existe.


Si no es nada oneMutex luego
Mutex(true, MutexName)
Otra cosa
' Cerrar el mutex y salir de la aplicación
' Porque sólo podemos tener una instancia oneMutex.Close().
Volver
End If

Console.WriteLine("Nuestra Aplicación") Console.Read()


End Sub

Clase final

// C#
Using System.Threading;

Programa de clase
{
Static void main(String[] args)
{
Mutex > Const string = "RUNMEONLYONCE MutexName";
Pruebe // intentar abrir el mutex
{
Mutex.OpenExisting > }
Capturas (WaitHandleCannotBeOpenedException)
{
// No se puede abrir el mutex porque no existe.
}

// Crearlo si no existe si (>
null)
{
Mutex, MutexName(true);
}
Otra cosa
{
// Cerrar el mutex y salir de la aplicación
// Porque sólo podemos tener una instancia
oneMutex.Close();
Volver;
}

Console.WriteLine("Nuestra Aplicación");
Console.Read();
}
}

11.  Genere el proyecto y resolver cualquier error. Compruebe que sólo una
instancia de la aplicación se puede ejecutar a la vez.

Resumen de la lección
■   para realizar operaciones matemáticas atómico, utilice la  clase entrelazadas.
■   Parabloquear los datos, utilice el bloqueo de C# o Visual
Basic SyncLock sintaxis.
■   Para bloquear los datos con un objeto de sincronización, utilice la  clase
Monitor.
■   Para bloquear los datos donde varios lectores pueden acceder a los datos a la
vez, pero un escritor en un momento puede cambiar los datos, utilice
un ReaderWriterLock.
■   Parasincronizar subprocesos entre AppDomains o límites de proceso, utilice
un mutex.
■   para roscas de mariposa con un objeto de sincronización basada en recursos,
utilice un semáforo.
■   para roscas de señal entre AppDomains o límites de proceso, utilice un evento.

Examen de la lección
Puede utilizar las siguientes preguntas para poner a prueba su conocimiento de la
información en la lección 2, "Compartir datos". Las preguntas también están
disponibles en el CD complementario si prefiere revisarlos en forma electrónica.
Nota   Respuestas
Las respuestas a estas preguntas y explicaciones de por qué cada opción de respuesta
es correcta o no se encuentran en la sección "Respuestas" al final del libro.

1.  Suponiendo que no hay un bloqueo de escritura en su lugar, ¿cuántos lectores pueden


leer datos neously simulta- con un ReaderWriterLock?
A.  0
B.   1
C.   10
D.   Unlimited
2.  Cuál de las siguientes se pueden utilizar para sincronizar subprocesos en AppDomain
Y los límites del proceso? (Seleccione todos los que correspondan)
A.   Clase Monitor
B.    Clase mutex
C.    Clase Semaphore
D.   C#  o Visual Basic bloqueo de  palabra clave SyncLock
Lección 3: El modelo de programación asíncrona
A través de la mayor parte de .NET Framework, es posible realizar tareas de una
forma no lineal. Utilizando el modelo de programación asincrónica (APM)
definidos a través de los
.NET Framework, puede hacer que sus aplicaciones funcionen mejor, ser más
respon- sive, y utilizar los recursos del sistema que se están ejecutando en la mayor
medida posible.

Después de esta lección, será capaz de:


■   comprender el modelo de programación asincrónica (APM).
■   Utilice la   clase ThreadPool.
■   utilizar la   clase Timer.
■   Utilice la   interfaz IAsyncResult para realizar llamadas asincrónicas.
■   entender cómo funcionan las excepciones en el APM.
Lección Tiempo estimado: 40 minutos

Comprender la programación asincrónica


La programación asincrónica es simplemente permitir que algunas de las porciones
de código que se debe ejecutar en subprocesos separados. Esto se conoce como el
modelo de programación asincrónica (APM). En .NET Framework, muchas clases
admiten el uso del APM suministrando   y EndXXX BeginXXX versiones de
métodos. Por ejemplo, la  clase FileStream tiene un  método de lectura que lee los
datos de la secuencia. Para apoyar el modelo de APM, también soporta    métodos
BeginRead y EndRead. Este patrón de utilizar métodos
BeginXXX y EndXXX permite ejecutar métodos de forma asincrónica, como se
muestra en el ejemplo siguiente:
'

V
B
Buffer() Dim como byte = Nuevo Byte(100) {}

Dim nombre As String = _ String.Concat(Entorno.SystemDirectory, "\\mfc71.pdb")

Strm FileStream = new FileStream(filename, _


FileMode.Open, FileAccess.Read, FileShare.Read, 1024 _
FileOptions.Asynchronous)

' Realiza la llamada asincrónica


Como resultado Dim =
IAsyncResult _
Strm.BeginRead(Buffer, 0, buffer.Length, nada, nada)
' Trabajar aquí mientras usted espera
413 Chapter 7 Threading Lesson 3: The Asynchronous Programming Model 413

' Llamar a EndRead se bloqueará hasta que el trabajo esté completo Async
Dim numBytes as Integer = strm.EndRead(resultado)

' No se olvide de cerrar la secuencia strm.Close().

Console.WriteLine("Leer {0} bytes", numBytes)


Console.WriteLine(BitConverter.ToString(buffer)

// C#
Byte[] = new byte buffer[100].

String filename =
String.Concat(Entorno.SystemDirectory, "\\mfc71.pdb");

Strm FileStream = new FileStream(filename,


FileMode.Open, FileAccess.Read, FileShare.Read, 1024 FileOptions.Asynchronous);

// Hacer la llamada asincrónica


Resultado = IAsyncResult strm.BeginRead(Buffer, 0 buffer.Length, null null);

// Hacer algún trabajo aquí mientras usted espera

// Llamar EndRead se bloqueará hasta que el trabajo esté completo Async


Int numBytes = strm.EndRead(resultado);

// No olvide cerrar la secuencia strm.Close();

Console.WriteLine("Leer {0} bytes", numBytes);


Console.WriteLine(BitConverter.ToString(buffer);

Para entender cómo funciona esto, echemos un vistazo a la clase FileStream.Leer la firma
del método:
' VB
La función Read(ByVal array() como Byte, _ ByVal offset As
Integer, _
Recuento ByVal As Integer) As Integer

// C#
Read(int matriz byte[], int offset, int count);

La BeginRead se parece mucho al  método de lectura:


' VB
Función BeginRead(ByVal array() como Byte, _ ByVal offset As Integer, _
ByVal numBytes As Integer, _
ByVal userCallback como AsyncCallback _
ByVal stateObject como objeto IAsyncResult)
414 Chapter 7 Threading Lesson 3: The Asynchronous Programming Model 414

/
/

C
#
IAsyncResult BeginRead(matriz byte[], int offset, int numBytes, AsyncCallback
stateObject userCallback, objeto);

Las diferencias incluyen el hecho de que devuelve un IAsyncResult en lugar del


número de bytes leídos y dos parámetros que se agregan a la firma del método para
apoyar a APM. Estos dos parámetros se explicarán más adelante en la sección que
describe el estilo de manejo de retrollamada APM.
El  método EndRead pretende finalizar la operación asincrónica:
'

V
B
Función EndRead(ByVal asyncResult como IAsyncResult) As Integer

/
/

C
#
Int EndRead(IAsyncResult asyncResult);

Al final de la operación, usted llamará a EndRead con el  objeto IAsyncResult y


devolverá el número de bytes leídos. El  método BeginRead, por lo tanto, maneja
los datos entrando en la operación asincrónica y EndRead gestiona la devolución
de datos desde la operación asincrónica.
Debe haber una manera de hacer la operación asincrónica y saber cuándo o dónde
llamar a los  métodos EndXXX. Ahí es donde vienen en técnicas de rendezvous.

Modelos de
Rendezvous
Hay tres estilos de aprendizaje con el APM para lidiar con el manejo de la
finalización de la llamada en una llamada asincrónica: espere hasta el hecho, el
sondeo y la devolución de llamada. Veamos cada uno de estos.
Ait-W  El modelo Until-Done espere hasta el hecho modelo le permite iniciar el
asynchro- nous llamada y realizar otros trabajos. Una vez que el otro se hace el
trabajo, usted puede intentar finalizar la llamada y se bloqueará hasta que se
complete la llamada asincrónica. La siguiente
415 Chapter 7 Threading Lesson 3: The Asynchronous Programming Model 415

Fragmento de código
proporciona un ejemplo:
'

V
B
Buffer() Dim como byte = Nuevo Byte(100) {}

Dim nombre As String = _ String.Concat(Entorno.SystemDirectory, "\\mfc71.pdb")

Strm FileStream = new FileStream(filename, _


FileMode.Open, FileAccess.Read, FileShare.Read, 1024 _
FileOptions.Asynchronous)
416 Chapter 7 Threading Lesson 3: The Asynchronous Programming Model 416

' Realiza la llamada asincrónica strm.Read(Buffer, 0


buffer.Length)

Como resultado Dim = IAsyncResult _


Strm.BeginRead(Buffer, 0, buffer.Length, nada, nada)

' Trabajar aquí mientras usted espera

' Llamar a EndRead se bloqueará hasta que el trabajo esté completo Async

Dim numBytes as Integer = strm.EndRead(resultado)

' No se olvide de cerrar la secuencia strm.Close().

Console.WriteLine("Leer {0} bytes", numBytes)


Console.WriteLine(BitConverter.ToString(buffer)

// C#
Byte[] = new byte buffer[100].

String filename =
String.Concat(Entorno.SystemDirectory, "\\mfc71.pdb");

Strm FileStream = new FileStream(filename,


FileMode.Open, FileAccess.Read, FileShare.Read, 1024 FileOptions.Asynchronous);

// Hacer la llamada asincrónica strm.Read(Buffer, 0


buffer.Length);
Resultado = IAsyncResult strm.BeginRead(Buffer, 0 buffer.Length, null null);

// Hacer algún trabajo aquí mientras usted espera

// Llamar EndRead se bloqueará hasta que el trabajo esté completo Async


Int numBytes = strm.EndRead(resultado);

// No olvide cerrar la secuencia strm.Close();

Console.WriteLine("Leer {0} bytes", numBytes);


Console.WriteLine(BitConverter.ToString(buffer);

Cuando  se realiza la llamada BeginRead, null (o Nothing en Visual Basic) valores son
speci- zos para la devolución de la llamada y el estado de los objetos. Porque no vamos a
estar utilizando una devolución de llamada, son innecesarios. Después de algunos trabajos se
realiza, el código llama al  método EndRead y esto bloqueará la rosca hasta que se realiza la
llamada asincrónica.
417 Chapter 7 Threading Lesson 3: The Asynchronous Programming Model 417

Polling modelo  el método de sondeo es similar, con la excepción de que el


código de sondeo el IAsyncResult para ver si se ha completado. El siguiente
fragmento de código pro- porciona un ejemplo:
'

V
B
Buffer() Dim como byte = Nuevo Byte(100) {}

Dim nombre As String = _ String.Concat(Entorno.SystemDirectory, "\\mfc71.pdb")

Strm FileStream = new FileStream(filename, _


FileMode.Open, FileAccess.Read, FileShare.Read, 1024 _
FileOptions.Asynchronous)

' Realiza la llamada asincrónica


Como resultado Dim = IAsyncResult
_
Strm.BeginRead(Buffer, 0, buffer.Length, nada, nada)

' Poll probando para ver si completa


Aunque no result.IsCompleted
' Hacer más trabajo aquí si la llamada no se completa
Thread.Sleep(10
0) Fin mientras

' Acabado, por lo que podemos llamar EndRead y devolverá sin bloquear
Dim numBytes as Integer = strm.EndRead(resultado)

' No se olvide de cerrar la secuencia


strm.Close().

Console.WriteLine("Leer {0} bytes", numBytes)


Console.WriteLine(BitConverter.ToString(buffer)

/
/

C
#
Byte[] = new byte buffer[100].

String filename =
String.Concat(Entorno.SystemDirectory, "\\mfc71.pdb");

Strm FileStream = new FileStream(filename,


FileMode.Open, FileAccess.Read, FileShare.Read, 1024
FileOptions.Asynchronous);

// Hacer la llamada asincrónica


Resultado = IAsyncResult strm.BeginRead(Buffer, 0 buffer.Length, null null);
418 Chapter 7 Threading Lesson 3: The Asynchronous Programming Model 418

// Encuesta probando para ver si


mientras completa (!result.IsCompleted)
{
// Hacer más trabajo aquí si la llamada no se completa
Thread.Sleep(100).
}
419 Chapter 7 Threading Lesson 3: The Asynchronous Programming Model 419

// Acabado, por lo que podemos llamar EndRead y devolverá sin bloquear int numBytes =
strm.EndRead(resultado);

// No olvide cerrar la secuencia strm.Close();

Console.WriteLine("Leer {0} bytes", numBytes);


Console.WriteLine(BitConverter.ToString(buffer);

Llamando a la  propiedad IsCompleted del  objeto IAsyncResult devuelto por


el Begin- Leer, podemos continuar trabajando como sea necesario hasta que se complete la
operación.
Callback modelo  El modelo de devolución de llamada requiere que se especifique un
método de devolución de llamada en e incluir cualquier estado que necesitamos en el
método de devolución de llamada para terminar la llamada. El modelo de devolución de
llamada puede verse en el siguiente ejemplo:
' VB
Buffer compartido() As Byte = Nuevo Byte(100) {}

Shared Sub TestCallbackAPM() Dim nombre As


String = _
String.Concat(Entorno.SystemDirectory, "\\mfc71.pdb")

Strm FileStream = new FileStream(filename, _


FileMode.Open, FileAccess.Read, FileShare.Read, 1024 _ FileOptions.Asynchronous)

' Realiza la llamada asincrónica


Resultado = IAsyncResult strm.BeginRead(Buffer, buffer.0, Longitud, ...
Nueva AsyncCallback(CompleteRead), strm)
End Sub

// C#
Static byte[] = new byte buffer[100].

Static void TestCallbackAPM()


{
String filename =
String.Concat(Entorno.SystemDirectory, "\\mfc71.pdb");

Strm FileStream = new FileStream(filename,


FileMode.Open, FileAccess.Read, FileShare.Read, 1024 FileOptions.Asynchronous);

// Hacer la llamada asincrónica


Resultado = IAsyncResult strm.BeginRead(Buffer, buffer.0, longitud,
Nueva AsyncCallback(CompleteRead), strm);
}

En este modelo, estamos creando un nuevo  delegado AsyncCallback, especificando un


método para llamar (en otro hilo) cuando se complete la operación. Además, estamos
420 Chapter 7 Threading Lesson 3: The Asynchronous Programming Model 420

especificando algún objeto que podamos necesitar como el estado de la llamada. Para este
ejemplo, nosotros
421 Chapter 7 Threading Lesson 3: The Asynchronous Programming Model 421

Enviar el objeto Stream en porque necesitaremos llamada EndRead y cerrar la


secuencia.
El método que creamos que se llama al final de la llamada se verá algo como esto:
' VB
Shared Sub CompleteRead(ByVal resultado como IAsyncResult)
Console.WriteLine("Lectura completada")

Dim strm como FileStream = CType(result.AsyncState, FileStream)

' Acabado, por lo que podemos llamar EndRead y devolverá sin bloquear
Dim numBytes as Integer = strm.EndRead(resultado)

' No se olvide de cerrar la secuencia


strm.Close().

Console.WriteLine("Leer {0} bytes", numBytes)


Console.WriteLine(BitConverter.ToString(buffer)
End Sub

// C#
Static void CompleteRead(IAsyncResult resultado)
{
Console.WriteLine("Lectura completada");

FileStream = (STRM) resultado de FileStream.AsyncState;

// Acabado, por lo que podemos llamar EndRead y devolverá sin bloquear int numBytes
= strm.EndRead(resultado);

// No olvide cerrar la secuencia strm.Close();

Console.WriteLine("Leer {0} bytes", numBytes);


Console.WriteLine(BitConverter.ToString(buffer);
}

Observe que en lugar de mantener el IAsyncResult, el código anterior se pasa como


un parámetro del método de devolución de llamada. Entonces podemos recuperar
el  objeto FileStream que aprobamos en como estado. A partir de ahí, todo es
idéntico a los otros modelos.

Excepciones y
APM
Cuando se usa el APM, podría haber operaciones que generan excepciones durante
el procesamiento asincrónico de una solicitud. Para permitir esto, las excepciones
son actu- Aliada lanzada durante la  llamada EndXXX. No se genera la excepción
422 Chapter 7 Threading Lesson 3: The Asynchronous Programming Model 422

en el momento en que se produce una excepción. (Si así fuera, ¿cómo podría
coger?) si necesita manejar cualquier
423 Chapter 7 Threading Lesson 3: The Asynchronous Programming Model 423

Excepciones, debe hacerlo en el momento en que la  llamada se realiza EndXXX. Por


ejemplo, podríamos cambiar el EndRead llamada del ejemplo anterior para detectar e
informar de cualquier IOExceptions que fueron tirados, así:
' VB
Dim numBytes as Integer = 0
Pruebe
NumBytes = strm.EndRead(resultado) Catch
Console.WriteLine("se produjo una excepción de E/S") End Try

// C#
Int numBytes = 0.
Pruebe
{
NumBytes = strm.EndRead(resultado);
}
Capturas (IOException)
{
Console.WriteLine("se produjo una excepción de E/S");
}

Manejo de excepciones de aplicación de Windows


Forms
En aplicaciones de Windows Forms, cuando se produce una excepción en cualquier
lugar de la aplicación (en el subproceso principal o durante llamadas asincrónicas), un
estándar de dia- cuadro de registro se muestra a los usuarios para informarles acerca de
la excepción y el proceso es asesinado. Como desarrollador de aplicaciones, usted
tiene la oportunidad de determinar cómo manejar esta situación. Puede hacerlo
mediante el registro para el  evento ThreadException sobre la  clase de aplicación.
Para controlar estas excepciones, debe inscribirse en este evento de la siguiente
manera:
' VB
Application.ThreadException AddHandler, _ AddressOf
Me.Application_ThreadException
Application.Run(New Form1)

Private Sub Application_ThreadException compartida(ByVal sender As Object, _ ByVal e As


ThreadExceptionEventArgs)
MessageBox.Show(String.Format("{0}", e.Exception)
End Sub

// C#
Application.ThreadException += new
ThreadExceptionEventHandler(Application_ThreadException);
Application.Run(new Form1();
424 Chapter 7 Threading Lesson 3: The Asynchronous Programming Model 424

Static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)


{
MessageBox.Show(String.Format("{0}", e.Exception);
}

El  delegado ThreadExceptionEventHandler especifica la convención de


llamada del controlador de eventos, mientras que la  clase
ThreadExceptionEventArgs sostiene la excep- ción de que se llamaba.

Más info
APM
Para obtener más información sobre la APM, ver el libro de Jeffrey
Richter, CLR via C#, segunda edición  (Microsoft
Press, 2006), en concreto, en
las páginas 599-620.

Utilizando el ThreadPool
Anteriormente en este capítulo, aprendió a crear sus propios subprocesos para
realizar programación chronous asyn-. En muchos casos, crear su propio hilo no es
necesaria ni sugerido. El sistema de enrollado en .NET admite un conjunto de
subprocesos incorporada que puede utilizarse en muchas situaciones donde usted
podría esperar a crear sus propios hilos. Por ejemplo, tomemos el mismo código
que hemos usado en la lección 1 para realizar algún trabajo en un subproceso:
'

V
B
Shared Sub WorkWithParameter(ByVal o como objeto) Dim
info As String = CType(oh, String)

Para x= 0 a 9
Console.WriteLine("{0}: {1}", info, _
Thread.CurrentThread.ManagedThreadId)
' Ralentizar la rosca y deje que otros subprocesos de trabajo
Thread.Sleep(10)
Siguiente
End Sub

// C#
Static void WorkWithParameter(object o)
{
Información = cadena (string) o;
(Int x = 0; x < 10; ++x)
{
425 Chapter 7 Threading Lesson 3: The Asynchronous Programming Model 425

Console.WriteLine("{0}: {1}", info,


Thread.CurrentThread.ManagedThreadId);
426 Chapter 7 Threading Lesson 3: The Asynchronous Programming Model 426

// Ralentizar la rosca y deje que otros subprocesos de trabajo


Thread.Sleep(10).
}
}

En lugar de crear un nuevo subproceso  y controlarla, podemos utilizar el ThreadPool para


hacer este trabajo utilizando su  método QueueUserWorkItem:
' VB
Dim workItem como nueva función WaitCallback(WorkWithParameter)
Si no ThreadPool.QueueUserWorkItem,"ThreadPooled(workItem") y a continuación
Console.WriteLine("No se pudo elemento de cola") End If

// C#
Función WaitCallback workItem = nueva función WaitCallback(WorkWithParameter);
Si (!ThreadPool.QueueUserWorkItem, "ThreadPooled(workItem")
{
Console.WriteLine("No se pudo elemento de cola");
}

Este no es un atajo para crear un subproceso para usted, pero en lugar de .NET mantiene un
conjunto de subprocesos que pueden ser reutilizados en su aplicación. Este conjunto de
subprocesos es más rápido, ya que las roscas son reutilizados como necesario, ahorrando
costos de instalación costosa. Además, ayuda a regular el número de subprocesos que se
están ejecutando en cualquier momento en un proceso por la cola de trabajos a ser
realizados. Como subprocesos están disponibles, el grupo de subprocesos puestos el nuevo
trabajo a la rosca.
La  clase ThreadPool admite métodos no sólo para la cola de elementos de trabajo, sino
también para manejar el ThreadPool. Los métodos más importantes de la  clase ThreadPool
están detalladas en la tabla 7-13.
Tabla 7-13  ThreadPool  métodos
estáticos
Nombre Descripción
GetAvailableThreads  devuelve el número de subprocesos que están
disponibles para utilizar en la piscina.
GetMaxThreads  devuelve el número máximo de threads este
proceso ThreadPool puede soportar.
GetMinThreads  devuelve el número mínimo de subprocesos que se
crean en cualquier momento. Esto representa el número
De roscas precompilados para su uso en el grupo de
subprocesos.
427 Chapter 7 Threading Lesson 3: The Asynchronous Programming Model 427

Tabla 7-13  ThreadPool  métodos estáticos

Nombre Descripción
QueueUserWorkItem  añade una pieza de trabajo para el grupo
de subprocesos para ser ejecutada por un
subproceso disponible.
RegisterWaitForSingleObject  permite una devolución de llamada que se
expidan para una determinada
WaitHandle cuando
ese WaitHandle es señalizado.
SetMaxThreads  Establece el número máximo de threads
en este proceso el grupo de subprocesos.
SetMinThreads  establece el número mínimo de subprocesos
creados en el grupo de subprocesos.
Pulida Utilizado para la cola de E/S de archivos
UnsafeQueueNativeO asincrónica Completion Ports, utilizando
ver- las    estructuras superpuestas y
NativeOverlapped. Consulte el Capítulo 13,
"interoperabilidad", para obtener más
información.
   Colas UnsafeQueueUserWorkItem un elemento de trabajo para un
subproceso para escenarios de alto
rendimiento. No propaga la pila de llamadas
o contexto de ejecución información al
nuevo subproceso.
UnsafeRegisterWait Permite una devolución de llamada que se
For- SingleObject expidan para una
determinada WaitHandle cuando
ese WaitHandle es señalizado. Para su uso en
escenarios de alto rendimiento. No
Propagar la pila de llamadas o contexto de
ejecución infor- mación al nuevo subproceso.

Limitar el número de subprocesos


en ThreadPool
428 Chapter 7 Threading Lesson 3: The Asynchronous Programming Model 428

La  clase ThreadPool admite métodos estáticos para configurar el número mínimo


y máximo de subprocesos en el grupo de subprocesos. En la mayoría de los casos,
el número de subprocesos en el grupo se establece en un número óptimo. Si usted
encuentra que su aplicación se limitó por los subprocesos del grupo de subprocesos,
puede establecer los límites que usted mismo. Cambiar estos números sólo afecta al
proceso actual.
Hay dos tipos de situaciones donde se desea cambiar el subproceso del grupo de
subprocesos límites: Hilo de rosca de inicio de inanición, y velocidad.
429 Chapter 7 Threading Lesson 3: The Asynchronous Programming Model 429

En un thread-inanición escenario, su aplicación está utilizando el grupo de subprocesos, pero


se ve obstaculizado porque tiene demasiados elementos de trabajo y que están a punto de
alcanzar el número máximo de subprocesos en el grupo. Para establecer el límite máximo de
subprocesos para su aplica- ción, puede simplemente usar ThreadPool.SetMaxThreads así:
' VB
Los subprocesos como Integer Dim
Dim completionPorts As Integer

' Obtiene el número máximo de threads del ThreadPool


ThreadPool.GetMaxThreads(threads completionPorts)

' Establecer y aumentar el número


ThreadPool.SetMaxThreads(threads completionPorts + 10 + 100)

// C#
Int los hilos.
Int completionPorts;

// Obtener el número máximo de threads del ThreadPool


ThreadPool.GetMaxThreads(out threads, fuera completionPorts);

// Establecer y aumentar el número


ThreadPool.SetMaxThreads(threads completionPorts + 10 + 100);

En primer lugar, este código obtiene el número máximo de subprocesos y los puertos de
finalización desde el ThreadPool.  A los puertos de finalización es un extraordinario nivel de
kernel que objeto subproceso asincrónico se utiliza para realizar operaciones de E/S de
archivo. Normalmente hay muchos más puertos de terminación que el número de
subprocesos administrados. A continuación, puede establecer la cantidad de subprocesos
simplemente especificando el nuevo techo valores que desee utilizar.
En los casos en que los costos de inicio mediante el grupo de subprocesos son caros,
aumentando el número mínimo de subprocesos puede mejorar el rendimiento.  El número
mínimo de subprocesos determina cuántos subprocesos se crea inmediatamente y esperar a
nuevos trabajos. Normalmente, el ThreadPool limita el número de hebras que se crean
durante la ejecución de un proceso a dos por segundo. Si las aplicaciones necesitan más
subprocesos creados más rápido, puede aumentar este tamaño. Cambiando el número
mínimo de subprocesos es similar a cambiar el número máximo de subprocesos:
' VB
Los subprocesos como Integer Dim
Dim completionPorts As Integer

' Se obtiene el número de threads del ThreadPool


ThreadPool.GetMinThreads(threads completionPorts)
430 Chapter 7 Threading Lesson 3: The Asynchronous Programming Model 430

' Establecer y aumentar el número


ThreadPool.SetMinThreads(threads, completionPorts + 10 + 100)

/
/

C
#
Int los
hilos.
Int completionPorts;

// Obtener el número de threads del ThreadPool


ThreadPool.GetMinThreads(out threads, fuera completionPorts);

// Establecer y aumentar el número


ThreadPool.SetMinThreads(threads, completionPorts + 10 + 100);

Este código es idéntica a la del ejemplo anterior, excepto que usa la  y
GetMinThreads
 Métodos
SetMinThreads.

ThreadPool  y Wait
Handle
En la lección 2, se enteró de que todos los objetos de sincronización a nivel de
kernel (Mutex, sema- phore, y evento) utilizar WaitHandle como su clase base. El
grupo de subprocesos también proporciona un mecanismo para utilizar subprocesos
en la piscina a esperar en estos manipuladores y disparar las devoluciones de
llamada a métodos cuando el WaitHandles están señalizados. Esto se hace
llamando a la rosca de la piscina.RegisterWaitForSingleObject, como se muestra
en el ejemplo siguiente:
'

V
B

' Crear un mutex y comenzar con


Dim mutex como nuevo
Mutex(true)

' Regístrese para una notificación


ThreadPool.RegisterWaitForSingleObject(mutex, _
Nueva WaitOrTimerCallback(MutexHasFired), Nada, el tiempo.Infinita, True)

' La señal mutex para causar la rosca al fuego


mutex.ReleaseMutex()
431 Chapter 7 Threading Lesson 3: The Asynchronous Programming Model 431

/
/

C
#

// Crear un mutex y comenzar con


Mutex mutex = nuevo
Mutex(true);

// Registrarse para una notificación


ThreadPool.RegisterWaitForSingleObject(mutex,
Nueva WaitOrTimerCallback(MutexHasFired), nulo Timeout.Infinite, true);

// Señal el mutex para causar la rosca al fuego


mutex.ReleaseMutex();

El  método toma el RegisterWaitForSingleObject WaitHandle objeto, así como del


delegado que apunta a un método que toma un objeto (que representa el estado de
conversación
432 Chapter 7 Threading Lesson 3: The Asynchronous Programming Model 432

Especificado en la llamada al método), y un valor de tipo Boolean que indica si se ha


agotado el tiempo de espera en lugar de la WaitHandle está señalizando. El  método de
devolución de llamada MutexHasFired podría tener este aspecto:
' VB
Shared Sub MutexHasFired(ByVal estado como Object, ByVal detenida As Boolean) Si caducado = True Then
Console.WriteLine("Mutex timed out")
Otra cosa
Console.WriteLine("got señaliza mutex")
End If
End Sub

// C#
Static void MutexHasFired(estado de objeto, bool caducado)
{
Si (caducado)
{
Console.WriteLine("Mutex timed out");
}
Otra cosa
{
Console.WriteLine("got señaliza mutex");
}
}

La   clase
SynchronizationContext
Escritura de código asincrónico en diferentes ambiente presenta diferentes problemas. Los
modelos de subprocesos en Windows para ms son diferentes t nad t hreading modelos en
ASP.NET. Un modelo de subprocesos de Windows Forms prefiere que cualquier código de
la interfaz de usuario se ejecuta directamente en la interfaz de usuario principal de "hilo".
Esto contrasta con ASP.NET, donde la mayoría del trabajo se realiza dentro de un grupo de
subprocesos para que llamadas asincrónicas puede ocurrir en cualquier subproceso en ese
grupo. Para lidiar con estos diferentes modelos de subprocesos, el marco de trabajo .NET
admite la  clase SynchronizationContext, que permite escribir código sin saber el modelo de
subprocesos de la aplicación en particular.
Para usar la  clase SynchronizationContext, primero debe obtener una instancia de la  clase
nizationContext Synchro- llamando a la  propiedad estática Current de
la SynchronizationCon- Clase de texto. Una vez que tenga una instancia de la  clase
SynchronizationContext, puede tomar cualquiera de las siguientes acciones:
■   Llame al    método Send de la clase SynchronizationContext para llamar a algún
código. Enviar llamadas ejecutará el código (quizá en un subproceso independiente),
sino que se bloqueará hasta que el código de ejecución completa hasta su regreso.
433 Chapter 7 Threading Lesson 3: The Asynchronous Programming Model 433

' VB
Dim As ctx SynchronizationContext = SynchronizationContext.Current

Ejecuta el código ' Enviar sincrónicamente


ctx.Enviar(AddressOf RunMe, "Hi")

// C#
SynchronizationContext ctx = SynchronizationContext.Current;

// Enviar ejecuta el código sincrónicamente


ctx.Enviar(RunMe, "Hi");

■   la  clase SynchronizationContext llamada el  método Post para llamar a algún


código. Llamar Post es más como el fuego y olvidarse de que se pone en cola
la solicitud y devuelve inmediatamente si es posible.
' VB
Dim As ctx SynchronizationContext = SynchronizationContext.Current

' El código Post ejecuta asincrónicamente


ctx.Post(AddressOf RunMe, "Hi")

// C#
SynchronizationContext ctx = SynchronizationContext.Current;

// El código Post ejecuta asincrónicamente


ctx.Post(RunMe, "Hi");

Dependiendo del modelo de subprocesos, tanto los    métodos Send y Post podría


no volver inmediatamente. Si se está ejecutando el código de forma asincrónica no
es compatible (como en el modelo de subprocesos de Windows Forms), ambos
métodos se ejecutará el código y regresar después de la ejecución.
Los    métodos Send y Post no devolver cualquier tipo de objeto sobre el que se
puede esperar en u obtener un valor de retorno (como la  interfaz IAsyncResult que
estábamos usando en los ejemplos anteriores). La  clase SynchronizationContext es
útil si necesita ejecutar código arbitrario pero no actúan sobre los resultados de ese
código.

Utilizando   objetos de temporizador.


Un temporizador es un objeto básico que va a disparar una llamada asincrónica a
un método basado en el tiempo.  Los objetos de temporizador son ejemplificadas
por la  clase Timer en el  espacio de nombres System.Threading. Cuando cree
un temporizador, debe especificar un  delegado TimerCallback que apunta al
método que desea ejecutar cuando el temporizador se dispara. Además, especifique
cuánto
434 Chapter 7 Threading Lesson 3: The Asynchronous Programming Model 434

Hasta que  se inicie el temporizador (con cero indicando inmediatamente) y cuánto tiempo
entre  disparos de temporizador. Por ejemplo, puede crear un temporizador que
desencadenará un  método TimerTick cada segundo, empezando de inmediato, como este:
' VB
Dim tm como temporizador = New Timer(Nueva TimerCallback(TimerTick) _ nada, 0, 1000).

Shared Sub TimerTick(ByVal estado como objeto)


Console.WriteLine("Tick")
End Sub

// C#
Tm = nuevo temporizador Timer(nueva TimerCallback(TimerTick), NULL, 0, 1000).

Static void TimerTick(object)


{
Console.WriteLine("Tick");
}

La  clase Timer también admite un  método de cambio que le permite re-especificar cuándo
 Los incendios del temporizador y el intervalo de tiempo entre las garrapatas:
' VB
Dim tm como temporizador = New Timer(Nueva TimerCallback(TimerTick) _ nada, 0, 1000).
' Uso de infinito para especificar para detener el temporizador por ahora
tm.Cambiar(Time.Infinito, 1000).

// C#
Tm = nuevo temporizador Timer(nueva TimerCallback(TimerTick), NULL, 0, 1000).
// El uso de infinito para especificar para detener el temporizador por ahora
cambio(tm.Timeout.Infinite, 1000).

Nota     clases Timer

Las tres   clases Timer son:


■ System.Threading.Timer  Esta es la clase discutidos en esta lección.
■ System.Windows.Forms.Timer  Esta clase timer desencadena eventos en el mismo
subproceso que el formulario usando el mensaje de ventana
WM_TIMER.  System.Windows.Forms.Timer  temporizador no está relacionado con
el archivo System.Threading.Timer  temporizador que usamos en esta lección.
■ System.Timers.Timer  una envoltura alrededor de
la System.Threading.Timer  para apoyar colocándolo sobre diferentes superficies de
diseño de Visual Studio.
435 Chapter 7 Threading Lesson 3: The Asynchronous Programming Model 435

Laboratorio: utilizar la   cola de ThreadPool para


elementos de trabajo
En esta pr ctica, usted creará una aplicación que usa el grupo de subprocesos para
hacer cola para llamar a métodos en subprocesos separados. Si se produce un
problema de completar un ejercicio, los proyectos terminados están disponibles en
el CD en la carpeta de código.
1.  Crear una aplicación de consola en blanco con el nombre ThreadPoolDemo.
2.  Incluir (o importación para Visual Basic) el  espacio de nombres
System.Threading.
3.  Crear un nuevo método para simplemente mostrar algún texto.
Llame ShowMyText. Acepta un parámetro de tipo object, y llamarlo Estado.
4.  Crear una nueva variable de cadena dentro del  método ShowMyText, y echó
el estado
Parámetro para una cadena mientras se almacenan en la variable de texto
nuevo.
5.  Dentro del  método ShowMyText, escribir el ManagedThreadId del
subproceso actual y escribir la nueva cadena en la consola.
6.  En el  método Main de la aplicación de consola, cree una nueva instancia de la
Función WaitCallback delegado que se refiere al  método ShowMyText.
7.  Utilice el ThreadPool para poner en cola varias llamadas a la función
WaitCallback delegado, speci- fying diferentes cadenas como el estado del
objeto. El código podría tener este aspecto:
' VB
Imports System.Threading

Programa de clase
Shared Sub Main(ByVal args() As String Dim)
devolución de llamada como función
WaitCallback = _
Nueva función WaitCallback(AddressOf ShowMyText)

ThreadPool.QueueUserWorkItem(llamada "Hello")
ThreadPool.QueueUserWorkItem(llamada "Hi")
ThreadPool.QueueUserWorkItem(llamada "Heya")
ThreadPool.QueueUserWorkItem(callback, "Adiós").

Console.Rea
d() End Sub

Shared Sub ShowMyText(ByVal estado como objeto) Dim


mitexto As String = CType(estado, String)
436 Chapter 7 Threading Lesson 3: The Asynchronous Programming Model 436

Console.WriteLine("Subproceso: {0} - {1}" _


Thread.CurrentThread.ManagedThreadId, Mitexto)

En
d
Su
b
Clase final
437 Chapter 7 Threading Lesson 3: The Asynchronous Programming Model 437

// C#
Using System.Threading;

Programa de clase
{
Static void main(String[] args)
{
Función WaitCallback callback = nueva función WaitCallback(ShowMyText);

ThreadPool.QueueUserWorkItem(llamada "Hello");
ThreadPool.QueueUserWorkItem(llamada "Hi");
ThreadPool.QueueUserWorkItem(llamada "Heya");
ThreadPool.QueueUserWorkItem(callback, "Adiós");

Console.Read();
}

Static void ShowMyText(object)


{
Cadena mitexto = (string)estado;

Console.WriteLine("Subproceso: {0} - {1}",


Thread.CurrentThread.ManagedThreadId, Mitexto);
}
}

8.  Genere el proyecto y resolver cualquier error. Compruebe que la aplicación de


consola suc- logre muestra cada una de las llamadas a los  métodos ShowMyText
en la consola. Tenga en cuenta que algunos de los elementos de trabajo pueden ser
ejecutados en diferentes subprocesos.

Resumen de la lección
■   El modelo de programación asincrónica (APM) puede mejorar la experiencia del
usuario al permitir que varias operaciones se realizan simultáneamente, al mismo
tiempo que se mejora la capacidad de respuesta de una aplicación.
■   para realizar operaciones asincrónicas sin la sobrecarga de la  clase Thread, utilice
la  clase ThreadPool.
■   crear periódicamente llamadas recurrentes, utilice la  clase Timer.
■   Para
recuperar el resultado de una operación asincrónica, utilice la  interfaz
IAsyncResult.
■   estar
preparados para atrapar excepciones al completar una operación asincrónica
(Generalmente con el  método EndXXX).

Examen de la lección
438 Chapter 7 Threading Lesson 3: The Asynchronous Programming Model 438

Puede utilizar las siguientes preguntas para poner a prueba su conocimiento de la


información en la lección 3, "El modelo de programación asíncrona." Las preguntas
también están disponibles en el CD complementario si prefiere revisarlos en forma
electrónica.
439 Chapter 7 Threading Lesson 3: The Asynchronous Programming Model 439

Nota   Respuestas
Las respuestas a estas preguntas y explicaciones de por qué cada opción de
respuesta es correcta o no se encuentran en la sección "Respuestas" al final
del libro.

1.  ¿Cuál es el método de la  clase ThreadPool se han utilizado para


el ThreadPool ejecutar algún código especificado en las roscas de la piscina?
(Seleccione todos los que correspondan).
A.  ThreadPool.RegisterWaitForSingleObject
B.   ThreadPool.QueueUserWorkItem
C.   ThreadPool.UnsafeRegisterWaitForSingleObject
D.   ThreadPool.UnsafeQueueUserWorkItem
2.  ¿Cómo se puede detener temporalmente
un temporizador de encendido?
A.  Llamar a Dispose en el temporizador.
B.    Temporizador de llamadas.Cambiar, y defina los valores de tiempo
de Timeout.Infinite.
C.   Deje el  objeto Timer fuera de ámbito.
D.    Temporizador de llamadas.Cambiar, y defina los valores de tiempo a
cero.
Chapter 7 Review 431

Repaso del cap tulo


Para practicar y reforzar los conocimientos aprendidos en este capítulo, puede realizar
las siguientes tareas:
■   Revisar el resumen del capítulo.
■   Revisar la lista de términos clave que se introdujo en este capítulo.
■   Complete los casos. Estos escenarios configurar situaciones del mundo real
involv- ción de los temas de este capítulo, y le pedimos a usted para crear una
solución.
■   completar las prácticas sugeridas.
■   tomar un examen de práctica.

Resumen del capítulo


■   La  clase Thread puede utilizarse para crear múltiples rutas simultáneo de ejecu-
ción en sus propias aplicaciones.
■   utilizando el bloqueo (SyncLock en Visual Basic) Palabra clave le permitirá
escribir thread- un acceso seguro a los datos de su código.
■   usted debe ser muy cuidadoso en escribir código seguro de rosca para evitar
situaciones de bloqueo.
■   El ReaderWriterLock clase puede usarse para escribir código thread-safe que es
menos propenso a permitir sólo un subproceso en un momento para acceder a sus
datos.
■   El WaitHandle clasesderivadas (Mutex, Semaforo, y  clases de eventos)
ejemplifican
Sistema operativo Windows a nivel de objetos de sincronización.
■   Gran parte de .NET Framework es compatible con el modelo de programación
asincrónica (APM) para permitir la ejecución asíncrona de código sin tener que
tratar directamente con el ThreadPool o hilos.
■   La  clase ThreadPool es una práctica que permite la rápida creación de
subprocesos para la cola hasta que se ejecute el código, así como para
esperar WaitHandle objetos derivados.
■   Lostemporizadores  son objetos útiles para activar el código en subprocesos
separados a intervalos específicos.

Términos clave
Chapter 7 Review 432
¿ Sabes lo que significan estos términos clave? Puede verificar sus respuestas mediante
la búsqueda de los términos en el glosario al final del libro.
432   Capítulo 7 Comentario

■   modelo de programación asíncrona


■   thread

■   Los objetos de kernel de Windows

Casos
En los siguientes casos , podrá aplicar lo que ha aprendido acerca de los temas de
este capítulo. Usted puede encontrar las respuestas a estas preguntas en la sección
de "respuestas" al final de este libro.

Caso práctico 1: Mejorar el procesamiento del servidor


Usted trabaja para una pequeña empresa de Internet. Como uno de sus
programadores, usted cre- ados una sencilla aplicación que lee los datos de una
base de datos una vez a la semana y envía correos electrónicos a los usuarios
registrados del sitio Web principal. La compañía ha estado haciendo bien y tiene
una gran base de datos de usuarios (más de 100.000). Ahora que el número de
usuarios istered reg- ha aumentado dramáticamente, su herramienta está tomando
demasiado tiempo para enviar e-mails. Tu jefe necesita que lo hacen mucho más
rápido.

Entre
vistas
La siguiente es una lista de personal de la empresa entrevistados y sus
declaraciones:
■   es jefe de departamento  "hemos notado que cuando se ejecute la
aplicación en nuestro servidor no se consume mucho tiempo de CPU. Más de
una CPU, pero los otros tres CPU son completamente sin utilizar."

Preg
unta
s
Responda las siguientes preguntas para el
administrador:
1.  ¿Por qué es la aplicación actual no utiliza todas las CPU?
2.  ¿Cómo va a resolver el problema de rendimiento?
3.  ¿Cómo sabes que tu aplicación no utilice demasiados subprocesos y traer una
máquina al detener?

Caso práctico 2: Múltiples aplicaciones


Usted es un desarrollador para una pequeña empresa que se especializa en el
desarrollo de instrumento- software de supervisión. La empresa crea una serie de
aplicaciones que cada monitor un conjunto diferente de instrumentos.
Lamentablemente, la mayoría de estos instrumentos aún utilizar interfaces.
Capítulo 7 Revisión  433

El sistema que sólo puede tener un único proceso de lectura de la interfaz en un


momento. El administrador necesita usted para crear un plan para asegurarse de que sólo
una de las aplicaciones de la empresa pueden acceder a la interfaz en un momento.

Pregunt
as
Responda las siguientes preguntas para el
administrador:
1.  ¿Cómo se puede sincronizar las aplicaciones para acceder a la interfaz de a uno por
vez?
2.  Cómo esto afectará el rendimiento de las aplicaciones?

Prácticas recomendadas
Que le ayudarán a dominar los objetivos contemplados en el presente capítulo, complete
las siguientes tareas.

Crear una   aplicación ThreadPool


Para esta tarea, se deben completar un mínimo de prácticas 1 y 2. Usted puede
completar en prácticas- tice 3 para una comprensión más profunda del ThreadPool.

Práctic
a 1
■   Crear una aplicación de prueba que escribe los datos en la consola, incluido el
subproceso que está utilizando el código.
■   Utilice el ThreadPool para cola 20 instancias de los datos de escritura de código.
■   Nota cuántos hilos se utilizan y con qué frecuencia son reutilizadas de la piscina
(observando el ManagedThreadId utilizado en diferentes instancias del código).

Práctic
a 2
■   muestran el tamaño mínimo y máximo del ThreadPool llamando a la rosca de la
piscina.GetMinThreads y ThreadPool.GetMaxThreads métodos.
■   Cambiar el número mínimo y máximo de ThreadPool's subprocesos por disminuir
y aumentar las roscas utilizando
el ThreadPool.SetMinThreads  ThreadPool.SetMaxThreads y métodos.
■   ejecutarla aplicación con diferentes configuraciones para ver cómo el grupo de
subprocesos funciona de forma diferente.
434 Chapter 7 Review

Prác
tica
3.
■   tomar la aplicación a diferentes configuraciones de CPU (CPU Hyper- CPU,
roscado y varias CPU), y ver cómo funciona el grupo de subprocesos ferently
dif- y cómo el número mínimo y máximo de subprocesos es distinta en
diferentes plataformas de CPU.

Tomar un Test
de práctica
Los tests de práctica en este libro's Companion CD ofrecen muchas opciones. Por
ejemplo, puede hacerse la prueba en un solo examen objetivo, o puede probar usted
mismo en todos los exámenes de certificación 70-536 contenido. Puede configurar
la prueba para que simula cuidadosamente la expe- riencia de tomar un examen de
certificación, o puede configurarlo en modo de estudio, de modo que usted puede
mirar las respuestas correctas y explicaciones después de responder a cada
pregunta.

Más info  práctica de pruebas


Para obtener más información acerca de la prueba de la práctica todas las
opciones disponibles, consulte la sección "Cómo usar los tests de práctica"
La sección en este manual de introducción.
Capítulo 8
Los dominios de aplicación y
servicios
En este capítulo se tratan dos temas distintos: los dominios de aplicación y servicios.
Dominios de aplica- ción  permiten llamar a ensamblados externos con la máxima
eficacia y seguridad. Los servicios son un tipo especial de ensamblado que se ejecuta en
segundo plano, pre- mandará ninguna interfaz de usuario, y se controla mediante el uso
de herramientas especiales. En este capítulo se dis- cusses cómo crear y configurar
dominios de aplicación, y cómo desarrollar e instalar los servicios.

Objetivos del examen en


este capítulo:
■   Crearuna unidad de aislamiento para Common Language Runtime de .NET
Framework
Aplicación mediante el uso de dominios de aplicación. (Consultar el espacio de
nombres System).
❑   crean un dominio de aplicación.
❑   Descargar un dominio de aplicación.
❑   Configurar un dominio de aplicación.
❑   recuperar la información de configuración de un dominio de aplicación.
❑   cargar ensamblados en un dominio de aplicación.
■   implementar, instalar y controlar un servicio. (Consulte la
sección System.ServiceProcess
Espacio de nombres).
❑   heredan de la  clase ServiceBase
❑     clase ServiceController y  clase ServiceControllerPermission
❑   ServiceInstaller  y ServiceProcessInstaller class
❑   SessionChangeDescription estructura y  enumeración SessionChangeReason

Las lecciones de
este capítulo:
■   Lección 1: crear dominios de aplicación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 437
■ La   Lección 2: Configurar dominios de aplicación . . . . . . . . . . . . . . . . . . . . . . . . . . .
448
■ La   Lección 3: Crear servicios de Windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
457

435
Antes de empezar
Para completar las lecciones de este capítulo, debe estar familiarizado con Microsoft
Visual
Basic o en C# y se sienten cómodos con las siguientes tareas:
■   Crearuna aplicación de consola en Microsoft Visual Studio, usando Visual
Basic o C#.
■   Sistema de agregar referencias a bibliotecas de clases para un proyecto.
■   Crear archivos de texto.
■   Agregar eventos en el registro de eventos.
Lección 1: crear dominios de aplicación
A menudo los desarrolladores necesitan ejecutar un ensamblado externo. Sin embargo,
la ejecución de un ensamblado externo puede conducir al uso de recursos ineficiente y
vulnerabilidades de seguridad. La mejor forma de gestionar estos riesgos es crear un
dominio de aplicación y llame a la Asamblea desde dentro del entorno protegido.

Después de esta lección, será


capaz de:
■   describir el propósito de un dominio de aplicación.
■   Escribir código que hace uso de la   clase AppDomain.
■   Crear un dominio de aplicación.
■   lanzar una asamblea dentro de un dominio de aplicación.
■   descargue el dominio de aplicación.
Lección Tiempo estimado: 20
minutos

¿Qué es un dominio de aplicación?


Un dominio de aplicación es un contenedor lógico que permite varios ensamblados para
ejecutar dentro de un único proceso, pero les impide acceder directamente a otras
asambleas" recuerdos. Los dominios de aplicación ofrecen muchas de las características
de un proceso, como sepa- tasa de espacios de la memoria y el acceso a los recursos. Sin
embargo, los dominios de aplicación son más eficientes que los procesos, permitiendo
que varios ensamblados que se ejecutan en diferentes dominios de aplica- ción sin la
sobrecarga de iniciar procesos separados. La figura 8-1 muestra cómo un solo proceso
puede contener varios dominios de aplicación.

Sistema operativo

Proceso

Tiempo de ejecución de .NET Framework

Dominio de aplicación en el dominio de la aplicación

Asamblea   General   General

Figura 8-1  dominios de aplicación mantener Asambleas separadas dentro de un solo proceso
Importante  contrastar los dominios de aplicación
y los procesos
El tiempo de ejecución de .NET Framework administra los dominios de
aplicación, mientras que el sistema operativo gestiona los procesos.

El mejor ejemplo de los dominios de aplicación en uso es Internet Information


Services (IIS)
Proceso de trabajo de ASP.NET 5.0, implementada por el Aspnet_wp.exe. Si 10
personas visitan un sitio Web ASP.NET simultáneamente, ASP.NET creará un
dominio de aplicación independiente para cada usuario. Básicamente, ASP.NET
ejecuta 10 instancias independientes de la Asamblea.
Cada instancia de la Asamblea puede almacenar una propiedad
llamada userName sin ningún con- cern que otros casos podrán tener acceso o
sobrescribir el contenido de la proposición- pobreza. Este mismo efecto puede
lograrse mediante el lanzamiento del mismo ensamblado en 10 procesos
independientes, pero la conmutación entre los procesos consume tiempo de
procesador, reduciendo así el rendimiento.
La mayoría de las veces, se recurrirá a los hosts en tiempo de ejecución para crear
automáticamente los dominios de aplicación para sus asambleas. Ejemplos de hosts
en tiempo de ejecución integrada en Microsoft Windows son ASP.NET, Internet
Explorer (que crea un dominio de aplicación único para todos los ensamblados
desde un sitio Web específico), y el sistema operativo. Puede configurar el
comportamiento de estos dominios de aplicación mediante herramientas amigables
como el Administrador de Internet Information Services y la herramienta de
configuración de .NET Framework.
Sin embargo, igual de Aspnet_wp.exe crea dominios de aplicación para aislar
varias instancias de un conjunto, puede crear sus propios dominios de aplicación
para llamar a asambleas con poco riesgo de que la Asamblea tomará cualquier
acción o acceder a cualquier recurso que no has spe- cifically permitidos. La figura
8-2 muestra cómo un ensamblado puede alojar dominios de aplicación.

Tiempo de
ejecución de .NET
Framework

Dominio de
aplicación
General General

Dominio de aplicación
Dominio de aplicación

Figura 8-2  conjuntos niño puede alojar dominios de aplicación.


Además de aislar un conjunto de razones de seguridad, puede utilizar los dominios de
aplicación para mejorar la confiabilidad y eficiencia:
■   Confiabilidad  utilizar dominios de aplicación para aislar las tareas que
podrían causar un proceso para terminar.  Si el estado del dominio de la
aplicación  que se está ejecutando una tarea se vuelve inestable, el dominio de la
aplicación puede ser descargada sin afectar
El proceso. Esta técnica es importante cuando un proceso debe funcionar durante
largos periodos sin reiniciar. También puede utilizar los dominios de aplicación
para aislar las tareas que no debe compartir datos.
■ La   eficiencia  si se carga un ensamblado en el dominio de aplicación
predeterminado, la Asamblea no puede descargarse de la memoria mientras se
ejecuta el proceso. Sin embargo, si abre un segundo dominio de aplicación cargue
y ejecute el ensamblado,
La asamblea se descargan cuando ese dominio de aplicación se descarga. Utilice
esta técnica para minimizar el espacio de trabajo de procesos de larga ejecución
que ocasión- aliado utilizan grandes bibliotecas de vínculos dinámicos (DLL).

La   clase AppDomain


Los dominios de aplicación son implementado en .NET Framework utilizando
el sistema.app- Clase de dominio. Para utilizar un dominio de aplicación, se crea una
instancia de la  clase AppDomain y, a continuación, ejecutar un ensamblado dentro de
ese dominio. La tabla 8-1 muestra la App-  propiedades del dominio.
Tabla 8-1  AppDomain
Propiedades
Nombre Descripción
ActivationContext  Obtiene el contexto de activación para el dominio de
aplicación actual.
ApplicationIdentity  obtiene la identidad de la aplicación en el dominio de aplicación.
ApplicationTrust  obtiene información describiendo los permisos concedidos
a una aplicación y si la aplicación tiene un nivel de
confianza que permite que se ejecute.
BaseDirectory  obtiene el directorio base que la Asamblea solucionador
utiliza para sondear los ensamblados.
CurrentDomain  Obtiene el dominio de aplicación actual para el subproceso
actual.
Esta propiedad le permite analizar el actual contexto de
dominio para determinar o verificar los permisos.
Tabla 8-1  AppDomain   Propiedades

Nombre Descripción
   Obtiene el dominio DomainManager manager que fue proporcionado
por el host cuando se inicializó el dominio de
aplicación.
   Directorio DynamicDirectory obtiene la resolución de la Asamblea que
utiliza para sondear los conjuntos creados
dinámicamente.
Pruebas  obtiene las evidencias asociadas con este dominio de
aplicación que se utiliza como insumo para la política
de seguridad. Para obtener más infor- mación sobre las
pruebas, consulte el capítulo 11, "La seguridad de la
aplicación".
FriendlyName  Obtiene el nombre descriptivo de este dominio de
aplicación. Para los dominios creados por el .NET
Framework, este nombre descriptivo adopta la forma
"ProjectName.VSHOST.exe". Debe especificar el
nombre descriptivo al crear dominios de aplicación
mediante programación.
Id Obtiene un entero que identifica de forma única el
dominio de aplicación dentro del proceso.
RelativeSearchPath  obtiene la ruta relativa al directorio base donde la
asamblea debería solucionador sonda para
ensamblados privados.
SetupInformation  obtiene la información de configuración del dominio de
aplicación para esta instancia.
ShadowCopyFiles  obtiene una indicación de si todos los ensamblados
cargados en el dominio de aplicación son
instantáneas.

La tabla 8-2 muestra los  métodos públicos AppDomain más importantes.


Tabla 8-2  AppDomain   métodos

Nombre Descripción
   Devuelve el conjunto ApplyPolicy nombre para mostrar
después de una política que se ha aplicado.
CreateComInstanceFrom  crea una nueva instancia de un determinado
tipo de COM.
Tabla 8-2  AppDomain   métodos

Nombre Descripción
CreateDomain  crea un nuevo dominio de aplicación. Utilice este
método en lugar de un  constructor de AppDomain.
CreateInstance  crea una nueva instancia de un tipo
especificado definido en un ensamblado
especificado.
CreateInstanceAndUnwrap  crea una nueva instancia de un tipo especificado.
CreateInstanceFrom  crea una nueva instancia de un tipo
especificado definido en el archivo de
ensamblado especificado.
CreateInstanceFromAndWrap  crea una nueva instancia de un tipo
especificado definido en el archivo de
ensamblado especificado.
DefineDynamicAssembly  define un conjunto dinámico en el dominio de
aplicación actual.
DoCallBack  ejecuta el código en otro dominio de aplicación que es
identificado por el delegado especificado.
ExecuteAssembly  ejecuta el ensamblado que contiene el archivo
especificado.
ExecuteAssemblyByName  Ejecuta un ensamblado.
GetAssemblies Gets las asambleas que se han cargado en el contexto
de ejecución de este dominio de aplicación.
GetCurrentThreadId  Obtiene el identificador del subproceso actual.
GetData  obtiene el valor almacenado en el dominio de
aplicación actual para el nombre especificado.
InitializeLifetimeService  invalidado. El AppDomain da una infinita vida- tiempo
evitando un arrendamiento sea creada.
IsDefaultAppDomain  devuelve un valor que indica si la apli- cación de
dominio es el dominio de aplicación predeterminado
para el proceso.
Tabla 8-2  AppDomain   métodos

Nombre Descripción
IsFinalizingForUnload  indica si este dominio de aplicación se
descarga, y los objetos que contiene son
terminados por el common language
runtime.
Load  carga un ensamblado en un dominio
de aplicación.
ReflectionOnlyGetAssemblies  devuelve a las asambleas que se han cargado
en el contexto de sólo reflexión del dominio
de la aplicación.
SetAppDomainPolicy  Establece el nivel de directiva de
seguridad para este dominio de
aplicación.
SetData  asigna un valor a una propiedad del
dominio de aplicación.
SetDynamicBase  Establece la ruta del directorio especificado
como la ubicación donde se almacenan los
archivos generados dinámicamente y se
accede a ellas.
   Especifica cómo SetPrincipalPolicy principales y objetos de
identidad debe ser conectado a un hilo si el
subproceso intenta enlazar a un principal
mientras se ejecutaba en este dominio de
aplicación.
SetShadowCopyFiles  enciende las copias sombra.
SetShadowCopyPath  Establece la ruta del directorio
especificado como la ubicación de los
conjuntos de instantáneas.
SetThreadPrincipal  establece el objeto principal predeterminado
que se adjunta a las roscas si intentan enlazar
a un principal mientras se ejecutaba en este
dominio de aplicación.
Descargar  descarga el dominio de aplicación
especificado.
Cómo crear un dominio de
aplicación
Para crear un dominio de aplicación, llame a uno de los sobrecargados CreateDomain
AppDomain. métodos. Como mínimo, debe proporcionar un nombre para el nuevo
dominio de aplicación. El código siguiente muestra este proceso:
' VB
Dim d como AppDomain.AppDomain CreateDomain =("NewDomain")

Console.WriteLine("dominio Host: " + AppDomain.CurrentDomain.FriendlyName)


Console.WriteLine("Dominio secundario: " + d.FriendlyName)

// C#
D = AppDomain.AppDomain CreateDomain("NewDomain");

Console.WriteLine("dominio Host: " + AppDomain.CurrentDomain.FriendlyName);


Console.WriteLine("Dominio secundario: " + d.FriendlyName);

Como demuestra el ejemplo de código anterior, puede acceder al dominio de aplicación


en su conjunto está funcionando actualmente en (que probablemente fue creado
automáticamente por el .NET Framework) accediendo AppDomain.CurrentDomain.

Cómo cargar ensamblados en un dominio


de aplicación
Crear un nuevo dominio de aplicación y el lanzamiento de un conjunto dentro de ese
dominio es tan sencillo como crear una instancia de la  clase System.AppDomain con
un nombre descriptivo y, a continuación, llamar al  método ExecuteAssembly, como
demuestra el código siguiente:
' VB
Dim d como AppDomain.AppDomain CreateDomain =("NewDomain")
D.ExecuteAssembly("Assembly.exe").

// C#
D = AppDomain.AppDomain CreateDomain("NewDomain");
D.ExecuteAssembly("Assembly.exe");

El AppDomain.ExecuteAssembly método  tiene sobrecargas que le permiten pasar com-


my argumentos de línea de comandos, también. Como una alternativa para proporcionar
la ruta completa a la Asamblea, puede agregar una referencia al ensamblado y, a
continuación, ejecutarlo por nombre usando
el AppDomain.ExecuteAssemblyByName método, como se muestra en el código
siguiente:
' VB
Dim d como AppDomain.AppDomain CreateDomain =("NewDomain")
D.ExecuteAssemblyByName("General").

// C#
D = AppDomain.AppDomain CreateDomain("NewDomain");
D.ExecuteAssemblyByName("General");
Llamar a una asamblea de esta manera proporciona aislamiento de la asamblea pero
no aprovecha la enorme potencia y flexibilidad integradas en los dominios de
aplicación. Lección 2 explica cómo configurar dominios de aplicación en más
detalle.

Cómo descargar un dominio de aplicación


Una de las ventajas de la carga ensamblados en nuevos dominios de aplicación es
que puedes descargar el dominio de aplicación en cualquier momento, liberando
recursos. Para descargar un dominio y los ensamblados dentro del dominio, llame
al  método Unload AppDomain.estática:
'

V
B
Dim d como AppDomain.AppDomain CreateDomain =("NewDomain")
AppDomain.Unload(d)

/
/

C
#
D = AppDomain.AppDomain CreateDomain("NewDomain");
AppDomain.Unload(d);

Ensamblados o tipos individuales no pueden ser


descargadas.

Laboratorio: creación de dominios y cargar


ensamblados
En este laboratorio, se crea un dominio de aplicación y, a continuación, cargar un
ensamblado utilizando dos dif erentes- técnicas: por nombre y por referencia. Si
usted encuentra un problema complet- ción de un ejercicio, los proyectos
terminados están disponibles en el CD en la carpeta de código.
    Ejercicio 1: cargar un ensamblado por Filename
En este ejercicio, se crea un dominio de aplicación y usarlo para ejecutar un
conjunto que muestra el archivo Boot.ini.
1.  Copiar el capítulo08\LECCION1-ShowBootIni carpeta desde el CD a tu disco
duro y abrir la versión en C# o Visual Basic versión del proyecto.
2.  Genere y ejecute la aplicación de consola ShowBootIni para comprobar que
funciona correctamente. Si no mostrar correctamente el archivo Boot.ini,
modifique la aplicación para visualizar cualquier archivo de texto.
3.  Crear una nueva aplicación de consola denominada AppDomainDemo.
4.  En su nueva aplicación de consola, escribir código para crear un  objeto AppDomain.
Por ejemplo, el código siguiente:
' VB
Dim d como AppDomain.AppDomain CreateDomain =("NewDomain")

// C#
D = AppDomain.AppDomain CreateDomain("New Domain");

5.  A continuación, escribir código para que se ejecute el ShowBootIni general dentro


del dominio de aplicación recién creado por proporcionar explícitamente la ruta
completa al archivo. Por ejemplo, el código de seguimiento funcionaría, pero tendrá
que ajustarse para reflejar donde guardó el archivo ejecutable:
' VB
D.ExecuteAssembly("ShowBootIni.exe").

// C#
D.ExecuteAssembly("ShowBootIni.exe");

6.  Genere el proyecto y resolver cualquier error. Compruebe que la aplicación de consola


suc- logre ShowBootIni.exe llama a la Asamblea y que muestra el archivo de texto suc-
logre.
    Ejercicio 2: cargar un ensamblado con
nombre de ensamblado
En este ejercicio, puede modificar la aplicación de consola que creó en el ejercicio 1 para
ejecutar un conjunto basado en el nombre del ensamblado en lugar del nombre de archivo.
1.  Abra el proyecto AppDomainDemo creado en el ejercicio 1.
2.  Agregue una referencia al  ensamblado ShowBootIni.
3.  Modificar la llamada al  método AppDomain.ExecuteAssembly para
llamar AppDomain
.ExecuteAssemblyByName en su lugar. Por ejemplo, podría usar el siguiente código:
' VB
Dim d como AppDomain.AppDomain CreateDomain =("NewDomain")
D.ExecuteAssemblyByName("ShowBootIni")

// C#
D = AppDomain.AppDomain CreateDomain("New Domain");
D.ExecuteAssemblyByName("ShowBootIni");

4.  Genere el proyecto y resolver cualquier error. Compruebe que la aplicación de consola


suc- logre ShowBootIni.exe llama a la Asamblea y que muestra el archivo de texto suc-
logre.
Resumen de la lección
■   Un dominio de aplicación es un contenedor lógico que permite varios
ensamblados para ejecutar dentro de un único proceso, pero les impide
acceder directamente a otras asambleas" recuerdos. Crear un dominio de
aplicación cada vez que desee iniciar una asamblea.
■   La  clase AppDomain contiene métodos para definir los privilegios, carpetas
y otras propiedades de un nuevo dominio de aplicación; el lanzamiento de un
conjunto; y descarga un dominio de aplicación.
■   Crear una  clase AppDomain, llame al  método AppDomain.CreateDomain
estático.
AppDomain no tiene constructores tradicionales.
■   Para cargar un ensamblado en un dominio de aplicación, se crea una instancia
de la  clase de dominio de aplicación y, a continuación, llame al  método
AppDomain.ExecuteAssembly.
■   Para
descargar un dominio de aplicación, llame al  método estático
AppDomain.Unload.

Examen de la lección
Puede utilizar las siguientes preguntas para poner a prueba su conocimiento de la
información en la lección 1, "Creación de dominios de aplicación." Las preguntas
también están disponibles en el CD complementario si prefiere revisarlos en forma
electrónica.

Nota   Respuestas
Las respuestas a estas preguntas y explicaciones de por qué cada opción de
respuesta es correcta o no se encuentran en la sección "Respuestas" al final
del libro.

1.  Cuáles  de las siguientes son razones válidas para crear un dominio de


aplicación? (Seleccione todos los que correspondan).
A.  Es la única manera de iniciar un proceso separado.
B.   Puede quitar el dominio de aplicación para liberar recursos.
C.   Los dominios de aplicación mejorará el rendimiento.
D.   Los dominios de aplicación proporcionan una capa de separación y
seguridad.
2.  Cuáles de las siguientes son maneras válidas para ejecutar un conjunto dentro
de un dominio de aplicación? (Seleccione todos los que correspondan).
A.  AppDomain.CreateDomain
B.   AppDomain.ExecuteAssembly
C.   AppDomain.ExecuteAssemblyByName
D.   AppDomain.ApplicationIdentity
3.  El comando que se utiliza para cerrar el dominio de aplicación en el seguimiento
código de ejemplo?
' VB
Dim d como AppDomain.AppDomain CreateDomain =("New Domain")
D.ExecuteAssemblyByName("MyAssembly")

// C#
D = AppDomain.AppDomain CreateDomain("New Domain");
D.ExecuteAssemblyByName("MyAssembly");

A.  D.DomainUnload()
B.   D = null
C.   D.Unload()
D.   AppDomain.Unload(d)
Lección 2: Configuración de los dominios de aplicación
Puede configurar dominios de aplicación para crear entornos personalizados para
los pe- Blies. La aplicación más importante de la modificación de la configuración
predeterminada para la apli- cación de dominio es restringir los permisos para
reducir los riesgos asociados a las vulnerabilidades de seguridad. Idealmente,
cuando se ha configurado un dominio de aplicación no sólo proporciona una unidad
de aislamiento, pero limita el daño que los atacantes pueden hacer si se explotan
con éxito un conjunto.

Después de esta lección, será capaz de:


■   Lanzamiento ensamblados en un dominio de aplicación con privilegios
limitados.
■   Configurar propiedades de dominio de aplicación para el control de las
ubicaciones de las carpetas y otros ajustes.
Lección Tiempo estimado: 25 minutos

Cómo utilizar un dominio de aplicación para lanzar


conjuntos con
Privilegios limitados
Restringir los permisos de un dominio de aplicación pueden reducir
considerablemente el riesgo de que una asamblea se llama realice alguna acción
maliciosa. Considere las siguientes sce- nario: usted compra un ensamblado a partir
de un tercero, y utilizar la Asamblea comu- nicate con una base de datos. Un
atacante descubre una vulnerabilidad de seguridad en el ensamblado de terceros y
lo utiliza para configurar una aplicación de spyware para que se inicie
automáticamente. Para el usuario, la vulnerabilidad de la seguridad es su culpa,
porque la aplicación de terceros de confianza general y lo ejecuté con privilegios
suficientes para instalar el software.
Considere ahora el mismo escenario con un dominio de aplicación con privilegios
limitados: un intruso descubre una vulnerabilidad de seguridad en el ensamblado de
terceros. Sin embargo, cuando el atacante intenta explotar la vulnerabilidad para
escribir archivos en el disco duro local, el archivo de solicitud de E/S es rechazado
por falta de privilegios. Mientras la seguri- dad de la vulnerabilidad todavía existe,
la limitación de privilegios asignados al dominio de aplicación le impidió ser
explotados.
En este ejemplo, el lanzamiento de conjuntos con privilegios limitados es un
ejemplo de la defensa en profundidad. Defensa en profundidad es la entidad
principal de seguridad de ofrecer varios niveles de protección, de modo que aún
están protegidos en caso de vulnerabilidad. Defensa en profundidad es
especialmente importante cuando se llama código externo, porque el código
externo pueden tener vulnerabilidades que no son conscientes, no puede impedir, y
no se puede corregir.
Las secciones que siguen describen cómo utilizar pruebas para configurar dominios de
aplicación. Hay varias otras maneras de controlar los permisos concedidos a un ensamblado.
Para obtener más información acerca de la seguridad de acceso a código, consulte el
Capítulo 11.

Cómo proporcionar evidencia de Host


para un ensamblado
Cuando se crea un dominio de aplicación y conjuntos de lanzamiento, usted tiene control
total sobre el host evidencia. La evidencia es la información que recopila sobre un
ensamblado en tiempo de ejecución para determinar qué grupos de código pertenece a la
Asamblea. Los grupos de código, en Tur n, disuadir la remoción de t él del conjunto de
privilegios. Las formas comunes de prueba incluyen la carpeta o sitio Web se está
ejecutando desde la asamblea y firmas digitales.
Mediante la asignación de pruebas a un ensamblado, puede controlar los permisos que se
asignan a la Asamblea. Proporcionar pruebas de un ensamblado, cree primero un Sys-
tem.Security.Policy.Pruebas de objeto y, a continuación, pasarla como parámetro para la
aplicación del dominio ExecuteAssembly método sobrecargado.
Cuando se crea un  objeto de pruebas con el constructor que requiere dos matrices de
objetos, debe proporcionar una matriz que representa el host de pruebas, y una segunda, que
proporciona el conjunto de pruebas. Cualquiera de las matrices puede ser nulo, y a menos
que usted específicamente ha creado una clase de pruebas de montaje, usted probablemente
tendrá que asignar la propiedad pruebas de host. Podría parecer extraño que  tiene evidencia
matrices objeto genérico en lugar de pruebas con establecimiento inflexible de tipos
de objetos. Sin embargo, la evidencia puede ser cualquier cosa: una cadena, un número
entero o una clase personalizada. Así que incluso si usted está utilizando los tipos de pruebas
integradas en la
.NET Framework, deberá agregarlos a un objeto Array.

Más info  pruebas

Para obtener más información sobre las pruebas,


consulte el Capítulo 11.

La manera más sencilla de controlar los permisos asignados a un ensamblado en un dominio


de aplica- ción es pasar la zona pruebas mediante el uso de
un System.Security.Policy.Zona objeto y System.Security.SecurityZone enumeración. El
siguiente código muestra el uso de la evidencia constructor que requiere dos matrices de
objetos mediante la creación de una zona objeto, agregarlo a un objeto Array llamado
hostEvidence, y, a continuación, utilizar el objeto Array para crear un  objeto
llamado internetEvidence Pruebas. Por último, que las pruebas objeto se pasa al dominio de
la aplicación del  método ExecuteAssembly junto con el nombre del archivo
De la asamblea. El siguiente ejemplo de código, que requiere el Sistema y
seguridad.
System.Security .Policy namespaces,  demuestra este proceso:
' VB
Dim hostEvidence como Object() = {Nueva Zona (SecurityZone.Internet)}
Dim internetEvidence como prueba = nuevas pruebas (hostEvidence, nada) Dim myDomain como
AppDomain.AppDomain CreateDomain =("MyDomain")
midominio.ExecuteAssembly("SecondAssembly.exe", internetEvidence)

// C#
Objeto [] hostEvidence = {nueva zona(SecurityZone.Internet)}; Evidencia
internetEvidence = nueva evidencia(hostEvidence, null); AppDomain myDomain =
AppDomain.CreateDomain("MyDomain");
midominio.ExecuteAssembly("SecondAssembly internetEvidence.exe");

El resultado es que el ensamblado especificado se ejecutará en un dominio de


aplicación aislada sólo con el conjunto de permisos concedidos al  grupo de código
Zona_Internet. Cuando el dominio de aplicación inicia la asamblea, el runtime
analiza la evidencia facili- tada. Porque la evidencia coincide con la zona de
Internet, el tiempo de ejecución asigna al  grupo de código Zona_Internet, que a su
vez, asigna la extremadamente restrictiva a Internet per- misión establecida.

   Controlar importantes pruebas


Ejecuta un ensamblado utilizando el   grupo de código Zona_Internet es útil para
maximizar la seguri- dad de aplicación porque la asamblea ha restringido sus
permisos como si proviniera del Internet. Pero la Asamblea no es necesariamente
proveniente de Internet puede ser almacenado en la misma carpeta que el
funcionamiento general. Básicamente, usted está proporcionando pruebas falsas
para el motor en tiempo de ejecución. Proporcionar evidencia para el tiempo de
ejecución también puede ser utilizado para otorgar un conjunto más  permisos
de los que normalmente reciben, que es un potente capacidad. Para controlar
esta capacidad, restringir la SecurityPermission
.ControlEvidence  permiso como se discutió en el Capítulo 11.

Cómo proporcionar evidencia de Host para un dominio


de aplicación
También puede aportar pruebas de la existencia de dominios de aplicación
completos. La técnica es similar a la aportación de pruebas para un conjunto nuevo,
y utiliza una sobrecarga del  main.CreateDomain AppDo- método que acepte
un  objeto de prueba, como en el siguiente ejemplo de código (que requiere
el System.Security y System.Security.Policy namespaces) muestra:
' VB
Dim hostEvidence como Object() = {Nueva Zona (SecurityZone.Internet)}
Dim appDomainEvidence como prueba = nuevas pruebas (hostEvidence, nada) Dim d como
AppDomain.AppDomain CreateDomain =("MyDomain", appDomainEvidence)
d.ExecuteAssembly("SecondAssembly.exe").
// C#
Objeto [] hostEvidence = {nueva zona(SecurityZone.Internet)}; Evidencia appDomainEvidence =
nueva evidencia(hostEvidence, null); d = AppDomain.AppDomain CreateDomain("MyDomain",
appDomainEvidence); d.ExecuteAssembly("SecondAssembly.exe");

Cómo configurar las propiedades del dominio de


aplicación
Puede proporcionar el common language runtime con la información de configuración
de un nuevo dominio de aplicación  utilizando la  clase AppDomainSetup. A la hora de
crear sus propios dominios de aplicación, la propiedad más importante
es ApplicationBase. La otra App- DomainSetup propiedades son utilizadas
principalmente por los hosts en tiempo de ejecución para configurar un dominio de
aplicación concreto. Cambiar las propiedades de una  instancia AppDomainSetup no
afectará a ningún AppDomain existente. Puede afectar sólo a la creación de un
nuevo AppDomain, cuando el  método se llama CreateDomain AppDomainSetup con
la instancia como un parámetro.
La tabla 8-3 muestra las  propiedades AppDomainSetup más útiles.
Tabla 8-3    propiedades AppDomainSetup

Nombre Descripción
ActivationArguments  obtiene o establece datos acerca de la activación de un
dominio de aplicación.
ApplicationBase  obtiene o establece el nombre del directorio raíz que
contiene la aplicación. Cuando el tiempo de ejecución
necesita para satisfacer una solicitud de tipo, busca el
ensamblado que contiene el tipo en el directorio
especificado por la  propiedad ApplicationBase.
ApplicationName  obtiene o establece el nombre de la aplicación.
ApplicationTrust  obtiene o establece un objeto que contiene
información de seguridad y confianza.
ConfigurationFile  obtiene o establece el nombre del archivo de
configuración de un dominio de aplicación.
DisallowApplication Especifica si la aplicación ruta base privada y la ruta del
- BaseProbing archivo binario están sondeado al buscar ensamblados
para cargar.
Tabla 8-3    propiedades
AppDomainSetup
Nombre Descripción
DisallowBindi Obtiene o establece un valor que indica si un
ng- redirige dominio de aplicación permite la redirección del
enlace de ensamblados.
- DisallowCode
Descargar Obtiene o establece un valor que indica si la descarga de
HTTP de asambleas es permitido para un dominio de
aplicación. El valor predeterminado es false, lo cual no es
seguro para los servicios (analizó en la lección 3, "Crear
servicios de Windows"). Para ayudar a evitar que los
servicios de descarga de código de confianza parcial,
DisallowPublish
establezca esta propiedad en true.
er- política
Obtiene o establece un valor que indica si la directiva de
editor de la sección del archivo de configuración se aplica
a un dominio de aplicación.
DynamicBase Obtiene o establece el directorio base donde el
directorio para los archivos generados
dinámicamente se encuentra.
LicenseFile Obtiene o establece la ubicación del archivo de licencia
asociado con este dominio.
LoaderOptimization  especifica la política de optimización utilizado
para cargar un archivo ejecutable.
PrivateBinPath  obtiene o establece la lista de los directorios bajo el
directorio base de aplicación que se probaron para
ensamblados privados.

Para aplicar estas propiedades a un dominio de aplicación, crear y Conf igurar una
 Objeto AppDomainSetup, y pasarlo (junto con un  objeto de pruebas) para
el AppDomain
.CreateDomain método. En el ejemplo de código siguiente se muestra este proceso:
' VB
' Crear e inicializar los ajustes de un segundo AppDomain
Dim anuncios AppDomainSetup AppDomainSetup = Nuevo
Ads.ApplicationBase = "file://" + System.Environment.CurrentDirectory
ads.DisallowBindingRedirects = False
Ads.DisallowCodeDownload = True
ads.ConfigurationFile = _
AppDomain.CurrentDomain.SetupInformation.ConfigurationFile

' Se crea el segundo AppDomain


Dim d como AppDomain.AppDomain CreateDomain =("New Domain", nada, ads)
// C#
// Crear e inicializar los ajustes de un segundo AppDomain. Anuncios AppDomainSetup
AppDomainSetup = new();
Ads.ApplicationBase = "file://" + System.Environment.CurrentDirectory;
Ads.DisallowBindingRedirects = false;
ads.DisallowCodeDownload = true;
ads.ConfigurationFile =
AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;

// Crear el segundo AppDomain


D = AppDomain.AppDomain CreateDomain("New Domain", null, ads);

Para examinar las propiedades para el dominio de aplicación actual, utilice


el AppDomain
.CurrentDomain.SetupInformation objeto, como demuestra el ejemplo de código
siguiente:
' VB
Anuncios Dim = AppDomainSetup AppDomain.CurrentDomain.SetupInformation
Console.WriteLine(ads.ApplicationBase) Console.WriteLine(ads.Nombredeaplicación)
Console.WriteLine(ads.DisallowCodeDownload) Console.WriteLine(ads.DisallowBindingRedirects)

// C#
Anuncios AppDomainSetup = AppDomain.CurrentDomain.SetupInformation;
Console.WriteLine(ads.ApplicationBase); Console.WriteLine(ads.Nombredeaplicación);
Console.WriteLine(ads.DisallowCodeDownload);
Console.WriteLine(ads.DisallowBindingRedirects);

Laboratorio: aplicación de control de privilegios de


dominio
En este laboratorio, se crea un dominio de aplicación con privilegios reducidos para
reducir los riesgos de seguridad de ejecutar un ensamblado externo. Si usted encuentra
un problema complet- ción de un ejercicio, los proyectos terminados están disponibles
en el CD en la carpeta de código.
    Ejercicio: cargar un ensamblado con privilegios restringidos
En este ejercicio, usted carga un ensamblado sin la concesión de privilegios para leer
archivos del sistema.
1. Copiar el capítulo08\lección2-ejercicio1-AppDomainDemo carpeta desde el CD a tu
disco duro y abrir la versión en C# o Visual Basic versión del proyecto.
2.  Agregue el System.Security y System.Security.Policy espacios de nombres para
su proyecto.
3.  Antes de la creación del  objeto AppDomain, cree un  objeto de pruebas
contienen- ción de la zona de seguridad de Internet. El código siguiente:
' VB
' Crear un objeto de pruebas para la zona de Internet
Dim safeZone como zona = New Zone(SecurityZone.Internet)
Dim hostEvidence como Object() = {Nueva Zona(SecurityZone.Internet)} Dim e como
prueba = Nueva Evidencia(hostEvidence, nada)

// C#
// Crear un objeto de pruebas para la zona de Internet
Zona safeZone = new Zone(SecurityZone.Internet);
Object[] = { hostEvidence nueva zona(SecurityZone.Internet) }; Pruebas e =
nueva evidencia(hostEvidence, null);

4.  Modificar la llamada al  método CreateDomain AppDomain.para


proporcionar la evidencia objeto que ha creado. Por ejemplo:
' VB
' Crear un AppDomain
Dim d como AppDomain.AppDomain CreateDomain =("NewDomain", e)

// C#
// Crear un AppDomain.
D = AppDomain.AppDomain CreateDomain("New Domain", e);

5.  Genere y ejecute la aplicación de consola AppDomainDemo. Esta vez, cuando


su asamblea intenta ejecutar ShowBootIni, el tiempo de ejecución generará
una SecurityException. El  dominio de la aplicación que ha creado se
encuentra en la zona de Internet, que carece de privi- leges para leer el archivo
Boot.ini. Si el ensamblado contiene una vulnerabilidad de seguridad o
deliberadamente código malicioso, proporcionando pruebas restrictivas para
el dominio de la aplicación podría haber evitado un ataque de seguridad como
una infección por virus o spyware.

Resumen de la lección
■   La manera más sencilla de utilizar un dominio de aplicación para iniciar una
asamblea con privilegios limitados es especificar una zona restringida, como
la zona de Internet, como medio de prueba.
■   Para configurar las propiedades de un dominio de aplicación, cree una
instancia de la  clase AppDomainSetup. A continuación, utilice la instancia al
crear el dominio de aplicación.
Examen de la lección
Puede utilizar las siguientes preguntas para poner a prueba tus conocimientos de la
información Les- hijo 2, "Configuración de dominios de aplicación." Las preguntas
también están disponibles en el CD complementario si prefiere revisarlos en forma
electrónica.

Nota   Respuestas
Las respuestas a estas preguntas y explicaciones de por qué cada opción de
respuesta es correcta o no se encuentran en la sección "Respuestas" al final del
libro.

1.  ¿Cómo utilizar las pruebas de tiempo de ejecución cuando se crea un dominio de


aplicación?
A.  Para determinar  la prioridad con la que se debe ejecutar el proceso
B.   Para identificar al autor de la asamblea
C.   Para determinar los privilegios que la Asamblea debe recibir
D.   Para realizar el seguimiento de las acciones de la asamblea para fines de
auditoría
2.  Cuál de los siguientes ejemplos de código se ejecuta un ensamblado como si
estuviera ubicada en la Internet? (Seleccione todos los que correspondan).
A.
' VB
Dim hostEvidence como Object() = {Nueva Zona (SecurityZone.Internet)} Dim e como prueba =
nuevas pruebas (hostEvidence, nada)
Dim d como AppDomain.AppDomain CreateDomain =("MyDomain", e)
D.ExecuteAssembly("Assembly.exe").

// C#
Objeto [] hostEvidence = {nueva zona(SecurityZone.Internet)}; Pruebas e = nueva
evidencia(hostEvidence, null);
D = AppDomain.AppDomain CreateDomain("MyDomain", e);
D.ExecuteAssembly("Assembly.exe");

B.
' VB
Dim hostEvidence como Object() = {Nueva Zona (SecurityZone.Internet)} Dim d como
AppDomain.AppDomain CreateDomain =("MyDomain")
Dim e como prueba = nuevas pruebas (hostEvidence, nada)
D.Pruebas = e
d.ExecuteAssembly("Assembly.exe").

// C#
Objeto [] hostEvidence = {nueva zona(SecurityZone.Internet)}; D =
AppDomain.AppDomain CreateDomain("MyDomain");
Pruebas e = nueva evidencia(hostEvidence, null); d.Pruebas = e;
d.ExecuteAssembly("Assembly.exe");
C.
' VB
Dim myDomain como AppDomain.AppDomain CreateDomain =("MyDomain")
Midominio.ExecuteAssembly("General.exe", nueva zona (SecurityZone.Internet)

// C#
AppDomain myDomain = AppDomain.CreateDomain("MyDomain");
Midominio.ExecuteAssembly("General.exe", nueva zona(SecurityZone.Internet);

D.
' VB
Dim e como prueba = Nueva Evidencia e.AddHost(Zona
nueva (SecurityZone.Internet)
Dim myDomain como AppDomain.AppDomain CreateDomain =("MyDomain")
Midominio.ExecuteAssembly("Assembly.exe", e)

// C#
Pruebas e = nueva evidencia();
E.AddHost(nueva zona(SecurityZone.Internet);
AppDomain myDomain = AppDomain.CreateDomain("MyDomain");
Midominio.ExecuteAssembly("Assembly.exe", e);

3.  ¿Cómo se puede establecer el directorio base para una aplicación en un


dominio de aplicación?
A.  Cree una instancia de la   clase AppDomain y, a continuación,
establezca la    propiedad Directorio dinámico.
B.   Cree una instancia de la  clase AppDomain y, a continuación, establezca
la BaseDirectory
La propiedad.
C.   Crear una instancia de la  clase AppDomainSetup y, a continuación,
establezca la  propiedad Base dinámica. Pase el  objeto
AppDomainSetup al  constructor de AppDomain.
D.   Crear una instancia de la  clase AppDomainSetup y, a continuación,
establezca la  propiedad Base de aplicaciones. Pase el  objeto
AppDomainSetup al  constructor de AppDomain.
4.  Usted necesita notificar al usuario si su ensamblado se ejecuta sin la
capacidad de usar HTTP para descargar ensamblados. ¿Cómo puede
determinar si tiene ese permiso?
A.
Examinar AppDomain.CurrentDomain.SetupInformation.DisallowCodeDown
load.
B.   Examinar AppDomain.CurrentDomain.DisallowCodeDownload.
C.
Examinar AppDomain.CurrentDomain.SetupInformation.DisallowPublisherP
olicy.
D.   Examinar AppDomain.CurrentDomain.DisallowPublisherPolicy.
Lección 3: Crear servicios de
Windows
Creación de servicios permite a r una asamblea de la ONU en el fondo, sin ninguna
interacción por parte del usuario. Los servicios son perfectos para cuando se quiere
monitorizar continuamente algo, cuando sus necesidades de armado para escuchar las
conexiones de red entrantes, o cuando se necesita para iniciar la asamblea antes de que
el usuario inicie sesión. Debido a su naturaleza única, los servicios requieren
consideraciones especiales de seguridad y de instalación.

Después de esta lección, será


capaz de:
■   describir el propósito de un servicio.
■   Crear un proyecto de servicio en Visual Studio.
■   Especificar propiedades para un servicio.
■   instalar un servicio manualmente.
■   Crear un proyecto de instalación de un servicio.
■   iniciar y gestionar un servicio mediante las herramientas incorporadas en Windows.
Lección Tiempo estimado: 45 minutos

¿Qué es un servicio de Windows?


Los servicios de Windows son los procesos que se ejecutan en segundo plano, sin
interfaz de usuario, y en su sesión de usuario. Los servicios pueden ser iniciado
automáticamente cuando la computadora se inicia, incluso si un usuario no inicia sesión.
Por lo tanto, los servicios son una manera ideal para implementar una aplicación que
debería estar funcionando constantemente y no necesita interactuar con el usuario.
Windows tiene docenas de servicios integrado, incluyendo Server (que comparte
carpetas en la red), que conecta con la estación de trabajo ( carpetas compartidas), y la
World Wide Web Publishing (que sirve páginas Web).

Nota   crear  servicios de Windows en diferentes versiones de Visual Studio


La plantilla del Servicio de Windows y la funcionalidad asociada no está disponible
en la edición estándar de Visual Studio.
Servicio aplicaciones funcionan de forma diferente a otros tipos de proyecto de
varias maneras:
■   El archivo ejecutable compilado que un proyecto de aplicación de servicio
crea debe estar instalado antes de que el proyecto pueda funcionar de manera
eficaz. No se puede depurar o ejecutar una aplicación de servicio presionando
F5 o F11; no puede ejecutar inmediatamente un servicio o paso en su código.
En su lugar, debe instalar e iniciar el servicio y, a continuación, adjuntar un
depurador al proceso del servicio.

Más info  Servicios de depuración


Para obtener más información acerca de los servicios de depuración,
consulte "Depurar aplicaciones de servicios de Windows"
En http://msdn.microsoft.com/library/en-
us/vbcon/html/vbtskdebuggingserviceapplications.asp.

■   a diferencia de usted hacer con algunos tipos de proyectos, debe crear la
instalación compo- nentes para aplicaciones de servicios. Los componentes de
instalación instalar y registrar el servicio en el servidor y crear una entrada
para el servicio con el administrador de control de servicios de Windows.
■   El  método principal para su aplicación de servicio debe emitir el comando
Ejecutar para los servicios que su proyecto contiene. El  método Run carga
los servicios en el Administrador de control de servicios en el servidor
correspondiente. Si utiliza Windows Ser- vicios plantilla de proyecto, este
método se escribe automáticamente para usted.
■   aplicacionesde servicios de Windows se ejecute en una ventana diferente
puesto que el inter- estación activa del usuario en sesión. Una estación de
ventana es un objeto seguro que con- mantiene un portapapeles, un conjunto
de átomos globales, y un grupo de objetos de escritorio. Debido a que la
estación de servicio de Windows no es una estación interactiva, cuadros de
diálogo planteadas desde dentro de una aplicación de servicio de Windows no
será visto y puede provocar que el programa deje de responder. Del mismo
modo, los mensajes de error deben ser registrados en el registro de sucesos de
Windows en lugar de planteadas en la interfaz de usuario.
■   aplicaciones de servicios de Windows se ejecutan en su propio contexto de
seguridad y que se inician antes de que el usuario inicia sesión en el equipo
con Windows en el que están instalados. Usted debe planear cuidadosamente
de qué cuenta de usuario para ejecutar el servicio dentro; un servicio que se
ejecuta bajo la cuenta de sistema tiene más permisos y privilegios que una
cuenta de usuario. El su servicio tiene más privilegios, los atacantes pueden
hacer más daño si logran explotar una vulnerabilidad de seguridad en su
servicio. - Das, debe ejecutar su servicio con los menores privilegios posibles
para minimizar los posibles daños.
Mundo
Real
Tony
Northrup
Comencé a usar .NET Framework tan pronto como los betas de la primera versión
estuvieron disponibles. Sin embargo, las versiones anteriores no admiten la
creación de servicios con el
.NET Framework. Yo no quería volver a otro entorno de desarrollo, así que me
apoyé en hacks para habilitar los ensamblados .NET que se ejecutan en segundo
plano. Habitualmente, me gustaría crear una aplicación de consola y, a
continuación, utilice Tareas programadas para configurarlo para que se inicie
automáticamente a una cuenta de usuario especial. Este sistema ha permitido que
el proceso se ejecuta continuamente en segundo plano, pero la técnica era difícil de
gestionar porque no pude utilizar el complemento Servicios para iniciar o detener
el servicio.

Cómo crear un proyecto de servicio


A un alto nivel, siga estos pasos para crear un proyecto de servicio:
1.  Crear un proyecto utilizando la plantilla de aplicación Servicios de Windows,
como se muestra en la figura 8-3. Esta plantilla crea una clase que hereda
de ServiceBase y escribe mucho del código de servicio básicos, tales como el
código para iniciar el servicio.

Figura 8-3 de  Visual Studio incluye la plantilla de aplicación de servicios de Windows.


2.  Escribir el código para la OnStart y OnStop procedimientos y anular cualquier
otros métodos que desee redefinir.
3.  Agregar la necesaria instaladores para su aplicación de servicio. De forma
predeterminada, una clase conteniendo dos o más instaladores se agrega a la
aplicación cuando se haga clic en el vínculo Agregar instalador: uno para
instalar el proceso, y uno para cada uno de los servicios asociados a su
proyecto contiene.
4.  Construya su proyecto.
5.  Crear un proyecto de instalación para instalar su servicio, y luego instalarlo.
6.  Use el complemento Servicios para iniciar el servicio.
Las secciones que siguen describen cómo implementar  estas capacidades en el nivel
de código.

Cómo implementar un servicio


Después de crear su servicio proyecto en Visual Studio, siga estos pasos para
implementar el servicio:
1.  En las propiedades de su diseñador, modifique la  propiedad
ServiceName.ServiceBase.
Cada servicio debe tener un nombre único; por lo tanto, es muy importante
cambiar esta configuración. ServiceName no es el nombre descriptivo que
aparecerá en el complemento Servicios. En su lugar, nombreservicio es
utilizado por el sistema operativo para identificar el ser- vicio y puede ser
utilizado para identificar el servicio mediante programación. Por ejemplo, un
servicio puede iniciarse desde la línea de comandos ejecutando ServiceName
Net Start.
2.  Agregar código para el  método OnStart para configurar cualquier sondeo o
vigilancia requiere el servicio. Nota que  no OnStart realmente hacen el
seguimiento. El  método OnStart debe volver al sistema operativo una vez
que el servicio de la opera- ción ha comenzado. No debe repetir
indefinidamente o bloquear. Para configurar un simple mecanismo de sondeo,
puede usar System.Timers. componente Timer. En el  método OnStart, tendría
que establecer los parámetros del componente y, a continuación, se establece
la  propiedad Enabled en true. El temporizador recaudaría eventos en el
código periódicamente, momento en el que su servicio podría hacer su
seguimiento. Se refieren al ejercicio de laboratorio 1, más adelante en esta
lección, por ejemplo.
3.  Agregue código al  método OnStop para llevar a cabo cualquier acción
necesaria para su servicio a tope.
4.  Opcionalmente, puede invalidar el OnPause,  OnShutdown OnContinue ,
y métodos. OnPause se llama cuando un usuario deja su servicio desde el
complemento Servicios (un evento raro).  Se llama OnContinue cuando un
servicio se reanuda desde un estado de pausa. Por último, OnShut- abajo se
llama cuando un equipo se apaga. Si no reemplazar estos métodos,
establezca ServiceBase.CanPauseAndContinue o ServiceBase.CanShutdown 
en true.
Cómo crear un proyecto para instalar un servicio
A diferencia de lo que ocurre con otras aplicaciones, usted no puede simplemente
ejecutar un archivo ejecutable del servicio. Esta limitación impide que se ejecuten y
depurar la aplicación directamente desde el entorno de desarrollo de Visual Studio. Los
servicios deben ser instalados antes de ejecutar. .NET Framework proporciona
las    clases ServiceInstaller y ServiceProcessInstaller para este propósito.
Utilice ServiceInstaller para definir la descripción del servicio, el nombre para mostrar,
el nombre del servicio y el tipo de inicio. Utilice ServiceProcessInstaller para definir la
configuración de la cuenta de servicio.
En la práctica, no tendrá que escribir código que utiliza el   servicio- ProcessInstaller
ServiceInstaller y clases porque Visual Studio generará automáticamente el código. Para
crear un servicio Instalador con Visual Studio, siga estos pasos:
1.  En Visual Studio, abra la vista de diseño para su servicio. Haga clic con el botón
secundario en el diseñador y, a continuación, haga clic en Agregar instalador.
Visual Studio crea un  componente ProjectInstaller.
2.  Establezca la  propiedad StartType para el  componente ServiceInstaller
ProjectInstaller a uno de los siguientes valores:
❑         Elservicio automático todavía se inicie automáticamente después de que
se inicia el equipo, sea o no un usuario inicia la sesión.
❑   Manual  (el valor predeterminado),  un usuario debe iniciar
manualmente el servicio.
❑   inhabilitado   el servicio no se inicia automáticamente, y los usuarios no
pueden iniciar el servicio sin primero cambiar el tipo de inicio.
3.  Establezca la descripción y DisplayName para el  componente ServiceInstaller.
4.  Especificar el contexto de seguridad para su servicio estableciendo la   propiedad
Account para el  componente ServiceProcessInstaller ProjectInstaller a uno de los
siguientes valores:
❑   LocalService   se ejecuta en el contexto de una cuenta que actúa como un
usuario sin privilegios en el equipo local, y presenta credenciales anónimas a
cualquier servidor remoto. Utilice LocalService para minimizar los riesgos de
seguridad.
❑   NetworkService   habilita el servicio para autenticar a otro equipo de la
red. Esto no se requiere autenticación para anónimo connec- nes, como la
mayoría de las conexiones a un servidor Web.
❑   LocalSystem   el servicio se ejecuta con privilegios casi ilimitada y pre-
mandará las credenciales del equipo a cualquier servidor remoto. Con este
tipo de cuenta presenta un grave riesgo de seguridad; cualquier
vulnerabilidad de la aplicación
Podría ser aprovechada para tomar el control total del equipo del usuario.
❑ El   usuario  (el valor predeterminado)  hace que el sistema solicitará
un nombre de usuario válido y contraseña cuando se instala el servicio (a
menos que haya establecido los valores para las    propiedades Nombre
de usuario y contraseña de tu ServiceProcessInstaller instancia).
5.  Definir el servicio objeto de inicio del proyecto. Haga clic con el botón
secundario en el proyecto en el Explorador de soluciones y, a continuación,
haga clic en Propiedades. En el Diseñador de proyectos, en la ficha de la
aplicación, seleccione su servicio desde el inicio del proyecto de lista de
objetos.
6.  Ahora construir su proyecto.
En este punto, puede instalar manualmente el servicio utilizando la herramienta
InstallUtil o crear un proyecto de instalación que proporcionará una instalación
basada en el asistente y una interfaz de Windows Installer (MSI) paquete. Las
secciones siguientes describen cada una de estas dos opciones.

Cómo instalar un
servicio manualmente
Después de implementar y construir su servicio, puede instalarlo manualmente.
Para instalar un servicio manualmente, ejecute InstallUtil.exe desde la línea de
comandos con el nombre del servicio como un parámetro. Para instalar el servicio,
ejecute yourservice InstallUtil.exe. Para desinstalar el servicio,
ejecute /u yourservice InstallUtil.exe.

Cómo crear un proyecto de


instalación de un servicio
1.  Agregar un proyecto de instalación a su solución actual, como se muestra en la
figura 8-4.
Figura 8-4  Agregar un proyecto de instalación simplifica la implementación de
servicios.
2.  Agregar la salida de su servicio de proyecto para el proyecto de instalación
mediante los siguientes pasos:
A.  Haga clic con el botón secundario en el proyecto de instalación en el
Explorador de soluciones, haga clic en Agregar y, a continuación, haga
clic en Resultados del proyecto.
B.   En el grupo de resultados del proyecto Agregar cuadro de diálogo, seleccione
los proyectos de servicio de la lista de proyectos, seleccione Resultado principal
y, a continuación, haga clic en Aceptar.
3.  Por último, agregue una acción personalizada para instalar el archivo ejecutable del
servicio siguiendo estos pasos:
A.  Haga clic con el botón secundario en el proyecto de instalación en el
Explorador de soluciones, haga clic en Ver y, a continuación, haga clic
en Acciones personalizadas.
B.   En el editor de acciones personalizadas, haga clic en Acciones personalizadas
y, a continuación, haga clic en
Agregar acción personalizada.
C.   En el cuadro de diálogo Seleccionar elemento en el proyecto, seleccione la
carpeta de la aplicación y, a continuación, seleccione Resultado principal
desde su servicio project name, como se muestra en la figura 8-5. Haga clic
en Aceptar. La salida principal se agrega a los cuatro nodos de las acciones
personalizadas: Install, Commit, Rollback y Uninstall.

Figura 8-5 La  creación de un proyecto de instalación de un servicio requiere


consideraciones especiales
D.   En el Explorador de soluciones, haga clic con el botón secundario en el
proyecto de instalación y, a continuación, haga clic en Generar.
Configuración del servicio de la carpeta de compilación incluye ahora un
archivo Setup.exe para instalar el servicio de forma interactiva y un archivo
MSI de despliegue automático del servicio.
Después de la instalación, puede desinstalar el servicio utilizando los métodos estándar:
manualmente, a partir de la herramienta Agregar o quitar programas, o automáticamente mediante
Windows Installer (MSI) herramientas.
Cómo gestionar y controlar un servicio
Después de instalar un servicio, debe iniciarlo. Si establece el tipo de inicio del servicio
a
Automático, reiniciando el equipo hará que el servicio se inicie. Si el inicio del servicio
Es de tipo manual, o si desea iniciar el servicio sin necesidad de reiniciar el equipo,
puede utilizar el complemento Servicios:
1.  Mientras haya iniciado la sesión como administrador o la cuenta de otro
usuario con privilegios para administrar servicios, haga clic en Inicio, haga
clic con el botón secundario en Mi PC y, a continuación, haga clic en
Administrar.
2.  Expanda Servicios y Aplicaciones y, a continuación, haga clic en Servicios.
3.  En el panel derecho, haga clic con el botón secundario en el servicio y, a
continuación, haga clic en Inicio, como se muestra en la figura 8-6.

Figura 8-6  Inicio Servicios desde el complemento Servicios.

Puede utilizar el mismo proceso para detener, pausar, reanudar o reiniciar el


servicio. Para cambiar el tipo de inicio del servicio o cuenta de usuario, haga clic
con el botón secundario en el servicio y, a continuación, haga clic en Propiedades,
como se muestra en la figura 8-7.
Figura 8-7  Configurar el tipo de inicio del servicio y de la cuenta de usuario
después de la instalación mediante la visualización del cuadro de diálogo Propiedades
de servicio.
También puede controlar los servicios desde la línea de comandos mediante el comando
Net con el formato net start  o net stop ServiceName ServiceName.
Los servicios de control a partir de un ensamblado, utilice System.ServiceProcess. clase
ServiceController. Esta clase le da la capacidad de conectarse a un servicio en el equipo
local o en un equipo remoto, examinar las capacidades del servicio, e iniciar, detener,
pausar o reanudar el servicio. El siguiente ejemplo de código, que exige tanto
el System.ServiceProcess (que se debe agregar manualmente una referencia en Visual
Studio) y los  espacios de nombres System.Threading, demuestra este proceso:
' VB
' Conectar al servicio de servidor
Dim sc como ServiceController = New ServiceController("Server").

' Detener el servicio


sc.stop()

' Espere dos segundos antes de iniciar el servicio


Thread.Sleep(2000)

' Inicie el servicio sc.Start()

// C#
// Conectarse al servicio de servidor
ServiceController sc = new ServiceController("Server");

// Detener el servicio
sc.stop();

// Espere dos segundos antes de iniciar el servicio


Thread.Sleep(2000);

// Iniciar el servicio sc.Start();

Laboratorio: crear, instalar e iniciar un servicio para


monitorizar un sitio Web
En este laboratorio, se crea un proyecto de servicio con Visual Studio y escribir código
para registrar el estado de una página Web cada 10 segundos. A continuación, cree un
proyecto de instalación para el servicio. Por último, instalar e iniciar el servicio.
    Ejercicio 1: Crear un servicio para monitorizar un sitio Web
En este ejercicio, crear y construir un servicio de Windows que se compruebe un sitio
Web cada 10 segundos  y escribir un mensaje en un archivo de registro que indica si el
sitio Web devuelve una página correctamente.
1.  Con Visual Studio, crear un proyecto utilizando la plantilla de aplicación de
servicios de Windows. Nombre del proyecto MonitorWebSite.
2.  Utilizando el servicio de vista de diseñador, cambiar el nombre y a Mon-
itorWebSite ServiceName. Establezca
la CanPauseAndContinue  CanShutdown y propiedades en True.
3.  Agregue el System.Timers, System.IOy  espacios de nombres System.Net para
el proyecto.
4.  Dentro de la  clase MonitorWebSite, cree un  objeto de temporizador. Por
ejemplo, el seguimiento código funcionará:
' VB
Privado como temporizador t = nada

// C#
Temporizador privado t = null;

5.  Dentro del   constructor MonitorWebSite (en Visual Basic, el  nuevo método


se encuentra en Service1.Designer.vb), configurar el temporizador para llamar
a un método cada
10 segundos, como se muestra en el código siguiente:
' VB
T = New Timer(10000)
AddHandler t.Transcurrido, nuevo System.Timers.ElapsedEventHandler(AddressOf _
Me.t_transcurrido)

// C#
T = new timer(10000);
T.Transcurrido nueva ElapsedEventHandler +=(t_transcurrido);

6.  Agregar código para el  método OnStart para habilitar e iniciar el


temporizador, como se demuestra a continuación:
' VB
T.Start()

// C#
T.Start();

7.  Agregue código al   método OnStop para detener el temporizador, como en el


siguiente ejemplo se muestra:
' VB
T.stop()

// C#
T.stop();

8.  Invalidar el OnPause, y OnShutdown OnContinue , métodos y agregar código


para iniciar y detener el temporizador, como se demuestra a continuación:
' VB
Protected Overrides Sub OnContinue()
T.Sta
rt() End
Sub
Protected Overrides Sub OnPause()
T.stop() End Sub

Protected Overrides Sub OnShutdown()


T.stop() End Sub

// C#
Protected override void OnContinue()
{
T.Start();
}

Protected override void OnPause()


{
T.stop();
}

Protected override void OnShutdown()


{
T.stop();
}

9.  En el método especificado para la ElapsedEventHandler, escribir el código para


comprobar el sitio Web y escriba el código de estado y hora actual a un archivo de
texto. Agregar un evento al registro de eventos si usted experimenta una excepción,
porque carecen de una interfaz de usuario para comunicar fácilmente la información de
la excepción para el usuario. El siguiente código muestra esto:
' VB
Protected Sub t_transcurrido(ByVal sender As System.Object, _ ByVal e As
System.Timers.ElapsedEventArgs)
Pruebe
' Enviar la solicitud HTTP
Dim As String = url "http://www.microsoft.com"
Dim g como HttpWebRequest = CType(WebRequest.Create(url), _ HttpWebRequest)
Dim r como HttpWebResponse = CType(g.GetResponse, HttpWebResponse)

' La respuesta de registro a un archivo de texto


Dim path As String = _ AppDomain.CurrentDomain.SetupInformation.ApplicationBase + _ "log.txt".
Dim tw como = New TextWriter StreamWriter(Ruta, True)
Tw.WriteLine(DateTime.Now.ToString + " " + url + " " + _
R.StatusCode.ToString)
Tw.Close().

' Cerrar la respuesta HTTP r.Close().


Catch ex As Exception
System.Diagnostics.EventLog.WriteEntry("application" _ "Excepción: " +
ex.Message.ToString)
Intente final
End Sub

// C#
Void t_transcurrido(object sender, ElapsedEventArgs e)
{
Pruebe
{
// Enviar la solicitud HTTP
String url = "http://www.microsoft.com";
HttpWebRequest HttpWebRequest (g =)WebRequest.Create(url); r =
(HttpWebResponse HttpWebResponse)g.GetResponse();

// La respuesta de registro a un archivo de


texto cadena path =
AppDomain.CurrentDomain.SetupInformation.ApplicationBase + "log.txt";
TextWriter tw = new StreamWriter(Ruta, true);
Tw.WriteLine(DateTime.Now.ToString() + " " + url + " " +
r.StatusCode.ToString());
Tw.Close();

// Cerrar la respuesta HTTP


r.Close();

}
Catch (Exception ex)
{
System.Diagnostics.EventLog.WriteEntry("application", "la excepción:
" + ex.Message.ToString());
}
}

10.  Generar el proyecto y resolver cualquier problema que aparezca. Tenga en


cuenta que (aún) no puede ejecutar el servicio, porque usted no ha creado un
instalador.
    Ejercicio 2: Crear un instalador de servicios
En este ejercicio, va a crear un instalador para el proyecto creado en el ejercicio 1.
1.  Agregar un instalador para su proyecto de servicio.
2.  Establezca las propiedades del instalador:
■   StartType   automático.
■   Descripción   "registra las respuestas de microsoft.com".
■   DisplayName   sitio Web "Monitor".
■   cuenta   LocalSystem. Tenga en cuenta que el uso de la cuenta
LocalSystem normalmente no es recomendado; sin embargo, este
proyecto requiere acceso para escribir un archivo de texto en el archivo
Sistema que  proporciona LocalSystem. Un método más seguro sería crear
una cuenta de usuario personalizada sólo con los privilegios necesarios; sin
embargo, esto podría distraer el propósito de este ejercicio.
3.  Definir el proyecto de servicio como objeto de inicio si aún no se ha definido.
4.  Agregar un proyecto de instalación a su solución y, a continuación, agregar la
salida de su servicio de proyecto para el proyecto de instalación.
5.  Agregar una acción personalizada para instalar el archivo ejecutable del servicio en
la carpeta de la aplicación.
6.  Construya su proyecto de instalación.
    Ejercicio 3: Instalar, iniciar y administrar el servicio
En este ejercicio, instalar y administrar el proyecto creado en los ejercicios 1 y 2.
1.  Inicie el programa Setup.exe que ha creado en el ejercicio 2, e instale el servicio
con la configuración predeterminada.
2.  Iniciar la Administración de equipos y, a continuación, seleccione el nodo de
servicios.
3.  Haga clic con el botón secundario en el sitio Web Servicio de monitor y, a
continuación, seleccione Iniciar. Observe que el
Complemento Servicios muestra el nombre y la descripción proporcionada en el
ejercicio 2.
4.  Espere 30 segundos y, a continuación, abra el archivo de texto que sus registros de
servicio para solicitar respuestas. Compruebe que está correctamente consultando
el servidor Web y escribe los resultados en el archivo de texto.
5.  Pausar el servicio, espere 30 segundos y compruebe que ya no agrega información
al archivo de registro.
6.  Reanudar el servicio, espere 30 segundos y compruebe que sigue agregando infor-
mación para el archivo de registro.
7.  Detenga el servicio mediante la apertura de una línea de comandos y ejecutar el
comando "net stop monitorwebsite".
8.  Por último, desinstala el servicio, vuelva a ejecutar Setup.exe.

Resumen de la lección
■   un servicio de Windows es un proceso que se ejecuta en segundo plano, sin
interfaz de usuario, en su sesión de usuario.
■   Para crear un servicio de Windows, utilice Visual Studio para crear un proyecto
utilizando el Win- dows plantilla Aplicación de servicio. A continuación, escriba
el código para el OnStart y OnStop procedimientos y anular cualquier otros
métodos que desee redefinir.
Agregar la necesaria instaladores para su aplicación de servicio. Por último,
crear un proyecto de instalación para instalar el servicio.
■   Para implementar un servicio, especifique el nombre del servicio, descripción
y tipo de inicio.
Entonces reemplazar el OnStart OnStop OnPause , ,, , y OnShutdown
OnContinue pro- cedimientos como sea necesario.
■   Para crear un proyecto para instalar un servicio, primero debe definir las
propiedades de un servicio- objeto Installer para especificar la descripción del
servicio, el nombre para mostrar, el nombre del servicio y el tipo de inicio. A
continuación, defina las propiedades de un ServiceProcessInstaller para
especificar la configuración de la cuenta de servicio. En este punto, puede
instalar manualmente el servicio, o crear un proyecto de instalación para el
servicio.
■   Para controlar manualmente un servicio, puede usar la herramienta de línea
de comandos Net o los ser- vicios snap-in. Alternativamente, puede
usar System.ServiceProcess.ServiceController clase para controlar un servicio
desde un ensamblado.

Examen de la lección
Puede utilizar las siguientes preguntas para poner a prueba tus conocimientos de la
información Les- hijo 3, "Crear servicios de Windows." Las preguntas también
están disponibles en el CD de iones complementaria si prefiere revisarlos en forma
electrónica.

Nota   Respuestas
Las respuestas a estas preguntas y explicaciones de por qué cada opción de
respuesta es correcta o no se encuentran en la sección "Respuestas" al final
del libro.

1.  Que tipo de cuenta debe usted elegir para minimizar los riesgos de seguridad?
A.  LocalService
B.   NetworkService
C.   LocalSystem
D.   Usuario
2.  Que tipo de cuenta debe usted elegir para minimizar la posibilidad de
problemas causados por permisos excesivamente restrictivos en el equipo
local?
A.  LocalService
B.   NetworkService
C.   LocalSystem
D.   Usuario
3.  Cuáles de las siguientes son maneras válidas para instalar un servicio en un equipo?
(Seleccione todos los que correspondan).
A.  Agregue un acceso directo a la asamblea al grupo Inicio del usuario.
B.   Usar InstallUtil para instalar el servicio.
C.   Configurar Tareas Programadas para lanzar su ensamblado en el inicio.
D.   Utilice Visual Studio para crear un instalador para su servicio.
4.  Las herramientas que puede utilizar para cambiar la cuenta de usuario de un servicio
después de que el servicio está instalado?
A.  Mi equipo
B.   Administración de equipos
C.   Net
D.   Configuración de Microsoft .NET Framework 2.0
Repaso del cap tulo
A nuevas prácticas y reforzar las habilidades aprendidas en este capítulo, puede
com- pleta las siguientes tareas:
■   Revisar el resumen del capítulo.
■   Revisar la lista de términos clave que se introdujo en este capítulo.
■   Complete los casos. Estos escenarios configurar situaciones del mundo real
involv- ción de los temas de este capítulo, y le pedimos a usted para crear una
solución.
■   completar las prácticas sugeridas.
■   tomar un examen de práctica.

Resumen del capítulo


■   los dominios de aplicación son contenedores lógicos que permiten ejecutar
varios ensamblados en un único proceso sin poder acceder directamente a
cada una de las notas del otro- ries. Los dominios de aplicación ofrecen
espacios de memoria independiente y acceso a recursos sin la sobrecarga de
crear un segundo proceso.
■   Cuando  se crea un nuevo dominio de aplicación, usted puede controlar
muchos aspectos de la configuración del dominio de aplicación. Lo que es
más importante, puede restringir los privilegios de los ensamblados que se
ejecutan dentro del dominio de aplicación, proporcionando la evi- dencia al
crear el dominio de aplicación o al iniciar el proceso.
■   Los servicios se ejecutan en segundo plano, sin proporcionar una interfaz
para el usuario. Cre- ellos un servicio es diferente de la creación de otros tipos
de aplicaciones porque no se puede ejecutar directamente un archivo
ejecutable del servicio. En su lugar, debe instalar manualmente el servicio o
crear un proyecto de instalación para el servicio. Otras consideraciones
incluyen servicios exclusivos de tipo de inicio, tipo de cuenta, y herramientas
de gestión.

Términos clave
¿ Sabes lo que significan estos términos clave? Puede verificar sus respuestas
mediante la búsqueda de los términos en el glosario al final del libro.
■ El   dominio de la aplicación
■   general pruebas
■ una   defensa en profundidad
Chapter 8 Review 473

■   pruebas

■   LocalService

■   LocalSystem

■   NetworkService

■   service

Casos
En los siguientes casos, podrá aplicar lo que ha aprendido sobre cómo utilizar los
dominios de aplicación y servicios. Usted puede encontrar las respuestas a estas
preguntas en la sección de "respuestas" al final de este libro.

Caso práctico 1: Creación de una herramienta de pruebas


Usted es un programador para el Museo de Ciencias. Los usuarios finales ejecutan su
aplicación desde diferentes ubicaciones. Porque el tiempo de ejecución de .NET
Framework asigna- dif erentes conjuntos de permisos basados en la ubicación de
ensamblado, la asamblea suele dirigir en un entorno de confianza parcial. Esta situación
ha causado problemas para los usuarios finales. Su jefe le pide que la entrevista personal
clave de la empresa y luego llegar a su oficina para responder algunas preguntas. Tu jefe
te necesita para crear una aplicación que crea un dominio de aplicación y lanza un
ensamblado en el nuevo dominio de aplicación con permisos de zona de Internet para
habilitar procedimientos de pruebas más realistas.

Entrevis
tas
La siguiente es una lista de personal de la empresa entrevistados y sus
declaraciones:
■   Customer Support Manager "Estamos recibiendo muchas llamadas de clientes
que quieren desplegar nuestra app desde un servidor Web. Parece que esto no
funciona por alguna razón, aunque. Los usuarios terminan recibiendo errores
diferentes. Desde la manera en que
Describir los errores, parece que la aplicación se bloquea en momentos diferentes,
dependiendo de si la aplicación es lanzada desde la Internet pública o la intranet
local del usuario. Ahora que acabamos de decirles a copiarlo a su com- puters local
y ejecutarlo, y que parece resolver el problema. La gente no le gusta este trabajo
temporal, aunque, y quieren saber por qué no podemos hacer que funcione desde
un servidor Web".
■   Desarrollo Manager  "Hablé con el Gerente de Atención al Cliente, y
parece que los usuarios están teniendo problemas debido a la seguridad de
acceso a código restringir- ciones. Necesitamos comenzar a probar nuestra
aplicación en distintas zonas, de modo que podamos
Identificar los problemas cuando están restringidos los permisos. Hágame un
favor, y escribir una aplicación que permite a nuestro equipo de
Aseguramiento de calidad para ejecutar nuestra aplicación en distintas zonas".

Preg
unta
s
Responda a las siguientes
preguntas:
1.  A un alto nivel, describen cómo crear la aplicación.
2.  Crear una aplicación que crea un dominio de aplicación y se inicia
la  asamblea CASDemands en el nuevo dominio de aplicación con permisos
de zona de Internet.

Caso práctico 2: Seguimiento de un archivo


Usted es un desarrollador de aplicaciones que trabajan para el departamento de TI
de Humongous insur- ance. Usted acaba de publicar un proyecto en el que hemos
estado trabajando durante meses. El hombre- ager ha decidido utilizar su tiempo
libre por tener que crear una herramienta para ayudar a los administradores de
sistemas mantener la integridad de los equipos de escritorio de la organización.

Entre
vistas
La siguiente es una lista de personal de la empresa entrevistados y sus
declaraciones:
■   IT Manager  "Gracias a la más reciente ronda de actualizaciones de
aplicaciones producidas por su equipo, todas nuestras aplicaciones soportan
archivos de configuración basado en XML. Esto es genial, porque permite a
nuestros usuarios más avanzados para afinar el conjunto de configuración-
Tings. Sin embargo, nos dimos cuenta de que uno de nuestros usuarios, hizo
un cambio que deshabilita la característica integrada en las funciones de
seguridad. Quiero que los usuarios puedan hacer algunos cambios, pero quiero
ser notificado si cambian la configuración que controla las funciones de
seguridad. Auditoría de archivos no es lo suficientemente precisa, porque
noti- fies de mí cuando el usuario realiza cualquier cambio en el archivo de
configuración. Necesito ser capaz de implementar el servicio a través de
nuestra infraestructura de Systems Management Server, así que por favor
proporcione un archivo MSI".
■   Desarrollo Manager  "no necesitamos para impedir que los usuarios
realicen cambios, y no sé cómo podemos hacer que de todos modos sin
bloquear todo el acceso al archivo de configuración. Sólo tenemos que añadir
un evento al registro de eventos si detectamos
Capítulo 8 Revisión  475

Que el usuario cambia la configuración de seguridad en el archivo de


configuración. Después de que el evento se agrega al registro de eventos, el
departamento de IT's Event Management infra- estructura habrá de notificar a un
administrador que puede resolver el problema. Necesitamos crear el evento
inmediatamente después de que el usuario guarde el cambio, sin embargo, para
dirigir un proceso cada noche no será suficiente".

Preguntas
Responda las siguientes preguntas para el administrador:
1.  ¿Qué tipo de aplicación se puede crear para atender la necesidad del departamento
de TI?
2.  ¿Cómo va a abordar la necesidad de implementar la aplicación con un archivo MSI?
3.  ¿Qué tipo de inicio podrás especificar?
4.  ¿Qué tipo de cuenta le especifique?

Prácticas recomendadas
Que le ayudarán a dominar los objetivos contemplados en el presente capítulo, complete
las siguientes tareas.

Crear una unidad de aislamiento para Common


Language Runtime dentro de una aplicación de .NET
Framework mediante el uso de dominios de aplicación
Para esta tarea, debe completar ambas prácticas.
■ La   Práctica 1  Crear un ensamblado que imita el malware mediante la lectura de
un archivo desde la carpeta Mis documentos del usuario actual y, a continuación,
conectarse a un servidor Web. A continuación, cree una segunda asamblea que
especifica las pruebas para crear un calzado restrictivo-
Ción para el dominio conjunto de malware e impide que la lectura de la
información personal del usuario.
■ La   Práctica 2  Crear un ensamblado que asigna grandes cantidades de memoria.
Ejecutar el ensamblado y utilizar el complemento de Rendimiento para supervisar
el uso de memoria del conjunto. A continuación, cree una segunda asamblea que
lanza la primera asamblea en un
Dominio de aplicación y, a continuación, descarga el dominio de aplicación.
Supervisar el uso de memoria del conjunto para verificar que los recursos son
desasignado.
476 Chapter 8 Review

Implementar,  instalar y controlar un servicio


Para esta tarea, usted debe completar al menos Práctica 1. Si desea una mejor
comprensión de los problemas involucrados con la implementación de servicios en
el mundo real, completar las prácticas 2 y 3.
■ La   Práctica 1  Crear un servicio que se escucha por conexiones de red
entrantes y utiliza la herramienta InstallUtil para instalar el servicio. Una vez
que haya verificado que funciona correctamente, utilice la herramienta
InstallUtil para desinstalar el servicio.
■ La   Práctica 2  Crear un servicio que realiza las tareas descritas en el caso
de la hipótesis 2, anteriormente en este capítulo.
■ La   Práctica 3  Modificar el servicio que ha creado en los ejercicios 1 y 2 de
la lección 3 para que se ejecute con la cuenta LocalService. Identificar los
privilegios que la cuenta LocalService requiere para permitir el
funcionamiento correcto del servicio. Crear
Una nueva cuenta de usuario con los privilegios necesarios, y configurar el
servicio para que se ejecute bajo la cuenta del nuevo usuario.

Tomar un Test de práctica


Los tests de práctica en este libro's Companion CD ofrecen muchas opciones. Por
ejemplo, puede hacerse la prueba en un solo examen objetivo, o puede probar usted
mismo en todos los exámenes de certificación 70-536 contenido. Puede configurar
la prueba para que simula cuidadosamente la expe- riencia de tomar un examen de
certificación, o puede configurarlo en modo de estudio, de modo que usted puede
mirar las respuestas correctas y explicaciones después de responder a cada
pregunta.

Más info  práctica de pruebas


Para obtener más información acerca de la prueba de la práctica todas las
opciones disponibles, consulte la sección "Cómo usar los tests de práctica"
La sección en este manual de introducción.
Capítulo 9
Instalación y configuración
Aplicaciones
En este capítulo se tratan dos temas distintos: la instalación de aplicaciones y
configuración de apli- caciones. Porque es imposible saber exactamente qué ajustes de
usuario será necesario para cualquier instalación, haciendo que su aplicación
configurable asegura que hacer esos cambios será trivial. Además, la creación de una
perfecta instalación reversible, no es sólo una opción en el mercado de hoy en día, es un
mandato. Además, la instalación tendrá un gran impacto en cómo los clientes
inicialmente ver su aplicación. Por lo tanto, garantizar que el proceso sea fácil, intuitiva
y completa es un requisito previo para las aplicaciones profesionales de hoy.

Objetivos del examen en


este capítulo:
■   integrar
la funcionalidad de gestión de configuración en un archivo .NET
Framework aplica-
Ción. (Consulte el  espacio de nombres
System.Configuration)
❑    clase Configuration y la  clase ConfigurationManager
❑    claseConfigurationElement,  clase y ConfigurationElementCollection
 Clase
ConfigurationElementProperty
❑    clase
ConfigurationSection, ConfigurationSectionCollection , configuración de
clase clase  clase ConfigurationSectionGroupCollection SectionGroup y
❑   Implementar   interfaz ISettingsProviderService
❑   Implementar   interfaz IApplicationSettingsProvider
❑   ConfigurationValidatorBase class

■   Crearuna personalizada de Microsoft Windows Installer para los componentes de


.NET Framework utilizando System.Configuration.Install namespace, y
configurar las aplicaciones de .NET Framework utilizando archivos de
configuración, variables de entorno, y la herramienta de configuración de .NET
Framework (Mscorcfg.msc).
❑     clase Installer
❑   Configurar
qué versión de tiempo de ejecución de una aplicación de .NET
Framework debe utilizar
❑   Configurar dónde debe buscar un ensamblado el tiempo de ejecución
❑   Configurar la ubicación de un ensamblado y qué versión del ensamblado
para utilizar
477
478 Chapter 9 Installing and Configuring Applications Before You Begin 478

❑   El tiempo de ejecución directa para utilizar la variable de entorno


DEVPATH cuando busque ensamblados
❑   AssemblyInstaller class

❑   ComponentInstaller class

❑   Configurar
una aplicación de .NET Framework mediante el uso de .NET
Framework
(Mscorcfg.msc)
❑   ManagedInstallerClass class

❑   InstallContext class

❑   InstallerCollection class

❑   InstallEventHandler delegado

❑   Configurar la recolección simultánea de elementos


❑   registrar objetos remotos mediante  archivos de configuración

Las lecciones de este capítulo:


■ La   Lección 1: Opciones de
configuración . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 480
■ La   Lección 2: Creación de un
instalador . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 511
■ La   Lección 3: Uso de la herramienta Configuración de .NET Framework
2.0 . . . . . . . . . . . . 520
■ La   Lección 4: Gestión de la configuración . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . 531

Antes de empezar
Para completar las lecciones de este capítulo, debe estar familiarizado con
Microsoft Visual
Basic o en C# y se sienten cómodos con las siguientes
tareas:
■   Crear una consola o aplicación Winforms en Microsoft Visual Studio
utilizando
Visual Basic o C#.
■   La salida directa a la ventana de depuración o la ventana de la consola.
■   Establecer un punto de interrupción en el depurador.
■ El   paso a un segmento de código.
479 Chapter 9 Installing and Configuring Applications Before You Begin 479

■ El   paso a lo largo de un segmento de código.


■ El   paso a través de un segmento de código.
■   Tener una comprensión básica de un archivo de configuración.
■   Tener una comprensión básica de la estructura del documento XML.
480 Chapter 9 Installing and Configuring Applications Before You Begin 480

Mundo Real
William Ryan
La tarea de configuración de software en los últimos años era muy diferente de lo que
supone hoy en día. No hace mucho, no hubo un  espacio de nombres
System.Configuration, y no hay un registro de Windows o el almacenamiento aislado.
Aun con la llegada de .NET Framework, la creación y personalización de una
aplicación la configu- ración era típicamente una tarea ardua. Al escribir mi primera
producción .NET de apli- cación, pasé más de seis horas en  crear los archivos de
configuración, crear las clases para manejar esos archivos y probar el código. Sólo para
obtener una línea de base comparativa, me reescribió la configuración de los elementos
de esa misma aplicación utilizando las nuevas herramientas que se incluyen con Visual
Studio. La tarea que me tomó más de seis horas para com- pleta de entonces se realiza
en menos de 15 minutos!
481 Chapter 9 Installing and Configuring Applications Before You Begin 481

Lección 1: Ajustes de configuración


Con respecto al desarrollo de aplicaciones, hay pocos absolutos. En lugar de
"siempre hacerlo de esta manera" o "esta forma es siempre mejor" enfoques, los
desarrolladores se enfrentan a desventajas. Algunas decisiones son más sencillos
que otros, pero la mayoría implican concesiones sin embargo. Hacer una
evaluación precisa de los costos de los beneficios y costos asociados con los
equilibrios es absolutamente crítica para entregar productos de calidad a tiempo y
dentro del presupuesto.
Un área que tiende hacia el absoluto, sin embargo, es la evitación de codificar
variables. Variables Hard-coding presenta múltiples desafíos. Aunque ciertamente
hay veces cuando hard-coding es aconsejable, como regla general, es mejor evitar
esa práctica.
.NET Framework proporciona a los desarrolladores un amplio conjunto de
herramientas para evitar hard-coding y hace que sea relativamente sencillo para
mejorar considerablemente la flexibilidad de su aplicación. Para comprender
plenamente el valor de las herramientas disponibles en .NET Framework 2.0, vale
la pena mencionar cómo
Las aplicaciones .NET que se había configurado en versiones anteriores de
Framework.
Antes de .NET Framework 2.0, los desarrolladores tenían dos maneras de gestionar
la configuración. El primer enfoque fue simplemente poner todos los ajustes en
la  sección appSettings del archivo de configuración. Esta estrategia ofrece la
ventaja de la simplicidad, pero esa sencillez llegó en el costo de tener que lidiar con
esos ajustes en una no-forma orientada a objetos. La otra forma es definir los
parámetros de configuración personalizados secciones en el archivo de
configuración y generar las clases correspondientes a consumir esos ajustes. Este
enfoque ha permitido a los desarrolladores tratar con las opciones de configuración
en una forma orientada a objetos completamente, pero esa funcionalidad llegó en el
costo de tener que escribir un montón de código (código que a menudo era
monótono y mucho tiempo para escribir. No era raro para un desarrollador que tuvo
que lidiar con aplicaciones no triviales para pasar casi dos horas de creación y
prueba de las secciones de configuración personalizadas en las versiones anteriores
de Framework. Utilizando las nuevas herramientas disponibles en .NET Framework
2.0, los desarrolladores pueden utilizar el último método para configurar incluso
una aplicación compleja en pocos minutos, porque ahora todos que monótono
código está escrito para usted.
482 Chapter 9 Installing and Configuring Applications Before You Begin 482

Con esto en mente, las principales ventajas de utilizar Configuración de .NET son
como sigue:
■   lepermite establecer y conservar la configuración sin tener que saber cuáles
son esos ajustes serán de antemano.
■   lepermite manejar su configuración en una intuitiva forma orientada a
objetos.
■   le permite leer y escribir la configuración sin dependencia en el registro de
Windows. Esto hará que su aplicación mucho menos intrusivas para seguridad y
administradores de red (ya que no habrá necesidad de modificar o conceder
privilegios en el registro de Windows). También ayudará a garantizar la
compatibilidad multiplataforma. (Otros sistemas operativos, como Linux y Mac
OS, no tienen un registro).

Después de esta lección, será


capaz de:
■   manipular ajustes comunes.
■   manipular la configuración de la aplicación.
■   recuperar ajustes específicos.
■   registrar componentes remotos.
Lección Tiempo estimado: 45
minutos

Configuración de .NET
Framework 2.0
El  espacio de nombres System.Configuration sirve como repositorio de todas las clases
que utilizan los desarrolladores para administrar la configuración.

Nota  ejemplos de código requiere una referencia al   espacio de


nombres System.Configuration.
Todos los ejemplos de código en esta lección asumen que el   espacio de nombres
System.Configuration ha sido importado. Esto puede lograrse mediante la
especificación de Imports System.Configuration  en la parte superior de una clase o
módulo en Visual Basic o especificar mediante System.Configuration  en C#.
También podría
Sería necesario añadir una referencia al archivo System.Configuration.dll
general a través de la visual
Studio haciendo clic en el menú Proyecto, haga clic en Agregar referencia,
seleccione la ficha .NET y selección
System.Configura
tion.

Este espacio de nombres es bastante completa y ofrece tanto de propósito general y de


tipos específicos para cada escenario de configuración concebible.

Nota   .NET 2.0


La   clase ConfigurationManager primero llegó a ser en Microsoft Enterprise Library.
Desde entonces, se ha convertido en un miembro de .NET Framework y reemplaza
muchos de los enfoques existentes para manip ulating- datos de configuración.

En la parte superior de la jerarquía lógica dentro del  espacio de nombres


System.Configuration son
Configuración de   clases y ConfigurationManager.
Cuando se utiliza cualquiera de los objetos que se examinan en este capítulo (o
cualquiera que son miembros del  espacio de nombres System.Configuration), los
nombres completos de objetos será necesario usar o importar el  espacio de
nombres System.Configuration tendrá que ser hecho. Esto se consigue agregando
Imports System.Configuration en la parte superior de un módulo de Visual Basic o
clase o utilizando System.Configuration en una clase de C#.
Estas dos clases tienen una intuitiva sinergia que se hace evidente cuando los use.
Asimismo, cabe señalar que ni tiene un constructor de la clase especificada. La
tabla 9-1 y la tabla 9-2 muestra las definiciones de clase más importantes de
la configuración y la configu- ración clases Manager, respectivamente. Preste
atención a las definiciones de clase de cada clase-usted verá porqué en breve.
Tabla 9-1
Configuración  propiedades y
métodos
Nombre Descripción
AppSettings Esta propiedad obtiene el  objeto AppSettingsSection
configura- ción de la sección que se aplica a
este  objeto Configuration
   Esta propiedad ConnectionStrings obtiene el  objeto
ConnectionStringsSection con- figuración sección que
se aplica a este  objeto Configuration
   Esta propiedad EvaluationContext obtiene el  objeto
ContextInformation sección de configuración que
se aplica a este  objeto Configuration
   Esta propiedad FilePath obtiene la ruta física al archivo de
configuración representada por este  objeto
Configuration
    Este método GetSection devuelve el
especificado ConfigurationSection
O
bj
et
o
GetSectionGroup  Este método devuelve el  objeto de
configuración especificado- SectionGroup
   Esta propiedad HasFile indica si existe un archivo de
configuración para el recurso representado por el
objeto de configuración
   Esta propiedad NamespaceDeclared obtiene o establece un valor que
indica si el archivo de configuración tiene un espacio
de nombres XML
   Esta propiedad RootSectionGroup obtiene
el ConfigurationSectionGroup para este
 Objeto de
configuración
Tabla 9-1  Configuración  propiedades y métodos

Nombre Descripción
Guardar  este método escribe la configuración contenida en
este  objeto de configuración a la actual config- uración archivo
XML
   Este método SaveAs escribe la configuración contenida en este  objeto
de configuración especificado a la con- figuración archivo XML

Tabla 9-2  Configuración  propiedades y métodos

Nombre Descripción
AppSettings Obtiene los  datos AppSettingsSection del actual aplica- ción
la configuración predeterminada
ConnectionStrings   ConnectionStringsSection obtiene los datos de la
configuración predeterminada de la aplicación actual
GetSection Retrieves una sección de configuración especificado
por la configuración predeterminada de la aplicación
actual
OpenExeConfiguration   abre la configuración de cliente especificado como un
 Objeto System.Configuration.Configuration
OpenMachine- Abre el archivo de configuración de la máquina en el equipo
Configuración actual como un  objeto System.Configuration.Configuration
- OpenMappedExe Abre la configuración de cliente especificado como
Configuración un  objeto System.Configuration.Configuration
utilizando el archivo especificado y asignación de nivel
de usuario
OpenMappedMachine-
Configuración Abre el archivo de configuración de la máquina
especificada como un  objeto
System.Configuration.Configuration mediante la
asignación del archivo especificado.

Dos cosas debe ser evidente en este punto. En primer lugar, ambas clases tienen dos
propiedades idénticas (AppSettings y ConnectionStrings). En segundo lugar, cada una de las
propiedades de la  clase ConfigurationManager devuelve   objetos de configuración. Estos
hechos deberían darle alguna sugerencia en cuanto a cómo interactúan estas dos clases. Para
recuperar la configuración, siga estos pasos:
1.  Declarar un  objeto de configuración.
2.  Utilizar los distintos métodos en ConfigurationManager con el prefijo "Abrir"
para abrir la aplicación o el archivo de configuración de la máquina. El
siguiente código muestra esto:
"VB
Dim cs como Configuration = _
ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None)
// C#
Configuración cs = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.ninguno);

' VB
Dim cs como Configuration = ConfigurationManager.OpenMachineConfiguration()
// C#
Configuración cs = ConfigurationManager.OpenMachineConfiguration();

' VB
Dim myMap como nuevo ExeConfigurationFileMap
MyMap.ExeConfigFilename = "DBConnectionStringDemo.exe.config" Dim cs
como Configuration = _
ConfigurationManager.OpenMappedExeConfiguration(myMap, _
ConfigurationUserLevel.None)

// C#
MyMap ExeConfigurationFileMap = new ExeConfigurationFileMap();
MyMap.ExeConfigFilename = @"DBConnectionStringDemo.exe.config";
configuración cs =
ConfigurationManager.OpenMappedExeConfiguration(myMap,
ConfigurationUserLevel.ninguno);

"VB
Dim myMap como nuevo ExeConfigurationFileMap myMap.ExeConfigFilename =
"DBConnectionStringDemo.exe.config"

Dim cs como Configuration = _


ConfigurationManager.OpenMappedMachineConfiguration(myMap)

// C#
MyMap ExeConfigurationFileMap = new ExeConfigurationFileMap();
MyMap.ExeConfigFilename = @"DBConnectionStringDemo.exe.config".

Configuración cs =
ConfigurationManager.OpenMappedMachineConfiguration(myMap);

Estos métodos son muy similares y, en última instancia, sirven a un propósito


similar, a saber, abrir un archivo de configuración y volver a los valores que
contiene un  objeto de configuración. La primera y la tercera son los métodos
utilizados para la apertura de los archivos de configuración específicos de la
aplicación, mientras que la segunda y la cuarta son los métodos utilizados para abrir
un determinado archivo de configuración de la máquina. Abrir y leer la
información de configuración utilizando los ajustes- tionManager clase, como los
ejemplos anteriores ilustran, es bastante intuitivo, pero algunas áreas necesitan
mayor aclaración.
La primera área que podría necesitar explicación es la  enumeración
ConfigurationUserLevel. Detalles de la  enumeración ConfigurationUserLevel se
proporcionan en la Tabla 9-3.
Tabla 9-3    enumeración ConfigurationUserLevel

Nombre Descripción
Ninguno  Obtiene el  objeto System.Configuration.Configuration
que se aplica a todos los usuarios
   Roaming PerUserRoaming Obtiene el  objeto
System.Configuration.Configuration que se aplica al  usuario
actual
PerUserRoaming- Local obtiene el  objeto System.Configuration.Configuration
AndLocal que se aplica al  usuario actual

La principal cuestión a tener en cuenta es que el valor por defecto es Ninguno, que podría
parecer ilógico a algunos.
La siguiente cuestión que podría necesitar elaboración implica
la ExeConfigurationFileMap objeto o "Archivo Mapa." No es sorprendente que, si desea
utilizar un archivo de mapas, el tiempo de ejecución necesita algún mecanismo para
informarle de que desea hacerlo, así como un mecanismo para diciendo donde se puede
encontrar dicho archivo. Este proceso se ve facilitado por la ExeConfig-  propiedad
Filename.
Cuando llame el   raton- OpenMappedMachineConfigu OpenMappedExeConfiguration
o método, estás informando a la ejecución de su intención de utilizar un archivo asignado. El
constructor lo exige, por lo que el único otro requisito mínimo es para especificar
una ubicación para el archivo. La aplicación que consume necesitará los permisos adecuados
para tener acceso a este archivo, por lo que deberá asegurarse de que el archivo existe y que
tiene permiso para acceder a él.

Más info  permisos y seguridad declarativa


Administración de permisos es una parte esencial de la creación de aplicaciones .NET
Framework seguras. El tema de permisos y seguridad declarativa e imperativa se
tratan en profundidad en el Capítulo 11. Información adicional está disponible en
MSDN en http://msdn.microsoft.com/library/  default.asp?URL=/library/en-
us/cpguide/html/cpconPermissions.asp.

Además de asegurar que el usuario tiene permisos de acceso al archivo, será necesario
comprobar la ubicación del archivo. Si se especifica una cadena vacía o en blanco para
el ExeConfig- propiedad Filename, el tiempo de ejecución generará una ArgumentException.
La figura 9-1 muestra el resultado de un intento de establecer la  propiedad
ExeConfigFilename de String.Empty.
Figura 9-1  al intentar establecer ExeConfigFilename  a una
cadena vacía

Por desgracia, si establece el ExeConfigFilename a un archivo que es inexistente, el


tiempo de ejecución, no le impide hacerlo (hasta más tarde cuando tenga valores
null cuando se esperaba otra cosa). Puede evitar este error al garantizar que se
dispone de un archivo válido antes de que se establezca esta propiedad. Puede
implementar cualquier lógica de flujo de control que desee, pero en aras de la
claridad, usaremos una aserción de depuración aquí para comprobar la existencia
del archivo:
"
V
B
Dim cs como Configuration = ConfigurationManager.OpenMachineConfiguration()
Debug.Assert(File.Exists(ExeFileName) _
"El archivo o ruta asignada está ausente o es incorrecto!").
MyMap.ExeConfigFilename = ExeFileName

/
/

C
#
MyMap ExeConfigurationFileMap ExeConfigurationFileMap = new
String();"DBConnectionString ExeFileName = @.exe.config";
Debug.Assert(File.Exists(ExeFileName)
"El archivo o ruta asignada está ausente o es incorrecto!");
MyMap.ExeConfigFilename = ExeFileName;

Ajustes
comunes
El término ajustes comunes se refiere a unas pocas áreas que determinan cómo se
ejecutan las aplicaciones.
Un ejemplo de esta funcionalidad es configurar una aplicación para que se ejecute
en una versión específica de .NET Framework. Por ejemplo, puede crear una
aplicación con una determinada versión de Framework, pero elegir ejecutarlo con
una diferente. Para poder hacerlo, debe especificar el supportedRuntime versión en
la sección de inicio. Si desea ejecutar la aplicación bajo la versión 1.1 de
Framework, debe escribir el siguiente código en la sección de configuración de la
aplicación o el archivo de configuración Web:
<?xml version="1.0"?>
<Configuració
n>
<Inicio>
<supportedRuntime version="1.1.4322" />
</startup>
</configuration
>
Sin embargo, puede haber casos donde la versión que desee ejecutar bajo no está presente en
la máquina. Existen normas estrictas que son seguidas en estas circunstancias:
■   Si la versión de Framework que con la que se construyó la aplicación está presente,
esa versión será utilizado por la aplicación.
■   Si la versión de Framework que la aplicación fue construida con no está presente y no
se especifica nada en la  etiqueta supportedRuntime versión, la aplicación se ejecutará
en la última versión del marco que está disponible en la máquina. Por lo que se
ejecutan en .NET Framework 2.0, si esa era la única versión presente, incluso si la
aplicación fue construido en virtud de las versiones 1.x.
■   Si la versión de Framework que la aplicación fue construida con no está presente, pero
el archivo de configuración especifica una  etiqueta supportedRuntime, .NET
Framework usará la versión en tiempo de ejecución especificado aunque la versión
especificada debe estar presente en el equipo.
Estas reglas son intuitivos. Si no dispone de la versión correcta del motor en tiempo de
ejecución que una aplicación necesita y no especifica una versión diferente, el tiempo de
ejecución hará todo lo posible para ejecutar la aplicación. Si el tiempo de ejecución no se
puede ejecutar el ensamblado con la versión disponible, tienes un problema.
Otro escenario común implica el uso de un ensamblado compartido y comprobar que
funciona con múltiples aplicaciones. La instalación de este determinado ensamblado en la
caché de ensamblados global (GAC) y desinstalarlo desde el GAC puede ser engorroso. Para
dar cabida a esta tarea, existe una variable denominada DEVPATH que pueden configurarse.
Para tomar ventaja de esto, dos cosas que hacer:
1.  Agregar una variable de entorno denominada DEVPATH que apunte a la ubicación de
la Asamblea.
2.  Establecer el  valor true para developmentMode. El siguiente fragmento de código
muestra cómo hacerlo:
<Configuración>
<runtime>
<developmentMode developerInstallation="true"/>
</runtime>
</configuration>

Otra tarea común implica especificar dónde una determinada versión de un ensamblado se
encuentra. Puede hacerlo mediante la herramienta Configuración de .NET Framework 2.0
(que se tratan en profundidad en la lección 3) o editando manualmente el archivo de
configuración del equipo. Hay un elemento específico, codeBase, que le permite especificar
la ubicación
Y la versión de un ensamblado de modo que cuando se carga el motor en tiempo de
ejecución, utilice lo que usted ha especificado. El código siguiente proporciona un
ejemplo de cómo hacerlo:
<Configuració
n>
<runtime>
<assemblyBinding xmlns="schemaname">
<dependentAssembly
>
<assemblyIdentity name="miprograma"
publicKeyToken="xxxxxxxxx" cultura="en-us" />
<codeBase version="x.0.0.0"
Href="http://www.adatum.com/myprogram.dll"/>
</dependentAssembly
>
</assemblyBinding>
</runtime>
</configuration
>

Otras opciones comunes son los valores de configuración que ya están definidos
por el .NET Framework. Consta de dos secciones principales,   appSettings y
connectionStrings. En muchos sentidos, estos valores de configuración sean
utilizados de forma idéntica a cualquier otro componente de configuración; sin
embargo, no contienen algunos matices que les dan ventajas sobre otros temas.
Porque son tratados de manera diferente a otros valores, tienen una ubicación
predefinida en el archivo de configuración donde deben colocarse. El siguiente
fragmento de código muestra un ejemplo de archivo de configuración que incluye
tanto una    sección appSettings y connectionStrings:
<?xml version="1.0" encoding="utf-8" ?>
<Configuració
n>
AppSettings <>
<add key="foo" value="Hello World!"/>
</appSettings>
<connectionStrings>
<claro/>
<add name="" AdventureWorksString
providerName="System.Data.SqlClient"="Datos
connectionString Source=localhost;
Initial Catalog=AdventureWorks; Integrated Security=true"/>
</connectionStrings>
</configuration
>

El código anterior crea un  elemento appSettings y connectionStrings


de valor. El  valor appSettings se llama foo y contiene el literal "Hola Mundo".
El  valor de las cadenas de conexión se denomina AdventureWorksString y
contiene una cadena de conexión que puede ser utilizado para conectarse a una base
de datos de SQL Server.
El enfoque utilizando una  sección appSettings es sencillo. Se especifica una clave,
que es el nombre que utilizará para identificar de forma única a  ese valor de modo
que se pueda recuperar, y que especifique un valor. El único propósito de la clave
es proporcionar un medio legible humano mediante el cual usted puede recuperar
un valor determinado. En este ejemplo, la palabra Foo
Podría ser un poco críptico, así un mejor ejemplo podría ser CompanyName. Leyendo el
archivo de configuración, la mayoría de cualquiera debería ser capaz de averiguar por qué
una clave denominada CompanyName se refiere. Aunque la  clase ConfigurationSettings es
obsoleta, la  propiedad AppSettings es todavía una parte de ella para mantener la
compatibilidad con versiones anteriores. Hay otras maneras, sin embargo, hacer referencia a
ella en .NET Framework 2.0. El siguiente código muestra un ejemplo sencillo de cómo
recuperar un valor AppSettings.
' VB
Dim HelloWorldVariable = ConfigurationSettings.AppSettings("foo")

// C#
Cadena HelloWorldVariable = ConfigurationSettings.AppSettings["foo"];

Nota   El significado de "obsoleta"


Si bien la palabra "obsoleto" tiene un significado general, también tiene un significado
preciso en el .NET Framework. Muchos métodos, propiedades y objetos que están en
desuso y, por lo tanto, considerados obsoletos todavía se mantienen en el marco para
proporcionar compatibilidad con versiones anteriores. Técnicamente hablando, todavía
puede utilizarlas sin causar su aplicación a romper. Sin embargo, artículos obsoletos que
no están garantizados para permanecer apoyados y buena práctica de programación
dicta que usted evitarlos a menos que exista una razón de peso para no hacerlo.
Asimismo, el uso de un elemento obsoleto resultará en una advertencia del compilador.
Dependiendo de su configuración de compilación, esto podría detener la aplicación de la
compilación.

No obstante, como se ha señalado, la  propiedad AppSettings es considerado obsoleto y


resultará en una advertencia del compilador. La forma correcta de usar AppSettings es
acceder a él a través del  objeto ConfigurationManager en lugar de a través del  objeto
ConfigurationSettings. El código siguiente muestra el método de .NET Framework 2.0 para
recuperar AppSettings:
"VB
Dim AllAppSettings como NameValueCollection = ConfigurationManager.AppSettings
Console.WriteLine(AllAppSettings("foo")
Console.WriteLine(AllAppSettings(0))

// C#
NameValueCollection AllAppSettings = ConfigurationManager.AppSettings;
Console.WriteLine(AllAppSettings["foo"]); Console.WriteLine(AllAppSettings[0]);

Para utilizar , necesita AppSettings para declarar una instancia del  objeto


NameValueCollection y establezca la  propiedad AppSettings del  objeto
ConfigurationManager. Después de hacer esto, puede acceder al valor mediante un índice o
basado en una búsqueda basada en cadena. En este ejemplo, sólo una  variable definida
AppSettings. Esta variable contiene el valor "Hello World" y una tecla de "foo". Suponiendo
que sea el único elemento en la colección,
La  variable AppSettings ocupa la colección primero (0 ª) índice. Asimismo, cada valor de
AppSettings del tipo System.String, por lo que no hay necesidad de convertir el valor a su
corres- pondiente tipo.
Hay otro matiz que necesita ser mencionado. Usted podría tener una instancia
donde usted necesita recorrer o enumerar su  valor AppSettings en contraposición a
referenciar expresamente. AppSettings clase implementa la  interfaz IEnumerable
(requiriendo el sistema.Colecciones namespace) y, debido a esto, puede enumerar
la colección de la misma manera que lo haría con cualquier otro objeto con
un enumerador. Se trata simplemente de declarar un  objeto IEnumerator y
establezca que el resultado del  método GetEnumerator de la  propiedad Keys de
su  instancia AppSettings. Desde allí, se puede caminar a través de la colección
llamando al  método MoveNext. El siguiente código ilustra la enumeración de los
valores en la  sección AppSettings:
"
V
B
Dim AllAppSettings como NameValueCollection = _
ConfigurationManager.AppSettings
Dim = AllAppSettings SettingsEnumerator como IEnumerator.Keys.GetEnumerator
Contador Dim como int32 =
0
Mientras SettingsEnumerator.MoveNext Console.WriteLine("elemento: el
valor {0}: {1}" _ AllAppSettings.Keys(Contador),
AllAppSettings(Contador))
Fin
mient
ras

/
/

C
#
NameValueCollection AllAppSettings =
ConfigurationManager.AppSettings.
Int32 contador = 0;
= AllAppSettings SettingsEnumerator IEnumerator.Keys.GetEnumerator();
Mientras (SettingsEnumerator.MoveNext())
{
Console.WriteLine("elemento: el valor {0}: {1}", AllAppSettings.Keys[contador],
AllAppSettings[Contador]);
}

Utilizando el  elemento ConnectionStrings es ligeramente más complicada, pero al


hacerlo, todavía es fácil e intuitiva. Porque muchas aplicaciones actuales implican
interactuar con una base de datos de algún modo u otro, los desarrolladores del
marco decidieron que necesitaban para proporcionar un elegante y seguro para
almacenar cadenas de conexión de base de datos. Si hard- Código de la cadena de
conexión, podrá reducir en gran medida la flexibilidad de la aplicación porque no
se necesita cambiar la cadena de conexión y redistribuir su aplicación cada vez que
cambia un nombre de servidor o hacer cualquier cambio sustantivo. Almacenar el
sentido cadena en un archivo de configuración le proporciona mucha más
flexibilidad porque simplemente puede editar el archivo y cambiar la configuración
si cambiar o agregar un servidor.
En las versiones anteriores de Framework, realmente no se hace distinción entre
una cadena de conexión y cualquier otra cadena. Una mejora importante con
respecto a la config- uración in.NET Framework 2.0 es el soporte de tipado fuerte
(aunque hay otras razones que tipado fuerte fue agregado). Esta mejora de la
capacidad de configuración incluye una disposición específica para acomodar  el
tipo de proveedor. (Consultar el  espacio de nombres System.Data  para más
información).
Precaución   consideraciones relativas a la seguridad de la cadena de conexión
Cadenas de conexión (y toda otra información confidencial) debe estar cifrado hash o
siempre que sea posible. Almacenar la cadena de conexión de base de datos en formato
de texto (legible) formulario puede presentar una enorme vulnerabilidad de seguridad y
deben evitarse si es posible.
Un error común es que si usted está utilizando un Windows confianza para autenticar su
aplicación contra una base de datos, no hay necesidad de cifrar este. Aunque esto puede
ser cierto en algunas circunstancias, no es cierto que en otros. Dependiendo de la
sofisticación del atacante, podría revelar detalles críticos aunque utilizando autenticación
de Windows minimiza cuánto un atacante puede discernir. En los casos donde no se
utiliza la autenticación de Windows, es muy peligroso para almacenar una cadena de
conexión en texto sin formato porque un atacante puede fácilmente discernir tanto un
nombre de usuario y una contraseña para acceder a la base de datos. Como tal, es muy
recomendable cifrar o hash esta información.

Más info  Cifrado y hash


Cifrado y hash son discutidos en detalle en el capítulo 12, "el usuario y la seguridad de
los datos", y esas técnicas deberían ser empleadas a menos que la seguridad no es un
problema.

En las versiones anteriores de Framework, realmente no se hace distinción entre una cadena
de conexión y cualquier otra cadena. Una mejora importante con respecto a la config-
uración en .NET Framework 2.0 es el soporte de tipado fuerte (aunque hay otras razones que
tipado fuerte fue agregado). Esta mejora de la capacidad de configuración incluye una
disposición específica para acomodar  el tipo de proveedor. (Consultar el  espacio de
nombres System.Data  para más información).
De forma predeterminada, puede acceder a la  propiedad ConnectionStrings casi
idénticamente a cómo tiene acceso AppSettings. La principal diferencia es que en lugar de
usar un  objeto de colección: NOMBRE;valor-, se utiliza un  objeto
ConnectionStringSettingsCollection en conjunción con el  objeto ConnectionStringSettings.
En primer lugar veremos cuál es la configuración del archivo de configuración tendrá que
parecerse. En aras de la exhaustividad, este archivo de configuración alberga las siguientes
bibliotecas: SqlClient, OracleClient, OleDby Odbc:
<?xml version="1.0" encoding="utf-8" ?>
<Configuración>
<connectionStrings>
<claro/>
<add name="AdventureWorksString" providerName="System.Data.SqlClient"="Datos
connectionString Source=localhost;Initial Catalog=AdventureWorks; Integrated
Security=true"/>
<add name="MarsEnabledSqlServer2005String"
providerName="System.Data.SqlClient"= "connectionString
server=Aron1;database=
Pubs;Trusted_Connection=True;MultipleActiveResultSets=true" />
<add name="OdbcConnectionString"
providerName="System.Data.Odbc"
ConnectionString=
"Driver={Microsoft Access Driver (*.mdb)};DBQ=C:\adatabase.mdb;
uid=admin;pwd=R3M3emberToUseStrongPasswords;"/>
<add name="AccessConnectionString"
providerName="System.Data.OleDb"="connectionString
Provider=Microsoft.Jet.OLEDB.4.0; Origen de
datos=\PathOrShare\mibd.mdb;
User Id=admin;Password=Rememb3rStr0ngP4sswords;" />
<add name="OracleConnectionString"
providerName="System.Data.OracleClient"
ConnectionString="Fuente de datos=MyOracleDB;Integrated Security=yes;" />
</connectionStrings>
</configuration
>

Para configurar cada uno de los anteriores (bibliotecas de datos OLEDB, ODBC,
OracleClient, SqlClient), realice los siguientes pasos:
1.  Especificar el  elemento evidente para eliminar cualquier conexión existente de
cadenas.
2.  Para cada cadena de conexión (por ejemplo, SqlClient, OracleClient, etc.),
agregar un  elemento Nombre. Este elemento permite el valor a ser
referenciado por su nombre sin que el desarrollador tenga que recordar los
índices.
3.  Especificar un  atributo providerName  para cada uno de los  elementos de
nombre  (por ejemplo,
System.Data.OracleClient).
4.  Especificar un  atributo connectionString  con la cadena de conexión
apropiada para conectarse al origen de datos.
En el ejemplo mostrado anteriormente, para cada tipo de biblioteca, he agregado
una cadena de conexión específica que le corresponda. Para la  biblioteca de
SqlClient, he añadido dos diferentes cadenas de conexión sólo para ilustrar que no
hay ningún problema asociado a hacerlo. La segunda  cadena de conexión
SqlClient difiere de la primera principalmente en que permitió a Multiple Active
Result Sets (MARS).
Ahora que tienes los valores establecidos en el archivo de configuración, a
continuación se explica cómo acceder a ellos:
"
V
B
Dim como MySettings ConnectionStringSettingsCollection _ =
ConfigurationManager.ConnectionStrings
Si no es nada entonces MySettings
Dim sb como StringBuilder nuevo
Dim individualSettings como ConnectionStringSettings
Para cada individualSettings en sb
MySettings.append("cadena de conexión completa:
"&_
IndividualSettings.ConnectionString)
Sb.append("Nombre de proveedor: " & individualSettings.ProviderName)
Sb.append("Nombre de sección: " & individualSettings.Name)
Siguiente

Console.WriteLine(sb.ToString) End
If
// C#
MySettings ConnectionStringSettingsCollection =
ConfigurationManager.ConnectionStrings;

Si (MySettings != null)
{
Sb = new StringBuilder StringBuilder();
(Foreach en individualSettings ConnectionStringSettings MySettings)
{
Sb.append("cadena de conexión completa: " +
IndividualSettings.ConnectionString + "\r\n");
Sb.append("Nombre de proveedor: " + individualSettings.ProviderName + "\r\n");
Sb.append("Nombre de sección: " + individualSettings.Name + "\r\n");
} Console.WriteLine(sb.ToString());
}

Para resumir lo que el código anterior, no crea una instancia de la   clase


StringSettingsCollection- Conexión y, a continuación, se establece que el resultado de
la configuración- Manager's  propiedad ConnectionStrings. Las tres propiedades son de
particular importancia: Nombre, ProviderName, y ConnectionString. De los tres,
el ConnectionString es probablemente la más importante porque es la que necesitas para
crear o instanciar un nuevo  objeto Connection. El siguiente código (que requiere
el System.Data, System. Data.SqlClient, System.Data.OracleClient,
System.Data.OLEDB y System.Data.Odbc ) de espacios de nombres puede ser añadido para
el  bucle foreach de los fragmentos de código anteriores para crear una conexión basada en
los valores de las propiedades:
"VB
Dim MyConnection como IDbConnection
Select Case.ProviderName individualSettings
Caso "System.Data.SqlClient"
MyConnection = New SqlConnection(individualSettings.ConnectionString) Caso "System.Data.OracleClient"
MyConnection = Nuevo(individualSettings OracleConnection.ConnectionString)
Caso "System.Data.OleDb"
MyConnection = new OleDbConnection(individualSettings.ConnectionString) Caso "System.Data.Odbc"
MyConnection = New OdbcConnection(individualSettings.ConnectionString) End Select

// C#
IDbConnection MyConnection = null;
Interruptor (individualSettings.ProviderName)
{
Caso "System.Data.SqlClient":
MyConnection = new SqlConnection(individualSettings.ConnectionString);
Break;
Caso "System.Data.OracleClient":
MyConnection = nuevo(individualSettings OracleConnection.ConnectionString);
Break;
Caso "System.Data.OleDb":
MyConnection = new OleDbConnection(individualSettings.ConnectionString);
Break;
Caso "System.Data.Odbc":
MyConnection = new OdbcConnection(individualSettings.ConnectionString);
Break;
}

Aunque el código anterior ilustra cómo recuperar los diferentes tipos de cadenas de
conexión almacenada en un archivo de configuración, típico de las aplicaciones que
no utilizan varios proveedores de bases de datos. El escenario más típico es
utilizando una sola base de datos o utilizando múltiples bases de datos del mismo
tipo, por ejemplo, múltiples bases de datos de SQL Server. Como tal, si usted sabe
lo que usted está buscando, la iteración de una colección es innecesario y
posiblemente ambigua. En consecuencia, es posible que desee recuperar
directamente la cadena de conexión para el proveedor que sabe que existe. Hay dos
formas de hacerlo. El primer enfoque consiste en utilizar el nombre de sección de la
sección Acceso a la biblioteca, como se muestra aquí:
'

V
B
Dim como MySettings ConnectionStringSettings _ =
ConfigurationManager.ConnectionStrings("AdventureWorksString")
Si no es nada entonces MySettings
Dim MyConnection como nueva SqlConnection(MySettings.ConnectionString)
Console.WriteLine(MySettings.ConnectionString)
E
n
d
I
f

/
/

C
#
MySettings ConnectionStringSettings =
ConfigurationManager.ConnectionStrings["AdventureWorksString"];

Si (MySettings != null)
{
SqlConnection cn = new SqlConnection(MySettings.ConnectionString);
Console.WriteLine(MySettings.ConnectionString);
}

El segundo método implica utilizar el índice que corresponde a la posición del


elemento en la  colección ConnectionStrings, tal y como se muestra aquí:
"
V
B
Dim como MySettings ConnectionStringSettings _ =
ConfigurationManager.ConnectionStrings(0)
Si no es nada entonces MySettings
Dim MyConnection como nueva SqlConnection(MySettings.ConnectionString)
Console.WriteLine(MySettings.ConnectionString)
E
n
d
I
f

/
/

C
#
MySettings ConnectionStringSettings =
ConfigurationManager.ConnectionStrings[0];

Si (MySettings != null)
{
SqlConnection cn = new SqlConnection(MySettings.ConnectionString);
Console.WriteLine(MySettings.ConnectionString);
}
Del mismo modo, si usted tenía varias bases de datos del mismo tipo (y de nuevo,
usaremos el ejem- plo de SQL Server), se pueden enumerar los valores por tipo y, a
continuación, cargarlos por consi- guiente. En el ejemplo siguiente, el archivo de
configuración proporciona dos cadenas de conexión a bases de datos de SQL Server,  y
AdventureWorksString MarsEnabledSqlServer2005String. Así que podemos buscar el
tipo (System.Data.SqlClient) y responder en consecuencia.
"V
B
Dim MyTypeSettings como ConnectionStringSettingsCollection _ =
ConfigurationManager.ConnectionStrings
Si no es nada entonces MyTypeSettings
Para cada typeSettings como en MyTypeSettings ConnectionStringSettings
Si typeSettings.ProviderName = "System.Data.SqlClient" Luego
Dim MyConnection como nuevo _(typeSettings
SqlConnection.ConnectionString)
Console.WriteLine("cadena de conexión: " & _
TypeSettings.ConnectionString)
End If
Siguiente
End If

// C#
MyTypeSettings ConnectionStringSettingsCollection =
ConfigurationManager.ConnectionStrings;
Si (MyTypeSettings != null)
{
(Foreach en MyTypeSettings typeSettings ConnectionStringSettings)
{
Si (typeSettings.ProviderName == "System.Data.SqlClient")
{
SqlConnection MyConnection = new
SqlConnection(typeSettings.ConnectionString);
Console.WriteLine("cadena de conexión " +
TypeSettings.ConnectionString);
}
}
}

El ejemplo anterior ilustra el uso de la  clase ConfigurationManager, que es el principal


mecanismo para recuperar y almacenar la información de configuración en Winforms o
aplicaciones de consola (o, más en general, en cualquier aplicación que no es una
aplicación Web). Aunque esta metodología trabajará en aplicaciones ASP.NET,
diferentes mecanismos pueden y deben ser utilizados para recuperar estos valores en
una aplicación Web. La principal diferencia entre uso Winforms y aplicaciones Web es
que las aplicaciones Web deben emplear WebConfigurationManager para administrar
la información de configuración en contraposición a ConfigurationManager. Aunque
esta afirmación es un poco de un oversimplifi- cación, es seguro decir que la  clase
WebConfigurationManager es idéntico para todos los fines y propósitos de la  clase
ConfigurationManager con tres excepciones:
■   La  clase WebConfigurationManager GetWebApplicationSection tiene un método
que la  clase ConfigurationManager no tiene. Este método permitirá recuperar
todo un

También podría gustarte