Ed. Garceta Programación Isabel Jimenez

También podría gustarte

Descargar como pdf o txt
Descargar como pdf o txt
Está en la página 1de 504

Técnico Superior en Desarrollo de Aplicaciones Multiplataforma y Web

DAM y DAW

10
:Ü>

i
Técnico Superior en Desarrollo de Aplicaciones Multiplataforma y Web
DAM y DAW

10
:Ü>

i
Programación
M

94

:! i

Sh

m
i'ii
Técnico Superior en Desarrollo de Aplicaciones Muitipiataforma y Web f.'
DAM y DAW
Programación

Isabel Jiménez Cumbreras

ISBN; 978-84-1545-259-1
IBERGARCETA PUBUCACIONES, S.L., Madrid 2013

Edición; 1."
Impresión: 1.®
N.° de páginas: 500
Formato: 20 x 26 cm

Reservados ios derechos para todos los países de lengua española. De conformidad con io dispuesto en el artículo 270 y siguientes del
código penal vigente, podrán ser castigados con penas de multa y privación de libertad quienes reprodujeren o plagiaren, en todo o en
parte, una obra literaria, artística o científica fijada en cualquier tipo de soporte sin la preceptiva autorización. Ninguna parte de esta
publicación, incluido ei diseño de la cubierta, puede ser reproducida, almacenada o trasmitida de ninguna forma, ni por ningún medio
sea éste electrónico, químico, mecánico, electro-óptico, grabación, fotocopia o cualquier otro, sin ia previa autorización escrita por parte
de ia editorial.

obra^^^ ^ CEDRO (Centro Español de Derechos Reprográficos), www.cedro.org, si necesita fotocopiar o escanear algún fragmento de esta

COPYRIGHT © 2013 IBERGARCETA PUBLICACIONES, S.L.

Programación

© Isabel Ma Jiménez Cumbreras

1.' edición, 1.= impresión


OI: 177/2013
ISBN: 978-84-1545-259-1
Deposito Legal: M-21456-2013
Imagen de cubierta: artjazz © fotoiia.com

Impresión:
PRINT HOUSE, marca registrada de Copiar, S. A.

IMPRESO EN ESPAÑA - PRINTED IN SPAIN

Nota sobre enlaces a páginas web ajenas: Este libro puede incluir referencias a sitios web gestionados por terceros y ajenos a IBERGAR
CETA PUBLICACIONES, S.L., que se incluyen sólo con finalidad informativa. IBERGARCETA PUBLICACIONES, S.L., no asume ningún tipo
de responsabilidad por ios daños y perjuicios derivados del uso de los datos personales que pueda hacer un tercero encargado del man
tenimiento de las páginas web ajenas a IBERGARCETA PUBLICACIONES, S.L., y del funcionamiento, accesibilidad y mantenimiento de
los sitios web no gestionados por IBERGARCETA PUBUCACIONES, S.L., directamente. Las referencias se proporcionan en ei estado en
que se encuentran en el momento de publicación sin garantías, expresas o implícitas, sobre ia información que se proporcione en ellas.
INDICE GENERAL

CAPITULO 1. INTRODUCCION

1.1. Introducción 2
1.2. Conceptos básicos 2
1.2.1. Algoritmo vs programa 2
1.2.2. Características de un programa 4
1.2.3. Lenguaje de programación 5
1.2.3.1. Clasificación en función de la cercanía a la máquina 6
1.2.3.2. Clasificación en función del propósito del lenguaje 7
1.2.3.3. Clasificación en función de la evolución histórica 7
1.2.3.4. Clasificación en función de la forma de ejecución 8
1.2.3.5. Clasificación en función de cómo afrontan las tareas a realizar 8
1.2.3.6. Clasificaeión en función del estilo de programación empleado 9
1.2.3.7. Clasificación en función de la capacidad de generar procesos concurrentes
o no 9
1.2.3.8. Clasificación en función de la interactividad 10
1.2.3.9. Clasificación en funeión de si son o no visuales 10
1.2.4. Código 10
1.2.5. Palabras reservadas del lenguaje 11
1.3. Diseño de un programa 11
1.3.1. Fase de análisis del problema 11
1.3.2. Fase de diseño del algoritmo 12
1.3.3. Fase de programación (codificación) 13
1.3.4. Fase de pruebas y depuración 14
1.3.5. Fase de documentación 15
1.3.6. Ejemplo de creación de un programa 15
1.4. Diagramas de flujo 20
1.4.1. Reglas para diseñar diagramas de flujo 20
1.5. Pseudocódigo 22
1.6. Proceso de compilación 22
1.6.1. Nuevas tendencias en el proceso de creación de código objeto 25
1.7. Entornos de desarrollo integrado de software o IDE 26
1.8. Microsoft Visual Studio 2012 Express 26
1.8.1. Registro de Visual Studio 2012 Express para escritorio 28
1.8.2. Página principal de Visual Studio 2012 Express 30
1.8.3. Crear un nuevo proyecto en Visual Studio 2012 Express 31
1.8.4. Estructura de un proyecto en Visual Studio 2012 Express 32
1.8.5. Guardar y abrir un proyecto existente en Visual Studio 2012 Express 33
1.8.6. Ejecutar un proyecto compilado sin errores 34
1.9. Entorno de desarrollo Netbeans 34
1.9.1. Instalación de Netbeans 7.3 + JDK 35
1.9.2. Pantalla inicial de Netbeans 7.3 36
1.9.3. Crear un nuevo proyecto en Netbeans 7.3 37
1.9.4. Estructura de un proyecto en Netbeans 7.3 39
1.9.5. Guardar y abrir un fichero existente Java Netbeans 7.3 39
1.9.6. Ejecutar un proyecto compilado sin errores en Netbeans 7.3 39
COMPRUEBA TU APRENDIZAJE 40
ACTIVIDADES DE AMPLIACIÓN 40

CAPITULO 2. METODOLOGÍA DE LA PROGRAMACIÓN


2.1. Introducción 44
2.2. Pseudocódigo 44
2.2.1. Elementos a tener en cuenta en el pseudocódigo 44
2.2.1.1. El entorno 45
2.2.1.2. Sentencias de asignación 46
2.2.1.3. Sentencias de entrada y salida de datos 48
2.2.1.4. Sentencias de control: alternativas 50
2.2.1.5. Sentencias alternativas concatenadas 62
2.2.1.6. Sentencias control: bucles 66
2.3. Teorema de la programación estructurada 88
COMPRUEBA TU APRENDIZAJE 89
ACTIVIDADES DE AMPLIACIÓN 89

CAPÍTULO 3.INICIACIÓN AL LENGUAJE C#


3.1. Introducción 92
3.2. Características de C# 92
3.3. El lenguaje de programación C# 93
3.3.1. Estructura de un programa en C# 93
3.3.2. Palabras reservadas en C# 96
3.3.3. Identificadores en C# 96
3.3.4. Comentarios en C# 97
3.3.5. Tipos de datos en C# 98
3.3.5.1. Tipos de datos integrados en C# 99
3.3.5.2. Literales 99
3.3.6. Declaración de variables en C# 102
3.3.7. Constantes en C# 104
3.3.8. Conversiones de tipos de datos en C# 105
3.3.9. Operadores en C# 106
3.3.9.1. Operadores aritméticos 106
3.3.9.2. Operadores relaciónales 108
3.3.9.3. Operadores lógicos 109
3.3.9.4. Operadores de asignación 110
3.3.9.5. Operador sizeof 11 1
3.3.9.6. Operadores a nivel de bits 112
3.3.9.7. Prioridad de los operadores 113
3.3.10. Operaciones de entrada/salida de datos en C# 114
3.3.10.1. WriteLine y write 115
3.3.10.2. ReadLine y read 116
3.3.11. Sentencias alternativas en C# 117
3.3.11.1. Sentencia alternativa simple 117
3.3.11.2. Sentencia alternativa doble 118
3.3.11.3. Sentencia alternativa múltiple 118
3.3.12. Visibilidad de los identificadores en C# 121
3.3.13. Sentencias repetitivas en C# 123
3.3.13.1. Bucle while en C# 123
3.3.13.2. Bucle do while en C# 124
3.3.13.3. Bucle for en C# 125
3.3.13.4. Sentencias de salto: break, continué y retum C# 127
3.3.13.5. Instrucciones de salto: goto case valor y goto default en sentencias
alternativas múltiples (switch) 129
3.4. Tipos de errores de compilación 129
3.5. Codificación de los algoritmos resueltos en el capítulo 2 131
3.5.1. Codificación del algoritmo resuelto 1 131
3.5.2. Codificación del algoritmo resuelto 2 132
3.5.3. Codificación del algoritmo resuelto 3 132
3.5.4. Codificación del algoritmo resuelto 4 133
3.5.5. Codificación del algoritmo resuelto 5 134
3.5.6. Codificación del algoritmo resuelto 6 135
COMPRUEBA TU APRENDIZAJE 136
ACTIVIDADES DE AMPLIACIÓN 136

CAPITULO 4. FUNCIONES

4.1. Introducción 140


4.2. Abstracción 140
4.3. Subprogramas 141
4.4. Funciones en 144
4.5. Llamada a una función en C# 145
4.6. Parámetros de una función 1^4
4.6.1. Paso de parámetros por valor en C# 156
4.6.2. Paso de parámetros por referencia en C# 156
4.7. Framework.net 1^^
4.7.1. Funciones de entrada/salida 158
4.7.2. Funciones numéricas 1^^
4.7.3. Funciones de manipulación de cadenas 160
4.7.4. Funciones de fecha y hora 160
4.7.5. Funciones de carácter 160
4.7.6. Funciones aleatorias 161
4.8. Recursividad 161
4.9. Sobrecarga de funciones 164
COMPRUEBA TU APRENDIZAJE 166
ACTIVIDADES DE AMPLIACIÓN 166

CAPÍTULO 5. TIPOS DE DATOS COMPUESTOS


5.1. Introducción 1^0
5.2. Arrays 170
5.2.1. Declaración y creación de un array en C# 171
5.2.2. Acceso a los datos de un array en C# 172
5.2.3. Inicialización de un array en C# 173
5.2.4. Indices de un array en C# 174
5.2.5. Arrays de caracteres en C# 174
5.2.6. Arrays como parámetros en C# 175
5.2.7. Ejemplo de uso de arrays en C# 176
5.2.8. Bucle foreach para la manipulación de arrays en C# 180
5.2.9. Operaciones comunes con arrays en C# 182
5.2.9.1. Algoritmos de búsqueda 187
5.2.9.2. Algoritmos de ordenación 192
5.2.10. Arrays multidimensionales en C# 201
5.2.10.1. Declaración y creación de arrays multidimensionales en C# 202
vi" Programación

5.2.10.2. Inicialización de arrays multidimensionales en C# 202


5.2.10.3. Ejemplo de uso de matrices en C# 204
5.2.10.4. Arrays irregulares o escalonados en C# 208
5.3. Cadenas de caracteres 21 1
5.3.1. Objeto StringBuilder en C# 216
5.4. Estmcturas 21 7
5.5. Enumeraciones 224
5.5.1. Definición de una enumeración en C# 225
5.5.2. Uso de enumeraciones en C# 226
COMPRUEBA TU APRENDIZAJE 227
ACTIVIDADES DE AMPLIACIÓN 228

CAPÍTULO 6.PROGRAMACIÓN ORIENTADA A OBJETOS


6.1. Programación y abstracción 234
6.1.1. Tipos abstractos de datos 236
6.2. Programación orientada objetos 238
6.2.1. Abstracción 239
6.2.2. Encapsulamiento 239
6.2.3. Medularidad 240
6.2.4. Jerarquía 241
6.2.5. Polimorfismo 242
6.2.6. Realización de programas orientados a objetos 242
6.3. Clases y objetos 243
6.4. Definición de clases en C# 245
6.4.1. Atributos en C# ' 246
6.4.2. Métodos en C# 246
6.4.3. Instancias en C# 248
6.4.4. Constmctores en C# 249
,, ^ los miembros de una clase en C# 250
jemplo de desarrollo de un proyecto con clases en C# 250
6.5.1. Configuración del proyecto en C# 250
6.5.2. Array de objetos en C# 253
6.6. Función Main en C# 256
6.7. Palabra reservada this Z] ZZl'. 258
6.8. Destructores en C# ZZZZZl ZZZZZZZZZZZZZZ 260
6.9. Variables estáticas ZZZZZZl 262
, l^^^'looes miembros de clase estáticos 264
6.10. Propiedades en C#..... 265
6.11. Indizadores en C# " 267
6.12. Sobrecarga de operadores en C# 270
COMPRUEBA TU APRENDIZAJE 272
ACTIVIDADES DE AMPLIACIÓN 273

CAPÍTULO 7. LENGUAJE DE PROGRAMACIÓN JAVA


7.1. Introducción 278
7.2. Lenguaje de programación Java 278
7.2.1. Características de Java2se 7 278
7.3. Estmctura de un programa Java 279
7.3.1. Palabras reservadas en Java 282
7.3.2. Identificadores en Java 283
7.3.3. Comentarios en Java 283
7.3.4. Tipos de datos en Java 285
7.3.5. Literales en Java 286
7.3.6. Declaración de variables en Java 287
7.3.7. Constantes en Java 287
7.3.8. Conversiones de tipos de datos en Java 288
7.3.9. Operadores en Java 289
7.3.9.1. Operadores aritméticos 289
7.3.9.2. Operadores relaciónales 290
7.3.9.3. Operadores lógicos 290
7.3.9.4. Operadores de asignación 291
7.3.9.5. Operadores instanceof 291
7.3.9.6. Operadores a nivel de bits 292
3.3.9.7. Prioridad de los operadores 293
7.3.10. Operaciones de entrada/salida de datos en Java 293
7.3.10.1. Métodos para la visualización de información a través de la
salida estándar 294
7.3.10.2. Métodos para la introducción de infonnación a través de la
entrada estándar 295
7.3.11. Sentencias alternativas en Java 297
7.3.12. Sentencias repetitivas en Java 298
7.4. Funciones y procedimientos en Java 299
7.4. 1. Parámetros 300
7.4.2. Sobrecarga de funciones 302
7.5. AiTays 302
7.5.1. Matrices 303
7.5.2. Ejemplo práctico de uso de arrays. El juego del ahorcado 304
7.6. Objetos String 307
7.7. Definición de clases y objetos en Java 309
7.7.1. Desarrollo de proyectos con varias clases en Java 312
COMPRUEBA TU APRENDIZAJE 313
ACTIVIDADES DE AMPLIACIÓN 314

CAPÍTULO 8. ENTORNO GRÁFICO


8.1. Introducción 318
8.2. Programación dependiente de eventos 318
8.3. Conceptos básicos 319
8.4. Creación de proyectos gráficos 320
8.4.1. Proyectos gráficos en Visual Studio 320
8.4.1.1. Agregar nuevos formularios 321
8.4.1.2. Entomo de trabajo 322
8.4.1.3. Controles 324
8.4.1.4. Desarrollo de una pequeña aplicación de ejemplo 325
8.4.1.5. Eventos y propiedades más comunes 329
8.4.1.6. Contenedores 335
8.4.1.7. Menús 337
8.4.1.8. Proyectos con múltiples formularios 342
8.4.2. Proyectos gráficos en Netbeans 351
8.4.2.1. Proyectos gráficos en Netbeans 351
8.4.2.2. Entomo de trabajo 351
8.4.2.3. Controles 353
8.4.2.4. Eventos y propiedades más comunes en controles Java 353
8.4.2.5. Ejemplo sencillo de aplicación Java 356
8.4.2.6. Contenedores en Java 359
8.4.2.7. Menús en Java 360
Programación

COMPRUEBA TU APRENDIZAJE 365


ACTIVIDADES DE AMPLIACIÓN 366

CAPITULO 9. HERENCIA,POLIMORFISMO E INTERFACES

9.1. Introducción 370


9.2. Espacios de nombres o paquetes 370
9.2.1. Declaración de espacios de nombres 370
9.2.2. Paquetes 372
9.3. Herencia y polimorfismo 372
9.3.1. Herencia 373
9.3.1.1. Constmctores en clases derivadas 375
9.3.1.2. Redefinición de métodos 378
9.3.1.3. Operadores is, as e instanceof 380
9.3.2. Polimorfismo 381
9.4. Clases abstractas 384
9.5. Interfaces 386
COMPRUEBA TU APRENDIZAJE 390
ACTIVIDADES DE AMPLIACIÓN 390

CAPÍTULO 10. FLUJOS DE ENTRADA/SALIDA Y CONTROL DE EXCEPCIONES


10.1. Introducción 394
10.2. Ficheros y directorios 394
10.2.1. Clasificación de los ficheros según su organización 395
10.2.1.1. Ficheros secuenciales 395
10.2.1.2. Ficheros aleatorios 396
10.2.1.3. Ficheros secuenciales indexados 396
10.3. Operaciones con ficheros 397
10.3.1. Apertura de ficheros 398
10.3.2. Operaciones de lectura/escritura en ficheros 398
10.4. Flujos de entrada/salida de datos 399
10.4.1. Clases que refieren flujos en C# 401
10.4.1.1. Clase Stream 402
10.4.1.2. Clase FileStream 402
10-4.1.3. Uso de flujos intermedios 405
10.5. Proceso de señalización en C# 407
10.6. Flujos de datos en Java 409
10.7. Proceso de señalización en Java 414
10.8. Otros flujos de datos en Java 415
10.9. Cuadros de diálogo en entorno gráfico para acceso a ficheros 416
10.10. Excepciones 419
10.10.1. Mecanismos de manejo de excepciones 420
10.10.2. Bloque try...catch 420
10.10.3. Jerarquía de errores 424
10.10.4. Creando nuestras propias excepciones 425
10.10.4.1. Creación de clases de error en C# 425
10.10.4.2. Creación de clases de error en Java 426
10.10.4.3. Lanzando excepciones 427
10.10.5. Consideraciones finales 428
COMPRUEBA TU APRENDIZAJE 429
ACTIVIDADES DE AMPLIACIÓN 429
CAPITULO 11. GESTION DE BASES DE DATOS. BASES DE DATOS ORIENTADAS
A OBJETOS

1 1 .1. Bases de datos relaciónales 432


11.2. Creación de una aplicación de gestión de bases de datos sencilla en C# 433
11.3. Elementos de una base de datos en C# 436
11.4. Crear una aplicación de base de datos en C# paso a paso 437
1 1.5. Uso de DataGrid para la visualización de datos de base de datos en C# 441
11.6. Bases de datos orientadas a objetos(BDOO) 444
11.6.1. Características de las bases de datos orientadas a objetos 444
11.6.2. Bases de datos orientadas a objetos en C# 445
11.6.2.1. Creación del modelo de entidades en C# 446
11.6.2.2. Obtención del script para la creaeión de la base de datos en
SQL SERVER EXPRESS 448
11.6.2.3. Ejecución del script para la creación de la base de datos 449
11.6.2.4. Uso de la base de datos 450
11.6.3. Gestor de base de datos orientado a objetos para Java 452
11.6.3.1. Configurar el plugin db4o en Netbeans y agregar librería db4o
a nuestro proyecto 452
11.6.3.2. Creación y configuración de un nuevo proyecto de base de datos 453
11.6.3.3. Configuración de las clases de nuestra base de datos 453
11.6.3.4. Configuración de de variable de acceso a la base de datos 455
11.6.3.5. Insertar datos en la base de datos tienda 456
11.6.3.6. Visualizar el contenido de la base de datos 458
11.6.3.7. Eliminación del contenido de la base de datos 460
COMPRUEBA TU APRENDIZAJE 461
ACTIVIDADES DE AMPLIACIÓN 461

CAPITULO 12. COLECCIONES

12.1. Colecciones 464


12.2. Colecciones en C# 464
12.2.1. Array 464
12.2.2. ArrayList y List 467
12.2.2.1. ArrayList 467
12.2.2.2. List 473
12.2.3. Hashtable 474
12.2.4. Dictionary 477
12.2.5. SortedList y SortedDictionary 477
12.2.6. Queue 478
12.2.7. Stack 478
12.3. Coleeciones en Java 479
12.3.1. Arraylist 479
12.3.2. Vector 481
COMPRUEBA TU APRENDIZAJE 481
ACTIVIDADES DE AMPLIACIÓN 482
PROLOGO

Este libro está diseñado para ser el libro de texto del módulo profesional Programación que
se imparte en los ciclos fonnativos de grado superior Desarrollo de Aplicaciones
Multiplatafomia y Desarrollo de Aplicaciones Web. Incluye los contenidos mínimos que
indican los RD 450/2010, de 16 de abril y 686/2010, de 20 de mayo, ampliando estos a los
contenidos mínimos expresados en los Decretos y Ordenes de algunas comunidades autónomas.
Es un libro enfocado a su uso diario en clase, con una secuencia de contenidos lógica y
personalizada en función de mi propia experiencia profesional. Cada capítulo está estructurado
de forma que veremos:
■ Contenido teórico.

■ Actividades a realizar durante el aprendizaje.


■ Preguntas para evaluar si los conocimientos han sido adquiridos.
■ Actividades de ampliación para reforzar los conceptos vistos.
El módulo profesional Progi-amación, impartido en primer año académico, está enfocado de
tal modo que sienta las bases del desarrollo de aplicaciones inforaiáticas. Se pretende con él
enseñar al alumno los elementos fundamentales de un programa asi como las pautas a seguir a la
hora de desarrollar software de tal modo que con los conocimientos adquiridos sea capaz de
afrontar el desairollo de cualquier programa asi como otros módulos profesionales tales como
Lenguajes de Marcas y Sistemas de Gestión de Infonnación, Programación Multimedia y
Dispositivos Móviles o Programación de Servicios y Procesos.
Estudiaremos:

■ Conceptos básicos relacionados con el software, como qué se entiende por programa o
algoritmo y qué diferencia existe entre programa y proceso, lenguaje de programación,
clasificación de los lenguajes de programación, compilación del software o entornos de
desarrollo preparados para la creación de software. Centraremos nuestra atención en
lenguajes como C# y Java y los IDE Visual Studio y Netbeans.
■ Metodología de la programación. Antes de comenzar a programar en C# o Java se
realizarán pequeños algoritmos al mismo tiempo que se estudian los componentes
principales de un programa estructurado. Creación de diagramas de flujo y
pseudocódigos.
■ Una vez se conocen los elementos que componen un programa estructurado se pasarán
a codificar estos en C# y Java. Variables, identificadores, tipos de datos simples y
compuestos, sentencias de control alternativas o bucles, etc., consiguiendo construir
aplicaciones funcionales.
■ Programación orientada a objetos. Los conceptos principales que confonnan este
paradigma de la programación. Clases, objetos, herencia, polimorfismo, interfaz, etc.
xiv Programación

■ Desarrollo de aplicaciones de consola y aplicaciones gráficas. Se profundizará en los


elementos que forman una aplicación gráfica, así como en la programación orientada a
eventos.
■ Flujos de datos. Programación de aplicaciones que gestionan el flujo de infomiación
entre diferentes tipos de dispositivos, sobre todo entre el software y un dispositivo de
almacenamiento elegido.
■ Gestión de errores tanto en la compilación como en tiempo de ejecución (excepciones).
■ Desarrollo de aplicaciones de gestión de bases de datos relaciónales o bases de datos
orientadas a objetos(BDOO)en C# y Java.
■ Colecciones típicas de C# y Java como ArrayList, List, etc.

Para fmalizar, quisiera hacer hincapié en que éste documento refleja claramente la parte de
contenidos y secuencia de contenidos de la programación didáctica que realizaría para el
módulo profesional Programación. Se ha realizado con mucho esmero, teniendo en cuenta los
Resultados de Aprendizaje y Criterios de Evaluación establecidos en los Reales Decretos
indicados al comienzo del texto.
CAPÍTULO 1

INTRODUCCIÓN

j CONTENIDOS / OBJETIVOS
Algoritmo vs Programa. Saber qué es un algoritmo,
Características de un progi^ama. diferenciación con el ténuino
programa. Características.
Lenguajes de programación.
¿Qué son? Tipos Saber qué es un lenguaje de
programación y tipos que existen.
Código. Fases de desarrollo del
software. Proceso de compilación Conocer las fases por las que pasa
Diagramas de flujo y
un programa antes de salir al
mercado y ser usado. Entender el
pseudocódigo
proceso de compilación.
Entornos de desarrollo
Iniciar el uso de IDEs, Visual
integrados (IDE). Visual Studio y
V Studio y Netbeans.
\ Netbeans.

RESUMEN DEL CAPÍTULO


Este primer capítulo pretende ser una pequeña introducción sobre conceptos básicos
relacionados con la programación de aplicaciones informáticas. Se introducirá el uso de
diagramas de flujo y pseudocódigos que serán de bastante ayuda en el siguiente capítulo
cuando se programen los primeros algoritmos. Además, se instalarán los DDEs que se van a
usar durante todo el libro: Visual Studio y Netbeans.
1.1. INTRODUCCION

Hoy día, todos estamos acostumbrados a usar un sistema informático y las diferentes
aplicaciones instaladas en él, los procesadores de texto, hojas de cálculo, vemos el correo o
ficheros gráficos, etc., todos tenemos ordenador en casa o acceso a algún PC.
Cuando se comienza a estudiar informática se inicia estudiando qué es un sistema
informático. Un sistema informático es un conjunto formado por tres partes: hardware, sofirware
y el hombre.
■ HARDWARE. Es la parte tangible del sistema, parte física. Se denomina hardware a
todos los componentes físicos que forman el sistema informático, todas aquellas
piezas que forman "la torre" del ordenador junto a la pantalla, impresora, y demás
elementos que aún no incluyéndose en "la torre" interactúan de algún modo con ella.
■ SOFTWARE. Es la parte no tangible. La forman todas aquellas aplicaciones o
programas instalados en el equipo. El software se clasifica en software base y
software de aplicación. El primero de ellos referencia al sistema operativo, necesario
para que podamos usar el PC, sin él el equipo no sería más que un conjunto de
componentes anclados a la placa base. El software de aplicación hace referencia a
todas aquellas aplicaciones diseñadas para realizar una operación concreta y que
pueden ser usadas sobre un software de aplicación concreto.
■ EL HOMBRE. Es una parte importante del sistema ya que es quien usa el mismo.
Sin la iteración del hombre de nada serviría que tuviéramos un hardware y un
software.

En este libro vamos a dedicamos al estudio del software, concretamente al desarrollo del
mismo. Se estudiarán conceptos básicos sobre desarrollo de aplicaciones, entornos de desarrollo
y lenguajes de programación como C# y Java.

NOTA: Si el lector desea profundizar en los aspectos hardware del sistema se


recomienda el libro "Fundamentos Hardware", en él se estudian todas las partes físicas, el
proceso de puesta en marcha de un equipo, problemas en el arranque y fonnas de
solucionar estos fallos mediante el uso de herramientas software. En caso de que desee
estudiar más profundamente el conjunto se recomienda el libro "Sistemas Informáticos"
donde además de conocer el hardware se analiza el software base, sistemas operativos
Windows y Linux (concretamente Ubutnu) y software de aplicación de interés como
antivirus, cortafuegos, etc.

1.2. CONCEPTOS BASICOS

1.2.1. ALGORITMO VS PROGRAMA

Antes de comenzar a desarrollar software es necesario que el lector tenga claro una serie de
conceptos básicos. Entre estos conceptos el término inicial a describir seria Algoritmo. Según la
RAE (http://www.rae.es) un Algoritmo es un conjunto ordenado y finito de operaciones que
permite hallar la solución de un problema. Imaginemos que queremos realizar un viaje a
Capítulo 1. Introducción 3

Londres y necesitamos comprar los billetes de avión, esta situación sería el problema que
debemos resolver. Para comprar' los billetes debemos:
1. Localizar las direcciones de varias agencias de viajes.
2. Usar algún medio de transporte e ir a la primera de las agencias en horario comercial.
3. Una vez en la agencia plantear vuestras necesidades al agente.
4. Atender a las explicaciones que os aporta el empleado de la agencia, horarios,
precios, etc.
5. Volver al paso 2 hasta que se hayan visitado todas las agencias.
6. Escoger entre todas las ofertas aquella que más nos interesa.
7. Volver a la agencia que tiene la mejor oferta o bien realizar una llamada telefónica (si
es posible).
8. Concretar forma de pago.
Para solucionar nuestro problema (comprar los billetes para realizar un viaje a Londres) se
deben seguir una serie de pasos u operaciones. Este conjunto de operaciones es finito y
naturalmente ordenado, la secuencia debe ser tal y como se indica, por ejemplo no podemos
concretar formas de pago si antes no se ha ido a una agencia. Asi, acabamos de desarrollar un
algoritmo, de hecho de fonna cotidiana estamos continuamente generando algoritmos ya que
continuamente se plantean problemas que debemos solucionar.
Ahora bien, cuando este problema debe ser resuelto en un ordenador, el algoritmo se
convierte en lo que se denomina programa. Seguimos usando una secuencia ordenada y finita de
pasos pero escrita en un lenguaje que la máquina es capaz de entender.
La unidad mínima de almacenamiento de un ordenador se denomina bit. Un bit puede
adquirir los valores 1 o O, ya que el sistema de numeración usado es el sistema binario. Un
conjunto de bits representan tanto los números como las letras que posterionnente veremos en
nuestra pantalla. Al conjunto de 8 bits se le denomina byte. Debido a que cada vez es mayor la
infonnación que se almacena en un equipo no usamos simplemente bytes para definir esta,
debemos utilizar los prefijos Kilo, Mega, Giga, etc., para representarla, de forma que:
1 byte
1 KB (Kilobyte) r"= 1024 bytes
1 MB (Megabyte) 2-°= 1024 KB
1 GB (Gigabyte) 2^°= 1024 MB
1 TB (Terabj^e) 2^°= 1024 GB
1 PB (Petabyte) 2^°= 1024 TB
1 EB (Exabyte) 2®°= 1024 PB
1 ZB (Zettabyte) 2™= 1024 EB
1 YB (Yottabyte) 2®°= 1024 ZB

Supongamos que realizamos la compra de forma tradicional dirigiéndonos a una agencia de viajes.
1.2.2. CARACTERISTICAS DE UN PROGRAMA

Un algoritmo, posteriormente convertido en programa, debe cumplir una serie de


características:

■ Debe ser finito. Estar formado por un conjunto finito de líneas de fomia que en algún
punto ve alcanzado su fin.
■ Debe ser legible. Es importante crear códigos "limpios" y fáciles de leer, con
tabulaciones y espacios que diferencien las partes del programa. Si desarrollamos
códigos bien estmcturados nos será más fácil la corrección de errores y modificación
del mismo.

Busing Systemj
using Systera.Collections.Generic;
using System.Linq;
using Systein.Text;

Bnaraespace HolaHundo{class PrograiTi{


É static void Hain(string[] args){Console.WriteLine("Hola frundo");|consoie. ReadLine();
}
}
.}

Figura 1.1. Código fuente con lineas de código mal estructuradas. Poco legible.
Busing System;
using System.Collections.Generic;
using System.Linq;
using Systera.Text;

Bnamespace HolaMundo{
B class Prograir{
B static void Hain(string[] args){
Consolé.WriteLine("Hola mundo");
Consolé.ReadLine();
}

Figura 1.2. Código fuente con iíneas de código bien estructuradas. Fácil de entender, detectar errores y
modificar.

■ Debe ser fácilmente modlficable, es decir, debe estar diseñado de fonna que debe ser
sencillo el proceso de actualización o modificación del mismo ante las nuevas
necesidades del usuario final.

■ Debe ser eficiente. El usuario acabará usando programas que solucionen sus
problemas de la mejor y más rápida forma posible, así debemos crear programas que
ocupen poco espacio en memoria y se ejecuten rápidamente.
■ Deben ser modulares. Esta característica ayuda a que el programa sea más legible y
fácil de entender. Debemos perseguir realizar algoritmos que se encuentren divididos
en subalgoritmos de forma que se disponga de un grupo principal desde el que
llamaremos al resto. Al usar subprogramas además incitamos la reutilización de
código y evitamos la repetición de este.
■ Deber estar estructurado. Esta característica engloba de alguna fonna las anteriores,
ya que un programa estructurado será fácil de leer, de modificar y estará compuesto
de subprogramas que permitirán la reutilización de código.

NOTA: A la hora de realizar las actividades que encontrará en los capítulos que
fonnan el libro se aconseja al lector que sus diferentes códigos fuente posean las
características mencionadas. Para ello:

■ Se comenzará cada subprograma un nivel hacia dentro con respeto al


programa en el que se incluye (pulsaremos la tecla TAB para empezar a
escribir una serie de caracteres.

■ Se unificará la nomenclatura utilizada a la hora de definir los elementos del


programa creados por el propio usuario, por ejemplo, los nombre de los
elementos seguirán unas normas a la hora de ser definidos.
■ Se comentará el código para tener constancia de qué función realiza cada
subprograma.
■ Siempre que sea posible dividiremos el código en partes.

1.2.3. LENGUAJE DE PROGRAMACION

Habitualmente el ser humano se comunica a través del uso del lenguaje, es decir, es capaz de
generar una serie de sonidos que fonnan lo que conocemos como fonemas, pasadas al papel son
las letras de nuestro alfabeto, qué combinadas constituyen las sílabas, palabras y frases. A este
lenguaje se le conoce como lenguaje natural.
Lenguaje natural = comunicación hombre •(-> hombre.
Un ordenador comunica sus componentes hardware mediante la emisión de señales eléctricas,
pulsos que vienen a fonnar los valores O (no se aplica voltaje) y 1 (si se aplica voltaje). Así, la
CPU da una orden a la memoria mediante una combinación de ceros y unos, algo similar a
1101001. A esta fonna de comunicación se le denomina lenguaje máquina.
Lenguaje máquina = comunicación máquina^ máquina.
Un lenguaje de programación es similar al lenguaje humano o lenguaje máquina, la diferencia
estriba en que la comunicación se realiza entre hombre y ordenador, es decir, im lenguaje de
programación es aquel por el que el PC es capaz de entender qué esta diciéndole el ser humano
que haga.
Lenguaje de programación = comunicación hombre <-> ordenador.
A la hora de combinar los diferentes sonidos en el lenguaje natural seguimos una serie de
reglas. Una frase está fonnada por sujeto + verbo + predicado, a su vez, cada verbo está
representado en una forma verbal concreta, sabemos que se ha finalizado una frase porque se
realiza una pausa antes de comenzar con la siguiente y existe un número limitado de letius y
palabras. En un lenguaje de programación pasa exactamente igual:

' Componente hardware con componente hardware.


■ Existe un número finito de palabras reservadas que podemos usar.
■ Las fi-ases o instrucciones poseen una secuencia que viene detenninada por el
lenguaje de programación que estemos usando.
■ Cada fi'ase finaliza de una forma concreta.

Los lenguajes de programación se pueden clasificar teniendo en cuenta diversos criterios;


■ Según la cercanía a la máquina.
■ Según la función o propósito del lenguaje.
■ Según la evolución histórica.
■ Según la manera en la que se ejecutan.
■ Según cómo afrontan las tareas a realizar.
■ Según el estilo de programación empleado.
■ Según la capacidad de generar procesos concurrentes o no.
■ Según la mteractividad.
■ Según si son o no visuales

1.2.3.1. CLASIFICACIÓN EN FUNCIÓN DE LA CERCANÍA A LA MÁQUINA


Unos párrafos más arriba estudiábamos que un ordenador solo entiende de O y 1 y qué
entendemos por lenguaje máquina. Existen lenguajes de programación bastante cercanos al
lenguaje máquina, que usan instrucciones similares a las que entiende la CPU de fonna directa;
mientras que otros están formados por palabras reservadas más cercanas a las que usamos
normalmente en el lenguaje natural.
Así, en función de si el lenguaje de programación está más cerca del lenguaje máquina o del
lenguaje natural encontraremos:
■ Lenguajes de bajo nivel. Es el lenguaje de programación más cercano al lenguaje
máquina. Está constituido por palabras reservadas difíciles de recordar y la sintaxis de
las instmcciones es bastante compleja ya que se suele trabajar a nivel de registro.
Ejemplo de este tipo es el lenguaje ensamblador.
■ Lenguajes de nivel medio. Son lenguajes que se acercan un poco más al lenguaje
natural introduciendo palabras reservadas similares a las que usamos habitualmente
como if, int o wbile pero que siguen proporcionando órdenes para trabajar a nivel de
bit. Un ejemplo claro de este tipo es el lenguaje de programación C.
■ Lenguajes de alto nivel. Lenguajes parecidos al lenguaje humano tal que su forma
de proceder, su sintaxis, etc., es más similar al lenguaje natural. Abstrae al
programador^ del funcionamiento interno de la máquina.

Persona encargada de escribir en un lenguaje de programación concreto un programa que posee cierta
finalidad.
Capítulo 1. Introducción 7

1.2.3.2. CLASIFICACION EN FUNCION DEL PROPOSITO DEL LENGUAJE

Podemos clasificar un lenguaje de programación en función del tipo de programas que son
capaces de generar;
■ Lenguajes de propósito general. Son lenguajes diseñados para realizar cualquier
tipo de programa, desde software base a software de aplicación. Un lenguaje de este
tipo es C.
■ Lenguajes de propósito específico. Lenguajes que se diseñan para actuar en una
determinada área y generar programas con una finalidad determinada. Estos lenguajes
dispondrán de palabras reservadas y funciones propias para la explotación del área de
trabajo concreta.

1.2.3.3. CLASIFICACIÓN EN FUNCIÓN DE LA EVOLUCIÓN HISTÓRICA


A lo largo de los años las necesidades de los usuarios que utilizan los sistemas informáticos y
el hardware del mismo, han ido evolucionando y cambiando. Los ordenadores de hace 20 años
no poseían elementos físicos con características demasiado sofisticadas de fonna que solo
podíamos ejecutar programas en modo texto, en cambio hoy día estamos acostumbrados a usar
entomos visuales. Así, se diferencian diferentes generaciones, cada una de ellas caracterizada por
lenguajes de programación que cumplían ciertas características según la época.
■ Primera generación. La fonnan los lenguajes máquina y lenguajes ensamblador.
Años 40 y 50 donde se usaban taijetas perforadas y el programador debía codificar en
ellas las combinaciones de ceros y unos que indicaban el tipo de cálculo a realizar. Se
comienzan a usar lenguajes ensamblador de forma que no se tenían que especificar
cadenas binarias, ya que se crearon una serie de sentencias que representaban los
cálculos, pero seguían siendo instrucciones complicadas a nivel de registro y era
necesario que conociéramos el juego de instrucciones del microprocesador.
■ Segunda generación. Aparecen lenguajes de nivel medio. Se comienza a desarrollar
el primer compilador de FORTRAN. Esta etapa perdura aproximadamente desde
1958 a 1970.

Sabías que...
FORTRAN (Formula Translating System) aparece debido a la necesidad de
agilizar las tareas y la dificultad que suponía trabajar con el lenguaje máquina o lenguaje
ensamblador. A finales de 1953, John W. Backus propone a sus superiores de IBM
comenzar a trabajar en un nuevo proyecto por el que se desarrollara un lenguaje de
programación más sencillo y cómodo de utilizar para programa el IBM 704. Fue a
mediados de 1954 cuando se consolida el primer borrador de la especificación y en
octubre de 1956 cuando aparece el primer manual de FORTRAN. Fue usado
ampliamente por la comunidad científica, desarrollando potentes programas de análisis
matemático. i

■ Tercera generación. En esta etapa empezamos a hablar de la denominada


programación estructurada. Los lenguajes de programación de este período se
caracterizan por el uso de subprogramas, variables locales o estructuras de datos
dinámicas. Ejemplos de lenguajes de programación de tercera generación son Pasca!,
Modula o C.

■ Cuarta generación. En esta generación encontraremos lenguajes desarrollados para


crear un determinado tipo de software, se configuran para tareas muy concretas.
■ Quinta generación. Generación asociada a lenguajes diseñados para abordar la
inteligencia artificial. Este tipo de lenguajes trabajan con una serie de reglas y heciios
y en función de estos deben concretar si una premisa conclusión es cierta o no.
Ejemplo de esta generación es Prolog. Además, comienza a ponerse en práctica la
programación orientada a objetos.
■ Generación visual. Nace a principios de la década de los 90 debido a la necesidad de
usar interfaces cada vez más amigables y fáciles.
1.2.3.4. CLASIFICACIÓN EN FUNCIÓN DE LA FORMA DE EJECUCIÓN
A la hora de crear un programa el primer paso que se lleva a cabo es la escritura del mismo;
existen entornos especializados para ello. A continuación, el nuevo software debe ser ejecutado,
ahora bien, la ejecución se puede realizar en función de si todo es código, es correcto o bien, si
una instrucción es correcta. Así, existen lenguajes diseñados de forma que comprueban todo el
codigo y si no hay ningún error en ninguna línea, este se ejecuta o bien si una instrucción es
correcta, esta se reproduce. Encontraremos según el modo de ejecución;
■ Lenguajes compilados. Los lenguajes compilados son aquellos que realizan un
análisis minucioso de las líneas de nuestro código en búsqueda de posibles fallos
léxicos, sintácticos o semánticos. Si nuestro código no tiene errores genera un fichero
llamado código objeto. A partir de este nuevo código usará un programa llamado
enlazador o linker que se encargará de añadir librerías'' u otro tipo de software
necesario para así obtener el código o archivo ejecutable. Hasta que nuestro código
fuente no esté libre de fallos no se ejecutará nuestro programa.
■ Lenguajes interpretados. Los lenguajes de programación que poseen la etiqueta de
interpretados ejecutan línea a línea el código creado. En caso de que exista alguna
instrucción con fallos esta no se ejecutará, pero seguirá el proceso con el resto de
líneas a no ser que la línea que falla sea una parte importante de nuestro programa.
1.2.3.5. CLASIFICACIÓN EN FUNCIÓN DE CÓMO AFRONTAN LAS TAREAS
A REALIZAR

Según esta clasificación encontraremos lenguajes imperativos y declarativos.


■ Lenguajes imperativos. Los lenguajes de programación imperativos son aquellos en
los que se escribe línea a línea qué se debe hacer, de forma que se indica una
secuencia de pasos a realizar para llegar a resolver el problema en cuestión que dio
pie a la realización del software. Tanto C# como Java son lenguajes imperativos.
■ Lenguajes declarativos. Los lenguajes de programación declarativa, en cambio,
incluyen una serie de premisas o conjunto de condiciones finitos y la conclusión a la

'Conjunto de programas, desarrollados por otros, que podemos usar en nuestros propios programas.
Capítulo 1. Introducción 9

que se debe llegar. En ellos no se indica de forma expresa los pasos a seguir para
alcanzar la solución, solo se plantean una serie de reglas y en función de estas se van
describiendo los pasos. Se usan normalmente en inteligencia artificial.

1.2.3.6. CLASIFICACIÓN EN FUNCIÓN DEL ESTILO DE PROGRAMACIÓN


EMPLEADO

A la hora de crear un programa podemos optar por escribir todo el código, línea a línea, como
un solo bloque, realizar pequeños bloques incluidos en el programa principal a los que se hace
referencia desde este o bien crear ficheros independientes con subprogramas a los que
accederemos según nuestras necesidades. Así, según lo explicado encontraremos:
■ Lenguajes de programación estructurados. En estos lenguajes encontraremos el
código tal que se haya estructurado en un conjunto de funciones. Suelen poseer una
función principal que es llamada en el momento de ejecución. Esta función hará uso
del resto según decida el programador. Podemos hablar además de programación
modular en los casos en los que se usan otros ficheros, llamados módulos, en los que
se agrupan funciones diseñadas para trabajar con el mismo tipo de datos o están
relacionadas según algún criterio establecido por la persona que crea la aplicación.
■ Lenguajes de programación orientada a objetos. En este tipo de lenguajes se usan
elementos denominados clases de fonna que se pretende con ellos reflejar al máximo
posible la realidad que rodea el software. Así, si en el tipo anterior se agrupan en
funciones líneas de código según tareas o acciones a realizar, en la programación
orientada a objetos la agrupación se realiza en función del objeto que se quiere
plasmar en el programa. Por ejemplo, si queremos realizar un programa que gestione
un concesionario de vehículos programaremos una clase que llamaremos vehículo, en
ella (en este conjunto de líneas) incluiremos todo lo que rodea a un coche, desde sus
características a las funciones que pueden desarrollarse sobre él.
1.2.3.7. CLASIFICACIÓN EN FUNCIÓN DE LA CAPACIDAD DE GENERAR
PROCESOS CONCURRENTES O NO
La concurrencia es la ejecución de varios procesos a la vez. Generalmente usamos de forma
errónea los ténninos programa y proceso. Debe quedar claro que;
■ Programa. Es el código que hemos generado, que es correcto y está listo para
ejecutarse pero aún no está en ejecución.
■ Proceso. Una vez damos la orden de ejecutar nuestro programa y este se coloca en
memoria y es decodificado instrucción a instrucción por el procesador se le denomina
proceso.

Encontraremos lenguajes de programación con funciones que penniten generar programas


concurrentes o no. Así, tendremos:
■ Lenguajes de programación concurrente. Proporcionan los mecanismos necesarios
para generar señales, tuberías, semáforos, etc., que permiten la ejecución concurrente.
■ Lenguajes de programación concurrente. Caso opuesto al anterior. Lenguajes que
no aportan ningún tipo de dato o función para producir concurrencia.
10 Programación

1.2.3.8. CLASIFICACION EN FUNCION DE LA INTERACTIVIDAD

Existen lenguajes orientados a sucesos o eventos o no, así:


■ Lenguajes de programación orientados a sucesos. Son aquellos que permiten la
iteración continua del usuario con el software de forma que realizará una tarea u otra
según el tipo de acción que el usuario realice.
■ Lenguajes de programación no orientados a sucesos. En este caso, el programa
seguirá una secuencia de acciones sin dar opción al usuario a modificar esta.

1.2.3.9. CLASIFICACIÓN EN FUNCIÓN DE SI SON O NO VISUALES


Para finalizar, hoy día usamos sobretodo aplicaciones con entorno gráfico similar a los
sistemas operativos actuales. Así, existen lenguajes de programación adaptados a los nuevos
tiempos que desarrollan este tipo de programas que además son orientados a sucesos.
■ Lenguajes de programación no visuales. Este tipo de lenguajes generan programas
en modo texto. Aunque actualmente primen los entornos visuales aún se sigue usando
este tipo de software en comandos para configuración y administración de equipos.
■ Lenguajes de programación visuales. Generan programas a los que se asocia un
entorno visual. Estos lenguajes usarán entornos de desarrollo que proporcionarán las
herramientas necesarias para la creación de paneles, botones, cuadros de texto, etc.,
de forma que el programador se evite la programación de estos componentes.
1.2.4. CÓDIGO
Aunque el término código se ha usado en varias ocasiones en los párrafos anteriores no ha
sido definido aún formalmente. Según la Real Academia de la Lengua una de las definiciones de
código es; Combinación de signos que tiene un determinado valor dentro de un sistema
establecido. Podemos usar esta definición para expresar la esencia de código de un programa, ya
que este está formado por un conjunto de signos (elementos del lenguaje, elementos generados
por el usuario, signos matemáticos, etc.) que tienen valor dentro de un sistema establecido, es
decir, en las especificaciones de un lenguaje de programación concreto.
Más comúnmente podemos decir que se define código al conjunto o subconjunto de líneas
que forman un programa. Estas líneas se configurarán con una estructura concreta y estarán
formadas de unos u otros elementos en función del lenguaje de programación que estemos
usando. Usaremos con frecuencia la palabra código fuente en lugar de código.
Además, en los lenguajes de programación compilados usaremos el témiino código objeto,
siendo este el código generado tras la compilación del código fuente. El código objeto es un
código máquina o bytecode de forma que a partir de un código fuente escrito en lenguaje natural,
obtenemos código que la máquina es capaz de procesar de forma directa.
Cada línea de código se suele llamar con fi-ecuencia instrucción. Una instrucción sigue una
serie de reglas de constmcción, de forma que según el lenguaje de programación que usemos una
instmcción poseerá una estmctura u otra.
Capítulo 1. introducción 11

1.2.5. PALABRAS RESERVADAS DEL LENGUAJE

Los lenguajes de programación usan una serie de palabras o símbolos que desempeñan una
función específica dentro un programa. A este conjunto se signos o combinación de ellos se les
denomina palabras reservadas.
Estas palabras solo pueden usarse en un concepto detenninado, nunca libremente por el
usuario.

Por ejemplo, en el lenguaje de programación C tiene como palabras reservadas if o while.


Ambas solo podrán ser usadas en caso de que se quiera iniciar una sentencia de control
condicional o un bucle.

1.3. DISEÑO DE UN PROGRAMA


A la hora de afrontar la realización de un programa debemos tener claro qué debemos hacer.
Es un error comenzar a crear software a lo loco ya que solo conseguiremos dedicar a este
menester más tiempo del necesario. Además, el proceso de creación no solo conlleva picar
código^ sino que una vez se ha generado un código ejecutable este debe ser probado, de forma
que se detecten posibles fallos de ejecución para su corrección antes de promocionarlo entre los
usuarios finales.

Así, a la hora de construir un programa se deben seguir una serie de pautas o fases:
■ Análisis del problema.
■ Diseño del algoritmo.
■ Codificación.

■ Prueba y depuración.
■ Documentación.

1.3.1. FASE DE ANÁLISIS DEL PROBLEMA


En esta fase se debe analizar con detenimiento el problema que se plantea. A la hora de
realizar un programa debemos tener clara cuál es su finalidad y a quien va dirigido. Así, debemos
tener una perspectiva cercana a la exactitud de qué elementos debe incluir y que tareas debe
realizar. Cuando el software es para nuestro uso personal nosotros mismo especificamos las
pautas y decidimos su funcionalidad, en cambio, cuando debe ser usado por otras personas, para
conocer sus necesidades debemos entrevistamos con ellos.

Así, nonnalmente, cuando una persona individual o una empresa nos encarga la realización de
un programa debemos concertar una entrevista de fonna que de ella obtengamos ima visión
particular de las necesidades del usuario o la empresa y de las tareas que debe realizar el
software.

La fase de análisis en una parte fundamental del proceso de desarrollo de un programa, una de
las más importantes, ya que un mal análisis puede hacer que un programa ya codificado y
probado deba realizarse de nuevo.

Expresión común en la jerga del programador.


En la fase de análisis debemos identificar:

■ En qué tipo de ordenadores esperamos ejecutar el software. Sistema operativo para el


que se diseña, microprocesador y memoria mínimos, etc. Las aplicaciones en función
de su codificación serán más o menos óptimas de forma que necesitarán ordenadores
de mayor o menor rendimiento. Debemos conocer esta característica justo en este
punto para en la fase de codificación adaptar las tareas a los recursos de los que
vamos a disponer.
■ A quién va dirigido el software. No es lo mismo diseñar un programa para personas
acostumbradas a usar todo tipo de aplicaciones y otras que utilicen mínimamente las
nuevas tecnologías.
■ Cuáles serán los datos de entrada. A la hora de usar el programa, cuáles serán los
datos que se van a procesar, por ejemplo, si vamos a diseñar una aplicación para una
cafetería los datos a introducir serán las características de los diferentes alimentos que
tendremos de venta a nuestros clientes.
■ Cuáles serán los datos de salida. En función de los datos de entrada y las operaciones
que realicemos con ellos cuáles serán los datos de salida esperados. En el caso de la
cafetería podría ser un ticket impreso con los diferentes productos consumidos.
■ Cuáles serán las operaciones a realizar para que a partir de unos datos de entrada
dados obtengamos la salida que hemos analizado con anterioridad.

entrevista CON
EL USUARIO FINAL

PROGRAMADOR

Figura 1.3. Fase de análisis de software representada gráficamente.

1.3.2. FASE DE DISEÑO DEL ALGORITMO


Una vez se tengan claras las tareas a realizar, los datos que se van a usar, en qué sistemas se
ejecutará el software, etc., pasamos a desarrollar el algoritmo que representa de forma técnica
cómo abordar el problema inicial.
En la fase de algoritmo usaremos diagramas de flujo y pseudocódigo. En la fase anterior
concretábamos todos los elementos que el programa debía tener en cuenta y las tareas a realizar.
Capítulo 1. Introducción 13

en esta se identifican soluciones a los problemas planteados, así como pasos de ejecución para
concretar las tareas.

La fase de diseño es una especificación detallada que facilita posterionnente la programación


en un lenguaje de programación concreto.

/ r (—i~-—I \ 'PROGRAMA: notaAsignatura


V-A'íj' I' I^H ^ ' I ¡ENTORNO: Nota es número entero
H i |.ALGORITMO:
ESS^nS Ici escribir "Introduce nota"
i SI Nota < 5 ENTONCES
<5 I escribir "Suspenso"
i SI NO
p- , 1 L_ I escribir ".2\probado"
Suspenso (
Suspenso ( Aprobado
Aprobado( I FIN SI
^ . V I FIN PROGRAMA

V —H pi" U—'J Figura 1.5. Fase de diseño, ejemplo de


Figura 1.4. Fase de diseño. Diagrama de flujo de pseudocódigo del diagrama de flujo
un prograrna simple. Muestra por pantalla la palabra representado en la Figura 1.4.
suspenso o aprobado en función de si la nota
introducida es menor o no que 5.

Existen unas pautas a seguir en el desarrollo de diagramas de flujo y pseudocódigo,


estudiaremos estas en siguientes apartados.

1.3.3. FASE DE PROGRAMACIÓN (CODIFICACIÓN)


Es en esta fase donde se usará un lenguaje de programación concreto de forma que el
algoritmo anterior se convierta realmente en un programa que pueda ser ejecutado. Llegados a
este punto solo tendremos que ver línea a línea el algoritmo anteriormente descrito y convertir
estas a los símbolos admitidos por el lenguaje de programación que vayamos a usar. En
ocasiones no será tan fácil la conversión, ya que existirán líneas que no puedan plasmarse con el
significado que se colocaron en el algoritmo usando el lenguaje de programación escogido
tendremos que buscar altemativas.
PROGFIAM.A: nota.Asignatura ————— 'Public void notaAsignatura () j
ENTORNO: Nota es número entero ■ >• int nota;
•ALGORITMO:
escribir "Introduce nota" > printf ("Introduce una nota");
leer Nota ^ scanf ("li", snota);
SI Nota < 5 ENTONCES ^ if (nota < 5) {
escribir "Suspenso" ^ printf ("Suspenso");
SI NO }
escribir "Aprobado" ^ else {
FIN SI printf ("Aprobado");
FIN PROGRAMA }

Figura 1.6. A la izquierda se muestra el pseudocódigo ejemplo del apartado anterior. A la derecha
vemos el programa de este algoritmo escrito en C++.
1. La definición de la función se realiza mediante void notaAsignatura, concretamente
son tipo de dato devuelto y nombre de la función.
2. Se establece el entorno, es decir, los elementos de almacenamiento de infonnación
(variables) que debemos usar. En nuestro programa a este componente se llama Nota.
3. Se escribe en pantalla el mensaje "'''Introduce una notd\ C y C-h- usan la función
printf para mostrar información al usuario mediante el display o dispositivo hardware
de salida por defecto.
4. Llegados a este punto el programa se detiene a la espera de que el usuario escriba la
información pedida. Se recoge el dato en el componente Nota antes declarado. Una
de las fimciones que utilizan C y C-H- para la lectura de infonnación es scanf. Esta
función necesita saber qué tipo de información va a recoger, es decir, si será un
número, un texto, un valor decimal o un carácter, de ahi el %¡.

NOTA: C-H- es un lenguaje de programación orientado a objetos y en él solemos usar ,


normalmente otro tipo de elementos para realizar la función de mostrar o recoger |
infonnación al o del usuario. Encontraremos líneas similares a cout « Mensaje cuando
queremos mostrar información al usuario y cin » variable cuando queremos realizar la
operación contraria.

5. A continuación el programa realizará una pequeña comprobación, en función de la


cual, realizará una acción u otra. C y C-H- utilizan la palabra reservada if para evaluar
una condición. Se traduce más o menos a si la condición se cumple... Así, si el
valor especificado por el usuario es menor que 5 se mostrará en pantalla el texto
Suspenso y en caso contrario (else) el texto Aprobado.

1.3.4. FASE DE PRUEBAS Y DEPURACIÓN


Una vez el programa se crea debemos comenzar a probrarlo antes de distribuirlo al usuario
final. Si bien es cierto que cuando se crea un software, este se prueba repetidamente, se abordan
los fallos más relevantes, pero cuando pasa a ser "probado" por el usuario final es posible que
suijan nuevos fallos de los que no nos hemos percatado. Así, cuando se crea un programa y se
saca al mercado es normal encontrar cada cierto tiempo actualizaciones del mismo, estas
proporcionan nuevas funciones pero además subsanan errores no controlados.
Durante las pruebas del software se suponen todas las situaciones en las que se puede
encontrar el programa y se observa cómo actúa ante ellas. La depuración consiste en el uso de
valores de prueba para la detección de posibles errores, corrección de los mismos, y así elaborar
una versión del software libre de fallos. Así la depuración es el proceso de detección de
errores en tiempo de ejecución y corrección de los mismos.
¡ pubiic void notañsignatura ("{ Imaginemos que el programa ejemplo
¡ int nota; Apartado 1.3.3. lo hemos codificado
j printf ("Introduce una nota"); de forma que la Imea if (nota 5) la
¡ scanf ("%i", &nota); hemos escfito como if(nota > 5).
¡ if (nota > 5) {
j ^ printf ("Suspenso"); Cuando iniciamos la fase de
¡ else { depuración y escogemos como valor de
¡ printf ("Aprobado"); prueba 8, es decir, nota = 8, ¿qué
! ^ sucederá?
Figura 1.7. Programa del Apartado 1.3.3 modificado.
Capítulo 1. Introducción 15

Al llegar a la condición vemos que esta dice algo como "si nota es mayor que 5 escribe
Suspenso", nuestra nota es 8, mayor que 5, así la palabra Suspenso es lo que se visualizará,
pero, ¿es correcta la solución? Evidentemente no, ya que una nota de 8 es muy aceptable y para
nada se considera suspenso. Dada esta situación, el desarrollador del programa sustituirá el
símbolo > por < y seguirá realizando nuevas pruebas.
Cuando todos los valores de entrada posible se hayan analizado y se hayan observado todas
las salidas obtenidas, si estas son correctas daremos por finalizada la fase de depuración y
pruebas.

Sl'-l Salida 1
Salida 2
Salida 3

Entrada 1
Entrada 2
Entrada 3

Entrada n

Figura 1.8. Fase de pruebas y depuración.

1.3.5. FASE DE DOCUMENTACIÓN


Para finalizar el proceso de creación de un programa crearemos documentación relacionada
con este. Es necesario que el usuario final conozca qué requisitos debe poseer el hardware, cómo
podemos ejecutar el software (formato de ejecución o posibles parámetros a usar), o cuáles son
los valores de entrada que acepta.
Existen aplicaciones diseñadas para la creación de documentación, los típicos ficheros de
ayuda que todos conocemos, aunque es fácil crear un manual con paquetes ofimáticos tales como
Microsoft Office y Libre Office. El primero de ellos utiliza Microsoft Word y el segundo Write.

1.3.6. EJEMPLO DE CREACIÓN DE UN PROGRAMA


En este apartado vamos a ejemplificar las fases anteriormente descritas, teniendo en cuenta un
problema real. Supongamos que una empresa se pone en contacto con nosotros y nos pide
realizar un software que debe detectar si un número introducido es par o no^.

^ Los ejemplos usados en estos apartados son muy sencillos ya que interesa que el lector entienda los
conceptos estudiados y no la programación en sí.
16 Programación

FASE 1. ANALISIS DEL PROBLEMA

Concertamos una entrevista con la persona encargada en la empresa que pueda hablamos
sobre el tipo de software que necesita. En esta entrevista preguntaremos qué tipos de datos de
entrada se deben usar, una vez se procese un dato qué salida se debe obtener, si se produce un
error qué se espera visualizar, etc.
Tras el encuentro queda claro que:
1. El programa solicitará un número. Solo se pueden comprobar los 100 primeros
números. El cero no es un valor válido.

2. En caso de que el número sea par se visualizará en la pantalla "El número introducido
es par".
3. En caso de que el número sea impar se visualizará en la pantalla "El número
introducido es impar".
4. Si el número a comprobar es el cero se visualizará por pantalla el mensaje "Valor
incorrecto, el cero no es un valor válido".
5. Si el número introducido es menor que O o mayor de 100 se mostrará en pantalla el
mensaje "Número introducido fuera de rango. Rango de número válido para
comprobar 1 - 100".
FASE 2. DISEÑO DEL ALGORITMO
Ya tenemos los datos necesarios para comenzar a trabajar. Sabemos qué desea el usuario final
que ocurra en determinadas situaciones de ejecución así que construiremos el diagrama de flujo
del programa y su pseudocódigo. Ejemplos de ambos elementos podrían ser los expuestos a
continuación.

Figura 1.9. Diagrama de Flujo del problema planteado. Fase de diseño.


Capítulo 1. Introducción 17

(1)PROGRAMA: numeroPar
(2)ENTORNO: Numero^, Modulo son números entero
(3)ALGORITMO:
(4)escribir "Introduce un numero"
(5)leer Numero
(6)SI Numero = O ENTONCES
(7)escribir "Valor incorrecto, el cero no es un valor
válido"
(8)SI Numero < O |
| Numero > 100 ENTONCES
(9)escribir "Número introducido fuera de rango. Rango de
número válido para comprobar 1 - 100"
(lO)SI NO
(11)Modulo = Resto de Numero/2
(12)SI Modulo = O ENTONCES
(13)escribir "El número introducido es par"
(14)SI NO
(15)escribir "El número introducido es impar"
(16)FIN SI
(17)FIN SI
(18)FIN PROGRAMA

Se coloca entre paréntesis al inicio de cada línea el número de esta.


Iniciamos el programa al que llamaremos
(1)PROGRAMA: numeroPar
numeroPar.

Usaremos dos elementos que guardarán


(2)ENTORNO: Numero , Modulo son infonnación. Se llaman Numero y Modulo y
números entero
ambos referencian valores enteros.

Visualizaremos el texto "Introduce un

(4)escribir "Introduce un numero" número" y esperaremos a que el usuario


(5)leer Numero ejecute la orden. Este número se almacenará
en el elemento Numero.

Comprobamos si el número introducido es


cero. Si es así debemos mostrar el mensaje
(6)SI Numero = 0 ENTONCES
"Valor incorrecto, el cero no es un valor
válido

En caso de que el número introducido sea


distinto de cero comprobaremos si está en el
rango de 1 a 100. En caso de que hayamos
(8)SI Numero < 0 |
| Numero > 100 escrito un valor negativo o mayor que 100 se
ENTONCES
visualizará en pantalla el texto "Número
introducido fuera de rango. Rango de número
válido para comprobar 1 - 100".
Si ninguno de los casos anteriores se dan
(lO)SI NO
(11)Modulo = Resto de Numero/2
pasaremos a deducir si el número escrito es
par o no. Para ello, almacenaremos en Modulo

^ Aunque estamos escribiendo pseudocódigo y este se asemeja al lenguaje que usamos normalmente,
evitaremos usar letras acentuadas, así como la ñ, en ningún elemento del programa ya que en la mayoría de
lenguajes de programación no se aceptan estas singularidades.
el resto de la división entera de Numero entre
dos.

Sí el resto de la división es cero el número


será par, así daremos la orden de visualizar en
(12)51 Modulo = 0 ENTONCES
pantalla el texto "El número introducido es
par".
Si el resto no es cero matemáticamente el
número será impar, y así lo haremos saber al
(14)51 NO
usuario con el texto en pantalla "El número
introducido es impar".

FASE 3. CODIFICACIÓN
La base anterior es una de las más complejas ya que concretamos exactamente las partes del
algoritmo. En esta todo es más sencillo ya que solo debemos "traducir" el algoritmo a un
lenguaje de programación concreto. En realidad esta fase será más o menos compleja en función
del lenguaje de programación que usemos.
Veamos línea a línea como será el código de nuestro programa teniendo en cuenta que
usaremos C-H- como lenguaje de programación.
(1)PROGRAMA: numeroPar void numeroParO {
(2)ENTORNO: Numero", Modulo son
int Numero, Modulo;
números entero
(3)ALGORITMO:
printf ("Introduce un numero");
(4)escribir "Introduce un numero"
scanf ("%i",Numero);
(5)leer Numero
if (Numero ==0) {
(6)51 Numero = 0 ENT0NCE5
printf ("Valor incorrecto, el
(7)escribir "Valor incorrecto, el cero
cero no es un valor válido");
no es un valor válido"
)
(8)51 Numero < 0|
| Numero > 100 else if (Numero < 0 |1 Numero > 100) {
ENT0NCE5 printf("Número introducido
(9)escribir "Número introducido fuera fuera de rando. Rango de número válido
de rando. Rango de número válido para para comprobar 1 - 100");
comprobar 1 - 100" }
else {
(10)51 NO
Modulo = Numero mod 2;
(11)Modulo = Resto de Numero/2
if (Modulo == 0){
(12)51 Modulo = 0 ENTONCES
printf ("El número
(13)escribir "El número introducido es
introducido es par");
par"
)
(14)51 NO
else {
(15)escribir "El número introducido es
printf ("El número
impar"
introducido es impar");
(16)FIN 51
}
(17)FIN 51
}
(18)FIN PROGRAMA }

^ Aunque estamos escribiendo pseudocódigo y este se asemeja al lenguaje que usamos normalmente,
evitaremos usar letras acentuadas, así como la fi,en ningún elemento del programa ya que en la mayoría de
lenguajes de programación no se aceptan estas singularidades.
Capítulo 1. introducción 19

void numeroParO {
int Numero, Modulo;
printf ("Introduce un numero");
scanf Numero);
if (Numero == 0) {
printf ("Valor incorrecto, el cero no es un valor válido");

else if (Numero < O Numero > 100) {


printf("Número introducido fuera de rando. Rango de número válido
para comprobar 1 - 100");

else {
Modulo = Numero mod 2;
if (Modulo == 0){
printf ("El número introducido es par");

else {
printf ("El número introducido es impar");

Esta es la función completa escrita en C-H-. Se ha comprobado que está libre de fallos
sintácticos, léxicos y semánticos.
FASE 4. PRUEBAS Y DEPURACIÓN
A partir de ahora comenzamos a ejecutar el software. Se debe comprobar que ante
detenninadas entradas las salidas son las esperadas. Las entradas que pueden presentar
problemas son:
■ Número 0.

■ Número negativo.
■ Número superior a 100.
Al ejecutar la aplicación visualizaremos un entorno en modo texto como el que se observa en
la Figura 1.10.
Introduce un numero Escribiremos un cero. Debe aparecer el texto
Valor incorrecto, el cero no es un valor válido
Introduce un numero

alor incorrecto, el cero no es un valor válido

Figura 1.10. Programa sin entorno visual Figura 1.11. Ejecución del software y comprobación
en ejecución. de las salidas en función de ios valores de entrada.

Realizaremos este proceso con todos los valores de entrada posibles. Observaremos las
salidas, y si son las que se plantearon en el algoritmo el programa estará listo para ser usado. Si
se produce algún fallo, o alguna salida no es la esperada para el valor introducido volveremos al
código fuente y localizaremos el lugar donde se produce el error en ejecución. Ya estudiaremos
cómo visualizar los cambios que se dan en los valores introducidos durante la ejecución mediante
la depuración en los diferentes entornos de programación que utilizaremos.
El código planteado realiza las funciones tal y como la empresa detalló de forma que
pasaremos a la fase de documentación.
FASE 5. DOCUMENTACION

Antes de entregar el software a la empresa que nos lo encargó realizaremos un breve manual
sobre el mismo. Este podría ser similar al que se muestra a continuación.

USO DE PAR-IMPAR vl.O

Para ejecutar el programa escribiremos en la línea de comando numeroPar.


Veremos la siguiente imagen: Los valores negativos y superiores a
100 tampoco podrán ser analizados, si
aparece el mensaje "Número introducido
fuera de rango. Rango de número válido
para comprobar 1 - 100" este será debido
a que hemos introducido un número
negativo o mayor que 100.
El rango de valores que podemos
analizar es de 1 a 100. El programa mostrará el texto "El
número introducido es par" o "El niimero
El valor cero no se analiza, de forma introducido es impar" según el valor
que si lo escribimos se mostrará el utilizado del rango 1 a 100 sea par o
mensaje de error: impar.
"Valor incorrecto, el cero no es un El programa acabará tras mostrar el
valor válido"
mensaje de información adecuado.

1.4. DIAGRAMAS DE FLUJO


Podemos decir que un diagrama de flujo, a veces denominados organigramas, es una
representación gráfica de un algoritmo, de forma que se describen las diferentes tareas y la
secuencia en la que se ejecutan para alcanzar una solución.
En algoritmos complejos podemos usar más de un diagrama de flujo. Diseñaremos el
diagrama de flujo principal y a partir de él iremos diseñado diagramas de cada tarea que se puede
realizar a partir de la inicial.
Ya hemos visto representaciones de este tipo de elemento en los Apartados 1.3.2 y 1.3.6, a
continuación vamos a concretar qué tipo de figuras se usan y qué tipo de información
proporcionan.

1.4.1. REGLAS PARA DISEÑAR DIAGRAMAS DE FLUJO


A la hora de crear nuestro propio diagrama de flujo debemos tener en cuenta que:
■ Siempre debemos incluir una forma con el texto Inicio y otra con el texto Fin para
delimitar los momentos en que empieza y acaba el algoritmo.
■ Debemos usar una serie de símbolos estándar.
Capítulo 1. introducción 21

Los elementos del diagrama de flujo deben estar concatenados mediante flechas que
indican la dirección de ejecución.

Indican inicio o fln de programa.


CD
Representan una instrucción, un paso a dar o proceso. Por ejemplo: nota =
4, suma=suma+l, etc.

Operaciones de entrada/salida de datos. Por ejemplo: visualiza en pantalla


suma.

Usaremos este símbolo si nos encontramos en un punto en el que se


realizará una u otra acción en función de la decisión que el usuario tome.

Conector. Pennite unir diferentes zonas del diagi'ama de fonna que se


redefme el flujo de ejecución hacia otra parte del diagrama.

Representa una función o subprograma.

Conector de página. Se usa cuando llegamos al final de una página y aún no


se ha acabado el diagrama de flujo.

Disco magnético.

Unidad de cinta magnética.

Salida. Visualización de los datos de salida por pantalla. Este suele ser
siempre el periférico de salida por defecto.

Salida de datos por impresora.

•• ^1 Flechas para indicar la dirección de flujo.


■ Los diagramas de flujo deben escribirse de arriba hacia abajo o de izquierda a
derecha.

■ Debemos evitar el cruce de líneas, para eso se define la forma conector. El uso de
conectores debe producirse cuando no exista otra opción.
■ Todas las líneas de flujo deben estar conectadas a algún objeto.
■ A la hora de escribir texto en las formas, este debe ser escueto y legible.
■ Todos los símbolos de decisión deben tener más de una línea de salida, es decir,
deben indicar qué camino seguir en función de la decisión tomada.
Existen otras fonnas que pueden usarse en un diagrama de flujo pero no creemos que sea
necesario profundizar en ellas.
22 Programación

Si el alumno lo desea puede localizar más información sobre diagramas de flujo u


organigramas en la web o bibliografía aportada por el profesor.
ACTIVIDAD 1.1 ACTIVIDAD 1.2

Dado el siguiente organigrama, indica qué Realiza el diagrama de flujo tal que se de
tarea realiza. un dato de entrada X. Si la X es igual a 1
mostraremos el texto "Has pulsado 1". En caso
de que sea 2 el texto a visualizar será "Has
pulsado 2".

Figura 1.12. Diagrama de flujo.

1.5. PSEUDOCODIGO
La palabra pseudocódigo puede ser traducida como falso lenguaje, y no hay nada más
acertado que su definición. Un pseudocódigo es una descripción informal de un programa, siendo
de gran utilidad a la hora de diseñar el algoritmo del mismo. En pseudocódigo usamos lenguaje
natural o similar para representar estructuras propias del lenguaje de programación que usaremos
en la codificación, es decir, simulamos un lenguaje de programación.
El pseudocódigo, aunque usa el lenguaje natural, no utiliza su amplia variedad de palabras, se
seleccionan una serie de palabras como reservadas para representar todas las estructuras que
posteriormente deberán ser codificadas mediante el lenguaje de programación.
Se han visto ejemplos de pseudocódigo en los apartados 1.3.2 y 1.3.6. Estudiaremos en más
profundidad las palabras reservadas en pseudocódigo cuando empecemos a conocer las
diferentes estructuras de control y su significado en el Capítulo 2.

1.6. PROCESO DE COMPILACIÓN


Cuando estudiábamos los tipos de lenguajes de programación veíamos que en función de
cómo se ejecutaban, teníamos lenguajes de programación compilados y lenguajes de
programación interpretados.
En este apartado vamos a estudiar el proceso de compilación de un software. La palabra
compilación refiere a la traducción de un programa en código fuente a código máquina o
bytecode. Compilador será el nombre de la herramienta software que es capaz de realizar la
traducción.
Capítulo 1. Introducción 23

Un compilador está formado por dos partes fundamentales, cada una de ellas se divide en
otras tantas necesarias para cubrir todos los pasos del proceso. Así, dispone de: Análisis y
Sintesis.

La parte de análisis se encarga de la comprobación de las líneas de código, análisis léxico,


sintáctico y semántico. La parte de síntesis es la encargada de la obtención del código objeto.
Esta, a su vez, se divide en diferentes fases donde se genera código intermedio y se optimiza.
análisis está
! compuesta por:

/ O^PPiViifiSS \ ' " Análisis léxico. Este análisis


j Y se encarga de comprobar si
/j \\ las palabras introducidas son
¡j^ \\ correctas. Se eliminan
blanco, líneas

izquierda a derecha formando


\\^
\\
^I
/J
tokens^ que no son más que
consecución de letras que
\\I ' j fonuan una palabra reservada
/ válidas o bien un dato.
V ■ Análisis sintáctico. Pasado
el análisis léxico y
habiéndose comprobado que
Figura 1.13.
Figura 1.13. Diagrama
Diagrama representativo
representativo del
del proceso
proceso de
de todas las palabras
compilación.
compilación. introducidas son propias del
lenguaje, realizaremos el
análisis sintáctico del código.
En el análisis sintáctico se comprueba la estructura de las frases, si son correctas y están
fonnadas según el lenguaje de programación que hayamos usado.
■ Análisis semántico. Este análisis se encarga de deducir si todas las instrucciones
poseen un significado semántico correcto. Por ejemplo, si tenemos una línea de
código donde se asigna una cadena de caracteres a un valor entero, se producirá un
error semántico ya que el lugar donde se va a almacenar el dato debería estar
preparado para números y no letras.
La fase de síntesis de divide en:

■ Generación de código intermedio. Existen compiladores que generan un código


intennedio. Esta representación intennedia no es aún código máquina de fonna que
puede ser usada por cualquier hardware, es algo así como un código capaz de ser
utilizado por diversos tipos de hardware. Llegados a este punto se han superado las
fases de análisis con lo que si existían errores de compilación estos se han
subsanado.

' También denominados como componentes léxicos.


24 Programación

■ Optímización de código. La optimización, como su nombre indica, procurará


mejorar el código intermedio, de forma que se consiga un código objeto más rápido
de ejecutar.
■ Generación de código objeto. Tras la etapa de optimización se obtendrá el código
objeto preparado para ser usado en una máquina concreta.
Todas las partes descritas están siempre relacionadas con la tabla de símbolos y el
manejador de errores.
Básicamente la tabla de símbolos no es más que una estructura que contiene información de
todos los tokens que pueden usarse en un lenguaje de programación concreto. Los analizadores
léxico, sintáctico y semántico agregarán información a la misma ya que existirán identificadores
y otro tipo de elementos que no forman parte de las palabras reservadas del lenguaje.
En cuanto al manejador de errores, es la parte encargada de tratar los posibles fallos
producidos durante toda la compilación en las diferentes etapas.
Supongamos que hemos usado un programa preparado para escribir código en un lenguaje de
programación concreto. Hemos generado un fichero con código fuente y queremos proceder a su
compilación para poder ejecutarlo. Este fichero contiene la instrucción int a=b+2;"'. Es una linea
muy sencilla, con ella estamos creando un lugar donde guardar información entera llamado a,
concretamente el valor a almacenar es la suma de b más 2.
Una vez se han descrito las partes de un compilador, teniendo en cuenta nuestro código
fuente, el proceso de compilación se podría reducir a:
1. Recibimos el código fuente, int a=b+2; y comenzamos la fase de análisis. Primera
etapa a superar, el analizador léxico.
2. Se separa la instrucción formando diferentes tokens. Obtendremos; int, a, =, b, +, 2,

3. Los tokens analizados son correctos. Algunos como int, =,+ se encuentran en la tabla
de símbolos con anterioridad, el resto se agrega a esta.
4. Al ser correcto el análisis léxico pasamos al análisis sintáctico. I— a —i
En este paso debemos aseguramos de que la frase o instrucción |||
sea correcta. Se genera un árbol sintáctico por el que se b + 2
deduce si la instrucción está bien formada en el lenguaje de Figura 1.14.
1.14. Árbol
Árbol
programación que estamos usando. En todo momento el sintáctico
sintáctico de
de la
la
manejador de errores estará alerta por si se produce algún tipo instrucción C++.
instrucción C++.
de error en alguna etapa.
5. Si no se han producido fallos en la estructura de la frase pasaremos a comenzar el
análisis semántico. Los errores sintácticos son errores de estructura, frases o
instmcciones mal formadas en un lenguaje concreto, por ejemplo si nuestra
instmcción hubiera sido int a=b+2; se hubiera producido un error sintáctico, ya que
en una asignación tras el igual debe aparecer valor o variable y no otro igual. El
análisis semántico analizará el significado de la frase. La palabra int indica que

Instrucción C++
Capítulo 1. Introducción 25

estamos creando una variable de tipo entera, así, b debe serlo para que al realizar la
suma el resultado sea entero y pueda almacenarse en a.
6. Si la fase semántica es superada correctamente se generará un código intemiedio.
7. A continuación se producirá la optimización del código intermedio.
8. Para finalizar el compilador obtendrá el código objeto, código máquina, listo para ser
linkado" o unido a diferentes librerías, y así obtener el fichero final a ejecutar.
La siguiente figura muestra el proceso completo de generación de software a partir de un
lenguaje compilado.

I > 7y. 1
' 'Mi

Programa ^
ejecutable

Figura 1.15. Diagrama representativo de las fases de creación de un programa ejecutable a partir de un
código fuente expresado en algún lenguaje de programación.

1.6.1. NUEVAS TENDENCIAS EN EL PROCESO DE CREACIÓN


DE CÓDIGO OBJETO
Los lenguajes de programación actuales utilizan un modelo híbrido para la obtención del
código objeto. Cuando generamos un código máquina tenemos en cuenta unas características
hardware concretas, es decir, estamos obteniendo un código objeto para un procesador concreto.
Así, si queremos que un código fuente sea ejecutado en varias arquitecturas será preciso compilar
estas para cada una de ellas.
Con el fin de ser más versátiles y puedan generar código fuente traducido a objeto accesible
por un mayor número de arquitecturas evitando el proceso de compilación, los lenguajes de
programación de hoy día generan un código intennedio. Exactamente crean una máquina virtual
para la que desarrolla el código compilado que posterionnente será interpretado por el hardware
real en el que se ejecutará.
Así, podemos decir que existen lenguajes de programación híbridos tales que:
■ Compilan el código fuente para obtener un código intennedio.

"El proceso de linkar o enlazar consiste en agregar a nuestro código fuente aquellas librerías externas
usadas en nuestro programa.
26 Programación

■ El código intermedio es interpretado cuando pasa a ser ejecutado sobre una máquina
concreta.

1.7. ENTORNOS DE DESARROLLO INTEGRADO DE


SOFTWARE O IDE
Es este libro vamos a abordar las estructuras principales de un programa, generalizaremos en
primer lugar para posteriormente centrar nuestra atención en lenguajes como C# y Java. Así,
antes de comenzar en el Capítulo 2 con la programación pura y dura de aplicaciones, vamos a
estudiar los diferentes entornos de desarrollo que necesitaremos usar, o al menos en este capítulo,
vamos a identificar sus partes y conocer cómo crear código fuente, cómo depurar este y proceder
a la ejecución del mismo.
Un entorno de desarrollo integrado, también denominado comúnmente como IDE, es un
software informático compuesto por una serie de herramientas que ayudarán al programador en
todas las fases de creación de software. Años atrás encontrábamos IDEs dedicados solo a un
lenguaje de programación, hoy día, cada vez es más frecuente encontrar este tipo de programas
preparados para codificar software en diferentes lenguajes'^.
Así, un entorno de desarrollo integrado dispondrá de: editor de código, compilador,
depurador y generador de interfaz gráfica (GUl'^). El depurador será el encargado de detectar
posibles fallos de ejecución.
Los entornos de desarrollo integrado que usaremos a lo largo de este libro serán Microsoft
Visual Studio 2012 Express, para la creación de aplicaciones C# y Netbeans 7.3 para la
creación de aplicaciones Java. Ambos son gratuitos. Del primero de ellos podemos obtener una
versión más completa si compramos el software a Microsoft, sin embargo, para la realización de
aplicaciones en el entorno aula nos es más que suficiente la versión Express.
1.8. MICROSOFT VISUAL STUDIO 2012 EXPRESS
Visual Studio es un IDE desarrollado por Microsoft que permite la generación de aplicaciones
para Windows codificadas en C#,VB.NET o C++. Existen diferentes versiones del producto:
■ Ultímate con MSDN.
■ Premium con MSDN.
■ Test Professional con MSDN.
■ Professional con MSDN.
■ Professional.

Se han colocado en orden las diferentes versiones según la funcionalidad y número de


características que poseen. La primera de ellas. Ultímate con MSDN,es la que proporciona una
mayor versatilidad al programador, mientras que Professional es la versión más modesta del
software.

O bien los entornos incluyen analizadores capaces de compilador o interpretar código de diferentes
lenguajes o se pueden agregar complementos al entomo de desarrollo.
GUI = Graphical User Interface, interfaz gráfica de usuario.
Capítulo 1. introducción 27

NOTA: MSDN (Microsoft Developer Network). Es accessible en la URL


http://msdn.microsoft.com/es-es, considerándose un centro de ayuda fundamental para
desarrolladores de software de Microsoft.

ACTIVIDAD 1.3

Accede a la web oficial de Microsoft Visual Studio. Localiza una tabla comparativa de todas
las versiones y observa las características de la versión Premium con MSDN.

En cuanto a la versión Express podemos elegir entre 5 opciones, cada una de ellas orientada al
tipo de dispositivo donde se va a ejecutar la aplicación o el entorno donde se visualizará. Así, en
la web de Microsoft encontraremos Visual Studio Express 2012 para:
■ Web. Diseñado para el desarrollo de aplicaciones web.
■ Windows 8. Esta versión pennite la creación de aplicaciones para Windows 8.
Sabemos que el entorno visual de este nuevo sistema operativo de Microsoft ha
cambiado drásticamente de forma que se ajusta más a las necesidades de usuario con
sistemas de pantalla táctil, así, las aplicaciones tal y como conocemos en Windows 8
tienden a desaparecer. Aprende a programar en este nuevo entorno con esta
herramienta.

■ Windows Desktop. Permite la creación de aplicaciones de escritorio en C#, Visual


Basic o C-H-.

■ Windows Phone. Pennite la creación de aplicaciones para sistemas operativos


Windows Phone 8.0 y Windows Phone 7.5. Así, podremos desarrollar aplicaciones y
juegos para móviles provistos de este software base.
■ Team Foundation Server Express 2012 con Update 2. Desarrollado para grupos de
trabajo pequeños, de como máximo 5 desarrolladores, de forma que se puedan crear
aplicaciones robustas y profesionalmente.
En este libro hemos optado por el uso de la versión Windows Desktop de Microsoft Visual
Studio 2012 para la realización de las actividades en C# y C-H-. Lo primero que debemos hacer
es descargar el archivo de instalación. La dirección web de descarga es:
http://www.microsoft.eom/visualstudio/esn/downloads#d-2012-express.
El archivo tiene formato ISO, es decir, es una imagen de disco. Podemos usar software de
quemado de CD para generar un CD/DVD con la aplicación y acceder a los ficheros para
proceder a la instalación o bien usar programas tipo Alcoholl20% que crean unidades de
CD/DVD virtuales de fonna que solo tendremos que montar la imagen ISO en una de ellas para
comenzar a usar su contenido.

La instalación del software es sencilla. Una vez montada la unidad virtual o introducido el CD
en la unidad lectora de CD/DVD, debemos acceder al contenido y hacer doble clic sobre el
fichero wdexpress_full.exe.
28 Programación

Veremos la primera pantalla del asistente


Visual Studio de instalación. En ella se proporciona
información sobre la capacidad de disco
Express 2012 para escritorio de necesaria y la ubicación de los archivos que se
Windows copiarán. Debemos activar la casilla Acepto
los términos y condiciones de la licencia para
B programa de irjsíalación requwe 4^7 para:
CvArchwos de programa^iAoosoft Visual 110
que se muestre el botón Instalar y comenzar
con el proceso. Así, clic en Instalar.
bebe acepi* bi 'fr-- rvK bei ti-r^ apaa poder óistalv el producía
El proceso finalizará con el reinicio del
Vtsual Stucfc)2012 envía automáticamerTte míormaciéo a Microsoft
sobre su expenenda con la instalación k notifica orando hay sistema. A partir de ahora veremos un nuevo
actuafaadwies disporcUes y lo conecta a contenido m línea. P*a
obtener mis ir^xmadón,consulte la Ot<U.at.ír, de p.-^at dad. icono en nuestro panel de todos los programas;
Acepto los térmnos y condkibnes de la licenoa.
Súmese al P'cg'c.rupo-j 12 ''^c:i'3dí'jc.d>?prujflcic>jino
paa ayudar a mgocar la calidad, confíabífidad y rendimiereo de
VS Express para Desktop
ViaralStuda

Figura 1.17. Acceso a Visual Studio versión


Figura 1.16. Pantalla de inicio de instalación de Express para aplicaciones de escritorio.
Visual Studio 2012 Express.

Tras acabar con la instalación y ejecutar el software debemos registramos para poder usar la
versión de evaluación de forma indefinida más allá de 30 días. Al ejecutar por primera vez
Visual Studio se visualiza una ventana en la que se indica la necesidad de registro como usuario,
en ella podemos directamente acceder al registro o cancelar. En el primer caso el programa nos
redirige a la web de registro, en el segundo accedemos a la ventana principal para comenzar con
el uso de Visual Studio Express 2012. Si no registramos en primer lugar el software podremos
hacerlo posteriormente haciendo clic en Ayuda Registrar producto.
1.8.1. REGISTRO DE VISUAL STUDIO EXPRESS 2012 PARA
escritorio
Para registrar Visual Studio Express es necesario mostrar la pantalla de registro. Si es la
pnmera vez que ejecutamos el programa aparecerá por defecto en primer lugar, en caso
contrario, si cancelamos el registro en la primera ejecución accederemos a ella tal y como se
indica al finalizar el apartado anterior(Ayuda -> Registrar producto).
Esta pantalla requiere que se introduzca un número o clave del producto. Esta clave la
concederá Microsoft tras el registro en línea. Así, para obtener el número identificativo del
producto seguiremos los siguientes pasos.
1. Clic en Registrar en línea.
2. Debemos registramos en la web como usuario msn. Si tenemos una cuenta msn válida,
por ejemplo Hotmail, accederemos al sistema con ella.
3. Rellenar el formulario de registro, está compuesto por varias pantallas. Los campos cuyo
nombre esté precedido por un asterisco (*) son obligatorios. A la hora de introducir la
información:
Capítulo 1. Introducción 29

■ Indicaremos Académico" en la lista


¿Se ha registrado en Visual Studio
para uso académico, profesional o
personal?
Visual Studio ■ En la zona Comunicación
Express 2012 para escritorio de Wii dejaremos desactivadas las casillas
de verificación.
Ene producto expirara en 30 dta(s).

Et r>e<esano registrarte para continuar usando Microsoft Visual Studio 4. Clic en Continuar.
Ejcpress 2012 para escmono de Windows.
Clave del producto: 5. Aún no ha finalizado el registro:
■ En la lista Identifique su rol
académico actual,
seleccionaremos Estudiante de
instituto.

■ Rellenar el cuadro Nombre de la


escuela con el nombre del instituto
en el que nos encontremos.
Figura 1.18. Pantalla de registro del software.

■ En la lista ¿Cuál es su principal área de estudio? escogeremos Ciencias


informáticas y de la información.
■ En el cuadro de texto ¿Para qué curso o clase está evaluando la posibilidad
de usar Visual Studio? escribiremos Primer curso de DAM.

6. Clic en Continuar.

7. Finalmente aparecerá la clave de registro. Copiar esta y Pegar en el cuadro Clave del
producto. Véase la Figura 1.18.

clave de producto:
MIVIVJ9-FKy74-W449Y-RB79G-8GJGJ

Figura 1.19. Clave de Visual Studio Express 2012 para la cuenta e información proporcionadas en el
registro de la aplicación.

Desde la misma web de registro podemos descargar una guía de uso del programa.

ACTIVIDAD 1.4

Descargar, instalar y registrar Visual Studio Express 2012 en el ordenador de clase.

"Este libro está destinado a ser libro de texto del módulo profesional Programación de los ciclos
formativos de grado superior Desarrollo de Aplicaciones Multiplatafonna y Desarrollo de Aplicaciones
Web, así el fonnulario de registro se rellena en función a esta infonnación. Si el lector ha adquirido el libro
sin ser alumno de ciclo, se aconseja que se rellenen estos campos con los datos que crean oportunos y se
acerquen más a su realidad profesional o personal.
30 Programación

1.8.2. PAGINA PRINCIPAL DE VISUAL STUDIO EXPRESS 2012

]AfU>e;'0 COTTAR VtR DCPUPJXR HERKAMEKTAS PfVUA VfNTAIJL AVUOA ^ \


"¡d d "'' ^ IS '' ^ . ^. 'v 4;
«Ac>*»c lae** ^ ctKM I9JV0 xMAtavrA} ««wm «tirfta" «r-jcA
d c - j" ► ^.

Nw«dAd«e*> el de«troIio <Jcl e^critofio de vv n-íc-.v.

-v
r*t AnM .
*tevt^4íd« f" %/V*»» C* •
)i;«vrtfad«s ce A«a*«<4
VWv*»«A rarvw
^
:
f r! drw'cAo dri 4

Inifoducdón e Uk «pliCKtonrv del e«"rftofto de W.'vk .v-.


kereduidoA «tu «cAcii:>crr^ 4 pf» ift> U «'V4.*iC«* ^
ApwR*»«#e#e«»A* «f*» er^-wi V.»* # •• I
«««*..•. tf» Aii^
i^wtwdo d» A^dCR» ^waw» ^ ¿ f
C-CK»*^''cete-.iBAtt ^

Recjrtot dr «T'e'idzJ^
RecufWí de «ipíeívíiíjje
•yr^. « *. (?I (*< , ^dCcr^Ck* tt* A9} p«r« /«eJJ
wéi Anpv« '
d» ifl P»rj »fJ' MíOOe-. .".T*
fc*crcMi« 0c API p#f« ••
.: j* R ^ »jc;«»c«>» * »r,;r4' íti*i6A dcptrei*—tn y icor ec . .v-rf ^.ív
Vid««»dc tty^' 'Aytúft m CK«iVic; e
teu UKV-e(<e • l'U **'

y
Figura 1.20. Página principal de Visual Studio Expresa 2012 para aplicaciones de escritorio Windows.
Al ejecutar la aplicación, sobre todo si es la primera vez que se accede al software y no se ha
configurado aún este, se muestra el entorno con una página de inicio llamada Página Principal.
Esta página pretende servir de ayuda al usuario de forma que pueda contar con todo tipo de
recursos y así mejorar sus aplicaciones, solventar problemas y sacar el máximo partido al IDE.
Si cerramos la página podemos volver a hacerla visible haciendo clic en Ver -> Página
principal.
La Figura 1.20 representa una captura de la página principal de Visual Studio. Las zonas
señaladas como ', v2/ y'..3. proporcionarán información al usuario sobre diversos aspectos:
1. Muestran información sobre las novedades de los diferentes lenguajes de
programación que se pueden usar en el DDE.
2. Zona de ayuda con instmcciones de cómo compilar y operar con la aplicación.
3. Zona de estudio y aprendizaje. Desde aquí podremos conocer los objetos que
posteriormente usaremos en nuestro código y solucionar problemas.
/"7^
La zona enmarca la barra de menús y herramientas. A lo largo del libro iremos utilizando
sus funciones de depuración, ejecución, etc.
En la zona v5/ encontraremos accesos directos para la creación de proyectos o apertura de
aquellos que se hayan generado con anterioridad. Además, los programas modificados
recientemente se visualizarán también aquí.
Finalmente, la zona "-I/ está compuesta por dos casillas de verificación que penniten
personalizar el inicio de Visual Studio Express 2012. Si permanecemos activa la casilla Cerrar
la página después de cargar el proyecto la página principal se cerrará para dejar paso a la
Capítulo 1. Introducción 31

nueva aplicación. En caso de mantener activa la casilla de verificación Mostrar página al inicio
estaremos confirmando que deseamos visualizar la página principal cada vez que accedamos al
programa. Estas características serán configuradas a gusto por el lector.

1.8.3. CREAR UN NUEVO PROYECTO EN VISUAL STUDIO


EXPRESS 2012
A la hora de comenzar a programar debemos crear un nuevo proyecto.
■ Si la Página principal es visible en la zona de la izquierda haremos clic en Nuevo
proyecto...
■ Si la Página principal se ocultó solo tendremos que hacer clic en el menú Archivo y
a continuación Nuevo proyecto...
Tras realizar una de las dos acciones anteriores veremos un cuadro de diálogo igual al que se
observa en la Figura 1.21.
Nuevo proyecto

Ordenar pon Predeterminado '.;~f Buicar en Ij Plantiilas instalad"'Ctrlȣ! "

I I Aplicación de Wndcws Forms Visual Bask Tipo: Visual Basic


Proyecto para crear una aplicación con
mVB una interíaz de usuario de Windows.
I I Aplicación WPF Visual Basic
Windows
Prueba Aplicación de consola
t Visual C»

í Visual C** Biblioteca de clases


Soluciones de Visual Studio

WindcwsApplicationl v ^

c:\u5er5M5aber\documcnts\visual studio ÍOlZiProjects I Examinar... j


Nombre de la soiuciorr: VVindcv.-sApplicationl ^ Crear directorio para la solución
f~i Agregar al control de codigo fuente

Aceptar |
j Cancelar

Figura 1.21. Cuadro de diálogo de Nuevo proyecto.

'1
1. Escogemos el tipo de proyecto que vamos a realizar en la zona Como ya se ha
estudiado en párrafos anteriores este IDE permite programar en Visual Basic, C-H- y
C#, así estos son los tres tipos de proyectos entre los que podremos elegir. Clic en
Visual C# y Windows.
2. Los iconos de la zona central variarán. La diferencia entre los proyectos que podemos
crear en Visual Basic y C# es mínima, básicamente el lenguaje de programación que
usaremos, sin embargo, si escogemos C++ para programar el abanico de posibilidad
se amplía pudiendo crear incluso archivos MAKE.
ACTIVIDAD 1.5

¿Qué es un archivo MAKE?

■ Aplicación de Windows Forms. Para desan-ollar aplicaciones con una


interfaz de usuario Windows compuesta por ventanas o también denominados
formularios.
32 Programación

■ Aplicación WPF. Aplicación cliente de Windows Presentation Foundation.


WPF es una tecnología de Microsoft que permite desarrollar interfaccs
interactivas, de gráficos bastante potentes que facilita la interacción del
usuario incluyendo animaciones, videos, audio, etc.
■ Aplicación de consola. Aplicaciones en modo texto. Al ejecutarlas veremos
£1 símbolo del sistema.

■ Biblioteca de clases. Permite la creación de bibliotecas o ficheros dll.

■ Proyecto vacío. Proyecto en blanco.


3. Para comenzar a usar el lenguaje y conocer sus estructuras de control escogeremos
Aplicación de consola. En la zona de la derecha ^-1' se mostrará infomiación
relacionada con la selección de proyecto que hagamos.
4. Antes de dar la orden de crear el nuevo proyecto debemos indicar su nombre, en el
cuadro de diálogo Nombre y establecer su ubicación, en el cuadro de diálogo con el
mismo nombre. Se aconseja crear una carpeta e ir ubicando en ella todos los
ejercicios que se vayan realizando a lo largo del libro.
5. Indicamos de igual modo el Nombre de la solución manteniendo pulsada la casilla
de verificación Crear directorio para la solución si queremos que esta acción se
produzca.
6. Clic en Aceptar.

NOTA: Visual Studio define contenedores conceptuales denominados proyecto y i


solución. Un proyecto está compuesto de un conjunto de ficheros de código fuente más '
una serie de metadatos (referencias a objetos, etc.). Una solución contiene un proyecto o i
varios que se encuentran relacionados entre sí más los metadatos y archivos que '
configuran la solución como un todo. \
g Una solución puede contener:aama !
I I— Proyectos m I
Figura 1.22. Esquema explicativo de Microsoft
t Elementos de una solución
| sobre proyecto y solución. Fuente:
http://msdn.mlcrosoft.com/es-
es/library/vstudlo/df8st53z(v=vs.100).aspx.

ACTIVIDAD 1.6

Accede a Visual Studio Express 2012 y crea un nuevo proyecto, aplicación de consola,
denominado HolaMundo. El nombre debe ser tal y como se indica, sin espacios en blanco.

1.8.4. ESTRUCTURA DE UN PROYECTO EN VISUAL STUDIO


EXPRESS 2012
Cuando creamos un nuevo proyecto o abrimos uno existente, en la zona de la derecha
solemos encontrar la ficha Explorador de soluciones. En ella visualizaremos todos los ficheros
Capítulo 1. introducción 33

que foirnan el proyecto y la solución a la que está asociado. Es similar al explorador de Windows
que conocemos ampliamente.
Entre los elementos que fonnan el proyecto
(2 'Q - í? « QÜ @1 A[P encontramos:
Euscjr en el E>plcradcr de sclucicnes (CtrI* }
Solución'HolaMundo' Proyecto) ■ Properties. Propiedades de los
elementos del proyecto. Es una
t> Properties
í> References caipeta. Si estamos generando una
Q App.config aplicación de consola solo
> C" Program.cs
encontraremos en esta carpeta el
fichero AssemblyInfo.es.
■ Rreferences. Referencias extemas,
código generado con anterioridad y
que podemos usar libremente en
nuestro proyecto.
Figura 1.23. Explorador de soluciones de Visual
Studio Express 2012.

■ App.config. Fichero XML de configuración de la aplicación.


■ Program.cs. Es el programa en sí, el fichero que contiene el código fuente. Si lo
expandimos podremos acceder a cada parte del mismo de fomia rápida.
En la zona central del entorno de desarrollo veremos el fichero de código fuente preparado
para ser editado. La imagen que observaremos será similar a la que se muestra en la Figura 1.24.

X —— —'^1 / En ^i.-' se lista una serie de referencias,


-usxng system; f clcmcntos que se usarán en el código, de ahí
using Systeir.Collections.Genenc; , ,
using systeir.Linq; quc comicncen con using. A continuación se
using sy5ten-..Text; dcclara un nuevo espacio de trabajo, que se
. using Systeic.Threading.TasKs; ii . j' i
(2 ^ llamara como hayamos dispuesto que se
S^ natnespace Hola."undo .
{ s denomine el proyecto, ^t./ (3# encapsula
- ciass p-cg-aT ^ ^ cualquier objeto en lo que en programación
:■> static void Hain(string[] args) ^ " denomínaiTios Clasc, de ahí que en U' se
■ } incluya el texto class program. Para finalizar,
j ^ programa ejecutará el contenido de la
función main '.
Figura 1.24. Código fuente C#.
Se han nombrado muchos conceptos que aún no se han definido pero que se entenderán más
adelante.

1.8.5. GUARDAR Y ABRIR UN PROYECTO EXISTENTE EN


VISUAL STUDIO EXPRESS 2012
El proceso de guardar y abrir un proyecto es bastante sencillo, y similar a las formas de
guardar y abrir que utilizan otras aplicaciones Windows.
Para guardar un proyecto:
■ Si queremos sobrescribir el proyecto con las nuevas características y modificaciones
haremos clic en Archivo y Guardar Nombre_Proyecto.
■ Si deseamos guardar el proyecto con las nuevas modificaciones en otra ubicación o
con otro nombre o ambas cosas, haremos clic en Archivo pero en esta ocasión
seleccionaremos Guardar Nombre_Proyecto como...
La orden guardar está relacionada con el elemento que tengamos activo en cada momento, es
decir, se guardará el fichero que esté visible o seleccionado de la lista de proyectos. Así, Visual
Studo Express 2012 proporciona la opción Guardar todo para almacenar todos los cambios de
todos los ficheros que integran el proyecto. Si hemos seleccionado la carpeta del proyecto al
hacer clic en guardar se almacenará toda la información del mismo.
La operación de apertura puede darse haciendo clic en Abrir proyecto o Abrir archivo del
menú Archivo.

1.8.6. EJECUTAR UN PROYECTO COMPILADO SIN ERRORES


Una vez hayamos creado nuestro proyecto tal que incluya una clase principal, clase que
contenga la función MainQ, podremos pasar a ejecutar este y comprobar su funcionamiento,
siempre y cuando el código fuente esté libre de errores de compilación.
La ejecución de una aplicación en Visual Studio se realiza a través del botón de la barra de
herramientas Iniciar o bien en el menú Depurar Iniciar depuración.
Aunque el proceso de compilación se lleva a cabo de forma automática, ya que al escribir
nuestras sentencias observaremos los posibles errores, léxicos, sintácticos o semánticos,
podremos dar orden de compilar a partir del menú Compilé'" Compilar Solución.
1.9. ENTORNO DE DESARROLLO NETBEANS
Como ya se ha comentado, además de C# vamos a estudiar Java y sus características. La
aplicación o EDE más usado junto a Eclipse para la creación de código Java es Netbeans. Por eso
creemos oportuno que el lector, además de mantener instalado en su PC Visual Studio Express
2012, proceda a la creación de código Java sobre esta plataforma.
Netbeans es un IDE gratuito, que se distribuye para diferentes platafomias, es decir, puede
usarse en Windows, Linux, Mac OS X, etc. Actualmente está disponible la versión 7.3 del
software (junio de 2013). Existen diferentes paquetes que se distinguen según su funcionalidad o
características. Todos ellos pueden descargarse desde su web oficial:
https://netbeans.org/downloads/.
Ya que sólo vamos a usar la plataforma para programar en Java, vamos a proceder a la
descarga de Java SE, si posteriormente quisiera agregar funcionalidad al entorno de desarrollo
integrado podré hacerlo mediante el acople de nuevos módulos. Java SE dispone de:
■ NetBeans Platform SDK.
■ Java SE.

■ Java EX.
Capítulo 1. Introducción 35

NOTA: SDK (Software Development Kit o Kit de Desarrollo de Software). Podemos


decir que es un conjunto de herramientas que incluyen elementos para programar y
realizar aplicaciones en un sistema concreto. Básicamente una API (Application
Programing Interface o Interfaz de Programación de Aplicaciones) con herramientas para
compilar, depurar, etc.

Para poder usar Java como lenguaje de programación, además del paquete de Netbeans
debemos descargar el JDK actual, acorde con la versión del IDE que hayamos descargado.
Las siglas JDK proceden de Java Development Kit, es decir Kit de Desarrollo para Java. Es
un software que incluye hen-amientas de desarrollo para la creación de aplicaciones Java. Así,
cuando vayamos a proceder con la instalación de Netbeans en primer lugar debemos haber
instalado el JDK correspondiente en nuestro equipo.
Podemos descargar el JDK actual desde el sitio URL que nos aconseja la web oficial de
Netbeans, http://java.sun.com/javase/downIoads/index.jsp. De fonna alternativa, y en lugar de
realizar la descarga de Netbeans y JDK por separado podemos descargar el pack con ambos
elementos desde la web http://java.sun.com/javase/downIoads/widget/jdk_netbeans.jsp.
Dicho todo esto, y presuponiendo que el lector no tiene instalado ni la versión JDK ni
Netbeans, vamos a proceder a descargar el paquete que contiene ambos elementos desde la
segunda dirección web propuesta en el párrafo anterior:
http://java.sun.com/javase/downloads/widget/jdk_netbeans.jsp.

; Java 6 and later versions are required for installmg and running the PHP and C/C++
i NetBeans Bundies, You can download the latest Java at lava con
i -
I JDK 6 and later versions are require/ 1 ^stalling and running the Java SE,Java EE and
, AJI ttetBeans Bundies You can tíownVtad standalone JDK or dovvnioad the latest.ink
: with I lelEeans IDE Java SE Ouncie. ^

Figura 1.25. URL https://netbeans.org/downloads/ de descarga^e software. En la imagen se observa la


zona inferior donde se indica donde localizar el JDK o el pack de IDE+JDK

1.9.1. INSTALACIÓN DE NETBEANS 7.3 + JDK


La instalación de Netbeans es sencilla. Ya que hemos descargado el pack completo donde
incluimos el JDK y será esto lo primero que se copia a nuestro equipo.
1. Doble clic sobre el fichero descargado. Si no hemos modificado el nombre de
descarga este fichero se llamará algo asi como jdk-6u45-nb-7_3-wlndows-i586.exe.
2. Tras la configuración del instalador aparecerá la primera ventana del asistente de
instalación. Aceptamos los términos de licencia e indicamos la ubicación (directorio)
donde queramos que se almacenen los arehivos del JDK.
3. Escogemos el directorio donde ubicaremos los ficheros del IDE.
36 Programación

4. Aparecerá una ventana informativa en la que entre otras cosas se orienta al usuario
sobre el espacio en disco necesario para la instalación. Hacemos clic en Install y
comenzará el proceso de forma instantánea.
5. Pasado un tiempo que dependerá del equipo que estemos usando podremos comenzar
a usar el entomo de desarrollo.

1.9.2. PANTALLA INICIAL DE NETBEANS 7.3

jRle Edh View Nsvigate Source Refactcr Run Oebug Profiie Team Toob Wíndow Hdp f X ¡
Q d ti : Kí C' B ¿3 I-' i'-i> * *.J)
'
|fac iáe Vaw KtwígMt Sowct Rcf*ctor Rur» Oebug Profiic Tom Tnná^lYirif|riwí Hdp
i f; £J «í % e- • 's 2.Í'•0-

Demos & Tutorials Featured Demo


TriraSarnpftProiect
Jxa SE «iptacadoni Error «th<e toadng coriient
Java ano JaoTx OUi «ap^^bcns
Contmtat^ Comer Ja/a EE & Ja/a V/»b Ap^'caaoai

CK** AfpbcaStons

PHP ^t<>{ilie9>0AS
Uotde ano E moeooeo i>ccu»ons

Al Oolne Docvmeruabon »

Figura 1.26. Pantalla de inicio de Netbeans.

Cuando ejecutamos Netbeans por primera vez la Figura 1.26 nos indica qué visualizaremos.
Al igual que ocum'a en Visual Studio veremos una Start Page o Página de inicio donde se
proporciona información al usuario sobre el IDE así como la fonna de utilizar este.
Encontraremos enlaces, tutoriales y demos bastante interesantes En la zona superior se ubica
la barra de menús y herramientas desde la que accederemos a toda la funcionalidad del software,
ya estudiaremos cómo hacer uso de algunos de sus comandos para compilar o depurar nuestros
programas ^l'. Para finalizar, la zona representada como solo apunta a la casilla de
verificación por la que podemos hacer que esta pantalla de inicio sea siempre o no visible cuando
accedamos a DDE.

Al cerrar la Start Page, en la zona de la izquierda podremos observar el explorador de


proyectos, archivos y servicios desde el que seleccionaremos un fichero u otro de nuestro
proyecto con facilidad.
Al igual que Visual Studio, Netbeans trabaja con contenedores que agrupan todos los
elementos necesarios para nuestra aplicación.
Capítulo 1. Introducción 37

1.9.3. CREAR UN NUEVO PROYECTO EN NETBEANS 7.3

Para crear un proyecto en Netbeans debemos:


1. Clic en File y a continuación en New Project...
2. Aparece el asistente de configuración de nuevo proyecto. En la primera de sus
ventanas se debe especificar la categoría y tipo de proyecto que queremos realizar.
IO Pr05«t

t. Choo«« i*raj««t Categorg*: Prejecta:

I j 1^ Qt A0pkaaon
I j ^ CaS5 Lfcirary
^ Pro^t mCM Cxstng Soeces
.J. Mívrn
JavARwPOfmProject
1^ MedJes
... u i

I Create* a new Java SC applKaOon n a standard ID6 pro^t, You can alse penefate a man daas
rt the pro)cct. Standard prp^rcts use an IDt-ocnrrated Ant build «cnpt r^,«nd debup
yc«.r provecí-

Figura 1.27. Cuadro de diálogo de nuevo proyecto.


/'7\
3. Entre las categorías, ^í.', podemos elegir Java, JavaFx, Netbeans Modules, etc. La
categoría JavaFx genera aplicaciones Java con características JavaFx activas. JavaFx
refiere una serie de productos y tecnologías que se usan para crear aplicaciones web
que poseen las caracteristicas y capacidades de aplicaciones de escritorio. A no ser
que se especifique lo contrario, a la hora de generar proyectos en Java en este libro
usaremos la categoria Java.
f -y ^
4. Cada categoría propone una serie de tipos de proyectos Si se desea saber más
sobre ellos podemos observar la parte inferior de la pantalla, el recuadro Descripción,
o bien podemos buscar infonnación en línea. Seleccionamos Java Application y clíc
en Next.

5. Al igual que en Visual Studio, en Netbeans debemos especificar Nombre del


proyecto. Ubicación del proyecto y si se debe crear una Clase principal, esto es, un
fichero cuyo contenido será procesado nada más se de orden de ejecución del
proyecto.

NOTA: Aún muchos de los conceptos que se están tratando pueden resultar extraños !
e incomprensibles para el lector. Rogamos que tenga paciencia, en este apartado estamos |
estudiando de fonna escueta los IDE que vamos a usar y las funciones principales de j
estos, pero será en otros capítulos cuando todos los contenidos se afiancen. Por lo pronto i
sólo debemos entender dónde hacer clic para generar un nuevo programa, y el tipo de
proyecto y propiedades que activar, más adelante entenderemos el porqué.
6. Estableceremos como nombre de proyecto HoIaMundo (sin espacio en blanco entre
palabras), como ubicación aquella que queramos, sería interesante crear una carpeta
nueva donde albergar todos los proyectos Java. Por último, mantenemos activa la
casilla Crear clase principal (Create Main Class).
7. Clic en Finish. Tras unos segundos veremos en el explorador de proyectos el nuevo
proyecto que acabamos de generar.

Ñame ano Location


Nombre del proyecto
1. Choosa Project Project fiame; Ho¡aMLrd^
2. Kameand Location

ProjectLocabon: C:VJ5ers\:5á)dV>xxmcnt5YíetSedf«Pro)ccts

Prcoect F^der; CrVJsersVsabdPoojnentsVtetSc


I Ubicación del i :-7 i.'—

; 1 Use Oedicated Foídcr toe Stonrig Lbranes

Líxa-iesFocer:

Offerent users and pr^ects can ^lare the same complation


fttraries(see Heb hr(¿tais).

«^ Create Main Oass holamundo.HotaMundo

<6ack r4£>t > fVüsh Cancd

Figura 1.28. Pantalla de configuración de características del nuevo proyecto del asistente de creación de
proyecto nuevo.

; Explorador de proyecte^J
SmwAmm Cm 0*1^ Uwm IMH W>í<íii
tiEjüCíi
•) Smtitge_ m ¿«WMfidajM
t iJ» j Htfcr» 'li'iy!• ^u &i

Proyecto
HoIaMundo

etwf Hela#

»fa34a rúa kuaimiMÍI a**ai <


■ J ícrí- .í- i.;;-

Elemento del proyecto


HoIaMundo

Figura 1.29. Ventana de Netbeans una vez se crea un nuevo proyecto


Capítulo 1. introducción 39

1.9.4. ESTRUCTURA DE UN PROYECTO EN NETBEANS 7.3

El proyecto en Netbeans se caracteriza por


Proj— s I Files i Services I
el símbolo de taza de café representativo del
B ^ HolaMundo
0 i¿] Source Packages lenguaje de progi-amación Java.
E t±j holamundo
Veremos inicialmente dos carpetas, Source
HolaMundo.java
±j a Ubraries
Packages, donde se almacenan los ficheros de
código fuente. Estos están agrupados por
paquetes que ya estudiaremos. La segunda
Figura 1.30. Proyecto HolaMundo visto desde el carpeta contiene las librerías Java disponibles
explorador de proyectos. para usar en nuestro programa.

Sabías que...
Muchos especulan el porqué del símbolo de taza de café o grano de café cuando
nos referimos al lenguaje de programación Java. Parece ser que una de las hipótesis
; que pisan con más fuerza es la de que el nombre Java procede de un tipo de café que
i
' tomaban los desarrolladores del lenguaje en una cafetería cercana. Si nos fijamos en los
primeros 4 bytes del fichero .class generado por el compilador observaremos que en
! hexadecimal aparecen como OxCAFEBABE.

1.9.5. GUARDAR Y ABRIR UN FICHERO EXISTENTE JAVA


NETBEANS 7.3

Podemos almacenar un fichero concreto sobrescribiendo su eontenido, guardar este con otro
nombre o guardar todos los ficheros que hayan sido modificados al mismo tiempo. Para ello
tenemos desde el menú File las opciones:
■ Save. Almacena el fichero que esté seleccionado.
■ Save as...Para guardar un fichero con otro nombre u otra ubicación.
■ Save all. Pemiite almacenar todos los ficheros que hayan sido modificados
reemplazando sus contenidos por las nuevas modificaciones.
De igual modo podremos abrir un nuevo proyecto a partir de File Open Project...
Además encontraremos opciones de importación y exportación tanto en Netbeans como en
Visual Studio. Son interesantes cuando queramos agregar elementos a nuestros proyectos.
1.9.6. EJECUTAR UN PROYECTO COMPILADO SIN ERRORES EN
NETBEANS 7.3
Netbeans es un entorno de programación que al igual que Visual Studio proporciona
infonnación en tiempo real de los fallos de compilación que se vayan produciendo. Veremos una
linea de color rojo en la zona errónea, subrayando esta, como hacen muchos editores de texto
cuando detectan palabras con faltas ortográficas.
A la hora de ejecutar una aplicación en Netbeans localizaremos
en la barra de herramientas el botón Run Proyect caracterizado con
una flecha de color verde. O bien accederemos al menú Run y Figura 1.31. Botón de
seguidamente Run Project. ejecución.

COMPRUEBA TU APRENDIZAJE
1. ¿Qué diferencia existe entre un algoritmo y un programa?
2. ¿Cuáles son las características de un programa?
3. ¿Qué se entiende por lenguaje de programación? ¿Qué diferencia existe entre un
lenguaje compilado y un lenguaje interpretado?
4. ¿Qué se entiende por código fuente? ¿Y código objeto?
5. ¿Cuáles son las fases de creación de un programa?
6. ¿Qué es un diagrama de flujo? ¿Qué se entiende por falso lenguaje?
7. ¿En qué consiste la fase de análisis léxico en el proceso de compilación?
8. ¿Qué es un IDE?
9. ¿Qué entiende Visual Studio por proyecto y solución?
10. ¿Qué pasos hemos de seguir para crear un nuevo proyecto en NetBeans?

ACTIVIDADES DE AMPLIACION
1. En el capitulo hemos hablado de la existencia de "enlazadores o linkers" en el
proceso de creación del programa ejecutable. Busca un poquito más de infonmación
sobre estos elementos, cómo funcionan, características, etc., y desarrolla un
documento breve donde incluyas todo lo que hayas localizado.
2. Imagina que te llaman de una empresa de venta de vinos para realizar un pequeño
programa. El software debe gestionar los clientes que acceden a la empresa. En la
fase de análisis, ¿qué preguntas realizarías a la persona que te encarga la realización
del trabajo? Desarrolla un pequeño esquema donde expliques con tus palabras qué
acciones ejecutaría el programa, qué información almacenaría, etc.
3. Ahora supón que trabajas para una empresa dedicada a la formación y quieren
comenzar a trabajar con enseñanza on-line. Te piden diseñar un programa para que el
alumno pueda contactar vía web y a partir de ahí comenzar a recibir la clase. ¿Qué
tipo de preguntas realizarías en la fase de análisis para conocer en profundidad lo que
Capítulo 1. Introducción 41

el usuario final te está pidiendo? Desarrolla un esquema donde indiques los elementos
que incorporarlas al programa y cómo lo estructurarías.
4. Crea un proyecto de consola en Visual Studio llamado Proyecto_Pruebal.
5. Realiza la actividad anterior en Netbeans.

6. C++ es una de los lenguajes de programación más usados. Investiga sobre él y


desarrolla un documento breve donde indiques sus características y uso principales.
7. En programación declarativa uno de los lenguajes de programación usados es prolog.
Realiza un documento breve donde hables sobre él. ¿Qué diferencias encuentras con
C++?
CAPÍTULO 2

METODOLOGIA DE LA PROGRAMACION

CONTENIDOS / OBJETIVOS
Pseudocódigo y diagramas de Conocer los elementos
flujo. fundamentales de los diagramas de
flujo y comenzar a usarlo.
Variables y constantes.
Saber qué son sentencias
Operadores.
alternativas: simple, doble y
Sentenicas de control múltiple.
alternativas.
Conocer las diferentes sentencias
Sentencias de control: bucles. repetitivas que nos podemos
encontrar.
Teorema de la programación
desarrollada. Conocer el teorema de la
\ programación estructurada.

RESUMEN DEL CAPÍTULO


Este capítulo es de gran importancia ya que se enseñan al lector las pautas esenciales
para realizar programas. Se estudian las diferentes sentencias de control, se realizan paso
a paso algoritmos resueltos, asentándose las bases de la programación estructurada. Si se
entienden los conceptos aquí mostrados la programación de aplicaciones C# y Java no
resultará compleja.
44 Programación

2.1. INTRODUCCION
En el Capítulo 1 se estudiaban conceptos informáticos relacionados con la programación de
aplicaciones, se introducían elementos como diagramas de flujo y pseudocódigo y se empezaban
a instalar los EDE que posteriormente usaremos para la generación de código C# y Java.
En este capitulo que acabamos de comenzar se pretende enseñar al alumno una serie de pautas
para realizar algoritmos que posteriormente puedan ser codificados en cualquier lenguaje de
programación, haciendo uso de diagramas de flujo y pseudocódigo. Además, se estudiarán las
sentencias de control y elementos más comunes en un algoritmo, como variables, constantes,
sentencias condicionales, repetitivas, etc.
Asi, el objetivo de este capitulo es asentar las bases de la programación, de forma que el
lector sepa a ciencia cierta plantear el algoritmo de un problema dado siguiendo una serie de
pasos por los que:
1. Analizará el problema planteado.
2. Diseñará un diagrama de flujo.
3. Traducirá el diagrama de flujo a pseudocódigo.

2.2. PSEUDOCÓDIGO
El pseudocódigo se puede definir como un lenguaje intermedio entre el lenguaje natural y el
lenguaje de programación que usaremos posteriormente para generar nuestro programa. Es un
lenguaje, también denominado falso lenguaje, que permite generar algoritmos dando soluciones a
problemas planteados. Este falso lenguaje, muy similar al lenguaje que usamos non-nalmcnte,
está sujeto a unas reglas concretas.

2.2.1. ELEMENTOS ATENER EN CUENTA EN EL


PSEUDOCÓDIGO
A la hora de generar un algoritmo en pseudocódigo se deben tener en cuenta que:
■ Las sentencias, al igual que en la mayoría de los lenguajes de programación, se
ejecutarán de arriba abajo, y se irán leyendo de una en una. Una sentencia es una
"frase" del algoritmo en la que se lleva a cabo una acción concreta o se realiza alguna
operación. Por ejemplo. Leer nota o a = b + 2.
■ Una sentencia comenzará a ejecutarse cuando la que se encuentra justo antes ha
finalizado.
■ Las sentencias finalizarán con el signo punto y coma (;).
■ En pseudocódigo definiremos lo que se denomina entorno. Un entorno agrupa los
objetos denominados variables y constantes.
■ Usaremos las palabras Inicio y Fin para establecer los limites del algoritmo.
■ Entre los diferentes tipos de sentencias encontramos:
Sentencias de asignación.
—>• Sentencias de entrada/salida de datos.
Capítulo 2. Metodología de la programación

Sentencias de control.

• Alternativas.

• Bucles.

2.2.1.1. EL ENTORNO

El entorno no es más que un grupo de objetos que usaremos para almacenar información de
algún tipo. Estos objetos se denominan variable y constante.
Una variable es un contenedor donde guardaremos un valor. Este contenedor será
referenciado mediante un identificador o nombre. El valor de este contenedor puede variar
durante la ejecución del programa.
Si usamos un ejemplo de la vida cotidiana podemos comparar una variable con una caja de
cartón sin cerrar. La caja, al iniciar el día puede estar vacía, a las 2 de la tarde contener un par de
libros y a las 8, los libros pueden haber sido extraídos y en su lugar ubicar una muñeca. A su vez
podemos escribir un nombre en el exterior de la caja, por ejemplo "Mis cosas". Así,"Mis cosas"
es el nombre o identifícador de la caja o variable mientras que los libros, la muñeca e incluso el
estado de vacío son los elementos que puede contener o los valores que puede alcanzar.
Una constante es similar a una variable en el contexto de que almacenan un valor y es
referenciada mediante un nombre, sin embargo este valor no variará a lo largo de la ejecución del
algoritmo. Si tenemos una constante llamada PI y al comenzar el pseudocódigo hemos
establecido que su valor es 3.1416 este será el dato que almacenará durante toda la ejecución.
Si volvemos a nuestro ejemplo de la caja de cartón, imaginemos que al despertar hemos
cogido una caja de cartón, hemos metido dentro un libro, se ha realizado una pequeña abertura en
uno de los lados, se ha rotulado con el nombre "Mis cosas" y se ha cerrado con cinta adhesiva.
En esa caja de cartón no podremos ni extraer ni insertar otros objetos, sin embargo siempre
podremos ver su contenido, además se llama "Mis cosas", hemos creado un objeto constante,
cuyo estado no variará a lo largo del tiempo.
A la hora de declarar los objetos del entorno en pseudocódigo':
■ Comenzaremos con la palabra Entorno seguida de dos puntos (:).
■ Tanto constantes como variables deben especificar el tipo de datos^ que van a
almacenar.

■ Para definir una constante usaremos la palabra reservada constante, seguida del tipo
de dato y del nombre. La sentencia finalizará con la asignación del valor deseado.
■ Para definir una variable especificaremos el tipo de datos que va a almacenar seguido
del nombre de la variable. En caso de que queramos asignar un valor inicial a esta
usaremos el símbolo igual seguido del valor correspondiente.

'Existe otra forma definir los objetos en un entorno en pseudocódigo pero se ha escogido esta debido a
que es la que se encuentra más acorde con los lenguajes de programación que se van a estudiar.
^ Tanto Java como C# son lenguajes de programación tipados, es decir, a la hora de declarar las
variables y constantes deben indicarse explícitamente el tipo de datos que van a almacenar. Existen otros
lenguajes que no lo son, por ejemplo PHP. En PHP el tipo de la variable se establece cuando se le asigna
un valor.
Declaración de una constante: i constante tipojiato nombre_constante=valor;
_ , ., j . ., : tipo dato nombre variable;
Declaración de una variable: : . r ~ ,
: tipo dato nombre variable=valor;
En pseudocódigo las líneas se verían del siguiente modo:

Entorno:
constante tipo dato nombre_constante==valor;
tipo_dato nombre;
tipo_dato nombre=valor;
Un ejemplo de algoritmo real sería algo así como lo que se muestra a continuación:
Entorno:
constante real PI=3.1416
entero radio=2;
real area;

En estas declaraciones de objetos, debemos tener en cuenta que:


■ Los tipos de datos de las variables se escribirán en minúsculas.
■ Las palabras reservadas del pseudocódigo, en este caso constante, también se
escribirán en minúsculas.

■ Los nombres de constantes se escribirán con todas sus letras en mayúsculas.


■ Los nombres de las variables se escribirán con la primera letra en mayúsculas. En
caso de que el nombre esté compuesto por más de una palabra no pondremos espacios
en blanco, distinguiremos estas colocando la primera letra de ambas en mayúsculas,
por ejemplo, AreaCirculo. Además, evitaremos usar letras como la ñ y acentos en las
vocales o cualquier otro tipo de signo de puntuación, ya que aunque aún estamos
usando un lenguaje intermedio de creación de algoritmos debemos ir
acostumbrándonos a las reglas que luego serán más restrictivas en los lenguajes de
programación que usaremos.

NOTA: El uso de mayúsculas o minúsculas en los nombres o identificadores de


variables es un poco cuestión de gustos. Existen desarrolladores que prefieren denominar
las constantes y variables con todas sus letras en minúsculas, en lugar de unir las palabras
en nombres compuestos y usar el guión bajo, etc.

La declaración de variables y constantes suele realizarse al inicio del pseudocódigo.

2.2.1.2. SENTENCIAS DE ASIGNACIÓN


Una sentencia de asignación es aquella que permite el almacenamiento de un valor en un
objeto del lenguaje denominado variable o constante. Ya hemos usado el término
subconscientemente en el apartado anterior cuando declarábamos objetos del entorno y le
proporcionábamos un valor inicial.
La asignación se realiza con el uso del símbolo igual (=). Un ejemplo de asignación sería
Radio=2; Podemos modificar el valor de una variable en cualquier parte del código.
Capítulo 2. Metodología de la programación 47

; Entorno:
I constante real PI=3.1416
; entero radio=2;

ALGORITMO RESUELTO 1

ENUNCIADO

Imaginemos que se nos pide la ereaeión de un algoritmo que debe contener una constante
llamada gravedad a la que asignaremos el valor 9.8 y las variables velocidad y tiempo, esta
última tendrá asignado el valor de 2.
DESARROLLO - FASE DE ANÁLISIS
Este es nuestro primer algoritmo y debido a que aún no hemos estudiado demasiadas
secuencias o instrucciones es muy simple. Lo primero que debemos hacer es analizar el
problema:
■ Leemos tantas veces como nos sea necesario el enunciado.

■ Una vez lo hayamos entendido pasamos a exponer nuestras conclusiones. Estas son:
—> Tenemos que crear una constante (gravedad) y asignarle 9.8.
Tenemos que crear una variable llamada velocidad.
—> Tenemos que declarar una variable llamada tiempo que tendrá como valor
inicial el número 2.

DESARROLLO - FASE DE CREACIÓN DEL DIAGRAMA DE FLUJO


Aquí debemos retroceder al capítulo 1 y recordar cómo se configura un diagrama de flujo u
organigrama. El diagrama resultante de las especificaciones dadas seria el que se muestra en la
Figura 2.1.
Comenzamos y finalizamos el algoritmo con los
V símbolos de Inicio/Fin ^1''.

s En la zona central, insertamos una forma


^ -•' que se usa para establecer una instrucción, un paso
o un proceso, la creación de constantes y variables
lo son. En ella incluimos tres líneas, una por cada
objeto a declarar en el entorno.
Figura 2.1. Diagrama de flujo de un Todos los objetos están relacionados con líneas
algoritmo que sólo declara variables y una , i • j
constante tlecna que establecen el flujo de ejecución.
DESARROLLO - FASE DE CREACIÓN DE PSEUDOCÓDIGO
Llegados a este punto solo tendremos que repasar los Apartados 2.2.1.1 y 2.2.1.2 para
recordar cómo se deben escribir las líneas relacionadas con constantes y variables en
pseudocódigo, al igual que el proceso de asignación. Este ejemplo es muy sencillo, aún no se han
estudiado ninguna otra estructura o secuencia, pero desde ya empezamos a codificar y analizar la
infonnación. Es importante que el lector en sus primeros pasos en la programación realice este
proceso una y otra vez ya que será el que le ayude a entender todo tipo de problemas y codificar
estos en lenguajes de programación complejos y potentes. Esta es la base de cualquier
desarrollador de software.
Entorno:
constante real GRAVEDAD=9.8;
real velocidad^;
entero tiempo;

2.2.1.3. SENTENCIAS DE ENTRADA Y SALIDA DE DATOS

La comunicación con el usuario será esencial para cualquier programa que diseñemos en un
futuro. Normalmente necesitaremos interactuar con el usuario de fonna que establezca los
valores que quiere usar en el algoritmo a partir de los cuales se generarán diferentes datos de
salida.

Los valores de entrada en un programa real serán insertados a través de la linca de


comandos, cuadros de texto de formularios, listas desplegables, etc., siempre teniendo en cuenta
que para proceder a la introducción de la información se usarán los dispositivos periféricos
estándar de entrada, tales como el teclado y el ratón. El hardware de entrada puede ser
modificado, incluso podemos establecer como datos de entrada aquellos que se encuentren
almacenados en un fichero de datos.
En cuanto a los datos de salida serán aquellos generados por las operaciones que el propio
algoritmo realice sobre los datos de entrada insertados previamente. La salida estándar será la
pantalla aunque también puede ser modificada de forma que la información de salida sea impresa
o almacenada en un fichero de datos.
Indicaremos en pseudocódigo que se debe realizar una lectura o escritura usando las palabras
reservadas leer y escribir.
i Lectura: i Leer nombre_yariable
i, i Escribir valor
Un ejemplo de cada una de estas instrucciones es:
Entorno:
constante real PI=3.1415;
int radio;
Inicio:
Escribir "Introduce el radio";
Leer radio;

Debemos tener en cuenta que:


■ La orden de leer siempre requerirá de una variable donde almacenar aquello que se
recoge de la entrada estándar.
■ La orden de escribir requiere de cualquier elemento que pueda ser mostrado o
exteriorizado a partir de la salida estándar. Así, junto a Escribir podemos encontrar
una fi-ase, una variable, constante e incluso en ocasiones una instrucción.

^ La variable velocidad se ha establecido como real aunque en el enunciado no se deja claro de que tipo
debe ser. Podemos decir que los tres objetos interactuarán de una forma o de otra en el futuro, y debido a
esto y al existir una constante real la variable velocidad también deberá ser de este tipo.
Capítulo 2. Metodología de la programación 49

El elemento del diagrama de flujo


que representa la ejecución de
operaciones de entrada/salida de datos Figura 2.2. Forma que usan los organigramas para
indicar que se va a llevar a cabo una operación de
es el que se muestra a la derecha.
Entrada/Salida de datos.

ALGORITMO RESUELTO 2

ENUNCIADO

Imaginemos que se nos pide la creación de un algoritmo que debe contener una constante
llamada gravedad a la que asignaremos el valor 9.8 y las variables velocidad y tiempo.
Debemos solicitar al usuario que especifique qué valor de tiempo tenemos que usar.
DESARROLLO - FASE DE ANÁLISIS
Lo primero que debemos hacer es analizar el problema:
■ Leemos tantas veces como nos sea necesario el enunciado.

■ Una vez lo hayamos entendido pasamos a exponer nuestras conclusiones. Estas son:
Tenemos que crear una constante (gravedad) y asignarle 9.8.
—> Tenemos que crear dos variable llamadas velocidad y tiempo.
Debemos pedir al usuario qué dato asignar a la variable tiempo. La petición
siempre estará compuesta de un mensaje indicando que se quiere y la
posterior lectura del dato que el usuario final incluye. Así al pedir debemos
escribir en pantalla que tipo de petición estamos realizando y leer el dato
que el usuario haya introducido.
DESARROLLO - FASE DE CREACIÓN DEL DIAGRAMA DE FLUJO
En el punto realizamos la declaración del
4, entorno como explicábamos en el Algoritmo
resuleto 1. En la zona procedemos a la petición
^ de la infonnación, debemos pedir al usuario el
valor del tiempo, para ello:
j - " Le hacemos saber al usuario que
nwiaHiHHuniaB i i»|i pantalla la frase "Introduce el tiempo".
queremos, es decir, escribimos en

El: ^ continuación esperamos a que el


usuario responda escribiendo el valor
Figura 2.3. Diagrama de flujo para el que considere oportuno. Este valor será
algoritmo resuelto 2. almacenado en la variable Tiempo.

DESARROLLO - FASE DE CREACIÓN DE PSEUDOCÓDIGO


Debido a que este problema es continuación del algoritmo anterior parte del pseudocódigo ya
está escrito, sólo debemos agregar la parte correspondiente a la lectura/escritura de la
información.
50 Programación

Entorno:
/* Zona (1) del diagrama de flujos */
constante GRAVEDAD=9.8;
real Velocidad;
entero Tiempo;
Inicio:
/*zona (2) del diagrama*/
Escribir "Introduce el tiempo";
Leer Tiempo;
Fin;

Usaremos los símbolos /* y */ para encuadrar un comentario en nuestro pseudocódigo. Un


comentario es un elemento que no es procesado. Sirven al programador de ayuda a la hora de
procesar el código en búsqueda de fallos o realización de modificaciones.

2.2.1.4. SENTENCIAS DE CONTROL: ALTERNATIVAS


La palabra alternativa puede sugerir al lector "opción de escoger", y así es, esa es la esencia
de este tipo de sentencias.
En una instrucción alternativa se establece una condición o regla. El código a ejecutar
dependerá de si la condición establecida se cumple o no. El usuario de nuestro programa, a la
hora de ejecutarlo, decidirá qué "camino" tomar.

Existen sentencias alternativas simples, dobles y múltiples.


■ Alternativas simples. Son aquellas en las que se ejecutarán un conjunto de
instmcciones solo si la condición establecida es cierta. El diagrama de flujo para este
tipo de sentencias sería similar al de la Figura 2.4.
■ Alternativas dobles. En una alternativa doble se especifica qué hacer en caso de que
la condición sea verdadera o falsa, es decir, si la regla establecida es verdadeia se
ejecutarán una serie de instrucciones pero si es falsa el algoritmo tendrá preparadas
otras tantas para ejecutar. Es clara la diferencia con la alternativa simple, en la
primera si la condición es cierta se ejecutan instrucciones y si no, no se hace nada y
en una alternativa doble se hace siempre algo, se ejecuta siempre al menos una
instracción ya se cumpla o no la condición. La Figura 2.5 muestra claramente este
tipo se sentencias de control.
■ Alternativas múltiples. En una alternativa múltiple, la expresión o regla a comprobar
no devuelve los valores verdadero o falso, devuelve un valor incluido en un rango
establecido por el programador y según este se realiza una acción u otra. Por ejemplo,
imaginemos una instrucción alternativa múltiple que evalúe los días de la semana. Si
es lunes realizaremos una serie de acciones, si es martes otras, etc. Una sentencia
alternativa múltiple puede ser construida a partir de sentencias alternativas dobles y
simples anidadas. La Figura 2.6 muestra la estructura de este tipo de sentencias.
El objeto que representa en un diagrama de flujo una alternativa es el rombo. En su interior se
indica la regla, condición o expresión.
Capítulo 2. Metodología de la programación 51

^yCohdidón
Verdadera
verdadera

INSTRUCaÓNl INSTHUCCIÓNl
INSTRUC

.^ INSTBUCaÓNZ
IMCTC
INSTRUCaÓN2 *..t'

ucaóN
INSTRUCCIÓN N

RESTO DEL 'i


RESTO DEL
^ALGORITMO ALGORITMO

Figura 2.4. Alternativa simple. Figura 2.5. Alternativa doble.

'u > Expresión

Valor 1 Valor2 Valor n

INSTRUCCIONES >TN5T1lUCaON£S i; INSTRUCCIONES


L5iAií.íü.iri r.,

JPÍRE^DEl
ÍJsS^ÓRITMO
Figura 2.6. Alternativa múltiple.

Las palabras reservadas en pseudocódigo que se usan para definir alternativas son SI, SI NO,
entonces u OPCION.
Veamos como se configuran alternativas simples, dobles y múltiples en pseudocódigo.
Si CONDICION entonces
Instrucción 1;
Instrucción 2;
Alternativa simple:
Instrucción n;
FinSi;
52 Programación

Si CONDICION entonces
Instrucción I;

Instrucción n;
Alternativa múltiple
Instrucción I.

Instrucción n;
FinSi;

Opción EXPRESION de
valor I: Intrucción/Instrucciones(I);
valor 2:Intrucción/Instrucciones(2);
Alternativa múltiple:
valor n: Inti-uccion/InstruccionesCn);
otro: Intrucción/Instrucciones pot
defecto;
FinOpcion;

NOTA: En las sentencias múltiples, si se contemplan todos los valores y ninguno


coincide con el que se introduce en Expresión se da opción a que el algoritmo ejecute una
serie de instrucciones por defecto, por ejemplo, si esperamos en Expresión introducir un
día de la semana y el usuario escribe marzo, este valor no se encontrará entre los posibles
de forma que se puede configurar la zona otro (pseudocódigo alternativa múltiple) para
comunicar que el día introducido no es correcto.

Para finalizar véase los siguientes ejemplos de pseudocódigo. Sentencia alternativa simple:
Entorno: Creamos tres variables, denominadas A, B y
entero A,B,Resultado; Resultado.
Inicio:
Comienza el algoritmo. A la variable A se le
A=10;
Escribir "Escribe un asigna el valor de 10. A continuación se pide al
número"; usuario que escriba un número que será
Leer B; guardado en B.
Sentencia de control: alternativa simple. Se
analiza la condición B < A. Si el número
Si B < A entonces
introducido en la variable B es mayor que 10,
Resultado=A-B;
Escribir Resultado; que es el valor que contiene A, entonces
FinSi; realizaremos la resta de A y B y el resultado lo
almacenaremos en la variable Resultado para
posteriormente mostrarlo por pantalla.
Tras comprobar la condición y ejecutar la
Resultado=A+B;
Escribir "A+B=";
sentencia de control seguirá la ejecución
Escribir Resultado; normal del algoritmo. Se almacena en
Fin; Resultado la suma de A y B. Se escribe el texto
literal "A+B="junto al valor de Resultado.
Capítulo 2. Metodología de la programación

NOTA: Al usar la orden Escribir podremos mostrar una frase, una variable, etc., tal y
como se estudiaba en el Apartado 2.2.1.3. Las frases literales se escribirán entre comillas
dobles mientras que si queremos que se visualice el contenido de ima variable
escribiremos tan solo su nombre. Cuando el programa se ejecuta el nombre de la variable
es sustituido por su valor.

El diagrama de flujo de este algoritmo sería el que se muestra en la Figura 2.7.

Resultado

Escribir"Escribe un número*, ':'


Escribir "Escribe un número"
LecrB
LeerB

..

VERDADERO VERDADERO

Resultado=A^B
Resultado=A-B . Resultado=A-(-B

rResuicn
,' Escrlbir.*A-B=t.

^iResultado=A-t-B

Escribir"A+B="
/ Escribir R^ultado Escribir Resultado .

Figura 2.7. Diagrama de flujo del pseudocódigo de p¡gura 2.8. Organigrama de la sentencia alternativa
la sentencia alternativa simple. doble

Sentencia alternativa doble:


Entorno:
entero A,B,Resultado;

Inicio: La prímera parte del algoritmo es similar al


A=10; anterior.
Escribir "Escribe un
numero";
Leer B;
Sentencia de control: alternativa doble. Se
analiza la condición B < A. Si el número
introducido en la variable B es mayor que 10,
Si B < A entonces entonces realizaremos la resta de A y B y el
Resultado=A-B; resultado lo almacenaremos en la variable
Escribir "A-B=";
Sino
Resultado. Además, mostraremos por pantalla
Resultado=A+B; el texto "A-B=". Si la condición no se cumple,
Escribir "A+B="; es decir B es mayor que A, entonces
FinSi; realizaremos la suma de A y B y guardaremos
el resultado en la variable con este nombre.
Finalizaremos el bloque visualizando en
pantalla el texto "A+B="
Finalizamos el pseudocódigo mostrando el
Escribir Resultado; contenido de la variable Resultado en la
pantalla o dispositivo de salida estándar.

Sentencia alternativa múítipíe:


Entorno:
Creamos cuatro variables: Op, A, B y
entero Op;
Resultado.
entero A,B,Resultado;

Inicio: sé asigna un O a la variable Resultado y se


Resultado=0; pide al usuario que escriba un valor. El dato
Escribir "Opción:";
Leer Op; será almacenado en la variable Op.

Pasamos a analizar el valor que contiene


Op. Si Op=l realizaremos la suma de A y B y
guardaremos el resultado en la variable
Opción Op de: Resultado. En caso de que Op sea igual a 2,
1: Resultado=A+B; realizaremos la resta de A y B almacenando el
2: Resultado=A-B;
3: Resultado=A*B; resultado en Resultado. Con Op=3 y Op=4 las
4: Resultado=A/B; acciones serán similares, la diferencia estriba
Otro: Escribir en la operación a realizar, una multiplicación y
Opción incorrecta";
FinOpcion; una suma respectivamente.
En caso de que Op no se encuentre en el
rango de valores de 1 a 4 se escribirá en
pantalla el texto "Opción incorrecta".
Escribir "El resultado Para finalizar se escribirá el resultado
es:"; obtenido antecedido de la frase "El resultado
Escribir Resultado;
Capítulo 2. Metodología de la programación

I;' Escribir"Opción"
leerOp

Otro

Resultado=AH-B ■ Resultado=A-B ■ Resuttado=Á*B ■ Resuttado=A/B Escribir"Opdón


. incorrecta"

Escribir"El resuitadaTés:"
1% : \ EscribirResultado

Figura 2.9. Diagrama de flujo del pseudocódigo de sentencia altematíva múltiple.

A la hora de establecer la condición en las sentencias alternativas simple y doble hemos


tenido que usar el símbolo <. Este símbolo se denomina operador de comparación. Entre los
OPERADORES DE COMPARACIÓN encontramos los siguientes:
OPERADORES
DE SIGNIFICADO
COMPARACIÓN
Menor que
Mayor que
Igual a
Distinto de
Menor o igual que
>= Mayor o igual que
NOTA: En pseudocódigo usaremos el signo = (igual) tanto para realizar la asignación
como la comparación de valores. Cuando estudiemos los lenguajes de programación C# y
Java veremos que se realiza distinción entre ambas operaciones. Si queremos comparar
dos valores usaremos = = (doble igual) mientras que si en cambio procedemos a la
asignación utilizaremos solo uno.

Además, se han usado los denominados OPERADORES ARITMÉTICOS:


OPERADORES
SIGNIFICADO
ARITMÉTICOS
Suma
Resta
Multiplicación
División
ALGORITMO RESUELTO 3

ENUNCIADO

Imaginemos que se nos pide la creación de im algoritmo que debe contener una constante
llamada gravedad a la que asignaremos el valor 9.8 y las variables velocidad y tiempo.
Debemos solicitar al usuario que especifique qué valor de tiempo tenemos que usar. En caso de
que este valor sea menor o igual a O se mostrará el mensaje "Tiempo incorrecto" y finalizará el
programa. Si el usuario escribe un valor de tiempo correcto, es decir positivo, se calculará el
valor de velocidad (gravedad x tiempo).
DESARROLLO - FASE DE ANÁLISIS
Lo primero que debemos hacer es analizar el problema:
■ Leemos tantas veces como nos sea necesario el enunciado.

■ Una vez lo hayamos entendido pasamos a exponer nuestras conclusiones. Estas son:

Tenemos que crear una constante (gravedad) y asignarle 9.8. 1

Tenemos que crear dos variable llamadas velocidad y tiempo. L'


—> Debemos pedir al usuario qué dato asignar a la variable tiempo. Así se
visualizará en pantalla un mensaje del tipo "Introduce tiempo" para
posteriormente leer y guardar en la variable tiempo el valor que el usuario
haya escrito.
Comprobamos el dato de tiempo introducido. Si la variable tiempo contiene
un valor inferior o igual a cero acabará el programa mostrándose el mensaje
"Tiempo incorrecto",
Si el valor incluido en tiempo es positivo, realizaremos la operación
gravedad x tiempo, guardaremos el valor en la variable velocidad y
mostraremos esta por pantalla. ^1'
DESARROLLO - FASE DE CREACIÓN DEL DIAGRAMA DE FLUJO
Una vez desglosado el problema podemos generar el diagrama de flujo (Figura 2.10).
En primer lugar creamos la constante GRAVEDAD y las variables Tiempo y Velocidad ^ L'.
El valor de la variable Tiempo debe ser introducido por el usuario de forma que mostramos por
pantalla el mensaje "Introduce el tiempo" y lo almacenamos en Tiempo A continuación
debemos comprobar si la variable Tiempo es positiva y mayor que cero, para ello usamos un
rombo (forma del organigrama que indica decisión) con el texto Tiempo > 0. Si esta regla es
cierta realizaremos las acciones englobadas en es decir, realizaremos la operación
GRAVEDAD x Tiempo, guardaremos el resultado en Velocidad y visualizaremos el contenido
de esta variable en pantalla finalizando así la ejecución. Si por el contrario la regla Tiempo > O es
falsa se mostrará el mensaje "Tiempo incorrecto" como se indica en y finalizará el algoritmo.

Zona del diagrama de flujo del algoritmo resuelto 3, Figura 2.10.


Capítulo 2. Metodología de la programación 57

Figura 2.10. Diagrama de flujo del algoritmo resuelto 3.

DESARROLLO - FASE DE CREACIÓN DE PSEUDOCÓDIGO


Llegados a este punto, después del análisis y creación del diagrama de flujo solo queda hacer
fonnal el algoritmo mediante el uso de un lenguaje intennedio.
Entorno:

/*Zona (1)*/
constante real GRAVEDAD;
real Velocidad;
entero Tiempo;
Inicio:
/*Las siguientes dos lineas forman la zona (2)*/
Escribir "Introduce el tiempo";
Leer Tiempo;
Si Tiempo > O entonces
/*Zona (4)*/
Velocidad=GRAVEDAD*Tiempo;
Escribir Velocidad;
Sino
/*Zona (5)*/
Escribir "Tiempo incorrecto'
FinSi;

ALGORITMO RESUELTO 4

ENUNCIADO

Supongamos que se nos pide hacer un pequeño menú de tres opciones. Este debe visualizarse
como sigue:
1. Calcular diámetro.

2. Calcular circunferencia.
58 Programación

3. Calcular área.

El objetivo del algoritmo es que al iniciar se pida al usuario el radio de un círculo y a


continuación se muestre el menú. Si el usuario selecciona la opción 1 se pasará a calcular el
diámetro del círculo cuyo radio se ha especificado. Si seleccionamos la opción 2 calcularemos
su circunferencia. La última de sus opciones calculará el área. Tras cada cálculo el resultado
debe ser mostrado por pantalla.
DESARROLLO - FASE DE ANÁLISIS
Dado el enunciado procedemos a desglosar este.
■ Leemos el enunciado una y otra vez hasta que tengamos suficientemente claro que
debemos hacer.

■ Debemos pedir al usuario el radio de un círculo. A la hora de realizar una petición


siempre tenemos que: escribir por pantalla un mensaje indicando qué queremos y leer
lo que el usuario introduzca almacenando el valor en una variable. Así, debemos crear
una variable para guardar el radio que puede llamarse con este mismo nombre Radio.
Partes >^1'' y ^2'' del diagrama de flujo.
■ Tenemos que visualizar en pantalla una serie de opciones, 1. Calcular diámetros - 2.
Calcular circunferencia, etc. Por cada una de estas líneas debemos realizar una
operación de escritura cuyo mensaje será la opción del menú correspondiente. Zona
^2' del organigrama.
■ Cada opción conlleva una acción. Si el usuario selecciona la opción 2 del menú se
deberá calcular la circunferencia. Este punto y el siguiente se encuentran englobados
bajo el recuadro ¿Se tienen claras las fórmulas que se deben usar?
1. Diámetro = Radio x 2. Necesitaremos una variable llamada Diámetro donde
almacenar el resultado de Radio x 2. En la zona marcada como ^ L' en el
diagrama veremos a declaración de variables mientras la operación en sí se
realizará en la zona enmarcada con el número -^2^ Esto ocurre de igual modo
con las otras dos opciones.
2. Circunferencia = 2 x PI x Radio. Necesitaremos una variable.
Circunferencia, donde guardar el resultado de la operación. Además, P1 es
una constante matématica que posee el valor 3.1416 de fonua que en nuestro
algoritmo definiremos una constante llamada de igual modo y que
almacenará este valor.
3. Area = 2 x PI x R^ o lo que es lo mismo Area = 2 x PI x R x R. La
constante habrá sido ya declarada pues se necesitó en la opción anterior. En
este punto solo queda declarar una variable llamada Area para almacenar
precisamente esta característica del círculo.
■ Para cada opcion escogida mostraremos en pantalla el resultado de la operación
realizada. Orden Escribir de la zona
Capítulo 2. Metodología de la programación 59

■ La opción que el usuario escoja debe ser almacenada en una variable para
'T"*
posterionuente ser evaluada, por ejemplo Op. Variable declarada junto al resto,
Se usará como expresión en

NOTA: Aunque a la hora de analizar un problema la necesidad del uso de variables


sea descubierta en diferentes zonas, ubicaremos todas ellas al inicio.

NOTA: Debemos tener en cuenta que muchos de los problemas a resolver serán
problemas matemáticos de los que no sepamos sus fómiulas o simplemente no las
recordemos. Aconsejamos al lector que ese no sea motivo para desesperar ya que un
programador no tiene por qué saber qué significa una fónnula, solo debe entender cómo
: desarrollar esta en un lenguaje de programación concreto.

DESARROLLO - FASE DE CREACIÓN DEL DIAGRAMA DE FLUJO


En la fase de análisis conseguimos averiguar todos los elementos que vamos a necesitar,
variables, constantes, partes del algoritmo. La fase de análisis finaliza cuando no existen ningún
tipo de dudas sobre lo que se debe hacer. Se aconseja al lector que a la hora de ejercitar como
programador no sea impaciente y empiece a programar antes de haber solucionado todas las
dudas planteadas en la fase de análisis ya que empezar a desarrollar el diagrama de flujo o
pseudocódigo sin tener claras todas las opciones del algoritmo nos puede hacer retroceder
continuamente haciéndonos perder mucho tiempo.

J 4
DESARROLLO - FASE DE CREACION DE PSEUDOCODIGO

A partir de ahora solo tenemos que convertir a pseudocódigo cada una de las partes incluidas
en el diagrama de flujo. Hagámoslo en este algoritmo paso a paso;
Entorno:

constante real PI=3.1415;


entero Radios-
entero Dia.metro;

real Circunferencias-
real Areas-

entero Op;

Inicio:
Escribir "introduce el radio";
Leer Radio;
Escribir "1.Calcular diámetro";
Escribir "2.Calcular
circunferencia";
Escribir "3.Calcular área";
Leer Op;

Opcion Op de:

"1™ 1: Diametro=Radio*2;
Escribir "El diamtero es:";
Escribir Diametro;

2:
Circunferencia=2*PI*RadiOs-
Escribir "La circunferencia
es:";

imif Escribir Circunferencia;

3:Area=2*PI*Radio*RadiOs-
Escribir "El Area es:";
Escrinir Area;
FinOpcion;
Fin;

Si lo vemos en un solo bloque tendremos:


I Entorno:

i constante real PI=3.1415;


; entero Radio;
»

!
I
entero Diámetro;
; real Circunferencia;
»

[ _ real Area; (Continua en la siguiente página)^


Capítulo 2. Metodología de la programación

entero Op;
Inicio:

Escribir "introduce el radio";


Leer Radio;
Escribir "1.Calcular diámetro";
Escribir "2.Calcular circunferencia";
Escribir "3.Calcular área";
Leer Op;
Opción Op de:
1: Diametro=Radio*2;
Escribir "El diamtero es:";
Escribir Diámetro;
2: Circunferencia=2*PI*Radio;
Escribir "La circunferencia es:";
Escribir Circunferencia;
3:Area=2*PI*Radio*Radio;
Escribir "El Area es:";
Escrinir Area;
FinOpcion;

ACTIVIDAD 2.1

DesaiTolla un algoritmo que pida al usuario un número que represente una cantidad expresada
en pesetas. Si tenemos en cuenta que 1 euro = 166,386 pesetas, muestra por pantalla la
conversión a euros de ese número de pesetas.
ACTIVIDAD 2.2

Realiza el ejercicio anterior teniendo en cuenta que el número de pesetas introducido por el
usuario debe ser mayor que cero. Si se introduce un número positivo de pesetas se realizará la
conversión, en caso contrario no se hará ninguna acción.
ACTIVIDAD 2.3

Realiza la Actividad 2.1 de forma que la cantidad de pesetas sea positiva (Actividad 2.2) pero
en esta ocasión, si el número introducido es negativo o igual a cero se debe mostrar al usuario la
frase "El valor de pesetas establecido no es correcto".
ACTIVIDAD 2.4

Imaginemos que queremos desarrollar un algoritmo tal que al escribir un día de la semana
aparezca por pantalla la actividad extraescolar a la que debe acudir nuestro hijo. Por ejemplo, si
ejecutamos el programa y escribimos martes que aparezca "Natación". Las actividades que se
realizan cada día son: lunes - psicomotricidad, martes - natación, miércoles - música, jueves -
natación, viernes - descanso. Los días sábado y domingo no se realizan actividades con lo que si
el usuario escribe por error alguno de estos días de la semana se debe mostrar el mensaje "Día sin
actividades". Si por equivocación, además, se escribe un día inexistente se debe mostrar en
pantalla "Día erróneo".
62 Programación

2.2.1.5. SENTENCIAS ALTERNATIVAS CONCATENADAS

A la hora de generar algoritmos podemos concatenar las diferentes estructuras o sentencias


que lo fonnan, siempre que sean sentencias complejas o las denominadas sentencias de control
que agrupan trozos de código. En una sentencia de control alternativa se puede colocar otra, ya
sea simple, doble o miáltiple segtín las necesidades, por ejemplo, podemos crear una sentencia
con la condición "si el ntímero introducido es mayor que O" y en su interior evaluar a su vez si
este es "menor que 5","mayor que 5 y menor que 10" y "mayor que 10", véase la Figura 2.12.

Figura 2.12. Estructuras de control alternativas concatenadas. Concretamente en una alternativa simple se
ha incluido una serie de alternativas dobles.

El diagrama de flujo se ha desarrollado teniendo en cuenta el análisis previo que se hace al


ejemplo:
■ Se ha introducido un ntimero. Se crea una variable (Numero) y se pide su valor al
usuario (Escribir y Leer).
■ Se hace una primera comprobación. Si el niimero es mayor que O se ejecutará algo y
en caso contrario finalizará el algoritmo.
■ Si el ntimero es mayor que O vamos a realizar dos comprobaciones más. Estas
comprobaciones son: Numero < 5 y Numero entre 5 y 10. Así, si Numero es menor
que 5 escribiremos el texto "El ntimero es menor que 5" y si Numero es mayor que 5
pero menor que 10 escribiremos "El ntimero está entre 5 y 10". Si ninguna de las
comprobaciones es correcta el texto que visualizaremos será "El ntimero es mayor
que 10".
Capítulo 2. Metodología de la programación

En pseudocódigo, este ejemplo se desan-ollaría como sigue:

Entorno:
entero Numero;
Inicio:
Escribir "Introduce el número";
Leer Numero;
Si Numero > O entonces
Si Numero < 5 entonces
Escribir "El número es menor que 5";
Sino
Si Numero > 5 Y Numero < 10 entonces
Escribir "El número está entre 5 y 10";
Sino
Escribir "El número es mayor que 10";
FinSi;
FinSi;
FinSi;
Fin;

Vemos cómo las sentencias alternativas no terminan hasta el final del algoritmo, indicándose
eon esto que unas están dentro de otras y hasta que la que se encuentra en un nivel más interior
no finalice no finalizará la principal.
En este tipo de algoritmos es esencial el uso de diferentes niveles en el código para detectar
con claridad a qué sentencia de control pertenece cada conjunto de instrucciones, y si se
producen errores o debemos realizar modificaciones será más fácil realizarlas en algoritmos bien
estructurados. Veamos el mismo pseudocódigo pero sin estructura alguna:
Entorno: I
entero Numero; :
Inicio: I
Escribir "Introduce el número"; I
Leer Numero; I
Si Numero > O entonces \
Si Numero < 5 entonces i
Escribir "El número es menor que 5"; :
Sino i
Si Numero > 5 Y Numero < 10 entonces I
Escribir "El número está entre 5 y 10"; j
Sino I
Escribir "El número es mayor que 10 , i
FinSi; i
FinSi; 1
FinSi; ;
Fin; i

Es mucho más complicado saber qué pertenece a qué. En ocasiones, a la hora de establecer
anidaciones se usan líneas de unión entre el eomienzo y fin de cada sentencia alternativa, esto
mismo se observa en la Figura 2.13.
64 Programación

Entorno:
entero Numero;
Inicio:
Escribir "Introduce el número";
Leer Numero;
Si Numero > O entonces
Si Numero < 5 entonces
Escribir "El número es menor que 5";
~ Sino
Si Numero > 5 Y Numero < 10 entonces
Escribir "El número está entre 5 y 10'
Sino
Escribir "El número es mayor que 10";
FinSi;
FlnSi;
FinSi;

Figura 2.13. Sentencias alternativas simples y dobles anidadas con lineas de conexión destacando cada
sentencia de control.

Si CONDICIÓN entonces
Instrucciones;
Sino
Intrucciones;^
Si CONDICIÓN entonces
Instrucciones;
Si CONDICIÓN entonces

Sino

Sentencias de control alternativas FinSi;


concatenadas:
Sino
Intrucciones;
Si CONDICIÓN entonces

Sino

FinSi;
FinSi;
FinSi;

Las combinaciones de concatenación son elevadas. En capítulos posteriores estudiaremos


cuáles usar para conseguir algoritmos más óptimos y eficientes. En progi-amas extensos es un
elemento a tener en cuenta ya que debemos crear aplicaciones que se ejecuten rápidamente y

Podemos encontrar otras instrucciones en la sentencia alternativa además otras sentencias de control.
Capítulo 2. Metodología de la programación 65

ocupen pocos recursos del PC, la menor cantidad de memoria posible, menor tiempo de
procesador, etc.
Para finalizar, en el ejemplo de concatenación que hemos usado, se han utilizado condiciones
compuestas del tipo Si Numero > 5 Y Numero <10 entonces. En esta regla se observa un nuevo
elemento. Y, que pennite realizar varias comprobaciones en una. Al igual que el símbolo < y >,
la Y es un operador en programación denominado OPERADOR LÓGICO.
Existen tres operadores lógicos Y,O y No.
OPERADORES
SIGNIFICADO
LÓGICOS
La condición final será cierta si todas las
reglas que la fonnan lo son
La condición final será cierta si al menos
una de las reglas que la forman lo es
Invierte el significado de la condición. Si
la regla es cierta se convertirá en falsa y
viceversa.

OPERADORES
EJEMPLOS SIGNIFICADO
LÓGICOS
Si R1 Y R2 entonces
hacer A Solo si R1 y R2 son verdaderos se hará A, en
Sino todas las demás posibilidades se ejecutará B.
hacer B
FinSi
Si R1 O R2 entonces Solo necesitamos que una de las dos
hacer A condiciones sea cierta para que se ejecute A, si
Sino R1 es verdadera O R2 es verdadera ejecutaremos
hacer B A. En cualquier otro caso, es decir, cuando R1 y
FinSi R2 sean falsas se ejecutará B.
Si No(Rl)entonces Si R1 es verdadero el No hará que se
hacer A convierta en Falso con lo que si R1 es cierto se
Sino ejecutará B y en caso contrario será A el código a
hacer B ejecutar.
FinSi
Para finalizar se muestra una tabla por cada operador (tabla de verdad )donde se observan
todos los posibles estados que pueden alcanzar las reglas que lo usen estos operadores.
REGLÁl I REGLA2 I ^GLAÍ Y REGLÁ2 P ^G^ "1
Falso Falso Falso Falso
Falso Verdad Falso Verdad
Verdad Falso Falso Verdad
Verdad Verdad Verdad Verdad

Las tablas de verdad suelen usarse en programación declarativa para resolver problemas de lógica y
averigua si un esquema de inferencia es formalmente válido.
REGLAl No(REGLAl)
Falso Verdad
Verdad Falso

ACTIVIDAD 2.5

Tenemos una variable entera llamada Radio. Se le asigna el valor de 4. Según esta premisa,
¿serán verdaderas o falsas las siguientes sentencias? Justifica tu respuesta.
a) Radio > 1 Y Radio < 4.
b) Radio > 1 y Radio <= 4.
c) Radio = 3 O Radio = 5.
d) Radio = 3 O Radio = 4.
e) No (Radio < 10).
f) No (Radio > 10).

2.2.1.6. SENTENCIAS CONTROL: BUCLES

Un bucle es un tipo de sentencia de control que hará que una o varias instrucciones se repitan
tantas veces como permita la configuración de dicha sentencia.
Un bucle:

■ Contiene al menos una instrucción.

■ Está controlado por una condición que detenninará el número de repeticiones.


La condición de control debe ser modificada de algún modo en el interior del bucle para
garantizar que este finalice en un momento determinado. Según el momento en el que se evalúe
la condición tenemos bucles:
■ Mientras. La condición se evalúa al comenzar, de fonna que puede darse el caso en
que no llegue a ejecutarse ninguna instrucción si la condición no es cierta desde el
principio.
■ Hacer ... Mientras. En este tipo de bucles la condición de control se evalúa al
finalizar el conjunto de instrucciones de forma que al menos una vez estas se
ejecutan. Como un tipo especial de este tenemos el bucle Repetir...Hasta, que
ejecutará un conjunto de instrucciones hasta que la condición sea verdadera. No
todos los lenguajes de programación implementan este tipo de sentencia de control.
■ Para. El conjunto de instrucciones se ejecuta un número concreto de veces. En este
tipo de bucles se usan los denominados contadores que controlan el número de
ejecuciones del mismo.
Cuando usamos un bucle Mientras o un bucle Hacer...Mientras, no sabemos con exactitud
el número de veces que se va a repetir el conjunto de instrucciones que lo forman. Sin embargo,
Capítulo 2. Metodología de la programación

un bucle Para está diseñado para ser ejecutado un número X de veces, sabemos de antemano el
número de veces que se ejecutará el conjunto de instrucciones que lo fonnan.

INSTRUCCIONES
CONDICIÓN

Verdadera

INSTRUCaONES

RESTO DEL
'RESTO DEL ¡
PROGRAMA
RROGRAMA
Figura 2.14. Sentencia de control Mientras. Figura 2.15. Sentencia de control Hacer...Mientras.

Contador= valor
^ : deinliáo
INSTRUCCIONES

A^lor final
>=

Contador
CONDICIÓN

Verdadera
INSTRUCaONES

RESTO DEL I-,5


Cúntador=Contado
PROGRAMA VSÍJ
Figura 2.16. Sentencia de control Repetir...Hasta.
Debemos tener mucho ojo con este tipo de bucles,
mientras la condición sea falsa se seguirá
ejecutando,justo al revés que el resto.
Figura 2.17. Sentencia de control Para.

En las sentencias condicionales del tipo Para.


Usamos un contador. Un contador es una variable de tipo entero cuyo fin es corno su
nombre indica "contar". Al ser una variable se declara como tal aunque cuando
analicemos los lenguajes de programación C# y Java veremos que esta declaración se
realiza en la misma estructura de control. El contador debe ser inicializado antes de
ser usado, es decir, debemos asignarle un valor inicial o valor de partida. Por eso en
el diagrama de flujo de la Figura 2.17. vemos en cuadro Contador=Va!or de inicio.
Usamos un valor final que pennitirá que el bucle finalice. Este valor debe ser mayor
o igual que el valor inicial asignado al contador para garantizar que al menos una vez
se ejecuten las instrucciones que contiene la sentencia de control. Así, la condición
del bucle debe ser como se muestra en la Figura 2.17. Valor final >= Contador.
Usamos un elemento denominado incremento. En realidad, este componente es
fundamental ya que permite que el contador se modifique en cada vuelta del bucle y
así llegue a alcanzar el valor final para que este acabe. El incremento puede ser un
ntámero entero u otra variable.

Usaremos acumuladores. Un acumulador es una variable que va modificando su


valor agregando al que tenía una cantidad. Pueden incrementar o disminuir su \alor.
El contador que usamos es a su vez un elemento acumulador ya que en cada vuelta
de bucle realizamos la operación Contador=Contador+Incremento. Será común
usar estas instrucciones de la fonna Contador+=Incremento. Algunos autores
establecen diferencias entre contador y acumulador de forma que un contador se
define como una variable que va modificando su valor un número estático de \eces,
ya se encuentre este almacenado o no en otra variable. Un acumulador verá
modificado su valor en función del valor de otra variable que también se modificará
en cada vuelta del bucle.

Ejemplo de contador Ejemplo de acumulador


Contador=0;
Inicio del bucle Acumulador=0;
Contador=Contador + 3 Incremento;
Fin del bucle Inicio del bucle
Contador=0; Escribe un valor
Incremento=3; Incremento—valor leído;
Inicio del bucle Acumulador=Acumulador+Incremento;
Contador=Contador + Incremento. Fin de bucle
Fin del bucle
La variable acumulador se incrementará
La variable contador siempre incrementa
dependiendo del valor que el usuario escriba
un número exacto de veces.
cada vuelta de bucle.

Analicemos a continuación el pseudocódigo para cada estructura de control estudiada:


Sentencia de control: Bucle Mientras. J
Mientras CONDICIÓN hacer i Ésta sentencia se interpreta literalmente como se :
Instrucción! ■ i ve en el pseudocódigo: mientras que la •
Insti~ucción2; i CONDICIÓN sea verdadera se ejecutarán las i
i instrucciones Intrucciónl; Instrucción2; etc. i
Instrucciónn; Entres las instrucciones encontraremos alguna
modificar CONDICIÓN; que se encargue de modificar la condición
FinMientras; establecida (modificar CONDICIÓN), que
asegurará el fin del bucle.
Sentencia de control: Bucle Hacer...Mientras. j
Hacer: ; Én este caso observamos cómo la condición se I
Instrucción!; I evalúa en la última línea de forma que al menos una j
i vez se ejecutarán las instrucciones. Al igual que en ;
Instrucciónn; i un bucle Mientras debemos establecer una línea del i
t r I

modificar CONDICIÓN;
DICIÓN; i tipo modificar CONDICION para asegurar la|
Mientras CONDICIÓN; i finalización del bucle. ¡
Capítulo 2. Metodología de la programación

Sentencia de control: Bucle Para.


i ■ c es el contador.
■ Vi es el valor inicial asociado al
contador.

■ Vf es el valor final que debe


alcanzar.

Para c= Vi a Vfincremento Inc hacer ■ Inc representa el incremento, el


valor en el que irá cambiando el
Instrucción1;
contador para alcanzar el Vf.
Instrucción2;
Este bucle se interpreta de forma que
iniciamos el contador con Vi,
Instrucciónn; comprobamos que contador no haya
FinPara; alcanzado el Vf y ejecutamos el
conjunto de instrucciones. Cuando
hayamos ejecutado la primera vez el
bucle se usa Inc para modificar el valor
de contador, se comprueba nuevamente
que no se haya alcanzado el valor final y
volvemos a ejecutar las instrucciones.
Sentencia de control: Bucle Repetir
Repetir
Ejecutaremos las instmcciones
Instrucciónl; Instrucciónl; Instrucción!; etc., hasta
Instrucción2; que la CONDICIÓN sea cierta. En el
momento en que la condición se cumpla
se finalizará el bucle.
Instnicciónn;
Hasta CONDICIÓN;
Cuando comenzamos a usar sentencias de control de este tipo es interesante realizar tablas de
seguimiento de los algoritmos para comprobar si funcionan como deben. A continuación y para
afianzar los conceptos relacionados con estas nuevas estructuras de un programa vamos a
analizar una serie de ejemplos a través de sus tablas de seguimiento.
Una tabla de seguimiento debe referenciar cada una de las variables del algoritmo además de
las salidas o resultados del mismo. Así, cada fila será enumerada con el número de línea del
código al que hace referencia y cada una de las columnas de la tabla se usará para observar los
valores que alcanzan las variables del problema en esa línea de código.
Imaginemos que tenemos el siguiente pseudocódigo:
; (l)Entorno:
; (2) entero Numero, Incremento;
j (3) entero Suma;
; (4)Inicio:
; (5) Numero=3;
! (6) Incremento=2;
; (7) Suma=Numero+Incremento;
; (8) Escribir "La suma es:"; (continua
(c( en la siguiente página)
; (9) Escribir Suma;
i__(l_0)Fin;
70 Programación

La tabla de seguimiento que usaríamos sería:


Numero Incremento Suma Pantalla

Tenemos una columna donde iremos colocando el número de fila del código que se está
analizando, las columnas para cada variable y la Pantalla.

j NOTA: Cuando comenzamos a programar se aconseja la realización de tablas de


seguimiento, ya que gracias a ellas veremos si los algoritmos creados son correctos, que
sucederá cuando se ejecuten estos y si las salidas que obtenemos son las esperadas y para
las que hicimos el código tal y como ahora lo vemos. Posteriormente, en los IDE que
usaremos realizaremos seguimientos de nuestras variables mediante las herramientas de i
depuración que nos brindan y el uso de puntos de interrupción. |

Si ejecutáramos nuestro programa, ¿cómo iríamos rellenando la tabla? Podemos seguir el


siguiente ordinograma:

mm

Figura 2.18. Ordinograma que muestra los pasos a seguir en la construcción de una tabla de seguimiento.
Capítulo 2. Metodología de la programación 71

Para el algoritmo planteado generemos la tabla de seguimiento. La primera sentencia que


encontramos es entero Numero,Incremento;
(2)entero Numero, Incremento;
Línea
/*solo realizamos una declaración
de Numero Incremento Suma Pantalla
de variable, la tabla permanece
vacia*/ CÓdÍ20

(3)entero Suma;

/*A1 igual que en la linea (2) sólo


Numero Incremento Suma Pantalla
se produce una declaración de
variable*/

(5)Numero=3;

/*Se produce la modificación de la Numero Incremento Suma Pantalla


variable Numero, asi insertaremos
una nueva fila en la tabla*/

(6)Incremento=2;
Numero Incremento Suma Pantalla
/*Nueva modificación, en esta
ocasión de la variable Incremento.
Nueva fila a nuestra tabla*/

Numero Incremento Suma Pantalla


(7)Suma=Numero+Incremento;

/*Nueva modificación y nueva fila*/

3+2=5

Numero Incremento Suma Pantalla


(8)Escribir "La suma es:";

/*No se realiza ninguna


modificación pero si una operación
de escritura*/ 3+2=5
La suma

Línea
de Numero Incremento Suma Pantalla
código
(5)
(9) Escribir Suma; (6)
(7) 3+2=5
/*Nuevamente una operación de E/S*/
La suma

La
(9) 3 2 5 Suma
es: 5
La columna Pantalla, en la última fila, indica cuál será el resultado del algoritmo. Finalmente
¿obtenemos lo que queremos? Si la respuesta es positiva querrá decir que el algoritmo está bien
formulado.

Veamos un ejemplo algo más complejo. Supongamos que tenemos que comprobar el
funcionamiento del siguiente pseudocódigo:
72 Programación

(1)Entorno:
(2) entero Nota;
(3)Inicio:
(4) Escribir "Introduce la nota del alumno'
(5) Leer Nota;
(6) Si Nota < 5 entonces
(7) Escribir "Suspenso";
(8) Sino
(9) Escribir "Aprobado";
(10) FinSi;
(11)Fin;

Generemos a continuación la tabla de seguimiento:

(2)entero Nota;
Línea
/*soIo realizamos una declaración de de Nota Pantalla
variable, la tabla permanece vacia*/ eódiso

(4) Escribir "Introduce'íá"ñc>'tá"déí"


Línea
alumno";
Pantalla
/*Se muestra este mensaje por codiizo
pantalla, insertamos una linea en la Introduce la
tabla indicando esta acción*/
nota del alumno

Línea
(5) Leer Nota;
de Nota Pantalla
/*En esta operación modificamos el código
contenido de Nota. Supongamos que el Introduce la
usuario introduce un 4*/
nota del alumno

(6) Si Nota < 5 entonces


/*En la linea 6 comenzamos una
sentencia alternativa, debemos Línea
comprobar en la tabla de seguimiento de Nota Pantalla
el valor que tiene Nota en la última código
fila. Nota es 4, esta condición se Introduce la
cumple con lo que ejecutaremos la
nota del alumno
sentencia que está justo debajo. La
tabla de seguimiento permanece sin
cambios.*/

(7) Escribir "Suspenso"; Línea


de Nota Pantalla
/*Nos encontramos ante una operación
de salida de información. Agregamos código
una nueva fila a la tabla e insertamos Introduce la
la palabra "Suspenso" en la columna nota del alumno
Pantalla*/
Suspenso
(11) Fin;

/*Las sentencias ubicadas entre 7 y 11


no se ejecutarán debido al
funcionamiento de las sentencias
alternativas*/
Capitulo 2. Metodología de la programación 73

Visto esto, supongamos ahora que tenemos un algoritmo que contiene un bucle. Hagamos la
tabla de seguimiento y así estudiaremos el funcionamiento de la sentencia de control.
(1)Entorno:
(2) constante real PI=3.1415;
(3) entero Radio, Circunferencia;
(4)Inicio:
Hacer:
Escribir "Introduce un Radio. (-1 para
finalizar)
Leer Radio;
Circunferencia=2 x PI x Radio;
Escribir "La circunferencia es:";
Escribir Circunferencia;
Mientras Radio <> -1;
(12)Fin;

La tabla de seguimiento sería la que sigue:


(2)constante real PI=3.1415; Línea
/*Creamos una constante, se de Radio Circunferencia Pantalla
carga en memoria PI, con el código
valor 3.1415*/ (2) 3.1415

(3) entero Radio,


Circunferencia; Línea
/*Se crean las variables de Radio Circunferencia Pantalla
Radio y Circunferencia pero código
no adquieren ningún valor. La (2) 3.1415
tabla permanece igual.*/
(5) Hacer: Línea
de Radio Circunferencia Pantalla
/*comienza el bucle
código
Hacer...Mientras.*/
(2) 3.1415
Línea
(6)Escribir "Introduce un de Radio Circunferencia Pantalla
Radio.(-1 para finalizar)"; código
/*Realizamos una operación de (2) 3.1415
Salida, asi que debemos
Introduce
rellenar la tabla. Nueva
fila, rellenamos la columna un Radio.
3.1415
Pantalla.*/ (-1 para
finalizar).
Línea
de Radio Circunferencia PI Pantalla
(7) Leer Radio;
/*En esta instrucción código
modificamos la variable (2) 3.1415
Radio, esta modificación debe Introduce
quedar reflejada en la un Radio.
3.1415
tabla.Supongamos que el (-1 para
usuario introduce un 2.*/ finalizar).
(7) 3.1415

(8) Circunferencia=2 x PI x Línea


Radio; de Radio Circunferencia PI Pantalla
/*Modificamos la variable código
Circunferencia con el valor (2) 3.1415
obtenido de la operación 2 x Introduce
PI X Radio, debemos insertar
un Radio.
una nueva fila a la tabla de 3.1415
(-1 para
seguimiento.*/
finalizar).
(7) 2 3.1415
(8) - I 3.1415
Línea
de Radio Circunferencia Pantalla
código
(9) Escribir "La (2) 3.1415
circunferencia es:"; Introduce un
/*Nuevamente realizamos una Radio. (-1
3.1415
operación de salida de datos para
con lo que insertaremos una finalizar).
nueva fila y rellenaremos la
3.1415
columna Pantalla.*/
3.1415

3.1415 circunferencia

Línea
de Radio Circunferencia Pantalla
código
(2) 3.1415
Introduce un
(10) Escribir Circunferencia;
Nuevamente una operación de
3,1415 «"<"<>•<->
para
entrada/salida de dados. La finalizar).
tabla de seguimiento se
3.1415
visualizará como se muestra a
la derecha.*/ 3.1415
La
3.1415 circunferencia

3.1415 circunferencia
es; 12
Línea
de Radio Circunferencia Pantalla

(11) Mientras Radio o -i- código


/*Llegados a este punto se' (2) 3.1415
evalúa la condición del Introduce un
bucle, ¿es Radio distinto de Radio.(-1
3.1415
-1? La respuesta es Si con lo para
que volveremos a la linea finalizar).
(6)*/ 3.1415
3.1415
La
3.1415
circunferencia

La variable Circunferencia es entera de forma que aunque la operación para obtener su valor genere
decimales el resultado debe ser entero. Al multiplicar 2 x 3.1415 x 2 el resultado será 12.56 pero en
Circunferencia solo almacenamos 12.
Capítulo 2. Metodología de la programación 75

La
(10) 2 12 3.1415 circunferencia
es: 12

Radio Circunferencia PI Pantalla

(2) 3.1415
Introduce un
Radio.(-1
(6) 3.1415
para
finalizar).
(6)Escribir "Introduce un
(7) 2 3.1415
Radio.(-1 para finalizar)";
/*En la pantalla volveremos a (8) 2 12 3.1415

ver el mensaje solicitando un La


nuevo radio.*/ (9) 2 12 3.1415 circunferencia
es:

La
(10) 2 12 3.1415 circunferencia
es: 12
Introduce un
Radio.(-1
(6) 2 12 3.1415
para
finalizar).

Radio Circunferencia PI Pantalla

3.1415
Introduce un
Radio.(-1
3.1415
para
finalizar).
3.1415
(7) Leer Radio;
3.1415
/*Supongamos que es esta
ocasión el usuario indica que
el radio es 5*/ 3.1415 circunferencia
es:

La
3.1415 circunferencia
es: 12
Introduce un
Radio.(-1
3.1415
para
finalizar).
3.1415

Radio Circunferencia PI Pantalla


(8) Circunferencia=2 x PI x
Radio;
3.1415
/*Volvemos a calcular la
circunferencia.*/
3.1415
fi nali/ar).
(7) 2 3.1415
(8) 2 12 3.1415
La
(9) 2 12 3.1415 circunferencia
es:

La
(10) 2 12 3.1415 circunferencia
es: 12
Introduce un
Radio. (-1
(6) 2 12 3.1415
para
finalizar).

3.1415

Radio Circunferencia PI Pantalla

3.1415
Introduce un
Radio.(-1
3.1415
para
finalizar).
3.1415
3.1415

(9) Escribir "La 3.1415 circunferencia


circunferencia es:"; es:

La
3.1415 circunferencia
es; 12
Introduce un
Radio.(-1
3.1415
para
finalizar).
3.1415
3.1415
La
3.1415 circunferencia
es:

Radio Circunferencia PI Pantalla

3.1415
Introduce un
(10) Escribir Circunferencia; Radio.(-1
3.1415
para
finalizar).
3.1415
3.1415
La
3.1415
circunferencia
Capítulo 2. Metodología de la programación 77

La
(10) 2 12 3.1415 circunferencia
es: 12
Introduce un
Radio.(-1
(6) 2 12 3.1415
para
finalizar).
(7) 5 12 3.1415
(8) 5 31 3.1415
La
(9) 5 31 3.1415 circunferencia
es:

La
3.1414 circunferencia
es: 31

Radio Circunferencia PI Pantalla

3.1415
Introduce un
Radio.(-1
3.1415
para
finalizar).
3.1415
3.1415

(11) Mientras Radio <> -1; 3.1415 circunferencia


/*Volvemos a evaluar la es:

condición. ¿Es Radio distinto La


de -1? Si, asi que 3.1415 circunferencia
continuamos la ejecución del es: 12
bucle.*/
Introduce un
Radio.(-1
3.1415
para
finalizar).
3.1415
3.1415

3.1415 circunferencia
es:

La
3.1414 circunferencia
es: 31

Radio Circunferencia PI Pantalla

(6)Escribir "Introduce un
Radio.(-1 para finalizar)"; 3.1415
/*Visualizamos esta frase por Introduce un
pantalla.*/ Radio.(-1
3.1415
para
finalizar).
3.1415 1
12 EIDE89II
12 3.1415
es:

La
12 3.1415 circunferencia
es: 12
Introduce un
Radio. (-1
12 3.1415
para
finalizar).
12 3.1415
31 3.1415

31 3.1415

31 3.1414
es: 31
Introduce un
Radio. (-1
31 3.1415
para
finalizar).

(7) Leer Radio;


Circunferencia PI Pantalla
/*Vamos a introducir -1 para
finalizar el código. Se ha
optado por no colocar Introduce un
nuevamente toda la tabla Radio.(-1
31 3.1415
debido a que es demasiado para
extensa.*/ finalizar).
31 3.1415

Circunferencia PI Pantalla

(8) Circunferencia=2 x PI x Introduce un


Radio; Radio.(-1
31 3.1415
para
finalizar).
31 3.1415
-6 3.1415

Circunferencia PI Pantalla

Introduce un

(9) Escribir "La Radio.(-1


31 3.1415
circunferencia es:"; para
finalizar).
31 3.1415
-6 3.1415
La
-6 3.1415
circunferencia
Capítulo 2. Metodología de la programación 79

Línea
de Radio Circunferencia PI Pantalla
código
Introduce un

3.1415
para
finalizar).
(10) Escribir Circunferencia; 3.1415
3.1415

3.1415 circunferencia

(10) -1 3.1415 circunferencia


es: -6
Línea
de Radio Circunferencia PI Pantalla
código
Introduce un
(11) Mientras Radio <> -1;
/*Volvemos a comprobar la 3.1415 Kadio-í-l
para
condición. ¿Es Radio distinto finalizar).
de -1? No. En esta ocasión al
ser la respuesta negativa
3.1415
fianlizará el bucle y 3.1415
continuaremos con las La
sentencias que resten.*/ 3.1415 circunferencia

3.1415 circunferencia
es: -6
Línea
de Radío Circunferencia PI Pantalla
código
Introduce un
Radio.(-1
3.1415
para
finalizar).
(12) Fin; 3.1415
3.1415

3.1415 circunferencia

(10) -1 3.1415 circunferencia


es: -6

Se aconseja al lector que observe la tabla de seguimiento y vea cuándo cambian los valores de
las variables y cómo estas mantienen el valor anterior justo hasta que este es modificado.
El pseudocódigo es susceptible a alguna mejora, ya que en el caso de que escribamos -1 para
acabar la aplicación no deberíamos calcular ni mostrar la circunferencia. Este error se corrige
incluyendo una sentencia alternativa de forma que compruebe si el radio introducido es distinto
de -1. Si es diferente realizaremos el cálculo, en caso contrario saltaremos a la condición del
bucle de forma directa.

ACTIVIDAD 2.6

Dado el siguiente pseudocódigo realiza la tabla de seguimiento del mismo.


Entorno: ;
entero Contador; :
Inicio: ;
Para Contador=0 a 10 incremento 2 hacer •
Escribir Contador; :
FinPara; :
Fin; i

ACTIVIDAD 2.7

Realiza el diagrama de flujo del pseudocódigo de la Actividad 2.6


A la hora de introducir bucles en nuestros códigos fuente debemos asegurarnos de que estos
finalicen en un momento determinado, es decir, se repitan un número finito de veces. Un bucle
mal configurado hará que la aplicación no avance, "se cuelgue", dejando atrás toda su
funcionalidad. El programador debe cuidar concienzudamente las condiciones de cada bucle y la
existencia de elementos que pennitan conseguir que esta condición deje de ser cierta.
Algunos programadores utilizan bucles infinitos para mostrar menús de forma indefinida,
finalizando estos mediante sentencias de ruptura, particularmente opino que esta no es una
práctica aconsejable.
Veamos a continuación el siguiente pseudocódigo y la tabla de seguimiento del mismo.
(1)Entorno:
(2) entero Contador;
(3)Inicio:
(4) Contador=0;
(5) Mientras Contador < 5 hacer
(6) Escribir "Hola Mundo";
(7) FinMientras;
(8)Fin;

Línea de código Contador Pantalla

(4) Contador=0;
(5) Contador < 5(Verdad)
(6)Escribir "Hola Mundo"; Hola Mundo

(5) Contador < 5 (Verdad)

(6) Escribir "Hola Mundo"; Hola Mundo

(5) Contador < 5(Verdad)

(6) Escribir "Hola Mundo"; Hola Mundo


Capítulo 2. Metodología de ía programación

La secuencia (5) - (6) se repetirá de foraia indefinida ya que no existe en el intenor del bucle
ninguna instrucción que modifique la variable Contador. Esta será siempre igual a O con lo que la
condición de Contador < 5 será siempre cierta. Para enmendar este error modificaremos el
código de forma que insertaremos la línea Contador=Contador+l antes o después de la línea
(6). En el pseudocódigo ejemplo que hemos usado la posición dentro del bucle de la nueva
instrucción no altera el algoritmo. Así, el pseudocódigo y la tabla de seguimiento con esta nueva
modificación se verán:

(1)Entorno:
(2) entero Contador;
(3)Inicio:
(4) Contador=0;
(5) Mientras Contador < 5 hacer
(6) Escribir "Hola Mundo'
(7) Contador=Contador+l;
(8) FinMientras;
(9)Fin;

Linea de codigo Contador Pantalla

(4) Contador=0;

(5) Contador < 5 (Verdad)


(6) Escribir "Hola Mundo"; Hola Mundo

(7) Contador=Contador+l;

(5) Contador < 5(Verdad)


(6) Escribir "Hola Mundo"; Hola Mundo

(7) Contador=Contador+l;
(5) Contador < 5(Verdad)

(6) Escribir Hola Mundo Hola Mundo

Pasadas unas vueltas más ocurrirá que la condición establecida sea falsa y finalizará el
programa.

Aunque si bien es cierto que confonne vayamos realizando programas optaremos por el uso
de un bucle u otro, además que algunos harán más óptimos el software, es posible usar cualquiera
de ellos en cualquier problema. Veamos a continuación cómo el pseudocódigo anteriormente
analizado puede ser descrito por los bucles estudiados.
Uso del bucle Mientras
(1)Entorno:
(2) entero Contador;
(3)Inicio:
(4) Contador=0;
(5) Mientras Contador < 5 hacer
(6) Escribir "Hola Mundo";
(7) Contador=Contador+l;
(8) FinMientras;
(9)Fin;
82 Programación

Uso del bucle Hacer...Mientras


(1)Entorno:
entero Contador;
(3)Inicio:
Contador=0;
Hacer
(6) Escribir "Homa Mundo";
Contador=Contador + l;
Mientras Contador < 5;
(9)Fin;
Uso del bucle Para
(1)Entorno:
entero Contador;
(3)Inicio:
(4) Para Contador=0 a 5 incremento 1 hacer
Escribir "Hola Mundo";
FinPara;
(7)Fin;
Uso del bucle Repetir hasta
(1)Entorno:
entero Contador;
(3)Inicio:
Contador=0;
Repetir
(6) Escribir "Hola Mundo";
Contador=Contador+l;
Hasta Contador >= 5;
(9)Fin;

ALGORITMO RESUELTO 5

ENUNCIADO

Se precisa realizar un algoritmo que calcule si los número introducidos por el usuario son
pares o impares. Para ello se debe pedir al usuario un número, se comprueba si este es par o
impar mostrando por pantalla los mensajes "El número es par" o "El m'imero es impar" según el
caso. A continuación se preguntará al usuario si desea finalizar la ejecución del software. Si su
respuesta en No (n), repetiremos el proceso descrito pidiendo un nuevo número, en caso
contrario, si especifica Si(s) finalizará el programa.
DESARROLLO - FASE DE ANÁLISIS
Dado el enunciado, pasamos a desglosar y analizar este en profundidad, de forma que quede
totalmente claro qué se debe hacer.
■ El programa repite hasta que el usuario escribe una s el proceso de averiguación de si
un número es par o impar, de forma que necesitaremos usar un bucle. Ya que no
sabemos con exactitud el número de repeticiones de las instrucciones incluidas en la
sentencia de control, usaremos un bucle tipo Mientras o Hacer...Mientras. Creemos
que el último es más aconsejable debido a que al menos una vez se debe procesar un
número y mostrar si es par o impar.
■ Tenemos que pedir al usuario un número, de forma que debemos realizar una
operación de escritura y otra de lectura para almacenar este. A la hora de guardar el
valor necesitaremos una variable que podemos llamar Numero.
Capítulo 2. Metodología de la programación

■ Además, cuando se pide al usuario que escriba si desea finalizar la ejecución, la letra
introducida debe ser almacenada en una variable para ser evaluada, de forma que
debemos crear una variable de tipo carácter.
■ Básicamente la secuencia de ejecución debe ser:
1. Creamos variable Numero y Salir.
2. Entramos en el bucle.

3. Pedimos al usuario el número que quiere procesar.


4. Comprobamos si el número introducido es par o impar. Según el resultado
escribimos en pantalla el mensaje "El número es par" o "El número es
impar".
5. A continuación preguntamos al usuario si desea finalizar.
6. Si el usuario escribe una s el bucle teraiinará y si escribe n volveremos al
paso 3.
DESARROLLO - FASE DE CREACIÓN DEL DIAGRAMA DE FLUJO
A partir del análisis realizado
podemos comenzar a desarrollar
el diagrama de flujo
correspondiente. Se muestra en la
Figura 2.19.

\ NOTA: A la hora de
' realizar comprobaciones con
I caracteres debemos tener
; especial cuidado cuando
' usamos mayúsculas y I—y —
í minúsculas, ya que la letra a ^
I y A no poseen el mismo
I código ASCII, con que
i aunque parezca extraño, no I >
son la misma letra en
programación. Los lenguajes
de programación poseen
funciones que permiten
convertir textos en —
. , , — Si
mayúsculas o minúsculas. —

Figura 2.19. Diagrama de flujo del algoritmo resuelto 5.


84 Programación

DESARROLLO - FASE DE CREACION DE PSEUDOCODIGO

Veamos paso a paso la traducción del diagrama de flujo al pseudocódigo.


Entorno:
entero Numero;
carácter Salir;

Inicio:
/*La petición del número se encuentra dentro
del bucle*/
Hacer
Escribir "Introduce un número";
Leer Numero;

Si (Resto{Numero/2))=0 entonces

Escribir "El número es par";

Escribir "El número es Escribir "El número es


impar." impar
FinSi;

■■ Escribir "¿Desea Escribir "¿Desea finalizar


(S/n)?";
Leer Salir Leer Salir;

Mientras SalirO's';

Entorno:
entero Numero;
carácter Salir;
Inicio:
/*La petición del número se encuentra dentro del bucle*/
Hacer
Escribir "Introduce un número";
Leer Numero;
Si (Resto(Numero/2))=0 entonces
Escribir "El número es par";
Sino
Escribir "El número es impar";
FinSi;
Escribir "¿Desea finalizar (S/n)?";
Leer Salir;
Mientras Saliro's';
Fin;

ACTIVIDAD 2.8

Realiza la tabla de seguimiento del algoritmo resuelto 5.


Capítulo 2. Metodología de la programación

ALGORITMO RESUELTO 6

ENUNCIADO

En el siguiente enunciado se pide al programador que realice un algoritmo por él, se solicite al
usuario la introducción de diez números y muestre por pantalla cuántos de ellos eran positivos,
cuántos negativos y cuántos cero.

DESARROLLO - FASE DE ANALISIS

Tras leer en varias ocasiones el enunciado deducimos que:


■ Debemos usar un bucle que se repetirá 10 veces. Como conocemos el número exacto
de repeticiones el más idóneo es el bucle Para.
■ Necesitamos guardar la cantidad de números positivos que tenemos, la de números
negativos y ceros de fonna que crearemos tres variables, Positivos, Negativos y
Ceros.

■ Dentro del bucle pediremos un número, de foraia que: mostraremos en pantalla el


texto "Introduce número" y recogeremos el valor que el usuario haya introducido.
Para ello necesitamos una variable que puede llamarse Numero.
■ Una vez tenemos el número comprobamos si es menor que cero. Si lo es,
incrementamos la variable Negativos. Si no es menor que cero, comprobamos si es
mayor. En caso afínuativo incrementamos la variable Positivos. Y si ninguna de las
situaciones expuestas se da será porque el valor introducido ha sido cero de forma
que incrementaremos la variable Ceros.
■ Al finalizar el bucle mostramos tres mensajes donde se indican total de positivos,
negativos y ceros.
■ Escuetamente:

o Declaramos la variables Numero, Negativos, Positivos y Ceros.


o Comenzamos el bucle.
o Pedimos un número y lo guardamos en Numero.
o Si Numero es menor que cero incrementamos Negativos.
o Si Numero es mayor que cero incrementamos Positivos.
o Si Numero es cero incrementamos Ceros.
o Al finalizar el bucle mostramos por pantalla las cantidades guardadas en las
variables Negativos, Positivos y Ceros.

DESARROLLO - FASE DE CREACIÓN DEL DIAGRAMA DE FLUJO


El organigrama para este planteamiento será el que se muestra en la Figura 2.20.
Figura 2.20. Diagrama de flujo del algoritmo resuelto 6.

DESARROLLO - FASE DE CREACIÓN DE PSEUDOCÓDIGO


Para finalizar pasamos a desarrollar el pseudocódigo. Antes de nada hemos dividido el
diagrama de flujo en zonas para así asociar partes del mismo con líneas de código. Obsérverse la
Figura 2.21.
Entorno:
entero Contador, Numero;
entero Positivos, Negativos, Ceros;
\ 1 -í Inicio:
Positivos=0;
Negativos=0;
Ceros=0;
Para Contador=0 a 10 incremento 1 hacer
Escribir "Introduce un número ;
sá /
Leer Numero;
Si Numero < Ó entonces
Negativos=Negativos+l;
Sino
Si Numero > 0 entonces
/'T ^ Positivos=Positivos+l;
vi-'
Sino
Ceros=Ceros+l;
FinSi;
FinSi;
Capítulo 2. Metodología de la programación 87

Inicio

Contador =0
Numero,Contador Negatlvoss:0
Negativos,Positivos,Ceros Posítivos=0
Ceros=0

Escribir'Introduce un
Contador <
número

Numero < O'

Numero >0

Escribir Negativos:,Escribir Negativos


Escribir "Positivos:",Escribir Positivos
Escribir"Ceros:",Escribir Ceros

Figura 2.21. Diagrama de flujo del algoritmo resuelto 6 dividido en zonas para desarrollar el pseudocódigo.

FinPara;
Escribir "Negativos:";
Escribir Negativos;
Escribir "Positivos:";
- Fin del bucle Escribir Positivos;
Escribir "Ceros:";
Escribir Ceros;
I Fin;

Entorno:
entero Contador, Numero;
entero Positivos, Negativos, Ceros;
Inicio:
Positivos=0;
Negativos=0;
Ceros=0;
Para Contador=0 a 10 incremento 1 hacer
Escribir "Introduce un número";
Leer Numero;
Si Numero < O entonces
Negativos=Negativos+l;
Sino
Si Numero > O entonces
Positivos=Positivos+I;

Ceros=Ceros+l;
FinSi;
FinSi;
FinPara;
Escribir "Negativos:";
Escribir Negativos;
Escribir "Positivos:";
Escribir Positivos;
Escribir "Ceros:";
Escribir Ceros;

ACTIVIDAD 2.9

Realiza la tabla de seguimiento para el algoritmo planteado en el caso resuelto anterior.


ACTIVIDAD 2.10

Realiza un algoritmo de fomia que se pida al usuario tres números y se ordenen estos en
forma creciente visualizándose por pantalla. Resuelve el problema tal y como se ha hecho con
cada algoritmo resuelto, desarrollando las fases de análisis, creación del diagrama de flujo y
pseudocódigo.
ACTIVIDAD 2.11

Realiza un algoritmo que pida números positivos al usuario. La petición de valores finalizará
cuando el usuario teclee el valor 0. El programa debe mostrar al finalizar el mayor de todos los
números y la meda aritmética. Resuelve el problema desarrollando las fases de análisis, diagrama
de flujo y pseudocódigo.

2.3. TEOREMA DE LA PROGRAMACIÓN


ESTRUCTURADA
El teorema de la programación estructurada establece que todo programa puede ser
implementando mediante el uso de tres tipos de estructuras de control:
■ Secuencial.

■ Alternativa.

■ Repetitiva.
Existen lenguajes, sobre todo en el pasado, que usaban estructuras GOTO. Un COTO
equivalía a un salto de línea, es decir, cada vez que veíamos GOTO x el programa saltaba a la
línea con número x. El teorema de la programación estructurada establece que este tipo de
sentencias no son necesarias y pueden ser sustituidas por un bucle, una sentencia alternativa
simple, doble o múltiple, o bien mediante una secuencia de instrucciones.
Aunque normalmente los científicos acreditan el teorema a Corrado Bohm y Giuseppe
Jacopini debido a un artículo de 1966 donde lo demuestran, existen indicios de que surge en
1946 con la descripción de la arquitectura de Von Neumann y el teorema de la forma normal de
Kleene.

Bóhm y Jacopini demostraron cómo se podían construir diagramas de flujo estructurados a


partir de otros asentando las bases de la programación estructurada que en principio
estudiaremos. Hoy día, prima la programación orientada a objetos, donde se usan los elementos
estudiados en este capítulo pero para la ejecución de acciones o métodos de unos componentes
denominados objetos.
Capítulo 2. Metodología de la programación 89

COMPRUEBA TU APRENDIZAJE
1. En un programa, ¿qué es una variable? ¿Y una constante?
2. ¿En qué consiste una petición de un dato al usuario? ¿Qué operaciones necesitaremos
usar?

3. ¿Qué tipos de sentencias de control alternativas existen?


4. ¿Cuál es el diagrama de flujo de una sentencia alternativa doble?
5. ¿Cuál es el pseudocódigo de una sentencia alternativa múltiple?
6. ¿Qué es un operador? ¿Qué tipos se han estudiado? ¿Para qué se usa cada uno de ellos?
7. ¿Qué es un bucle? ¿Para qué se usan?
8. ¿Qué tipos de bucles existen? ¿Cuáles son los pseudocódigos que los diferencian?
9. ¿Qué es una tabla de seguimiento? ¿Cuál es su uso?
10. ¿Qué entiendes por el Teorema de la programación estructurada?

ACTIVIDADES DE AMPLIACION
DesaiTolla un algoritmo que pida al usuario dos números e indique mediante un
mensaje en pantalla si son iguales. En caso de que no lo sean debe mostrar por
pantalla cuál es mayor. Enfoca el problema como se ha hecho en el capítulo con la
resolución de los algoritmos resueltos, desarrollando la fase de análisis, diagrama de
flujo y pseudocódigo.
Realiza una tabla de seguimiento del algoritmo generado en la Actividad 1. ¿Es
correcto? ¿Funciona tal y como indica el enunciado?
Realiza un algoritmo que pida al usuario la nota de un alumno. Este debe procesar
esta nota de forma que muestre los mensajes que se indican en función de:
■ nota < 3 / Mensaje:"Muy deficiente".
■ 3 <= nota < 5 / Mensaje: "Suspenso".
■ 5 <= nota < 6 / Mensaje: "Suficiente".
■ 6 <= nota 7 / Mensaje: "Bien".
■ 7 <= nota < 9 / Mensaje: "Notable".
■ 9 <= nota <= 10 / Mensaje: "Sobresaliente".

Realiza la tabla de seguimiento de la Actividad 4.


Realiza un algoritmo que pida al usuario una hora (se aconseja pedir horas, minutos y
segundo como elementos diferentes, no como un conjunto) . A continuación se
debe mostrar por pantalla qué hora será pasado un minuto. Es importante que se
realice la fase de análisis, de diagrama de flujo y pseudocódigo.
90 Programación

6. Realiza la tabla de seguimiento para la Actividad 5. Usa valores extremos, por


ejemplo 4 horas 59 minutos O segundos, o O horas 3 minutos y 59 segundos.
7. Realizar un algoritmo que nos indique si un año detemiinado es bisiesto, debemos
pedir al usuario el año. Un año es bisiesto si es múltiplo de 4, a excepción de los que
siendo múltiplos de 4 también son múltiplos de 100 pero no de 400.
8. Realiza un algoritmo que dada una fecha expresada en día/mcs/año se pida un número
de días y muestre qué fecha será transcurridos ese número de días. Recordad que el
mes de febrero tiene sólo 28 dias a excepción de los años bisiestos.
9. Supongamos que somos los encargados de una tienda de vinos y licores que tiene una
serie de tarifas y descuentos. Si un vino cuesta más de 60 euros se aplica un
descuento del 10% al precio. Si se compran como mínimo 20 botellas, se aplica un
descuento del 20% al total. En caso de que se compren 20 botellas de aquellas que
cuestan más de 60 euros cada una, el descuento que se aplicará será en lugar del 10%
el 25%. Realiza el organigrama del problema y haz un seguimiento al mismo para
comprobar su funcionamiento.
10. Realiza un algoritmo que muestre por pantalla todos los números pares menores que
20.

11. Necesitamos realizar un programa que muestre por pantalla la suma de todos los
números impares y de todos los números pares menores que cien.
12. Realiza la Actividad 11 pidiendo al usuario en qué valor finalizar, es decir si en la
Actividad 11 se pide números menores que 100 aquí pedimos al usuario que
introduzca también este dato.
13. Realiza un diagrama de flujo que dé solución al cálculo del factorial de un número.
(n!=nxn-l xn-2 x ... 1).
14. Realiza un algoritmo que pida dos números, X e Y de fonna que se pretende calcular
X^.

15. Realiza el diagrama de flujo que dé solución al problema de calcular la multiplicación


de dos números mediante el uso de sumas.
16. Realiza un programa que pida un número al usuario y a continuación indique cuáles
son sus divisores.
CAPITULO 3

INICIACION AL LENGUAJE C#

CONTENIDOS OBJETIVOS

Características de C#, estructura Conocer las características


de un programa. principales del lenguaje de C# así
como la estructura de un programa.
Identificadores, tipos de datos,
variables, constantes y ámbito de Entender qué es un identíficador.
visibilidad. Saber qué tipos de datos podemos
usar, cómo crear variables,
Sentencias de control
eonstantes, etc.
alternativas en C#.
Entender la estructura de las
Sentencias de control
sentencias de eontrol, y su uso.
repetitivas en C#.
Conocer los elementos
Ejercieios resueltos.
fundamentales en C#.

RESUMEN DEL CAPITULO

Una vez se ha estudiado en el Capítulo 2 el desarrollo de algoritmos, debemos


empezar a codifiear estos. Para ello, comenzamos con el lenguaje de programación C#.
En este eapítulo se estudiarán earacterístieas del lenguaje, sentencias de eontrol,
declaración de variables, constantes, etc., elementos básieos y fundamentales de la
programaeión estructurada.
3.1. INTRODUCCION

C# es un lenguaje de programación orientado a objetos creado por Microsoft. Fue


desarrollado para competir con el gran éxito del lenguaje de programación Java de Sun
Microsystems. Forma parte de la platafonna .NET y como todos los lenguajes que la constituyen
es un lenguaje compilado que conforma un código intermedio denominado MSIL (Microsoft
Intermedíate Language - Lenguaje Intennedio de Microsoft). Actualmente existen diferentes
IDEs capaces de desarrollar código C#, entre ellos Microsoft Visual Studio que hemos
comenzado a ver en el Capítulo 1.
La arquitectura de C# fue desarrollada por Anders Hejlsberg, autor de Turbo Pascal y
principal desarrollador de Delphi. C# se crea para la plataforma .NET constituyéndose como
lenguaje natural de esta.

j NOTA; Podemos decir que la plataforma .NET es un framework de Microsoft en


respuesta a la plataforma Java. Se pretende proporcionar un entorno rápido, robusto,
económico y seguro para el desarrollo de aplicaciones. Es una capa de software que se
ubica entre el sistema operativo y el programador, de forma que se abstrae por completo
los detalles del software base. Una de sus características es que es multilenguaje, tal que |
cualquier lenguaje de programación puede adaptarse y ejecutarse en ella.
Microsoft la define como "wn entorno para la construcción, desarrollo y ejecución de |
servicios web y otras aplicaciones que consiste en tres partesfundamentales: el Common
Language Runtime (entorno de ejecución), las Framework Classes (clases de la
plataforma)y ASP.NET\ ;
Anteriormente hemos definido .NET como framework, esto es una estructura ya
precondeciba de la cual se parte para el desarrollo de otros componentes.
^ ^ . .. J
La sintaxis de C# deriva de C y C++, utilizando un modelo de objetos similar a Java,
incluyendo nuevas mejoras. Fue aprobado como estándar en diciembre de 2001 por la ECMA'
Intemational (ECMA-334). También está estandarizado por ISO (ISO/IEC 23270). La última
versión estable de C# es la 5.0(15 de agosto de 2012).

3.2. CARACTERÍSTICAS DE C#
C# pretende ser un lenguaje de programación:
■ Simple. Fácil de usar.
■ De propósito general. Puede usarse para generar cualquier tipo de aplicación para
cualquier ámbito.
■ Orientado a objetos.
■ Fuertemente tipado. Se dice que un lenguaje de programación es tipado cuando a la
hora de crear variables es necesario establecer el tipo de datos que se va a almacenar
en ellas.

ECMA - European Computer Manufacturers Association


Capítulo 3. iniciación ai lenguaje C# 93

■ Usa el denominado Recolector de basura. El uso del recolector de basura hace que
no sea necesario ejecutar instrucciones de destrucción de objetos.
■ Usa instrucciones seguras de fonua que a la hora de utilizarlas no se produzcan fallos.
Por ejemplo, C o C++ penuitían en la evaluación de condiciones en las estructuras
alternativas el uso tanto de una expresión condicional como aritmética. En C# solo se
penriite expresiones condicionales de fonua que no puedan confundirse los
operadores de igualdad y asignación y se den fallos difíciles de detectar.
■ Unificación de tipos. En C# todos los tipos derivan de una superclase llamada
System.Object heredando todos sus elementos.
■ No pennite el uso de punteros para conseguir mayor seguridad en las aplicaciones.

Es un lenguaje de programación creado con el objetivo de desarrollar aplicaciones


distribuidas, además de aplicaciones complejas para ejecutar en sistemas operativos potentes
como Windows 7, 8, diferentes distribuciones Linux, así como nuevos dispositivos como
móviles, PDAs o tablets.

3.3. EL LENGUAJE DE PROGRAMACIÓN O#


En párrafos anteriores se definía C# como un lenguaje de programación orientado a objetos,
sin embargo en él se pueden implementar todos los elementos de la programación estructurada.
De hecho, en este primer capitulo dedicado a C# vamos a usar el lenguaje como si se tratara de
un lenguaje estructurado sin tener en cuenta el modelo de objetos que estudiaremos en capítulos
posteriores. A continuación, implementaremos en C# todos los componentes de programación
estructurada vistos en el Capitulo 2.

3.3.1. ESTRUCTURA DE UN PROGRAMA EN C#


Una aplicación C# puede estar formada por uno o más fícheros de código fuente teniendo en
cuenta que uno de ellos es el principal, el ejecutable, que se encargará de hacer referencia de
algún modo al resto. Por ejemplo, si creamos una aplicación de consola, esta puede estar formada
por varios fícheros de código, sin einbargo solo imo tendrá la etiqueta de ejecutable y cuando
demos la orden de poner en marcha el programa serán las lineas de este las que se leerán.
Para estudiar la estructura de un fichero de código fuente en C#, vamos a acceder a Visual
Studio y generar un proyecto de consola^ llamado Ejemplo. Si tenemos alguna duda en el
proceso de creación de proyectos en Visual Studio véase en el Capitulo 1 el Apartado 1.8.3.
CREAR UN NUEVO PROYECTO EN VISUAL STUDIO EXPRESS 2012.
Tras crear el nuevo proyecto. Visual Studio genera un único fíchero de código fuente, que
dará lugar al ejecutable de la aplicación. Veremos algo similar a la Figura 3.1.
El fíchero de código fuente generado se denomina Program.cs siendo es la extensión de los
ficheros C#.

Aplicación de consola
Todo código C# se encuentra encapsulado en
Busing Systeraj una clase. En nuestro ejemplo \emos como
using System.Collections.Generic;
using System.Linq; aparece la línea class Program abriéndose una
using System.Text; llave y cerrándose esta casi al finalizar el
using System.Threading.Tasksj programa. Entre estas llaves incluiremos
Bnamespace Ejemplo
atributos y métodos. En primera instancia sólo se
pretende que el lector se familiarice con esta
El class Prcg.-ain estructura, no que la entienda, el entendimiento
I { llegará cuando estudiemos la programación
B static void [-lainístringí] args)
{ orientada a objetos.
}
}
Así, solo debemos tener en cuenta que el
> nuevo código que escribamos debe incluirse
Figura 3.1. Código fuente del fichero
entre las llaves de inicio y cierre de la clase
Program.cs. Program, más concretamente entre las llaves de
la función Main.
Toda clase que vaya a ser ejecutada debe eontener la función Main. Una función es un trozo
de código al que se le denomina con un nombre, de fonua que cada vez que queramos ejecutar
ese código solo tendremos que indicar el nombre de la función. Por ejemplo, supongamos en
pseudoeódigo que tenemos el siguiente trozo de código:
Entorno:
entero Suma, Numl, Num2;
Inicio;
Escribir "Introduce número 1";
Leer Numl;
Escribir "Introduce número 2";
Leer Num2;
Suma=Numl+Num2;
Escribir "La suma de ambos número es:";
Escribir Suma;

Si queremos realizar íá suiña de más ¿euVpar de número tendríamos que repetir este código
una y otra vez en el programa prineipal. Para evitar esto creamos una función o subprograma de
forma que:
Subprograma Sumar Entero's
Entorno:
entero Suma, Numl, Num2;
Inicio:

Escribir "Introduce número 1";


Leer Numl;
Escribir "Introduce número 2";
Leer Num2;
Suma=Numl+Num2;
Escribir "La suma de ambos número es:";
Escribir Suma;
Fin Sumar Enteros;
A partir de ahora podemos realizar el código principal como sigue:
/*Declaración del subprograma*/
Subprograma Sumar_Enteros
Entorno:

Suma, Numl, Num2;


Capítulo 3. iniciación al lenguaje C# 95

Inicio:
Escribir "Introduce número 1";
Leer Numl;
Escribir "Introduce número 2";
Leer Num2;
Suma=Numl+Num2;
Escribir "La suma de ambos número es:";
Escribir Suma;
Fin Sumar_Enteros;
/*Inicio del programa principal*/
Entorno:
entero A; // Variable del programa principal"
Inicio:
Sumar_Enteros;
Escibir "Dame una valor para a";
Leer A;
Sumar Enteros;

Cada vez que en el programa principal aparece la línea Sumar_Enteros; se ejecuta el código
que se encuentra entre Subprograma Sumar•JEnteros y Fin Sumar_Enteros;

using System;
La Figura 3.2 muestra de forma
using Systefs.Collcctions.Gencric; diferenciada cada zona de un programa C#.
using Systeffi. Linq;
using Systeffi.Text;
.using Systetr.Thrcading.Tasks;
En la parte superior, se incluyen
referencias a paquetes extemos. A groso
'^airespace Ejen-plo
{
modo podemos decir que un paquete es un
elemento de C# que incluye un conjunto de
clases. Estas clases a su vez están formadas
'tatic void N^in(sfing[ j args;
{ I por funciones que pueden sernos útiles en
int nuffil, nur2;
int suira;
nuestras aplicaciones. Si queremos usar un
C;'ccíf.WriteLine("Introduce un núxepo"); paquete externo colocaremos la palabra
nu»l • Syste».:* t "r.ParseCCcr.súlc.ReadlineO);
Cs-isol'..WriteLÍne("Introduce otro nútrero**); reservada using seguida del nombre del
nuffi2 » Systeffi.Id*r:.Par5e(CcniíIe.RcadLine());
sutca • nunl •f nuis2;
paquete en la parte superior del código.
Ccnsclc.WriteLineCLd suea esr^-í-suma); Cuando creamos un proyecto, tal y como
Censóle.ReadLine();
^ hemos hecho con el proyecto Ejemplo, se
public getfiorrbre()| genera un paquete, que permitirá agmpar
return this.noffibrej^S nuestros propios métodos.
Un paquete debe contener al menos una
clase. Las clases se definen con el encabezado
que se muestra en Si queremos que
publíciñtgcUÍQ nuestro código sea ejecutable, es decir
{
return this.n; estamos generando un .exe en lugar de una
)
public vcid setM(int nue%"0)
librería u otro elemento, la clase debe
{
this.n ■ nuevoij
incorporar una función denominada Main
-X
}
¿ y (podemos verla en la primera zona O)
Figura 3.2. Partes de un programa O#.

Además de /**/ podemos usar // para comentarios de una sola linea.


Para finalizar, una clase suele estar formada por variables de clase o atributos y métodos
Cada método a su vez puede contener otras variables, siendo estas locales al método o
función, es decir, son variables visibles únicamente el período de tiempo en que se este usando la
función a la que pertenecen.

3.3.2. PALABRAS RESERVADAS EN C#

Cada línea en un programa escrito en C# está compuesta por una serie de caracteres que se
agrupan formando los denominados tokens. Un token no es más que un componente lé.xieo que
conforma el vocabulario del lenguaje. En lenguaje natural un token podría ser una palabra,
tenemos en cuenta que cuando escribimos usamos letras que según como se agrupen formarán
unos componentes léxicos u otros dando significados diferentes a la sentencia final.
Básicamente, cada sentencia C# estará fonnada por determinados tokens, tales como
palabras reservadas, identifícadores, cadenas de caracteres u operadores.
El término Palabra reservada refiere un conjunto de palabras que realizan una función
concreta en el lenguaje. A la hora de crear nuevos elementos en nuestro programa no podremos
usar ninguna de estas palabras para ello. La siguiente tabla muestra el conjunto de palabras
reservadas que utiliza C#(http://msdn.microsoft.com/en-us/library/x53a06bb.aspx).

abstract break byte case

catch char checked class const continué decimal

default delegate do double else

explicit extem false finally fixed for

foreach goto if implicit intcrface

intemal is lock long namespace nuil

object operator out override prívate protected

public readonly ref retum sbyte sealed short

sizeof stackalloc static string switch this

throw true try typeof ulong unchecked

unsafe ushort using virtual volatile while

3.3.3. IDENTIFICADORES EN O#
Un identificador es un componente del lenguaje de programación que como su nombre indica
se encarga de identificar un elemento concreto, da nombre a un elemento del programa. Usamos
identificadores cuando creamos una variable, una constante, declaramos una nueva clase,
creamos una función, etc. A la hora de definir un identificador cada lenguaje de programación
establece una serie de reglas que indican qué caracteres y símbolos podemos combinar para
formar este. En C#:
Capítulo 3. iniciación al lenguaje O# 97

■ Un identificador es una secuencia de caracteres alfanuméricos (suma, numl, num2,


sumar_numeros, etc.). Entre estos caracteres podemos usar vocales acentuadas y la
letra ñ aunque particularmente no se aconseja la utilización de estos.
■ Todo identifícador comienza con un carácter, en mayúsculas o minúsculas, nunca un
número, de fonna que 3num NO será un identifícador válido.
■ C# hace distinción entre mayúsculas y minúsculas de forma que SUMA, Suma y
suma serán identifícadores diferentes.

A la hora de crear un identifícador se aconseja:


■ Usar nombres significativos, relacionados con el elemento al que vaya a ser
asignado. Por ejemplo, si queremos realizar un función que sume don números lo
usual es establecer como identifícador de dicha función algo similar a
SumaNumeros.

■ No usar nombres excesivamente largos.


■ Si queremos usar nombres compuestos por más de una palabra podemos adoptar
varios criterios, uno de ellos es colocar la primera letra de cada palabra en
mayúsculas, por ejemplo SumarNumeros. Otro es usar el carácter guión bajo (_J, u
otro carácter especial para diferenciar cada palabra del identifícador, por ejemplo
Sumar_Numeros.

ACTIVIDAD 3.1

Indica si son correctos los siguientes identifícadores:


1H2 hola true
resta2-3 $ prueba 2numeros

3.3.4. COMENTARIOS EN C#

Un comentario es un elemento del lenguaje de programación que da al programador la opción


de establecer pequeñas descripciones de las partes o elementos de su programa. El uso de
comentarios es bastante importante, sobre todo en programas extensos. Se aconseja establecer un
breve comentario antes de iniciar cada función donde se indique cuáles son sus valores de
entrada, de salida e infonnación sobre qué tipo de operación realizará.
En C# podemos usar dos tipos de comentarios que nos serán conocidos ya que lo hemos
usado en pseudocódigo.
■ /*Comentario*/: Comentario de varias líneas. Cuando queramos establecer
información que ocupará más de una línea antepondremos /* a esta. Finalizaremos el
comentario con */.

■ //Comentario: Comentario de una sola línea.


98 Programación

r nancspftcc Ejei^lo
{
r-, class P-cQfaT
I <
/•Funcíén p-lncip»l ^
Recibe un aToy de cadenas de ca-'acte'-es
fio devuelve ningún vale- de salida*/
static void K«in(strlng[] args)

//Oecle-ac de va-iables ""í— ^SSÍSí


int nucí» nus¿; L*—^u-J^eeEHSe
int su«a;
C-nsc l'_'.HriteLÍne('Intrcdjce un nú*e"o");
nuffl - SysteiB.!• t : r.Parse(Cc-se le.ReadLine());
1 c'• s ; .WriteL ine("Intrcduce ote© núte'c");
nuíB2 • SysteiE. r T.ParseíCc-se Ir.fteadl íne());
Stau • nua.l e nwc2;
Ct's.1-.WriteLine(".a su^a es:* ♦ suea);
Ccnsr >.ReadLine();

Figura 3.3. Código fuente comentado.

3.3.5. TIPOS DE DATOS EN C#


C# es un lenguaje de programación tipado, es decir, cada variable, atributo, constante o valor
devuelto por una función debe encontrarse en un rango de elementos previamente establecido,
debe ser de un tipo de datos concreto.
Un tipo de datos incluye un rango de valores que puede adoptar además de una serie de
restricciones a aplicar sobre ellos, como operaciones que se pueden realizar. Por ejemplo, el tipo
de datos int recoge el rango de números enteros comprendido entre -2.147.483.648 y
2.147.483.647 a los que se les puede aplicar operaciones del tipo +, -, *, etc.
En C# encontramos diferentes tipos de datos:
■ Tipos de datos integrados o básicos como int, char o float. Son tipos de datos
usados muy comunmente en las aplicaciones y fonnan parte de la sintaxis del
lenguaje.
" Tipos de datos creados por el usuario, es decir objetos de clases dcsanolladas por
el programador a través del uso de class o interface.
Además, estos se pueden definir como:
■ Tipos de valor de forma que las variables de estos tipos almacenan datos. Todos los
tipos integrados son alias de algún tipo valor, por ejemplo int es el alias del tipo
valor System.Int32.
■ Tipos de referencia. Las variables de estos tipos almacenan una referencia a los
valores reales. Suelen denominarse Objetos.
■ Tipos de puntero. Este tipo solo puede usarse en modo usafe (modo no seguro) y es
una referencia directa a una dirección de memoria. Cuando usamos punteros estamos
accediendo de forma directa a una dirección de memoria determinada que almacena
un dato del tipo especificado por el puntero.
Así, dispondremos de una serie de tipos creados e incluidos en la sintaxis de C#(tipos valor o
integrados en el lenguaje), tipos que serán definidos por el usuario a través de class o interface u
otras clases o interfaces ya definidas por C#(tipos referencia) y tipos puntero.
Capítulo 3. Iniciación al lenguaje C# 99

3.3.5.1. TIPOS DE DATOS INTEGRADOS EN C#

A continuación se muestra una tabla donde se incluyen los tipos de datos integrados en el
propio lenguaje de programación.
Tipo Descripción Rango de valores Alias

SByte Bytes con signo 8 [-128, 127] sbyte

Byte Bytes sin signo 8 [0, 255] byte

Intl6 Enteros cortos con signo 16 [-32.768, 32.767] short

Ulntló Enteros cortos sin signo 16 [0, 65.535] ushort

Int32 Enteros nonnales 32 [-2.147.483.648, int


2.147.483.647]
UInt32 Enteros nonnales sin 32 [0, 4.294.967.295] uint
signo
Int64 Enteros largos 64 [-9.223.372.036.854.775.808, long
9.223.372.036.854.775.807]

UInt64 Enteros largos sin signo 64 [0- ulong


18.446.744.073.709.551.615]
Single Reales con 7 dígitos de 32 [1,5x10-^^-3,4x10^'^] float
precisión
Double Reales de 15-16 dígitos 64 double
de precisión
Decimal Reales de 28-29 dígitos 128 decimal
de precisión
Boolean Valores lógicos 32 true, false bool

Char Caracteres Unicode 16 ['\uOOOO', AuFFFF'] char

String Cadenas de caracteres Variable El pennitido por la memoria string

Object Cualquier objeto Variable Cualquier objeto object


Así, C# incluye un tipo de datos llamado Boolean. En realidad, este es el nombre del objeto
en System, su alias es bool. Esto quiere decir que a la hora de crear una variable de este tipo
podremos hacerlo indistintamente usando System.Boolean o bool. En los siguientes apartados
estudiaremos la declaración de variables y comenzaremos a entender claramente el porqué de
esta tabla.

3.3.5.2. LITERALES

Un literal en un lenguaje de programación, ya sea este C# o cualquier otro, es la


representación de los valores exactos que puede adoptar un tipo de datos concreto. Por ejemplo,
podemos decir, que un tipo de datos entero puede contener valores formados a partir de los
dígitos 0 a 9,siendo estos los literales para este tipo concreto.
100 Programación

Básicamente un literal es cualquier valor que puede adoptar un tipo de datos. Al igual que
existen diferentes tipos de datos, encontramos diferentes tipos de literales;
■ Enteros.

■ Reales.

■ Lógicos.
■ Carácter.

■ Cadena.

■ Valor nulo.

LITERALES ENTEROS

Un literal entero se puede representar en C# mediante el sistema de numeración decimal o


hexadecimal, es decir, podemos representar un número mediante el uso de los dígitos O a 9 o bien
los dígitos de O a 9 y letras A a F, precedidos estos últimos por Ox. Tanto un sistema de
numeración como otro permite el uso de los signos - y + para establecer si son valores negativos
o positivos.
Literales enteros representados en el sistema de numeración decimal validos podría ser 89,
1024 o-13.

Literales enteros representados en el sistema de numeración hexadecimal válidos podrían ser


OxAA,-0x12 o 0x24F.
El número de valores literales enteros que podemos representar es infinito sin embargo a la
hora de crear una variable y asignarle un literal de este tipo debemos conocer el rango que este
permite, por ejemplo, a una variable de tipo byte se le asignan literales enteros, pero solo son
validos los valores de O a 255, si quisiera asignar el valor -4 a esta variable el compilador
mostraría un mensaje de error.
LITERALES REALES

Valores reales, parecidos a los anteriores, pero siempre deben expresarse en formato decimal,
aunque no presenten decimales. Para ello se hace uso del punto (.), separador de parte entera y
parte decimal. Por ejemplo, si queremos asignar un cero a una variable real debemos escribir 0.0,
el O sin la parte decimal no está permitido entre estos literales, este sería un literal entero.
Ejemplos de estos literales podrían ser 12.345, -0.1 o 1.764. En el caso de que queramos
representar valores grandes podemos utilizar el símbolo E o e, tal que se está expresando el
literal a través de una base y un exponente. Ejemplo de literales reales expresados con el uso de
base y exponente podrían ser 2.3E12 o 0.2e-10.
LITERALES LÓGICOS
Se entienden por literales lógicos los valores true y false. Los literales lógicos se usan con el
tipo de datos bool, siendo estos los únicos valores que puede adoptar. Nomialmente se usan en
sentencias condicionales para determinar las instrucciones que se deben ejecutar, siendo true
equivalente a condición cierta y falsa condición falsa.
LITERALES CARÁCTER
Representan el conjunto de caracteres alfanuméricos, es decir, letras, números y signos
encapsulados entre el símbolo de comilla simple ('). Ejemplos de este tipo de literales serían 'a'.
Capítulo 3. Iniciación al lenguaje O# 101

'M','8' o todo el conjunto de caracteres unicode. Debemos prestar atención a la hora de usar
estos valores, ya que no es lo mismo 'M' que 'm'. Además, si a la hora de usar variables de tipo
carácter le asignamos el valor '9' debemos siempre tener en cuenta que este dato es un carácter y
no pueden realizarse ningún tipo de operación aritmética sobre él.
Existen una serie de caracteres especiales, véase la siguiente tabla.
Código
Carácter Carácter Carácter
de escape
Tabulación
Comilla simple \' Retroceso
horizontal
Tabulación
Comilla doble \" Salto de página
vertical
Barra
Carácter nulo \0 Nueva línea
invertida

Alanua \a Retomo de carro

Ccn5cle.WriteLine("Hola \n f-'undo|");

Figura 3.4. Parte del código fuente donde se usa Figura 3.5. Zona de la consola que muestra el
una función de visualización de información en
mensaje "Hola Mundo". Lo vemos separado en dos
pantalla. En ella el carácater de escape '\n'.
partes debido a que \n conlleva un salto de linea.

LITERALES CADENA DE CARACTERES

Los literales de cadena de caracteres no son más que un conjunto de caracteres encapsulados
entre comillas dobles. Una cadena puede estar compuesta por ninguno, uno o varios caracteres.
No es lo mismo 'c' que "c", el primero es un literal de tipo carácter mientras que el segundo
aunque esté compuesto por un único elemento será considerado como un literal cadena.
Cuando usamos literales de caracteres podemos incluir en ellos los caracteres especiales
vistos tales como \', W, etc. Así, si entre un par de dobles comillas incluimos el código de escape
\t este será traducido como una tabulación, de forma que el texto que se encuentre antes o
después de \t se mostrará visiblemente separado. Sin embargo, si delante de esta cadena de
caracteres colocamos el símbolo @,el código de escape no será interpretado y se mostrará como
un carácter más.

Icnsole.WriteLine(@"Hola \n f'undo");
Consolé.WriteLine("Hola \n "■■undo"*);
w^"nrey7/cvvsers/i53aevuocumentsAVfsuarya'~

iHola Nn Hundo ^

Figura 3.6.Uso de @ para obtener un literal plano. Figura 3.7. Uso de caracteres especiales en las
Vemos como aparece el carácter especial como cadenas de caracteres. En la Figura se usa un
otro más, no es interpretado. salto de linea que es interpretado correctamente.
LITERAL NULO

El literal nulo, o nuil, se usa en objetos de clase. Un objeto adquiere este valor de forma
transparente al usuario cuando se crea un objeto pero no se incializa, es decir, creamos una
variable de objeto pero no damos valor inicial a esta.
3.3.6. DECLARACION DE VARIABLES EN C#

En pseudocódigo veíamos que existían unos elementos en un programa que se encargaban de


almacenar información, datos. A estos elementos se les llamaba variables. El pseudocódigo
precede a cualquier lenguaje de programación y normalmente, todo aquello que se codifica en
pseudocódigo puede codificarse en el lenguaje escogido por el programador.
En los apartados anteriores hemos estudiado qué son identificadorcs, tipos de datos y literales
y es esto todo lo que necesitamos para entender cómo se lleva a cabo la creación de una variable.
Asi, para declarar'' una variable necesitaremos:
■ Concretar el tipo de datos que va a almacenar la variable.
■ El nombre o identificador que la definirá durante todo el programa.
■ Los valores o literales que podrán asignárseles, además de qué valor será su valor
inicial.

Una declaración de variable válida en C# podría ser: char letra;


Para entender la importancia del Capitulo 2, y que es en él donde se ha desarrollado el
algoritmo y a continuación la codificación es meramente una conversión a un lenguaje, vamos a
realizar el paso de convertir la declaración de una variable en lenguaje pseudocódigo a lenguaje
de programación C#:
Pseudocódigo
Entorno: ; . ;
; int numero; ;
entero numero;

Hemos estudiado diferentes tipos de datos, valor, referencia o puntero y entre los de tipo valor
encontrábamos una serie de tipos básicos incluidos en la propia sintaxis de C#. La sintaxis
formal de creación de variables de tipos de datos simples es la que sigue:
Creamos una variable cuyo identificador es
Nombre_de_Variable y almacenará literales
tipo_de_datos Nombre_de_Variable;
correspondientes al tipo de datos establecido como
tipo de datos.
Declaración de una variable llamada Var, que
almacena literales del tipo de datos establecido
tipojdejdatos Var=valor; . como tipo_de_datos. Además, en esta instrucción
estamos asignando un valor inicial de valor a dicha
variable. Estamos Inicializando la variable.
Similar a la primera sintaxis. En esta ocasión al
contener Vari, Var2 y Var3 el mismo tipo de datos
tipojdejdatos Vari, Vari, Var2;
pueden declararse en la misma linea separándose
estas por el carácter coma (,).
Declaración de la variable Var del tipo de datos
tipojdejdatos Var;
Var=valom;
tipo de_datos y posterior inicialización al valor
valorn.

^ El término declarar una variable se considera al momento en que se hace referencia por primera vez
a ella estableciéndose el tipo de datos que va a almacenar y su identificador como elementos mínimos.
Capítulo 3. iniciación al lenguaje C# 103

NOTA: En C# no esta pennitido el uso de variables sin inicializar, es decir, a la hora


de declarar una variable es necesario que ya sea en la misma declaración o en líneas
sucesivas se le asigne un valor determinado antes de ser usada.

Como ejemplo de declaraciones de variables en C#:


Variable llamada letra del tipo de datos
char letra=
char, es decir, contendrá literales de carácter.
Concretamente en la declaración se inicializa
esta al valor 'a'.
Creación de la variable cuyo identificador
float numero; es numero. Del tipo de datos float, contendrá
números decimales. Aún está sin inicializar.
booi condicioni23; i Variable booleana llamada condicionl23.
condicioni23=true; I Inicializada a true.
Todos los tipos simples, que a su vez son tipos de valor, derivan de la clase
System.valueType. Decíamos que los tipos de datos integrados eran alias de objetos incluidos en
System, de fonua que podemos crear variables de este tipo usando sus objetos. Así, podemos
realizar las siguientes declaraciones:

int numero=new int();


Declaración de una variable de tipo entera,
equivale a int numero=0;
Declaración de una variable de tipo
char letra; carácter llamada letra que es inicializada
letra=new char(); posteriormente al valor por defecto '\0'.
Equivale a char letra='\0';
I NOTA: La instrucción del tipo int numero=new intQ se divide en dos partes. La
¡ primera de ellas es int numero, en ella se produce la declaración, es decir, se indica que
j estamos creando una variable de tipo entera. La segunda, new intQ es la parte de
i contrucción y reserva de espacio en memoria, así como la inicialización de la variable, si
I omitiéramos esta parte no podríamos operar con ella. Al usar new intQ estamos
¡ realizando una llamada al constructor de la clase, siendo una de las funciones principales
; del contructor la inicialización. Estudiaremos estos conceptos cuando veamos las clases y
i los objetos.

Si lo desea el lector puede ver los valores a los que se inicializan los diferentes tipos de
variables en función de su tipo de datos en la siguiente referencia web:
http://msdn.microsoñ.com/es-es/library/83fhsxwc(v=vs.80).aspx.
En C# las palabras reservadas se colorean en azul. Aparecerá una línea de color verde
subrayando una variable cuando esta ha sido creada pero no se usa en ninguna instrucción.
Si observamos una línea de color roja bajo una instrucción esta indica que se ha producido un
error. Este debe ser corregido si deseamos ejecutar el programa.
El código de la Figura 3.8 muestra todas las variables que se han usado a lo largo de la
explicación de declaraciones de tipos.
//Declaración de variables
int nutnl, num2j
char l^et^ = 'a';
float mjme^j
bool cp_ndici.onl23j
condicionl23 = false;
int nuevo = new int();
char ^aract^;
carácter = new char();

Figura 3.8. Ejemplo de declaración de variables en C#.

ACTIVIDAD 3.2

Crea una variable de tipo real con 7 dígitos de precisión denominada décima. Asígnale el
valor 23.4. A continuación crea otra variable de tipo char a partir del constructor de forma que su
valor inicial sea '\0'.

3.3.7. CONSTANTES EN C#

Como ya se explico en el Capitulo 2, una constante se encarga de almacenar un valor que no


se modificará en ningún momento durante la ejecución del programa. En C# indicamos que un
elemento es una constante anteponiendo al tipo de datos la palabra reservada const, ejemplo:
const double PI=3.1415;
La inicialización de la constante debe realizarse en el mismo instante en que se declara como
obvio.

static void Main(string[] args)


{
const double PI = 3.1415;
int radio;
double circunferencia;

Consolé.VJriteLineC'Escribe el radio del círculo");


radio=System.Int32.Parse(Ccnscle.ReadLine());
circunferencia = 2 * PI * radio;
Console-WriteLine("la circunferencia es;"+circunferencia);
Consolé.ReadLine();

Figura 3.9. Programa que calcula la circunferencia de un circulo. Se ha declarado la constante PI.
Además de las constantes como las hemos estudiado, en ocasiones precisaremos en nuestros
programas utilizar literales que no hayan sido incluidos como valor de ninguna variable, es decir,
literales constantes. Por ejemplo, si queremos realizar una comparación podemos escribir
directamente numero < 3, siendo 3 una constante literal. Para cada tipo de datos C# dispone de
este tipo de constantes, ahora bien, si usamos literales constantes numéricas, cada vez que
escribamos un número este será tomado como entero, ¿qué puedo hacer para que sea detectado
por el compilador como un tipo long?; además, con el uso de números decimales, por defecto un
Capítulo 3. Iniciación al lenguaje O#

número con punto se trata como doublé, ¿cómo podemos indicar que sea registrado como float
para que ocupe menos espacio?
En C# podemos usar los caracteres u, 1 o f para indicar si estamos tratando con números
unsigned, long o float respectivamente. Según esta infonnación:
- 13/int ■ 13L/long ■ 13u/unsigned
- 2.1234/double - 2.1234f/float ■ 13ul /ulong
Podemos usar las letras u, 1 y f en mayúsculas o minúsculas, a continuación del literal
numérico. Podemos realizar combinaciones de las mismas, por ejemplo: ul.

3.3.8. CONVERSIONES DE TIPOS DE DATOS EN C#

Normalmente un programa estará compuesto por gran multitud de variables, declaradas al


inicio del programa, cada una de ellas de diferentes tipos de datos. Al realizar operaciones sobre
ellas y almacenar los resultados es probable que tengamos que adaptar estos para que sean
guardados correctamente.
Normalmente, cuando asignamos a una variable un valor de un tipo de datos de menor
tamaño la conversión se hace implícitamente, es decir, de fonna automática, por ejemplo, si
asignamos a una variable de tipo double una variable int la operación se realizará de forma
implícita sin tener que adaptar el código. Si realizaremos la operación al revés la conversión sería
explícita de forma que sí deberíamos de usar algún mecanismo de conversión. Normalmente
utilizamos lo que se denomina casting, es decir, colocamos entre paréntesis el tipo de datos al
que queremos convertir el valor de la variable que vamos a asignar.
Veamos el siguiente código:

En se han declarado tres variables. Una de


ellas es de tipo double, mientras que num y num2
son de tipo int. Se considera al tipo int un tipo menor
double resultado;
.^nt nuil) = 3, num2=9;J
9
1 que double.

resultado = num; En realizamos la asignación resuItado=num,


^numZ = (int)resultado;
ambas variables son de tipos de datos distinto, sin
embargo, como la asignación se realiza de rm int a un
Figura 3.10. Conversión implicita y explícita
de variables.
double no se producen fallos, de forma automática el
valor de num, es decir 3, se asigna a resultado como
3.0.

En la siguiente línea todo cambia. Ahora estamos realizando la asignación de un valor


double a una variable int, es decir, estamos asignando a un tipo de menor tamaño otro de mayor
capacidad con lo que la asignación no puede darse a no ser que se use algún método de
conversión del valor de double. Así, entre paréntesis colocamos el tipo de datos de la variable a
la que vamos a asignar, es decir int, seguido del valor o variable en este caso double. El valor de
resultado se adaptará para que pueda guardarse, quepa, en la variable entera num2. La línea se ve
como en la Figura 3.10 num2=(int)>'esultado.
En ningún momento estamos realizando la conversión de la variable, es decir, si resultado es
double seguirá siendo double tras el casting, solo adaptamos su contenido, su valor.
La sintaxis básica de esta operación será:

variable =(íipo_de_dato_menor) variable_tipo_dato_rnayor;


variable — (tipo_de_dato_menor) expresión^;
Ejemplos:
/*La conversión de un tipo int a char consiste en la extracción del valor de
tipo carácter asociado a ese número en el código ASCII o Unicode. A la inversa
se almacenará en la variable entera el código asiciado al carácter indicado*/
char a=(char)115;
int num=(int)'a';

//Otro ejemplos de conversión


int resultado = (int)(10.3/4);
int resultado = (int)13.98;

ACTIVIDAD 3.3

Averigua si el siguiente código fuente tiene errores. En caso afinnativo tras localizarlos
corrígelos.
int p=3.0;
double resultado;
resultado = p*2;
p=(doiible)(8.1/5);
resultado=p;

3.3.9. OPERADORES EN C#

A la hora de formar las instrucciones, en muchas ocasiones estas son expresiones en las que
se concatenan diferentes tipos de tokens mediante un elemento denominado operador. Existen
diferentes tipos de operadores, ya en el Capítulo 2 avanzábamos la existencia de algunos, C#
posee una gran variedad de ellos.
Clasificaremos los operadores en:
■ Aritméticos.

■ Relaciónales.

■ Lógicos.
■ Asignación.
■ Operador sizeof.
■ Nivel bits.

3.3.9.1. OPERADORES ARITMÉTICOS

Los operadores aritméticos, como su nombre indican, peirniten la creación de expresiones


aritméticas.

La expresión da como resultado un tipo de datos de mayor tamaño.


Capítulos. Iniciación al lenguaje C# 107

Nombre Descripción
Positivo Indica que el número es positivo. Operador uñarlo.
Negativo Se usa para establecer el número como negativo. Operador
unario.
Suma Suma aritmética de dos valores.
Resta Resta aritmética de dos valores.
Multiplicación Multiplicación aritmética de dos valores.
División División aritmética de dos valores.
Módulo Obtiene el resto de la división entera.
Incremento Incrementa en uno el valor que contenga en operando. Es un
operador unario.
Decremento Disminuye en uno el valor del operando. Igualmente unario.
NOTA: A la hora de trabajar con operadores debemos tener en cuenta que existen
operadores uniarios y binarios. Un operador unario es aquel que solo necesita un
operando mientras que un operador binario precisará de dos. Por ejemplo, el operador -
usado para el cambio de signo o establecer un número negativo es unario, sin embargo, la
suma es binaria ya que precisa de dos valores u operandos para proceder.

Los operandos que usaremos con estos operadores serán de los tipos básicos int, float, double,
char, etc. Si los operandos son del mismo tipo la expresión resultante de aplicar el operador será
del tipo de los operandos. En caso de que usemos operandos de diferentes tipos de datos, uno
será de mayor tamaño que otro de fonna que el de menor tamaño se convertirá al de mayor rango
y el resultado final será de este último tipo, por ejemplo 13.2+4 dará como resultado 17.2, es
decir un dato double.

Podemos realizar operaciones entre números enteros y caracteres de forma que se toma el
código interno del carácter para realizar la operación siendo el resultado final int. De igual modo,
si realizamos una operación sobre dos variables tipo char el resultado final será entero.
Debemos tener en cuenta ciertas peculiaridades a la hora de usar algunos de estos operadores:
DIVISIÓN
A la hora de usar el operador división (/), la división puede ser tomada como división entera
o división real, es decir, si los operandos que se usan son enteros el resultado de la división será
entera, en caso contrario será double. Por ejemplo:
Ya que 10 y 2 son tomados por el compilador como números enteros el
10/4=2
resultado de la división también lo será. Obtendremos el valor 2.

En el momento en que uno de los operandos es real la división pasará a


proporcionar un resultado también real. Así, si en lugar de escribir 10,
10.0/4=2.5
sustituimos este valor por 10.0 estamos indicando que es un valor double de
forma que el resultado de la división será 2.5.
INCREMENTO

El operador de incremento no proporcionará el mismo resultado si lo colocamos delante o


detrás de la variable que se desea incrementar.
Primero se usará el valor de x y posteriormente se realiza el incremento.
Si se establece x++ en una única línea, esta apreciación no se nota pero si
nos encontramos con la siguiente instrucción y=x++, ¿qué ocurre?
Recordamos que primero se asignará a y el valor de x y posteriormente se
incrementará esta, asi si x=3 el resultado final de la expresión será v=3,
x=4.

En esta otra sentencia el proceso será el inscrso al explicado en el


^ ^^ párrafo de arriba. Primero se produce el incremento de la sariable y
posteriormente se usará esta. Así, en el ejemplo y=++x suponiendo que .\=3
obtendremos como resultado y=4 y x=4.
DECREMENTO

El operador de decremento actúa de forma similar al de incremento.


Primero se usará el valor de x y posteriormente se rcaliz.a el deeremento.
Si x=3 y realizamos la operación y=x—, y=3 y x=2;
En esta otra sentencia el proceso será el inverso al explicado en el
párrafo de arriba. Primero se produce el decremento de la variable y
posteriormente se usará esta. En la instrucción y=~x donde x=3
obtendremos como resultados y=2 y x=2.
SUMA

El operador suma suele usarse para sumar dos operandos numéricos o de tipo carácter, sin
embargo tiene otra utilidad, la de concatenar cadenas de caracteres.
Así, podemos usar el operador con dos datos de tipo string o bien un dato de tipo string y otro
de cualquier otro tipo de forma que en esta última casuística el resultado final será también
string.
Tenemos dos cadenas de caracteres, "Hola" y
"Mundo", al realizar la expresión estamos asignando a
string saludo="Hola" +"Mundo" la variable saludo el valor HolaMundo. El resultado
no contiene espacios en blanco ya que no lo hemos
especificado en las cadenas operando.
En esta otra sentencia estamos creando una
variable de tipo cadena (string) con el nombre
mensaje. Le asignamos la frase "El valor es:" junto al
string mensaje="El valor
valor de la variable cuyo nombre es variable. Al
es:"+variable:
realizar la concatenación de operandos de diferentes
tipos con un operando de tipo string el resultado final
será de tipo cadena de caracteres.

3.3.9.2. OPERADORES RELACIONALES

Los operadores relaciónales se utilizan en sentencias condicionales. Los hemos estudiado ya


en el Capítulo 2, algunos son similares a los usados en pseudocódigo.
Capítulos. Iniciación al lenguaje C# 109

Operador Nombre Descripción

Devuelve verdadero si una variable es menor estrictamente


Menor que(menor que otra.
estricto) a < 3. Para que la expresión sea cierta, a debe ser como
máximo 2.

1
Devuelve verdadero si una variable es mayor estrictamente
Mayor que (mayor que otra dada.
estricto) a > 3. Para que la expresión sea cierta, a debe ser como
mínimo 4.

Similar a menor pero en este caso se incluye en valor


úbicado a la derecha en la comparación.
Menor o igual
a <=3. Para que la expresión sea cierta, a debe ser como
máximo 3.

Similar a mayor con la diferencia de que se incluye en la


comparación el valor que se encuentra a la derecha.
Mayor o igual
a>=3. Para que la expresión sea cierta es necesario que a
como mínimo tenga asignado el valor de 3.
Comprueba si los valores de los operandos son iguales. Para
Igual a evitar confusiones existe distinción entre el operador de
comparación igual(=)y el de asignación (=).
Distinto a Comprueba si los valores de los operandos son diferentes.

3.3.9.3. OPERADORES LOGICOS


Ya estudiábamos su definición en el Capítulo 2, se usan sobre todo para concatenar
condiciones y realizar una acción u otra en función de la veracidad o falsedad de los resultados.
En C# encontramos los siguientes operadores lógicos:
Operador Nombre Descripción

Proporcionará como resultado el valor true en caso de que las


AND o Y
condiciones o variables booleanas que concatenan sean ciertas,
lógico
en cualquier otra combinación el resultado será falso.
Proporciona como resultado el valor false solo si las condiciones
OR u O lógico o variables booleanas que concatenan son ambas falsas. En
cualquier otra easuística se obtendrá como resultado final true.
Invierte el valor resultado de la condición o variable de tipo
booleana sobre la que se aplique. Si la expresión da como
Not o No lógico
resultado true al aplicar este operador se tomará a false y a la
inversa.

Realiza la operación AND bit a bit siempre que usemos


And o Y lógico
variables de los tipos integrados. Realiza comparación bit a bit
a nivel de bit.
de cada operando.
^nivd dtfbk° ^ Realiza la operación Or bit a bit. Similar al operador &.
Además de los operadores lógicos estudiados, en este grupo encontramos el operador de
asignación condicional (?:). Este permite asignar a una variable un valor u otro en función de si
una condición es cierta o falsa. La sintaxis de componente es la siguiente:
i — —

i variable=(condición)?va¡or_si_yerdad:valor_si_falso; :
Un ejemplo de uso sería;
//declaración de las variables nota y mensaje "•
¡ int nota; :
; string mensaje=""; ;

! /*Las siguientes lineas permiten la comunicación con el usuario, almacenamos :


I en nota el valor de 1 a 10 correspondiente a la nota del aliimno*/
i Consolé.WriteLine("Introduce la nota del alumno"); ;
i nota=int.Parse(Consolé.ReadLine()); :

I /*La variable mensaje almacenará el texto "El alumno esta aprobado" o "El ;
: alumno esta suspenso" en función de la condición (nota>=5). El símbolo ? ;
■ separa la condición del resto de la instrucción. Los valores a asignar en ;
i función de si la condición es cierta o falsa se separan mediante .*/
I mensaje=(nota>=5)?"E1 alumno esta aprobado":"El alumno esta suspenso"; :
I Consolé.WriteLine(mensaje); ;

La línea mensaje=(nota>=5)?"El alumno esta aprobado":"EI alumno esta suspenso"; se


interpretará del siguiente modo:
Si la condición nota>=5 es cierta asignaremos a la variable mensaje el valor El alumno esta
aprobado en caso contrario la cadena de caracteres a almacenar será El alumno esta suspenso.

ACTIVIDAD 3.4

Realiza un pequeño programa que tenga una variable entera a la que se le asignará el valor 10
por defecto. La aplicación debe comprobar si este número es par o impar y en función de esta
condición debe mostrar el mensaje "El número es par" o "El número es impar". Aunque aún no
la hemos estudiado, usa el método Consolé.WriteLine(mensaje); para mostrar el texto
correspondiente en pantalla como en el ejemplo anterior. Usa el operador ?:.

3.3.9.4. OPERADORES DE ASIGNACIÓN


Los operadores de asignación permiten que el valor que se encuentra a la derecha del
operador se almacene en el identificador que se encuentra a la izquierda. El operador de
asignación por excelencia es el signo =.

const char letra='A


int numero=10;
mensaje=(condición)?opción_l:opción_2;

En C# se permite el uso de asignaciones múltiples, es decir, en una misma línea puede


aparecer en más de una ocasión el signo =, por ejemplo:
Capítulos, iniciación ai lenguaje C# 111

mt numero=2,numero2,numero3; I
I

numero3=numero2—numero;
II \
numero3=numero2+(numero=6); |
Las asignaciones se realizan de derecha a izquierda. Así, en numero3=numero2=numero
tomamos el valor de numero y se lo asignamos a numero2. A continuación, el valor de
numero2 es asignado a numero3. En el ejemplo de código mostrado, esta línea daría como
resultado numero=2, numero2=2 y numero3=2.
En la instrucción numero3=numero2+(numero=6) las asignaciones igualmente se realizan
de derecha a izquierda tal que primero se asigna un 6 a la variable numero (numero=6). A
continuación se suman numero2 y numerol y el resultado es almacenado en numero3. Para
esta línea de código y teniendo en cuenta que anteriormente la asignación hizo que numero,
numero2 y numero3 tomaran el valor 2, numero=6, numero2=2 y numero3=8.
Como ya se ha indicado en alguna ocasión se diferencia el operador de asignación del de
igualdad para evitar confusiones, tal que usamos un solo signo igual para la asignación (=) y dos
símbolos(=)para la comparación.
Además del = encontramos una serie de operadores en este grupo que permiten simplificar
instrucciones en las que se modifica el valor de una variable usando su valor anterior, por
ejemplo variable=variable+10;
Formato Formato
Operador Descripción
largo corto

+= Incrementa el valor de la variable en la cantidad x=x+y; x+=y;


que se especifique.
Resta el valor de la variable en la cantidad que se x=x-y; x-=y;
especifique.
*= Multiplica el valor de la variable en la cantidad x=x*y; x*=y;
especificada.
/= Divide el valor de la variable en la cantidad que x=x/y; x/=y;
se indique.
%= Asigna el valor del resto tras la división de la x=x%y; x%=y;
variable con una cantidad especificada.

3.3.9.5. OPERADOR SIZEOF

El operador sizeof devuelve el tamaño en bytes de un tipo de datos concreto. Su sintaxis es:

sizeof(tipo_de_datos);
sizeof(Objeto);
Encontraremos un mayor sentido al uso de este operador cuando trabajemos con objetos.
Existen otros operadores, como el operador punto (.) que igualmente utilizaremos con estos
tipos de datos,justo cuando queramos acceder a los elementos del mismo.
Ccnscie.KriteLine("Un tipo de datos int ocupa:" + si:ecf(int) + " bytes de rero-ia");

Figura 3.11. Uso del operador sizeof().

3.3.9.6. OPERADORES A NIVEL DE BITS

Los operadores a nivel de bits también se denominan bitwise. Comparan bit a bit los \alorcs
de las variables que se incluyen como operandos. Los operadores & y 1 tienen un
comportamiento algo especial. En caso de que se usen operandos booleanos o condiciones que
devuelven el valor true o false estos operadores se convertirán en operadores lógicos perezoso, a
continuación estudiaremos este concepto.
Operador Descripción Ejemplo

AND o Y lógico a nivel de


& 3
bit.

GR y O logico a nivel de bit.


XOR.

NOT o No logico a nivel de


bit.

Desplazamiento a la
izquierda, cada bit es
recorrido x posiciones a la
izquierda.
Desplazamiento a la derecha
Cada bit es recorrido x bits a
la derecha.

Imaginemos que tenemos el siguiente código fuente:


int a=3,b=5;
int c=a&b;

¿Que valor alcanzará la variable c?


a=011
b=101
011(AND)
101

001
~c^I 001
Si un bit es 1 equivale a Verdad y si es O es Falso. Teniendo en cuenta las tablas de verdad
estudiadas en el Capítulo 2 el resultado de c es 001 = 1.
Capitulo 3. Iniciación ai lenguaje C# 113

Si los operandos a analizar son booleanos, es decir, son expresiones que devuelven un valor
verdadero o falso, los operadores & y|realizan el AND u OR de las condiciones que concatenen,
eso sí, la evaluación que realizan es perezosa.
Sabemos que si una de las condiciones es falsa el resultado final de im AND es falso y si una
de las condiciones de un OR es true, el resultado final también lo será. Según esto, podemos
evitar que se evalúen todas las condiciones de una expresión con los operadores & y |, a esto se le
denomina evaluación perezosa.
,„ , Si a<6 da como resultado false la sentencia completa devolverá false, no se
a<6 & b>c , '1 ^
evaluara b>c.

a<6 'I b>c b>c.


expresión completa será true, no evaluaremos o comprobará

3.3.9.7. PRIORIDAD DE LOS OPERADORES

En muchas de las instrucciones de código que escribiremos para nuestros programas


usaremos más de un operador. A la hora de resolver expresiones en las que intervienen más de un
operador, debemos tener claro la prioridad de cada uno de ellos, cuál de ellos se ejecutará antes
que el resto. Podemos usar el operador paréntesis o corchete para agrupar operaciones a realizar
pero si estos no están presentes cómo se debe proceder.
A continuación se muestra una tabla extraída de la web oficial de Microsoft
(http://msdn.microsoft.com/es-es/library/aa691323(v=vs.71).aspx) donde se establece la
prioridad entre operadores. Los operadores colocados más arriba de la tabla tienen mayor
prioridad.

x.y f(x) a[x] x-H- x— new typeof checked unchecked


+6 _7 ¡ ^ ++X -X (T)X
* /%

« »

= *= /= %= += «= »= &= 1=
Nonnalmente todos los operadores tienen una asociatividad. Esto es, el orden en el que se
deben ejecutar si no existen paréntesis que delimiten las operaciones. Cuando tenemos una

Operador de signo. Establece el número como positivo.


^ Operador de signo. Establece el número como negativo.
114 Programación

expresión con operadores de la misma prioridad, la asociatividad es la que indica cuál debe
tenerse en cuenta primero. Por ejemplo, la expresión x+y-z se evalúa como si estuviera escrita
(x+y)-z debido a que la asociatividad de estos operadores es de izquierda a derecha, es decir, se
irán ejecutando los operadores que vayamos encontrando en dirección derecha.
Todos los operadores binarios a excepción de la asignación (=) y el operador especial
condicional (?:) son asociativos por la izquierda, es decir, se ejecutan los operadores de izquierda
a derecha. La asociatividad de = y ?: es por la derecha, es decir, la ejecución de órdenes se realiza
de derecha a izquierda, por ejemplo cuando realizábamos la operación
numero3=numero2=numero esta se traducía a numero3=(numero2=numero) de forma que la
primera asignación que se realizará será numero2=numero.

ACTIVIDAD 3.5

Dada las variables enteras x=3, y=4 y z=2 indica que se asignará a la variable resultado en
cada expresión:
resultado=x+y*z;
resultado=(x+y)*z;
resultado=++x%y;
resultado=x+z/y;

3.3.10. OPERACIONES DE ENTRADA/SALIDA DE DATOS EN C#

C# es un lenguaje de programación que no incorpora instrucciones para la entrada/salida de


datos, es necesario el uso de clases que sí disponen de métodos encargados de estas operaciones.
Concretamente la clase a la que nos referimos se denomina Consolé, incluida en el espacio de
nombres System.
Consolé dispone de los métodos WriteQ, WrlteLineO, ReadQ y ReadLineQ. Para utilizarlos
será necesario anteponer el nombre de la clase, es decir:
■ Consolé. WriteLineQ;
■ Consolé. WriteQ;
■ Console.ReadLineQ;
■ Consolé.ReadQ;
Además, teniendo en cuenta que Consolé está incluida en el paquete System, si en la cabecera
del código no aparece la línea using System; tendremos que usar estos métodos incluyendo
System en la línea de la forma System.ConsoIe.WriteLineO;

NOTA: El punto (.) es un operador que atin no hemos estudiado ya que se usa sobre ¡
? todo en variables de tipos de datos compuestos, tipo estructura u objetos. Se utiliza para ;
í acceder a los elementos individuales de estos tipos, es decir, a los campos de una i
estructura o bien a los atributos y métodos de un objeto. ¡
Capítulo 3. iniciación ai lenguaje O# 115

3.3.10.1. WRITELINE Y WRITE

Los métodos WriteLine y Write pemiiten mostrar infonnación al usuario a través de la salida
estándar, normalmente la pantalla. WriteLine además de mostrar infoniiación realiza un salto de
línea tal y como indica su propio nombre.
Ya que estos métodos penniten la visualización de infonnación, esta debe incluirse de algún
modo en la función. La sintaxis básica de WriteLine y Write es la siguiente:

Consolé. WriteLine(expresión); !
Console.Write(expresión); j
Expresión abarca a cualquier información que pueda ser visualizada, ya sea un literal, una
cadena de caracteres o una instrucción.

Consolé.WriteLine("Hola Mundo"); //Muestra una cadena de caracteres.

//Muestra la cadena Hola seguida del contenido de la variable nombre.


Consolé.WriteLine("Hola" + nombre);

Consolé.Write(3); //Escribe el número 3 en la pantalla.

Consolé.Write(13.4+3/7); //Muestra el resultado de la operación 13.4+3/7.


Otro formato válido del uso de estas funciones es el que sigue:
Consolé. WriteLine(Cadena_de_formato,parámetrol,parámetro!,...,parámetron);
Consolé. Write(Cadena_deJ'ormato, parámetrol,parámetro!,...,parámetron);
La Cadena_de_formato representa una cadena de caracteres donde se incluyen elementos
del tipo {0}, {1}, etc., cada uno de estos valores incluidos entre llaves representa uno de los
parámetros que siguen a la cadena de fonnato teniendo en cuenta el orden en el que aparecen.
Para comprender esta segunda fonna de visualización, veamos un ejemplo. Supongamos que
necesitamos mostrar por pantalla el valor de las variables x e y en la frase "El valor de x es
variableX y el valor de y es variableY" donde en el texto, x e y son literales mientras que
variableX y variableY deben ser sustituidos por el valor concreto de estas. El resultado final debe
ser similar al que se muestra en la Figura 3.12.
I de X es 3 u el ualor de y es 10

^Yaríable^ r Variable y

Figuras.12. Mensaje ejemplo a mostrar a partir de WriteLine.


Podemos conseguir que el usuario visualice este mensaje mediante la siguiente instrucción:
I Consolé.WriteLine("El valor de x es " + x + " y el valor de y es " + y);
O bien:

¡ Consolé.WriteLine("El valor de x es {0} y el valor de y es {l}",x,y); :


En esta segunda forma se escribe la cadena completa sin necesidad de hacer uso del operador
+. Los lugares concretos donde debe aparecer el valor de una variable se identifican con {n}
siendo n la posición del parámetro. Los parámetros que queramos incluir se listarán separados
por comas tras la cadena de caracteres de fonnato, siendo el orden determinante como se ha
116 Programación

indicado. La posición en la que está un parámetro vendrá dada por número de orden cmpc/aiido
a contar en cero.

Parámetro 2

int X = 3, y = 10; ^
Ccn5cle.WriteLine("El valor de x es {0} y el valor de y es {l}",x,y);
Tv, '

I Parámetro 1 j
Figura 3.13. Uso de cadena de formato para mostrar información por la salida estándar.

3.3.10.2. READLINE Y READ

Estos métodos se encargan de leer infonnación introducida por el usuario a través del
dispositivo de entrada estándar, el teclado.
El método ReadLine recoge todo lo que el usuario ha escrito hasta pulsar la tecla ENTER.
esto puede ser un simple carácter, número o cadena de caracteres. Si bien es cierto, todo lo que se
recupere mediante estos métodos será tratado como un entero (Read) o una cadena de caracteres
(ReadLine). Si el usuario escribe el valor 13 este debe ser convertido si se quieren realizar
operaciones con él ya que por defecto es tratado como una cadena de caracteres.
La función Read devuelve un valor de tipo entero (código ascii del carácter escrito), con lo
que debemos realizar un casting a la operación de lectura. Solo leerá el primer carácter escrito de
forma que si el usuario teclea "ab" o "12" la lectura con Read solo tendrá en cuenta la letra 'a' y
el dígito '1'.
_ — " — - -- -- — - _ — - — - —

char letra;
string cadena;
letra=(char)Consolé.Read();
cadena=Console.ReadLine();
Consolé.Writeline("Ha escrito la letra {0} y la frase (1}", letra,cadena);
Cuando queramos convertir ervalórintroducidó a partir del método ReadLine() no podremos
usar un simple casting, será necesario utilizar operaciones de conversión propias de los objetos a
los que queremos convertir. Por ejemplo, si hemos pedido al usuario que introduzca un valor
float debemos incluir la función Console.ReadLine en el método float.Parse(); La función Parse
espera que entre sus paréntesis se introduzca una cadena de caracteres que es lo que precisamente
devuelve el método Console.ReadLine().
Podemos usar la función Parse() por cada uno de los tipos de datos integrados vistos:
int.ParseO, double.Parse(), float.Parse(), long.Parse(), etc.
int numerol, numero2,resultado;
Consolé.WriteLine("Introduce un número");
numerol=int.Parse(Console.ReadLine());
Consolé.WriteLine("Introduce otro número");
numero2=int.Parse(Consolé.ReadLine());
resultado=numerol+numero2;
Consolé.WriteLine("La suma de {0} y {1} es:{2}",numerol,numero2,resultado);
Consolé.ReadLine();®

® Usamos ReadLine en la última línea para poder visualizar el resultado ya que el programa finalizará
cuando el usuario pulse ENTER.
Capítulos. Iniciación al lenguaje C# 117

ACTIVIDAD 3.6

Realiza un pequeño programa de consola que pida al usuario el valor del lado de un cuadrado
y muestre como resultado el área del mismo.

ACTIVIDAD 3.7

Realiza un pequeño programa que pida al usuario una letra y muestre por pantalla el carácter
siguiente al especificado.

3.3.11. SENTENCIAS ALTERNATIVAS EN O#

En el Capítulo 2 se estudiaban las diferentes sentencias alternativas y su funcionalidad.


Veamos en C# cómo se definen sentencias alternativas simples, dobles y múltiples, así como se
produce la concatenación de estas.

3.3.11.1. SENTENCIA ALTERNATIVA SIMPLE

En C# la sentencia altemativa simple se especifica mediante la palabra reservada if.

Condición representa cualquier expresión que de como resultado un valor booleano, por
ejemplo true, a<5 o(a>b S¿& b<c).
Por sentencias entendemos cualquier tipo de instrucción que podamos escribir en C# o grupo
(bloque) de instrucciones. El uso de llaves no es necesario si solo vamos a incluir una línea de
código.

int nota;
Consolé.WriteLine("Introduce nota");
nota=int.Parse(Consolé.ReadLine());
if (nota>=5)
Consolé.WriteLine("El alumno esta aprobado");
Consolé.ReadLine();

int numerol,numero2, resta;


consolé.WriteLine("Introduce el primer número");
numerol=int.Parse(Consolé.ReadLine());
consolé.WriteLine("Introduce el segundo número");
numero2=int.Parse(Consolé.ReadLine());
if (numerol > numero2){
resta=numerol-numero2;
Consolé.WriteLine("El resultado de restar {0} y {1} es {2}",
numerol, numero2, resta);
118 Programación

3.3.11.2. SENTENCIA ALTERNATIVA DOBLE

La sentencia alternativa doble permitía que se realizara una acción ya se cumpliera o no la


condición establecida. En C# una sentencia alternativa doble se codifica como sigue:

if(condición){ \
Sentencias; ;
} \
else{ \
Sentencias; i
} \
Al igual que en las sentencias alternativas simples no es necesario el uso de llaves si
sentencias se reduce a una única instrucción, tanto en la parte relativa al ¡f como al else.

NOTA: Si el lector está empezando a tomar contacto con la programación de !


aplicaciones, se aconseja que se usen siempre las llaves en todas las sentencias de control ¡
para evitar confusiones. !

int numerol,numero2
char operación;
Consolé.WriteLine("Introduce el primer número");
numerol=int.Parse(Consolé.ReadLine());
Consolé.WriteLine("Introduce el segundo número");
numero2=int.Parse(Consolé.ReadLine ());
Consolé.WriteLine("Indica operación a realizar (0-Suma/1-Resta)
operacion=(char)Consolé.Read();
if (operación == ~0'){
Consolé-WriteLine("La suma de {0} y {1} es {2}", numerol,
numero2, numerol+numero2);

else{
Consolé.WriteLine("La resta de {0} y {1} es {2}", numerol
numero2, numerol-numero2);

3.3.11.2. SENTENCIA ALTERNATIVA MÚLTIPLE


C# usa dos tipos de sentencias alternativas múltiples:
Estructura alternativa múltiple 1 Estructura alternativo múltiple 2
if(condición_!){ switch(variable){
Sentencias; case valor_1: Sentencias;
} break;
else if(condición_2){ case valor_2: Sentencias;
Sentencias; break;
}
case valor_n: Sentencias;
else if(condición_n){ break;
Sentencias; default: Sentencias;
Capítulos. Iniciación al lenguaje O# 119

break;

Sentencias;

La estructura alternativa múltiple 1 tiene bastante similitud con las sentencias alternativas
simple y doble. La primera de las condiciones es precedida por if mientras que se pueden
contemplar algunas más anteponiendo else if. Para finalizar, si ninguna de las condiciones es
cierta se ejecutarán las instrucciones contempladas en el else siempre que exista.
Así, la primera estructura múltiple sería interpretada por el compilador del siguiente modo:
Si condición_l es cierta se ejecutan las instrucción que se encuentran entre las siguientes
llaves y finaliza la expresión condicional, si no es cierta condición_l se comprueba condición_2.
Si condición_2 es true se ejecutarán las sentencias que se incluyen justo detrás de ella, si no,
seguirán evaluándose condiciones hasta que una de las condiciones sea válida, lleguemos a una
sentencia else o bien no existan más condiciones a evaluar.
La estructura alternativa múltiple 2 se estudió tal cual en el Capítulo 2, es decir, veíamos
como en función del valor de una variable se ejecutaban unas líneas de código u otras. Así,
tendremos una variable definida en un detemiinado tipo de datos y la sentencia swltch
proporcionará todos los posibles valores válidos por los que se deben ejecutar determinadas
instrucciones. La palabra reservada case precede a cada valor válido. Las instrucciones incluidas
para un valor válido deben finalizar con la instrucción break de forma que se garantice que
cuando uno de los case sea evaluado finalice la estructura múltiple.
Además, es nonual encontrar una palabra reservada default que indica las instrucciones que
se deben ejecutar en caso de que ninguno de los valores de los case se hayan validado.
— — -1

char opcion; |
Consolé.WriteLine("O.- Sumar"); i
Consolé.WriteLine("1.- Restar"); I
Consolé.WriteLine("2.- Salir"); I
Consolé.WriteLine("Opción:"); i
opcion = char.Parse(Consolé.ReadLineO); I
switch (opcion) ;
{ i
case 'O': Consolé.WriteLine("Has escogido sumar"); 1
break; i
case '1': Consolé.WriteLine("Has escogido restar"); '
break;
case '2': Consolé.WriteLine("Has escogido salir");
break;
default: Consolé.WriteLine("La opción seleccionada es
incorrecta");
break;

Consolé.ReadLine();

Al declarar opción de tipo char cada case debe contemplar un valor incluido entre comillas
simples, 'O', '1' y '2', aunque representen números. En caso de que el usuario escriba un valor
distinto de O, 1 y 2 se ejecutará la línea bajo default escribiéndose por pantalla el texto La
opción seleccionada es incorrecta.
El código del ejemplo anterior se puede expresar mediante sentencias if - else if del siguiente
modo:

char opcion;
Consolé.WriteLine("O.- Sumar");
Consola.WriteLine("1.- Restar");
Consolé.WriteLine("2.- Salir");
Consolé.WriteLine("Opción:");
opcion = char.Parse(Consolé.ReadLineO);
if (opcion == 'O'){
Consolé.WriteLine("Has escogido sumar");
}
else if (opcion == '1'){
Consolé.WriteLine("Has escogido restar");
}
else if (opcion == '2'){
Consolé.WriteLine("Has escogido salir");

else {
Consolé.WriteLine("La opción seleccionada es incorrecta");

: Consolé.ReadLine();

NOTA: En una sentencia switch podemos obligar a que más de un valor ejecuten las
mismas instrucciones si omitimos el uso del break. Por ejemplo, el siguiente código fuente
está permitido en C#.
int mes;
Consolé.WriteLine("Introduce el mes");
mes=xnt.Parse(Consolé.ReadLine());
switch(mes){
case 2: Consolé.WriteLine("El mes tiene 28 dias");
break;
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12: Consolé.WriteLine("El mes tiene 31 dias");
break;
case 4:
case 6:
case 9:
case 11: Consolé.WriteLine("El mes tiene 30 dias");
break;
}
Consolé.ReadLine();
Si el valor del mes es 3, al acceder al switch y no contener el case correspondiente un
break se ejecutarán las sentencias de los siguientes case. De este modo se alcanzará el case
12 que mostrará por pantalla el mensaje y finalizará la ejecución.
Capítulo 3. iniciación al lenguaje C# 121

ACTIVIDAD 3.8

Codifica un programa que pida al usuario un número que represente una cantidad expresada
en pesetas. Si tenemos en cuenta que 1 euro = 166,386 pesetas, muestra por pantalla la
conversión a euros de ese número de pesetas.
ACTIVIDAD 3.9

Realiza el ejercicio anterior teniendo en cuenta que el número de pesetas introducido por el
usuario debe ser mayor que cero. Si se introduce un número positivo de pesetas se realizará la
conversión, en caso contrario no se hará ninguna acción.
ACTIVIDAD 3.10

Realiza la Actividad 3.8 de fonna que la cantidad de pesetas sea positiva (Actividad 3.9) pero
en esta ocasión, si el número introducido es negativo o igual a cero se debe mostrar al usuario la
frase "El valor de pesetas establecido no es correcto".
ACTIVIDAD 3.11

Imaginemos que queremos desarrollar un programa tal que al escribir un día de la semana
aparezca por pantalla la actividad extraescolar a la que debe acudir nuestro hijo. Por ejemplo, si
ejecutamos el programa y escribimos martes que aparezca "Natación". Las actividades que se
realizan cada día son: lunes - psicomotricidad, martes - natación, miércoles - música, jueves -
natación, viernes - descanso. Los días sábado y domingo no se realizan actividades con lo que si
el usuario escribe por error alguno de estos días de la semana se debe mostrar el mensaje "Día sin
actividades". Si por equivocación, además, se escribe un día inexistente se debe mostrar en
pantalla "Día erróneo".

3.3.12. VISIBILIDAD DE LOS IDENTIFICADORES EN C#

Anterionnente, con el uso de sentencias alternativas hemos introducido al lector en la


expresión bloque de sentencias. Un bloque de sentencias o sentencia compuesta siempre se
encuentra delimitado por un par de llaves tal que indican el inicio ({) y cierre (}) de dicho
bloque. Si declaramos un identifícador en un bloque este solo será visible en él, es decir, empieza
a poder ser usado cuando se inicia, aparece la primera llave de apertura hasta que se cierra,
aparece la llave de cierre.
Veamos el siguiente trozo de código:
int n;
Consolé.WriteLine("Introduce un valor numérico"); ;
n=int.Parse(Consolé.ReadLine()); •
if (n>5){ ;
int j=n; j
n++; i
} ;
Consolé.WriteLine("La variable j tiene el valor {0}",j); ;
Consolé.ReadLineO; I

Al intentar ejecutar se produce el error de compilación que se muestra en la Figura 3.14.


1 error ! O 3d. e ter :i =•;

Descripción
nombre' contexto:

Figura 3.14. Panel "Lista de errores" de Visual Studio. Es la zona donde se podrán visualizar los diferentes
errores de compilación que se vayan produciendo.

Si observamos detenidamente el código veremos que la variable n es accesible desde


cualquier parte del mismo ya que está declarada, aunque no se hayan incluido, entre las Ihu cs de
la función principal Main. En cambio, la variable j se crea entre las llaves de la sentencia
alternativa if, de forma que sólo será visible desde que se declara hasta que se acaba el bloque
donde se declaró. En el momento en que la llave del bloque if se cierra la variable j deja de
existir. Al escribir la sentencia Console.WriteLine("La variable j tiene el valor {0}",j); fuera
de los límites de donde puede ser usada, el compilador detecta el uso de una variable inexistente
por lo que muestra el mensaje de error El nombre 'j' no existe en el contexto atual.
Si vamos más allá y teniendo en cuenta todas las lineas del código fuente analizado podemos
además afirmar que la variable n solo podrá ser usada en los límites de la función Main ya que
para el resto de funciones de la clase donde se ubica es inexistente.
Ejwpio El primcr par de llaves es el que usa la
ciBí' -----a- sentencia if, este es nuestro primer
p{ bloque. A continuación vemos como la
/•Función p-incípal
/"Funciói p''incípal . ii j - - «
Recibe un «rray de cadenas de cacacteras
R«cibe un d^'"ay de cadenas de cdfacteres StatiC VOld Main USa Ulia llaVC dC IIIICIO
fio devuelve ningún valer de salida*/
St1tirv0¡rrlin(strlngn
static void Kain(string[] args) Y otfa dc fin para delimitar sus lincas,
,~í
A encontramos aquí nuestro segundo
\ int "í
n; J • 1 ' '
I Cc-ül-r.KriteLineCIntroduce un valor nuaérico");
Cc-t:ilT.KriteLine("lntroduce un valor nue.érico"); blOCJUC, QCnOlTllIlSClO ITláS COIllUnmcntC
n«int.Parse(Cc"o-:le.ReadLine());
I "^-'-nt.Parse(c.".-.e.ReadLine());
if (n>5){
int j«n;
función. Siguícndo la tcoría de bloque, la
I variable n solo podrá ser usada en la
i } ' función Main.
f Ccn:.cá'?.VÍriteLine(''i.a
Ccns.c^e.VlriteLineCi.a variable j tiene el valor
ccnscie.ReadLineo;
Consc-o.ReadLÍne(); EncontraiTios un nuevo par de llaves,
esta vez se inicia en la línea class
«—} Program. Así, disponemos de otro
bloque de sentencias denominado clase.
Figura 3.15. Identificación de tipos de bloques en un
programa C#.

En esta clase encontraremos atributos y métodos de clase accesible por todos los elementos de
esta incluidos entre las llaves.

Para finalizar, el bloque que incluye a todos los demás es el namespacen o nombre de
espacio que encapsula a una o un conjunto de clases. Aunque las llaves indican los límites de
acceso, al trabajar con clases podemos usar operadores de visibilidad que indicarán si algunos
elementos son accesibles o no.

Los conceptos función, clase y namespace serán vistos con más detenimiento en los
siguientes capítulos.
Capítulos. Iniciación al lenguaje O# 123

Los identificadores no pueden encontrarse duplicados en el mismo bloque, por ejemplo, en la


misma clase no pueden existir dos funciones idénticas, sin embargo si pueden repetirse estos en
diferentes ámbitos. Podemos tener una clase llamada círculo en el espacio de nombre Figuras y
CalculosGeometricos.

if (n>5){
int j=n;

int j;
Consolé.WriteLine("La variable j tiene el valor {0}",j);
jlonsole.ReadLine()j
}

O 1 Ya se ha definido una variable local denominada "j" en este ámbito|

Figura 3.16. Identificador repetido en el bloque. Error de compilación indicando que la variable ya se ha
declarado.

class P^cg'-am
{
int j;
/ static void Kain(string[] args)
{ .
int n;
Censóle.WriteLine("Introduce un valor numérico");
n"int.Parse(Censóle.ReadLineO);
^ if (n>5){
p > int j-n;
n++;
Consolé.WriteLine("La variable j tiene el valor {0}",j);
Con so le.ReadLineO;
}

Figura 3.17. Variable] declarada en dos ámbitos distintos. Declaración permitida, no se producen errores
de compilación.

3.3.13. SENTENCIAS REPETITIVAS EN C#


En el Capítulo 2 estudiábamos tres tipos de sentencia repetitivas incluyendo una cuarta que no
siempre es usada en todos los lenguajes y que puede llevar a confusión. Estas eran Mientras,
Hacer...Mientras y Para.

3.3.13.1. BUCLE WHILE EN C#

La sentencia while es equivalente al bloque Mientras...FinMientras que estudiábamos en el


Capítulo 2 como parte de elementos del pseudocódigo. Su sintaxis es la siguiente:
while (condición){ ;
Sentencias; ;
} i
Al igual que ocurría con las sentencias alternativas, no es necesario utilizar cl símbolo llave
para establecer los límites del bucle si en su interior solo vamos a colocar una instrucción. El
siguiente ejemplo es perfectamente válido:
int i=0;
while(i<10)
i++;

Sin embargo, se aconseja al lector que utilice las llaves para evitar confusiones.
La condición del bucle será una expresión que dará como resultado un valor booleano que
determinará la continuidad o no en el bucle. En este tipo de sentencias de control repetiti\as
puede darse el caso de que nunca lleguen a ejecutarse las instrucciones que incluyen, ya que
pudiera ocurrir que la expresión condicional no sea cierta.
Veamos un ejemplo de código en el que necesitamos utilizar una senencia repetitiva,
supongamos que tenemos que desarrollar un programa que dado un número indique cuántos
divisores tiene. Si el lector lo desea, puede desarrollar el diagrama de flujo, pseudocódigo y
código fuente sin mirar la solución y así comprobar sus conocimientos.
— - - — ____ — __ — — ________ — _____ - - -1

int numero, totalDivisores=0;


int contador = 1;
Consolé.WriteLine("Introduce un numero");
numero=int.Parse(Consolé.ReadLine());
while (contador <= numero)
{
if (numero % contador == 0)
{
totalDivisores++;
}
contador++;
}
Consolé.WriteLine("El número tiene {0} divsores",totalDivisores);
Consolé.ReadLine();

ACTIVIDAD 3.12

Realiza la tabla de seguimiento del código fuente de ejemplo.


ACTIVIDAD 3.13

Realiza un programa que pida dos mimeros x e y, y realice la multiplicación de ambos


mediante el uso de sumas.

ACTIVIDAD 3.14

Realiza un programa que muestre por consola algo similar a lo que ves en la Figura 3.18.

Figura 3.18. Cuadrado formado por asteriscos.


Capítulos, iniciación al lenguaje O# 125

3.3.13.2. BUCLE DO WHILE EN C#

El bucle do...wh¡le es esquivalente al bucle estudiado en pseudocódigo como


hacer...mientras. Este tipo de bucles siempre será ejecutado al menos una vez ya que la
condición será evaluada al finalizar el conjunto de sentencias.
La sintaxis de este tipo de bucles es la siguiente:

do{ i
Sentencias; ¡
}while (condición); •
Al igual con sentencias de control anteriores podemos omitir el uso de llaves si Sentencias se
refiere a una sola instrucción. Veamos un pequeño ejemplo para que el lector comprenda
claramente esta nueva sentencia. Imaginemos que tenemos que desarrollar un programa que pida
al usuario por cada mes del año, el número de días en los que ha llovido mostrando por pantalla
el total de días con precipitaciones. Se aconseja al lector que antes de observar el código fuente
intente realizarlo, siguiendo el proceso: análisis, desarrollo del diagrama de flujo y
pseudocódigo.
int totalDias=0; ;
int precipitacionesMes; I
int nies=l; I
do{ ;
Consolé.WriteLine("Introduce las precipitaciones del mes {0}",mes); I
precipitacionesMes=int.Parse(Consolé.ReadLine()); I
totalDias=totalDias+precipitacionesMes; i
mes++; 1
}while(mes <=12); ;
Consolé.WriteLine("Ha llovido este año durante {0} dias",totalDias); ;
Consolé.ReadLine(); !

ACTIVIDAD 3.15

Realiza la tabla de seguimiento del programa anterior.


ACTIVIDAD 3.16

¿Qué modificaciones realizarías en el código fuente anterior si quisieras calcular la media de


días con precipitaciones por mes?

3.3.13.3. BUCLE FOR EN C#

Un bucle for equivale a la sentencia de control Para estudiada en el Capítulo 2, es la parte


dedicada al pseudocódigo. Constituye un bucle en el que conocemos el número de veces que se
ejecutarán las instiucciones que incluya. Su sintaxis se muestra a continuación.
— — — — I

for (inicialización; condición; modificación){ i


Sentencias; \
1 i
Un bucle for es controlado por una variable o conjunto de ellas que deben ser iniciadas y
modificadas en la eabecera del mismo. Cada zona, inicialización, condición y modifícación está
separada de las demás gracias al signo punto y coma (;).
126 Programación

Inicialización. Representa la asignación del valor inicial a la variable que controla al


bucle. Nonnalmente se declara y asigna un valor al mismo tiempo.
Condición. Expresión que determina cuándo finalizará el bucle.
Modificación. Expresión aplicada sobre la variable del bucle que hará que su \ alor se
vea alterado. La modificación es necesaria ya que pro\ocará en un momento
determinado que la condición deje de ser cierta.
int nuinero=0;
float media=O.Of;
for (int 1=0;i<10;i++){
Consolé.WriteLine("Introduce un numero");
numero=int.Parse(Consolé.ReadLine());
media=media+numero; //Podemos cambiar la linea por media+^numero;
}
media=media/10;
Consolé.WriteLine("La media de los diez numerous es:{O)",media);
Consolé.ReadLine();

ACTIVIDAD 3.17

Realiza una tabla de seguimiento del programa de ejemplo.

//Programa que muestra los números impares de 1 a 100


for(int 1=1;i<=100;i+=2){
Consolé-WriteLine(i);

Consolé.ReadLine();

//Bucle for en el que se usa más de una variable


for(int 1=0, j=2;i<10; i++, j+=2){
Consolé.WriteLine(j);

Al igual que con el resto de sentencias de control podemos no usar las llaves si es una única
instrucción la que ejecuta el bucle, aunque como siempre se aconseja utilizarlas.
Las variables que se declaran en el bucle, en la cabecera del mismo, solo serán visibles en él.
ACTIVIDAD 3.18

Realiza un programa que muestre los números divisibles de 7 entre 1 y 100.


Un bucle for puede implementarse de diferentes maneras existiendo ciertas configuraciones
de cabecera que conllevarán determinados efectos:
■ for(;;). Si encontramos esta cabecera estaremos indicando que el bucle es infinito. No
existe ninguna variable que lo controle, de forma que se repetirá una y otra vez hasta
que encuentre una instrucción de corte, si existe.
■ for(;i<10;i-H-). Encontraremos esta cabecera si la variable i se ha declarado e
inicializado fuera. A veces puede interesar al programador usar la variable que
controla el bucle más allá de sus límites, de forma que es creada fuera de este.
Capítulos. Iniciación al lenguaje C# 127

En realidad podemos dejar en blanco cada una de las tres regiones de la cabecera de un bucle
for, nunca debemos olvidar el signo delimitador punto y coma. Con estas anomalías
conseguiremos bucles infinitos, es decir, que un programa esté ejecutándose de forma
continuada, estado no muy recomendable para muchos programas o software de aplicación
específicos.
Un bucle for puede convertirse en while y viceversa como estudiábamos en pseudocódigo.
int 1=0; xnt 1=0;
for(xnt 1=0;i<10;1++){ while(i<10){ do{
Consolé.Write(i); Consolé.Write(i); Consolé-Write(i);
i++; i++;
} while(i<10);

3.3.13.4. SENTENCIAS DE SALTO: BREAK,CONTINUE Y RETURN C#


Una sentencia de salto es aquella que interrumpe de algún modo la ejecución de ima sentencia
de control.

■ break. Encontraremos esta palabra reservada en una sentencia de control repetitiva o


un switch. La instrucción break interrumpe un bucle indicándose que la ejecución del
programa debe continuar en la siguiente instrucción después del mismo. En ima
sentencia altemativa múltiple switch el uso de break garantiza que cuando un caso sea
cierto solo se ejecuten las líneas de código relacionadas con él.
■ continué. La sentencia continué solo puede utilizarse en sentencias de control
repetitiva como los bucle for, while y do...while. Cuando es ejecutada no se ejecutan
el resto de instrucciones siguientes incluidas en el bucle, se transfiere el control a la
condición en caso de que se encuentre en un bucle while o do...while o se produce el
incremento de la variable de control en un bucle for.

■ return. Usaremos retum en un elemento del lenguaje que hemos nombrado pero que
aún no se ha estudiado en profundidad, las funciones. La instrucción retum obligará
la finalización de la función. Se usa comúnmente para comtmicar funciones
secundarias con funciones principales pennitiendo el intercambio de valores a través
de variables de retomo.

static void Main(string[] args)


{
int numero;
double (tedia = 0.0;
Consolé.WriteLine("£scribe números hasta que aparezca un nuevo mensaje");
for (int i = 0; i < 50; i++)
{
numero = int.Parse(Console.ReadLine());
if (nu(tero ==0)
{
break;

^
media = iredia + numero;
} y
media = media / 50;
Ccriscie.WriteLine("La media es:{0}", icedia);

Figura 3.19. Ejemplo de uso de la sentencia break. Si numero es igual a cero se romperá el bucle de
forma que la siguiente sentencia a ejecutar es media=media/50:

static void Main(string[] args)


{
int numero;
double media « 0.0;
Consolé.ViíriteLineCEscribe números hasta que aparezca un nuevo mensaje");
for (int i = 0; i < 50; i++)
{ A
numero = int.Parse(Cot sole.ReadLine());
if (numero == 0)
í continué;
n
}
media = media + numero;
}
media = media / 50;
Censóle.WriteLine("La media es:{0}'', media);

Figura 3.20. Ejemplo de uso de la sentencia continué en un bucle for. Si numero es igual a cero se
incrementará el valor de i. Si la condición aún es cierta se volverá a acceder al bucle y ejecutar sus
instrucciones. Las instrucciones que se encontraran después de continué, si este se alcanza, no se
ejecutarán.

static void l'iain(string[] args)


1 {
I int numero,i=0;
í double media « 0.0;
j Consolé.WriteLine("Escribe números hasta que aparecca un nuevo mensaje");
while(i < 50)

^ numero^
Y.int.Par
Parse(Console.ReadLine());
if (nuroiro"0)
{ \
continué;
}
media s inedia -t- numero;
i++;
}
media » media / 50;
Consolé.WriteLine("La media es:{0)", media);

Figura 3.21. Ejemplo de uso de la sentencia continué en un bucle while. Si numero es igual a cero se
ejecuta continué de forma que las demás sentencias (media=media+numero; y i++;) no se llevarán a
cabo. Tras comprobar la condición, si sigue siendo cierta se volverá con la ejecución de la sentencia de
control while.

public int suma(int a^int b)


{
int resultado = 0j Figura 3.22. Ejemplo de uso de la sentencia
if (a > 8 && b > 0) return. En una función llamada suma se ejecutan
una serie de instrucciones hasta que se alcanza la
{ sentencia return. En ese momento finaliza la
resultado = a + b; función y se pasa al programa que realiza la
} llamada el valor almacenado en resultado.
return resultado)]
}
Capítulos. Iniciación al ienguaje C# 129

3.3.13.5. INSTRUCCIONES DE SALTO: GOTO CASE VALOR Y GOTO


DEFAULT EN SENTENCIAS ALTERNATIVAS MÚLTIPLES(SWITCH)
Además de las instrucciones de salto estudiadas para incluir en las diferentes sentencias de
control, encontrarnos otras que se usan en la estructura switch.
Sabemos que switch es una sentencia de control altemativa múltiple en la que se evalúa mi
valor almacenado en una variable. Este valor estará limitado de forma que encontraremos en la
estructura switch una sentencia case por cada dato que pueda adoptar la variable. Ahora bien, en
ocasiones puede que frente a determinados valores queramos que se realicen operaciones
similares y por esta razón aparecen sentencias del tipo goto case valor y goto default.
Veamos el siguiente código fuente de ejemplo.
static void Main(string[] args)
{
int op=0;
do

{
Censóle.WriteLine("Escribe un valor de 1 a 5")j
op = int.Parse(Ccnscle.ReadLineO);
switch (op)
{
case 1: Consolé.WriteLine("Estoy en uno");
break;
case 2: goto case 1;
case 3: Consolé.WriteLine("Estoy en el tres");
break;
case 4: goto default;
case 5: Consolé.WriteLine("Estoy en el cinco");
break;
default: Consolé.WriteLine("0 has escogido la opción 4 o un número no válido");
break;
}
} while (op != 0);
}

Figura 3.23. Uso de las sentencias goto case valor y goto default.
Los case 1, 3 y 5 funcionan tal y como hemos estudiado con anterioridad. Si el usuario escoge
alguno de estos valores se mostrará el correspondiente mensaje en pantalla. Tras la visualización
la instrucción break tomará el control tal que acabará la sentencia switch y se pasará a
comprobar si la condición del bucle sigue siendo cierta.
En caso de que el usuario escoja el case 2 el compilador leerá goto case 1, es decir, "salta al
case 1" y esto es lo que hará, de forma que se visualizará el mensaje "Estoy en uno" y finalizará
el switch.

En caso de que el usuario escoja la opción 4 el compilador leerá goto default, o lo que es lo
mismo,"salta a default". Pasaremos a ejecutar las líneas de código que se encuentren en default
en nuestro switch.

3.4. TIPOS DE ERRORES DE COMPILACIÓN


Visual Studio es un entorno que interactúa continuamente con el usuario comunicándole en
cada momento errores que se estén produciendo o advirtiendo de la posibilidad de que estos se
den. Así, al mismo tiempo que picamos código pueden darse:
130 Programación

■ Errores de compilación. Fallos que si no son corregidos no permitirán el linkado y


ejecución del software.
■ Advertencias o warnings. Estos no son exactamente errores ya que podemos
compilar y ejecutar el programa. Sin embargo deben tomarse en cuenta y si es posible
detectar su origen y solucionarlos ya que pudieran producir posteriores errores de
ejecución que provocarán un mal funcionamiento del software.
El compilador emitirá una serie de mensajes dando información relativa al error o waming
producido tal que al programador le sea relativamente fácil solucionarlo.
En Visual Studio veremos estos mensajes en la parte inferior de la pantalla en el panel Lista
de errores, al mismo tiempo que se observan subrayados de diferentes colores bajo las
sentencias que provocan el fallo.

< ,i
Ü* CpnsQ^WriteLineCEscribe un valor de 1 a 5**);

(0p>
(
<*>« X: «n w*)x
trc«k;
<««« 2t |c.tA c«t« 1;
c«»c li ri «n «]

c*w «1 gote

iUpe««roC(*C»corx,iM.. « ca'KitfnUwadc.
b'cM;

í 0 u*á.'.'ri£nc{3:

Descripción
T. oi
í V 1 ti
El nom
nombre 'Consol' no existe en el contexto actual
Cwipcicn •—""'1 1 ' '■ ~

Figura 3.24. Errores de compilación en Visual Studio. El error se indica en la misma línea de código (parte
superior de ia imagen) y en el panel Lista de errores (parte inferior de la imagen). El mensaje es muy
representativo dando suficiente información para la corrección del fallo.

vold M*in(4trlng[J «rgi)

n; \

Corrvíi».Mrltetirt«<"E4C''4b* un valer de 1 • >')5


op • Lnt.ParietLoiTle.AeadLlrteO);
Stcitch (ep)
(
ca»* l: Cen>cle.WrlteJ,irte{"tfttey en ufte'){
break;
cate 2¡ $oto cate 1;
caic 5: Co^-.ílf.writetlneCCitcy en el tres*);
breah}
ea&e «: gete default;
cate 5: Ccvc Je.WrltelineCEctpy en el cinco");
break;
default: vC'Clc.writeLinet'O hat etccglde la opsidn 4 o un núaere ne vilido"};

ahite (Op t>

I 1 advertencid

Descripoon

Figura 3.25. Advertencias. En el ejemplo se advierte al programador de que se ha creado una variable
nunca va a ser utilizada. Podemos ejecutar la aplicación pero si es cierto que deberíamos eliminar la linea
ya que una variable ocupa espacio en memoria.
Capítulos. Iniciación al lenguaje C# 131

3.5. CODIFICACIÓN DE LOS ALGORITMOS RESUELTOS


EN EL CAPÍTULO 2
3.5.1. CODIFICACIÓN DEL ALGORITMO RESUELTO 1
ENUNCIADO

Imaginemos que se nos pide la creación de un algoritmo que debe contener una constante
llamada gravedad a la que asignaremos el valor 9.8 y las variables velocidad y tiempo, esta
última tendrá asignado el valor de 2.
PSEUDOCÓDIGO
Entorno: ■
constante real GRAVEDAD=9.8; I
real velocidad; |
entero tiempo; ;

CÓDIGO FUENTE
const float GRAVEDAD=9.8f; ;
float velocidad; 1
int tiempo; ;

3.5.2. CODIFICACIÓN DEL ALGORITMO RESUELTO 2


ENUNCIADO

Imaginemos que se nos pide la creación de un algoritmo que debe contener ima constante
llamada gravedad a la que asignaremos el valor 9.8 y las variables velocidad y tiempo.
Debemos solicitar al usuario que especifique qué valor de tiempo tenemos que usar.
PSEUDOCÓDIGO
Entorno: j
/* Zona (1) del diagrama de flujos */ i
constante GRAVEDAD=9.8; i
real Velocidad; i
entero Tiempo; ;
Inicio: i
/*zona (2) del diagrama*/ j
Escribir "Introduce el tiempo"; !
Leer Tiempo; ■
Fin; i

CODIGO FUENTE

const float GRAVEDAD=9.8f;


float velocidad;
int tiempo;
Consolé.WriteLine("Introduce el tiempo");
tiempo=int.Parse(Consolé.ReadLine());
132 Programación

3.5.3. CODIFICACION DEL ALGORITMO RESUELTO 3

ENUNCIADO

■ Imaginemos que se nos pide la creación de un algoritmo que debe contener una constante
llamada gravedad a la que asignaremos el valor 9.8 y las variables velocidad y tiempo.
Debemos solicitar al usuario que especifique qué valor de tiempo tenemos que usar. En caso de
que este valor sea menor o igual a O se mostrará el mensaje "Tiempo incorrecto" y finalizará el
programa. Si el usuario escribe un valor de tiempo correcto, es decir positivo, se calculará el
valor de velocidad (gravedad x tiempo).
PSEUDOCÓDIGO
Entorno:

/*Zona (1)*/
constante real GRAVEDAD;
real Velocidad;
entero Tiempo;
Inicio:
/*Las siguientes dos lineas forman la zona (2)*/
Escribir "Introduce el tiempo";
Leer Tiempo;
Si Tiempo > O entonces
/*Zona (4)*/
Velocidad=GRAVEDAD*Tiempo;
Escribir Velocidad;

/*Zona (5)*/
Escribir "Tiempo incorrecto";
FinSi;

CÓDIGO FUENTE
const float GRAVEDAD=9.8f;
float velocidad;
int tiempo;
Consolé.WriteLine("Introduce el tiempo");
tiempo=int.Parse(Consolé.ReadLine());
if(tiempo>0){
velocidad=GRAVEDAD*tiempo;
Consolé.WriteLine(velocidad);

else{
Consolé.WriteLine("Tiempo incorrecto");

3.5.4. CODIFICACIÓN DEL ALGORITMO RESUELTO 4


ENUNCIADO

Supongamos que se nos pide hacer un pequeño menú de tres opciones. Este debe visualizarse
como sigue:
1. Calcular diámetro.

: , 2. Calcular circunferencia, .j
Capítulos. Iniciación al lenguaje O# 133

3. Calcular área.

El objetivo del algoritmo es que al iniciar se pida al usuario el radio de un círculo y a


continuación se muestre el menú. Si el usuario selecciona la opción 1 se pasará a calcular el
diámetro del círculo cuyo radio se ha especificado. Si seleccionamos la opción 2 calcularemos
su circunferencia. La última de sus opciones calculará el área. Tras cada cálculo el resultado
debe ser mostrado por pantalla.

PSEUDOCODIGO

Entorno:

constante real PI=3.1415;


entero Radio;
entero Diámetro;
real Circunferencia;
real Area;
entero Op;
Inicio:

Escribir "introduce el radio";


Leer Radio;
Escribir "1.Calcular diámetro";
Escribir "2.Calcular circunferencia";
Escribir "3.Calcular área";
Leer Op;
Opción Op de:
1: Diametro=Radio*2;
Escribir "El diamtero es:";
Escribir Diámetro;
2: Circunferencia=2*PI*Radio;
Escribir "La circunferencia es:";
Escribir Circunferencia;
3:Area=2*PI*Radio*Radio;
Escribir "El Area es:";
Escrinir Area;
FinOpcion;

CODIGO FUENTE

const float PI=3.1415f;


int radio,diámetro,op;
float circunferencia,area;
Consolé.WriteLine("Introduce el radio");
radio=int.Parse(Consolé.ReadLine());
Consolé.WriteLine("1.Calcular diámetro");
Consolé.WriteLine("2.Calcular circunferencia");
Consolé.WriteLine("3.Calcular área");
op=char.Parse(Consolé.ReadLine());
switch(op){
case '1': diametro=radio*2;
Consolé.WriteLine("El diámetro es:{O)",diámetro);
break;
case '2':circunferencia=2*PI*radio;
Consolé.WriteLine("La circunferencia es:{0}",circunferencia);
break;
case '3': area=2*PI*radio*radio;
Consolé.WriteLine("El area es:{O} area);
break;

3.5.5. CODIFICACION DEL ALGORITMO RESUELTO 5

ENUNCIADO

Se precisa realizar un algortimo que calcule si los números introducidos por el usuario son
pares o impares. Para ello se debe pedir al usuario un número, se comprueba si este es par o
impar mostrando por pantalla los mensajes "El número es par" o "El número es impar" según el
caso. A continuación se preguntará al usuario si desea finalizar la ejecución del software. Si su
respuesta es No (n), repetiremos el proceso descrito pidiendo un nuevo número, en caso
contrario, si especifica Sí(s) finalizará el programa.
PSEUDOCÓDIGO
Entorno:
entero Numero;
carácter Salir;
Inicio:
/*La petición del número se encuentra dentro del bucle*/
Hacer
Escribir "Introduce un número";
Leer Numero;
Si (Resto(Numero/2))=0 entonces
Escribir "El número es par";
Sino
Escribir "El número es impar";
FinSi;
Escribir "¿Desea finalizar (S/n)?";
Leer Salir;
Mientras Saliro's';

CODIGO FUENTE
int numero;
char salir;
do{

Consolé.WriteLine("Introduce un número");
numero = int.Parse(Consolé.ReadLine ());
if ((numero % 2) == 0){
^ L:onsole.WriteLine("El número es par");
else {
Consolé.WriteLine("El número es impar");

Consolé.WriteLine("¿Desea finalizar (s/n)?");


salir = char.Parse(Consolé.ReadLineO);
} while (salir != 's');
Capítulo 3. Iniciación ai lenguaje C# 135

3.5.6. CODIFICACION DEL ALGORITMO RESUELTO 6

ENUNCIADO

En el siguiente enunciado se pide al programador que realice un algoritmo por él, se solicite al
usuario la introducción de diez números y muestre por pantalla cuántos de ellos eran positivos,
cuántos negativos y cuántos cero.

PSEUDOCODIGO

Entorno:
entero Contador, Numero;
entero Positivos, Negativos, Ceros;
Inicio;
Positivos=0;
Negativos=0;
Ceros=0;
Para Contador=0 a 10 incremento 1 hacer
Escribir "Introduce un número";
Leer Numero;
Si Numero < O entonces
Negativos=Negativos+l;
Sino
Si Numero > O entonces
Positivos=Positivos+l;
Sino
Ceros=Ceros+l;
FinSi;
FinSi;
FinPara;
Escribir "Negativos:";
Escribir Negativos;
Escribir "Positivos:";
Escribir Positivos;
Escribir "Ceros:";
Escribir Ceros;

CODIGO FUENTE

int numero;
int positivos=0;
int negativos=0;
int ceros=0;
for (int contador=0; contador<10; contador++){
Consolé.WriteLine{"Introduce un número");
numero=int.Parse(Consolé.ReadLineO);
if(numero<0){
negativos++;

else{
if(numero>0){
positivos++;
}
else{
ceros++;
Consolé.WriteLine("Negativos:{O}",negativos);
Consolé.WriteLine("Positivos:{O}",positivos);
Consolé.WriteLine("Ceros:{O}",ceros);
Consolé.ReadLine();

COMPRUEBA TU APRENDIZAJE
1. ¿Cuáles son las características principales de C#?
2. ¿Para qué se usa using en C#? ¿Y la función Main? ¿Cuál es la estmctura básica de
un programa C#?
3. ¿Qué se entiende por identifícador? ¿Qué reglas debe cumplir en C# un identificador
para que sea válido en el lenguaje?
4. ¿Qué tipos de datos podemos usar en C#? ¿Qué podemos decir del siguiente dato
2.3f?

5. ¿Qué estamos haciendo en la línea de código int a=9;? ¿Qué debemos hacer si
queremos realizar la siguiente asignación int a=3.2;? ¿Es válida la siguiente
instrucción: int num=Console.ReadLineO;?
6. ¿Qué tipos de operadores encontramos en C#? ¿Qué función desempeña el operador
sizeofQ;?
7. ¿Es correcta la siguiente sentencia: if(a=3){..,}? Justifica tu respuesta.
8. ¿Si declaramos una variable entre las llaves de un bucle for podremos visualizar esta
cuando el bucle finalice? Justifica tu respuesta.
9. ¿Qué diferencias encuentras entre break, continué y return?
10. ¿Qué tipos de errores pueden aparecer durante la creación de código fuente?

ACTIVIDADES DE AMPLIACION
1. Realiza un programa que muestre las tablas de verdad de los operadores &&, 1| y !.
Utiliza variables booleanas. Recuerda que en el Capítulo 2 tienes estas tablas.
2. En esta actividad se espera analizar los operadores a nivel de bits vistos en el
capítulo. Para ello, realiza un programa que pida al usuario dos números enteros.
Realiza todas las operaciones a nivel di bits vistas y muestra los resultados obtenidos
por pantallas,justifica razonadamente estos.
3. Realiza un programa que muestre el tamaño de los tipos de datos vistos en el capítulo.
4. Realiza un programa que pida al usuario su nombre y dos apellidos. Una vez los haya
introducido deberá mostrar estos del siguiente modo:
Capítulos. Iniciación al lenguaje C# 137

Nombre Apellido 1 Apellido2


Los espacios en blanco se consiguen aplicando dos tabulaciones.
5. ¿Son correctas las siguientes instrucciones?
char a=9; char a=0.0; int a=(int)9.3;
int a=l0/2.0: float a=4.3; float a=4.3f;
string frase=Console.ReadLine(); string frase='a';

6. Realiza un programa para una tienda tal que en función de las compras que realicen
sus clientes se les aplique un decuento u otro:
■ Si el importe de la compra es menor de 200 euros no se aplica descuento.
■ Si el importe de la compra es cómo mínimo 200 euros pero no supera los 500
se aplica un descuento del 5%.
■ Si el importe de la compra oscila entre los 500 y 1000 euros el descuento a
aplicar es del 10%.
■ Si el importe supera los 1000 euros se aplicará un descuento del 15%.
7. Realiza un programa que pida al usuario un año y muestre por pantalla si este es
bisiesto.

8. Realiza un programa que calcule el factorial de un número dado por el usuario.


9. Usa el operador ?: y realiza un programa que dados dos número muestre un mensaje
indicando cuál es el mayor de los dos.
10. Usa el operador ?: para realizar un programa que dados dos números indique si el
segundo es divisor del primero.
11. Realiza un programa que separe los dígitos de un número. Por ejemplo, dado el
número 234 se debe mostrar visualizar como resultado 2-3-4.

12. Realiza un programa tal que dado un nútnero de 5 cifras compruebe si es palíncromo.
13. Realiza varios programas tal que se muestren las siguientes figuras en pantalla:

00000 0 00000
@000 00 0000
@00 @00 000
00 0000 00
0 00000 0

Figura 1 Figura 2 Figura 3


14. Muestra por pantalla todos los números que se encuentren en el rango de O a 100 que
sean múltiplos de 5 pero no de 3.
15. Calcula utilizando únicamente multiplicaciones. Los valores de x e y serán pedidos
al usuario.

16. Realiza un programa que pida al usuario el lado de un cuadrado. Este valor debe ser
menor de 20. Dibuja en pantalla el cuadrado que corresponde con el valor
introducido.
17. Realiza un programa que muestre la siguiente figura por pantalla.

###
#####
###
#

18. Realice un programa que muestra la tabla de código ASCll por pantalla de la
siguiente fonna:
1 - carácter 2 - carácter 3 - carácter

4 - carácter 5 - carácter 6 - carácter

19. Mejora el ejercicio anterior de forma que en esta ocasión las columnas se muestren
como siguen:
1-carácter 31-carácter 61-carácter ...
2 - carácter 32 - carácter 62 - carácter ...

20. Realiza un programa tal que pida al usuario una fecha del año (días y mes) y muestie
por pantalla los días transcurridos desde el 1 de enero.
21. Realiza un programa que pida al usuario un número y muestre por pantalla si este es
primo o no.
22. ¿El siguiente código es correcto? Localiza los errores.

; int i=0,nuin;
¡ while (i>0){
I num+=i;
CAPITULO 4

FUNCIONES

CONTENIDOS OBJETIVOS

Funciones. Estructura, Estudiar el diseño descendente,


declaración e invocación. entender qué es una función, tipos y
Parámetros. Paso por valor y su uso en C#.

referencia. Uso de reí y out. Saber qué es un parámetro y


Eramework .NET cómo usarlos teniendo encuenta la
existencia de paso por valor y
Recursividad referencia.
Sobrecarga de funciones Saber qué es un ffamework y
estudiar algunas flmciones del
ffamework .NET.

Entender la recursividad y la
sobrecarga de funciones.

RESUMEN DEL CAPITULO

En este capítulo se estudia el diseño descendente. Se verá la estructura básica de una


función, los conceptos de declaración y llamada a una función, así como el paso de
parámetros a estas por valor y referencia. Además se verán conceptos como recursividad
y sobrecarga de funciones para finalizar con una muestra de funciones predeterminadas
en el framework .NET.
140 Programación

4.1. INTRODUCCION
Hasta ahora hemos estado realizando pequeños programas, aplicaciones de consola con no
demasiadas líneas de código. En la vida real, los programas son extensos y a veces inabordables
por una única persona. Además, en muchas ocasiones, existen grupos de líneas de código que
deben repetirse a lo largo de toda la aplicación.
Normalmente, cuando se desarrolla un software se establecen grupos de trabajo donde una
serie de programadores se encargan de codificar cada parte de la aplicación. La codificación se
hace de tal forma que ningún programador tiene por qué conocer exhaustivamente cómo
funciona o qué líneas posee el código del compañero para poder usarlo. Esta es básicamente la
esencia del diseño descendente o Top-Down.
El diseño descendente pone en práctica la frase "divide y vencerás" de forma que se aborda
un problema dividiendo este en partes más pequeñas. Se dividirá nuevamente cada parte en otras
tantas cuantas veces sea necesario hasta limitar acciones más fáciles y capaces de ser
desarrolladas por una única persona.

mmmm

SUBPROBLEMA 1.1 SUBPROBLEMA l.N SUBPROBLEMAN.l SUBPROBLEMA N.N

SUBPROBLEMA 2.1 SUBPROBLEMA 2.N

Figura 4.1. Esquema de diseño descendente.


El diseño descendente:

■ Simplifica la programación.
■ Permite que un programa pueda ser desarrollado por más de ima persona.
■ Contribuye a la creación de bloques de código reutilizable.
■ Favorece la abstracción.

4.2. ABSTRACCIÓN
Según la Real Academia de la Lengua Española, abstracción es "/a acción y efecto de
abstraer o abstraerse", siendo abstraer "'separar por medio de una operación intelectual las
cualidades de un objeto para considerarlas aisladamente o para considerar el mismo objeto en
su pura esencia o noción". Si esta defmición la aplicamos al desarrollo de software se ajustaría
bastante bien a uno de los paradigmas de la programación orientada a objetos por la que al
usuario solo le interesa saber qué operaciones puede realizar con un objeto, pero no la forma en
la que esas operaciones han sido diseñadas o codificadas.
En programación estructurada existen mecanismos de abstracción de forma que un
programador puede desarrollar un bloque de código y que este sea funcional y pueda ser usado
por otros programadores pero sin conocer los detalles de la programación del mismo.
Así, podemos definir la abstracción como el mecanismo por el que se consigue que un bloque
de código pueda ser usado sin conocer los detalles de su programación. La persona que lo utiliza
sólo necesitará conocer qué función desempeña, que elementos necesita para comenzar a
funcionar y cuáles serán las salidas obtenidas.
En programación estructurada se procede a la abstracción de acciones mientras que en
programación orientada a objetos la abstracción se da en los datos.

static void r.ain(string[] args)

int nurrl, num2, suira - 0;


Ccnsclf.WriteLine("Intrcduce un núxero");
nuiBl ■ int.Parse(Ccnssle.ReadLine());
Ccnscle.WriteLineCIntroduce otro núxero");
nuíiZ - int.Parse(Ccnsoie.ReadLine());
suca ■ numl + nua;2;
Cc-sc.le.WriteLine('La suma de {O) y {1} es:{2}-, numl, nuiii2
CcnscIe.ReadLine();

static void Kain(5tring[] args)


{
sumarliumeros()
;
}
public static void sumarNumero(){

Figura 4.2. Ejemplo de código con y sin abstracción.

En la Figura 4.2 se observa un código fuente sin abstracción, donde las operaciones a realizar
son visibles por todos aquellos que tengan que acceder a programar o incluir funcionalidad en el
Main. En la parte inferior, el mismo código se encapsula de algún modo de forma que no
conocemos las instrucciones usadas, solo sabemos que tenemos un identifícador llamado
sumarNumerosO.

4.3. SUBPROGRAMAS
Cuando desarrollamos un software teniendo en cuenta el diseño descendente, existen dos
elementos fundamentales, el programa principal y los subprogramas que ayudarán en la
resolución del problema.
En el programa principal es donde se soluciona el problema planteado, origen del software, y
es donde incluiremos las denominadas llamadas a subprogramas. El programa principal suele ser
sencillo y no muy extenso, además al observarlo se denota claramente la funcionalidad del
problema que se está desarrollando.
Un subprograma en cambio se encargará de resolver una parte del problema. Para ello puede
usar otros subprogramas. Un subprograma se codifica pero no se ejecuta hasta que en alguna
línea del código principal u otro subprograma se le "llame". La llamada a un subprograma
consiste en escribir el nombre de este cuando nos sea necesario.

Los subprogramas pueden codificarse junto al programa principal, en el mismo fichero o en


ficheros extemos. En caso de que se incluyan en ficheros extemos será necesario hacer alguna
referencia a estos para poder usar el subprograma, ejemplo using System;
Los subprogramas suelen ser denominados funciones o procedimientos. La diferencia entre
ambos es que devuelven o no un valor al programa que los solicita tras ejecutarse. Por ejemplo,
tma función, tras ejecutarse se comunica con el programa principal mediante la devolución del
valor de una variable o expresión, en cambio, un procedimiento es un subprograma que se
encarga de realizar una serie de operaciones sin devolver ningún valor al programa principal o
subprograma que lo ha solicitado.
FUNCION En pseudocódigo, a la hora de
crear una función usamos la
Función NombreJuncion(parámetros) retorna tipo palabra reservada Función seguida
Entorno: de una lista de parámetros
//declaración variables locales (variables) que vamos a usar en
Inicio ella. Cuando la función llega a su
//instrucciones fin se ejecuta una instmcción de
devuelve expresión; devolución de valor. Esta

FínNombreJunción; instrucción finalizará


completamente la función.

PROCEDEVUENTO Un procedimiento comienza


Subprograma Nombre_subp (parámetros) con la palabra reservada
Entorno: Subprograma en pseudocódigo.
//declaración variables locales Como se observa en la sintaxis
Inicio: básica se omite la instrucción de
instrucciones; devolución.
FinNombresubj

; Función Sumar_Dos_Numeros(entero a,entero b)retorna entero ;


; Inicio: I
; entero resultado=0; ;
1 resultado=a+b; 1
i devuelve resultado; i
FinSumar_Dos_Numeros; \
Ejemplo de pseudocódigo que"répres^^^ función. La función se llama
Sumar_Dos_Numeros y al iniciar está recibiendo como entrada dos variables (parámetros),
ambos de tipo entero, a y b. La frase retorna entero nos indica que esta función cuando finalice
va a devolver un valor entero. Accedemos al código, en él se declara una variable entera llamada
resultado a la que posteriormente se le asigna el resultado de sumar a y b. Para finalizar
devolvemos este valor al subprograma que haya realizado la llamada de la función gracias a la
línea devuelve resultado;
//Declaración y construcción de una función llamada Sumar_Dos_Numeros
Función Sumar_Dos_Numeros(entero a,entero b)retorna entero
Inicio:
entero resultado=0;
resultado=a+b;
devuelve resultado;
FinSumar_Dos_Numeros;

//Svibprograma principal donde se usarán todos los subprogramas


Subprograma Principal
Entorno:
entero numl,num2;
entero suma;
Inicio:
Escribir "Introduce un numero";
Leer numl;
Escribir "Introduce otro numero";
Leer num2;

//Llamada a la función
suma=Sumar_Dos_Numeros(a,b);

Escribir "El resultado de sumar ambos números es:";


Escribir suma;

El pseudocódigo anterior muestra un ejemplo de uso de ima función. Esta se crea en esta
ocasión en el mismo fichero de código fuente que la función principal que la va a utilizar. Se
observa cómo en la declaración, los valores de entrada o parámetros se establecen indicando el
tipo de datos que almacenan, sin embargo, algo más abajo, cuando se utiliza la función no es
necesario indicar este, solo debemos escribir las variables del mismo tipo que se van a usar
(Sumar_Dos_Numeros(a,b);).
El usar funciones implica la devolución de un valor de forma que cuando se hace la llamada a
la función esta debe ser asignada a alguna variable o escribirse en alguna instrucción, tipo salida
de datos. La llamada a una función o procedimiento consiste tan solo en escribir el nombre del
mismo sin olvidar los paréntesis y posibles parámetros de entrada.
Subprograma Sumar_Dos_Numeros()
Entorno:
entero numl, num2;
entero suma=0;
Inicio:
Escribir "Introduce un numero";
Leer numl;
Escribir "Introduce otro número";
Leer num2;
suma=numl+num2;
Escribir "La suma de ambos números es:";
Escribir suma;
FinSumar_Dos_Numeros;
En este pseudocódigo estamos desarrollando un procedimiento. Vemos que no existen ni en la
cabecera ni en el código referencia a retomo de ningún valor. Se realizan todas las operaciones
en el subprograma y una vez este fianliza cede el control al programa principal o subprograma
que lo llamó. Es equivalente a los algoritmos que hemos estado realizando a lo largo de estos
primeros capítulos.
144 Programación

/♦Declaramos un procedimiento llamado Sumar_Dos_Numeros que no necesita


parámetros de entrada*/
Subprograma Sumar_Dos_Numeros ()
Entorno:
entero numl, num2;
entero suma=0;
Inicio:
Escribir "Introduce un numero";
Leer numl;
Escribir "Introduce otro número";
Leer num2;
suma=numl+num2;
Escribir "La suma de ambos números es:";
Escribir suma;
FinSumar_Dos_Numeros;

//Subprograma principal donde se usarán todos los subprogramas


Subprograma Principal
Entorno:
Inicio:
Sumar_Dos_Numeros();

Al igual que ocurría con eí ejemplo en el que creábamos y utilizábamos una función, en este
se declara y crea el subprograma en el mismo fichero donde se ubica el programa principal. Sin
embargo, en el código del programa principal solo se realiza la llamada al procedimiento, ya que
es este quien codifica todas las operaciones de entrada y salida. La llamada no implica ninguna
asignación como podemos observar en la línea Suniar_Dos_NunierosOv
Conforme se realicen actividades, se desarrollen programas, iremos adquiriendo experiencia y
sabremos en cada momento qué tipo de subprograma usar (subprograma o función) y si debemos
utilizar parámetros o no.

4.4. FUNCIONES EN O#
C# dispone de dos mecanismos para implementar la modularídad. funciones y clases. El
término modularídad se ha definido ya de forma subliminal en este capítulo pues constituye la
esencia del diseño descendente, sin embargo establezcamos a continuación una definición
formal.

Medularidad es la propiedad que permite la división de un programa en partes más pequeñas


llamadas módulos, siendo cada uno de ellos tan independientes como sea posible del bloque
principal y resto de partes. Los módulos pueden compilarse por separado manteniendo
conexiones con el resto. Cada lenguaje de programación modular afronta esta de diferentes
formas.

Esta primera aproximación a la programación modular nos centraremos en el uso de


funciones. C# dispone además de bibliotecas estándar de clases llamadas Frameworks que nos
serán muy útiles ya que incluyen funciones ya definidas listas paras usar en nuestras
aplicaciones. Esta biblioteca puede ser usada por todos los lenguajes .NET.
Una función, como ya se vio en pseudocódigo, es un grupo de instrucciones delimitadas por
un símbolo llave de apertura y otro de cierre. C# no hace distinción entre función y
procedimiento, en cuanto a que no usa palabras reservadas especiales para detemiinar si estamos
usando uno u otro tipo de bloque, sabremos si tenemos entre manos una función o un
procedimiento si devolvemos algún valor o no, y si en el interior del bloque encontramos la
sentencia de retomo.

La estmctura básica de una función en C# es la siguiente:

Modificadores tipojievuelto nombreJunción (lista_de_parámetros){


Sentencias;
retiirn expresión;//Solo en caso de que tipojievuelto sea distinto de void
}
Modificadores. Palabras reservadas que aplicadas sobre la función le proporcionan
características adicionales relacionadas con la visibilidad y otros aspectos que ya
estudiaremos. Por ejemplo, podemos encontrar las palabra public, prívate o static.
tipo_devuelto. En caso de que la función devuelva algún valor indicaremos aquí el
tipo de datos básico que devuelve (int, char, etc.). Si la función no tiene retomo
debemos indicarlo con la palabra reservada void.
nombre_función. Es el identificador de la función que usaremos para realizar la
llamada cuando queramos utilizarla. Debemos usar nombres representativos que
indiquen qué tipo de acción realizará la nueva estmctura del lenguaje de
programación.
Iista_de_parámetros. Representa el conjunto de pares tipo-identificador (variables)
que usaremos en la función. El uso de parámetros pennite que puedan utilizarse los
valores asignados a variables creadas en otras zonas del código.
return expresión. Sentencia que indica que debe devolverse un valor.
Concretamente, el valor devuelto por la función expresión puede ser una variable o el
resultado de una expresión aritmética, booleana, etc.

O, modificador public, permite que la


función pueda usarse más allá de la clase donde
^34 int suii:ar_DosjJurteros(int a, int b)
public int 5u(rar_Dos /4ufrero5(int a, int b)
se ha creado. ©,tipo de dato devuelto, en el
cjemplo sc retoma un valor entero. El nombre
{
resultado >6;
int resultado = 6;
jltado.a + b;
resultado - a t b;
f de la fúnclón, o, Suniar_Dos_Numeros
|í 0^
jrn resultado;
return resultado; '41 ^^9 i, segulda dc la lista de parámetros, v. Para
}
, . , 1,1 finalizar tenemos seleccionado como @ la
sentencia return. Es en este punto donde se
■a 4.3.
Figura 4.3. Elementos
Elementos de
de una
una función.
función. devuelve el valor entero que se especificó en la
cabecera. El valor devuelto es el almacenado en
una variable llamada resultado.

4.5. LLAMADA A UNA FUNCIÓN EN O#


La llamada a una función, ya sea en C# o cualquier otro lenguaje de programación indica el
momento en el que se hace referencia a ella en el código fuente, es decir, el momento en el que
se incluye entre las líneas de código para ser usada. Con la llamada a una función estamos
diciendo que queremos que se ejecuten las líneas de código de esta función. Realizar la llamada a
una función es poco más que escribir su nombre y parámetros en la función que desea usarla.
Veamos un ejemplo.
Supongamos que nos piden la realización de un pequeño programa de consola que muestra un
menú con las siguientes opciones:
1. Información de red.

2. Ping.
3. Salir.

A la hora de analizar la aplicación observamos que:


■ Debemos preparar un menú con las opciones que se piden.
■ Debemos tener un trozo de código por cada opción del menú (función).
■ Para que el usuario pueda usar las opciones tanto como desee, el programa debe
ejecutarse una y otra vez, es decir, debemos configurar un bucle que finalice cuando
el usuario pulse 3.
■ Además, debemos tener una sentencia de control alternativa por la que el usuario
pueda escoger la opción que desee, una sentencia alternativa múltiple.
//FUNCIONES

/*Función menú*/

/ Función información de red*/


/*Función ping*/
/*Función principal*/
Hacer
Mostrar menú
escoger opción
ejecutar opción escogida
Mientras opción distinta de 3
Según el análTsTsV reaHcemos"cada''fúncióñV Recorda la estructura que deben tener
(modificador tipo nombre parámetros) y según esto vamos a preguntamos una serie de
cuestiones, para así tener clara cuál será la cabecera de cada una.
Comenzamos por la función menú.
1. ¿Cómo llamaremos a la función? El nombre más acertado sería mena, es
representativo de la tarea que va a realizar y corto.
2. ¿Necesita algún parámetro? Al parecer no, ya que la función que realizará será la de
mostrar una serie de opciones con lo que no necesita que el programa principal le
proporciones información adicional.
3. ¿Devolverá algún valor? Sí, ya que en esta misma función solicitaremos al usuario
que escoja una opción. Una función puede desarrollarse de muchas maneras,
podríamos crearla de forma que solo mostrara por pantalla las opciones y la función
principal preguntara al usuario qué opción escoger, sin embargo, y debido a que el
escoger una opción va ligado al propio menú, se considera que es mejor que se
incluya con esta función. Así, devolveremos el tipo de dato que vayamos a usar para
establecer las opciones. Este será entero ya que cada línea del menú comienza con un
número que es representativo de la misma.
¿Debemos usar modificadores? Sí. Aún no hemos alcanzado los conceptos necesarios
para responder a esta pregunta adecuadamente. Nos limitaremos a escribir la palabra
reservada static. Estudiaremos los modificadores al mismo tiempo que las clases y
objetos.

Función estática menú


Parámetros de entrada: ninguno
Valor devuelto: entero

estática entero menu() {...)

static int menú()

int opcion;
Consolé.WriteLine("MENU");
Console.WriteLineC'l. Información");
Consolé.WriteLine("2. Ping");
Consolé.WriteLine("3. Traza");
Consolé.WriteLine("4. Salir");
Consolé.WriteLine("¿Qué deseas hacer?");
opcion = int.Parse(Consolé.ReadLineO);
return opcion;

Función necesaria para mostrar información de red. Esta función proporciona datos
relacionados con la red:

1. ¿Cómo llamaremos a la función? Existen muchas opciones, información, info_red,


informac¡on_de_red, informacionRed, infoRed, etc., escogemos infoRed. Es un
identificador muy orientativo y corto, dejamos al lector que use aquel que más le
guste.

2. ¿Necesita algún parámetro? No. La infonnación será extraída del sistema con lo que
la función principal no necesitará comunicarse con esta.
3. ¿Devolverá algún valor? Antes de responder a esta pregunta pensemos qué queremos
hacer realmente en esta función. Debido a que el objetivo de este ejemplo es que el
lector entienda por qué usar funciones, cómo configurarlas y utilizarlas, no vamos a
incluir demasiado código C# nuevo que pueda llevar a confusión. Así, la función
infoRed mostrará todas las interfaces de red del equipo, de cada una de ellas
proporcionará información sobre: descripción de la interfaz, si usa el protocolo IPv4 o
IPv6, la dirección MAC y el tipo de adaptador. La función se limitará a mostrar
información al usuario a través de la pantalla, una vez se visualice esta, pasaremos el
control al programa principal para que vuelva a activar el menú, con lo que no existe
ningún intercambio de variables entre el programa principal y la función, así NO se
devolverá ningún dato. En este lugar en la cabecera de la fiinción escribiremos void.
4. ¿Debemos usar modificadores? Utilizaremos la palabra static.
~ — — — — — — — — —1

/* ;
Función estática infoRed I
^Parámetros de entrada: ninguno ;
Valor devuelto: ninguno

estática void infoRed () {...}

static void infoRedO


{
/*Guardamos en interfacesRed todas las interfaces de red de nuestro
equipo*/
Networkinterface[] interfacesRed =
NetworkInterface.GetAllNetworkInterfaces();

/*Por cada interfaz hacemos lo que esta en el bucle foreach


Veremos el bucle foreach cuando estudiemos la estructura de datos
array*/
foreach (Networkinterface adaptador in interfacesRed)

//Descripción de cada interfaz


Consolé.WriteLine(adaptador.Description);

//Tipo de interfaz
Consolé.WriteLine("Tipo de adaptador:(O}
,adaptador.NetworkinterfaceType);

//Dirección física o MAC


Consolé-WriteLine("Dirección MAC:{O}"
,adaptador.GetPhysicalAddressO .ToStringO);

if (adaptador.Supports(NetworkInterfaceComponent.IPv4))
Consolé.WriteLine("Versión del protocolo IP: IPv4");

Consolé.WriteLine("Versión del protocolo IP: IPv6");

Consolé.WriteLine("-
}
Consolé.ReadLine();

Función ping. ErobJeVivoTe e¥ta fiincTónlérá básica^^^ comprobar si existe comunicación


con algún otro nodo de la red. Ping es un comando usado normalmente por los administradores
de redes para realizar una primera comprobación del estado de las comunicaciones entre los
equipos informáticos de la red corporativa que administran. Básicamente, el modo de
funcionamiento del comando consiste en el envío de un paquete de datos al ordenador destino. Si
se recibe una respuesta a los datos enviados se interpretará tal que se ha podido establecer
contacto y la comunicación es correcta, en cambio si no recibimos respuesta, el destino es
inalcanzable, se produce error en la comunicación. Usaremos ping en modo consola siguiendo la
sintaxis ping dirección IP.
La función ping que queremos desarrollar en este ejemplo realizará una función similar al
comando original, la diferencia la encontraremos en la salida que mostrará al usuario, ya que tan
solo escribiremos el mensaje Correcto, host accesible o Fallo, host inaccesible
Según lo explicado, antes de comenzar a desarrollar la función, daremos respuestas a nuestras
cuatro preguntas.
1. ¿Cómo llamaremos a la función? Ya que ejecutará el comando ping vamos a llamarla
así, es representativo de la función que va a desarrollar.
2. ¿Necesitamos algún parámetro? Cuando ejecutamos ping necesitamos indicar la
dirección IP con la que establecer comunicación. Esta dirección puede pedirse dentro
de la misma función o ser pasada como parámetro. En esta ocasión vamos a hacer uso
de estos parámetros, configuraremos la función ping con un único parámetro pero,
¿de qué tipo? Posterionuente observaremos en la codificación que el método en C#
que hará un llamamiento al comando ping del sistema operativo necesita que la
dirección IP se exprese como una cadena de caracteres, así, nuestro parámetro será de
este tipo de datos, string.
3. ¿Devolverá algún valor? La función muestra información al usuario a través de la
salida estándar, de fonua que las líneas relacionadas con esta visualización pueden
realizarse dentro de la función. No es necesario ningún dato de retomo.
4. ¿Debemos usar modificadores? Como en las anteriores funciones el único
modificador que usaremos será static.
— I

/* i
Función estática ping •
Parámetros de entrada: cadena de caracteres (dirección IP) !
Valor devuelto: ninguno ;

estática void ping(cadena ip) {...} I


*/ i

static void ping(string ip) I


{ i
/*Tipo de datos (clase) que contiene métodos que hacen llamamientos a I
comandos de red*/ ;

Ping ping = new PingO; I

/*ping.Send(ip) equivale al commando del sistema ping ip ;


El método ping.send devuelve el estado de la comunicación. En caso de I
que haya sido correcta devuelve la palabra Success, si no se alcanza el ;
host observaremos la palabra TimedOut*/ ;

PingReply respuesta = ping.Send(ip); i


/*Si se obtiene respuesta o no personalizamos el mensaje como se |
muestra. Realizamos la misma operación si la respuesta es negativa.*/ ;
if (respuesta.Status.ToStringO == "Success") !
{ i
Consolé.WriteLine("Correcto, host accesible"); |

Consolé.WriteLine("Fallo, host inaccesible");

Llegados a este punto hemos finalizado la primera parte del ejemplo, hemos desarrollado
todas las funciones. En Visual studio, debemos crear un nuevo proyecto e incluir todas estas
funciones entre las llaves de la clase (línea que comienza con la palabra resei-vada class).
Finalmente, debemos ver algo similar a esto;
namespace Funciones
{
class Program

Función estática menú


Parámetros de entrada: ninguno
Valor devuelto: entero

estática entero menú () {...}


*/
static int menú()
{
int opcion;
Consolé.WriteLine("MENU");
Consolé.VíriteLine("1. Información");
Consolé.WriteLine("2. Ping");
Consolé.WriteLine("3. Salir");
Consolé.WriteLine("¿Qué deseas hacer?");
opcion = int.Parse(Consolé.ReadLine());
return opcion;

Función estática infoRed


Parámetros de entrada: ninguno
Valor devuelto: ninguno

estática void infoRedO {-.}


*/
static void infoRedO
í
Networjcinterface[] interfacesRed =
NetworkInterface.GetAllNetworkInterfaces O ;
foreach (Networkinterface adaptador in interfacesRed)
{
Consolé.WriteLine(adaptador.Description);
Consolé.WriteLine("Tipo de adaptador:{O)"
,adaptador.NetworkinterfaceType);
Consolé.WriteLine("Dirección MAC:{0}"
adaptador.GetPhysicalAddressO .ToStringO);
if (adaptador.Supports(NetworkinterfaceComponent.I Pv4))
^ Consolé.WriteLine("Versión del protocolo IP: IPv4");
}
else
{
Consolé.WriteLine("Versión del protocolo IP: IPv6");
}
Consolé.WriteLine("
Función estática ping
Parámetros de entrada: cadena de caracteres (dirección IP)
Valor devuelto: ninguno

estática void ping (cadena ip) {...)

static void ping(string ip)

Ping ping = new Ping();


PingReply respuesta = ping.Send(ip);
if (respuesta.Status.ToString() == "Success")
{
Consolé.WriteLine("Correcto, host accesible");

Consolé.WriteLine("Fallo, host inaccesible");

Función estática Main (function principal)


Parámetros de entrada: Array de cadenas de caracteres (args)
Valor devuelto: ninguno
*/
static void Main(string[] args)

}//Fin de la clase Program


}//Fin del espacio de nombre Funciones
Hasta el momento vemos código foente delimitado y denominado mediante nombres.
Observamos el nombre menú que delimita una serie de líneas de código entre sus llaves, serán
esas las que se ejecuten cuando se utilice, igual ocurre con el método infoRed y ping. Todas las
funciones están declaradas pero aún no están siendo usadas. Vamos a desarrollar el código a
incluir en el Main.

static void Main(string[] args) i


{ i
int op; ■
string ip; i
do :
{ :
op=menu(); |
switch(op){ :
case 1: infoRedO; //Llamada a la función infoRed ;
break; i
case 2: Consolé.Write("Indica dirección IP:"); I
ip = Consolé.ReadLineO; I
ping(ip); //Llamada a la función ping j

break; ;
case 3: Consolé.WriteLine("Gracias por usar este :
software"); ^
break;
default: Consolé.WriteLine("Opción inexistente"); :
break;
152 Programación

Consolé.ReadLine();

Las llamadas se realizan en los casos 1 y 2. Observamos que una llamada a una función
equivale a colocar su nombre seguida de paréntesis en el lugar donde se desea sea ejecutada.
Cuando el usuario escoja la opción 1 accederemos al case 1 y se leerá infoRedO, se detecta que
esta es una función y que debe ser localizada en el resto del código. Una vez la encontramos
pasan a ejecutarse sus líneas. Al finalizar, la ejecución continúa justo después de la llamada a la
función en el código principal.
Si la función tiene parámetros, a la hora de realizar la llamada estos no pueden omitirse. Por
ejemplo, la cabecera de la función ping es static p¡ng(string ip) de forma que cuando la usemos
debemos especificar entre los paréntesis un valor que sea compatible con el parámetro
establecido. En el caso 2, en la función principal, la llamada se muestra como pingfip), la
diferencia con la declaración es la omisión del tipo de datos. Esto es así debido a que en la
llamada se incluye entre los paréntesis variables o expresiones del tipo de dato que indica la
declaración, es decir, cuando realizamos la llamada pasamos a ejecutar el código, de forma que
es preciso establecer valores concretos a los parámetros de entrada.
Como regla general:
//Declaración de lafunción
modificador tipo_devuelío nombre_función(tipoparí, tipopar2,..., tipoparn){
Sentencias;
return expresión; //Si tipo_devuelto es distinto de void

static void Main(string[]args){


Sentencias;

Sentencias;

Resumiendo:
■ Definición o declaración de la función. Desarrollo de la función, especificación del
código fuente de esta.
■ Invocar o llamar a una función. Momento en que se hace referencia a ella en otra
zona del código de forma que se da la orden de ejecución de esta.
■ Paso de parámetros. Si la función tiene parámetros, cuando realizamos la llamada
estos deben ser sustituidos por variables o expresiones del tipo de datos
correspondiente.
■ Valor devuelto. En caso de que la función devuelva un valor este debe ser usado en
una expresión, aunque no producirá error si no se considera.
La Figura 4.4 es bastante representativa.
NOTA: Una función que no devuelve valor finaliza cuando acaban sus instrucciones,
accedemos a la llave de cierre. En caso de que usemos la sentencia return, será ahí donde
finalice. Aunque suele estar al final, podemos encontrar retum en cualquier parte del |
código, en ocasiones hasta varias veces.

1ENU 'jUiROGRAMA EN EJECUCIÓN


L. Infornación
i. Pina
J. Salir
IQué deseas hacer?
■Indica dirección ip{l92^168.1.i_ |

EJECUCIÓN

case 2: Cc.".-^ie.Write('Indica dirección IP:')


ip = Ct|^sole.ReadLine();
ping(ip);
break: AccedemosÁL CÓDIGO DE t» FUNaóN y este se ejecuta

static void ping(stringlip)H^»jj^JIjS*wM


Ping ping « new Pin£();
FINAUZA LA FUNCIÓN Y LA PingReply respuesta = ping.Send(ip);
if (respuesta.Status.ToStringO " "Success")
EJECUCIÓN CONTINUA EN LA
{
SIGUIENTE INSTRUCCIÓN DEL Ccn:.i".e.WriteLine("Correcto, host accesible"); i
CÓDIGO PRINCIPAL }
else

Censóle.WriteLine("Fallo, host inaccesible");


>

Figura 4.4. Llamada a una función en la ejecución de un programa.

ACTIVIDAD 4.1

Crea una función que pida el lado de un cuacirado y calcule su área. Usa posteriormente esta.

ACTIVIDAD 4.2

Codifica una función que pida los valores necesarios para calcular el área de un triángulo. Usa
posteriormente esta.

ACTIVIDAD 4.3

Crea una función que calcule la potencia a partir de la multiplicación de la base x las y
veces del exponente. Usa posterionuente esta función.

ACTIVIDAD 4.4

Desarrolla una función que devuelva verdadero o falso según si el carácter que recibe como
parámetro es o no una vocal (vocal = verdadero, consonante = falso). Codifica una función
154 Programación

principal donde se pidan de forma continuada caracteres (finaliza cuando se introduce 0) y


compruebe para cada uno de ellos si se trata de una vocal o una consonante.

4.6. PARÁMETROS DE UNA FUNCIÓN


Un parámetro en una función permitirá la comunicación de esta con el código donde se
invoca, es decir, permitirá que esta pueda usar los valores o variables declaradas dentro de la
función que la llama.

static void pin^strlng ip) J


Ping ping - new PingO;
PingPepiy respuesta « ping.Send(ip);
if (respuesta.Status.ToStringO -» "Success")
{
Censóle.KriteLine("Correcto, host accesible");
}
else
{
Consolé.KriteLine(".=allo, host inaccesible");
}

i static void Kain(string[] args)


int op;
string ip;.^^ Variable ip solo visible aquí
do Para
Para poder usarla en
{ la función ping
op=ir.enu(); .
debemos pasarla
switch(op){l
case 1: infoRed(); como parámetro
break;
case 2: Co.nsole.Write^PÍndica dirección IP:");
i ip ■j-&s«tcIej,peáaLine();
; pinJ(ip);J
i break;I ^

Figura 4.5. Uso de parámetros.


Normalmente, en la mayoría de los lenguajes de programación, se distinguen dos métodos
para pasar parámetros a una función: paso de parámetros por valor y paso de parámetros por
referencia.

■ PASO DE PARÁMETROS POR VALOR. En este tipo de paso de parámetros


cuando se ejecuta la función se hace una copia del parámetro pasado, de forma que
las posibles modificaciones realizadas sobre él, a nivel local a la función, no alteren
su valor en la función principal. Cuando el paso se hace por valor podemos usar
como parámetros en la llamada a la función variables, constantes o expresiones.
■ PASO DE PARÁMETROS POR REFERENCIA. En este tipo de paso de
parámetros las modificaciones realizadas a nivel local afectarán al valor del
parámetro más allá de los límites de la función. En el paso por referencia solo
podemos usar como parámetros variables.
Para que queden claros los conceptos, imaginemos una función intercambio que, como su
nombre indica realiza el intercambio de valores entre dos variables.
Capitulo 4. Funciones

static void intercambio(int a,int b) {


int c;
c=a;

a=b;
b=c;

Resultado final

a=4 y b=6

Llamada a intercambio
Paso por valor

Figura 4.6. Paso de parámetros por valor.

Static void intercdírbio(ref int a,ref int b)


{
int c;
c - a; <
a • b;

.j ^ a=5yb=4
<r- ~r u n

- Función principal
Static void Main(string[] args)
i{
í b=6
j int a, b;
I a ■ 4;
b ■ 6;
^ intercairbio(ref a, ref b);
^ CcPScle.Kr^eLineCasiB} y b*!!}", a, b); '

Llamada a intercambio
Paso por referencia

Figura 4.7. Paso de parámetros por referencia.


En un paso por referencia el parámetro y la variable ocupan el mismo espacio en memoria,
son el mismo elemento.

4.6.1. PASO DE PARÁMETROS POR VALOR EN C#


El paso por valor en C# no requiere ningún tipo de modificador adicional, de hecho, todas las
funciones de ejemplo que hemos usado hasta ahora utilizan el paso de parámetros por valor.
En la función tan solo especificaremos el tipo de datos y el nombre del parámetro mientras
que en la invocación sólo la variable, constante o expresión que usaremos.
static int div_E(int dividendo, int divisor)
{
int resultado=0;
while(dividendo>=divisor){
resultado++;
dividendo = dividendo - divisor;
}
return resultado;

4.6.2. PASO DE PARÁMETROS POR REFERENCIA EN O#


En C# existen dos formas de pasar parámetros por referencia, usando las palabras reservadas
ref y out. La diferencia entre el uso de uno u otro radica en la necesidad de que los parámetros
deben estar inicializados o no. En el caso de usar ref las variables que pasamos como parámetros
deben estar inicializadas en la ejecución y uso de la función. Sin embargo, cuando utilicemos out
no será necesaria la inicialización ya que se presupone el dato out como un valor de salida y le
será asignado un valor en el código de la propia función.
Normalmente usaremos out cuando necesitamos que una función devuelva más de un valor.
Por ejemplo, supongamos una función que debe devolver la circunferencia y área de un círculo:
static double calculoCirculo(int radio, out int area) ;
double circunferencia; ;
circunferencia=2*3.1415*radio; i
area = 2 * radio * radio; i
return circunferencia; '

El código fuente representa'a'Ta'"fúncióncaVcúíóarcu^^ que calcula la circunferencia y el


área de un círculo con radio dado. Debido a que solo podemos revolver un valor, se ha optado
porque la función devuelva la circunferencia del círculo. Se usa un parámetro llamado area que
no está inicializado y junto al que se muestra la palabra reservada out. El parámetro área se está
pasando por referencia, de forma que en la línea area=2*rad¡o*radio se modifica el valor de
área prevaleciendo este en la función principal.
static void Main(string[] args)" 1
int r = 2; 1
double circunferencia; 1
int area; ;
circunferencia = calculoCirculo(r, out area); 1
Consolé.WriteLine("La circunferencia es:{0} y el area es {1}" I
,circunferencia,area_)_;_}_ I
Cuando invocamos el método la palabra reservada out debe volver a colocarse junto al
parámetro.
En las funciones con paso de parámetros se incluirá la palabra reservada ref junto al
parámetro tanto en la declaración como en la invocación a la función.
Resumiendo:

modificacadores tipo_devuelto fiambreJunción(reftipo parí,.... reftipo parn){ i


Sentencias; 1
retiirn expresión; í
} • : i
static voidMain(string args[]){ i

nombre_función(refvari,..., refvarn); i t

modificacadores tipojdevuelto nombreJunción(out tipo parí,..., out tipo parn){


Sentencias;
return expresión;
}

static voidMain(string args[]){

nombre_función(outvarl,..., out varn);

ACTIVIDAD 4.5

Escribir una función llamada redondear tal que acepte como parámetros un número real y un
número entero, siendo el primero de ellos el que se debe redondear, según se intuye en el nombre
de la función. La función devolverá el número redondeado la cantidad de posiciones decimales
que establezca el parámetro entero a través del propio parámetro real, es decir, cuando
realicemos la llamada: redondear (numero, posiciones); será en número donde se almacena el
resultado, la función no devuelve ningún tipo de datos.
Ejemplo: redondear (2.3658,2) -> 2.37.

ACTIVIDAD 4.6

Realizar un programa permita cualcular el área de diferentes figuras geométricas de fonna


que muestre el siguiente menú:
a. Círculo.

b. Cuadrado.
c. Rectángulo.
d. Triángulo.
e. Salir.

Se deben usar funciones. Se aconsej'a que el lector practique el paso de parámetros por valor y
referencia tal y como se ha estudiado, aunque lo ideal en este tipo de ejercicios es que se
devuelva el valor pedido de forma normal.
Recuerda que;
Área del círculo = PI * radio^
Área del cuadrado = lado^
Área del rectángulo = ancho * alto
Área del triángulo = base * altura / 2

4.7. FRAMEWORK .NET


Todas las versiones de C# disponen de una biblioteca estándar de clases y funciones que
proporcionan soporte para las operaciones más utilizadas. Así, existen operaciones que no
tendremos que codificar, ya que han sido creadas por otros programadores y podemos usarlas en
nuestras aplicaciones. Un ejemplo es el objeto Consolé y sus métodos WriteLine y ReadLine
usados frecuentemente en los ejercicios resueltos de este libro.
El Framework estará estructurado en clases que contienen funciones bastante útiles. A su vez,
estas clases se agrupan en espacios de nombres o paquetes en función del área conceptual en que
se encasillen.

Podemos organizar el framework en diferentes grupos teniendo en cuenta la funcionalidad de


las clases que lo forman. Encontraremos funciones:
■ de entrada/salida.
■ numéricas.

■ de manipulación de cadenas.
■ fecha y hora.
■ de carácter.
■ aleatorias.

4.7.1. FUNCIONES DE ENTRADA/SALIDA


En este grupo podemos encontrar la clase Consolé que incluye las operaciones necesarias
para operar con la entrada, salida y error estándar en aplicaciones de consola. Para poder usar los
elementos de la clase Consolé escribiremos esta seguido del carácter punto. Visual Studio
muestra todos los métodos y atributos acompañados de una pequeña descripción de estos y su
sintaxis nada más teclear el símbolo.
O SetWindovvSce
A T,tle
>22222222 bool Conjole.TrcatContfolCA5lnput ^
> V/indowHtight Obtiene o establece un valor que indica si la combinación de la tecla modificadora
VVindowleft
System.ConsolcModffiers.Conlrol y de la tecla de consola Systen.ConsoIcKej.C (Ctri*C)se trata
como una entrada ordinaria o como una interrupción controlada por el sistema operativo.
> WmdowTop
> VVmdowWidth Excepciones:
O VVrrte S>'stemJOJOException
0 V/frteLme

Figura 4.8. Acceso a los métodos de una clase en Visual Studio.

Si el lector lo desea puede usar la referencia web http://msdn.microsoft.coni/es-


es/library/system.console.aspx y ver todos los métodos y propiedades que contiene. Los más
característicos son:

■ Write y WriteLine. Métodos para mostrar infonnación en pantalla.


■ Read y ReadLine. Métodos para recoger información introducida por el usuario a
través de la entrada estándar, el teclado.
■ CapsLock, NumberLock y KeyAvailable. Estados del teclado. Son propiedades.
■ Clear. Método que pennite borrar la ventana.

4.7.2. FUNCIONES NUMÉRICAS


La clase encargada de proporcionar las funciones numéricas es Math, podemos ver todos sus
métodos y propiedades en la referencia web http://msdn2.microsoft.com/es-
es/Iibrary/system.math.aspx. Veamos algunos ejemplos de funciones numéricas.
Funciones matemáticas:

■ Truncate(x). Calcula la parte entera de un número decimal.


■ Abs(x). Devuelve el valor absoluto de un número.
■ Pow(x,y). Calcula x^.
■ Sqrt(x). Calcula la raíz cuadrada de un número dado.

Funciones trigonométricas:
■ Acos(x), Asin(x) y Atan(x). Arco coseno, seno y tangente de x.
■ Cos(x), Sin(x) y Tan(x). Coseno, seno y targente de x.
Funciones logarítmicas:
■ Exp(x). Calcula e^
■ Log(x). Logaritmo neperiano de x.
■ LoglO(x). Logaritmo decimal de x.

4.7.3. FUNCIONES DE MANIPULACIÓN DE CADENAS


La clase String es la encargada de proporcionar los métodos necesarios para la manipulación
de cadenas de caracteres. Podemos estudiar todas sus propiedades y funciones en la referencia
web http://msdn.microsoft.com/es-es/Iibrary/system.string.aspx. A continuación veamos
algunos de los métodos y propiedades más usadas:
■ Length. Propiedad que obtiene la longitud de la cadena de caracteres.
■ Compare(string,string). Compara las cadenas de caracteres incluidas entre los
paréntesis indicando su ordenación.
■ CompareTo(String). Otro método de comparación de cadenas. Compara la cadena
actual con la que se especifica entre los paréntesis.
■ Equals(String). Determina si dos cadenas de caracteres son iguales.
■ IndexOf(char). Devuelve la posición de la primera aparición del carácter
especificado entre los paréntesis.
■ SpIit(Char[]). Divide la cadena en partes en función del elemento delimitador
especificado.
■ ToLowerQ. Devuelve una copia de la cadena con todos sus caracteres en minúsculas.
■ ToUpperQ. Devuelve una copia de la cadena con todos sus caracteres en mayúsculas.
4.7.4. FUNCIONES DE FECHA Y HORA
Para obtener la fecha y hora del sistema usaremos la clase DateTime. La referencia web a esta
clase es http://msdn.microsoft.com/es-es/library/system.datetime.aspx. Algunos de sus
métodos y propiedades son:
■ Date. Obtiene la fecha. Es una propiedad.
" Month y Year. Propiedades para obtener el día, mes y año.
■ Hour, Minute y Second. Propiedades para obtener horas, minutos y segundos.
" Now. Obtiene un objeto DateTime con sus propiedades inicializadas a la fecha y hora
actuales.

4.7.5. FUNCIONES DE CARÁCTER


El objeto que proporciona funciones para manipulación de caracteres es Char. La referencia
web a esta clase es http://msdn.microsoft.com/es-es/library/system.char.aspx. Encontraremos
propiedades y métodos del tipo:
■ IsLetter(c). Indica si el carácter introducido c es una letra.
■ IsDigit(c). Indica si el carácter introducido c es un dígito.
■ ToUpper(c). Convierte c a mayúsculas.
■ ToLower(c). Convierte c a minúsculas.
4.7.6. FUNCIONES ALEATORIAS

Para la simulación de situaciones aleatorias se usará en C# la clase Random. A la hora de


trabajar con números aleatorios debemos:
1. Iniciar la semilla de números aleatorios. Acción que solo se realiza una vez cuando se
crea el objeto. La semilla será el valor inicial a partir del que se reproduce el ciclo de
creación de números aleatorios.

2. Generar números aleatorios.

Para generar los números aleatorios disponemos de diferentes métodos:


■ NextQ. Genera un número aleatorio entero.
■ Next(valor). Devuelve un número aleatorio positivo menos que valor.
■ Next(m¡n,max). Devuelve un número aleatorio positivo en el intervalo min y max.
■ NextDoubleO- Devuelve un número aleatorio entre 0.0 y 1.0.
- - - — - -- — - — — — — ___ — )

static int numeroLoteria(Random ale) |


{ i
return ale.Nextd, 4 6); I
) i
static void Main(string[] args) !
{ i
Random aleatorio = new Random(); |
Consolé.WriteLine("Números de la lotería primitiva:"); |
for (int i = 0; i < 6; i++) I
{ i
Consolé.Write ("{ O ) numeroLoteria(aleatorio)); '<
} ;
Consolé.ReadLineO; ;

Este pequeño progi'ama ejemplo tiene un faíío que podremos suplir en el siguiente capítulo
cuando estudiemos el tipo de datos array. Los números aleatorios pueden aparecer repetidos.

4.8. RECURSIVIDAD
Cuando se estudian las funciones es común hablar de funciones recursivas. Una función es
recursiva cuando entre sus líneas realiza una llamada o invocación a sí misma.

modificador tipo_devuelto nombre^función (tipoparí,..., tipoparn){

nombreJunción(varl,... ,var2);

Se pueden distinguir dos tipos de recursividad:


■ Recursividad directa: Es aquella en la que la función realiza una llamada a sí misma
desde un punto concreto entre sus llaves de inicio y fin.
■ Recursividad indirecta: Es aquella en la que la función realiza una invocación a otra
función que es quien llama a la primera.
Todos los problemas recursivos presentan una estructura similar, contemplan:
■ Un caso base que permite la finalización del problema.
■ Casos recursivos que hacen que la función vuelva a ser ejecutada.
Veamos un ejemplo de función recursiva. Vamos a calcular el factorial de un número. Antes
de configurar la función vamos a analizar el problema y encontrar el o los casos base y casos
recursivos.
■ — — - -- -- -- -- -- -- - — - -- -- -- - — - -- -- -- -- - —

Elfactorial de un número n es: |


n!= n*(n-l)*(n-2)*(n-3)*...*1 •
Si quisiéramos crear una fiinción en C# que calculara el factorial de un número esta podría
definirse de este modo: int factoriaI(int numero).
¿Cuál podría ser el caso base? El caso base finaliza el programa, de fonua que teniendo en
cuenta esta premisa, podemos preguntamos cuándo finaliza el cálculo del factorial. El factorial
acaba cuando llegamos a 1, así, si nuestra función tuviera que calcular el factorial de 1, este sería
1.

factorial(l)=l;
¿Cuál podría ser el caso recursivo? Si observamos el cálculo del factorial líneas más arriba,
veremos que multiplicamos continuamente un número por su inmediatamente anterior, de fonna
que podemos decir que el factorial de un número podría expresarse igualmente como:
n!=n*(n-l)!, o lo que es lo mismo
factorial(n)= n*factorial(n-l);
Dicho todo esto:

■ Caso base: si n=l, factorial(l) devuelve 1;


■ Caso recursivo: si n>l,factorial(n)=n*factorial(n-l);
— — -1

Función factorial(entero n) retorna entero{ 1


Si n > 1 entonces |
//Caso recursivo |
devuelve n*factorial(n-1); i
Sino I
devuelve 1; //Caso base ;
FinSi; ;
} I

En C# la aplicación se vería como se indica a continuación:


static void Main(string[] args)
{
int numero;
Consolé.WriteLine("Introduce un número");
numero = int.Parse (Consolé.ReadLine());
Consolé.WriteLine("El factorial de {0} es {1}",
numero, factorial(numero));
Consolé.ReadLine();

Factorial(n) K—

n'Factoria!(n,) 3=4-1
factor¡al(4)=4"5=24
4"Factorla!(3)

n,"Factorial (n,) 2=3-1


factorial(3)=3"2=6
3*Factorial(2)

Oj'Factorial (Oj)
1=2-1
2*Factorial(1) factorial(2)=2"l=2

[ FactorÍ3l(l)=l[—
Figura 4.10. Ejemplo de ejecución, cálculo del
Figura 4.9. Ejecución recursiva de la función
factorial de 4.
factorial(n).

Aunque las Figuras son muy representativas y muestran el proceso de ejecución de vma
función recursiva, vamos a desarrollar una tabla de seguimiento suponiendo que el valor de
entrada sea 4(n=4).

Resultado de la función Recursión


4 > 1 Verdad 'factorial(4)=4 * factorial(3)
3 > 1 Verdad •factotial(3)=3 * factorial(2)
2 > 1 Verdad
1 > 1 Falso
'•factorial(2)= 2*l =2
■ factorial(3)=3"^2 =6
■ factorial(4)=4*6 = 24

Si observamos detenidamente el proceso recursivo es una repetición a la inversa de las


mismas operaciones. La función factorial puede ser codificada mediante el uso de un bucle.
static int factorial(int numero){
int fact=l;
for(iiit i=numero;i>l;i—){
fact=fact*i;
}
return fact;
}

Tabla de seguimiento para el caso en que número sea 4:


Línea de código fact Resultado de la función
fact=l 1
for(int i=numero;i>l;i—) 1
fact=fact*i 1*4=4

fact=fact*i 4*3=12 3
i—, 2>1 Verdad 12 2
fact=fact*i 12*2=24 2
i--, 1>1 Falso 24 1

ACTIVIDAD 4.7

Realiza una función recursiva que calcule el Fibonacci de un número especificado, teniendo
en cuenta que:

■ Fibonacci(n)= Fibonacci(n-l)+ Fibonacci(n-2).


■ Fibonacci(0)=l.
■ Fibonacci(l)=l.

4.9. SOBRECARGA DE FUNCIONES


En C# es posible crear en una misma aplicación funciones con el mismo nombre, ya que el
lenguaje las diferenciará en función de los parámetros que contengan. Así, podemos tener dos
funciones que se llamen igual pero tengan diferente número de parámetros o parámetros de
diferentes tipos.
Cuando tenemos una función definida de diferentes formas en un mismo programa, decimos
que se ha sobrecargado la función.
Por ejemplo, supongamos la función suma.
static void suma(){
/*Solicita los números a sumar, realiza la operación y muestra el
resultado por pantalla*/
}
static int suma(int a){
/*Pide un número y suma este con el pasado como parámetro. Devuelve el
resultado*/
}

Tenemos dos representaciones de la íúnción suma, aunque el objetivo es sumar dos números,
cada una hace la operación de diferente forma. El nombre de ambas funciones es el mismo, la
Capítulo 4. Funciones 165

diferencia estriba en los parámetros que reciben. La primera no usa parámetros, mientras que a la
segunda le pasamos un número entero. A la hora de realizar la llamada se diferenciarán
claramente aunque se llamen igual, con lo que no existirá ambigüedad alguna.
Para sobrecargar una función es necesario que cada declaración de esta se diferencie en sus
parámetros:
■ Tengan diferente número de parámetros.
■ Tengan parámetros de diferentes tipos.
■ Tengan la misma cantidad de parámetros pero de diferentes tipos.
■ Tengan diferente número de parámetros de diferentes tipos de datos.
int suma (int a,int. b) { string suma(string a,string b){
return a+b; return a+b;

void suma(){
Vector suma(Vector a) {
int ni, n2, resultj-
Vector resultado;
Console.WriteLine("Introduce los numerous");
resultado.x=x+a.x;
nl=int.Parse(Consolé.ReadLine ());
resultado.y=y+a.y;
n2=int.Parse(Consolé.ReadLine ());
resultado.z=z+a.z;
result=nl+n2;
return resultado;
Consolé.WriteLine("La suma es {0}", result);

Cuando se realice la llamada a la función en el programa principal u otro subprograma, C#


busca:

Si existe una coincidencia exacta entre los tipos de parámetros de la función que se
esta invocando y una de las funciones sobrecargadas. En este caso, ejecuta la función
con la que ha encontrado coincidencia.
Si no existe una coincidencia exacta pero puede producirse conversión de tipos, se
ejecuta la función sobrecargada con el mismo número de parámetros.
string suma(string a,string b){
return a+b;

double suma (double a,dotible b) {


return a+b;

static void Main(string args[]){


int ni,n2,resultado;
string cadl,cad2;
resultado=suma(ni,n2); /*No existe una coincidencia exacta de tipos
pero el tipo int puede convertirse a double,
de tipo menor a mayor, con lo que se ejecuta
la función suma*/

Consolé.WriteLine ("{O)",suma("Hola"," mundo"));


/* En esta ocasión si existe una coincidencia exacta, tenemos un método
suma al que pasamos como parámetros dos cadenas de caracteres.*/
COMPRUEBA TU APRENDIZAJE
1. ¿Qué entendemos por abstracción?
2. ¿Qué es un subprograma? ¿Qué tipos existen?
3. ¿Qué entiendes por framework?
4. ¿Cuál es la estructura básica de una función en C#? Explica brevemente cada parte.
5. ¿Qué diferencia existe entre declaración y llamada a una función?
6. ¿Qué diferencia existe entre paso de parámetros por valor y por referencia?
7. ¿Qué diferencia existe entre el uso de ref y out? ¿Cuándo se utiliza cada uno?
8. ¿Qué función utilizaremos para convertir a mayúsculas una cadena de caracteres en el
framework .NET?

9. ¿Qué entiendes por recursividad? ¿Qué entiendes por caso base y caso recursivo?
10. ¿Qué entiendes por sobrecarga de funciones? ¿Cómo se consigue?

ACTIVIDADES DE AMPLIACION
1. Realiza un programa que muestre por pantalla la fecha del dia siguiente al que se
especifica. Para ello debes usar las siguientes funciones:
A. diasMes(mes). Calcula el número de dias que tiene el mes especificado entre
los paréntesis.
B. diaSiguienteQ. Da solución al problema planteado utilizando diasMes(mes).
2. Utilizando las funciones creadas en la Actividad 1 realiza un programa que calcule la
fecha correspondiente pasados n días.
3. Realiza un programa que dado un número real separe su parte entera de su parte
decimal devolviendo dos números enteros. Usa funciones.
4. Dado un número binario realiza un programa utilizando funciones, que devuelva el
número decimal correspondiente a dicho binario.
5. Realiza la operación inversa a la Actividad 4, de forma que dado un número
expresado en el sistema de numeración decimal se obtenga su binario
correspondiente.
6. Realiza un programa usando funciones de forma que muestre un menú como sigue:
A. Convertir binario-decimal.
B. Convertir decimal-binario.

C. Convertir decimal-octal.

D. Convertir decimal-hexadecimal.

E. Convertir binario-octal.
F. Convertir binario-hexadecimal.

G. Salir.

7. Realiza un programa que simule el lanzamiento de un dado n veces. Se debe


preguntar al usuario cuántas veces debemos realizar el lanzamiento. Además, irá
contando el número de veces que sale el 1, el 2, el 3, etc., tal que finalmente muestre
la probabilidad obtenida para cada número.
A. probabilidad número n=veces que aparece n / tiradas.
8. Realiza un programa que proporcione resultado para rellenar una quiniela de fútbol
(posibles resultados 1, x o 2). Los resultados deben mostrarse por pantalla como si
estuviéramos rellenando la papeleta correspondiente del sorteo.
9. Realiza un pequeño programa que escoja un número al azar y pida al usuario que
adivine este.

10. Desarrolla una función recursiva a la que se le pasará como parámetro un número
entero debiendo mostrar este número al revés. Por ejemplo, si el número es 3265 la
función debe mostrar el número 5623.

11. Realiza la función anterior de forma que devuelva una cadena de caracteres. Una vez
obtenido el número al revés se debe comprobar si el número original es o no
palíndromo.
12. Escribe una función recursiva que calcule la potencia de un número. El formato de la
función debe ser similar a: potenc¡a(base,exponente).
13. Realiza una función recursiva que calcule el producto de dos números mediante
sumas repetidas. producto(a,b).
14. Realiza un programa que calcule el primer número primo a partir de uno dado.
15. Realiza un programa que muestre por pantalla una pirámide de números tal que el
usuario indique el nivel de esta. Por ejemplo, si el usuario especifica nivel 4, debe
mostrarse la pirámide como sigue;

12321

1234321

16. Crea una función que dadas dos fechas indique el número de días transcurrido entre
ambos.

17. Desarrolla una función que dado un número muestre sus divisores.
18. Realiza un programa que pida al usuario un intervalo, es decir, solicite dos números
que establezcan un intervalo, por ejemplo 5 y 8. A continuación debe mostrar una
tabla similar a la que sigue para los números incluidos en el intervalo junto a los
extremos.

Introduce el primer número del intervalo: 5.


Introduce el segundo número del intervalo: 8.
8 I 64 I 512 I 40320
19. Realiza una función que dibuje en la pantalla un hexágono. Usa para ello la función
de la clase Consolé, SetCursorPositionO-
20. Realiza un programa que pueda mostrar en pantalla las figuras geométricas:
cuadrado, rectángulo y triángulo, con los valores de lados y alturas que se desee. Al
mostrar una figura se borrará la pantalla mediante el método ClearQ de la clase
ConsoIeQ. Cuando se pulsa la tecla C se mostrará el cuadrado, cuando pulsemos R
visualizaremos el rectángulo y al pulsar T el triángulo. Utiliza los métodos de captura
de teclas de la clase Consolé.
21. Realiza una función recursiva que dado un número de como máximo cinco cifras
calcule la suma de todas ellas.
22. Realiza una función recursiva que dado un número de como máximo diez cifras
calcule el total de dígitos que contiene.
23. Realiza un programa en el que a través de un menú se de opción al usuario a calcular
el máximo común divisor y mínimo común múltiplo de los números indicados.
Realiza las funciones que creas oportunas.
CAPITULO 5

TIPOS DE DATOS COMPUESTOS

CONTENIDOS OBJETIVOS

Arrays unidimensionales y Conocer los tipos de datos


multidimensionales. Operaciones. compuestos distinguiendo entre
arrays, estructuras y enumeraciones.
Algoritmos de búsqueda y
Entender la necesidad de uso de estos
ordenación.
tipos de datos compuestos.
Arrays irregulares.
Conocer los aspectos principales
Estructuras. de arrays de una o varias
dimensiones.
Enumeraciones.
Saber cómo declarar/crear arrays
de una o varias dimensiones,
estructuras y enumeraciones.

RESUMEN DEL CAPITULO

Para finalizar la programación estructurada estudiaremos los tipos de datos


compuestos. Entre ellos encontramos arrays de una o varias dimensiones, estructuras y
enumeraciones. Realizaremos operaciones comunes con estos tipos y se desarrollarán
algoritmos de búsqueda y ordenación de bastante utilidad.
5.1. INTRODUCCION
Hasta ahora hemos estado realizando pequeños programas en los que cada vez que
necesitábamos almacenar información, hemos usado los tipos de datos simples int, char, double o
float. Supongamos ahora que necesitamos realizar un programa que debe almacenar infonnación
relacionada con una persona, nombre, apellidos, dirección, etc., todos estos datos deben estar de
algún modo agrupados; o bien necesitamos almacenar un número x de valores decimales, ¿cómo
podríamos con los tipos de datos simples mantener esta información? Tendríamos que hacer algo
como lo que se muestra en la siguiente tabla.
string nombrePersonal;
string apellPersonal; double a
stiring apel2Personal; double b
stirng direccionl; double c
string nombrePersonaZ; double d
string apellPersonaZ; double 0
string apel2Persona2; double f
stirng direccionZ; ...

Algo así sería inverosímil en programación. Con las herramientas que hasta ahora conocemos
no podríamos abordar problemas de este tipo.
Existen otros tipos de datos denominados tipos de datos compuestos que están fomiados por
los tipos de datos simples que ya conocemos.
En este capítulo estudiaremos arrays, cadenas de caracteres, estructuras y enumeraciones.

5.2. ARRAYS
Un array es un tipo de datos compuesto que permite almacenar un número x de elementos del
mismo tipo. El uso de array consigue que mediante una sola declaración hagamos referencia a un
conjunto de valores, estos valores pueden ser de los tipos de datos simples estudiados o bien de
algún tipo de datos diseñado por el usuario.

13 17 15 21 27 47

Figura 5.1. Array de números enteros.

Cada elemento del array se referencia mediante su posición en la tabla, empezando a contar
en O, es decir, si quisiéramos acceder al número 2 de la tabla de la Figura 5.1., diríamos que
queremos el valor que se encuentra en la posición 2 del array.

13 17 15 21 27 47

Posición

Figura 5.2. Elementos representativos de un array.


Capítulo 5. Tipos de datos compuestos

Así, al trabajar con arrays debemos diferenciar entre su contenido y sus posiciones. El
contenido del array puede ser de cualquiera de los tipos de datos estudiados mientras que cada
posición será un número entero de O a n, siendo n el número de elementos del array menos 1.
Una de las características a tener en cuenta a la hora de crear un array es su dimensión. La
dimensión indica a groso modo el número de filas que tendrá. A mayor dimensión mayor número
de valores. Entraremos en el estudio de este ténriino más adelante, por lo pronto trabajaremos
con arrays de una única dimensión, una única fila.

5.2.1. DECLARACIÓN Y CREACIÓN DE UN ARRAY EN C#


En C# un array es un tipo de referencia. Cuando trabajamos con estos tipos se mantiene una
referencia a la dirección de memoria donde se almacena, asi como la zona de memoria reservada
para almacenar la información, es decir, cuando creamos una array se almacena la referencia a la
primera posición de este y la cantidad de memoria que precisará. Según esto, cuando precisemos
usar un array:

■ Crearemos el identificador que permitirá acceder a los datos, es la referencia que


apuntará a la primera posición del array. A esto se le conoce como declaración.
■ Reservaremos el espacio en memoria para guardar todos los datos que mantendrá el
array. A esto se le conoce como creación del array.

Figura 5.3. Elementos en la declaración y creación de un array.


La sintaxis para declarar un array es la siguiente:
I tipo_de_datos[]identificador;
Y la sintaxis para la creación del array es:
1 identificador—new tipo_de_datos[Número_de_elementos];
En ocasiones podemos encontrar amíias partes en la misma linea:
; tipo_de_datos[]identificador—new tipo_de_datos[Niimero_de_e¡ementos];
■ tipo_de_datos. Representa el tipo de datos de los elementos del array, por ejemplo.
int, float, String o un Objeto.
■ corchetes,[]. Es el símbolo representativo de un array. Es el que indica al compilador
que la nueva variable será una tabla.
■ identificador. Nombre del array. En realidad es el nombre de la referencia, del punto
donde comienza el array.
■ new. Palabra reservada del lenguaje que reserva espacio en memoria para los
elementos del tipo de datos compuesto.
■ número_de_eIementos. Total de valores que almacenará el array.
Debemos tener en cuenta que:
■ No podremos acceder a las posiciones de un array hasta que este no haya sido creado.
■ Podemos inicializar un array a través de la palabra reservada new o asignando una
referencia a otro array que haya sido inicializado con anterioridad.
int []números; //Declaración de un array llamado números ■
char []letras; //Declaración de un array llamado letras ;
numeros=new int[5]; /*Creación del array números, reservamos espacio en ;
memoria para albergar 5 números enteros.*/ .
letras=new char[20]; //Creación de array letras, contendrá 20 caracteres. j
Los arrays letras y números podrían haberse creado y declarado en la misma línea como se
indica a continuación:

int []numeros=new int[5];


char []letras=new char[20];

5.2.2. ACCESO A LOS DATOS DE UN ARRAY EN C#


Para acceder a los datos almacenados en un array necesitamos indicar el nombre de este, así
como la posición en la que se encuentran estos. Así, tenemos un array llamado numeres y
queremos acceder al valor almacenado en la posición 3 escribiremos: numeros[3].
Usaremos la sintaxis vista tanto para acceder a su contenido y visualizar este como para
proceder a su modificación.

identificador[posición];//Extrae el valor de la posición indicada. \


identificador[posición]=valor;//Asigna un valor a la posición indicada. ;
■ identíficador. Nombre del array o referencia.
■ posición. Número entero que se encuentra en el intervalo de O a n-1, siendo n el
número total de elementos del array.
Es importante que la tabla esté inicializada y correctamente creada antes de ser usada. En caso
de que no se haya incluido ninguna sentencia tipo new o asignación a una referencia existente, el
acceso a la posición provocará un fallo en la ejecución, debido a que se intenta acceder a una
posición de memoria que no se ha reservado.
//Forma incorrecta //Forma correcta

int []num; int []num;


num[4]=3; num=new int[10];
num[4]=3;
Capítulo 5. Tipos de datos compuestos

5.2.3. INICIALIZACION DE UN ARRAY EN C#

La inicialización de un array en C# o la asignación de un valor inicial a cada posición puede


realizarse de varias formas. Al declarar y crear un array se está inicializando este al valor
correspondiente al tipo de datos que almacena. Por ejemplo, al ejecutar la siguiente sentenciaánt
Ilnum=ne>v int|101; estaremos inicializando cada posición a O, valor por defecto del tipo de
datos entero.

stdtic void M«in(5tring[] args)

int[] nuB ■ new int[10];


for (int 1 • 0; i < 10; !++)•<
{
Cen so!e.KriteLine(nuit[i]);
}
Cc-isclc.ReadLineO;
}

Figura 5.4. Inicialización al valor por defecto del tipo de datos.


Además, podemos realizar la inicialización en el momento de la creación del array
estableciendo entre llaves los valores que este debe contener.
int []num=new ¡nt[4]{1,2,3,4];
El número de elementos que incluyamos entre las llaves debe ser igual al valor que
especifiquemos entre los paréntesis. El valor incluido entre los paréntesis se puede omitir de
forma que el número de elementos que finalmente tendrá el tipo compuesto será igual al total de
valores incluidos en las llaves.
int []num=new int[]{1,2,3,4}; /*No es necesario escribir un 4 entre paréntesis ya que el
número finito de elementos escritos entre las llaves proporciona el total de elementos del array*/
Otra opción de inicialización relacionada con estas:
int [] num={l,2,3,4};
Para finalizar, podemos inieializar los elementos de un array mediante un bucle que lo recorra
y o bien asigne valores concretos a cada casilla o solicite al usuario los datos a guardar.
int []nuin=new int[10];
for(int i=0;i<lQ;i++){
Consolé.WriteLine("Dame numero");
num[i]=int.Parse(Consolé.ReadLine());
}
class Prcgraa
{ X
static void Main(5tring[] args) ^
{ int[] nua ■ new int[3];
X
int[] nuffl2 " ncw int[] {Ij2j3};
int[] num3 - { 5>6,7};
for (int i «• 0; i < 3; i++)
í
Console.WriteLineC\t{0}\t{l}\t{2}'',nun[i],num2[i),nu«3[i]);
>
Conscle.ReadLineO;

y Resultados tras recorrer los tres array?.¿ l


Figura 5.5. Diferentes formas de inicialización de un array.

5.2.4. INDICES DE UN ARRAY EN C#

Hasta ahora, cada vez que hemos realizado un acceso a una posición de un array, se ha usado
un literal entero. La posición siempre debe ser un valor entero, ahora bien, podemos usar
variables, como en el interior de los bucles vistos o expresiones.
Imaginemos el array int [] valores = new int [10]. Todas las opciones de acceso que se
muestran a continuación son válidas:

■ valores[n], siendo n un número entero.


■ valores[0].
■ valores[n-l], siendo n un valor entero.
■ valores[n*3+4], siendo n un valor entero.
■ valores[funcion(x)], siempre que funcion(x) devuelva un número entero.
■ valores[num[6]], siendo num[6] un array de números enteros.

5.2.5. ARRAYS DE CARACTERES EN C#


Los arrays de caracteres fueron la primera aproximación de C y C-H- a las cadenas de
caracteres ya que estos lenguajes no soportan el tipo string.
Un array de caracteres se declara y crea como hemos estudiado hasta ahora, la diferencia
radica en su visualización, ya que para obtener la cadena completa que almacena solo es
necesario usar su identificador.
I — — i

i int []num=new int[]{1,2,3}; i


: char []letras=new char[]{'h','o','1','a'}; :
i Consolé.WriteLine(letras); ;
t

A la hora de visualizar el contenido de letras no es necesario realizar un bucle ya que de


forma automática veremos la palabra hola en pantalla.
Capítulo 5. Tipos de datos compuestos

5.2.6. ARRAYS COMO PARAMETROS EN 0#

Un array puede ser pasado como parámetro en cualquier función teniendo en cuenta que este
siempre es pasado por referencia ya que como se ha explicado con anterioridad es un tipo de
datos referencia. Según esto, todas las modificaciones que realizamos sobre el objeto en el
interior de la función se verán reflejadas en él, una vez acabe esta.
Debemos tener claro que no es lo mismo pasar un array como parámetro que un elemento del
array ya que en el primero de los casos se está realizando un paso por referencia y en la segunda
ocasión por valor. Veamos un pequeño ejemplo:
static void modifica(int []n){
n[l]=9;
)
static void modifica(int a) {
a=9;
)
static void Main(string [] args){
int [] numeros=new int[]{1,2,3};

//Llamamos a la función modifica(int a);


modifica(números[1]);
for(int i=0;i<3;i++){
Consolé.WriteLine ("{O}\t",números[i]);
)

//Invocación de la función modifica(int []n);


modifica(números);
for(int i=0;i<3;i++){
Consolé.WriteLine ("{O)\t",números[i]);
}
Consolé.ReadLine();

En el ejemplo hemos sobrecargado la fiincióñ modificar de fonna que en la primera instancia


de esta pasamos como parámetro un array completo, mientras que en la segunda un único valor.
A la hora de usar ambas:

modifica(}mmeros[l]);
Pasa como parámetro el valor almacenado en la posición 1 del array números, de forma que
aunque se indique en el código interior de la función la modificación a 9 de este valor no se verá
alterado. En cambio en la línea:
modifica(nwneros);
Estamos pasando como parámetro el array completo, una referencia, de forma que la
modificación indicada se reflejará una vez acabe la función.
La salida para este pequeño trozo de código será algo así como:
Si la función void modifica(int a) hubiera sido declarada de la siguiente forma void
modifica(ref int a) estaríamos pasando el dato por referencia y sí se verían reflejadas las
modificación en el array una vez acaba la ejecución de la función.

5.2.7. EJEMPLO DE USO DE ARRAYS EN C#

Una vez hemos estudiado los conceptos básicos relacionados con este tipo compuesto de
datos veamos un pequeño ejemplo. En esta actividad crearemos una pequeña aplicación que
mantendrá los precios de los productos de una tienda del barrio. El programa debe mostrar un
menú en el que se visualicen las opciones:
1. Nuevo producto.
2. Precio medio.

3. Listar productos.
4. Salir.

Además, tendremos dos arrays, uno de tipo string que almacenará los nombres de los
productos y otro de tipo real que guardará los precios. Para relacionar cada producto con un
precio, tendremos en cuenta que el nombre que ocupa la posición cero del primer array tiene
como precio el que aparece en la posición cero del segundo array; el nombre de la posición uno
del primer array corresponde con el precio de la posición uno del segundo array y así
sucesivamente.

Las variables de tipo array serán:


— —— — 1

I string []noinbres=new string[100]; !


I double []precios=new double[100]; i
Como máximo nuestra tienda almacenará 100 productos. En lugar de usar este literal entero,
crearemos una constante de forma que si más adelante queremos modificar este valor no
tengamos que ir línea a línea cambiándolo. Finalmente tendremos:
const int MAX_ELEMENTOS=100;
string []nombres=new string[MAX ELEMENTOS];
double []precios=new double[MAX~ELEMENTOS];
Ya que en el Capítulo 4 estudiamos el diseño descendente hagamos uso de él. Crearemos una
función para mostrar y escoger una opción del menú asi como una función para cada orden que
se debe realizar. Las funciones que desarrollaremos serán:
/*Función menú
Muestra las opciones del menú de nuestra aplicación y permite escoger una de
ellas.
Parámetros de entrada: ninguno.
Salida: valor de la opción escogida.*/

static int menú(){


Capítulos. Tipos de datos compuestos 177

/*Función insertarProd
Inserta un nuevo producto si no esta la tienda llena. Pedirá al usuario nuevo
nombre y precio y insertará esta información al final de las tablas nombres y
precios
Parámetros de entrada: tabla nombres y precios, número total de productos
almacenados.
Salida: ninguna.*/

static void insertarProd(string[]nom,double []p, ref int totalProductos){

/*Función promedio
Realiza el promedio o media aritmética de todos los precios de los productos
de la tienda.
Parámetros de entrada: tabla precios y el número de productos almacenados.
Salida: promedio calculado.*/

static double promedio(double []p, int totalProductos){

/*Funci6n listarProductos
Muestra los productos de la tienda de forma que se listan nombres junto a
precios de la forma:
Nombre producto Precio

a 12
b 1
Parámetros de entrada: tabla nombres y precios, número de productos
almacenados.
Salida: ninguna.*/

static void listarProductos(string []nom,double []p, int totalProductos){

Función principal Main. La constante MÁX^ELÉ^^ se crea fuera de cualquier


método, entre las llaves de la clase Program para que sea accesible por todos. En la función
Main crearemos los arrays de nombres de productos y precios e inicializaremos la variable
numElementos a cero, siendo esta la que controle el total de elementos almacenados hasta un
momento dado en nuestras tablas.

Básicamente el programa se limita a usar un bucle que finalizará cuando el usuario indique
qué opción es igual a 4. Cada vuelta de la sentencia repetitiva se borrará la pantalla
(ConsoIe.CIearO), visualizaremos el menú, se dará opción al usuario a escoger la instrucción a
ejecutar y se evaluará el switch desde el que el software nos redirigirá a la función que
corresponda según el caso determinado.
— — — — -1

const int MAX_ELEMENTOS =100; ;


static void Main(string[] args) I
{ i
int opcion=0; !
string [] productos=new string[MAX_ELEMENTOS]; ;
double [] precios=new double[MAX_ELEMENTOS]; 1
int numElementos=0; ~ 1

Consolé.Olear();
opcion = menú O;
switch (opcion)

case 1: insertarProd(productos,precios,ref numElementos);


break;
case 2: Consolé.WriteLine("El precio promedio de todos los
productos almacenados es:{0}",
promedio(precios, numElementos));
break;
case 3: listarProductos(productos, precios, numElementos);
break;

default: Consolé.WriteLine("La opción escogida no es


válida, vuelva a intentarlo");
break;

Consolé.ReadLine();
) while (opcion != 4);

Función menú. Ya hemos cónstmidos^^^^ parecidas. Cuando tengamos que desarrollar


menús en aplicaciones solo tendremos que usar el método WriteLine de Consolé para mostrar
cada opción y mantener una variable que capture el valor escogido por el usuario.
- — — t
static int menú() ;

int op;
Consolé.WriteLine("1. Nuevo producto.");
Consolé.WriteLine("2. Precio medio.");
Consolé.WriteLine("3. Listar productos.");
Consolé.WriteLine("4. Salir.");
Consolé.WriteLine("Escoge opción:");
op = int.Parse(Consolé.ReadLineO);
return op;

Función insertarProdVPasárémos cóiñó esta función la tabla nom (almacena los


nombres de los productos), la tabla p (guarda los precios de los productos) y el total de productos
que ya están almacenados (totalProductos). Los arrays nom y p pasan por referencia al igual que
la variable totalProductos a la que se le añade el modificador ref. Es necesario que
totalProductos sea pasado por referencia debido a que al insertar un nuevo elemento en ambas
tablas se incrementa el total de productos que hay en ambas.
static void insertarProd(stringt] nom, double[] p, ref int totalProductos)
if (totalProductos < MAX ELEMENTOS)
{
Consolé.WriteLine("Introduce ®1 nombre del nuevo producto");
nom[totalProductos] = Consolé.ReadLine();
Consolé.WriteLine("Introduce el precio del nuevo producto");
p[totalProductos] = double.Parse(Consolé.ReadLine());
totalProductos++;

Consolé.WriteLine("No se pueden insertar más productos");


Capítulos. Tipos de datos compuestos 179

Función promedio. Devolverá la media aritmética de todos los productos. Sumará todos los
precios y dividirá el resultado entre el total de productos almacenados. Para poder realizar la
operación será necesario que se pase como parámetro el array con los precios de los productos
además del total de elementos que contiene. El número total de elementos no hace falta que sea
pasado por referencia ya que no va a ser modificado en el interior de la función.
static double promedio (dotible[] p, int totalProductos) i
{ i
double media =0.0; ;
if (totalProductos > 0) i
( i
for (int i = 0; i < totalProductos; i++) ;
{ I
media = media + p[i];
} i
media = media / totalProductos; ¡
) :
return media; ¡
} ;
Función listarProductos. Muestra un listado de todos los productos de la tienda, visualizando
en primer lugar el nombre y a continuación su precio. Para poder realizar la operación debemos
pasar como parámetro a la función el amay de nombre, la tabla de precios y el total de productos
almacenados hasta el momento. Si no se han insetado productos se mostrará el mensaje "No se
ha almacenado aún ningún producto".
static void listarProductos(string[] nom, double[] p, int totalProductos) I
{ i
if (totalProductos > 0) ;
{ i
Consolé.WriteLine("\tPR0DUCT0S\t\t\tPRECI0"); i
Consolé.WriteLine(" "); ;
for (int i = 0; i < totalProductos; i++) ;
{ i
Consolé.WriteLine("\t{O}\t\t\t{1}",nom[i],p[i]); <

Consolé.WriteLine("No se ha almacenado aún ningún producto");

ACTIVIDAD 5.1

Crea un programa que almacene las notas de una asignatura de todos los alumnos de la clase.

ACTIVIDAD 5.2

Continúa la actividad anterior de forma que se muestre por pantalla cuál es la mayor y menor
nota.
ACTIVIDAD 5.3

Finaliza la Actividad 5.2 de forma que crees una función que obtenga la nota media de los
alumnos para la asignatura dada.

5.2.8. BUCLE FOREACH PARA LA MANIPULACIÓN DE ARRAYS


EN C#
Como hemos visto en otros apartados utilizamos bucles for para la manipulación de tablas.
Gracias a un bucle for podemos inicializar los datos del array, acceder a todos ellos y realizar
operaciones como búsquedas y eliminación que aún no hemos estudiado.
Cuando estudiábamos las sentencias repetitivas, veíamos los bucles for, vvhlle y do...while.
En C# encontramos otro tipo de sentencia de control repetitiva conocida como bucle foreach. Es
muy utilizada sobre todo cuando usamos tablas de objetos.
La sintaxis de un bucle foreach es la siguiente:

foreach (tipo_de_dato nombre_elemento in array){ ;


Sentencias; :
} i
Veamos el siguiente ejemplo:
int []nuineros=new int[]{1,2,3,4,5,6}; ■
foreach (int n in números){ I
Consolé.WriteLine("El número es:{0}",n); ;
} i
El bucle foreach recorrerá todo el array hasta llegar al último dato, de forma que en cada
vuelta asignará a una variable el valor de la posición que se esté analizando en ese momento.
Para entender el concepto hagamos la tabla de seguimiento del ejemplo.
Línea de código numeres Pantalla

int []numeros=new...

foreach... numeros[0] El número es: 1

foreach... numerous[l] El número es: 2


2 3 4 5

foreach... IIBBBBBB numerous[2] El número es: 3


0 1 2 3 4 5

foreach... numerous[3] El número es: 4

foreach... numerous[4] El número es: 5

foreach... numerous[5] El número es: 6


Capítulos. Tipos de datos compuestos 181

La función promedio que utilizábamos en el apartado anterior podría modificarse utilizando


un foreach del siguiente modo:
//Uso de bucle for I
static doiible promedio(double[] p, int totalProductos) ;
{ I
double media =0.0; I
if (totalProductos > 0) i
{ i
for (int i = 0; i < totalProductos; i++) i
{ i
media = media + p[i]; ■
) :
media = media / totalProductos; ;
} !
return media; !
} i
— — i

//Uso de bucle foreach |


static double promedio(double[] p, int totalProductos) 1
{ !
double media =0.0; ;
if (totalProductos > 0)
{ i
foreach(double valor in p){ ¡
media=media+valor; I
} i
media=media/totalProductos; j
) ;
return media; ;
) I
Resumiendo:

■ Un bucle foreach se usa para recorrer todos los elementos del array.
■ Se ejecutarán las instrucciones para todos los elementos del array.
■ En la cabecera foreach (tipo variable in array), variable es un elemento de solo
lectura. Se encargará de guardar el valor de la posición del array que está siendo
analizada. Este valor puede ser interpretado y podremos realizar operaciones con él,
pero sin embargo no podremos modificar la posición del array a la que referencia.
■ El array que queremos recorrer con el foreach debe estar deelarado y creado antes de
ser recorrido.

NOTA: Podemos saber el número de elementos almacenados en un array utilizando la


propiedad Lengtb. Al ser una propiedad se usa sin paréntesis, precios.Lengtb;

ACTIVIDAD 5.4

Crea un array de tipo de dato string. Inicializa este a las cadenas de caraeteres que desees. A
continuación, y haciendo uso del bucle foreach, muestra por pantalla cada cadena con el número
de caracteres que contiene. La función que devuelve el número de caracteres de un tipo string es
Lengtb.
ACTIVIDAD 5.5

Crea un array de números enteros. Inicializa cada posición al valor que desees. Muestra por
pantalla para cada elemento del array si este es par o impar. Utiliza el bucle foreach.

5.2.9. OPERACIONES COMUNES CON ARRAYS EN C#

Cuando utilicemos arrays, además de la inicialización o inserción posterior de elementos


puede interesamos la eliminación, modificación o búsqueda de algún dato. Veamos a partir del
ejemplo del Apartado 5.2.7., las funciones de eliminación, búsqueda y modificación de un array.
Partimos de un programa que muestra el siguiente menú:
1. Nuevo producto.
2. Precio medio.

3. Listar productos.
4. Salir.

Modificaremos este de forma que se vea:


1. Nuevo producto.
2. Precio medio.
3. Listar productos.
4. Modificar producto.
5. Eliminar producto.
6. Buscar producto.
7. Salir.

Añadiremos las siguientes funciones:


— -i

/*Función modificarNombre
Se encarga de cambiar el nombre de un producto por otro solicitado en la misma
función.
Parámetros de entrada: Elemento del array de productos que se desea modificar.
Salida: ninguna.*/

static void modificarNombre(ref string nom){


}
/*Función modificarPrecio
Modificará un precio de la tabla precios. En la función principal buscaremos
el producto a modificar y extraeremos la posición.
Parámetros de entrada: Elemento de la tabla precios que se desea modificar.
Salida: Ninguna.
*/
static void modificarPrecio(ref double p){

/*Función eliminarProducto
Eliminará el producto y precio almacenados en las tablas nom y p en la
posición posEliminar. El numElementos, o lo quo___fs___lo__^m_ismq,^ el total_ _de_
Capítulo 5. Tipos de datos compuestos

elementos de la tabla debe decrementar.


Parámetros de entrada: Tablas productos y precios, el total de productos
almacenados y posición del producto que se quiere borrar.
Salida: ninguna.*/

static void eliminarProducto(string[] nom, double[] p, ref int numElementos,


int posEliminar) {

/*Funci6n buscarNombre
Buscará el producto en la table de productos, cuyo nombre coincide con el
nombre pasado como parámetro. Si el producto se encuentra en la tabla devuelve
su posición, en caso contrario -1.
Parámetros de entrada: Tabla de productos, total de elementos de la tabla y
nombre del producto buscado.
Salida: Posición del producto buscado o -1.*/

static int buscarNombre (string []nom, int numElementos,string nombreBuscado) {

Funciones modificarNombre y modifícarPrecio. Estos dos subprogramas realizan


básicamente la misma secuencia de operaciones. Reciben como parámetro la referencia al
elemento de la posición que se desea modificar y en su interior se solicitará un nuevo nombre o
nuevo precio.

static void modificarNombre(ref string nom)


{
Consolé.WriteLine("Introduce el nuevo nombre:");
/*Modifleamos nom, parámetro pasado por referencia. En la función
principal veremos que se pasará una referencia a una celda concreta de
nuestra tabla.*/
nom = Consolé.ReadLineO ;
}

static void modificarPrecio(ref double p)


{
Consolé.WriteLine("Introduce el nuevo precio:");
/*A1 igual que en la función modificarNombre en esta linea modificamos
el parámetro p pasado por referencia que en la función principal
corresponderá con una celda concreta de la tabla precios.*/
p = double.Parse(Consolé.ReadLineO);

Función eliminarProducto. Elimina un producto de la tabla. La eliminación consiste en


sobreescribir el producto, en ningún momento modificamos la estructura de nuestros tipos de
datos, seguimos manteniendo dos arrays de MAX_ELEMENTOS. Cuando se conoce la posición
del producto a eliminar sobrescribiremos esta, tanto en la tabla productos como precios y
decrementaremos en uno el número de elementos total de la tabla.
184 Programación

E emento a e ¡minar

Total de elementos = Total de elemento^!

I Limite del array


Solo leeremos hasta aquí ya que
el tota de elementos es 4 pero la
última posición sigue rellena

Figura 5.6. Proceso de eliminación de datos en un array.

static void eliminarProducto(string[] nom, dotible[] p, ref int numElementos, ;


int posEliminar) '
{ ;
/*Recorreinos el array desde la posición a eliminar hasta el final ya que ;
todos los elementos deben ser desplazados una posición a la izquierda.*/ :
for (int i = posEliminar; i < numElementos; i++) i
{ i
nom[i] = nom[i +1]; I
p[i] = p[i + 1]; ;
} ;
numElementos—; I
i i
Debemos incluir como parámetro de la ñmción pasado por referencia el número de elementos
del array ya que se usa para limitar el proceso de sobreescritura y posteriormente será
decrementado ya que tendremos un elemento menos en las tablas productos y precios.
Ya que sobrescribimos una posición por la siguiente adyacente a ella vemos las líneas:
nom[i]=nom[i+l] y p[i]=p[i+l].
Función buscarNombre. Esta función será usada en muchas ocasiones durante el código
principal, ya que antes de modificar o eliminar un elemento este se debe buscar en la tabla
adecuada.

A la hora de buscar debemos recorrer el array hasta que el elemento buscado se encuentre o
lleguemos al fmal de este. En nuestro ejemplo buscaremos un producto concreto, si se localiza la
función buscarNombre devolverá la posición de este, en caso contrario se devolverá el número
entero -1. Este valor es muy usado en la funciones de búsqueda de objetos como identificativo de
dato no encontrado.
Existen diferentes mecanismos de búsqueda: búsqueda secuencial o lineal y búsqueda
binaria. Veremos estos a continuación tras finalizar de analizar el código fuente de nuestro
programa ejemplo. Básicamente la primera de las búsquedas es la que hemos aplicado en la
función buscarNombre, ya que el proceso de búsqueda consiste en recorrer el array de fonna
secuencial hasta encontrar una coincidencia exacta con el dato buscado.
Capítulos. Tipos de datos compuestos 185

static int buscarNombre(string []nom, int numElementos,string nombreBuscado)

int buscado = -1;


int 1=0;
while (buscado == -1 && i<numElementos)

/*Usamos la función CompareTo para comparar cadenas de


caracteres. La función devuelve O en caso de que ambas cadenas
coincidan. Si no son iguales devolverá un valor positivo o
negativo en función de si la cadena con la que comparamos esta
alfabéticamente después o antes de la variable string que hace la
llamada a CompareTo().*/
if(nom[i].CompareTo(nombreBuscado)==0)

buscado = i;

return buscado;

La nueva función principal se ve modificada tal y como se muestra a continuación:


const int MAX_ELEMENTOS = 100;
static void Main(string[] args)
1
int opcion=0, subopcion;
string nomAux;
int pos;
string [] productos=new string[MAX_ELEMENTOS];
double [] precios=new double[MAX_ELEMENTOS];
int numElementos=0;
do
{
Consolé.Clear();
opcion = menú O ;
switch (opcion)
{
case 1: insertarProd(productos, precios,
ref numElementos);
break;
case 2: Consolé.WriteLine("El precio promedio de todos los
productos almacenados es:{0}"
,promedio(precios, numElementos));
break;
case 3: listarProductos(productos, precios, numElementos);
break;
case 4:Consolé.WriteLine("Qué producto desea modificar:");
nomAux = Consolé.ReadLine();
pos = buscarNombre(productos, numElementos, nomAux);
if (pos < 0)
{
Consolé.WriteLine("Producto no encontrado");

/*A la hora de modificar un producto damos opción al


usuario a que indiqué que se debe modificar, sólo el
nombre, el precio o ambas cosas.*/
("¿Qué desea modifi_car_?
(l-noiTibre/2-precio/3-ambos");
subopcion = int.Parse(Consolé.ReadLineO);

switch (subopcion)
{
case 1: modificarNombre(ref productos[pos]);
break;
case 2: modificarPrecio(ref precios[pos]);
break;
case 3: modificarNombre(ref productos[pos]);
modificarPrecio(ref precios[pos]);
break;

break;
case 5:
if (numElementos > 0)
{
Consolé.WriteLine("Indica el nombre del producto
a eliminar");
nomAux = Consolé.ReadLine();
pos = buscarNombre(productos,numElementos
,nomAux);
if (pos >= 0)
í
eliminarProducto(productos, precios,
ref numElementos, pos);
}
else

^ Consolé.WriteLine("El producto que se


desea eliminar no existe");
}

Consolé.WriteLine("No hay productos almacenados


en el almacén");
}
break;
case 6: Consolé.WriteLine("Indica el nombre del producto
a buscar");
nomAux = Consolé.ReadLine();
pos = buscarNombre(productos,numElementos, nomAux);
if (pos >= 0)
{
Consolé.WriteLine("Producto encontrado");
Consolé.WriteLine("[O} - [i)' Productos[pos]
, precios[pos]);

break;
default: Consolé.WriteLine("La opción escogida no
es válida, vuelva a intentarlo");
break;

Consolé.ReadLine();
} while (opcion != 7);
Capítulos. Tipos de datos compuestos 187

En los casos 4 y 5 relativos a la modificación y eliminación hacemos uso de la fimción


buscarNombre ya que antes de modificar o eliminar debe conocerse la posición de la tabla sobre
la que se debe realizar la acción, siempre que esta exista.

5.2.9.1. ALGORITMOS DE BÚSQUEDA


Existen dos tipos de algoritmos de búsqueda que podrán ser implementados posteriormente en
el lenguaje C# u otro tipo de lenguaje de programación. Estos se denominan algoritmo de
búsqueda secuencial o lineal y algoritmo de búsqueda binaria.
En los algoritmos de búqueda tenemos un elemento denominado clave que no es más que el
elemento buscado.

1. Búsqueda secuencial. En la búsqueda secuencial o también denominada en


ocasiones lineal se procede a buscar un elemento desde im punto a otro de forma
secuencial, es decir, se exploran de forma consecutiva los elementos del array hasta
localizar la clave o llegar al último elemento.
^ I Clave = 2 j

Elemento encontrado

Figura 5.7. Proceso de búsqueda secuencial.

Búsqueda binaria. La búsqueda secuencial puede aplicarse sobre cualquier tabla,


sin embargo, si esta está ordenada podemos usar la búsqueda binaria, consiguiendo
algoritmos más óptimos. En la búsqueda binaria buscamos el elemento central de la
lista, si coincide con el valor cable acaba la búsqueda, en caso contrario si el valor
buscado es superior, la búsqueda continuará de la mitad hacia el último elemento del
array; si el elemento es menor la búsqueda continúa desde la mitad al primer
elemento y así de forma sucesiva hasta que no tengamos más elementos para
comparar o hayamos encontrado la clave.

32=32
VALOR ENCONTRADO

Figura 5.8. Búsqueda binaria.


Algoritmo búsqueda secuenclal
Función BusquedaSecuencíal(entero[]tabla,entero totalElementos,entero clave) retoma entero
Entorno:
entero pos, índice;
Inicio:
pos= -I;
indice=0;
Mientras índice<totalElementos Ypos== -1 hacer
Si tabla[indice]=clave entonces
pos=índice;
FinSi;
■S*.. : indice++;
FinMientras;
|: ^ devuelve pos;
' FinBusquedaSecuencial;

Subprograma Principal Q
Entorno:
j entero []tabla={.,.} //Tabla con todos los valores
^ . entero totalElementos—size; //Total de elementos almacenados en el array.
^ entero clave—valor; /*Suponemos un array de enteros aunque puede ser de
cualquier tipo, sólo debemos tener precaución a la hora de
realizar la comparación ya que todos los tipos no tienen
y''- sobrecargado el operador ==*/
entero posEncontrado;

posEncontrado—BusquedaSecuencial(tabla, totalElementas, clave),

Fundón búsqueda secuendal en C#


static int BusquedaSecúenTiTl(TnT'ntabla7int totalElementos, int clave) {
int pos, índice;
pos= -1;
indice=0;
while (indice<totalEleinentos && pos== -1) í
(tabla[índice] =clave) {
pos=índíce;
}
índíce++;

return pos;
Capítulos. Tipos de datos compuestos 189

Algoritmo búsqueda binaria


Función BusquedaBínaria(entero []tabla,entero totalElementas,entero clave) retorna entero
Entorno:
entero posCentro,posInicio, posFin;
entero valorCentral;
Inicio:
poslnicio=0;
posFin=totalElementos-l;
MientrasposInicio<=posFin hacer
//Posición central de la tabla
posCentro=(posinicio+posFin)/2;

//Valor que esta en el centro del array


valorCentral=tabla[posCentro];

//Si el valor central coincide con la clavefinaliza lafunción


Si clave=valorCentral entonces
return posCenti'o;
Sino
//Si la clave es menor que el elemento del centro
//Seguimos buscando desde elprincipio hasta la mitad de la tabla
Si elave<valorCentral entonces
posFin=posCentral-l;
Sino
//Si la clave es mayor que el elemento central
//Seguimos buscando desde la mitad hasta elfinal
posInicio=posCentral+l;
FinSi;
FinSi;
FinMientras;
devuelve -I;
FinBusquedaSecuencial;

Subprograma Principal Q
Entorno:
entero []tabla={...} //Tabla con todos los valores
entero totalElementos—size;//Total de elementos almacenados en el array.
entero clave=valor; /*Suponemos un array de enteros aunque puede ser de
cualquier tipo, solo debemos tener precaución a la hora
realizar la comparación ya que todos los tipos no tienen
sobrecargado el operador ==*/
entero posEncontrado;
Inicio:
posEncontrado —BusquedaBinaria(tabla,totalElementos,clave);

FinPrincipal;
Fundón búsqueda binaria en C#
static int BusquedaBinaria(int []tabla,int totalElementos,int clave) {
int posCentro,posinicio, posFin;
int valorCentral;
poslnicio=0;
posFin=totalElementos-l;
while(posInicio<=posFin){
posCentro=(posInicio+posFin)/2;
valorCentral=tabla[posCentro];
if(clave==valorCentral){
return posCentro;
}
else if (clave<valorCentral){
posFin=posCentro-l;

else{
posInicio=posCentro+l;

return

Tabla de seguimiento de la búsqueda binaria para la tabla:

Clave=54.

'■'■•i ' Línea de código osCentro valorCentral osinicio devuelve


poslnicio=0; O
)osFm=totalElementos-l; O 10-1=9
while(posInicio<=posFin) Verdad O 9
)osCentro=(posInicio+posFin)/2; o 9
valorCentral=tabla[posCentro o 9
if(clave=valorCentral) Falso o 9
else if (clave<valorCentral) Falso o 9
else{posInicio=posCentro+l; 5 9
while(posInicio<=posFin) Verdad 5 9
)osCentro=(posInicio+posFin)/2; 5 9
valorCentral=tablarposCentro 5 9
if(clave=valorCentral) Falso 5 9
else if (clave<valorCentral) Falso 5 9
else{posInicio=posCentro+l; 9
while(posInicio<=posFm) Verdad 9
)OsCen1xo=(posInicio+posFin)/2; 9
valorCentral=tabla[posCentro 9
if(clave=valorCentral) Verdad 9
return posCentro; 9

ACTIVIDAD 5.6

Realiza un programa que utilice como tipo de datos principal un array de caracteres ordenado.
Usa la función de búsqueda binaria tal que se busque el carácter solicitado por el usuario en el
Capítulo 5. Tipos de datos compuestos

array. Si existe dicho carácter debe mostrar en pantalla la posición en la que se encuentra, en
caso contrario el mensaje "El carácter buscado no es un elemento de la tabla".

ACTIVIDAD 5.7

Vamos a comparar la eficacia de los algoritmos de búsqueda vistos. Realizar un pequeño


programa que tenga como tipo de datos principal una tabla de n números enteros ordenados. La
aplicación debe mostrar un pequeño menú con las siguientes opciones:
1. Búsqueda.
2. Comparar.
3. Salir.

Cuando pulsemos la opción 1 se realizará una búsqueda binaria y secuencia! del mismo valor,
solicitado este al usuario. Se deben registrar las iteraciones que se producen en cada bucle de
cada algoritmo, de forma que además del array principal tendremos un array entero para
almacenar las vueltas de bucle de la búsqueda secuencial y otro para el registro de vueltas de la
búsqueda binaria.
Al pulsar la opción 2 se mostrarán por pantalla los arrays relacionados con las vueltas de
bucles dadas del modo que se indica:
Valores búsqueda secuencia:
BS-0: 10 I BS-1:6 |BS-2: 8 |...
Valores búsqueda binaria:
BB-0; 3 I BB-1: 1 |BB-2:6 |...
ACTIVIDAD 5.8

Realiza una pequeña aplicación que mantenga los datos relacionados con mediciones de
experimentos en un laboratorio. Cada experimento tiene asociada una cantidad real
representativa y el nombre de la persona que lo realizó. Como máximo en un mes se realizan 60
experimentos, pasado este tiempo todos se guardan en una tabla historial (que ya se ha
programado) y se resetea nuestra infonnación, es decir, los tipos de datos donde se almacenan
nombres de experimentos, de personas que lo realizan y cantidades se vacían (todos los
experimentos pasan a llamarse exOO, las cantidades 0.0 y los nombre "vacio").
El programa debe pennitir escoger entre las siguientes opciones al usuario:
1. Almacenar nuevo experimento.
2. Eliiuinar experimento fallido.
3. Modificar experimento.
4. Contabilidad de experimentos exitosos y fallidos.
5. Listado de experimentos agrupados por persona que lo realiza.
6. Salir.

Un experimento se considerará exitoso si su cantidad asociada es mayor de 7.5.


192 Programación

5.2.9.2. ALGORITMOS DE ORDENACION

Los algoritmos de ordenación permiten ordenar un array en función de un criterio establecido


en el propio algoritmo. Encontramos diferentes tipos de algoritmos de ordenación: selección,
inserción, burbuja, shell y quicksort.
ALGORITMO DE ORDENACIÓN POR SELECCIÓN
El procedimiento para la ordenación en este algoritmo es el que sigue:
1. Buscamos el elemento más pequeño de la tabla y lo colocamos en la primera
posición, 0. Para ello intercambiamos el valor de la posición con el de la posición
donde encontramos el valor menor. Ya tenemos el elemento menor al principio de la
tabla.

2. A partir de la posición 1, buscamos el nuevo valor más pequeño. Cuando lo


localicemos, lo ubicamos en la posición 1 de la tabla intercambiado las casillas como
en el paso 1. El siguiente valor más pequeño ya está ubicado donde le eorresponde.
3. Realizar el proceso repetidamente desde la siguiente posición no ordenada hasta el
final, hasta que hayamos recorrido todo el array.
Leyenda:
H Valor más pequeño.
Intercambio.
a - ■ ; Parte ordenada.
Zona donde buscar valor más pequeño.

Tabla original:
12 5 3 13 2 9 7
Valor más pequeño: 2,intercambiar con la posición 0.
5 3 13 1 2 1 9 7
5 3 13 9 7
5 3 13 flT l 9 7

Valor más pequeño: 3,intercambiar con la posición 1.


2 5 3J 13 12 9 7

■ 13 12 9 7
13 12 9 7

Valor más pequeño: 5, intercambiar con la posición 2.


13 12 9 7
■ 2 3 13 12 9 7
2 3 13 12 9 7

Valor más pequeño: 7,intercambiar con la posición 3.


'^5^1 13 12 9 7

3 5 7 12 9 13
Valor más pequeño: 9, intercambiar con la posición 4.
2 3 5 7 13
2 3 5 7 13
9 I 12 13
Capítulos. Tipos de datos compuestos 193

Valor más pequeño: 12, intercambiar con la posición 5.


3 5 1 7 1 9" I 12 I 13 "
2 3 5 7 9 "MÍ 13~
2 I 3 I 5 I 7 I 9 I I2I 13
Finalmente se ha ordenado el array. La ordenación es ascendente, puede programarse para
que sea descendente.
El algoritmo de este tipo de búsqueda se muestra a continuación.
Subprograma Selección (entero []tabla, entero NumeroElementos) i
Entorno: ;
entero indiceMenor, i, j, aux; ;
Inicio: I
/*Recorre todo el array buscando el valor más pequeño desde la primera ;
posición desordenada*/ ;
Para i=0 a NumeroElementos-2 incremento 1 hacer •
indiceMenor = i; I
/*Busca la posición del elemento más pequeño*/ j
Para j=i+l a NumeroElementos-1 incremento 1 hacer !
Si tabla[j] < tabla[indiceMenor] entonces ;
indiceMenor = j; i
FinSi; I
FinPara; !
/* Realiza el intercambio del elemento más pequeño */ ;
Si i != indiceMenor entonces •
aux = tabla[i]; i
tabla[i] = tabla[indiceMenor]; |
tabla[indiceMenor] = aux ; \
FinSi; ;
FinPara; \
FinSelección; !

static void Selección(int []tabla, int NumeroElementos)


{
int indiceMenor, i, j, aux;
for(int i=0;i<NumeroElementos-l;i++){
indiceMenor = i;
for(int j=i+l;j<NumeroElementos;j++){
if(tabla[j] < tabla[indiceMenor]){
indiceMenor = j;
}
}
if(i != indiceMenor)]
aux = tabla[i];
tabla[i] = tabla[indiceMenor];
tabla[indiceMenor] = aux ;

ALGORITMO DE ORDENACION POR INSÉRCCIÓN


El algoritmo de ordenación por inserción consiste en generar una tabla a partir de la que ya se
tiene, tal que cada nuevo elemento insertado se coloca en su posición correcta.
Imaginemos que tenemos la tabla usada en la ordenación por selección:
12 I 5 I 3 I 13 I 2 I 9 I 7
Generamos una nueva tabla con el primer elemento de la tabla a ordenar, tabla[0], 12.

El siguiente valor es tabla[l], 5. ¿Es mayor o menor que 12 que ya esta insertado? Menor, así
debe ser colocado antes.

La nueva tabla se genera ordenada. El siguiente valor a insertar es 3, tabla[2]. ¿Dónde debe
colocarse? Justo antes de 5, tabla[0]. Realizamos justo en esa posición la inserción.
3 I 5 I 12
Repetidamente realizamos la misma operación;
3 I 5 I 12 I iTI
2 3 5 12 13
2 3 5 9 12 13
2 3 5 7 9 12 13
A modo formal, el algoritmo sigue el siguiente procedimiento:
1. La lista inicial estará formada por un único elemento ordenado, tabla[0].
2. Insertamos el elemento tabla[l] en la nueva lista, antes o después del valor insertado
en la posición anterior según sea este menor o mayor.
3. Para cada elemento de la tabla a ordenar se buscará en la nueva lista su posición
correcta y se desplazarán a la derecha todos los valores para dejar la casilla libre al
nuevo dato.

4. Se insertará el nuevo dato en la posición correcta.


Tabla original:
12 I 5 3 13 I 2 1 9 I 7
Nueva Tabla ordenada:

12
Siguiente dato a insertar: 5
5< 12

Siguiente dato a insertar: 3


Posición correcta: O, dejamos vacía esta para el nuevo dato.
I 5 I 12
Insertamos el 3:

Siguiente dato a insertar: 13


Posición correcta: 3
Capítulo 5. Tipos de datos compuestos

3 I 5 I 12 I 13
Realizaremos este proceso hasta que no tengamos más elementos en la tabla original.
El algoritmo para este tipo de búsqueda se muestra a continuación:
Subprograma Inserción (entero [] tabla, entero NumeroElementos)
Entorno:
entero i, j;
entero aux;
Inicio:
Para i=l a NumeroElementos-1 incremento 1 hacer
j = i;
aux = tabla[i];
Mientras j > O Y aux < tabla[j-1] hacer
tabla[j] = tabla[j-1];

FinMientras;
tabla[j] = aux;
FinPara;
FinInserción;

static void Inserción (int [] tabla, int NumeroElementos){


int i, j;
int aux;
for(int i=l;i<NumeroElementos;i++){
j = i;
aux = tabla[i];
while (j > O && aux < tabla[j-1]){
tabla[j] = tabla[j-1];
j —;
}
tabla[j] = aux;

ACTIVIDAD 5.9

Realiza un pequeño programa que permita al usuario realizar las siguientes operaciones.
1. Inicializar array de números reales.
2. Ordenar usando el método de selección.

3. Ordenar usando el método de inserción.

4. Insertar un nuevo elemento en el array.


5. Eliminar un elemento dado su índice.

Controlar los errores que puedan producirse.

ALGORITMO DE ORDENACIÓN POR BURBUJA


El algoritmo de ordenación por burbuja es el más conocido y también el menos eficiente, ya
que en cada pasada para ordenar un elemento compara cada uno de ellos con su adyacente de
forma que si sus posiciones son incorrectas se intercambian. Al finalizar cada pasada el elemento
más grande se va colocando al final de la lista.
Recordemos nuestra tabla desordenada:

12 5 3 13 2 9 7
Comparamos las posiciones O y 1, tabla[0] y tabla[l], 12 y 5, están desordenados, se
intercambian:

5 12 3 13 2 9 7
A continuación, comparamos las posiciones 1 y 2, tabla[l] y tabla[2], 12 y 3, están
desordenados con lo que se procede al intercambio.
5 3 12 13 2 9 7
Comparamos las posiciones 2 y 3, tabla[2] y tabla[3], 12 y 13. Están ordenados con lo que no
se realiza ninguna acción.
5 3 12 13 2 9 7
Comparamos las posiciones 3 y 4, tabla[3] y tabla[4], 13 y 2. Nuevamente están
desordenadas. Procedemos a intercambiar.

5 3 12 2 13 9 7
Comparamos las posiciones 4 y 5, tabla[4] y tabla[5], 13 y 9. Intercambiamos ambas.
5 3 12 2 9 13 7
Comparamos las posiciones 5 y 6, tabla[5] y tabla[6], 13 y 7. Intercambiamos ambas ya que
están mal posicionadas.
5 I 3 I 12 I 2 I 9 I 7 I 13 I
Tras esta primera pasada, hemos conseguido que el mayor de los valores esté ya bien
posicionado. En la siguiente recorreremos el array hasta la posición anterior, es decir la número
5. El proceso se repite sucesivamente hasta alcanzar la posición 0.
A modo formal, los pasos seguidos en el proceso son:
1. Primera pasada. Comparamos elementos adyacentes. Si está mal posicionados ya que
el primero debe ser menor que él según se intercambian los valores. Realizamos las
comparaciones adyascentes hasta alcanzar la posición n-1 siendo n el total de
elementos de la tabla.
2. Segunda pasada. Comparamos elementos adyascentes y realizaremos los
intercambios oportunos hasta la posición n-2.
3. Siguientes pasadas. Comparamos elementos adyascentes hasta alcanzar la posición O,
estando ya esta ordenada.
El algoritmo para la búsqueda por burbuja es el que sigue:
Subprograma Burbuja (entero []tabla, entero NumeroElementos)
Entorno:
booleano ordenado = falso;
entero pasada=0, j, aux;
Inicio:
Mientras pasada<NumeroElementos-l Y No(ordenado) hacer
ordenado=verdad;
Para j = O a NumeroElementos-pasada-2 incremento 1 hacer
Si tabla[j] > tabla[j+l] entonces
ordenado = falso;
aux = tabla[j];
tabla[j] = tabla[j+1];
Capítulo 5. Tipos de datos compuestos

tabla[j+l] =
FinSi;
FinPara;
FinMientras
FinBurbuja;

static void Burbuja (int []tabla, int NumeroElementos){


bool ordenado = false;
int pasada=0, j, aux;
while(pasada<NumeroElementos-l && !(ordenado)){
ordenado=true;
for(j = O;j<NumeroElementos-pasada-l;j++){
if(tabla[j] > tabla[j+l]){
ordenado = false;
aux = tabla[j];
tabla[j] = tabla[j+1],•
tabla[j+1] = aux;

ACTIVIDAD 5.10

Prueba el algoritmo de burbuja. Desarrolla un programa en el que nada más iniciar pidas al
usuario los valores de un array de números enteros. A continuación, debes mostrar paso a paso
cómo se produce la ordenación burbuja del mismo.

ALGORITMO DE ORDENACIÓN SHELL


El algoritmo sliell es una mejora del algoritmo de inserción directa. En este caso se toman
grupos de elementos separados un intervalo y se comparan estos, si no están ordenados se
intercambian.

Vamos a usar la tabla ejemplo que hemos utilizado en otros algoritmos de ordenación:
12 I 5 I 3 I 13 I 2 I 9 I 7
Este array tiene un total de 7 elementos, tomaremos n=7.
Realizamos n/2 grupos, separados n/2 números entre ellos. n/2=7/2=3. Coloreamos en azul
los valores de la primera sublista.
12 I 5 I 3 I 13 I 2 I 9 I 7
Debemos realizar intercambios ya que no están ordenados.
7 I 5 I 3 I 12 I 2 I 9 I 13 I
Seguimos fonuando grupos de 3 a partir del segundo valor del array. Volvemos a colorear los
valores que formarán el nuevo grupo.
7 I 5 I 3 I 12 I 2 I 9 I 13
En esta ocasión solo fonuan el grupo dos números, no están ordenados con lo que tenemos
que proceder a intercambiarlos.
7 I 2 I 3 I 12 I 5 I 9 I 13
Repetimos el proceso a partir del siguiente número (3).
7 2 3 12 5 9 13
Los valores están ya ordenados, seguimos formando grupos.
7 I 2 I 3 I 12 I 5 I 9 I 13
Igualmente están ordenados. No podemos seguir formando grupos de números separados tres
posiciones de forma que volvemos a dividir entre dos nuestro intervalo, 3/2 = 1. Al llegar a 1 la
ordenación es similar a una ordenación por inserción directa.
2 7 3 12 5 9 13
2 3 7 12 5 9 13
2 3 5 12 7 9 13
2 3 5 7 12 9 13
2 3 5 7 9 12 13
2 3 5 7 9 12 13
Los pasos a seguir en este tipo de ordenación son los siguientes:
1. Teniendo en cuenta que la lista original tiene n elementos realizamos la operación
n/2, consideramos un incremento o salto entre los elementos de n/2, es decir,
obtenemos n/2 subarrays y cada elemento en las sablistas estarán separados n/2
espacios.
2. Se comparan las parejas o grupos de números en cada sablista y si no están ordenados
se intercambian.

3. Dividimos la lista n/4, con salto de n/4.


4. Se realizan grupos y se comprueban como en el punto 2.
5. Realizamos la operación una y otra vez hasta que el valor de salto o incremento sea 1.
En este punto pasamos a realizar una inserción directa.
El algoritmo de ordenación shell se muestra a continuación:
Subprogram Shell(entero tabla[], entero NumeroElementos)
Entorno:
entero intervalo, i, j, k,temp;
Inicio:
intervalo = NumeroElementos / 2;
Mientras intervalo > O hacer
Para i = interval a n-1 incremento 1 hacer
j=i- intervalo;
Mientras j >= O hacer
k=j+ intervalo;
Si tabla[j] <= tabla[k] entonces
j = -1;
Sino
temp = tablaíjl;
tabla[j] = tabla[k];
tabla[k] = temp;
j -= intervalo;
FinSi;
FinMientras;
FinPara;
intervalo = intervalo / 2;
FinMientras;
FinShell;
Capítulo 5. Tipos de datos compuestos

static void Shell(int tabla[], int NumeroElementos)


{
int intervalo, i, j, k,temp;
intervalo = NumeroElementos / 2;
while (intervalo > 0){
for(i = interval;i<n;i++){
j=i- intervalo;
while (j >= 0){
k=j+ intervalo;
if (tabla[j] <= tabla[k]){
j = -1;

else{
temp = tablaíj];
tabla[j] = tabla[k];
tabla[k] = temp;
j -= intervalo;

intervalo = intervalo / 2;

ALGORITMO DE ORDENACIÓN QUICKSORT


Este algoritmo de ordenación es uno de los más rápidos y eficientes. Usa la técnica de divide
y vencerás tal que divide la lista a ordenar en partes que ordenará a su vez. Básicamente se
escoge un elemento de la lista como pivote tal que se forman dos sablistas, en una de ellas se
almacenan los elementos menores que el pivote y en la otra los elementos mayores. Hecho esto
se vuelve a aplicar el proceso a las sablistas obtenidas.
Supongamos la tabla ejemplo que hemos usado en los anteriores algoritmos de ordenación:
12 5 3 13 2 9 7
Escogemos un valor como pivote, por ejemplo el primer valor y creamos dos sublistas,
números menores y mayores que el pivote.
5 I 3 I 2 I 9 I 7 I I 12 I 13|
Sablista menores pivote Sablista mayores
La sablista de mayores está ya ordenada y está compuesta por un solo elemento. Debemos
ordenar la lista de menores.

5 I 3 I 2 I 9 I 7
Escogemos como pivote el primer elemento, 5.

menores pivote mayores


Seguimos dividiendo en sublistas. Una vez las sublistas están ordenadas siempre se
ensamblarán colocando sublistas de menores, seguidas de pivote y sablista de mayores.
2 3 5 7 9
2 3 5 7 9 12 13
Formalmente el algoritmo sigue estos pasos:
1. Elegimos un elemento pivote.
200 Programación

2. Creamos dos sublistas tal que en la de la izquierda se introducen los elementos


menores al pivote, en los de la derecha los mayores al pivote. El pivote ocupará la
posición central.
3. Realizaremos la operación con las sublistas sucesivas.
El algoritmo quicksort se define como sigue:
Subprograma Quicksort(entero []tabla, entero primero, entero ultimo)
Entorno:
entero i, j, central;
entero pivote,tmp;
Inicio:
central = (primero + ultimo)/2;
pivote = tabla[central];
i = primero;
j = ultimo;
Hacer
Mientras tabla[i] < pivote hacer
i++;
FinMientras;
Mientras tabla[j] > pivote hacer

FinMientras;
Si i<=j entonces
tmp = tabla[i];
tabla[i] = tabla[j];
tabla[j] = tmp;
i++;
j~;
FinSi;
Mientras i <= j;
Si primero < j entonces
Quicksort(tabla, primero, j);
FinSi;
Si i < ultimo entonces
quicksort(tabla, i, ultimo);
FinSi;
FinQuicksort;

static void Quicksort(int []tabla, int primero, int ultimo)


{
int i central;
int pivote,tmp;
central = (primero + ultimo)/2;
pivote = tabla[central];
i = primero;
j = ultimo;
do{
while (tabla[i] < pivote)(
i++;

while (tabla[j] > pivote)]

if (i<=j){
tmp = tabla[i];
tabla[i] = tabla[j];
Capítulo 5. Tipos de datos compuestos

tabla[j] = tmp;
i++;
j--;
}
)while (i <= j);
if (primero < j){
Quicksort(tabla, primero, j);
)
if (i < ultimo){
quic)csort(tabla, i, ultimo);
)

ACTIVIDAD 5.11

Realiza un programa que muestre un menú desde el que se puedan realizar todas las
ordenaciones vistas en este apartado. Usa arrays de otros tipos de datos como double, char o
string. Recuerda que para este último tipo de datos debes usar funciones como CompareTo para
descifrar cuál de las cadenas comparadas es menor o mayor.

NOTA: Todos los arrays que hemos usado en los algoritmos de ordenación han sido j
de tipo entero. Se optó por este tipo ya que es sencillo y no complica la explicación de |
estos algoritmos que definitivamente es lo importante del apartado. !

5.2.10. ARRAYS MULTIDIMENSIONALES EN C#

Hasta ahora hemos estado usando arrays de una dimensión, conocidos también como
vectores, tablas o listas. Estos airays se recorren con ayuda de un solo índice. En este apartado
vamos a estudiar arrays de más de una dimensión para los que necesitaremos más de un índice a
la hora de ser recorridos.

Los arrays multidimensionales más comunes son los de dos dimensiones o bidimensionales,
también llamados matrices. Es común representar este tipo de datos como una tabla compuesta
por una serie de filas y columnas:

dato(0,0) dato(0,l) dato(0,2) dato(0,n)


dato(1,0) dato(l,l) dato(l,2) dato(l,n)
dato(2,0) dato(2,l) dato(2,2) dato(2,n)

dato(n,0) dato(n,n)

Entre los paréntesis (0,0),(1,0), etc., hemos colocado los índices que identifican a cada celda
de la matriz. El primero de ellos representa la fila y el segundo la columna, tal que dato(2,l) está
referenciando el valor ubicado en la fila 2 y la columna 1.
Podemos generar arrays de tantas dimensiones como queramos aunque es cierto, que la
mayoría de los problemas más complejos como máximo necesitarán 3 dimensiones. En este
apartado centraremos nuestra atención en los arrays bidimensionales.
5.2.10.1. DECLARACION Y CREACION DE ARRAYS MULTIDIMENSIONALES
EN C#

Al igual que veíamos en los arrays de una única dimensión, los arrays multidimensionales
deben declararse y crearse adecuadamente antes de poder ser usados.
La declaración incorpora en los corchetes una coma por cada dimensión adicional, es decir, si
queremos declarar un array de dos dimensiones pondremos una coma entre los corchetes, si es de
tres dimensiones, ubicaremos dos, y así sucesivamente.
'
I I

\ tipo_de_datos[,
] identificador; \
Ejemplos:
int [,] números;
double [„]reales;
En cuanto a la creación, debemos establecer el tamaño real de cada dimensión. Usaremos el
operador new para realizar la reserva de memoria.

identificador=new tipojde_datos[TAM, TAM] J


Ejemplos:
numeros=new int[10,10];
double [„]reales=new double[2,5,6];
La declaración y creación puede realizarse en la misma línea como se indica a continuación.
I tipo_de_datos[,] identificador=new tipo_de_datos[TAM, TAM]; .
El número total de elementos del array será igual al resultado de multiplicar filas y columnas.

5.2.10.2. ÍNICIALIZACIÓN DE ARRAYS MULTIDIMENSIONALES EN C#


La inicialización de un array multidemensional es similar a la de arrays de una única
dimensión, con la diferencia de que debemos controlar un número mayor de índices. Podemos
dar valores iniciales a la matriz:
■ De forma individual.
■ En el momento de la declaración.
■ Mediante el uso de una sentencia de control repetitiva.

INICIALIZACIÓN Y ACCESO A CADA ELEMENTO DEL ARRAY


Para acceder a un dato en un array multidimensional debemos conocer los índices de posición
de este. En un array de dos dimensiones, para acceder a un dato se debe indicar la fila donde se
encuentra además de su columna de la siguiente forma:
i matriz[fila, columna]=va¡or; \
Capítulo 5. Tipos de datos compuestos 203

matriz[0,0]=12;
matriz[0,l]=13;
12 13 matriz[0,2]=5;

I íy ^ — matriz[6,4]=6;
\ 4 Realizar la inicialización de esta forma es
^ bastante tedioso y generaría demasiado
-L: Z Z Z Z— código. En el ejemplo tenemos una matriz de
I 1 I 39 I 5 154 16 gj tuviéramos que realizar la
Figura 5.9. Representación gráfica de una inicialización de una matriz de 100x100 el
matriz bidimensional. proceso sería inviable.
INICIALIZACIÓN EN EL MOMENTO DE CREACIÓN DE LA MATRIZ
Podemos inicializar una matriz justo en el momento en que se crea esta, al igual que veíamos
con los arrays de una única dimensión. Usamos las llaves para delimitar grupos de elementos de
cada fila y columnas.

tipo[,]matr¡z—new iipo[filas,columnas]{{elementos_Jila}{elementos_columnas}} \
tipo[,]matriz={{elementos_fila}{elementos_columnas}} i
tipo[,]matriz=new tipo[,]{{elementos_Jilas}{elementos_columnas}} :

Colocaremos los elementos de una misma fila entre llaves. Cada


fila, a su vez, se agmpa al resto mediante un par de llaves y separadas
por comas.

En la Figura 5.10, la primera fila está compuesta por los valores


12 y 13, a la hora de inicializar, esta fila se representa del siguiente
modo: {12,13}. Las dos siguientes filas se identificarán tal y como
sigue: {2,3} y {1,39}.
A la hora de agrupar todas las filas, el formato es similar a este:
Figura 5.10. Matriz de
3x2. {{filal},{fila2},..., {filan}}
{{12,13},{2,3},{1,39}}.
; int [,]matriz=new int[3,2]{{12,13},{2,3},{1,39} };
; int [,]matriz={{12,13), {2,3},{1,39}};
int [,}matriz=new int[,]{ {12,13},{2,3},{1,39}};
Las filas siempre se representan en orden y contendrán tantos elementos como columnas
como se considere oportuno en fimción de la aplicación.
INICIALIZACIÓN MEDIANTE EL USO DE BUCLES
Si al trabajar con arrays de una dimensión usábamos un único bucle que recorría este, en
arrays de varias dimensiones es necesario mantener un bucle por cada dimensión adicional. En
arrays de dos dimensiones, la inicialización mediante sentencias de control repetitiva se realiza
del siguiente modo:
//Inicialización de todos los valores a cero
constante entero FILAS=f;
constante entero COLUMNAS=c;

Subprograma InicializaMatriz(entero [,]matriz)


Entorno:
entero i,j;
Inicio:
Para i=0 a FILAS incremento 1 hacer
Para i=0 a COLUMNAS incremento 1 hacer
matriz[i,j]=0;
FinPara;
FinPara;
FinInicializaMatriz;

//Inicialización en función de los valores dados por el usuario


constante entero FILAS=f;
constante entero COLUMNAS=c;

Subprograma InicializaMatriz(entero [,]matriz)


Entorno:
entero i,j;
Inicio:
Para i=0 a FILAS incremento 1 hacer
Para i=0 a COLUMNAS incremento 1 hacer
Escribir "Introduce número";
Leer matriz[i,j];
FinPara;
FinPara;
FinInicializaMatriz;

const int FILAS=10;


const int COLUMNAS=10;

static void inicializa(int [,]matriz){


for(int i=0;i<FILAS;i++)(
for(int j=0;j<COLUMNAS;j++){
matriz[i,j]=0; , „
/*Console.WriteLine("Introduce numero );
matriz[i,j]=int.Parse(Consolé.ReadLine());*/

5.2.10.3. EJEMPLO DE USO DE MATRICES EN C#


Para entender definitivamente el concepto de matriz o array bidimensional vamos a realizar
un pequeño ejercicio donde vamos a trabajar con las típicas matrices matemáticas, realizando las
operaciones propias de estas.
Vamos a desarrollar una aplicación que realice operaciones con matrices de 3x3. El software
mostrará el siguiente menú:
1. Rellena la primera matriz.
2. Rellena la segunda matriz.
Capítulo 5. Tipos de datos compuestos 205

3. Visualiza las matrices.

4. Suma matrices.

5. Producto por un escalar.


6. Producto de matrices.

7. Traspuesta.
8. Salir.

Realizaremos una función por cada opción del menú.


Antes de nada recordemos algunas de las operaciones matemáticas a realizar:
Suma de matrices

1+5=6 2+6=8

3+7=10 4+8=12

C=A+B donde Cij=Aij+Bjj.


Producto por un escalar

X 5=

C=A*num donde Cij=Aij*num.


Producto de matrices

1*5+2*7=19 1*6+2*8=22

3*5+4*7=43 3*6+4*8=50

C=A*B donde Cij=Yk=^Ai¡i *


Traspuesta de una matriz

A donde Aij=A ji
/♦Función menú
Muestra el menú, pide al usuario que escoja una opción y devuelve la opción
elegida.
Parámetros de entrada: ninguno.
Salida: Número entero.
*/
static int menú()
{
int opcion;
Consolé.WriteLine("1. Rellena la primera matriz");
Consolé.WriteLine("2. Rellena la segunda matriz");
Consolé.WriteLine("3. Visualiza las matrices");
Consolé.WriteLine("4. Suma matrices");
Consolé.WriteLine("5 . Producto por un escalar");
Consolé.WriteLine("6. Producto de matrices.") ;
Consolé.WriteLine("7. Traspuesta");
Consolé.WriteLine("8. Salir") ;
Consolé.WriteLine("Escoge opción:");
opcion = int.Parse(Consolé.ReadLine O);
return opcion;
}

/♦Función inicializa
Inicializa la matriz pasada como parámetro.
Parámetros de entrada: Matriz de números enteros.
Salida: ninguna.
♦/
static void inicializa(int[,] matriz)

Consolé.WriteLine("Escribe un número y pulsa ENTER hasta que aparezca el


menú");
for (int 1=0; i < FILAS; i++)
{
for (int j =0; j < COLUMNAS; j++)
{
matriz[i, j] = int. Parse(Consolé.ReadLineO) ;
}

/♦Función sumar
Suma las matrices pasadas como parámetro. El resultado no se almacena,
simplemente se muestra por pantalla.
Parámetros de entrada: matrices a sumar.
Salida: ninguna.
♦/
static void sumar(int[,] mi, int[,] m2)

Consolé.WriteLine("La suma de matrices es:") ;


for (int i = 0; i < FILAS; i++)
{
for (int j =0; j < COLUMNAS; j++)
{
Console.Write("(0}\t", mi[i, jl + m2[i, j]);
}
Consolé.Write("\n");
}
}
/♦Función mostrar
Visualiza en pantalla la matriz pasada como paj;ámet_ro__de__la__fjDrma_:
Capítulo 5. Tipos de datos compuestos 207

numl num2 num3


num4 numS num6
num7 numS num9
Parámetros de entrada: Matriz de número enteros.
Parámetros de salida: ninguno.
*/
static void mostrar(int[,] matriz)
{
Consolé.WriteLine("La matriz es:");
for (int i = 0; i < FILAS; i++)
{
for (int j =0; j < COLUMNAS; j++)

Console.Write("{0}\t",matriz[i,j])
}
Consolé.Write("\n");

/*Función prodEscalar
Realiza el producto escalar de una matriz por un número dado. El resultado se
muestra por pantalla, no es almacenado.
Parámetros de entrada: numero entero y matriz de números enteros.
Salida: ninguna.
*/
static void prodEscalar(int num, int[,] matriz)

Consolé.WriteLine("El producto de la matriz por el escalar es:");


for (int 1=0; i < FILAS; i++)
{
for (int j =0; j < COLUMNAS; j++)
{
Consolé.Write("{0}\t", matriz[i, j]*num);
}
Consolé.Write("\n");

/*Función product
f'-ssliza el producto de dos matrices y muestra el resultado por pantalla,
atendiendo a las especificaciones de cálculo de producto de matrices antes
visto.
Parámetros de entrada: las matrices de números enteros a multiplicar.
Salida: ninguna.
*/
static void producto(int[,] mi, int[,] m2)

int matriz_producto=0;
for (int le = 0; k < FILAS; k++)
{
for (int j = 0; j < FILAS; j++)
{
matriz_producto=0;
for (int i = 0; i < COLUMNAS; i++){
matriz_producto += ml[k,i] * m2[i,j];
}
Consolé.Write("{O}\t", matriz producto);
}
Consolé.Write("\n");
/*Función traspuesta
Realiza la traspuesta de una matriz dada. Muestra el resultado por pantalla.
Parámetros de entrada: matriz de números enteros.
Salida: ninguna.
*/
static void traspuesta(int[,] matriz)
{
for (int i = 0; i < FILAS; i++)
{
for (int j = 0; j < COLUMNAS; j++)
{
Consolé.Write("{O}\t", matriz[j, i]);
}
Consolé.Write("\n");

NOTA: Al igual que ocurría en arrays unidimensionales, el paso de parámetros en


arrays de más una dimensión se realiza por referencia, de forma que las modificaciones
que se realicen en el interior de la función que se invoca permanecerán una vez acabe
esta.

ACTIVIDAD 5.12

Imagina que debes mantener las notas de los alumnos de tus tres asignaturas. En cada
asignatura tienes 15 alumnos, tal que se debe mantener la información mediante una variable que
gráficamente presenta esta estructura:
Asignatura1 5 6 2.3 4.3 5.7
Asignatura2 3 3 5 6 6
Asignatura3 2 1 2.3 5.6 5
Crea una aplicación con la que puedas:
1. Insertar las notas de la asignatura seleccionada.
2. Insertar todas las notas.

3. Calcular la nota media de la asignatura seleccionada.


4. Ordenar asignaturas de forma ascendente.
5. Estadística.

6. Salir.

La opción Estadística muestra la cantidad de notas entre O y 3, entre 3.1 y 5, entre 5.1 y 7,
entre 7.1 y 9 y 9.1 y 10.

5.2.10.4. ARRAYS IRREGULARES O ESCALONADOS EN C#

Una matriz irregular o escalonada no es más que un array cuyos elementos son a su vez
tablas. Gracias a este tipo de array, podemos usar matrices con un número x de filas tal que cada
fila puede tener un número diferente de columnas.
Capitulo 5. Tipos de datos compuestos 209

Podemos anidar tantas tablas como queramos, de forma que tendremos multitud de tablas
enlazadas. Todas los arrays deben ser del mismo tipo de datos.

o ^ 1 2 3 4 5

. I ^1 O I 15 10 13

. ^1 76 12

Figura 5.11. Arrays escalonados o irregulares.


La declaración y creación de un array escalonado es como sigue:
i tipo[][]nombre=new tipo[][]{new íipo[TAM],new t¡po[TAM2],...,new tipo[TAMN]};
[tipo[]
[]nombre=new tipo[FILAS][]{new tipo[TAMl],...,new tipo[TAMN]};

[][]matriz=new int[][]{new int[2],new int[6],new int[4]);


int [][]matriz=new int[3][]{new int[2],new int[6],new int[4]};

Las declaraciones anteriores dan lugar a un tipo de datos similar al que se representa en la
Figura 5.12.

Figura 5.12. Matriz escalonada resultante de:


int nDnnatriz=new int[]n{new ¡nt[2],new int[6],new int[4]};
Al igual que hacíamos con arrays unidimensionales y multidimensionales podemos inicializar
un array escalonado en el mismo momento de su creación. Para ello debemos establecer valores a
cada tabla que se asignará a cada fila de dicho array.
r - ->
i tipo o[]nombre=new tipo[]0{new tipo[TAM']{vl,v2,...,vn},...,new tipo[TAM¡{vI,...,vn}}; i

int [][]matriz=new int[][]{new int[]{1,2,3},new int[]{8,7,9}};


char [][]matriz=new char[2][]{new char[5],new char[]{'a','b','C } };

No es necesario indicar el tamaño ya que los valores que se incluyen entre las llaves imponen la
dimensión.
A la hora de acceder a cada elemento de un array escalonado, debemos indicar la fila en la
que se encuentra y su columna, como hacíamos con arrays de una y varias dimensiones, eso sí,
aquí usaremos un par de corchetes para especificar cada índice.

1 nombre[fila][columna]=valor;

NOTA: A la hora de acceder a un valor debemos tener especial precaución debido a


que cada fila puede apuntar a un array de tamaño diferente.

static void Main(stringí] args)

char[][] letras = new char[2][] {new char[]{'a','e','i','o','u' }


,new char[]{'b','c','d','e','f','g','h','j','k','l','ni','n'.

Consolé.WriteLine("Muestra las vocales:");


for (int j = 0; j < 5; j++)
{
Consolé.Write("{O}\t",letras[O][j]);
}
Consolé.WriteLine("\nMuestra las consonantes");
for (int j = 0; j < 23; j++)
{
Consolé.Write("{0}", letras[1][j]);
Consolé.Write((j%5==0)?"\n":"\t");
}
Consolé.WriteLine("La primera vocal es:{O)",letras[ O][ O ]);
Consolé.WriteLine("La primera consonante es:{1}",letras[1][0]);
Consolé.ReadLine();
}

En el ejemplo usamos un bucle for para recorrer cada fila ya que cada una apunta a diferente
número de elementos.

ACTIVIDAD 5.13

Realizar la Actividad 5.12 tal que en cada asignatura tengamos un número diferente de
alumnos.

ACTIVIDAD 5.14

Imagina que tienes que realizar una aplicación para una tienda de forma que esta necesita
almacenar sus productos en categorías:
T. T 1 TI 1 Queso fresco Yogures
Lácteos Leche Puleva _,
Día danone
Coca cola
Bebidas

Dulces Palmeras Cañas

La primera cadena de caracteres representa el nombre de la categoría y los demás casillas los
nombres exactos de productos. Como se observa no tendremos la misma cantidad de productos
en cada categoría. La aplicación debe permitir realizar las siguientes operaciones:
Capítulo 5. Tipos de datos compuestos 211

Inicializar la matriz.

Ordenar cada categoría.


Contar los productos de una categoría.
Añadir, modificar y eliminar un producto.

NOTA: Podemos conocer el número de filas de un array multidimensional mediante la


propiedad Rank. El método GetLength(fila), devolverá el número de elementos de una fila
concreta. Si hemos realizado la siguiente declaración:
double [, ] reales=new doiible[,] { {1.0,2.O),{3.0, 4.O }, O { 6.0,7.O } }

El siguiente código devolverá los valores 2 y 2 para la propiedad Rank y el método


GetLength(l).
Consolé.WriteLine ("Filas:(0)",reales.Rank);

Consolé.WriteLine ("Elementos fila 1:{ O )",reales.GetLength (1));

5.3. CADENAS DE CARACTERES


Ya hemos usado en varias ocasiones el tipo de datos string para representar cadenas de
caracteres. En otros lenguajes de programación como C y €++ este tipo de datos no existe como
tal sino que se configuraba a partir de un array de caracteres, tal que cada posición podía ser leída
o escrita. En C# la esencia de un tipo string es exactamente esta, se puede tratar como una cadena
de caracteres a la que solo se tiene acceso de lectura.
string frase="Hola Mundo";
Consolé.WriteLine("El carácter que ocupa la posición 3 es:{0}
,frase[3]"); //OPERACIÓN PERMITIDA
frase[3]='A'; //OPERACIÓN NO PERMITIDA

Cada vez que escribamos en nuestro código fuente combinaciones alfanumérícas entre
comillas dobles ("") el compilador tomará esta como un valor de tipo string. Además, podemos
inicializar variables string a partir de arrays de tipo carácter creados con anterioridad.
char []letras=char[]{ 'M','u','n','d',
'o'};
string frase=new string(letras);
Usamos la palabra reservada new para crear un objeto string al que pasaremos como
parámetro el array de tipo carácter letras.
El tipo de datos string dispone de una serie de métodos y propiedades de bastante utilidad.
■ Length. Devuelve el tamaño o número de caracteres.
■ ToCharArrayO. Método que devuelve un array de caracteres, de forma que en cada
posición de este array encontraremos un carácter de la cadena almacenada en el
string. Convierte un string a un array de caracteres.
■ SubStringO. Extrae una parte de la cadena. Este método está sobrecargado de fonua
que encontraremos SubString(int inicio) y SubStríng(int inicio, int tamaño). El
primero de ellos extrae una subcadena desde la posición inicio hasta el final. El
segundo permite establecer más concienzudamente los limites de la subcadena, ya
que debemos especificar dónde empieza y qué tamaño tiene.
■ CopyToO. Copia un número detenninado de caracteres situados en una posición
concreta del string en una posición de un array de tipo carácter.
■ CompareToQ. Compara la cadena de caracteres que contiene el string que llama al
método con otra dada. Devuelve O si ambas cadenas son iguales, un valor negativo o
positivo en función de si la cadena está antes o después alfabéticamente de la pasada
como parámetro.
■ ContainsQ. Devuelve un valor booleano que indica si la cadena pasada como
parámetro forma parte del string que invoca al método.
■ IndexOfQ. Este método devuelve el índice de la primera aparición de un carácter o
cadena pasado como parámetro en el string que hace la llamada. En caso de que no se
encuentre el carácter o la cadena el método devolverá -1.
■ Inserto. Inserta una cadena de caracteres pasada como parámetro en el string que
invoca al método.
■ TrimO. Quita todas las apariciones de un carácter concreto en el inicio y fin de la
cadena de caracteres.
■ RepIaceQ. Sustituye un carácter o string por otro dado.
■ RemoveO. Elimina un número de caracteres de una instancia concreta.
■ SpIitQ. Permite separar una cadena de caracteres en partes, cada una de ellas está
delimitada por un elemento que será especificado en la función. Devuelve un array de
string.
■ ToLowerQ. Devuelve una copia del string que realiza la llamada en minúsculas.
■ ToUpperQ. Devuelve una copia del string que realiza la llamada en mayúsculas.

/*Función Burbuja
Realiza la ordenación mediante el método de ordenación burbuja ya visto.
Parámetros de entrada: Array de tipo string y número de elementos de esta.
Salida: Ninguna.
*/
Burbuja(string[] tabla, int NumeroElementos)
bool ordenado = false;
int pasada = O, j;
string aux;
while (pasada < NumeroElementos - 1 && '(ordenado))
{
ordenado = true;
for (j =0; j < NumeroElementos - pasada - 1, j++)
{
if (tabla[j].CompareTo(tabla[j + 1])>0)
{
ordenado = false;
aux = tabla[j];
tabla[j] = tabla[j + 1];
tabla[j + 1] = aux;
Capítulo 5. Tipos de datos compuestos

/*Función mostrar
Muestra los array introducidos por parámetros
Parámetros de entrada: Array de tipo string.
Salida: ninguna.
*/
static void mostrar(string[] p)
{
for (int i = 0; i < p.Length; i++)
{
Consolé.Write("{O)\t",p[i]);
}
Consolé.Write("\n");

static void Main(string[] args)


{
string[] palabras = { "hola","adiós",
"mundo","árbol","pera","manzana","sol"};
Consolé.WriteLine("Las palabras de las que partimos son:");
mostrar(palabras);
Burbuja(palabras, palabras.Length);
Consolé.WriteLine("El array ordenado es:");
mostrar(palabras);
Consolé.ReadLine();

En el siguiente ejemplo hacemos uso de la función Spiit. Supongamos que debemos realizar
una aplicación que dada una dirección IP muestre: dirección de red, máscara, puerta de enlace y
dirección de broadcast.
static void Main(string[] args)
{
string direccionlP;
char[] separador = { );
string[] numeroslp;
Consolé.WriteLine ("Introduce la dirección IP");
direccionIP=Console.ReadLine();
numeroslp = direccionlP.Split(separador);
if (IpValida(numeroslp))
{
Consolé.WriteLine("Para la dirección IP dada:{0)", direccionlP);
Consolé.WriteLine("Dirección de red:{0)", direccionRed(numeroslp));
Consolé.WriteLine("Máscara de red:{0)", mascara(numeroslp));
Consolé.WriteLine("Puerta de enlace:(0)", gateway(numeroslp));
Consolé.WriteLine("Dirección de Broacast:{O)", broadcast(numeroslp));
)
else
{
Consolé.WriteLine("La dirección introducida no es una IPv4 válida");
)
Consolé.ReadLine();

La función principal se encarga de invocar a los métodos:


■ direccionRed(numerosIP): Devuelve la dirección de red de una IP dada. La
dirección IP se pasa como parámetro y como array de string, es decir, en este y los
demás métodos pasamos como parámetro un array tal que cada posición almacena un
número de la dirección IP introducida.

■ mascara(numerosIP): Devuelve la máscara de red de una dirección IP dada.


■ gateway(numeros IP): Devuelve la puerta de enlace de una dirección IP dada.
■ broadcast(numerosIP): Devuelve la dirección de broadcast de una IP dada.
■ IpValida(numerosIp): Antes de usar ninguno de los métodos explicados se
comprueba si la dirección IP es válida. Tomaremos como IP válida aquella que esté
compuesta por 4 números siendo estos valores entre 1 y 254.
El programa es susceptible de mejora, dejamos al lector la opción de que modifique este si lo
desea.

Utilizamos la función Split para separar cada número de la dirección IP. Esta función propia
del tipo string necesita que se pase como parámetro un array de tipo carácter en el que se
incluyan aquellos caracteres delimitadores que permitan separar el string. En nuestro código este
array se declara y crea como sigue:
r~— — —I

i char[] separador = { ^.' }; I


Podríamos haber hicMdó'entré las iTa^^ caracteres como separadores hubiéramos
querido tener en cuenta.
i^numeroslp = direccionIP.Split(separador); _ ;

/*FUNCIÓN IpValida*/
static bool IpValida(string[] parteslp)
bool valida = true;
int i = 0;
if (parteslp.Length != 4)
valida = false;

else
{
while (i < parteslp.Length && valida)
int parte = int.Parse(parteslp[i])'
if (parte < 1 || parte > 254)
{
valida = false;

return valida;
Capítulos. Tipos de datos compuestos 215

/*FUNCIÓN mascara*/
staliic string masca ra (string []parteslp)

string mascaraResultado = "0.0.0.0";

int parte = int.Parse(parteslp[0]);


if (parte >= 1 && parte <= 127)
{
mascaraResultado = "255.0.0.0";
)
else if (parte >= 128 && parte <= 191)
{
mascaraResultado = "255.255.0.0";
}
else if (parte >= 192 && parte <= 223)
{
mascaraResultado = "255.255.255.0";
)
j;eturn mascaraResultado;

/*FUNCIÓN direccionRed*/
static string direccionRed(string []parteslp)
{
string mascaraRed = mascara (parteslp);
if (mascaraRed.CompareTo("255.0.0.O") == 0)
{
mascaraRed= parteslp[0] + ".0.0.0";
}
else if(mascaraRed .CompareTo("255.255.0.0")==0){
mascaraRed = parteslp[0] + "." + parteslp[l] + ".0.0";
}
else if (mascaraRed.CompareTo("255.255.255.O") == 0)
{
mascaraRed = parteslp[0] + "." + parteslp[l] +
"." + parteslp[2] + ".O";
}
return mascaraRed;

/*FUNCION gateway*/
static string gateway(string[] parteslp)
{
string dirGateWay = "E.E.E.E";
int primerNumero = int.Parse(parteslp[ O ]);
if (primerNumero >= 1 && primerNumero <= 127)
{
dirGateWay = parteslp[0] + ".0.0.1";
}
else if (primerNumero >= 128 && primerNumero <= 191)
{
dirGateWay = parteslp[0] + "." + parteslp[l] + ".0.1";
}
else if (primerNumero >= 192 && primerNumero <= 223)
í
dirGateWay = parteslp[0] + "." + parteslp[1] +
+ parteslp[2] + ".1";
}
return dirGateWay;
/^FUNCIÓN broadcast*/
static string broadcast(string[] parteslp)
{
string dirBroadcast="E.E.E.E";
int primerNumero=int.Parse(parteslp[0]);
if (primerNumero >= 1 && primerNumero <= 127)
{
dirBroadcast = parteslp[0] + ".255.255.255";
}
else if (primerNumero >= 128 && primerNumero <= 191)
{
dirBroadcast = parteslp[0] + "."+parteslp[1]+".255.255";
}
else if (primerNumero >= 192 && primerNumero <= 223)
{
dirBroadcast = parteslp[0] + + parteslp[l] +
+ parteslp[2]+ ".255";
}
return dirBroadcast;

Ejemplo de uso de la función IndexOfO- Vamos a modificar la función IpValida de fornia


que reciba como parámetro un string igual a la dirección IP a analizar y cuente las veces que
aparece el carácter punto. Si la dirección tiene tres puntos será correcta, en caso contrario no será
valida. Solo atenderemos a la premisa de que la dirección es valida si tiene tres puntos, no
entraremos en las cantidades, dejamos al lector que realice las modificaciones oportunas al
método.

static bool IpValida(string dirlP)


bool valida = false;
int puntos = o, i = dirlP.IndexOf('•'); /*Devuelve la primera aparición
del punto*/
while (i!=-i) //El método IndexOf devuelve -1 si no encuentra el carácter
{
puntos++;
i = dirIP.indexOf('.',i+l); /*Busca el siguiente punto a partir de la
siguiente posición del punto localizado*/
if (puntos == 3)
{
valida = true;
}
return valida;

5.3.1. OBJETO STRINGBUILDER EN C#


Como hemos estudiado el tipo string es de solo lectura y cada vez que queremos realizar una
modificación sobre él, debemos generar otro string que almacene los cambios producidos.
Además,cuando debemos formar una cadena a partir de otras debemos usar de forma continuada
estructuras como se muestran a continuación:

I string cadena="Hola"+nombre+apell+apel2;
i string dirRed=numl+"."+num2+"."+num3+".O";
Capítulos. Tipos de datos compuestos 217

Lo veíamos en el ejercicio anterior.


C# dispone de un objeto llamado StringBuilder que pennite solventar los problemas del tipo
string realizando concatenaciones de cadenas de fomia más eficiente.
Al generar un objeto StringBuilder podemos modificar cada posición como queramos, usando
para ello el símbolo corchete [].
/*Declaración y creación de una instancia de StringBuilder a la que se le j
asigna la cadena "Hola Mundo"*/ í
StringBuilder cadena = new StringBuilder("Hola Mundo"); ;
i

//Modificamos la posición 4, es decir, cambiamos el espacio por una / ¡


cadena[4] = V ; j

//El método WriteLine muestra por pantalla "Hola/Mundo" I


Consolé.WriteLine(cadena); i
Consolé.ReadLine(); i

Debemos declarar y crear (resei^var espacio en memoria) el objeto. Así, es necesario usar la
palabra reservada new.

StringBuilder cadena=new StringBuiIder(cadena de caracteres);


Además de la sobrecarga del operador [], encontraremos métodos como Append que perrmte
concatenar una cadena al final de la instancia que hace la invocación o AppendFormat que
agrega varias cadenas que se ajustan a un formato especificado.
static StringBuilder broadcast(string[] parteslp) i
{ i
StringBuilder dirBroadcast=new StringBuilder(""); ;

else if (primerNumero >= 192 && primerNumero <= 223)


{
dirBroadcast.AppendFormat("{0}.{1}.{2}.255",parteslp[O ]
,parteslp[1],parteslp[2]);
}
return dirBroadcast;

ACTIVIDAD 5.15

Realiza un pequeño programa que pida al usuario 5 nombres. Estos deben ser introducidos
siguiendo el orden: nombre apellido 1 y apellido2, y modifique el formato para que se visualicen
de la forma: apellido 1 apellido2, nombre. Usa el objeto StringBuilder.

5.4. ESTRUCTURAS
Una estructura es un tipo de datos compuesto tal que agmpa variables de diferentes tipos. Por
ejemplo, imaginemos que queremos desarrollar un programa que debe almacenar los datos de
nuestros alumnos junto a sus notas. Hasta ahora podemos realizar la aplicación haciendo uso de
arrays de diferentes tipos de datos, tal que cada vez que queramos insertar, eliminar o modificar
un alumno debemos averiguar el índice de este y proceder a la operación en todas las tablas. Esta
es ima forma se subsanar el problema pero no es la más acertada, ya que cada array es
independiente del resto, en algún momento podemos cometer algún error de programación y
podemos enlazar datos que no tiene nada que ver unos con otros.
Podemos decir que una estructura es un tipo de datos creado por el usuario que no llega a
tener la potencia de una clase pero que permite mantener agrupada información relacionada con
un elemento concreto de nuestro programa.

Nombres,array de tipo String Alumno 1 Alumno 2


1 1 h-:~
Pepe Juan Isabel

Apellidosl, array de tipo String

Martín Jiménez Pérez

Apellidos2, array de tipo String

Pérez Vázquez Pérez Ligero

Asignatural, array de tipo doubie Alumno 3 Alumno N

Juan Mana

,5 7,8 Jiménez López


Vázquez Ligero
7,8 3,0
AsignaturaN,array de tipo String

9,8 10,0

Figura 5.13. Necesidad de uso de estructuras (a la derecha).


En C# la creación de una estructura se produce con el uso de la palabra reservada struct.
public struct nombref
Campos
métodos
Propiedades

Prestar especial atención;


■ En el interior de una estructura no podemos inicializar las variables a no ser que estas
se hayan declarado como constantes o les precede el modificador static.
■ No podemos declarar un constructor o destructor predeterminado. En el Capítulo 7
definiremos estos términos.
Ya que tomamos la estructura como un tipo de dato compuesto y personalizado por el
usuario, a la hora de crear una variable de este nuevo tipo la declaración será similar a la que
hemos estado realizando hasta ahora:

i tipojdato nombre; i
I estructura nombre; i
I alumno clase; //Siendo alumno una estructura ;
1 coche arosa; //Siendo coche una estructura j
r — 1

public struct punto ;


Capítulos. Tipos de datos compuestos 219

public int X;
piiblic int Y;
);
static void Main(string[ args)

punto coordenada;
coordenada.X=1O;
coordenada.Y=5;

X e Y son campos de la estnictura. Se usa el modificador public para que podamos acceder a
ellos desde cualquier otra función (recordamos las características de visibilidad de los objetos en
C#).
A la hora de usar la estructura declaramos una variable de tipo punto: punto coordenada; El
símbolo punto (.) permite acceder a los elementos públicos creados en nuestro tipo de datos. Otro
modo de desarrollar esta estructura es:
publxc struct punt

public int getXO

public int getYO

piiblic void setX(int x)

this.X = x;

piiblic void setY (int y)

this.Y =

Normalmente las variables que se incluyen en una estructura o una clase no se declaran
públicas, es decir, no se pennite el acceso directo a ellas a través del operador punto, se usan
métodos o funciones que penniten su visualización o modificación.
Así, en nuestra nueva estructura punto declaramos una variable llamadaX y otra Y de tipo
entero a la que podremos acceder mediante los métodos set y get. Un método que empieza por
set se refiere a la modificación de un campo de la estructura, mientras que get conseguirá
exteriorizar o visualizar el valor de un campo. Ya que un método set modifica un campo debe
recibir como parámetro el nuevo valor. El método get obtiene un valor con lo que no recibe
parámetros pero sí devuelve el valor de un campo concreto. Todos los métodos son public para
que puedan ser usados.

public int getX() /*La variable se llama X, nuestro método se denomina getX, \
al ser un método get devuelve el valor de la variable X, 1
de ahi public int.*/ ;
Las estructuras son tipos de datos valor. Podemos crear arrays d^e tipo estructura.
Para entender definitivamente este tipo de datos, vamos a desarrollar un pequeño programa
que gestiona una agenda de direcciones. La estructura del tipo de dato a usar es la siguiente:

Pepe Isabel María


Martín Pérez López
Pérez Pérez Ligero
Plaza la Caña 3 C/Descubridores, 13 Av. Andalucía, 7
959121212 954213456 956280080
633121213 693135467 649231205
pepemp(S)gmail.com isperez(S)hotmail.com [email protected]

Figura 5.14. Estructura de datos de la aplicación.


Según se observa de cada contacto debemos almacenar:
■ Nombre.

■ Apellido 1.
■ Apellido 2.
■ Dirección.

■ Teléfono fijo.
■ Teléfono móvil.

■ Dirección de correo electrónico.


Como máximo vamos a almacenar un total de 100 contactos, usaremos un array de contactos
para almacenar estos y gestionarlos.
" — — — — — 1

public struct contacto í

public string nombre; //Declaración de la estructura


pTJblic string apellidol; //Hemos optado por usar el modificador public
public string apellido2;
public string dirección;
public string telFijo;
public string telMovil;
public string mail;
};
static void Main(string[] args)

contacto[] agenda = new contacto[100]; /*Creación de un array de 100


contactos*/

Podremos realizar ías siguientes operaciones sobre la agenda.


■ Insertar un contacto.

■ Modificar un contacto existente.

Eliminar un contacto.
Capítulo 5. Tipos de datos compuestos 221

■ Buscar un contacto por Apellidos 1 y 2.


■ Ordenar contactos por Apellido 1.
Realizaremos una función por cada acción.
/*Función crearContacto
Solicita al usuario información relativa a un Nuevo contacto almacenando esta
en una variable que es pasada como parámetro por referencia.
Parámetros de entrada: contacto nuevo.
Salida: ninguna*/
static void crearContacto(out contacto nuevo)
t
Consolé.WriteLine("Introduce nombre");
nuevo.nombre = Consolé.ReadLine();
Consolé.WriteLine("Introduce primer apellido");
nuevo.apellidol = Consolé.ReadLine();
Consolé.WriteLine("Introduce segundo apellido");
nuevo.apellido2 = Consolé.ReadLine();
Consolé.WriteLine("Introduce dirección");
nuevo.dirección = Consolé.ReadLine();
Consolé.WriteLine("Introduce teléfono fijo");
nuevo.telFijo = Consolé.ReadLine();
Consolé.WriteLine("Introduce teléfono móvil");
nuevo.telMovil = Consolé.ReadLine();
Console.WriteLine("Introduce mail");
nuevo.mail = Consolé.ReadLine();
}

/*Función insertar
Inserta un contacto ya creado con anterioridad en la agenda.
Parámetros de entrada: Array de contactos (agenda), total de contactos
almacenados y el contacto a insertar.
Salida: Ninguna.*/
static void insertar(contacto[]ag,ref int totalContactos, contacto nuevo)

ag[totalContactos] nuevo;
totalContactos++;

/*Función modificar
Se encarga de modificar un contacto insertado en una posición concreta del
array. Se visualiza cada campo del contacto, el usuario debe escribir '*s' o
'n' según quiera modificar o no el dato. Si escoge ^s' debe escribir el nuevo
valor.
Parámetros de entrada: la agenda y la posición del contacto que se quiere
modificar.
Salida: Ninguna.*/
static void modificar(contacto[] ag, int posContacto)
{
char mod=' ';
Consolé.WriteLine("¿Qué desea modificar? Iremos mostrando cada dato,
si quiere modificar esciba s, para no modificar n");
Consolé.WriteLine(ag[posContacto].nombre);
mod = char.Parse(Consolé.ReadLineO);
if(mod=='s')
{
ag[posContacto].nombre=Console.ReadLine();
Consolé.WriteLine(ag[posContacto].apellidol);
mod = char.Parse(Consolé.ReadLineO);
if(mod=='s')
{
ag[posContacto].apellidol=Console.ReadLine();
}
Consolé.WriteLine(ag[posContacto].apellido2);
mod = char.Parse(Consolé.ReadLineO);
if(mod=='s')
{
ag[posContacto].apellido2=Console.ReadLine();
}
Consolé.WriteLine(ag[posContacto].dirección);
mod = char.Parse(Consolé.ReadLine O);
if(mod=='s')
{
ag[posContacto].direccion=Console.ReadLine();
}
Consolé.WriteLine(ag[posContacto].telFijo);
mod = char.Parse(Consolé.ReadLineO);
if(mod=='s')
{
ag[posContacto].telFijo=Console.ReadLine();
Consolé.WriteLine(ag[posContacto].telMovil);
mod = char.Parse(Consolé.ReadLine O);
if(mod=='s')
{
ag[posContacto].telMovil=Console.ReadLine();
}
Consolé.WriteLine(ag[posContacto].mail);
mod = char.Parse(Consolé.ReadLine O);
if(mod=='s')
(
^ ag[posContacto].mail=Console.ReadLine();

/*Función eliminar
Elimina un contacto de la agenda.
Parámetros de entrada: agenda, número total de contactos de la agenda y
posición del elemento a eliminar.
Salida: Ninguna.*/
static void eliminar(contacto[] ag, ref int totalContactos, int posEliminar)
for (int i - posEliminar; i < totalContactos 1; i++)

ag[i] = ag[i + i];


}
totalContactos—;

/*Funci6n buscarApellidos
Esta function se utilize en las opciones de eliminar y modificar ya que antes
de realizar estas operaciones se debe localizer el element. Busca un contacto
cuyos apellidos coincidan con los dados.
Parámetros de entrada: agenda, número total de contactos de la agenda y los
apellidos a buscar.
Salida: Posición del elemento buscado o -1 si no encuentra coincidencias.*/
^static int buscarApellidos(contacto[] ag, int totalContactos,string
Capítulo 5. Tipos de datos compuestos 223

string ap2)
{
int buscado = -1;
int i = O;
while (i < totalContactos && buscado == -1)
{
if (ag [ i].apellidol.CompareTo(api) == O
&& ag[i].apellido2.CompareTo(ap2) == 0)
{
buscado = i;

return buscado;

/*Función ordenarApellido
Ordena la agenda. El criterio de ordenación es el primer apellido.
Parámetros de entrada: Array de contactos, primera posición o posición desde
la que empezar a buscar y última posición o posición donde finalizar la
búsqueda.Se usa el algoritmo Quicksort visto para la ordenación.
Salida: Ninguna.*/
static void ordenarApellido(contacto[] ag, int primero, int ultimo)
{
int i, j, central;
contacto tmp;
string pivote;
central = (primero + ultimo) / 2;
pivote = ag[central].apellidol;
i = primero;
j = ultimo;
do

while (ag[i].apellidol.CompareTo(pivote)<0)

while (ag[j].apellidol.CompareTo(pivote)>0)
{

if (i <= j)
{
tmp = ag[i];
ag[i] = ag[j];
ag[j] = tmp;
i++;

) while (i <= j);


if (primero < j)

ordenarApellido(ag, primero, j);


}
if (i < ultimo)

ordenarApellido(ag, i, ultimo);

/*Función menú
Muestra las opciones del programa y devuelve la elección del usuario.
Parámetros de entrada: Ninguno.
Salida: Opción seleccionada por el usuario.*/
static int menú()
{
int opcion;
Consola.WriteLine("1. Insertar contacto.");
Consolé.WriteLine("2. Modificar contacto.");
Consolé.WriteLine("3. Eliminar contacto.");
Consolé.WriteLine("4. Buscar contactos por apellidos.");
Consolé.WriteLine("5. Ordenar contactos por apellido.");
Consolé.WriteLine("6. Mostrar todos los contactos.");
Consolé.WriteLine("7. Salir");
Consolé.WriteLine("Elige opción");
opcion = int.Parse(Consolé.ReadLineO);
return opcion;
}

/*Función mostrarAgenda
Muestra la agenda.
Parámetros de entrada: La agenda y el total de contactos almacenados.
Salida: Ninguna.
*/
static void mostrarAgenda(contacto[] ag, int totalConstactos)
{
for (int i = 0; i < totalConstactos; i++)
{
Consolé.WriteLine("{0} {1},{2}", ag[i].apellidol, ag[i].apellido2,
ag[i].nombre);
Consolé.WriteLine("{O}", ag[i].dirección);
Console.WriteLine("{O)-{l}", ag[i].telFijo, ag[i].telMovil);
Consolé.WriteLine("{O}", ag[i].mail);
Consolé.WriteLine(" ");
}
}

La función principal, Main, incorpora como en otras ocasiones una sentencia repetitiva
(do...while) y una sentencia alternativa múltiple (switch) para que el usuario escoja la operación
a realizar.

ACTIVIDAD 5.16

Crea una función Main que use todas estas funciones de la agenda y muestre las opciones que
se describen en menu().

5.5. ENUMERACIONES
Una enumeración, tipo enumerado o enum es un tipo especial de estructura compuesta por un
conjunto de constantes. Cada elemento de una enumeración estará asociado a un valor de un tipo
de datos concreto excepto char, siendo el tipo de datos por defecto int. En enumeraciones enteras,
el primer elemento de la numeración tiene asignado por defecto el valor 0.

enum Tallas {XS,S,M,L,XL}


En el ejemplo se ha creado una enumeración denominada Talla. En su interior encontramos
los elementos XS, S, M, L, XL. A XS se le asigna por defecto el valor O, a S el 1, así
sucesivamente.
Capítulo 5. Tipos de datos compuestos

Cuando queramos hacer uso de la enumeración escribiremos:

i escogerTalla(Tallas.M) i
I cambiarTalla(Tallas.L) \
En las funciones de ejemplo, cuando en escogerTalla introducimos como parámetro Talla.M
este equivale a 2, es decir, invocar al método de la forma escogerTalla(2) surtiría el mismo
efecto que escogerTalla(Tana.M), sin embargo el segundo de los casos resulta más sencillo y
recordaremos mejor la Talla.M que el valor 2 para esta talla.
Una enumeración:

■ Aporta claridad al código fuente.


■ Facilita el mantenimiento del software.
■ Facilita al programador el desarrollo del programa ya que es más fácil recordar un
texto asociado a un contexto que un número.

5.5.1. DEFINICIÓN DE UNA ENUMERACIÓN EN C#


Para crear una enumeración debemos seguir la sintaxis que se muestra a continuación:
enum nombre_enumeración:tipo{
identificadores=valor;
}
nombre_enumerac¡ón. Es el nombre que le daremos a la estructura.
tipo. Se refiere al tipo de datos numérico que proporcionará los valores a asignar a
cada elemento. Por ejemplo, si el tipo es byte podremos asignar a cada identificador
valores del tipo 2, 4 o 6. El tipo se puede omitir de forma que el tipo por defecto es
int. Podemos usar todos los tipos de datos a excepción del char: byte, sbyte, short,
ushort, uint, int, long o ulong.
identiflcadores. Los nombres de las constantes o literales que van a formar el tipo
enumerado junto al valor que se le asocia. El valor no es necesario establecerlo. Si no
se eseribe el primer elemento de la enumeración tiene asignado el valor O y el resto
los valores eonsecutivos en función de su orden. Podemos alterar la secuencia de
valores si modificamos algún identificador en la lista de constantes.
envim Tallas{
emim Tallas{
XS,S,M=5,L,XL XS=1,S,M,L,XL=0

Hemos creado una enumeración taí que 1 Hemos creado una enumeración tal que
XS=0, S=l, M=5,L=6 y XL=7. XS=1, S=2, M=3,L=4 y XL=0.
A la hora de asignar valores a los identiflcadores de la enumeración podemos usar datos
concretos o expresiones cuyos resultados se encuentren en el rango de valores pennitido por el
tipo de dato asociado a la enumeración.
envun Tallas{ ; Creamos la enumeración llamada Tallas donde S equivale a 1 y los
s=l,M,L, ; valores asociados a XS y XL se obtienen tras realizar las operaciones S-
I 1 y L+J
VT.=T J-1 . J ^ *
5.5.2. USO DE ENUMERACIONES EN C#

A la hora de utilizar los valores de una enumeración debemos especificar su nombre seguido
del operador punto.
I ,

i nombre_enumeracion.nombreJiteras ¡
En nuestra enumeración de ejemplo, a la hora de usar alguna de las tallas escribimos:
Tallas.XS
Tallas.XL

Ya que los elementos de una enumeración están conectados con valores de tipo numérico, ya
sea entero, byte, etc., podemos asignar a variables de estos tipos los literales de una enumeración.
Así podemos encontrar código fuente como sigue:
1 Creamos una enumeración donde
enum Tallas{ I referenciamos las tallas y una estructura que
xs,s,M,L,XL ; almacena información sobre una persona.
j

i En el método principal creamos una


pubiic struct personal I persona, variable, con el nombre yo. A

}
Slic '
i continuación, la variable talla de la
I estructura persona es asignada a una de las
public static void Main(string args[]){ ! tallas válidas de la enumeración:
persona yo; I yo.taIla=Tallas.M; Eso sí, debemos realizar
^ yo.talla=(int)Tallas.M; ! conversión a entero (en este caso) en la
; asignación.
Dado que internamente íos elementos de una enumeración referencian tipos de datos
numéricos, podremos usar los operadores relaciónales, aritméticos, etc., con ellos.
enum CoidigoDeError
{
Opcionincorrecta,
DivisionPorCero,
NumeroGrande

static string textoError(int codiqo)


{
string texto="";
if (codigo == (int)CodigoDeError.DivisionPorCero)

texto="Has intentado realizar una división entre cero ;


}
else if (codigo == (int)CodigoDeError.Opcionincorrecta)
{
texto="La opción escogida no es la correcta";
}
else if (codigo == (int)CodigoDeError.NumeroGrande)
{
texto="El número es excesivamente grande, se sale del rango.";
}
return texto;
Capítulo 5. Tipos de datos compuestos

static void Main(string[] args)


{
int op;
in-t numl, num2;
Consolé.WriteLine("Introduce el primer número");
numl = int.Parse(Consolé.ReadLineO );
Consolé.WriteLine("Introduce el segundo número");
num2 = int.Parse(Consolé.ReadLineO );
do
{

Consolé.WriteLine("MENU");
Consolé.WriteLineC'l. Suma.");
Consolé.WriteLine("2. Resta.");
Consolé.WriteLine("3. Multiplicación.");
Consolé.WriteLine("4. División.");
Console.WriteLine("5. Salir.");
Consolé.WriteLine("Escoge una opción.");
op = int.Parse(Consolé.ReadLine O);
switch (op)
{
case 1: Consolé.WriteLine("La suma es:{O}",numl+num2);
break;
case 2: Consolé.WriteLine("La resta es:{0}", numl - num2);
break;
case 3: Consolé.WriteLine("La multiplicación es:{0}"
, numl * num2);
break;
case 4:
if (num2 == 0)

Consolé.WriteLine(textoError((int)
CodigoDeError.DivisionPorCero));
}
else
{
Consolé.WriteLine("La división es:{0}"
, numl / num2);
}
break;
case 5: Consolé.WriteLine("Gracias por confiar
en nuestra empresa");
break;
default: Consolé.WriteLine(textoError((int)
CodigoDeError.Opcionincorrecta));
break;
}
Consolé.ReadLine();
} while (op != 5);

COMPRUEBA TU APRENDIZAJE
1. ¿Qué es un array? ¿A qué nos referimos cuando hablamos de un array de ima
dimensión?
2. ¿Cuál es la sintaxis básica de creación e inicialización de un array? ¿Qué hace la
palabra reservada new?
3. ¿Cómo accedemos a un array bidimensional?
4. ¿Qué estamos haciendo en la siguiente línea de código fuente: int [][]m=new [][]{new
int[2],new int[12]};?
5. ¿A la hora de pasar como parámetro un array, este es pasado por valor o por
referencia? ¿Y una estructura? ¿Y un elemento concreto del array?
6. ¿Cuál es la sintaxis de un bucle foreach? ¿Cuál es su funcionamiento?
7. ¿En qué consiste una búsqueda binaria? ¿Cómo realiza la ordenación de un array el
algoritmo quicksort?
8. ¿Para qué usamos la propiedad Rank? ¿Y el método getlength()?
9. ¿Podemos modificar un carácter de un tipo string? ¿Para qué usamos el método
CompareToQ?
10. ¿Qué es una estructura? ¿Para qué podemos usarlas? ¿Y una enumeración? Escribe la
sintaxis de creación de una estructura y un tipo enumerado.

ACTIVIDADES DE AMPLIACION
1. Desarrolla una pequeña aplicación que use un array bidimensional como pantalla,
cada casilla es un pixel que por defecto se caracteriza por el carácter *.

* * * * * * * * *

* * * * * * * * * *

El programa tendrá el siguiente menú:


1- Elegir carácter.
2- Cuadrado.

3- Rectángulo.
4- Triángulo.
5- Círculo.

6- Mostrar pantalla.
Veamos cómo debe funcionar. Si escogemos 1 pediremos al usuario que inserte un
carácter que será aquel que usaremos para dibujar (el carácter de dibujo por defecto es
@). Si escogemos cualquiera de las opciones restantes pediremos al usuario la posición
dónde empezar a dibujar (en el caso del rectángulo y el cuadrado es la posición de la
esquina superior izquierda, el triángulo el vértice superior y el círculo el centro).
Capítulo 5. Tipos de datos compuestos

además de las medidas de los lados o radio. Si seleccionamos 2 y posición (1,1) lado 3
y el carácter de dibujo es + debe modificarse nuestra matriz pantalla del siguiente
modo;

( I I 1 \ I I i l I i

La opción 6(mostrar) mosti ará la matriz por pantalla.


2. Realiza un programa que calcule los 6 números para rellenar una quiniela primitiva
controlando que no se repita ninguno de ellos.
3. Crea un programa que almacene los meses del año. Se pedirá al usuario el número de
mes y se visualizará su nombre.
4. Inicializa un array de números reales y ordena este usando uno de los algoritmos de
ordenación vistos.

5. Amplía el programa de la Actividad 4 de fonna que tras ordenar muestre los números
en pantalla separados por guiones.
6. Mediante el uso de arrays pide al usuario un número y muestra por pantalla si este es
palíndromo o no. Debes pedir el número completo, no digito a digito.
7. Realiza un programa que pida al usuario la introducción de un carácter hasta que este
pulse el carácter @. El programa debe mostrar las veces que se ha escrito cada
carácter. Recordad que los caracteres tienen asociado un número o código ASCII que
comienza en cero. Utilizad arrays para hacer el ejercicio.
8. Realiza un programa que pida caracteres al usuario. Finalizará la introducción de
caracteres cuando el usuario pulse @ o haya escrito 20 caracteres. A continuación se
deben mostrar los elementos almacenados en mayúsculas.
9. Realiza un programa que encripte una cadena de caracteres (solo podemos usar
caracteres en minúsculas o números). La encriptación consiste en:
■ Dada una cadena de caracteres, en la cadena encriptada cada carácter será
igual a él mismo más el carácter que se encuentra a continuación. Si nos
encontramos en el último valor le sumaremos 4.
■ Una vez realizada la operación anterior sumaremos 3 a cada carácter
obteniendo finalmente la cadena encriptada definitiva.
10. Realiza un nuevo programa de encriptación tal que se pida al usuario la cadena de
caí acteres a encriptar y la clave de encriptación que también será una cadena (ambas
cadenas compuestas por caracteres en minúsculas o números).
■ Localiza el carácter mayor de la clave de encriptación.
■ Suma el carácter obtenido en el punto anterior a cada carácter de la cadena.
■ Agrega la clave de encríptación a la cadena de fonna que cada carácter de la
clave se posicione cada dos caracteres de la cadena principal. El exceso de
caracteres se colocará al final.

Ejemplo:
Cadena a encriptar: coco.
Clave de encríptación: game.
Carácter mayor de game -> m
coco + m = oUUU.

Agregar game a oÜÜÜ -> goÜaÜÜme.


Cadena encriptada: goÜaÜÜme.
11. Codifica un método que realice una operación similar a indexOf(caracter) pero que
devuelva un array con todas las posiciones donde se encuentra el carácter buscado.
12. Realiza una función que devuelva una cadena de caracteres con la siguiente cabecera
recortarDerecha(cadena,tamaño) que recortará por la derecha el parámetro cadena la
cantidad de caracteres especificado en tamaño, siempre que tamaño sea menor que la
longitud de la cadena de caracteres.
recortarDerechaC'Hola Mundo",4)="Hola M"
13. Realiza una función con la siguiente sintaxis eliminarCaracter(cadena, carácter) que
modifique el parámetro cadena de forma que elimine de este todas las apariciones de
carácter especificado.
eliminarCaracterC'Hola Mundo",'o')="Hla Mund"
14. Realiza una función que elimine los espacios de sobra en una cadena de caracteres
dada.

15. Crea una matriz de dos dimensiones (4x9) tal que se inicialice con números enteros
aleatorios entre O y 100. Muestra por pantalla el mayor, el menor, la media de todos y
la media aritmética por fila.
16. Crea un programa que genere una matriz unitaria. Una matriz unitaria tiene N filas y
N columnas tal que todos los elementos están a O a excepción de la diagonal que está
inicializada a 1.

17. Realiza un programa que pida una matriz al usuario (array bidimensional) y diga al
usuario si es simétrica.
18. Crea un programa que permita gestionar los alumnos de una asignatura. Cada alumno
está definido mediante una estructura con información relativa a su nombre y
apellidos y su nota media. Estos alumnos están almacenados en un array. Calcula la
Capitulo 5. Tipos de datos compuestos

media aritmética de la clase, e indica cuántos alumnos están por encima y por debajo
de la nota media calculada.

19. Desarrolla un programa que gestione los empleados de una empresa, de cada
trabajador se debe almacenar: nombre, edad, cargo (puesto de trabajo) y sexo. Realiza
las siguientes operaciones:
■ Insertar, eliminar y modificar un empleado.
■ Mostrar el número de hombre y mujeres que hay en la empresa.
■ Mostrar la edad media de los empleados. Además, calcular la edad media de
hombre y mujeres por separado.
■ Mostrar el nombre del empleado más joven y del más viejo.
20. Desarrolla un programa tal que dado un DNI averigua la letra que le corresponde para
formar el NIF y muestre este por pantalla. Tendremos en cuenta la siguiente
infonnaeión:

Debemos obtener un número entre O y 23, ambos inclusive, para obtener un valor en
la tabla equivalente de caracteres que vemos a continuación. El numero de 1 a 20 se
calcula dividiendo el DNI entre 23 y trabajar solo con la parte entera. Multiplicamos a
la parte entera por 23 y restamos el DNI original con el DNI calculado.
0='T' 7='F' I4='Z' 21='K'
1='R' 8='P' 15='S' 23='E'
2='W' 9='D' 16='
3='A' IO='X' I7='V'
4='G' I1='B' 18='H'
5='M' 12='N' 19='L'
6='Y' 13='J' 20='C'
CAPITULO 6

PROGRAMACION ORIENTADA A OBJETOS

CONTENIDOS OBJETIVOS

Programación orientada a Entender qué es la programación


objetos. Fundamentos. orientada a objetos. Sus elementos
Clases y objetos. Atributos y principales.
métodos. Constructores, miembros Conocer la funcionalidad de
estáticos, destructores, la referencia constructores y destructores.
this.
Entender por qué usar arrays de
Arrays de objetos. objetos y su potencial.
Propiedades. Indizadores y Saber en qué consisten las
sobrecarga de operadores. propiedades, indizadores y la
sobrecarga de operadores.

RESUMEN DEL CAPITULO

En este capítulo comenzamos el estudio de la programación onentada a objetos que


nos pennitirá afrontar problemas más complejos y cercanos a la vida cotidiana. Se
definirán y usarán los elementos esenciales de este paradigma de la programación: clases,
objetos, constructores, destructores, miembros estáticos, sobrecarga de operadores, etc.
6.1. PROGRAMACION Y ABSTRACCION
En el Capítulo 4 comenzábamos a estudiar el término abstracción aunque no
profundizábamos demasiado en él. En este capítulo vamos a hacer un mayor hincapié en él ya
que caracteriza el nuevo paradigma de la programación, la POO (Programación Orientada a
Objetos).
La abstracción permite encapsular el código y aislar este del propio diseño, de forma que
cuando usamos el software sabemos qué hacer con él, pero no sabemos qué código se ha escrito
para ello.
Existen diferentes mecanismos de abstracción:

■ Procedimientos y funciones. Este fue el primer mecanismo de abstracción, lo hemos


estudiado en el Capítulo 4. Podíamos encapsular código en bloques denominados
funciones y procedimientos aunque este tipo de abstracción no es demasiado bueno.
Las funciones y procedimientos se ubican normalmente junto al programa principal
que los utiliza.
■ Módulos. Los módulos proporcionan una abstracción más robusta ya que tanto datos
como procedimientos pueden ubicarse en una zona privada no accesible por aquellos
que desearan usar la funcionalidad de los elementos abstraídos. Así podíamos tener
una zona privada, archivo independiente en el que datos y procedimientos se
declaraban por separado. No existe relación directa entre variables y funciones más
allá de que algunas podían ser usadas en los procedimientos. Los componentes de
una zona privada se usaban en la misma zona, mientras que se podía desarrollar una
zona pública que utilizará elementos privados y pudiera ser a su vez utilizada en el
programa principal.
■ Tipos abstractos de datos. Un tipo abstracto de datos o TAD es un tipo definido por
el programador que puede ser usado de forma similar a los tipos de datos incluidos en
un lenguaje de programación concreto. Es un tipo de datos creado por el
programador, que incluye rango de valores que pueden usarse, así como métodos para
la manipulación de esta información. A la hora de crear un tipo abstracto de datos:
—>■ Se establece una definición del tipo que queremos realizar.
—Se definen un conjunto de operaciones que manipularán los valores asociados
al tipo de datos.
Se usan mecanismos de protección de los datos, de forma que solo puedan
manipular estos las funciones desarrolladas para este menester.
-> Se permite que el tipo de dato pueda ser instanciado, de fonna que pueda ser
usado en diferentes contextos.
■ Objetos. Un objeto es un tipo abstracto de datos al que se añaden nuevas
características. Se incorporan términos como paso de mensajes, métodos, herencia o
polimorfismo. Todos estos términos se irán estudiando en los apartados de este
capítulo.
Capítulo 6. Programación orientada a objetos 235

Codigo fuente

Función 1 Modulo 1 Modulo 2

Función 2

N Función
2
Función 1
1

Programa principal
Función 1.

Función 2.

Programa principal:
Función 2

Figura 6,1, A la izquierda, uso de funciones y procedimientos y a la derecha programa estructurado en


módulos.

Tipo abstracto de datos Programa principal

Definición del TAD

Uso operación 1 sobre el


Operación 1
TAD

Uso operación 2 sobre el


Operación 2 TAD

Uso operación n sobre el


Operación n
TAD

Figura 6.2. Tipos abstractos de datos. A la izquierda el desarrollo de un TAD con valores y operaciones a
usar sobre las variables. A la derecha uso del TAD en un programa. Se declara el tipo y posteriormente
usamos este.
236 Programación

Objeto

Atributos

Métodos

Mensaje

Hereda

Objeto Objeto

Atributos Atributos

Métodos Mensaje Métodos

Figura 6.3. Objeto.

6.1.1. TIPOS ABSTRACTOS DE DATOS

Un tipo abstracto de datos abarca un conjunto de valores y operaciones definidos mediante


una especificación independiente de cualquier representación, es decir, el TAD corno tal
dependerá de la especificación que se haga de él, nunca de la implementación en un lenguaje de
programación determinado.
Por ejemplo, cuando se definen los números enteros, sabemos el rango de valores que
podemos usar y las operaciones, pero en ningún momento sabemos cómo se realizan estas
operaciones, como se implementan. Además, existen múltiples formas de implementar un mismo
TAD.

Para entender qué es un TAD e introducir la programación orientada a objetos vamos a


desarrollar un TAD para los números complejos.
A la hora de desarrollar un tipo abstracto de datos debemos saber;
■ Qué nombre vamos a asignarle.
■ Qué valores va a usar o valores permitidos.
■ Qué operaciones pueden aplicarse a los valores establecidos.
Si nuestro objetivo es realizar una especificación para los tipos de datos complejos:
Capitulo 6. Programación orientada a objetos

■ Su nombre puedes ser complejo.


■ En cuanto a los valores pemiitidos, un número complejo está formado por números
reales, de forma que el rango de números reales será el peraiitido para nuestro tipo de
datos.

■ Para finalizar, las operaciones' que podemos realizar con números complejos son:
—> Crear un número complejo a partir de dos números reales dados.
—> Sumar, restar y multiplicar números complejos.
—> Extraer la parte real de un número complejo.
Extraer la parte imaginaria de un número complejo.
Así, la especificación fonnal sería:

especificación complejos :
usa: reales '• i
tipo: complejo :
operaciones: i
. complejo: real X real-> complejo i
suma: complejo x complejo -> complejo . ,j
resta: complejo X complejo-> complejo : i
multiplicación: complejo X complejo-> complejo j
parteEntera: complejo -> real '\
partelmaginaria: complejo -> real I
variables: :
x,y,z,p: reales i
ecuaciones: •
suma(complejo(x,y); complejo(z,p)) = complejo(x + z, y + p) i
resta(complejo(x,y); complejo(z,p)) = complejo(x - z, y - p) ;
multiplicación(complejo(x,y); complejo(z,p)) = complejo ((x*z)-(y*p), i
(y*z)+(x*p)) ;
parteReal(complejo(a, b)) = a i
parteImaginaria(complejo(a,b)) = b i
fin-especificación . .. i

La especificación denota que usaremos números reales para definir cada parte de un número
complejo (real e imaginaria), se observa en el apartado usa. Indica para cada operación su
nombre, parámetros de entrada y salida, por ejemplo: suma: complejo x complejo -> complejo
denota que suma es el nombre de la operación, se usarán dos números complejos (complejo x
complejo) y la salida de la función será otro número complejo (-> complejo). Además, en el
apartado ecuaciones se detalla en qué consistirá cada operación antes definida.
Este es el TAD de números complejos, ahora podrá ser implementado en cualquier lenguaje
de programación y cada método podrá realizarse de varias formas, sin embargo, lo
verdaderamente importante es la especificación, no la posterior implementación.

Podrían incluirse algunas más pero no vamos a profundizar en exceso en este tema.
Un TAD simplemente reconoce los valores y operaciones que se pueden realizar, cuando
hablamos de objetos, en esencia es básicamente un tipo abstracto de datos al que se agrega
términos como herencia, polimorfismo, paso de mensaje, etc.

ACTIVIDAD 6.1

Implementa en C# la especificación de números complejos dada. En Visual Studio crea un


nuevo proyecto y aunque no es así como se programa, crea una función por cada operación y una
estructura llamada complejo. Una vez tengas todo, usa la función principal para utilizar el tipo
abstracto de datos.

ACTIVIDAD 6.2

Busca en la web o bibliografía aportada por el profesor otras especificaciones sencillas de


TAD.Implementa alguna en C#.

6.2. PROGRAMACIÓN ORIENTADA OBJETOS


La programación orientada a objetos también conocida como POO es el paradigma de
programación que prima hoy día ya que refleja y se ajusta mucho mejor a la realidad que rodea al
software. En la programación orientada a objetos se da mucha más importancia a los datos, y se
definen operaciones que modificarán de algún modo o utilizarán estos.
Uno de los objetivos de la programación orientada a objetos es reflejar la realidad, de fonna
que los elementos de un programa se ajusten a elementos de la vida cotidiana. Por ejemplo,
supongamos que queremos realizar una aplicación para un taller de vehículos. En un programa
estructurado definiríamos funciones independientemente de los datos, tal que por un lado
crearíamos el código de la función y posteriormente en el programa principal se establecería una
variable o variables sobre la que aplicaríamos cambios, podría ser un array de una estructura en
la que guardáramos tipo de vehículo, matrícula, color, etc. En programación orientada a objetos
creamos un objeto que simula un coche con sus características generales y las funciones
incluidas, tal que en la función principal crearemos variables de este objeto y usaremos los
métodos aplicados a unos datos concretos en cada momento (véase la Figura 6.4).
La programación orientada a objetos permite la creación de software cada vez más complejo a
partir de unidades o bloques de código reutilizable. Las propiedades más importantes de la
orientación a objetos son:
■ Abstracción. En orientación a objetos mostramos únicamente las características
esenciales, qué hace el objeto y para qué se crea, abstrayendo elementos como la
implementación.
■ Encapsulamiento. En la orientación a objetos se oculta y asegura la infonnación.
■ Medularidad. La orientación a objetos es modular, tendremos diferentes objetos
independientes del resto y reutilizables.
■ Jerarquía. En orientación a objetos encontraremos una jerarquía de elementos tal
que unos objetos descienden de otros, como ocurre en la vida real, adquiriendo sus
características.
Capítulo 6. Programación orientada a objetos 239

■ Polimorfismo. En orientación a objetos podremos usar operaciones sobre diferentes


objetos tal que el resultado dependerá del elemento que se esté referenciando en ese
momento.

6.2.1. ABSTRACCIÓN
La abstracción permitirá recolectar y representar las características esenciales de un objeto,
dejando atrás aquellas que no tienen tanta importancia. Además, se centra en el objeto tal y como
lo conocemos en la vida real, de fomia que nos centramos en lo que es capaz de hacer pero no en
cómo lo hace. Básicamente, definiríamos la abstracción como la fonna de describir una entidad
del mundo real sin importar la complejidad que está presente y el poder utilizar esta en cualquier
aplicación.

Función cambiarColor (coche)! características:


color
ruedas usa objeto coche

Función cambiarAceite(coche){ Funciones


Función Principal {
Función cambiarColor (){ coche A, B.
}
A->cambiarAceite()
B->cambiarAceite()
B-^cambiarColor()
Función cambiarAceite {){
Función repararChapa(coche}{

estructura coche{
colora- Función repararChapa (){
ruedas;
}

Función Principal {
coche A, B.
cambiarAce¡te(A)
cambiarAce¡te(B)
cambiarCo!or(B)

PROGRAMA ORIENTADO A OBJETOS

Figura 6.4. Ejemplo de codificación para un taller de vehículos. A la izquierda un modelo estructurado a
la derecha un modelo orientado a objetos.

6.2.2. ENCAPSULAMIENTO

El encapsulamiento se refiere a la ocultación de la información, de forma que los datos


internos de un objeto están ocultos al mundo exterior, solo se conoce de él su esencia, es decir,
qué podemos hacer con él.
Por ejemplo, con frecuencia en capítulos anteriores hemos desarrollado código fuente,
proyectos, en los que hemos usado la clase Consolé, más concretamente el método WriteLlneO.
Consolé se encuentra dentro del espacio de nombres System siendo una clase" de este paquete.

Podemos definir por el momento clase como versión genérica de un objeto.


Si en Visual Studio usamos el Examinados de objetos, este se hace visible haciendo doble clic
en el Explorador de soluciones, References -> System, podemos buscar la clase Consolé y a su
vez WriteLine. Observaremos que solo conocemos de este método lo esencial:
■ Su nombre.

■ Los parámetros de entrada que puede admitir.


■ Resumen de su funcionalidad.

Se encuentra encapsulado en el paquete, tal que no podremos acceder a su implementación o


a características no tan importantes.

I Pfogram.cs
Examinar: Mi solución

System.Conio!e . © V/riteLine{str¡ng, object object)


^ iS Tipos base 0 V/riteLine(string, object)
Object
d' MiCfCsoft.V/in32.SessionSvvitchReason.ConsoleConnect
© WriteLine(object)
t? MicrosoftW¡n32.SessionSwitchReason.ConsoleDisconnecti
O WtiteL¡ne(ulong)
^ System.ConsoleCancelEventArgs
> a System.ConsoleCancelEvenfHandler
t> r? System.ConsoleColor T. "
. ^public static void WriteUinefstrino valué)
...nilh If Ctatir-VOltl Wntol inofi;

Miembro de Svstom.Consolé

Resumen:
Escribe el valor de cadena especificado, seguido del terminador de linea actual, en el
Tlujo de salida estándar.

Parámetros:
valué: Valor que se va a escribir.

Excepciones:

Figura 6.5. Clase Consolé junto a método WriteLine en el paquete System,

6.2.3. MODULARIDAD
Esta propiedad se refiere a la forma en la que los elementos en programación orientada a
objetos se encuentran organizados en módulos, facilitando asi la encapsulación y abstracción de
la información.

La medularidad permite dividir una aplicación en partes. Estas partes deben ser tan
independientes como sea posible del resto así como de la aplicación principal. A cada parte se la
denomina módulo. Al crear módulos independientes de la propia aplicación, estamos
contribuyendo a la creación de código reutilizable, tal que si hemos desarrollado un módulo para
un programa concreto, esta pueda usarse en cualquier otro. En nuestro ejemplo de aplicación de
taller de vehículos, si desarrollamos un módulo o clase llamado coche, si es independiente puede
utilizarse en este programa como en un concesionario.
Capitulo 6. Programación orientada a objetos 241

Abs() Min() Max() WriteüneO ReadLine()

Programa 1 Programa 2

int num=¡nt.Parse(Console.ReadLine()); int valor=int.Parse(Consoie.ReadLine());


Math.Abs(num); Math.Abs(valor);
Console.WriteL¡ne(num); Console.WriteLine(valor);

Figura 6.6. Módulos MATH y CONSOLE usados en diferentes programas.

6.2.4. JERARQUÍA
Mediante la jerarquía se ordenan las abstracciones u objetos de forma que se establecen
relaciones de algún tipo entre ellas. Podemos hablar de herencia entre clases tal que los
elementos de una clase son heredados por otra o incluso sus métodos pueden ser sobrecargados.
Supongamos en nuestra aplicación para el taller de vehículos que debemos además de
mantener información sobre los eoches, motocicletas, etc., que se reparan, controlar datos
relacionados con los empleados, personal en general de la empresa. En el taller cada empleado o
grupo de empleados está asociado a un departamento, y todos tienen un sueldo y un rango. Así,
podríamos desaiTollar una abstracción denominada Empleados donde almacenemos datos
relacionados con todos los empleados independientemente de su función y un objeto por cada
tipo de empleado, teniendo en cuenta para definir el tipo el departamento en el que se ubica.
Finalmente, podríamos desarrollar una jerarquía de clases como la que se observa en la Figura
6.7.

EMPLEADOS
■ Nombre y apellidos.
■ Dirección.
■ Teléfono.
■ Sueldo base

MECANICO DIRECTOR

Horas extra. Viajes programados.


Plus por reparación Citas programadas.

Figura 6.7. Jerarquía (herencia) entre clases.


242 Programación

De todos los empleados almacenaremos información relativa a su nombre y apellidos,


dirección o teléfono, sin embargo, la información relacionada con horas extras o viajes no
compite a todos los trabajadores. Todos los elementos comunes se ubican en una superclase o
superelemento y cada subclase incluye en su implementación componentes propios de ella.

6.2.5. POLIMORFISMO

El término polimorfismo es una palabra griega que significa muchas formas. La palabra es
ideal para definir esta característica, ya que en programación orientada a objetos podemos tener
métodos con igual nombre pero con implementaciones diversas tal que dependiendo del objeto
que se use, este método o función realizará una u otra operación.
El polimorfismo se usa bastante junto a la herencia, tal que cuando una clase deriva de otra
hereda sus características y operaciones. En la clase que hereda se puede redefinir cualquier
método heredado, de forma que en función de la referencia a objetos que usemos en cada
momento se estará ejecutando un método u otro.
"Un mismo mensaje enviado a objetos diferentes dará como respuesta salidas diferentes", es
decir, si tenemos varios objetos con métodos similares, según se referencie uno u otro objeto el
resultado final será diferente. Cuando tenemos varios objetos que derivan o heredan de un objeto
base se aplica extensamente esta definición al usar arrays del objeto base. Véase la Figura 6.8.
EMPLEADOS
■Nombre y apellidos.
■ Dirección.
■ Teléfono.
■ Sueldo base

MECANICO DIRECTOR

Calcular sueldo - Calcular sueldo

Array de tipo EMPLEADOS donde almacenamos MECÁNICOS y DIRECTORES

Mecánico Mecánico Director Mecánico Director Mecánico

El método Calcular sueldo que se ejecutará en cada caso depende de la referencia a objeto

Figura 6.8. Ejemplo de polimorfismo.

6.2.6. REALIZACIÓN DE PROGRAMAS ORIENTADOS A


OBJETOS
A partir de ahora, cuando tengamos que resolver un problema, debemos empezar a pensar de
forma diferente a como lo hemos estado haciendo. En programación orientada a objetos
seguiremos usando los mecanismos de programación estructurada tales como sentencias
repetitivas, alternativas, etc., sin embargo debemos plantear un sistema donde los datos presenten
una mayor importancia.
Capítulo 6. Programación orientada a objetos 243

El objetivo de la orientación a objetos es plasmar la realidad de forma que programaremos los


elementos lo más próximo posible a como lo vemos en su enlomo natural. Así, cuando tengamos
que afrontar un problema de este tipo:
■ Nos preguntaremos sobre aquellos elementos de los que realmente es interesante
almacenar infomiación, es decir, localizaremos los posibles objetos de nuestro
programa.

■ De cada elemento debemos conocer cuáles son sus características o cuáles son las
características que lo representan.
■ De cada elemento debemos conocer las operaciones a realizar ya que cada conjunto
de datos permitirá la realización de un conjunto de operaciones.
■ Finalmente, desarrollaremos un enlomo donde usaremos los elementos configurados.
Por ejemplo, a la hora de desarrollar la aplicación para nuestro taller de reparación de
vehículos, podemos resolver las siguientes cuestiones para determinar los elementos que vamos a
incluir:

1. ¿Qué elementos del taller necesitan ser guardados o tenidos en cuenta en la


aplicación? Los vehículos que serán reparados y los empleados.
2. ¿De cada elemento, qué datos nos interesan? De los vehículos: la hora de entrada en
el taller, daños que presenta, matrícula, marca y modelo, etc., de cada empleado:
nombre y apellidos, dirección, sueldo base, cargo, reparaciones asociadas, etc.
3. ¿A cada elemento, qué operaciones vamos a asociar? A un vehículo podemos cambiar
el aceite, reparar la chapa, cambiar el color, poner a punto, etc., a los empleados
podemos modificar el sueldo base, establecer horas de trabajo, etc.
4. En cuanto a la aplicación principal que use estos objetos, podemos plantear un
programa que almacene de fonua dinámica los vehículos que van entrando en el
taller, así como los empleados que trabajan en él y estructurar la fimcionalidad
mediante un menú de opciones que permita usar las operaciones anteriormente
planteadas para cada objeto.
Veremos en las actividades que iremos desarrollando, que no es complicado comenzar a
pensar en objeto y una vez nos hayamos familiarizado con este paradigma es la mejor de las
opciones de programación.

6.3. CLASES Y OBJETOS


Una clase es la definición de un objeto, es el elemento que describe los componentes de im
objeto a modo general. Decimos que hemos creado un objeto cuando damos valores y usamos los
componentes definidos en la clase.
Por ejemplo, si continuamos usando nuestro taller de vehículos, una clase podría ser la clase
coche, en la que se describe de fonna general este elemento de la vida cotidiana. Se establecería
que un choche se caracteriza por:
■ Tener ruedas de unas características.
■ Tener una matricula.

■ Tener un número de puertas.


■ Ser de una marca y modelo concreto.
■ Etc.

Ahora bien, si en nuestro taller hace entrada un Seat Arosa de tres puertas, las características
antes mencionadas adoptan ciertos valores:
■ Ruedas tipo X.4 más la de repuesto.
■ Matrícula H1467W.

■ 3 puertas.
■ Seat. Modelo Arosa.

■ Etc.

Así tenemos la clase coche y el objeto Seat Arosa.


A la hora de crear una clase definimos lo que denominamos propiedades o atributos y
métodos de clase.

■ Atributos. Un atributo se refiere a una característica concreta de un objeto de la vida


cotidiana. Cuando se establece un atributo en la clase, se define como cuando
creábamos variables en nuestros programas estructurados, indicando tipo de datos y
nombre con el que reconoceremos la característica.
■ Métodos de la clase. Los métodos dan funcionalidad a la clase, es decir, reflejan las
operaciones que esta puede realizar sobre los atributos. Los métodos son similares a
las funciones o procedimientos que creábamos en programación estructurada pero
orientados a modificar y actuar sobre las propiedades de la clase.
Un objeto da valores concretos a los atributos que define la clase y usa sus métodos. Cuando
rm objeto utiliza un método concreto se dice que está mandando un mensaje. Esto en cierto
modo tiene su lógica ya que estamos dando una orden a un componente que refleja la realidad. Si
en el taller tenemos que cambiar el color de un vehículo, diremos cambia el color al operario de
tumo,le estamos mandando el mensaje de que tiene que realizar esa acción concreta.
A la hora de mandar un mensaje a un elemento concreto de un objeto usaremos el operador
punto, por ejemplo, arosa.cambia_color.
A menudo damos el nombre de estado a los valores que en un objeto toma en un momento
concreto de la vida del programa donde se está usando. Además, cuando creamos un objeto,
damos valores a las propiedades de la clase, decimos que estamos instanciando esta.

Figura 6.9. Estructura de una clase.


Capítulo 6. Programación orientada a objetos 245

verde, seat, arosa, arosa.poner a punto


hl467w, etc.

Atributos
Objeto Ceed
color, marca,
modelo, matrícula,
blanco, kia, ceed,
etc.
1667cvf, etc. ceed.cambiar color

II' Métodos.
cambiar color,
cambiar aceite, poner
a punto, etc.

. Objeto fiesta

negro,ford,fiesta,
fiesta.cambiar aceite
2374cmg, etc.

Figura 6.10. Atributos, métodos de clase y mensajes.

6.4. DEFINICION DE CLASES EN C#


La sintaxis básica para definición de clases en C# es la que muestra a continuación:

■ class. Palabra reservada del lenguaje que indica el inicio de la definición de una clase.
■ nombre. Nombre con el que será conocida la clase.
■ atributos. Características que definen la clase.
■ métodos. Funciones u operaciones sobre los atributos permitidas por la clase.
Los miembros de una clase: atributos y métodos, puede declararse como públicos (public) o
privados (prívate). Estos modificadores de visibilidad detenninarán el acceso a un miembro de
la clase.

■ Miembros públicos. Son elementos accesibles desde fuera de la clase. Recordamos


que una de las características de la orientación a objetos era la encapsulación de la
información. Así, si no se especifica que un miembro es publico, este será solo
visible, accesible, es decir, solo podrá ser usado por otros miembros de la clase. En
cambio, si se define como público estamos indicando que otros objetos puedan
realizar llamadas a estos miembros.
■ Miembros privados. Estos componentes solo podrán ser usados por otros miembros
de la clase, nunca por otras clases o la interfaz principal donde se instancien estas. Por
defecto, al definir un método o atributo, si no especificamos modificador de
visibilidad, este será privado.
Existen otros modificadores de visibilidad que usaremos en determinadas situaciones:
■ Miembros protected. Un miembro protected define un elemento privado con
matices. Usamos este modificador cuando trabajamos con varias clases tal que unas
heredan de otras. Así, cuando existe herencia (término que estudiaremos más
profundamente), los miembros que queremos sean privados suelen declararse como
protected, de forma que seguirán siendo privados a clases que no tienen ninguna
relación con la que los incluye pero sin embargo, podrán ser usados por las clases que
heredan.

■ Miembros interna!. Definimos un miembro intemal cuando queremos que este sea
accesible solo dentro del fichero donde se ha creado. Por ejemplo, si creamos un
atributo intemal, solo podrá ser usado en el mismo fichero o ensamblado. Si
tuviéramos una instancia de la clase en otro fichero, no podríamos modificar o usar
este atributo. En cierto modo son miembros públicos para su fichero pero privados
para el resto.
■ Miembros protected intemal. Son miembros accesibles desde la clase que los
contiene a sus derivadas dentro del ensamblado al que pertenecen.

6.4.1. ATRIBUTOS EN C#
Un atributo recoge una característica concreta de un objeto y no es más que una variable que
se ajusta a ella, definida mediante un tipo de datos y un nombre.
I - -I

I tipo_dato_atributo nombre_atributo; ;
■ tipo_dato3tributó.""Éf tipó de'd^^^ ser cualquiera de los que ya hemos
estudiado. Podemos usar tipos de datos básicos como int, double o cbar, tipos de
datos complejos: estructuras o arrays y otros objetos. A la hora de usar como tipo
de dato un objeto, podemos utilizar los objetos predefinidos e incluidos en C# o bien
objetos creados por el propio usuario.
■ nombre_atributo. Es un identificador válido. Este nombre debe ser representativo y
orientativo del valor o tipo de información que almacenará.
I class empleado { |
I string nombre; //Atributo que almacenará el nombre del empleado 1
I string apellidol; I
; string apellido2; i
I string NIF; //Atributo que almacenará el NIF del empleado I

6.4.2. MÉTODOS EN C#
Un método se puede definir como función o procedimiento de una clase. Bloque de código al
que se le asocia un nombre igualmente representativo y que puede procesar parámetros y
Capítulo 6. Programación orientada a objetos 247

devolver valores del procesamiento dado. Desde los métodos podremos acceder libremente a los
atributos de la clase, cambiando estos o usando su contenido. La sintaxis básica de im método es:

tipojievuelto nombre_niétodo(parámetros){ i
sentencias; I
} \
■ tipo_devueIto. Tipo de dato de la variable que será devuelta por el método, en caso
de que se use la instrucción return en algún momento en el código. Si el método no
devuelve ningún valor usaremos la palabra reservada void como cuando codificamos
funciones en programación estructurada.
■ nombre_método. Nombre que daremos al método. Cuando tengamos que ejecutar tm
conjunto de instrucciones concretas, mandaremos im mensaje al objeto usando este
nombre.

■ parámetros. Pares (tipo, nombre) que reflejan variables que serán pasadas al método
para que puedan ser usadas en sus sentencias. Recordamos que si existen variables
extemas, necesitaremos acceder a ellas de algún modo.
■ sentencias. Conjunto de instmcciones, operaciones, que debe realizar el método.

NOTA: A la hora de definir la visibilidad de los atributos y métodos, los primeros se


establecen como privados mientras que los segundos como públicos. La definición de los
atributos no tiene por qué incluir la palabra reservada prívate ya que lo serán por defecto,
escribiremos public delante de cada método, siempre que este sea usado desde la interfaz
principal, si son métodos internos a la clase también se declararán como privados.

NOTA: Recomendamos usar un fichero .es por cada nueva clase que vayamos a
incluir en nuestro software. Todas ellas bajo el mismo espacio de nombres.

class empleado {
string nombre; //Atributo que almacenará el nombre del empleado
string apellidol;
string apellido2;
string NIF; //Atributo que almacenará el NIF del empleado
double sueldoBase;

public double calculaSueldo(int horasExtra, double precioHora) {


double sueldo=sueldoBase;
sueldo=sueldo+horasExtra*precioHora;
return sueldo;

Véase la Figura 6.11.


Existen diferentes tipos de funciones miembro:
■ Constructores y destructores. Se ejecutan de forma automática cuando se crea o se
destruye un objeto.
■ Selectores. Funciones diseñadas para devolver los valores de los atributos miembros
de la clase.
■ Modificadores. Permiten que a través de ellas el programa principal u otros códigos
fuente puedan cambiar los valores de los atributos miembros de la clase.
■ Operadores. Sobrecargas de operadores ya existentes de fonna que se le aplica una
nueva funcionalidad para que realice alguna operación cuando se aplique sobre la
clase que los usa.
■ Iteradores. Se usan para procesar colecciones de objetos como arrays.

6.4.3. INSTANCIAS EN C#

Una vez que se ha declarado una clase con sus atributos y métodos, el siguiente paso es la
instanciación de esta, es decir, uso de la clase, creación de un objeto de clase.
La instanciación consiste en la creación de una variable de una clase, un objeto. A la hora de
crear un objeto, la sentencia que usaremos estará compuesta de definición y creación.

INTERFAZ PRINCIPAL

EMPLEADOl

Juan, Orellana,
Moro, 29876543L,
1200
EMPLEADO empleado EMPLEADOl
CalculaSueldo Jempleado EMPLEAD02
Nombre, Apellidol,
Apell¡do2, NIF, Console.WriteLIneC'Sueldo;",
sueldoBase

CalculaSueldo
a EMPLEAD02
EMPLEADQl.CalculaSueldo(10,12);

Console.WriteLine("Sueldo:",
Isabel, Jiménez, EMPLEAD01.CalculaSueldo(0,0);
Pérez, 29871243L,
1400

CalculaSueldo

Figura 6.11. Clase empleado. Instancias, programa principal y uso de los métodos.
En la definición indicamos la clase que vamos a instanciar, mientras que en la creación se
produce la reserva real de memoria para la variable.
I

; nombre_de_clase nombre_instancia—new nombre_de_clase(parámetros); i


La palabra new se encarga de la reserva de memoria y devuelve una referencia a la dirección
de memoria donde será almacenado el objeto. Así, los objetos son tipos referencia.
■ nombre_de_cIase. En la primera aparición se refiere al nombre de la clase que se va
a instanciar. En el segundo caso se refiere al constructor de la clase, que debe tomar
el nombre de esta. El constructor es un método especial de la clase que estudiaremos
en el siguiente apartado y como método puede recibir tantos parámetros como le sean
necesarios.
Capítulo 6. Programación orientada a objetos 249

■ nombre_¡nstancia. Nombre de la variable de objeto o instancia.


empleado Isabel=new empleado("Isabel","jiménez");

El espacio reservado en memoria para un objeto dependerá de la cantidad de elementos o


miembros que contenga. Cuando un objeto no está siendo referenciado es sacado de la memoria
para liberar espacio gracias a lo que se denomina recolector de basura.
Cuando creamos un objeto de una clase, cada atributo será inicializado a los valores por
defecto del tipo de datos usado o bien a un valor concreto establecido en la función constructora.

6.4.4. CONSTRUCTORES EN C#
Un constructor es una función miembro de una clase que se ejecuta de forma automática
cuando se crea una instancia de esta. El constmctor de una clase se diferencia del resto porque
debe llamarse igual que la clase (respetando mayúsculas y minúsculas) y nunca se especifica en
su definición un tipo de datos devuelto, ni siquiera void.
El constructor admite parámetros, siendo un método que puede sobrecargarse, es decir,
podemos tener varios contructores con diferente número o tipo de parámetros.
Los constructores tienen como objetivo principal la inicialización de las variables o atributos
de la clase.

No es obligatorio definir un constructor en cada clase, aunque se suele hacer. Así


establecemos los valores que deseamos para cada atributo de forma automática en la creación del
objeto, en lugar de hacerlo en otro momento mediante métodos. Ya que la esencia de la
instanciación es el asignar valores a los atributos de la clase, es en cierto modo lógico desarrollar
un constructor que realice esta operación en la creación del objeto.
class empleado {
nombre; //Atributo que almacenará el nombre del empleado
string apellidol;
string apellido2;
string NIF; //Atributo que almacenará el NIF del empleado
doijble sueldoBase;

empleado(){ //Constructor sin parámetros


NIF="00.000.000-A";
}
public empleado(string n, string apl){ //Constructor con parámetros
nombre=n;
apellidol=apl;
}
double calculaSueldo(int horasExtra, double precioHora){
doiible sueldo=sueldoBase;
sueldo=sueldo+horasExtra*precioHora;
return sueldo;

Cuando no definimos un constructor en una clase los atributos tomarán por defecto valores
asociados a sus tipos de datos. Por ejemplo, si tenemos un atributo de tipo entero, se le asignará
por defecto el valor O, en caso de un atr ibuto de una clase, el valor por defecto será nuil.
NOTA: La palabra nuil es un literal que representa una referencia vacía, es decir, una
variable referencia que no apunta a ningún objeto, ningún espacio en memoria. Cuando
trabajamos con tipos de datos int el valor por defecto es O, es un tipo de dato valor; en
cambio, los arrays u objetos son tipos de dato referencia tal que al inicializarse deben
apuntar a una referencia de memoria donde se almacena un objeto, si no asignamos ninguna
referencia la inicialización por defecto será a nuil.

6.4.5. ACCESO A LOS MIEMBROS DE UNA CLASE EN C#

Podremos acceder a los miembros de una clase en función de su visibilidad y el lugar donde
se quiera llevar a cabo el acceso. Al comienzo del Apartado 6.4 estudiábamos los diferentes
modificadores de visibilidad y el efecto que producían sobre sus métodos miembros.
Recordamos que podemos usar las palabras public, prívate, protected, internal o protected
internal. Normalmente estableceremos como públicos los métodos miembros y privados los
atributos.

Para acceder a un elemento de la clase usaremos el operador punto (.) de la siguiente forma:
■ — —-—-— — * —1

objeto.ati'ibuto; :
objeto.método; •
Si los miembros de la clase son privados, al realizar el acceso desde otra clase se producirá un
error sintáctico. Estos deben ser públicos o protegidos en el caso de que la case derive de la que
contiene los miembros a los que se quiere acceder.
6.5. EJEMPLO DE DESARROLLO DE UN PROYECTO
CON CLASES EN C#
Una vez se han definido los elementos principales de un programa orientado a objetos vamos
a desarrollar en C# un ejemplo para que queden claros los conceptos y además el lector sepa
cómo estructura un proyecto en diferentes ficheros o clases.
En el Capítulo 5, mediante el uso de estructuras, se creaba una aplicación que mantenía los
contactos de una agenda. Vamos a retomar este ejemplo, y realizar un objeto que refiera cada
contacto a almacenar.

6.5.1. CONFIGURACIÓN DEL PROYECTO EN 0#


Antes de comenzar a programar nuestra clase, vamos a preparar el entorno de trabajo;
1. Accedemos a Visual Studio y creamos un nuevo proyecto al que llamaremos agenda.
El tipo de proyecto que usaremos: Aplicación de consola, nombre agenda.
2. En el explorador de soluciones clic con el botón derecho del ratón sobre el proyecto
que acabamos de crear (agenda).
3. En el menú contextual Agregar... y a continuación clic en Clase.
4. Aparece una ventana con todos los nuevos elementos que podemos incluir en nuestro
proyecto, clase se encontrará seleccionada. En la parte inferior escribimos el nombre
Contacto para establecerlo como nombre de la clase.
Capítulo 6. Programación orientada a objetos

5. Clic en Agregar.
6. Llegados a este punto tendremos un proyecto con el fichero Programs.cs que
usaremos para definir la funcionalidad de la agenda y un fichero Contacto.cs que
editaremos con los miembros de la clase contacto.

7. Doble clie sobre el fichero Contacto.cs para que aparezca la pantalla de edición,
veremos algo similar a lo que se observa en la Figura 6.12.
8. Cada contacto en la agenda debe mantener información relativa a: nombre, apellidol,
apellido2, dirección, telFijo, telMovil y mail.
9. Los atributos serán privados por lo que si queremos modificar o visualizar estos
debemos desarrollar un método de acceso y modificación para cada atributo.
10. Crearemos varios constructores. Uno que inicializai-á solo nombre, apellidos y
teléfono móvil; otro referido a nombre, primer apellido, teléfono móvil y para
finalizar un tercero que inicializa nombre, primer apellido, teléfono móvil y mail.
^
Agenda.Contacte
F'using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
; using System.Threading.Tasks;

.Tnamespace Agenda
{
iz' j class Contacto
! {

Figura 6.12. Ciase Contacto en Visual Studio.


11. Finalmente, el código de nuestra clase será el siguiente:
class Contacto
{
//ATRIBUTOS
string nombre;
string apellidol;
string apellido2;
string dirección;
/*Declaramos los teléfonos como tipo string ya que no vamos
a realizar operaciones aritméticas con ellos*/
string telFijo;
string telMovil;
string mail;

//CONSTRUCTORES
public Contacto(string n, string api, string ap2, string telfM)
{
nombre = n;
apellidol = api;
apellido2 = ap2;
telMovil = telfM;
piiblic Contacto(string n, string api, string telfM) ;
{ i
nombre = n; I
apellidol = api; ;
telMovil = telfM; i
} i
public Contacto(string n, string api, string dir, string telfM, string ;
telF, string correo) I
{ i
nombre = n; ;
apellidol = api; ■
dirección = dir; I
telMovil = telfM; ;
telFijo = telf.; ;
mail = correo; I
} i
A partir de aquí únicamente falta definir métodos propios de la clase, tales como los de
modificación y acceso a los datos:
— I

/*Cambia la variable nombre. Normalmente los métodos que modifican los


atributos suelen iniciar con la palabra set seguido del nombre del
atributo. Como parámetro incluiremos siempre el nuevo valor para el
miembro de la clase.*/
pijblic void setNombre(string n)
{
nombre = n;
}

/*E1 método que devuelve el valor de un atributo suele comenzar con la


palabra get seguido de su nombre. No reciben parámetros pero devuelven
el valor de dicho atributo.*/
piiblic string getNombre()
{
return nombre;
}
public void setApellidol(string api)
{
apellidol = api;

public string getApellidol()


{
return apellidol;

NOTA: No es obligatorio el uso de get o set para iniciar los nombres de estos
métodos, e incluso no tienen por qué contener el nombre del atributo al que hacen
referencia, particularmente considero que es una buena forma de distinguir los
' componentes de la clase y su función. Se usará esta nomenclatura a lo largo del libro.
¡ _
Si accedemos al fichero Program.cs podemos crear una instancia de Contacto escribiendo
algo similar a alguna de las líneas que se muestran :

Debemos usar obligatoriamente uno de los constructores definidos.


Capítulo 6. Programación orientada a objetos 253

Contacto Isabel=new Contacto ("Isabel","Jiménez","Cumbreras","666112233");


Contacto Isabel=new Contacto ("Isabel","Jiménez","666112233");
Contacto Isabel=new Contacto ("Isabel","Jiménez","666112233","isabelQgrt.com");

A continuación, escribiendo el nombre de la variable seguido de punto, observaremos todos


los miembros de la elase Contacto que podremos usar.
- — - — — 1

Isabel.setApellido2("Cumbreras"); ;
Isabel.setTelfMóvil("676342134"); I
Isabel.setEmail("is_ji_cu0grct.com"); ;

Contacte a = new Ccntactc("isabel","3ir.enes","959112233");


a.

O getApellidol
string Ccntacto.getApeliidolO
O getDireccion
O GetHashCode
© getNombre
O getTelMovi!
O GetType
© setApellidol
© setApellidol

Figura 6.13. Métodos y miembros públicos de la clase Contacto.


A partir de ahora realizaremos las actividades propuestas siguiendo un estilo de programación
similar al usado en este apartado. Resumiendo:
1. Crearemos un proyecto en el que
agregaremos un fichero por cada PROYECTO TALLER
clase.
Clase COCHE
2. Los atributos de eada clase serán ATRIBUTOS(PRIVADOS)
Clase MOTO
privados.
CONSTRUaOR

3. Usaremos el modificador público Clase CAMIÓN


MÉTODOS SELECTORES(PÚBLICOS)
sobre aquellos métodos que deban ser
METODOS MODIFICADORES(PÚBLICOS)
usados externamente por otras clases.
MÉTODOS(PÚBLICOS)
4. Usaremos métodos para acceder o
Clase PRINCIPAL
modificar los atributos de la clase.
5. Crearemos al menos un constructor
por defecto para cada clase. Figura 6.14. Estructura básica de un proyecto con
clases.

6.5.2. ARRAY DE OBJETOS EN C#


En el programa principal (Program.cs) que usará nuestra clase, podemos realizar tantas
instancias como queramos para definir un contacto. Sin embargo, si estamos desarrollando una
agenda electrónica, esta podrá tener multitud de contactos almacenados con lo que es inviable
realizar una instancia independiente para cada uno de ellos.
En estos tipos de problemas se opta por usar arrays de los objetos que acabamos de diseñar.
Establecemos dos variables enteras que definen el tamaño máximo del array y los elementos
almacenados en un momento concreto, además del array de objetos. La sintaxis general sería
algo así como:
--- — -- — — - -- -- -- ---- ---n

const int TAM_MAX=máximo; •


int numeroElementos=0: i
Objeto[]nombre_array=new Objeto[TAM_MAX]; i
■ TÁM_MAX. Constañte qüé almacena el número máximo de elementos que puede
incluir el array.
■ numeroElementos. Variable que controla el número total de elementos almacenados
en el array en un momento determinado.
■ nombre_array. Nombre que se dará al nuevo tipo de datos complejo. En la línea
donde se define el nombre se está a su vez reservando espacio en memoria para
albergar un número X de contactos.
Es importante entender que en la declaración de un array de objetos se está declarando y
reservando espacio a través del new para el array no para todos los objetos a los que referencian,
justamente en esta palabra está la clave. Los objetos son variables refereneia de forma que
cuando creemos nuestro array de objetos, cada casilla o elemento del array será inicializado a
nuil, es decir, no apuntan a ningún lugar en memoria con lo que no podrán ser usados.
El proceso de creación de arrays de objetos va íntimamente ligado a la instaneiación posterior
de cada objeto de la clase, ya sea justo después de la creación del array o antes de ser usado. Asi,
debemos realizar alguna de las siguientes operaciones para poder operar con los objetos de eada
easilla de nuestro array de objetos.
/*Inicialización de todos los elementos a la vez. Se está estableciendo una referencia a memoria
para cada casilla del array de objetos.*/
for (int i=0;i<TAM_MAX;i++){
^ nombre_array[i]=new ObjetoQ;

//Inicialización antes de ser usado


nombre_array[7]=new ObjetoQ;

Desarrollemos el fíehero Programs.cs para usar la clase Contacto.


//Funciones propias de Programs.cs
static int menú()

int opcion;
Consolé.WriteLine("1. Insertar contacto");
Consolé.WriteLine("2. Visualizar agenda");
Consolé.WriteLine("3. Salir");
Consolé.WriteLine("Escoge una opción");
Capítulo 6. Programación orientada a objetos 255

opcion = int.Parse(Consolé.ReadLine O);


return opcion;

S'katxc Contacto nuevoContacto()

string nombre, api,ap2,telefonoMovil;


Console.WriteLine ("Introduce el nombre");
nombre = Consolé.ReadLine();
Consolé.WriteLine ("Introduce el primer apellido");
api = Consolé.ReadLineO;
Consolé.WriteLine("Introduce el segundo apellido");
ap2 = Consolé.ReadLineO;
Consolé.WriteLine("Introduce el teléfono móvil");
telefonoMovil = Consolé.ReadLine();
Contacto nuevo = new Contacto(nombre, api, ap2, telefonoMovil);
return nuevo;

static void insertar (Contacto[] ag, ref int nTotal, Contacto nuevo)
{
ag[nTotal] = nuevo; //nuevo es un contacto ya creado
nTotal++;

static void mostrar (Contacto[] ag, int nTotal)


{
for (int i = 0; i < nTotal; i++)
{
Consolé.WriteLine("Nombre\t\tApellidol\t\tApellido2\t\t
Telefono Móvil");
Consolé.WriteLine("{0)\t\t{0}\t\t{0}\t\t{0}",ag[i].getNombre()
,ag[i].getApellidol(),ag[i].getApellido2(),ag[i].getTelMovil());

//Función principal que se ejecutará al inicio


static void Main(string[] args)
{
const int TAM_MAX = 100;
int numeroElementos = 0;
int op=0;

//Creación del array de objetos


Contacto[] agenda = new Contacto[TAM_MAX];
do

op = menú ();
switch (op)
{
case 1: Contacto n = nuevoContacto(); /*Creación de un nuevo

contacto*/
insertar(agenda, ref numeroElementos, n);
break;
case 2: mostrar(agenda,numeroElementos);
break;
256 Programación

Consolé.ReadLine();
} while (op != 3);

Objeto [] array=new Objeto[TAM];


array[l].metodD(); ERROR

nuil nuil nul nuil nuil nuil Objeto I nuil nuil nuil I nuil
arrayíl]
arravll]

Cada casilla esta preparada para recibir un Cada casilla está preparada para recibir un
objeto creado anteriormente objeto creado anteriormente. Arrayjl] ya contiene
una referencia a objeto

Figura 6.15. Array de objetos. Errores en la creación de arrays de objetos.

6.6. FUNCIÓN MAIN EN 0#


A estas alturas son muchas las ocasiones en las que hemos usado la función Main. Todas los
proyectos cuentan con una clase principal que la contiene y en todas ella la estructura es la
siguiente: static void Main(string [] args). Una vez se han estudiado las estructuras compuestas,
sabemos que pasamos a la ñmción un array llamado args de tipo string, es decir, cada una de sus
casillas almacena una cadena de caracteres. Existen otras definiciones de cabecera para este
método:

■ static void MainQ;


■ static int MainQ;
■ static int Main(string [] args);
En los prototipos de función Main donde int es el tipo devuelto, el usuario podrá conocer
posibles errores de ejecución, ya que es esto lo que devuelve la función. Ahora bien, ¿por qué
usar un array de string como parámetro? La función Main es la que se invoca cuando damos
orden de ejecutar nuestro proyecto, tenemos una clase principal y en ella la función Main que es
ejecutada de forma automática en el inicio. Si escribiéramos el nombre del fichero que contiene
la función principal en una consola de texto se ejecutaría esta. Conocemos multitud de comandos
Linux o recordamos antiguos comandos MS-DOS a los que se pennitía añadir una secuencia de
valores al ser ejecutados, esto es lo que precisamente hace el array de string, reeoger los valores
de las opciones que el usuario escribe junto al nombre del fichero ejecutable.

static void Main(string [] args){ //Fichero Program.cs en funcionPrincipal


Consolé.WriteLine(args [0]);}
Capítulo 6. Programación orientada a objetos 257

,V"rr:rív¡ .. ■
Nombre de nuestro proyecto C#
funcionPrinctpal
Al escribir funcionPrincipai se
ejecuta automáticamente la
función Main.

Console.WriteUne(airBs[0]}; ¡' On Símbolo del ^'stema ♦ furvcio pal.exe Hola

uncionPrxnca.pAl*exe Parámetro
argslOj)

Figura 6.16. Uso parámetros en la línea de comandos.


El fichero ejecutable lo encontraremos en la carpeta de proyecto, concretamente en la
subcarpeta debug que se encuentra en bin. Podemos acceder al símbolo del sistema en Windows
(Accesorios Símbolo del sistema), a la ubicación del fichero y a continuación escribir este,
seguido de su extensión y tantos parámetros como deseemos o se nos permita usar.
Ruta de acceso
►| funcicnPrincipsI > funcionPrincipai ► bin ► Dcbug p
|S Ayuda I
lartif ccn Correo electrónico Grabar Nueva cari

Biblioteca Documentos
Pcbug

I ^ funcionPrincipal.vshostexe.manifest
i funcionPrincipal.exe.config Ejecutable
¡ funcionPrmcipal.vshcst.exe^gpírfíí'''^
■ funcionPrincipal.exe
I D funcionPnncipal.pdb
■ . luncionPrmcipal.vshostete

Figura 6.17. Acceso al fichero ejecutable de nuestro proyecto.


Podemos pasar parámetros a la función principal a través del propio entorno de Visual Studio.
Para ello seguiremos los siguientes pasos:
1. Una vez tengamos nuestro proyecto configurado y listo para ejecutarse, clic en
Proyecto -> Propiedades de nombre... donde nombre es el nombre de nuestro
proyecto.

2. Aparecerá una nueva ventana desde la que podremos configurar multitud de


características de nuestro proyecto. Haremos clic en Depurar, que se encuentra entre
la lista de opeiones de la izquierda.
3. Localizaremos la lista Argumentos de la línea de comandos: en el cuadro de lista en
blanco escribiremos todos los valores que queramos pasar como parámetros a nuestra
funeión separados por un espaeio en blanco.
4. Al ejecutar el proyecto Visual Studio recoge los datos escritos en la lista y los pasa a
la función Main como si hubieran sido escritos en la línea de comandos. En la
función principal serán tratados según haya diseñado el programador.
258 Programación

I fundonPrindpal -o X Program.cs

Apiicadón
Configuracióru 1(Debug)activa Plataforma: |(AnyCPU)activa
Compilar

Eventos de compilación Opciones de inicio

Argumentos de la linea de comandos: Hotaj


Recursos

Servicios

Configuración
Directorio de trabajo:
Rutas de acceso de referencia

Firma
n Habilitar depuración de código nativo
Seguridad
Habilitar el proceso de hospedaje de Visual Studio
Publicar

Análisis de código

Figura 6.18. Ventana propiedades del proyecto. Opciones de depuración.

ACTIVIDAD 6.3

Crea una clase llamada Persona que almacene información del tipo: nombre, apellidos,
DNI, sexo, fecha de nacimiento, peso y altura. El atributo fecha de nacimiento debe ser una
estructura (fecha) que guarde día, mes y año.
Configura la clase tal que tenga al menos un constructor y un método selector y modificador
por cada propiedad. Además, crea un método llamado mostrar que visualice todos los datos de
una persona en el formato que prefieras.

ACTIVIDAD 6.4

Desarrolla un programa principal que use la clase Persona creada en la Actividad 6.3, tal que
pueda almacenar como máximo 20 personas. Muestra un menú que pennita: insertar, eliminar y
modificar una persona. Añade las funciones o métodos que necesites.

ACTIVIDAD 6.5

Modifica el programa ejecutable de la Actividad 6.4 tal que además realice las funciones de
ordenar personas por apellidos, ordenar personas por fecha de nacimiento, calcule el peso medio
de las personas almacenadas, así como la edad media y la altura.

6.7. PALABRA RESERVADA THIS


La palabra reservada this es la que usaremos cuando queramos hacer referencia al objeto que
estamos programando. Es bastante útil ya que cuando la anteponemos a algún atributo o método
inequívocamente estamos indicando al compilador que ese elemento es del objeto en el que nos
encontramos.

Gracias a this podemos pasar parámetros con el mismo nombre que los atributos de la clase
ya que la referencia a objeto distinguirá claramente qué elemento es el atributo y cuál es el
parámetro.
Si retomamos la clase contacto del Apartado 6.5.1, podríamos modificar sus métodos tal que:
Capítulo 6. Programación orientada a objetos 259

//Llamamos a cada parámetro como los atributos de la clase,


public Contacto(string nombre, string apellidol, string apellido2,
string telMovil)
i
this.nombre = nombre;
t-his.apellidol = apellidol;
this.apellido2 = apellido2;
this.telMovil = telMovil;
}

El operador this penriite diferenciar el atributo de la clase del parámtero.


Además podemos usar la palabra reservada this para hacer referencia a uno de los
constructores de la clase y establecer invocaciones entre ellos.

//Primer constructor
Contacto(string nombre, string apellidol, string apellido2,
string telMovil)
{
this.nombre = nombre;
this.apellidol = apellidol;
this.apellido2 = apellido2;
this.telMovil = telMovil;
}
//Segundo constructor. Hace uso del primer constructor
public Contacto(string nombre,string apellidol,string apellido2,
string telMovil, string telFijo):this(nombre, apellidol, apellido2, telMovil)

this.te1Fijo = telFijo;

En el código ejemplo se crea un primer constructor al que pasamos cuatro parámetros:


nombre, apellidol, apellido2 y telMovil. Las variables de la clase (atributos) correspondientes
serán inicializadas con los valores de estos parámetros.
En el segundo constructor usaremos cinco parámetros ya que además de los datos indicados
en el párrafo anterior inicializaremos el valor de telFijo. Para no repetir código, ya que la primera
parte del segundo constructor es similar a la del primero, se hace referencia a este a través de la
palabra reservada this antecedida de dos puntos (:thls). Esta sintaxis indica que se debe ejecutar
el primer constructor y una vez finalice este pasar a ejecutar el segundo, es decir, inicializaremos
primero los datos nombre, apellidos 1 y 2 y telMovil y finalmente el telFijo.
El segundo constructor, aunque solo inicializa un valor, pasa como parámetro tantos como
necesitará el primero para posterionnente transmitírselos en su llamada.

public constructor(tipolparl,..,tiponparn):thís(parl,...,parx){

thís.atributo=pairy;
260 Programación

int numero;
char carácter;

public A(int n){


■ numero=n;

public A(int n, char c):th¡s(n){


caracter=c;

Primero se ejecuta
el constructor llamado con this

IvLlamada al constructor de un solo parámetro,


Ú, pasamos soto n, es una llamada no '
Ih, una declaración i vÁüÁi
Figura 6.19. Uso de la referencia this para invocar otros constructores.

6.8. DESTRUCTORES EN O#
Un destructor es una función especial de la clase que se ejecuta de forma automática cuando
se elimina un objeto. Al igual que un constructor no devuelve ningún tipo de dato, ni siquiera
void y se denomima como la clase aunque anteponiendo el símbolo No recibe parámetros y se
usa normalmente para liberar recursos, ficheros, conexiones de red, etc.

Sentencias;

-Contacto(){
Consolé.WriteLine("Objeto destruido exitosamente");

En C# existe un elemento denorñTríádó récoíé^ de basura (garbage collector). Constituye


un mecanismo de gestión de memoria tal que en el momento en que existan elementos no
referenciados estos se eliminan liberando espacio de la memoria principal.
Cuando creamos un objeto y posteriormente asignamos este a nuil estaremos eliminando su
referencia. Cada cierto tiempo el recolector de basura irá revisando aquellos objetos que ya no
estén referenciados. Si encuentra alguno lo elimina de memoria pero antes ejecuta el destructor
de este si se ha programado. Si queremos que el recolector localice objetos no referenciados y los
elimine en un momento concreto usaremos el objeto GC (garbage collector) junto al método
Collect(), para obligar a que se produzca la recolección o búsqueda de elementos no
referenciados y se proceda a su eliminación. Véase el siguiente ejemplo:
Capítulo 6. Programación orientada a objetos 261

class Ejemploi
String nombre;
public Ejemplo(string nombre){ //(l)
this.nombre=nombre;
Consolé.WriteLine ("El objeto {0} ha sido creado",this.nombre);

~Ejemplo () { //(2)
Consolé.WriteLine ("El objeto {0} ha sido eliminado"
,this.nombre);
)

class Program {
static void Main(string [] args) {
Ejemplo A=new Ejemplo("A"); //(3)
Ejemplo B=new Ejemplo("B");
A=null; //(4)
GC.CollectO; //(5)
}

Según las anotaciones realizadas a partir de comentarios:


(1): Constructor de la clase. Recibe como parámetro un nombre que será representativo de
esta. Cargamos el atributo nombre de la clase con el parámetro pasado al constructor.
(2): Programamos el destructor. Cuando el objeto vaya a ser eliminado de memoria se
mostrará la frase "El objeto nombre ha sido eliminado", siendo nombre el valor de este
atributo.

(3): En este punto, ya en el programa principal, creamos una variable de la clase a la que
llamamos 'A'. Realizamos la misma operación con la variable 'B'.
(4): Asignamos nuil a la variable A, es decir, estamos eliminando la referencia del objeto. En
el momento en que el recolector de basura compruebe los objetos creados y localice que este no
está referenciado, será eliminado.
(5): En lugar de esperar a que el recolector se ejecute cuando tenga previsto damos la orden
de que se produzca la recolección mediante GC.CollectO- Veremos en pantalla el mensaje "El
objeto A ha sido eliminado".
No usamos modificadores de visibilidad cuando codificamos el destmctor, la cabecera public
~Gbjeto NO está permitido.

ACTIVIDAD 6.6

Localiza en la web o bibliografía aportada por el profesor información más detallada sobre el
recolector de basura y el modelo de reserva de memoria de C#. Desarrolla un esquema breve con
la información que hayas obtenido.

ACTIVIDAD 6.7

Añade un destructor a la clase creada en la Actividad 6.3


Sabías que...
En C# podemos realizar llamadas en cascada a los métodos de un objeto. Por
ejemplo, supongamos que tenemos el método crearObjeto() que devuelve un objeto con
sus atributos inicializados, de forma directa podemos llamar al método mostrar() que
permitirá visualizar el contenido de la información:
Objeto A = new Objeto():

A.crearObjetoO. mostrar();

El objeto A invoca a crearObjeto desde el que, acto seguido, se visualiza su


contenido.

NOTA: En C# encontramos un nuevo modificador readonly. Este se usa para la


creación de constantes que no tienen por qué ser inicializadas desde el principio en la
aplicación principal. Si utilizamos readonly podremos inicializar constantes en el
contructor u otro método de la clase. Por ejemplo:
I class Ejemplo{ :
; public readonly double PI;

! piiblic Ejemplo (double valor){ :


; PI=3.1415; ;

La operación PI=12 posterior a la inicialización no está permitida.

6.9. VARIABLES ESTATICAS


Una variable estática es una variable de clase, de forma que su valor será común a todas las
instancias de esta, pudiendo ser modificada por todas ellas.
Para entender el concepto véase el siguiente ejemplo:
— — — — — — — — — — - — ---I

class Variables ;
{ i
int var_normal; I
static int var_estatica; //(1) ;

public Variables(int vN) //(2) i


{ i
this.var_normal = vN; 1
} i
public void setVarNormal(int vN) 1
{ i
var normal = vN; '
Capítulo 6. Programación orientada a objetos 263

public void setVarEstatica(int vE) //(3)

var estatice = vE;

ptiblic int getVarNormal()


i
return var_normal;
)
piablic int getVarEstatica ()

return var estática;

En el código se han distinguido varias zonas con un comentario del tipo (número):
■ (1). Creamos la variable estática. Para definir estático un atributo de la clase solo
tenemos que anteponer la palabra reservada static a su declaración, static tipo_dato
nombre;.
■ (2). Se define el constructor de la clase. Este inicializa la variable var_normal que
como su nombre indica es un atributo como los que hemos estado usando hasta el
momento.

■ (3). Creamos métodos para el acceso a las propiedades de la clase. Como


establecíamos en este mismo capítulo tratábamos de usar el menor número de
miembros de clase públicos, los atributos deben ser privados, de forma que para
llegar a ellos, ver su contenido o modificar este, debemos desarrollar métodos set y
get (selectores y modificadores).
A continuación se crea una función principal que utiliza la clase:
static void Main(stringi] args)
{
Variables A = new Variables(10); //(l)
Variables B = new Variables(20);

//(2)
Consolé.WriteLine("La variable normal de A es:{ O }",A.getVarNormal());
Consolé.WriteLine("La variable normal de B es:{0}", B.getVarNormal());

A.setVarEstatica(12); //(3)

//(4)
Consolé.WriteLine("Somos A y la variable estática vale:{0}"
, A.getVarEstaticaO);
Consolé.WriteLine("Somos B y la variable estática vale:{0}"
, B.getVarEstatica O);

Consolé.ReadLine();

■ (1). Creamos dos variables de nuestra clase Variables, las llamamos Á y B. Los
valores de var_nomal para A y B son 10 y 20 respectivamente.
■ (2). Realizamos una pequeña comprobación por la que observamos los valores de
var_nonTial para A y B.
(3). Cambiamos el valor de nuestra variable estática, var_estatica. Observamos que
el cambio se realiza desde la instancia A,sin embargo, al ser var_estatica un miembro
static si usamos desde B el método getVarEstatica() el valor devuelto será 12.
(4). Visualizamos el contenido de la variabe estática mediante el uso de los métodos
getVarEstaticaO de A y B y ambos devuelven el mismo valor, ya que una variable
estática es una variable de clase, de forma que todas las instancias de la clase acceden
a ella y pueden modificarla en cualquier momento permaneciendo el cambio para el
resto de instancias.

var estatice

a uai^iable normal de f) es:10


a variable normal de B es:20
omos B y la variable estática vale:12
omos B i; la variable estática vale:12

Figura 6.20. Salida del programa ejemplo usado en la explicación de variables estáticas.

NOTA: Podemos usar la variable estática sin necesidad de crear una instancia de la
clase siempre que esta sea pública.
i class Variables{ j
I I

I public static int var_estatica; ;

class Program{
static void Main(string [] args){
Variables.var_estatica=27;

Consolé.WriteLine("var_estatica={O)"
,Variables.var estática);

Resumiendo:

■ Anteponemos la palabra static a aquellos atributos que estén destinados a ser


estáticos.

■ Los miembros de clase existen incluso cuando no se han creado instancias de la clase.
■ Podemos acceder a un atributo estático indicando Nombre_Clase.var_estática.
Normalmente definiremos variables estáticas públicas para no obligar a la
instanciación en su uso.

6.9.1. FUNCIONES MIEMBROS DE CLASE ESTÁTICOS


Al igual que podemos declarar atributos de tipo static se pueden definir funciones estáticas.
Estas funciones sólo podrán usar las variables que hayan sido definidas también como estáticas.
Agreguemos a nuestra clase ejemplo Variables del Apartado 6.9 un método estático llamado
mostrar().
Capítulo 6. Programación orientada a objetos 265

piiblic static void mostrar () |


{ i
Consolé.Wr i teLine ("Variable normal;{ O }",var_normal); //ERROR ;
Consolé.Wr i teLi ne("Variable normal:{0}", var_estatica); ;
} i
La línea; ConsoIe.Wr¡teLine("Var¡abIe normal:{0}",var_normal); provoca un error de
compilación, debido a que var_normaI no está definida como estática y no puede incluirse en la
función.

■ Convertiremos una función en estática utilizando la palabra reservada static delante


del tipo devuelto.
■ Solo podremos usar variables static en las funciones estáticas.
■ Al igual que los atributos estáticos, una función estática puede ejecutarse realizando
una llamada desde la clase sin necesidad de instanciar ningún objeto, ejemplo:
Variables.mostrar().
■ Las funciones estáticas no aceptan redefíniciones.

ACTIVIDAD 6.8

Crea una clase llamada NodoRed que almacene infonnación sobre: nombre del equipo,
dirección IP, máscara de red, dirección de broadcast, puerta de enlace, dirección de red y
servidores DNS. La clase debe tener una variable estática que se incremente en uno cada vez que
se instancie la clase. Desarrolla los métodos que creas oportunos para visualizar y modificar las
variables, asi como una función estática que pennita ver el número de NodoRed creados hasta un
momento detenninado. Programa una función Main que use la clase. El objetivo es practicar el
uso de miembros estáticos.

6.10. PROPIEDADES EN C#
En repetidas ocasiones hemos hecho hincapié en el uso de atributos privados, tal que si
queremos visualizar o modificar estos el proceso se realizará a partir de un método. Sin embargo,
es cierto, que seria más cómodo escribir de fonna automática instancla.atributo. Las
propiedades penniten que de cara al usuario, un atributo parezca directamente accesible aunque
de forma interna una propiedad no es más que un método selector o modificador.
Véase el siguiente ejemplo.
■ (1). Aunque aún no hemos explicado la sintaxis básica de una propiedad, en esta línea
se está creando una. La propiedad Campo modificará o visualizará el valor del
atributo campo.
■ (2). La palabra reservada set (poner o cambiar) inicia el bloque de código para la
modificación de la variable. Cuando usemos la propiedad se ejecutarán las líneas de
esta zona si queremos proceder a la asignación de nuevos valores.
■ (3). campo=value. Valué referencia el valor que se asignará a la propiedad, siendo
campo como ya sabemos el atributo de la clase.
■ (4). El bloque get incluye las líneas de código que deberán ejecutarse cuando se haga
una llamada desde el objeto a la propiedad. Normalmente get devuelve el valor de la
variable sin más mientras set cambia este.

La sintaxis básica de una propiedad es la siguiente;


I

tipo_propiedad nombre_propiedad{ i
set{ ;
código_para_set;//atributo=value; '<
} \
get{ i
código_para_¿et;//return atributo; '•
} !
; :I

La función principal usaría esta propiedad del siguiente modo:

static void Main(string[] args)


{
Propiedades a = new Propiedades();
a.Campo = 12; //(1)
Consolé.WriteLine(a.Campo); //(2)
Consolé.ReadLine();

(1). Usamos la propiedad como si fuera el atributo aunque no lo es. Al escribir


a.Campo=12; estamos accediendo a la propiedad Campo, concretamente al bloque
set ya que estamos usando el signo igual, estamos realizando una asignación. El
código para set es campo=vaIue, valué es sustituido por 12 ya que la asignación lo
exige, de forma que indirectamente estamos asignando al atributo el valor 12.
(2). En esta línea usamos la propiedad de la forma a.Campo tal que estamos
solicitando el valor de la propiedad, es decir, estamos solicitando la ejecución del
bloque get. Este bloque contiene la línea return campo y es precisamente esto lo que
hace la propiedad, devolver el valor del atributo campo, con lo que en pantalla
veremos un 12.

ACTIVIDAD 6.9

Modifica la Actividad 6.8 de forma que en lugar de usar métodos set y get utilices
propiedades para cada atributo de la clase.
Capítulo 6. Programación orientada a objetos 267

6.11. INDIZADORES EN C#
En ocasiones usamos en nuestras clases tipos compuestos como arrays, tal que si queremos
acceder a ellos debernos generar métodos a los que pasar como parámetro una posición. Los
indizadores permiten el acceso "directo" a cada casilla del array ya sea para escribir o leer de él.
Un indizador usa el operador [ ] para acceder a colecciones definidas dentro de la clase, de
fomia que el propio objeto instanciado puede ser tratado como un array facilitando la
codificación del programa y su comprensión.
Objeto A=new ObjetoO;
A[l]="Hola Mundo";

Un indizador es bastante similar a las propiedades vistas, la diferencia se encuentra en el paso


de parámetro, ya que estableee una posición o elemento a modificar o usar.
Entre los corchetes de un indizador escribiremos un índice que no tiene por qué ser un
número entero.

La definición de un indizador es la siguiente:


tipo_dato this[índice]{ •
set{ i
Sentencias;//array[índice]=value; ¡
} i
get{ ■
Sentencias;//return array[índice]; :
} i
} \
Un ejemplo de uso de un indizador sería el que se muestra a continuación:
class Indices
{
int[] tabla = new int[10]; //(l)
public int this[int Indice] //(2)
{
set //(3)
{
if (Índice >= O && Índice < 10)
{
tabla[índice] = valué;
}

get //(4)
{
if (índice <0 1 ! índice >= 10)
{
return 0;
)
else
{
return tabla[índice];
class Program

static void Main(string [] args)

Indices T = new Indices(); //(5)


T[3] = 13; //(6)
Consolé.WriteLine("Element 3 = {0}",T[3]); //(7)

■ (1). La clase Indices cuenta con una variable de tipo array. Codificaremos el objeto
para que podamos acceder a ella directamente desde la instancia de la clase.
■ (2). Cabecera del indizador. Usaremos un índice de tipo entero int índice.
■ (3). set permitirá modificar un elemento del array. Controlamos si el índice del array
es correcto, ya que este debe encontrarse en el rango de O a 9.
" (4). get por el contrario devolverá el valor ubicado en una de las casillas de nuestro
array tabla. Al igual que en set comprobamos que el índice introducido sea correcto.
■ (5). Creamos un objeto llamado T de la clase Indices.
■ (6). Usamos T como si esta íuera una variable de tipo array. En la primera línea TI3]
estamos accediendo sin que el usuario se de cuenta al atributo de tipo array de la clase
Indices, concretamente a la posición 3, modificando esta con el valor 13. En la
segunda aparición del indizador estamos accediendo nuevamente a la posición 3 para
visualizar su contenido. Usamos set y get respectivamente.
Los índices del indizador no tienen por qué ser números enteros como ya se ha explicado, e
incluso pueden usarse más de un índice. Véase el siguiente ejemplo:
class Norabre
{
/*La clase usa dos arrays de tipo string, almacenan nombres y apellidos.
Las posiciones refieren el nombre y apellido de una misma persona*/
string[] nombres = { "sabel", "Pepe", "Juan", "Maria"};
string[] apellidos = { "Perez", "Vázquez", "López", "Camacho"};

/*Método que busca un nombre, si existe devuelve su posición en caso


contrario -1*/
public int buscaNombre(string nomBuscado)
int i=0;
foreach (string nom in nombres)
{
if (nom.Compárelo(nomBuscado) == 0)
{
return i;
}
i++;
}
return -1;
Capítulo 6. Programación orientada a objetos 269

/*Función similar a la anterior, se diferencia de esta en que busca


coindidencia de nombre y apellido*/
public int buscaNombreApellidos(string nomBuscado, string apeBuscado)
{
int i = 0;
while (i<nombres.Length)
{
if (nomBuscado.CompareTo(nombres[i]) == O
&& apeBuscado.CompareTo(apellidos[i]) == 0)
{
return i;

return -1;
}

/*Primer indizador al que pasamos como parámetro una cadena de caracteres


en lugar de un número entero, se introduce el nombre y usamos el método
buscaNombre () para localizar su posición*/
public int this[string nombre]

return buscaNombre(nombre);

/*Segundo indizador al que pasamos como parámetro dos cadenas de


caracteres, se introduce el nombre y apellidos. Usamos el método
buscaNombreApellidos O para localizar su posición*/
public int this[string nombre, string apellido]

return buscaNombreApellidos(nombre, apellido);

La clase Program se desarrollará de la siguiente forma:


class Program
(
static void Main(string[] args)
{
//Creamos un objeto de la clase Nombre
Nombre ejemplo = new Nombre();

//A partir de aqui usamos el indizador de un solo parámetro


/*ejemplo["Pepe"] ejecutará la función buscaNombre y devolverá el
número de posición o -1, según esto la sentencia alternativa if mostrará
un mensaje u otro en pantalla.*/
if (ejemplo["Pepe"] != -1)
{
Consolé.WriteLine ("Pepe está en la lista");
}
else
{
Consolé.WriteLine r'Lo__siento, no encontramos a Pepe");}
/*En este segundo caso, usamos el indizador con dos parámetros, buscamos
tanto nombre como apellido*/
if (ejemplo["Juan", "Vázquez"] != -1)
{
Consolé.WriteLine("Juan Vázquez está en la lista");
}

Consolé.WriteLine("Lo siento, no encontramos a Juan Vázquez");

Consolé.ReadLine();

ACTIVIDAD 6.10

Modifica el proyecto realizado en la Actividad 6.3 de fonna que crees la clase Persona y una
clase llamada ListaPersonas. En esta última incluiremos un array donde almacenaremos objetos
tipo Personas y se incluyen todos los métodos para insertar, modificar, eliminar, etc. Utiliza los
mecanismos vistos hasta ahora, propiedades, indizadores, etc. Crea un progi'ama principal que
declare una variable de tipo ListaPersona y se encargue de gestionar los contactos de la agenda.

6.12. SOBRECARGA DE OPERADORES EN O#


La sobrecarga de operadores consiste en dar una mayor funcionalidad a un operador ya
existente permitiendo que pueda operar con otros objetos para los que no está diseñado por
defecto. Por ejemplo, el operador aritmético suma(+) está diseñado para operar con números, ya
sean enteros o reales pero podemos sobrecargarlo para que pueda usarse con alguna de las clases
que hayamos diseñado.
Hasta ahora hemos estado realizando métodos del tipo:
—- — ________________________________

Objeto A=new ObjetoQ; :


Objeto B=new ObjetoQ; \
Objeto C=A.suma(B);/*Donde el método suma realiza esta operación sobre los atributos de A \
y B del modo que el programador haya diseñado*/ ;
En este apartado vamos a aprender a usar la sobrecarga de operadores para que podamos
realizar la operación de la forma:

Objeto A=new ObjetoQ; i


Objeto B=new ObjetoQ; \
Objeto C=A+B;/*A y B son Objetos para los que por defecto no existe definida ninguna •
función para el operador aritmético +*/ ;
C# no permite la creación de nuevos operadores aunque la mayoría de los que ya hemos
estudiado pueden sobrecargarse. Básicamente el uso de sobrecarga de operadores aporta
legibilidad al código.
La sobrecarga de un operador se realiza mediante la codificación de una función que sigue
una sintaxis determinada. Estudiaremos esta a continuación. Para mantener la legibilidad es
necesario que usemos operadores relacionados con la operación que realizará con los atributos de
Capítulo 6. Programación orientada a objetos 271

las clases que los usan, por ejemplo, si usamos el operador relacional = debemos realizar
comparaciones de igualdad entre atributos, no sería correcto realizar operaciones de asignación
ya que confundiría al usuario o programador que use la clase.
La sobrecarga de un operador conlleva la creación de una función con las siguientes
características:

public static tipo_devuelto operaíor simbolo(operandos){


Sentencias;
}
Las palabras reservadas public y static deben incluirse ya que el operador debe ser
público para ser accesible desde más allá de la clase, y además debe ser im método de
la clase, es decir miembro estático. El orden en el que se coloquen ambas es
indiferente.

tipo_devueIto. Tipo de dato que devolverá un operador, por ejemplo si se sobrecarga


el operador resta para que opere sobre dos objetos de clase Vector el resultado de esta
operación debe ser otro Vector. Este será el tipo_devuelto. No puede ser void, por
definición todo operador devuelve un valor de algún tipo.
operator. Palabra reservada que indica que se está sobrecargando el operador que
aparece a continuación.

símbolo. Operador,+, -, *, /, <, <=, etc.


operandos. Incluiremos entre los paréntesis tantos objetos como operandos necesite
el operador. Por ejemplo, el operador suma necesita dos operandos con lo que para
este operador pasaremos dos parámetros, el operador cambio de signo solo precisa de
un operando, así que en su sobrecarga solo añadiremos un parámetro.
Sentencias. Conjunto de operaciones que debe ejecutar el operador.
class Vector

//ATRIBUTOS
int X;
int y;
int z;

//CONSTRUCTOR
public Vector(int x, int y, int z)
{
this.x = x;
this.y = y;
this.z = z;

//SOBRECARGA DEL OPERADOR +


/*Se agrega funcional al operador más de forma que a partir de ahora puede
sumar además de tipos numéricos variables de tipo Vector.*/
static public Vector operator +(Vector a, Vector b)
{
Vector resultado=new Vector(O, O,0);
resultado.X = a.x + b.x;
resultado.y = a.y + b.y;
resultado.z = a.z + b.z;
return resultado; }
272 Programación

El operador suma necesita de dos operandos, por eso entre sus paréntesis se incluyen dos
parámetros (Vector A, Vector B). A la hora de realizar la operación se suman los atributos de
cada operando y se van almacenando en una variable Vector llamada resultado. Este será el
objeto devuelto en la función.
class Program
{
static void Main(string[] args)
{
Vector A = new Vector(2, 3, 4);
Vector B = new Vector(1, 1, 1);
Vector R = A + B;

En el programa principal creamos una variable Vector con los valores A(2,3,4) y una variable
también de tipo Vector con los valores B(1,1,1). El resultado de realizar la operación R=A+B
será R(3,4,5).
A la hora de sobrecargar un operador debemos tener en cuenta sus características y que no
podemos alterar la precedencia respecto a otros operadores, asociatividad, ubicación y número
total de operandos.

ACTIVIDAD 6.11

Completa la clase Vector usada de ejemplo en el apartado y agrega las sobrecargas de los
operadores resta, multiplicación, producto por un escalar, mayor que, menor que e igual. El
operador mayor comprobará que todos los atributos del primer vector sean menores que los del
segundo. Devuelve -1 en caso contrario. Las operaciones menor_que e igual devolverán -1 en
caso de que los atributos del primer Vector sean mayores que los del segundo o diferentes.. =
devolverá O si los atributos del primer Vector son iguales a los del segundo Vector.

COMPRUEBA TU APRENDIZAJE
1. ¿Qué entiendes por abstracción? ¿Qué mecanismos existen?
2. ¿Qué entiendes por tipo abstracto de datos(TAD)?
3. ¿Cuáles son las propiedades de la programación orientada a objetos o POO?
Descríbelas brevemente.

4. ¿Qué diferencia existe entre clase y objeto?


5. ¿Qué elementos forman una clase? ¿Cómo creamos una clase en C#?
6. En una clase, ¿qué es un método constructor? ¿y un método destructor?
7. ¿Cómo se declara un array de objetos? ¿Qué consideraciones debemos tener en
cuenta a la hora de usar un array de objetos?
8. ¿Qué función tiene la palabra reservada this? ¿En qué situaciones la usamos?
9. ¿Qué entiendes por miembro estático de clase? ¿Cuáles son sus características?
Capítulo 6. Programación orientada a objetos 273

10. ¿Qué es una propiedad en C#? ¿Y un indizador? ¿Qué conseguimos al sobrecargar un


operador?

ACTIVIDADES DE AMPLIACION
1. Crea una clase llamada Fecha para almacenar y operar con fechas. Debe tener los
métodos;

■ Días DiaSemanaQ. Devuelve el día de la semana de la fecha almacenada en


la clase. Dias es una enumeración con los valores Lunes, Martes, etc. Por
ejemplo, 30/5/2013 devuelve Jueves.
■ Fecha DiaSiguienteQ. Devuelve la fecha correspondiente al día siguiente de
la fecha almacenada. Devuelve una fecha.

■ void mostrarFechaO- Muestra por pantalla la fecha con el formato


dd/mm/aaaa.

■ bool fechaCorrectaO- Devuelve verdadero o false en función de si la fecha


es correcta o no.

■ bool mayor(Fecha otraFecha). Devuelve true si fecha>otraFecha.


■ bool menor(Fecha otraFecha). Devuelve true si fecha<otraFecha .
■ bool ¡gual(Fecha otraFecha). Devuelve true si fecha>otraFecha.
2. Realiza un programa que use la clase Fecha. Debe contener un menú que permita
ejecutar todos sus métodos.
3. Modifica la Actividad 1 tal que sustituyas las funciones mayor, menor e igual por los
operadores <,> e =.
4. Modifiea la Actividad 2 para hacer uso de los operadores sobrecargados en la
Actividad 3.
5. Imagina que quieres crear un programa que te permita organizar los DVD de
películas que tienes en casa. Crearemos una clase Películas donde recogeremos toda
la información relacionada con un film. Algunos campos de la clase podrían ser:
Titulo, Actores (array de nombres), Director/Directores (array de nombres), fecha
de salida, formato, etc. Desarrolla además un programa principal con el que puedas:
insertar, modificar, eliminar, buscar o visualizar una película. Incluye más
funcionalidad si lo deseas.
6. Crea una clase Coche en la que debes incluir los atributos privados: modelo,
matrícula, marca, color. Configura:
■ Constructores que creas necesarios y el destructor.
■ Los modificadores y selectores necesarios para cada atributo privado, de la
forma setModeloQ, getModeloQ,setMatriculaQ, getMatriculaQ, etc.
■ Un método mostrar que permita visualizar los atributos almacenados.
7. Modifica la Actividad 6 de forma que en lugar de modificadores y selectores uses
propiedades.
8. Crea una clase llamada ListaCoches que contenga como atributos un array de la clase
Coche creada en la Actividad 6. Crea un programa principal que use esta clase de
forma que suponiendo un objeto ListaCoches llamado A podamos realizar las
siguientes operaciones:
■ A[pos]. Devuelve el coche que se encuentra en la posición pos. Si la posición
aportada no es válida devolverá nuil.
■ A[matrícula]. Devuelve la posición del coche cuya matrícula conindice con
la establecida entre los paréntesis. En caso de no encontrar esta devolverá -1.
■ A[marca]. Devuelve la lista de coches que son de la marca especificada entre
los corchetes.

9. Crea una clase llamada Rectángulo que permita almacenar infonnación relativa a
esta figura geométrica como: ancho, alto, posX, posY, carácter. posX y posY
representan las coordenadas x e y de su posición en pantalla (posición de la esquina
superior izquierda). El atributo carácter almacenará el carácter que usaremos a la
hora de visualizar el rectángulo, por defecto es la X. Los métodos que incluiremos en
la clase son:

■ Constructores.

■ Destructor.

■ Propiedades.
■ esValido(anchoPantalla, altoPantalIa). Este método devuelve verdadero o
falso en función de si las coordenadas del rectángulo y sus dimensiones son
aptas para una pantalla de ancho y alto especificado en los parámetros. La
pantalla es representada mediante una matriz de máximo 15x15 de tipo
carácter. Por defecto cada casilla del array esta inicializado con el carácter #.
A partir de esta matriz podemos definir pantallas de 12x5, 9x9, etc., según
deseemos.
■ dibuja(anchoPantaIla, altoPantalla). Dibuja el rectángulo en una pantalla
de ancho y alto pasado como parámetro. Se deben comprobar estas
coordenadas tal que no superen 15. Además, debemos hacer uso del método
esValidoQ para saber si podemos dibujar.
■ Desarrolla una aplicación que haga uso de la clase creada.
10. Modifica la Actividad 9 de forma que las coordenadas vengan representadas por una
estructura de datos llamada coordenadas.
11. Modifica la Actividad 9 de forma que la Pantalla sea tomada como otro objeto.
Nuestro proyecto estará formado ahora de dos clases: Rectángulo y Pantalla. La
clase Pantalla tendrá una matriz de cómo máximo 15x15, usará un indizador para
acceder a ella. Los métodos esValido y dibuja recibirán ahora un objeto de tipo
Pantalla. Modifica la aplicación principal para adaptarla a las nuevas definiciones de
métodos y objetos.
Capítulo 6. Programación orientada a objetos 275

12. Amplía el programa de la Actividad 11 de forma que añadas las clases Triangulo y
Circulo tal que en la misma pantalla puedas dibujar todas las figuras geométricas
definidas. Configura las clases con constructores, destructor, propiedades y
sobrecarga de operadores si lo crees oportuno.
13. Crea un nuevo proyecto e importa a este la clase Persona creada en la Actividad 6.3
y la clase ListaPersona de la Actividad 6.10. En esta última añade las siguientes
sobrecargas de operadores:
■ Sobrecarga del operador +, sustituirá a la función insertar persona en la lista.
No devuelve ningún valor ya que la modificación la realiza sobre los
parámetros. Los operandos serán la lista de personas y la persona que
queramos añadir tal que cuando en el programa principal escribamos
arrayPersonas+Persona se agragará esta persona al array a la primera
posición vacia.
■ Sobrecarga el operador -. Este debe hacer la operación contraria a la suma.
Eliminará la persona que indiquemos como segundo operando. El operador
debe buscar la persona y si existe será eliminada, en caso contrario mostrará
un mensaje de error "La persona a eliminar no existe".
■ Si se te ocurren otras sobrecargas ponías en práctica.
14. Estamos pensando en crear un videojuego. Para ello debemos desarrollar un objeto
por cada componente de este. Empezamos creando una clase llamada Marciano. Esta
clase almaeenar infonnación relativa al alto y ancho del objeto, una matriz del ancho
y alto especificados donde dibujaremos el objeto, el carácter representativo de este,
etc. El objetivo es que tengamos un marciano dibujado en pantalla que sea similar a
esto:

Cuando se ejecute el programa principal, debemos tener dibujado en pantalla im objeto de


tipo Marciano (la tabla muestra la matriz donde se alojan los caracteres que forman la figura, no
se muestran tal cual en pantalla). El objeto se visualizaría realmente asi:

Definir un objeto Pantalla que delimitará la zona en la que se puede mover el marciano.
Crear un programa principal tal que al pulsar una de las teclas de flecha el Marciano se desplace
una posición siempre que sea posible, es decir, no llegue al limite de la pantalla. Usa métodos del
tipo Console.ReadKey para averiguar qué tecla ha pulsado el usuario.
15. Crea una pequeña aplicación de consola que gestione una tienda de móviles. La
tienda distribuye dispositivos móviles tales como télefonos y tablets. Se mantiene
información sobre todos los productos de la tienda, elimina o inserta un nuevo
producto, visualiza precios individuales según producto solicitado, etc. Echa un poco
de imaginación a la actividad y crea todos los métodos y atributos que consideres
necesitará la aplicación.
CAPITULO 7

LENGUAJE DE PROGRAMACIÓN JAVA

CONTENIDOS OBJETIVOS

Características del lenguaje de Introducir al lector en un lenguaje


programación básica. de programación cada vez más usado
como Java.
Tipos de datos simples.
Elementos del lenguaje. Entender el uso de los elementos

Estructuras de control y tipos de de un programa en Java para crear


aplicaciones de menor o mayor
datos compuestos.
complejidad.
Programación orientada a
Conocer cómo define Java tipos
objetos en Java.
simples y complejos, estructuras de
control, clases, etc.

RESUMEN DEL CAPÍTULO


En este capítulo comenzamos el estudio del lenguaje de programación Java. Se verá la
sintaxis de las estructuras vistas a lo largo de los anteriores capítulos. Java es un lenguaje de
programación muy usado hoy día y se cree oportuno que el lector sepa configurar programas
a partir de él. El libro esta pensado para que el lector afiance sus conocimientos de
programación en los lenguajes más usados en la actualidad.
7.1. INTRODUCCION
Hasta ahora hemos usado como lenguaje de programación C# y gracias a él hemos podido
entender los paradigmas de programación estructurada y orientada a objetos. Los elementos de
un programa son siempre similares, nomialmente encontraremos sentencias alternativas,
repetitivas, variables, constantes, etc., la diferencia estriba en las palabras reservada y cómo se
definen en el lenguaje de programación concreto.
En este capítulo vamos a estudiar cómo se declaran las sentencias que ya se han estudiado, en
el lenguaje Java, conociendo algunas de sus peculiaridades.

7.2. LENGUAJE DE PROGRAMACIÓN JAVA


Si accedemos a la web de Oracle leeremos;
"Java es la base para prácticamente todo tipo de aplicaciones en red y es el estándar
mundial para el desarrollo y distribución de aplicaciones móviles,juegos, contenido basado en
web y software empresarial. Con más de 9 millones de desarrolladores en todo el mundo. Java
le permite desarrollar e implementar eficientemente aplicaciones y servicios interesantes. Con
herramientas completa, un ecosistema maduro y sólido rendimiento. Java ofrece portabilidad de
aplicaciones en los entornos de computación más dispares".
Este pequeño párrafo define claramente Java, siendo uno de los lenguajes de programación
más usados actualmente. Fue desarrollado por James Gosling y Sun Microsystems en mayo de
1995 como lenguaje de desarrollo de Internet, aunque ya anteriomiente, en 1991 había sido
presentado como lenguaje de programación de componentes eléctricos. Su sintaxis es parecida a
los lenguajes C y C-H- aunque no posee tantas facilidades de acceso a bajo nivel como estos.
La primera versión aparece en enero de 1996, era JDK LO. Posteriormente, la versión JDK
1.1 en febrero de 1997 para en diciembre de 1998 surgir la versión J2SE 1.2 (nombre clave
Playground) a partir de la que empezamos a denominar a la platafomia como Java 2 y
reemplazar JDK por J2SE (Java 2 Platform, Estándar Edition) para distinguir la plataforma base
de las plataformas J2EE (Java 2 Platform, Enterprise Edition) y J2ME (Java 2 Platform, Micro
Edition).
A día de hoy, la última versión lanzada al mercado O'ulio de 2011) ha sido la denominada
como Dolphin.

7.2.1. CARACTERÍSTICAS DE JAVA2SE 7


Las características que diferencian Java de otros lenguajes de programación se enuncian a
continuación:

■ Independencia de la plataforma. Con Java desarrollamos aplicaciones que pueden


ser ejecutadas bajo cualquier hardware o la mayoría del hardware del mercado, y bajo
la mayoría de sistemas operativos. Se genera un bytecode' que posterionnente será
traducido para la máquina donde deseemos ejecutar el programa.

Recordamos que bytecode es un código intermedio anterior al lenguaje máquina que posteriormente
es traducido para el equipo donde se esté usando.
Capítulo 7. Lenguaje de programación Java 279

■ Alto rendimiento. Genera aplicaciones rápidas y más óptimas que otros lenguajes de
programación.
■ Fácil de aprender. Java es un lenguaje de programación cada vez más usado en
entornos educativos, ya que es un lenguaje provisto de herramientas que lo
configuran como un entorno amigable y relativamente fácil de usar. Además, el
manejo de memoria, hilos y errores de ejecución (excepciones), lo denotan de
bastante potencia para desarrolladores tanto principiantes como especializados.
■ Basado en estándares. Java y su tecnología asociada avanzan gracias a Java
Community Process. Java Community Process es im proceso formalizado que
pennite que se definan nuevas versiones y características de la platafonna. Se puede
localizar más información en la referencia web http://www.jcp.org/en/home/index.
■ Uso a nivel mundial. Java es una platafonna libre con lo que su uso se ha extendido
a nivel mundial, tal que dispone de multitud de desarrolladores (número que aumenta
progresivamente en el tiempo), atraídos por la gran cantidad de información, librerías,
herramientas, etc., que existen.
■ Entornos runtime consistentes. Un entorno runtime o entorno de ejecución actúa
como un intennediario entre el sistema operativo yJjava. En Java esta formado por la
Máquina Virtual de Java o JVM, un conjunto de bibliotecas y otros elementos
necesarios para que una aplicación Java sea ejecutada.
■ Optimizado para el control de dispositivos embebidos. Proporciona soporte para
dispositivos embebidos.
■ Recolector de basura. Al igual que C# usa este elemento de forma que elimina
automáticamente aquellos objetos que no referencien ningún punto en memoria, es
decir, hayan sido asignados a nuil en im momento determinado.

NOTA: Debido a que C# proviene de una convergencia entre C, C-H- y Java, el


estudio de este último lenguaje de programación no nos será complicado. j

7.3. ESTRUCTURA DE UN PROGRAMA JAVA


En un proyecto Netbeans podemos incluir tantos ficheros de código fuente como queramos.
Entre ellos debe existir uno que contenga la función principal que será ejecutada al pulsar el
botón de iniciar ejecución del entorno de desarrollo.

NOTA: Aunque usamos entornos de desarrollo integrados para facilitamos el trabajo


de creación de código fuente, compilación y posterior ejecución, podemos comprobar
nuestros ficheros Java a partir de aplicaciones de consola. El JDK incluye herramientas
tales como javac.exe para la compilación del código fuente, java.exe que permite la
ejecución de ficheros Java ya compilados o javadoc.exe que permite la generación de
documentación.

En el Capítulo 1 estudiábamos cómo crear im nuevo Proyecto de Netbeans, concretamente en


el Apartado 1.9.3. CREAR UN NUEVO PROYECTO EN NETBEANS 7.3. La creación de este
nuevo proyecto incluye un único fichero ejecutable (siempre que hayamos configurado este para
que lo sea en la casilla de verificación correspondiente a la hora de crearlo), se denominará igual
que el proyecto y tendrá la extensión .java.
Cuando estemos realizando una aplicación en Java, el fichero que contiene el código fuente
posee la extensión .java mientras que una vez se ha compilado, el fichero de código intennedio o
bytecode posee la extensión .class. Este último fichero será el que debamos usar para ejecutar
nuestro programa en cualquier sistema, siempre que disponga de la máquina virtual que sea
capaz de procesarlo.
Al igual que ocuría en C#, el fichero de código fuente de Java se encuentra encapsulado entre
las llaves de una clase y un paquete o espacio de nombres. En Java la clase ejecutable debe
llamarse igual que el proyecto, posterionnente podremos añadir nuevos ficheros o clases con
nombres representativos al tipo de datos que configuran.
package prueba; Observamos la clase Prueba. Esta
comienza en la línea public class
Prueba.

Entre las llaves de esta clase


" 0autíior -safcei
incluiremos atributos y métodos, tantos
public class Prueba { como necesitemos en nuestro código para
posteriormente realizar llamadas a ellos
en la función mainQ. Su sintaxis es
^ Gparaii args rúe ccimand liue argurr.euus similar a la vista en C# a diferencia de
*/ que la palabra reservada main se escribe
public static void main(String[] args) {
completamente en minúsculas.
// IC3C cede applrcauicn Icgic r.ere
} El formato de cabecera nos es muy
conocido:

public static void main (String []


Figura 7.1. Estructura de un programa Java.
args){...}.
Como ocurre en C#, la función es estática y a su vez pública para que pueda ser ejecutada.
Además no devuelve ningún valor (uso de void) y utiliza como parámetro un array de cadenas de
caracteres que constituirán las opciones que el usuario podrá introducir a la hora de ejecutar el
programa en la consola de comandos. Como en Visual Studio podemos establecer estos
parámetros de entrada a través de NetBeans, para ello:
1. Clic con el botón derecho del ratón sobre el proyecto en el panel de exploración del
proyecto y en el menú contextual Properties.
2. En el cuadro de diálogo, en la zona de la izquierda donde se muestran las categorías
localizaremos la opción Run y clic sobre ella.
3. Veremos el nombre de la clase que incluye la función main en el cuadro de texto
Main Class y justo abajo el cuadro de texto Arguments.
4. En el cuadro de texto Arguments escribiremos todas aquellas opciones que queramos
se tomen como parámetros por el programa principal. Separamos estas mediante un
espacio en blanco.
5. En la próxima ejecución del programa el array args tomará los valores de Arguments
tal que args[0] referirá el primer valor escrito, args[l] el siguiente valor después del
espacio en blanco, etc.
Capítulo 7. Lenguaje de programación Java

Q Project Properties - Prueba

CcKnfíguration: <de^uita>nf¡g>
Ubraries
S o Buld
Cwnpdng pnjeba.Prutí»
o Pad'^ging
' >-> Documentmg Arguments:

Woriang Directory:
0 O Appbcabon
-> Web Start VM Opbons:
o Formattmg
(e.g. -XmsU^)

Run vuth Java Web Start


(To run and debi^ the appkation v\ith Java Web Start,íwst enable Java Web StarQ

Figura 7.2. Cuadro de diálogo de propiedades de un proyecto en Netbeans.

Además de la función maín y la palabra reservada package ^^seguida del nombre del
paquete al que pertenece la clase, en un programa Java solemos encontrar una o varias líneas
precedidas por la palabra import ^^en la zona superior de la pantalla justo después del nombre
del paquete. Estas líneas incluyen referencias a otras clases ya creadas que incorporan
funcionalidad ya que penniten hacer uso de métodos creados en ellas en nuestro programa, son
equivalentes a los usíng en C#.

pacicage prueba;
irrpsrr java.io.*;' ''

public das» Pmeba {


public 3Z&ZL<z ir.c auna (ir.r r.l, ir.c r.5){
ir.b resulbado"0;
resultadc«ni^r.2;
rerurr. resultado;

) >
public static vcid main(Strmg(] args) <
int r.uir.i;
int nuir2;

Systert. c Jt.printlr.C'Zntrcduce un valor");


try {
BufferedReader teclado-.new BufferedReader(new InputStreaitReader(Syscen.ir.)) i
nuir.l-Integer.paraeint(teclado.readLine());
nuni2*"Integer.paraeint(teclado.readLine())?
Systeit. c ut.prir.tln(íuna (nunl,nun'.2));
> catch (ICExcepticn ex) {
System.out.println("Hrrc r ú-j £ 5");
>

Figura 7.3. Elementos comunes de un programa Java. Fichero Prueba.java.

Posterionnente se define la clase podemos incluir más de una clase en el mismo fichero
siempre que solo una de ellas sea pública, esta debe denominarse de igual modo que el fichero
.java como ya se ha indicando anterionnente. A continuación se establecen los datos y métodos
miembros que necesitemos. En el ejemplo se presenta ima función miembro de la clase llamada
suma que posteriormente será usada en la función principal dado que maín es una
función estática la función suma también debe serlo.

Antes de comenzar a profundizar en el lenguaje realicemos algunas comparaciones rápidas


con C#:

Java

Uso de ficheros extemos


using import
Identificación del paquete o espacio de trabajo
namespace package
Método para la salida de datos por el dispositivo estándar
ConsoIe.WriteLineO System.out.printlnO
Lectura de información a partir del dispositivo de entrada estándar
ConsoIe.ReadLineO BuíferedReader (teclado)
tecladc.readLineO
Conversión de tipos
int.ParseO Integer.parselntO

NOTA: En Java, de forma automática al incluir determinadas sentencias (por ejemplo


BufferedReader... teclado.readLine()), se precisa la inclusión de mecanismos de
detección de errores en tiempo de ejecución, es decir, control de excepciones, de ahí las
instmcciones try...catch. Estudiaremos con mayor detenimiento el control de errores de
ejecución en posteriores capítulos.

7.3.1. PALABRAS RESERVADAS EN JAVA

En Java se determinan como palabras reservadas las que se incluyen en la siguiente tabla.
Estas no pueden ser usadas por desempeñar otra función que no sea para la que fueron diseñadas.

abstract continué siwtch boolean default

synchronized break package

double implements prívate threadsafe byvalue

import protected extends instanceof

public transient
Capítulo 7. Lenguaje de programación Java 283

7.3.2. IDENTIFICADORES EN JAVA

Un identificador en Java se distingue por las siguientes características:


■ Debe comenzar por una letra, el símbolo $ o subrayado (_).
■ Está eonstituido por una combinación de letras y números.
■ Existe distinción entre mayúsculas y minúsculas.
Como en C# podemos usar palabras acentuadas y el carácter ñ, aunque no se recomienda.
Además, se aconseja:
■ euando un identdificador esté fonnado por dos o más palabras pasaremos a
mayúsculas el primer carácter de la segunda palabra en adelante, por ejemplo,
resultadoSuma.

■ si el identificador se encarga de dar nombre a una clase o a una interfaz lo


denominaremos comenzando con una letra en mayúsculas, por ejemplo class
Cuadrado.

■ al definir constante usaremos siempre palabras con todas sus letras en mayúsculas,
por ejemplo PI.
Como ya sabemos debemos escoger identificadores claros y, como su nombre indica, que
identifiquen adecuadamente la variable, constante, función, etc. Nunca podemos usar como
identificador alguna de las palabras reservadas estudiadas en el Apartado 7.3.1.

7.3.3. COMENTARIOS EN JAVA


La sintaxis de un comentario en Java es la siguiente:
--- - — — — ^ —-— — — — --— —I

//Comentario de una sola línea \


/^Comentario de más \
de una línea*/ \

/**Comentario de documentación */ ;
Un comentario de documentación se utiliza en la generación de documentación automática.
Java dispone de una herramienta llamada javadoc.exe que usa los comentarios encasillados entre
!** y */ para generar un documento que resume las características de los componentes de nuestra
aplicación.
Veamos un ejemplo de uso de javadoc, vamos a desarrollar documentación para el código
mostrado en la Figura 7.3; incluiremos algún comentario de documentación.
package prueba;
import java.io.*;

public class Prueba {


^ "k "k
* Función suma —> Realiza la suma de los operandos incluidos como
parárateros
* @param ni —> Primer operando, debe ser entero
"^2 --> Segundo operando, debe ser entero
* Qreturn —> Resultado de sumar ni y n2
*/
public static int suma(int ni, int n2){
int resultáño=0;
resultáño=nl+n2;
return resultáño;

* Función principal
* 0param args —> Aunque se define no vamos a incluir opciones en el
código
*/
public static void main(String[] args) {
int numl;
int num2;
System.out.println("Introduce un valor");
try {
y **
* BufferedReader; Objeto para el control de entrada de
información desde teclado
*/
BufferedReader teclado=
new BufferedReader(new InputStreamReader(System.in));
numl=Integer.parseint(teclado.readLine());
num2=Integer.parseint(teclado.readLine ());
System.out.printIn(suma(numl,num2));
/**
* Captura de errores en la entrada/salida de información
*/
} catch (lOException ex) {
System.out.println("Error de E/S");

Para generar la documentación:


1. Clic en Run.
2. A continuación clic en Generate Javadoc.
3. Pasados unos segundos se abrirá nuestro navegador con la documentación generada
ya que javadoc genera documentación en formato HTML.
AII Classes
Pnjéba Packaoe Class Use Tree Deorecated Index Help
F=£V FACKAGE K=XT FAD-AC-E

Package prueba

Class Summarj'

Packaoe Class Use Tree Deprecated Index Helo


F?Ev FACIASE KEXT FA.CK.,

Figura 7.4. Página de inicio de documentación para la aplicación de ejemplo que incluye la clase Prueba.
Si hacemos clic en la clase, visualizaremos su contenido y los comentarios que anterionuente
hemos incluido entre las líneas de código fuente.
Capítulo 7. Lenguaje de programación Java 285

Constructor Detail

Prueba

putl:.c Prueba(

Method Detail

Comentario de documentación^ I
Función sinna --> ReaEza la suma de los operandos incluidos como parámteros

Parameters:
r.l —> Primer operando, debe ser entero
r.2 —> Segundo operando, debe ser entero
Retums:
—> Resultado de sumar ni v a2

putlic seacic void raain(íava.lar.c.Serinotl arga)

Función principal Comentario de documentación

Parameters:
ares - "> Aunque se define no \-amos a incluir opciones en el código
V '

Figura 7.5. Documentación generada por javadoc para el programa Prueba.

7.3.4. TIPOS DE DATOS EN JAVA


En Java encontramos tipos de datos primitivos y de referencia. Entre los tipos primitivos o
incluidos en el lenguaje:
■ Boolean. Equivalente al tipo de dato bool en C#. Acepta los valores true o false.
■ char. Abarca todos los caracteres, dígitos y símbolos según el formato UNICODE.
■ Tipos numéricos. Son aquellos que representan números. Estos pueden ser enteros o
reales:

Tipos enteros: byte (-128 a 127), short (-32768 a 32767), int (-2^' 2^'-l) y
long(-2" a 2"-l).
—> Tipos reales: float (-3.4x10^^ a -1.4x10^^, 1.4x10"^^ a 3.4x10^^) y double (-
1.8x10^°^ a -4.9xl0-^^\ 4.9x10"^^^ a 1.8x10^°®).
Recordamos que los tipos referencia eran aquellos que generaban variables que apuntaban a
una dirección de memoria en lugar del dato concreto. Entre estos encontramos los objetos de
Java que hayan sido creados por el usuario o propios del lenguaje, además de los tipos de datos
compuestos como arrays.
7.3.5. LITERALES EN JAVA

En el Capítulo 3 dedicado a C#, concretamente el Apartado 3.3.5.2. LITERALES, se


estudiaba qué era un literal y los tipos de literales en C#. En java:
■ Literales enteros. Por defecto cualquier número entero en una sentencia es tomado
como tipo int. Si queremos que sea tenido en cuenta como long agregamos al número
la letra L o 1.

■ Literales reales. En cuanto a los números reales, cualquier número decimal es


tratado como double. Se puede modificar esta característica agregando la leti-a F
(mayúscula o minúscula) al número, y así sea interpretado como float. Podemos usar
el carácter e y representar valores en formato base"'^'"'"''""'.

NOTA: Los literales numéricos se pueden representar en el sistema de numeración


decimal, octal o hexadecimal. Si comenzamos el número en Ox estaremos indicando que
se está usando el sistema hexadecimal. En caso de que comencemos con el valor O
estaremos diciendo al compilador que el sistema de numeración usado es el octal. En
cualquier otro caso se interpretará el uso del sistema de numeración decimal.

■ Literales lógicos. Los literales lógicos serán los que usemos a la hora de dar valor a
variables de tipo booleanas: true y false.
■ Literales carácter. Los literales de este tipo se representan mediante el uso de
comillas simples ('). Son literales de tipo carácter todos aquellos valores
alfanuméricos incluidos en el código ASCII. Entre este tipo encontramos una serie de
valores especiales (secuencias de escape):

Secuencia
carácter
de escape

Salto de línea

Tabulación

Retomo de carro

Comilla simple
Contrabarra

Comillas dobles

Salto de página
Carácter nulo

Literales cadena de caracteres. Las cadenas de caracteres serán representadas


mediante el uso de comillas dobles,"cadena".
Literal nulo. El literal nulo se representa mediante el carácter nuil.
Capítulo 7. Lenguaje de programación Java

7.3.6. DECLARACION DE VARIABLES EN JAVA

La declaración de variables en Java es similar a la que ya hemos estudiado en C#, la


diferencia estriba en las palabras reservadas que cada lenguaje use para definir los tipos de datos.

típo_dato nombre_yariable:
tipojiato nombre_yariable=literal;
tipo_dato vari, var2, var3;

int num=3;
float reales=2.4f;
char letra;
boolean encontrado;
encontrado=t.rue;

NOTA: Cada instrucción en Java finaliza igualmente en punto y coma (;).

Al declarar una variable Java, si esta no es inicializada, adquiere un valor por defecto. Así, las
variables de tipo numérica, tomar el valor cero, las variables booleanas el valor false, los tipos
String (cadenas de caracteres) u otros objetos el identificador nulo nuil y los caracteres el valor
'\uOOOO'.

7.3.7. CONSTANTES EN JAVA

Aunque const es una palabra reservada del lenguaje, actualmente no se usa para definir ima
constante. La palabra que antepone Java al tipo de dato de la constante que vamos a crear es
final. La sintaxis de declaración de una constante se muestra a continuación.

I final tipo_dato nombre_variable=identificador; :


La declaración de una constante es similar a la de una variable con la diferencia de que debe
incluir la palabra reservada final y como es obvio, debe ser inicializada justo en el momento en
que se crea, ya que recordamos que una constante no puede ser modificada en ninguna otra línea
del código fuente.
piiblic class HolaMundo {
static final double PI=3.1415;
static BufferedReader teclado=
new BufferedReader(new InputStreamReader(System.in));
static int radio;
public static void main(String [] args){
try {
System.Gut.println ("Introduce el radio");
radio=Integer.parseInt(teclado.readLine());
System.out.println("El area es:"+(2*PI*radio));
} catch (lOException ex) {
System.out.println("Error al recoger los datos");
}
7.3.8. CONVERSIONES DE TIPOS DE DATOS EN JAVA

Cuando creamos una variable o usamos literales, podemos convertir estos para adaptarlos,
asignarlos, a variables de otros tipos. La conversión se realiza de fonna implícita entre
determinados tipos de datos o explícita mediante la realización de casting o uso de las Clases
envolventes de las primitivas.

NOTA: Las clases envolventes de las primitivas son clases que referencian los tipos
de datos primitivos ya estudiados.
Tipo de dato Clase envolvente
b5de Byte
short Short
int Integer
long Long
float Float
double Double
char Character
boolean Boolean
Estas clases trabajan con el tipo de datos primitivos y su rango de valores
proporcionando métodos de conversión de tipos, comprobación, etc.
i int num=20; |
; Integer entero=new Integer(num); ;
; byte numeroCorto=entero.byteValue(); i

Los tipos de datos que ocupan un menor tamaño son convertidos implícitamente, es decir, si
hemos declarado una variable long y le asignamos el 34, aunque este literal se toma como entero
al ser long un tipo mayor convierte implícitamente el entero 34 a long. Igualmente ocurre con el
tipo de datos double y float, siendo double el de mayor tamaño, podemos asignar a un double un
float o int pero nunca al contrario.
La conversión mediante casting sigue la siguiente sintaxis:
variable — (tipo_de_dato)variable_tipo_mayor;
variable =(tipo_de_dato)expresión;

byte num=(byte)12;
char caracter=(char)199;

Las clases envolventes proporcionan métodos estáticos del tipo parseClaseQ, por ejemplo
parselníQ. Si usamos objetos de clases envolventes podemos utilizar métodos del tipo
tipoValueO por ejemplo doubleValueQ- Véase a continuación los siguientes ejemplos.
int num;
System.out.println("Introduce un número");
num=Integer.parseInt(teclado.readLine());
Capítulo 7. Lenguaje de programación Java

Integer dividendo=13;
int divisor=2;
doiable resul tado=dividendo.doubleValue()/divisor;

char digito;
System.out.println("Introduce un digito (0-9):");
digito=(char) System.in.read();
if(Character.isDigit(digito)) {
System.out.println ("0)<. Buen trabajo");
}
else {
System.out.println ("Te dije que escribieras un dígito");
}

ACTIVIDAD 7.1

Busca en la web o referencia aportada por el profesor las clases envolventes estudiadas en
este apartado y observa los métodos que incluyen. Localiza algún programa ejemplo que las
utilice y estudia su funcionamiento, si es necesario, ya que aún no se han estudiado todos los
elementos del lenguaje, pide ayuda al profesor.

7.3.9. OPERADORES EN JAVA

Como en C# los operadores en Java se clasifican en: aritméticos, relaciónales, lógicos, de


asignación, operador instanceof y operadores a nivel bits (bitwise).

7.3.9.1. OPERADORES ARITMÉTICOS

Nombre Descripción
Positivo Indica que el número es positivo. Operador unario.
Negativo Se usa para establecer el número como negativo. Operador
unano.

Smna Suma aritmética de dos valores.


Resta Resta aritmética de dos valores.
Multiplicación Multiplicación aritmética de dos valores.
División División aritmética de dos valores.
Módulo Obtiene el resto de la división entera.
Incremento Incrementa en uno el valor que contenga en operando. Es un
operador unario.
Decreniento Decrementa en uno el valor del operando. Igualmente unario.
El operador + puede usarse para la concatenación de cadenas de caracteres de la forma
cadenal + cadena2.

Los operadores de incremento y decremento procederán de igual modo que estudiábamos en


C#, debemos tener precaución a la hora de colocarlo antes o después de la variable a modificar
ya que aunque esta siempre cambia en un valor las variables asociadas a la expresión donde se
use el operador pueden adquirir datos diferentes en función de su posición. Véase el Apartado
3.3.9.1. OPERADORES ARITMÉTICOS del Capítulo 3.
7.3.9.2. OPERADORES RELACIONALES

Operador Nombre Descripción

Menor que (menor Devuelve verdadero si una variable es menor estrictamente


estricto) que otra.

Mayor que (mayor Devuelve verdadero si una variable es mayor estrictamente


estricto) que otra dada.
Similar a menor pero en este caso se incluye el valor
Menor o igual
úbicado a la derecha en la comparación.
Similar a mayor con la diferencia de que se incluye en la
Mayor o igual
comparación el valor que se encuentra a la derecha.
Igual a Comprueba si los valores de los operandos son iguales.
Distinto a Comprueba si los valores de los operandos son distintos.

7.3.9.3. OPERADORES LOGICOS

Operador Nombre Descripción

Proporcionara como resultado el valor true en caso de que las


ANDo Y
condiciones o variables booleanas que concatenan sean ciertas,
lógico
en cualquier otra combinación el resultado será falso.
Proporciona como resultado el valor false solo si las condiciones
OR u O lógico o variables booleanas que concatenan son ambas falsas. En
cualquier otra casuística se obtendrá como resultado final true.
Invierte el valor resultado de la condición o variable de tipo
Not o No 1'^' booleana sobre
resultado true allaaplicar
que seeste
aplique. Si lase
operador expresión
tomará ada como
false y a la
inversa.

Se asigna a una expresión un valor en función de la condición


Asignación establecida. La sintaxis es:
condicional
var=(condición)?valor_si_yerdad:valor_siJ'also;

class Circulo{
int radio;
public calculaArea()throws IOException{
BufferedReader teclado=new BufferedReader(
new InputStreamReader(System .in))
System.out.println("Introduce un nuevo radio");
radio=Integer.parseint(teclado.readLine());
System.out.prinln((radio<100)?PI*2*radio:"Radio excesivo");
}}
Capitulo 7. Lenguaje de programación Java 291

ACTIVIDAD 7.2

Realiza la Actividad 3.4 en Java. Usa System.out.println("texto") para mostrar los textos en
pantalla.

7.3.9.4. OPERADORES DE ASIGNACIÓN


El operador de asignación en Java es el símbolo =, un único símbolo igual que lo diferencia
del operador de igualdad(^). Como en C# podemos realizar asignaciones múltiples en la misma
línea (Véase Apartado 3.3.9.4.0PERAD0RES DE ASIGNACIÓN).
Formato Formato
Operador Descripción
largo corto

Incrementa el valor de la variable en la cantidad x=x+y; x+=y;


que se especifique.
i

Resta el valor de la variable en la cantidad que se x=x-y; x-=y;


especifique.
Multiplica el valor de la variable en la cantidad x=x*y; x*=y;
especificada.
Divide el valor de la variable en la cantidad que x=x/y; x/=y;
se indique.

Asigna el valor del resto tras la división de la x=x%y; x%=y;


variable con una cantidad especificada.

7.3.9.5. OPERADORES INSTANCEOF

El operador instanceof pemiite realizar una comprobación entre objetos, interfaces o arrays
de fonna que devuelve verdadero si el operando de la izquierda es exactamente igual al operador
de la derecha. Este operador presenta utilidad cuando trabajamos con clases que heredan unas de
otras.

Veamos el siguiente ejemplo, aunque aún no hemos explicado la sintaxis de sentencias de


control en Java, es muy similar a C#(recordamos que este último procede de Java, C y C-H-), y
al lector le resultará sencillo de interpretar.
piiblic class OperadorInstanceOf { I
I

!
I

pioblic static void main(String[] args) throws lOException { 1


Object [] numeros=new Object[5]; //(l) ;
i

doiible mediaEnteros=0.O; //(2) i


int totalEnteros=0; ;
double mediaReales=0.O; ;
int totalReales=0; I

I
I

numeres[O]=new Integer(10); //(3) 1


numeres[1]=new Double(2.4); i
numeres[2]=new Integer(15); ;
numeres[3]=new Double(3.4);
numeres[4]=new Character('a'); ;
for (int i=0;i<5;i++){ /7(4)

if(números[i] instanceof Integer){ //(5)


mediaEnteros+=Integer.parseint(numeres[i].toString ());
totalEnteros++;
}

else if (numeres[i] instanceof Deuble){ //(6)


mediaReales+=Deuble.parseDeuble(numeres[i].teString ());
tetalReales++;
}
}

System.eut.println("La media de les numeres enteres es;"


+mediaEnteres/tetalEnteres); //(7)
System.eut.println("La media de les números reales es:"
+mediaReales/tetalReales); //(8)

■ (1). Creamos un array de Objetos (Object). De esta clase heredan todas las demás.
Aún no hemos estudiado el concepto de herencia así que podemos decir algo así
como que si creamos un array de este tipo posteriormente, al ser la clase de la que
heredan todas, cada casilla puede ser inicializada con cualquier tipo de objeto que
deriva, por ejemplo Integer, BufferedReader, etc.
■ (2). El objetivo de la aplicación es obtener la media aritmética de todos los números
enteros y reales insertados en el array a partir de objetos Integer o Double, por esa
razón se crean las variables inediaEnteros, mediaReales, totalEnteros y
totalReales.

■ (3). Inicialización del array. Al ser un array de objetos, cada casilla debe ser
inicializada a partir del operador new como ocurría en C#. Si observamos en cada una
de ellas incluimos diferentes tipos, Integer, Double o Character. Recalcar que esto es
posible gracias a la herencia. Estudiaremos en profundidad estos conceptos en
capítulos posteriores.
■ (4). El bucle Para (for) se encargará de recorrer el array de objetos llamado números
y analizar cada casilla.
" (5). En este punto entra en juego el operador instanceof. Para cada casilla
comprobamos si la instancia de clase u objeto almacenado es de tipo Integer, si es así
usamos su valor para almacenar en mediaEnteros, si no, comprobamos la siguiente
condición que nuevamente usa el operador.
■ (6). En esta segunda sentencia (else if) comprobamos si el tipo de objeto almacenado
en el array es una instancia de Double, si es así, recogemos su valor en mediaReales.
■ (7). Estas instrucciones muestran los resultados obtenidos tras realizar la media
aritmética de números enteros y reales.
Clase instanceof Objeto(instancia) i

7.3.9.6. OPERADORES A NIVEL DE BITS

Como en C# Java dispone de operadores que trabajan a nivel de bits.


Capítulo 7. Lenguaje de programación Java 293

Operador Descripción Ejemplo

AND o Y lógico a nivel de


i& 13
bit.

GR y O lógico a nivel de bit.


XOR. 13

NOT o No lógico a nivel de


bit.

Desplazamiento a la
izquierda, cada bit es
recorrido x posiciones a la
izquierda.
Desplazamiento a la derecha.
» / »> Cada bit es recorrido x bits a
la derecha.

Complemento.

3.3.9.7. PRIORIDAD DE LOS OPERADORES

□ O
++ - ! ~

new (tipo_dato)expresión
* / %

« » »>

<><=>=

= += .= *= /= %= A=: 1= »>=
7.3.10. OPERACIONES DE ENTRADA/SALIDA DE DATOS EN
JAVA
En apartados anteriores en este mismo capítulo hemos usado métodos que permitían la
entrada/salida de datos. En estos párrafos vamos a estudiarlos en profundidad.
Hasta el momento, tanto en Java como en C# estamos realizando aplicaciones de consola, y
según esto las operaciones de entrada y salida de datos se realiza a través del teclado o la consola
del sistema, los dispositivos de entrada/salida estándar.
Los objetos que referencian la entrada y salida estándar son System.in y System.out
respectivamente.
7.3.10.1. MÉTODOS PARA LA VISUALIZACIÓN DE INFORMACIÓN A
TRAVÉS DE LA SALIDA ESTÁNDAR
A la hora de escribir información en pantalla la sintaxis a seguir será la siguiente;

System.out.métodoQ;
//Siendo método cualquier método de impresión de información en la salida estándar

System.out.println ("Texto"); /*E1 método println("texto") escribirá el texto


incluido entre paréntesis además de un salto
de linea*/

Además de printlnQ existen otros muchos métodos:

Método Descripción

rintln(char)
println(char[]) El método println() se sobrecarga de fonua
rintln(double) que podemos usar como parámetro cualquiera de
los tipos de datos primitivos, arrays, objeto
rintln(int) String y cualquier otro objeto derivado de
irintln(Iong) Object.
rintlnCi ava.Iang.Object)
rintIn(java.lang.String)
El método printQ imprime información en
pantalla sin realizar un salto de línea.
printO
Encontramos print(boolean), print(char),
print(char []), etc.
Permite escribir una cadena usando los
printf("cadena de formato",
valores de variables sin necesidad de utilizar el
variables)
operador +

En método printf("formato",valores) es similar a Console.WriteLine("formato",valores)


de C#. printfO simula la función con el mismo nombre usada en C y C++.

I System.out.printf("cadena deformato vari,...,vam); I

I System.out.println ("Los números son:%d - %d", numl, num2); •


Én la cadena de formato debemos incluir caracteres especiales que posterionnente serán
sustituidos por las variables o literales separados por comas. En el ejemplo la cadena de formato
muestra toda la información que se debe visualizar. En pantalla veremos el texto los números
Capítulo 7. Lenguaje de programación Java

son: seguido de un número entero (así lo denota %d), este será el valor almacenado en la primera
de las variables después de la cadena de fonnato (numl). A continuación se muestra un guión -
para finalizar con un nuevo número entero(%d)equivalente a num2.
Suponiendo que numl=2 y num2=4 la salida es: Los números son: 2-4.
A la hora de generar la cadena de formato, en Java se debe especificar con el símbolo
adecuado, el valor concreto que se va a referenciar. Por ejemplo, si en la cadena de control
escribimos %d la variable o literal que debe encontrarse en la posición adecuada debe ser entera,
en caso contrario se produce un error de ejecución, ya que los tipos de datos no serían los
mismos.

C# evoluciona en este aspecto con su método WríteLine.

Console.WriteLine("El mañero {0} es igual a {1}", numl,num2);


/*Entre ¡laves colocamos la posición del elemento ubicado después de la cadena deformato.
No se comprueba tipo de datos, pudiendo usarse las variables más de una vez en la cadena*/

System.out.printfC'El número %d es igual a %d", maní, num2);


/*E1 primer %d referencia según su posición al primer elemento después de la cadena de
formato, obligando además a que este deba ser entero*/
Los caracteres especiales que podemos usar con printf() son:

caracteres especiales Significado


%d Número entero
%c Carácter
%s Cadena de caracteres
%f Número real de tipo float o double
Número float o double que debe mostrarse en formato x dígitos parte
%x.yf
entera e y dígitos parte decimal
Número float o double que debe mostrarse en notación científica.
Muestra un valor dado en notación científica del tipo l.le+02. x
%x.ye %n
establece número de dígitos parte entera e y número de decimales,
incluyendo +e y -e
%o Número entero que debe mostrarse en octal
%h Número entero que debe mostrarse en hexadecimal

7.3.10.2. METODOS PARA LA INTRODUCCION DE INFORMACION A


TRAVÉS DE LA ENTRADA ESTÁNDAR
El Objeto que usamos para referimos a la entrada estándar en Java es System.in. Sin
embargo, a diferencia de System.out, este no goza de demasiado métodos de recogida de
infonnación, de hecho solo podremos usar el método readQ qtie recoge el número entero
equivalente al código ASCII del carácter que se haya pulsado en el teclado.

int num=Systein.in.read (); //(l)


System.out.printf("%c",(char)num); //(2)
■ (1). System.in.readQ espera a que el usuario pulse una tecla. Po ejemplo, si el usuario
pulsa la tecla 2 el valor almacenado en num será 50, valor correspondiente del código
ASCII.

■ (2). Si queremos mostrar por pantalla el carácter pulsado o utilizarlo en una sentencia
altemativa, repetitiva, etc., debemos realizar un casting a la variable tal y como se
especifica en esta línea.
Existen otros objetos Java que proporcionan nuevos mecanismos de recogida de información
de la entrada estándar. Los objetos InputStreamReader y BufferedReader penuitcn identificar
el flujo de información de entrada e incluir métodos para procesar este flujo.
Un Stream se refiere a un flujo de bytes, de forma que si observamos el nombre del objeto
InputStreamReader podemos deducir que está diseñado para encargarse de tratar un flujo de
entrada de datos (InputStream) que solo va a ser leído. A la hora de utilizar el objeto
InputStreamReader debemos indicarle qué flujo de datos va a usar.
La sintaxis básica para definir un flujo de datos de entrada no es más que la definición y
creación de un objeto de la clase InputStreamReader.
! InputStreamReader nombre_yariable=new InputStreamReader(flujo); !

InputStreamReader flujoEntrada=new InputStreamReader(System.in); .


Al incluir entre los paréntesis del constm^^ de InputStreamReader eí objeto System.in
estamos indicando que el flujo de datos que se debe capturar es el procedente de la entrada
estándar, por defecto el teclado.
Ahora bien, InputStreamReader mantiene métodos de lectura de información que no son
demasiado prácticos. Seguimos encontrando el método readQ pero tiene la misma funcionalidad
que en System.in. Para poder recoger información de teclado de fonna más cómoda, usaremos el
objeto BufferedReader. Este usará el flujo de entrada definido por el objeto InputStreamReader.
BufferedReader teclado=new BufferedReader(Objeto InputStreamReader); \

InputStreamReader flujoEntrada=new inputStreamReadar(System.in);


BufferedReader teclado=new BufferedReader(flujoEntrada);
Otra forma de realizar la definición y creación es:
— — —

i BufferedReader teclado=new BufferedReader(new InputStreamReader(System.in));


No es necesario que definámorñingúnivariable InputStreamReader, podemos proceder a la
creación directa entre los paréntesis que es donde va a ser usada.
Cuando realizamos un programa en el que el usuario debe introducir infonnación, la
probabilidad de que se produzcan errores de ejecución es alta. Para que no se den salidas
inesperadas Netbeans alerta al usuario de que debe incluir el código en un bloque try...catch.
Por lo pronto, como aún no hemos estudiado sentencias de control de excepciones, nos
limitaremos a hacer clic sobre el símbolo de bombilla a la izquierda de la línea de código y
seleccionar Add throws ciause for java.io.IOException.
Visual Studio no es tan restrictivo.
Capítulo 7. Lenguaje de programación Java 297

±nc num=SY3teia.in.read();

35 QSurround Stiatement with try-catch


36 <5 Surround Block with try-atch

Figura 7.6. Advertencia de captura de errores de ejecución. Insercción de sentencias de control de


excepciones.

ACTIVIDAD 7.3

Realiza las Actividades 3.6 y 3.7 del Capítulo 3 en Java.

7.3.11. SENTENCIAS ALTERNATIVAS EN JAVA

Las sentencias de control alternativas en Java son en sintaxis iguales a las vistas en C#.
SENTENCENCIA ALTERNATIVA SIMPLE

SENTENCIA ALTERNATIVA DOBLE


■ -■'-"'vi.'iV.'''
if(condición){
Sentencias;
}
else{
Sentencias;
}

int numerol,numero2
char Operación;
Buf feredReader teclado=new Buf feredReader (new InputStreamReader (System. in) ) ;

System..out .println ("Introduce el primer número");


numerol = Integer .parseint (teclado. readLine () ) ;
System.out.println ("Introduce el segundo número");
numero2= Integer.parseint (teclado.readLine() ) ;
System. out.println ("Indica operación a realizar (0-Suma/l-Resta) ") ;
operacion=(char)teclado.read() ;
if (operación == ~0') {
System. out .printf ("La suma de %d y %d es %d \n", numerol,
numero2, numerol+numero2);
}
else {
System.out.printf ("La resta de %d y %d es %d \n", numerol,
numero2, numerol-numero2);
}
298 Programación

SENTENCIAS ALTERNATIVAS MULTIPLES

Estructura alternativa múltiple 1 : Estructura alternativo múltiple 2


if(condicion_l){
Sentencias; switch(variable){
} case valor_1: Sentencias,
else if(condición_2){ break;
Sentencias; case valor_2: Sentencias,
} break;

else if(condición_n){ case valor_n: Sentencias;


Sentencias; break;
} defaiilt: Sentencias;
else{ break;
Sentencias;
}
Véase el Apartado 3.3.11.2. SENTENCIA ALTERNATIVA MULTIPLE del Capítulo 3, ya
que las consideraciones explicadas en él se tienen también en cuenta en el lenguaje de
programación Java.

ACTIVIDAD 7.4

Realiza las Actividades 3.8 a 3.11 del Capítulo 3 en Java. Observa que los codigos fuente son
muy similares, a excepción de las operaciones de entrada/salida de información.

NOTA: En Java, como en C#, los identifícadores son visibles entre el par de llaves
donde son definidos. Así, si creamos una variable entre las llaves de una función esta será
local a la función y solo podrá ser usada en ella.

7.3.12. SENTENCIAS REPETITIVAS EN JAVA

BUCLE WHILE

while (condición){
Sentencias;
}
' BUCLE DO
do{
Sentencias,
}while (condición),
'BÚCLÉ FÓR
for (inicialización; condición; modificación){ :
Sentencias; i
} i
Además de las sentencias repetitivas vistas en Java podemos usar las palabras reservadas
break, continué y return. Si el lector necesita refrescar su uso véase el Apartado 3.3.13.4.
Capítulo 7. Lenguaje de programación Java 299

SENTENCIAS DE SALTO: BREAK, CONTINUE Y RETURN C# donde se estudiaba su


funcionalidad.

int i= O; int 1=0;


while(i<1 O) { do { i for(int 1=0;i<10;i++){

i++;
)while(i<10);

ACTIVIDAD 7.5

Realiza un programa que pida un número al usuario y devuelva la suma de todos sus dígitos.

ACTIVIDAD 7.6

Realiza un programa que pida al usuario número y muestre por pantalla si estos son primos o
no. La aplicación finalizará cuando el usuario escriba -1.

ACTIVIDAD 7.7

Codifica los algoritmos resueltos en el Capítulo 2(1 a 6)usando el lenguaje de programación


Java.

7.4. FUNCIONES Y PROCEDIMIENTOS EN JAVA


En Java las funciones y procedimientos se declaran de forma similar a C# aunque existen
algunas diferencias en relación al paso de parámetros, ya que siempre son pasados por valor.

Procedimientos !
- ^

modificadores voidnombreJunción(tipol varl,...,tipon vam){ j

Funciones

modificadores tipo_devuelto nombre_función(tipol varl,...,tipon varn){

return expresión;

modificadores. Los modificadores proporcionan determinadas características de


visibilidad a la función. Si no establecemos ningimo el subprograma será tomado
como prívate, es decir, no será visible más allá de los límites de la clase donde se ha
declarado. Además de prívate, una función puede ser definida como public, static y
protected.

tipo_devuelto. Para funciones, el tipo_devuelto establece el tipo de datos del valor


que devolverá la función.

nombre_función. Identificador que distingue a la función del resto. Será el nombre


que usaremos para invocarla en cualquier otra función siempre que su visibilidad lo
penuita.
tipol vari. Tipo de datos y nombre que representa un parámetro pasado al
subprograma.

pufclic stacic inc suziza (int ni, inc n2){


return nl-rn2;

Figura 7.7. Función suma en Java.

7.4.1. PARÁMETROS
En Java los parámetros son siempre pasados por valor, es decir, siempre usamos una copia de
la variable que se utiliza en la invocación o llamada del subprograma, a excepción del tipo
compuesto array. Los objetos, pese a que podamos pensar que son pasados por referencia, son
pasados por valor, aunque si se realizan modificaciones sobre sus propiedades estas prevalecen
generando un pseudo paso por referencia. Véase el siguiente ejemplo para entender el concepto.
Supongamos que tenemos un proyecto en Netbeans que incluye la definición de una clase
llamada Ejemplo y la clase que contiene el programa principal llamada Parámetros. En
Ejemplo se define un único atributo de tipo entero, int numero; además de un constructor que
inicializa el atributo y los métodos necesarios para visualizar y modificar este.
public class Ejemplo {
//Atributo
int numero;

//Constructor
public Ejemplo(int n){
this.numero=n;
}

//método selector y modificador


public int getNumeroOí
return numero;
}
public void setNumero(int n){
this.numero=n;

Picamos el siguiente código en nuestra principal, junto a diferentes funciones para


comprobar cómo se realiza el paso de parámetros de objetos:
static void modificaObjeto(Ejemplo objE){ //(l)
Ejemplo objAux=new Ejemplo(30);
objE=objAux;
}
static void modificaObjeto 2(Ejemplo objE) { //(2)
objE.setNumero(30);
}
public static void main(String[] args) {
objEl=new Ejemplo(10); //(3)
objE2=new Ejemplo(20);
System.out.printf("Antes de usar la función, objEl=%d y objE2=%d\n"
,objEl.getNumero(),objE2.getNumero()); //(4)
modificaObjeto(objEl); //(5)
Capítulo 7. Lenguaje de programación Java 301

System.out. printf ("Después de usar la función modificaObjeto, objEl=%d


y objE2=%d\n",objEl.getNumero O,objE2.getNumero O); //(6)

modificaObjeto_2(objEl); //(7)
System.out.printf("Después de usar la función modificaObjeto_2,
objEl=%d y objE2=%d\n",objEl.getNumero(),objE2.getNumero()); //(8)

(1). El método modificaObjeto recibe como parámetro un objeto de tipo Ejemplo. La


función crea un objeto Ejemplo auxiliar con un atributo igual a 30 y se lo asigna al
objeto pasado como parámetro. Podemos pensar que tras usar la función el objeto
pasado como parámetro cambiará por el auxiliar, de forma que su atributo pasará a
vale 30.

(2). El método modiflcaObjeto_2 recibe como parámetro un objeto de tipo Ejemplo


pero en lugar de asignar a este otro objeto modifica su atributo número, de forma que
le asigna el valor 30.
(3). Ya en la función principal creamos dos objetos de tipo Ejemplo (estos fueron
definidos con anterioridad), el primero de ellos obJEl tiene un atributo numero con
valor 10, mientras que el segundo denominado objE2 tiene un atributo numero con
valor 20.

(4). Realizamos una primera comprobación. Mostramos por pantalla los atributos de
objEl y objE2 antes de ser modificados. En pantalla visualizaremos la fi-ase: Antes
de usar la función, objEl=10 y objE2=20.
(5). Usamos el primer método de modificación pasando como parámetro el objeto
obJEl. Al llamar al método, accedemos a su código y se ejecutan sus líneas de forma
que se genera un objeto Ejemplo auxiliar y se asigna a objEl.
(6). Al volver a comprobar los valores de nuestros objetos de prueba, observamos que
la salida que obtenemos es: Después de usar la función modificaObjeto, objEl=10
y objE2=20. No se ha producido modificación alguna, es decir, el objeto ha sido
pasado por valor de fonna que al realizar la asignación objE=objAux se usa una
copia del parámetro y al finalizar la función este sigue presentando el mismo valor
que antes de ejecutarse el procedimiento.
(7). Invocamos nuestro segundo subprograma de modificación. El objeto objEl es
pasado como parámetro pero en el interior de la función se modifica su propiedad, no
el objeto como tal.
(8). Al realizar una nueva comprobación mediante el uso de una cadena de
caracteres, observamos la frase: Después de usar la función modificaObjeto_2,
objEl=30 y objE2=20. Aunque el objeto no es pasado como referencia en su
totalidad o tal y como conocemos y hemos estudiado el paso de parámetros por
referencia, si se modifican sus propiedades los cambios permanecen una vez finaliza
la función.

ACTIVIDAD 7.8

Realiza las Actividad 4.1a 4.4 del Capítulo 4 en Java.


7.4.2. SOBRECARGA DE FUNCIONES

En Java podemos definir multitud de funciones con el mismo nombre siempre que contengan
diferente número de parámetros, parámetros de diferentes tipos o ambas cosas.
Así, podemos encontrar código fuente como el que sigue:
public void mostrar O {

public void mostrar(Objeto E){

public void mostrar(Objato E, int num){

A la hora de realizar la llamada no existirá ambigüedad, es decir, si escribimos mostrarQ el


compilador sabe que debe hacer referencia a la primera de las funciones, en caso de escribir
mostrar(Aux), siendo Aux de tipo Objeto, se procederá a ejecutar las líneas de código de la
función mostrar(Objeto E) y si invocamos a mostrar como mostrar(Aux, 10) estaremos
llamando al tercero de los subprogramas.

7.5. ARRAYS
La sintaxis de declaración y creación de un array en Java es similar a la vista con anterioridad
en el Capítulo 5 para C#.
~ — - — - -- — _ _ _ -—

tipo_dato[]nombre_array;//Declaración. •
nombre_array=new tipo_dato[TAM_MAXJ;//Reserva de espacio en memoria, creación. :
tipo_dato[]nombre_array=ne}v tipo_dato[TAM_MAX]; ¡

int [] array=new int[10] ;


char [] cadenaCaracteres=new char[20];
Accedemos a cada dato escribiéndó eTño^^ del array y su posición entre corchetes, así, si
quisiéramos conocer el contenido de la posición 3 de la variable array creada como ejemplo en el
recuadro un poco más arriba escribiríamos array[3]. Recordamos que la primera casilla de un
array está identificada con la posición cero^.
Como valor de posición podemos establecer un literal entero, variable entera o expresión que
como resultado proporcione un valor también entero positivo.
A la hora de inicializar un array en Java usaremos estructuras parecidas a C# con algunas
restricciones. La inicialización puede realizarse dato a dato cuando este vaya siendo necesario,
por ejemplo:
[//Declaración de variables del prograina____ j

^ No todos los lenguajes de programación que utilizan esta estructura compleja empiezan a contar en
cero la primera posición de un array. Existen lenguajes que trabajan en base 1, es decir, el primer Índice de
posición de una tabla es 1 no 0.
Capítulo 7. Lenguaje de programación Java 303

BufferedReader teclado=new BufferedReader(new InputStreamReader(System.in));


int array=new int[10];
doubke resultado=0.O;

//Inicialización del primer elemento del array


System.out.println("introduce el primer número");
array[0]=Integer.parseInt(teclado.readLine());

//Inicialización del cuarto elemento del array


System.out.println("introduce otro número más");
array[4]=Integer.parseInt(teclado.readLine ());

//Inicialización del Segundo elemento del array con las suma de las posiciones

array[2]=array[0]+array(4];
Para inicializar todos los elementos del array a la vez, usaremos normalmente estructuras
repetitivas que soliciten un valor al usuario o bien introduzcan un valor aleatorio o fijo a cada
celda.

La inicialización también puede darse en el momento de la creación del objeto.


1ipo_dato[]nombre_array=new tipo_dato[]{varl,...,varn};
tipo_dato[]nombre_array={varl,...,varn};
Los valores se incluyen entre llaves y serán asignados según su posición a cada casilla del
array. Netbeans genera un error si escribimos un valor entre los corchetes en la creación del
array. Se supone que al incluir los datos que debe contener la tabla de fonna implícita se está
indicando cuál es su TAM MAX.
int [] array=n6w int[4]{1,2,3,4); //ERROR
int [] array=new int[]{1,2,3,4 }; //Instrucción CORRECTA

En Java como en C# un array tiene una serie de métodos o propiedades asociados, entre estos
elementos encontramos la propiedad length que devuelve el número de valores que puede
almacenar el array.
int [] array=new int[10];
array[1]=3;
array[5]=4;
System.out.println ("El número total de valores
que podemos almacenar es:"+array.length);

7.5.1. MATRICES
Seguimos en Java haciendo uso de arrays de más de una dimensión. Su sintaxis es similar a la
usada en C# para matrices escalonadas. Introducimos un par de corchetes por cada dimensión
adicional. Usaremos normalmente arrays bidimensionales, llamados también matrices.
i tipo_dato[][]nombre=new tipo_dato[TAM_MAX][TAM_MAX2];

int [][]numeros=new int[2][];


números[O]=new int[]{1,2,3,4,5}
En el ejemplo anterior se denota el hecho de que son matrices escalonadas. La primera fila de
la tabla almacena un máximo de 5 caracteres mientras que la segunda un total de 9.

8 9

7.5.2. EJEMPLO PRÁCTICO DE USO DE ARRAYS. EL JUEGO


DEL AHORCADO
Vamos a programar el juego del ahorcado en Java. Haremos uso de todos los elementos vistos
hasta ahora, manteniendo un array de tipo earáeter eon la palabra a adivinar y otro en blaneo de
igual tamaño para ir mostrando al jugador.
Por si algún lector no recuerda en qué consiste el juego:
■ El programa elige una palabra que debe ser adivinada.
■ Eljugador, cada vez, preguntará si una letra concreta pertenece a la palabra.
-» Si la letra se encuentra en algún lugar de la palabra el jugador no será
penalizado y podrá seguir preguntando si otra letra fonna parte de la palabra

Si la letra no se encuentra en la palabra el jugador será penalizado. La


penalización consiste en ir mostrando partes de un dibujo de un ahoreado de
forma que este se va completando según falla el usuario.
■ El juego finaliza si el usuario acierta la palabra, con lo que ganará la partida, o se
eompleta el dibujo del ahorcado, indicando que ha perdido.
Crearemos las siguientes variables:
~ ~ — — I

//Almacena la palabra a adivinar I


char []palabraSecreta={'a','r','r','a','y'}; ;

/ Palabra que se mostrará al usuario durante la jugada, en principio|


inicializada a Su tamaño es igual al de la palabra Secreta, de hay que j
tomemos su tamaño para crear el array palabra*/ 1
char []palabra=new char[palabraSecreta.length]; ;
I

/*Matriz que se irá rellenando en función de los fallos que se vayan ;


produciendo. Muestra el dibujo del ahorcado.*/ ;
char [][]pantalla=new char[5][5]; ;
I
I

//letra almacenara la letra que el usuario seleccione en cada turno I


char letra; i
I

//La variable pos será usada para la búsqueda de la letra indicada por el I
usuario en la palabraSecreta. Si existe la letra se devolverá su posición en ;
esta variable, que a su vez será utilizada para rellenar el array palabra. ;
int pos; I

/*Las siguientes variables determinarán si un jugador gana la partida ;


(ganador) y contabilizan el número de fallos y aciertos según los cuales el 1
jugador será "ahorcado" o no*/ i
boolean ganador=false; i
int fallos=0; 1
Capítulo 7. Lenguaje de programación Java

int aciertos=0;

Entre las funciones que pueden sernos útiles tenemos:


/*Inicializa los arraya palabra y pantalla. El primero de ellos a través
uso de guiones (_) mientras que el segundo será cargado con el valor O*/

public static void inicializaJuego(char [] palabra, char [][] pantalla){


for(int 1=0;i<palabra.length;i++) {
palabra[i]=' ;
}
for(int 1=0;i<pantalla.length;i++) {
for(int j=0;j<pantalla[i].length;j++){
pantalla[i][j]='0';
}
)

/*Mostrará los arrays palabra y pantalla, observamos que tanto en la


inicial ización como en la visualización se usan bucles for, en pantalla
observamos un bucle for anidado. Al especificar pantalla[i].length estamos
controlando el hecho de que cada fila pueda tener un número diferentes de
casillas o columnas*/

public static void mostrar(char [] palabra, char [][] pantalla){


System.out.printIn ("Palabra");
for(int 1=0;i<palabra.length;i++) {
System.out.print(palabra[i]);
}
System.out.println();
for(int 1=0;i<pantalla.length;i++){
for(int j=0;j<pantalla[i].length;j++){
System.out.print(pantalla[i][j]);
}
System.out.printlnO ;

/*E1 método buscarLetra será de utilidad a la hora de comprobar si la letra


que el usuario indica se encuentra en la palabraSecreta. Este método pretende
ser similar al IndexOf que ya hemos usado en C#*/

public static int buscarLetra(char [] palabraSecreta, char letra, int


comienzo){
int posicion=-l;
i=comienzo;
while (i<palabraSecreta.length && posicion==-l){
if(palabraSecreta[i]==letra){
posicion=i;

return posición;
/*Método que irá modificando la matriz pantalla que se encarga de contener el
estado del "ahorcado". A cada nuevo fallo se dibujará una parte del
ahorcado.*/

piiblic static void dibujoAhorcado(int fallos, char [][] pantalla) {


switch(fallos){
case 1: pantalla[4][O]=pantalla[4][1]=pantalla[4][2]='0';
break;
case 2: pantalla[3][1]=pantalla[2][1]=pantalla [ 1 ][1]
=pantalla[0][1]='0';
break;
case 3: pantalla[0][2]=pantalla[O][3]='0';
break;
case 4: pantalla[1][3]='0';
break;
case 5: pantalla[2][3]='0';
break;
case 6: pantalla[1][2]='0';
break;
case 7; pantalla[1][4]='0';
break;
case 8: pantalla[3][2]='0';
break;
case 9: pantalla[3][4]=•0';
break;

/*Función principal*/
static void main(String[] args) throws lOException {
'^^s.r []palabraSecreta={'a','r','r','a','y');
char []palabra=new char[palabraSecreta.length];
char [][]pantalla=new char[5][5];;

char letra;
int pos;
boolean ganador=false;
int fallos=0;
int aciertos=0;

System.out.println("EMPIEZA EL JUEGO!!!");
inicializaJuego(palabra,pantalla);
mostrar(palabra,pantalla);

while (fallos <=9 && (ganador){


System.out.println("Letra:");
letra=(char)System.in.read();
if((pos=buscarLetra(palabraSecreta,letra,0))!=-l) {
palabra[pos]=palabraSecreta[pos];
aciertos++;
while((pos=buscarLetra(palabraSecreta,letra,pos+1))!=-l){
palabra[pos]=palabraSecreta[pos];
aciertos++;
}
)
else{
fallos++;
dibujoAhorcado (fallos, pantalla) ;_
Capitulo 7. Lenguaje de programación Java 307

mostrar(palabra,pantalla);
if(aciertos==palabraSecreta.length){
ganador=true;

if (ganador==true){
System.out.println ("Ganaste! I !");
}
else {
System.out.println("Ohhhi !! Has perdido!! i");

ACTIVIDAD 7.9

En el Capítulo 5 estudiábamos operaciones con arrays, tipos de búsquedas y métodos de


ordenación. Codifica en Java alguno de los algoritmos vistos.

ACTIVIDAD 7.10

Realiza la Actividad 5.14 del Capitulo 5.

7.6. OBJETOS STRING


Entre los tipos de datos primitivos o básicos del lenguaje de programación Java no
encontramos ninguno que esté diseñado para el almacenamiento y procesamiento de cadenas de
caracteres. Podemos usar arrays de caracteres, como hemos visto anteriormente, pero es algo
engorroso. Asi, debemos recurrir a clases diseñadas de forma que usan cadenas y proporcionan
métodos útiles para la gestión de estas. Clase String.
Cada vez que queramos declarar una variable de tipo cadena de caracteres debemos instanciar
la clase String. Existen varias fonnas de declarar un objeto de tipo String:

String nombrejyariable=new String(literal_cadena_caracteres);


String nombre_yariable=literal_cadena_caracteres;
String nombre_yariable=new String(char[Jarray);
String nombre_yariable=new String(variabIe_tipo_string);

string cadena="Hola Mundo";


String cadena2=new String("Hola Mundo");

char []caracteres={'H','o','1', 'a', ' 'M', 'u', 'n', 'd', 'o'};


String cadena3=new String(caracteres);

String cadena4=new String(cadena2);


Entre los métodos más usuales de variables tipo String encontramos:
■ char charAt(int índice). Devuelve el carácter ubicado en la posición índice.
int compareTo(Str¡ng cadena). Compara la cadena que usa el método con cadena
devolviendo un número entero menor que cero, mayor que cero o cero en función de
si la cadena es menor, mayor o igual alfabéticamente hablando.
int compareToIgnoreCase(String cadena). Compara dos cadenzas tal y como hace
compareTo sin tener en cuenta mayúsculas y minúsculas.
boolean equals (Object objeto). Compara el String con el objeto pasado como
parámetro devolviendo true si son iguales y false en caso contrario.
int indexOf(int carácter). Devuelve la posición de la primera ocurrencia de carácter
en la cadena de caracteres. Aunque el parámetro es de tipo entero, escribiremos
normalmente un carácter que será traducido implícitamente a su código ASCll. El
método permite tres sobrecargas, todas devuelven un número entero: ¡ndexOf(int
carácter,int inicio), indexOf(String cadena),¡ndexOf(String cadena, int inicio).
boolean isEmptyQ- Devuelve true si la cadena está vacía, es decir, su longitud es
cero.

int lengtbQ. Devuelve el número de caracteres de la cadena. No confundir con la


propiedad length vista en arrays.
String replace (char caracterAntiguo, cbar caracterNuevo). Devuelve una cadena
en la que se han reemplazado los caracterAntiguo por caracterNuevo.
String [] split(String expresión). Divide una cadena en partes teniendo en cuenta los
elementos de la cadena expresión, devolviendo un array de tipo String.
String toLowerCaseO- Devuelve un array con todos los caracteres de la cadena que
hace la llamada al método en mayúsculas.
String toUpperCaseQ. Devuelve un array con todos los caracteres de la cadena que
hace la llamada al método en minúsculas.

String trimO- Devuelve una copia de la cadena eliminando los espacios en blanco.
String vaIueOf(tipo variable). Devuelve la cadena de caracteres resultante de
convertir la variable del tipo pasado como parámetro.

ACTIVIDAD 7.11

Modifica el ejemplo del juego del ahorcado visto en el Apartado 7.5.2. de forma que en lugar
de arrays de tipo carácter uses objetos de tipos String.

public static String directorioTrabajoUsuario(String linea){


String noiiíbre="";
//usuariol:FXWUuZ.vwXttg:500:501:usuariol apellido
:/home/usuariol:/bin/bash
String []campos=linea.split(":");
return campos[5];

public static void main(String[] args) throws IGException {


String lineaFichero="isabel:x:1001:1001:
Isabel Jiménez:/home/isabel:/bin/bash";
System.out.println(directorioTrabajoUsuario(lineaFichero));
Capítulo 7. Lenguaje de programación Java

En el ejemplo se ha querido realizar una pequeña aplicación, que puede ser modificada
posteriomiente cuando conozcamos todos los elementos del lenguaje, convirtiéndose en un
software de interés para sistemas operativos Linux.
Los sistemas operativos Linux almacenan en un fichero llamado /etc/passwd los nombre de
los usuarios registrados en el sistemas e infonnación relativa a ellos como cuál es su directorio de
trabajo.
En el programa de ejemplo suponemos que analizamos una línea de este programa y
obtenemos su directorio de trabajo usando la función split. Posterionnente puede ser modificado
para que leamos directamente del fichero /etc/passwd línea a línea y podamos extraer más
infonnación.

ACTIVIDAD 7.12

Realiza un pequeño programa que dada una cadena de caracteres almacenada en un objeto de
tipo String devuelva el número de letras en mayúsculas y minúsculas.

ACTIVIDAD 7.13

Realiza un ejercicio tal que dada una cadena de caracteres devuelva esta invertida.

NOTA: Java, a partir de su versión 1.5, soporta un tipo de datos compuesto ya


estudiado en C#, las enumeraciones. Su función y uso son similares a las estudiadas en el
Capítulo 5, con la diferencia de que no se asocia un valor a cada elemento de la
enumeración. La sintaxis nos será familiar:

i enum nombre={vaIorl, valor!,...,valor3}; i

enum dias={lunes, martes, miércoles,...,domingo};


System.out.println(dias.lunes);

7.7. DEFINICION DE CLASES Y OBJETOS EN JAVA


En el Capítulo 6 hemos estudiado los conceptos fundamentales de programación orientada a
objetos, aplicables a cualquier lenguaje de programación orientado a objetos. En este apartado
vamos a estudiar la sintaxis que Java utiliza para la representación de clases e instanciación de
estas, así como métodos, atributos, etc. El código nos será muy familiar.
Declaración de clases:

modificador class nombre{


Propiedades;
Métodos;
}
La palabra reservada class se usa para la definción de una clase en Java. Ñónñafme^^
usamos el modificador public además de especificar el nombre que queremos asignarle a esta.
Una clase puede definirse como pública (public), protegida (protected), privada (private)
y de paquete (package), para ello sustituiremos la palabra modificador en la declaración por
una de las incluidas entre paréntesis.
■ public. Una clase pública es accesible desde cualquier clase en cualquier paquete.
■ prívate. Las clases privadas solo son accesibles desde el archivo en el que se
declaran.

■ protected. Son clases privadas para aquellas que no heredan de ella.


■ package. Cuando una clase no tiene modificador y es visible en todo el paquete, es
decir, para las clases que pertenecen al mismo paquete que esta. Así, la ausencia de
modificador es similar a usar package.

NOTA: Aunque en varias ocasiones, tanto en C# como en Java, hemos utilizado el


término espacio de nombres o paquete, aún no hemos dedicado tiempo a estudiarlo en
más profundidad. Dedicaremos un espacio en los próximos capítulos.

Declaración de atributos:
r 1

I tipo_dato nombre_atributo; i
Normalmente un atributo no suele incluir modificadores antes del tipo de dato ya que deben
ser privados o de la clase y no accesibles con facilidad. Si bien es cierto, existen propiedades
estáticas que deben ser públicas ya que suelen ser desarrolladas para ser usadas directamente
desde la clase y no desde instancias de esta.
Declaración de métodos en Java:
j ^

I modificador tipojdevuelto nombrejnétodo(tipoparí,...,tipo parn){ :


I Sentencias; •
; return expresión; ;
\} i
Los modificadores a usar con los métodos siguen siendo public, protected y private.
public class Figuras!
double radio;
final double PI=3.1415;

piablic double VolumenEsfera(){


return (PI*radio*radio*radio*4)/3;
}
public double AreaCirculo(){
return 2*PI*radio*radio;

En Java encontraremos diferentes tipos de métodos:


■ static. Un método estático o método de clase puede ser usado directamente desde la
clase en lugar de instanciar esta. Podemos crear igualmente atributos estáticos. Un
método tipo static usa variable definidas en la clase como estáticas.
Capítulo 7. Lenguaje de programación Java

■ abstract. Comprenderemos este método cuando estudiemos la herencia. Un método


se declara como abstract cuando no va a ser programado en la superclase pero sí en
las clases que heredan de esta.
■ final. Al declarar un método como final se está indicando que este no puede ser
sobrescrito.

■ native. Un método nativo es un método escrito en otro lenguaje de programación (C


y C++) que queremos incluir en nuestro programa y que en ese lenguaje presenta
mejor rendimiento. En Java se coloca la cabecera de esta sustituyendo el cuerpo por ;
(punto y coma).
■ synchronized. Se usa en aplicaciones multihilos.
Además, como en cualquier clase, debemos definir:
■ Constructores.

■ Selectores.

■ Modificadores.

NOTA: Java, al igual que C#, usa el denominado Recolector de basura (Garbage
collection) tal que pasado cierto tiempo aquellos objetos que no están referenciados,
apuntan a nuil, son eliminados de forma directa. En Java, no podemos definir destructores
por clase como hacíamos en C#, ya que el uso del recolector garantiza el cierre de flujos
de datos, eliminación de objetos, etc. El lenguaje dispone de una función, finalizeQ,
heredada por todas las clases desarrolladas y que se encarga de llamar a recolector de
basura para que se encargue de eliminar antes del tiempo previsto elementos no
necesarios. La sintaxis de la función es:

\ public voidfinalizeQ{ 'r ,yy''. ■ . ; .. y|


: Sentencias; .,r >
\I /l '■ -■. • r4'i

NOTA: Java no soporta la sobrecarga de operadores o el uso de propiedades.


Debemos plasmar su funcionalidad a través de los métodos de la clase.

La instanciaeión de un objeto sigue la sintaxis siguiente:

//Declaración
Nombre_Clase nombre_yariable;

//Creación
nombre_yariable=new Nombre_ClaseQ;

//Declaración y creación
Nombre_Clase nombrejyariable=new NombreJZlaseQ;
Es importante como en C# recalcar la necesidad de crear o usar el operador new para reservar
espacio en memoria, ya que si solo se realizará la declaración el objeto no podría ser usado.
Una vez instanciado un objeto, el acceso a los miembros de la clase se realiza a través del
operador punto (.). En Java, como también vimos en C#, usaremos el operador this para
referenciar a la propia clase, así como los métodos y atributos de esta.
Recordar que a la hora de crear arrays de objetos es necesario inicializar cada objeto de cada
casilla de la clase debido a que la declaración y creación del array no realiza esta operación. Asi,
a la hora de usar un objeto almacenado en un array debemos antes haberlo creado, ya sea en el
inicio de la aplicación o antes de ser usado.

//Declaración creación del array


Clase[]nombre_array=new Clase[TAM_MAX];

//Creación de los objetos de cada casilla -forma 1 (todos a la vez)


for(int i=0;i<TAM_MAX; i++){
nombre_array[i]=new ClaseQ;
}

//Creación de un objeto concreto -forma 2(deforma individual antes de ser usada)


nombre_array[pos]"^new ClaseQ;

7.1A. DESARROLLO DE PROYECTOS CON VARIAS CLASES EN


JAVA
Al igual que en C#, a la hora de desarrollar aplicaciones en las que incluiremos más de una
clase, usaremos ficheros independientes para definir estas. Seguiremos los pasos que a
continuación se dictan:

1. Crear proyecto en Netbeans.


2. A la izquierda, en el explorador de proyectos, veremos el nuevo proyecto. Al hacer
clic sobre el signo más junto a su nombre se expandirá este y veremos sus archivos.
3. Expandir la carpeta Source Package. Visualizaremos un paquete de igual nombre
que el proyecto pero con su primera letra en minúsculas.
•Q figuras
Figura 7.8. Simbología para representar paquetes Java en Netbeans.
4. Clic con el botón derecho sobre el paquete. Colocamos el cursor sobre New y a
continuación clic en Java Class.

5. Escribimos el nombre de la clase.

6. Clic en Finish. Desde este momento visualizaremos un nuevo fichero en el paquete.

NOTA: Normalmente solemos tener un paquete con todas las clases no ejecutables y
otro con clases ejecutables. Por lo pronto, y hasta que no se estudien los paquetes,
usaremos el package por defecto para almacenar todas.
Capítulo 7. Lenguaje de programación Java

ACTIVIDAD 7.14

Crea un nuevo proyecto en Java llamado Figuras e incluye en él una clase por cada figura
geométrica que prefieras. Corno mínimo usa Círculo, Rectángulo y Triángulo.

ACTIVIDAD 7.15

Configura cada clase de Figuras con los atributos y métodos que desees. Puedes incluir
métodos del tipo: Perímetro, Area o Mostrar. Los atributos deben ser los parámetros
fundamentales de cada figura geométrica, por ejemplo del círculo el radio.

NOTA: C# es un lenguaje que surge de C, C-H- y Java, las similitudes con Java son
muchas. Hemos estudiado que existen componentes que ircopora C#, debido a su
procedencia de C y C++ que no incluye Java, sin embargo si el lector ha entendido todos
los conceptos vistos para C# en Capítulos anteriores no le será complicado programar en
Java.

COMPRUEBA TU APRENDIZAJE
1. ¿Cuáles son las características principales de Java?
2. ¿Cuál es la estructura básica de un programa Java?
3. ¿Qué es un comentario de documentación? ¿Para qué se usa? ¿Qué es Javadoc?
4. ¿Cómo se realiza la conversión entre tipos de datos primitivos en Java?
5. ¿Para qué se usa el operador instanceof?
6. ¿Qué clases se usan para la introducción o salida de datos en Java en aplicaciones
de consola? ¿Qué métodos son los más usuales?
7. ¿Cómo se realiza el paso de parámetros en Java? Explica el paso de parámetros a
funciones.

8. ¿Cuál es la estructura básica de una clase en Java? ¿Qué tipo de atributos y


métodos podemos incluir? ¿Podemos definir métodos destructores?
9. ¿Cómo definimos arrays de objetos en Java?
10. ¿Es posible la sobrecarga de operadores en Java? ¿Cómo definimos propiedades?
314 Programación

ACTIVIDADES DE AMPLIACIÓN
1. Crea una clase llamada libro. Incluye los atributos que creas sean
representativos de un libro, constructores, métodos getter y setter (selectores y
modificadores) y la sobrecarga del método toString que devuelva una cadena
de caracteres con el nombre y autos del libro.
2. Crea una clase llamada ListaLibros que incluya un an-ay de libros. La clase
debe contener métodos que permitan gestionar el array, es decir, insertar un
libro, eliminar un libro, modificar o mostrar, etc.
3. Implementa una pequeña aplicación de consola que use las clases desarrolladas
en las Actividades 1 y 2 tal que presente un menú en el que puedas gestionar
los libros de una biblioteca.

4. Realiza un programa que pida al usuario un número decimal y compruebe si es


un valor real correcto. No usar clases envolventes, el programador debe
recoger la cadena de caracteres escrita desde teclado y analizarla para
posteriormente deducir si es un valor numérico correcto.
5. Crea una clase llamada estrella. El objetivo es que esta clase represente el
dibujo de una estrella, de forma que debe incluir un atributo de tipo array de
caracteres tal, que sirva para representar gráficamente el objeto y un método
mostrar que lo visualice por pantalla. Crea una aplicación de consola que use la
clase creada de fonua que cada x tiempo muestre una estrella en pantalla en un
lugar concreto.
6. Modifica la Actividad 5 de forma que puedan existir más de una estrella en la
pantalla a la vez. ¿Cómo enfocarías el problema? ¿Cómo procederías en la
resolución de la actividad?

7. Desarrolla en Java una clase que refleje una estructura tipo cola de números
enteros. Una cola es un conjunto de elementos tal que el último en llegar se
coloca al final de la cola y se van eliminando o sacando elementos de la cola
desde el principio de esta (recordad el funcionamiento de la cola del cine).
8. Crea una clase Persona de la que sólo debemos mantener infonuación
relacionada con su Nombre, Apellidos y NIF. Configúrala añadiendo los
métodos y propiedades que creas oportunas.
9. Usa las clases creadas en las Actividad 7 y 8 de fonna que desarrolles un
programa para un cine. La cola será una cola de personas (copiamos el código
fuente de cola y lo modificamos). La cola se rellenará con x personas y de
forma repetitiva se irá pidiendo a cada una el número de entradas que se desea
comprar (agregando esta característica a la clase) y las butacas (se mostrará el
aforo tal que el usuario pueda escoger un asiento). Una vez acabe la compra se
eliminará el usuario de la cola. Debemos mantener variables que almacenen el
número de entradas disponibles, número de entradas vendidas así como
información sobre la pantalla y el texto escrito (podemos crear una matriz de
20x20 como información de asientos en la sala).
Capítulo 7. Lenguaje de programación Java 315

10. Realiza una pequeña aplicación que pida al usuario que escriba su correo
electrónico y compruebe si este es correcto.
11. Crea una clase llamada Equipo donde almacenes información relativa a un
sistema infomiático: número de serie, modelo y marca de placa base,
características del micrprocesador, memoria RAM, etc. Incluye los métodos
que creas oportunos teniendo en cuenta que deben almacenarse además de las
incidencias que puedan producirse.
12. Crea una clase listaEquipos que pennita mantener una lista de ordenadores
(clase Equipo) de fonna que podamos realizar operaciones del tipo eliminar,
modificar o agregar.
13. Crea una pequeña aplicación de consola que gestione una serie de ordenadores.
Debes usar las clases anteriores.
CAPITULO 8

ENTORNO GRÁFICO

CONTENIDOS OBJETIVOS

Programación dependiente de Entender los componentes de una


eventos. Creación de aplicaciones aplicación gráfica y la aplicación en
gráficas. sí tanto en Java como C#.

Controles y eventos en C# y Saber los tipos de controles que


Java. Desarrollo de aplicaciones podemos usar, sus propiedades,
gráficas con múltiples fonriularios. métodos, así como los eventos que
Controles y propiedades. podemos aplicar sobre ellos.
Elementos contenedores y Saber desarrollar aplicaciones con
menús desplegables. múltiples formularios, menús y
barras de herramientas.

RESUMEN DEL CAPÍTULO |


Dados los tiempos que corren no podemos explicar cómo realizar programas sin
dedicar tiempo al desarrollo de aplicaciones gráficas. En este capítulo se estudian los
diferentes componentes de un proyecto visual tanto en Java como en C# y la denominada
programación orientada a eventos. Objetos contenedores, controles, cuadros de diálogo,
codificación de eventos, etc.
8.1. INTRODUCCION
Hasta ahora hemos profundizado en aspectos fundamentales de programación, hemos
estudiado las estructuras básicas, los elementos, clases y objetos. Hemos analizado los lenguajes
de programación C# y Java, de forma que a estas alturas sabemos cómo codificar los
componentes de un programa usando cualquiera de ellos.
Se han configurado proyectos tanto en Visual Studio como Netbeans con un conjunto de
clases pero todos ellos en modo consola. Hoy día, con ordenadores cada vez más potentes las
aplicaciones son sobre todo gráficas y fáciles de usar. Además los sistemas operativos se diseñan
en entornos gráficos muy conseguidos, a veces prima la fonna en la que se da a conocer o se
visualiza una aplicación que lo que puede hacer.
En este capítulo vamos a estudiar cómo desarrollar entorno gráficos en C# y Java, así como
los fundamentos de este tipo de aplicaciones en las que el usuario interactúa directamente sobre
los componentes.

8.2. PROGRAMACIÓN DEPENDIENTE DE EVENTOS


Todos los programas desarrollados hasta el momento siguen una misma pauta, presentan un
planteamiento secuencial, de forma que comienzan en un punto, va bifurcándose pero a fin de
cuentas somos nosotros los que desarrollamos el software, los que creamos desde el principio
hasta el final nuestro programa.
Cuando desarrollamos programas con entornos gráficos esto cambia ya que no existe una
secuencia en las instrucciones, o al menos en las instrucciones que conforman el programa como
un todo. En un programa con entorno gráfico se codifican cada parte significativa del entorno, de
forma que cuando el usuario interactúe con esta realice alguna acción.
Así, los programas diseñados con entornos gráficos se dicen que son programas orientados a
eventos, ya que el programa irá respondiendo a los diferentes eventos externos (se hizo clic en
un botón, se pulso el botón de cerrar ventana, etc.)
A la hora de crear aplicaciones gráficas estas serán ejecutadas sobre un sistema operativo
concreto y partimos de que conocemos el entorno, los componentes que suele utilizar y los
eventos o sucesos a los que responde. Cuando creamos un programa gráfico para Windows
sabemos que estará compuesto de ventanas, y que estas ventanas tendrán botones, cuadros de
texto, etc., que reaccionarán a clic de ratón, pulsaciones de teclas, etc.
A partir de ahora diseñaremos nuestras aplicaciones como respuesta a un conjunto de eventos.
Si observamos la Figura 8.1, veremos una ventana con un botón en el que se muestra la frase
"Cambiar texto". Además, la ventana cuenta con la barra de título y los botones de control
propios de cerrar, minimizar y maximizar ventana. Cuando el usuario haga clic sobre alguno de
los botones, o el propio formulario se ejecutará una acción si así lo deseamos, de forma que se
capturan los eventos o acciones que el usuario lleva a cabo y en respuesta la aplicación realiza
una secuencia de operaciones. Básicamente en esto consiste la programación orientada a eventos
en la que se basan los nuevos programas gráficos. Sí debemos saber qué tipos de eventos es
capaz de capturar el sistema operativo, de forma que cuando se produzcan nuestra aplicación esté
preparada.
Capítulos. Entorno gráfico 319

Hola Mundo!!

CamDtar Texto

Figura 8.1. Aplicación gráfica Java.

8.3. CONCEPTOS BÁSICOS


Antes de comenzar a diseñar una aplicación gráfica debemos tener claros algunos conceptos:
■ Formulario. Un formulario es un objeto contenedor que agrupa los componentes de
nuestra aplicación o parte de ellos. Básicamente un formulario es una ventana del
sistema operativo. Podemos crear aplicaciones que trabajen con ima o varias ventanas
con lo que configuraremos proyectos con uno o más fonnularios.
■ Control. Componente que podemos insertar en un fonnulario u otro tipo de
contenedor. Denominamos control a cualquier objeto gráfico que podamos incluir en
los fonnularios de nuestra aplicación. Los controles son clases predefinidas con una
serie de propiedades y métodos asociados. Cuando insertamos un control en realidad
estamos instanciando un objeto de la clase a la que pertenece.

prívate void |lnitidlizeCofrponent() 0-^ Forml

{
this.buttonl « nevv Systea5.Windows.Foras.£jttcn();
thi5.SuspendLoyout();
//
Q buQonl D
// buttonl
//
this.buttonl.Location • new Systea.Drawing.Pcint(25, 56);
this.buttonl.Narre • "büttonl";
this.buttonl.Sire ■ new Systena.0rawing.Sirc(75, 23);
this.buttonl.Tabindex « O;
this.buttonl.Text = "buttcnl";
this.buttonl.UseVisualStyleBackColor ■ true;

Figura 8.2. inserción de un botón en el formularlo C# en Visual Studio.

En la Figura 8.2 se muestra la inserción de un botón en el entomo gráfico y las líneas de


código que se autogeneran en este proceso. La línea this,buttonl=new
System.Windows.Forms.ButtonOí denota la instanciación que antes comentábamos. Se crea
una variable de la clase Button y se configuran sus caracteristicas con los valores por defecto
para este tipo de objeto.
■ Propiedades. Las propiedades conforman los atributos de un objeto gráfico, sus
características. Mediante las propiedades podemos configurar el texto que deben
mostrar, sus dimensiones, el color de fondo que deben adquirir, etc.
■ Eventos. Aunque ya hemos estado hablando de eventos es necesario que se defina en
este apartado ya que es un elemento fundamental de un programa con entorno
gráfico. Podemos definir evento como "señal" recibida por parte de la aplicación
desde cualquier componente que pueda interactuar con ella: teclado, ratón, etc.,
normalmente generada por la persona que usa el software. Los eventos serán
gestionados por:
—> El sistema operativo.
—>■ Por el lenguaje de programación que estemos usando.
Por el programa de forma que cada evento tendrá asociado una función que
se activará cuando este se produzca.
■ Métodos. Ya que un control u objeto gráfico procede de una clase, al igual que
dispone de una serie de atributos o propiedades, estará dotado de funciones que
permitirán crear un efecto sobre él. Así, un método es una función de la clase.

8.4. CREACIÓN DE PROYECTOS GRÁFICOS


A continuación vamos a estudiar cómo realizar proyectos gráficos en los entornos IDE
utilizados.

8.4.1. PROYECTOS GRÁFICOS EN VISUAL STUDIO


Los pasos a seguir para crear un proyecto gráfico C# en Visual Studio se enuncian a
continuación:

1. Clic en Archivo y Nuevo proyecto.


2. Seleccionamos Aplicación de Windows Form.
3. Escribimos el nombre del nuevo proyecto como hacíamos en las aplicaciones de
consola.

4. Clic en Aceptar.

Una vez se han seguido estos pasos veremos en pantalla los elementos que se agrupan en la
Figura 8.3. En la zona de la izquierda se encuentra el formulario en vista de diseño. Iremos
agregando controles según vayamos necesitándolo a partir del cuadro de herramientas. El
cuadro de herramientas proporciona todos los elementos que se pueden incluir en un fonnulario.
Cada componente puede ser configurado con una serie de características, es decir, podemos
modificar sus propiedades en función de su tipo y las restricciones establecidas para ella. La
modificación de una propiedad se realiza a partir del cuadro de propiedades, situado en la zona
inferior derecha de la Figura 8.3. Para finalizar observamos el explorador de soluciones, desde el
que podemos agregar nuevos formularios o contenedores a nuestra aplicación siempre que lo
creamos oportuno.
Capítulo 8. Entorno gráfico 321

Explotador de solucionen

(Zi 'o - íf Q ® la O ^¡
SuiCiji en el EApícradoi de soÍuc«ones (Ctrl*")

JJÜ Solución 'EjemploFcrm' (1 proyecto)


^ S E|emploForfn
> Properties

P ^ Fcrml.cs ~
P C* Program.cs

^ Todos tos Windows Forms


Puntero
IP BackgroundV/crker
¿° BindingNavigator
yj BindingSoufce
G3 Button Propiedades
Forml S>'StcmAV¡ndovvs.Form$.Fofm
m ChecfcBox
CheckedListBox
ColorOidlog Backgroundimage |□\ (r(n inguno)
ComboBox BfickgroundlmageLayout Tile
a ContcxtMenuStrip Cursor Default
Defau^
¿*l DataGridVtew Í3 Font Microsoft Sans Seri^ 8,25pt
DataSet
ForeColor Hi ControITect
Q DateTimePicker FormBorderSt>*le
RightToLeft
DirectoryEntry
RightToLeftlayout
<P Directcr>'Searcher
Text
12 DomainUpDown UseV.'aitCursor
o ErrorProvider Text
S EvenlLog Texto asociado al control.

Figura 8.3. Componentes fundamentales de un proyecto C# en Visual Studio.

8.4.1.1. AGREGAR NUEVOS FORMULARIOS

Probablemente nuestras aplicaciones están fonnadas por más de un formulario tal que todos
interactúen de alguna fonma entre ellos. Por defecto, cuando creamos un proyecto gráfico en
Visual Studio, el proyecto dota a esta aplicación de un único objeto Fonn. Si necesitamos incluir
alguno más:
1. En el Explorador de soluciones clic con el botón derecho del ratón sobre nuestro
proyecto. Nos colocamos sobre Agregar... y Agregar Windows Form...
2. En el cuadro de diálogo localizamos el cuadro de texto nombre, escribimos en él el
nombre que daremos a este nuevo contenedor.
3. Clic en Agregar.
Forml,es Finalizados estos pasos veremos en el Explorador
Form2.cs Windows el nuevo componente. Sabremos que se
Figura 8.4. .Iconos representativos de objeto tratavisual
de imStnHin
formulario debido a la simbología que
formulario en el Explorador de soluciones. ^
8.4.1.2. ENTORNO DE TRABAJO

Una vez hemos creado un nuevo proyecto gráfico en Visual Studio veremos una pantalla
similar a la que observamos en la Figura 8.5.

ICaXAR VER PROVECTO COfcV&.&R| DfPÜ^ CQUPO rORM&TO HERIUMimAS PRUCBA VimAyU ATUCU

O- > ► kycaALctb^
^ Cufdro d< hcrtKTxenUi • 9 X FemO a*

g Ei-'-acrto ti Iv.-íí's d< ►if*» P•


' g- «i TodovtotWmdOMnFonm
& M PunScfp
¡J5 Se^"^ 1 p»cyntc)
" SP BícVgrcwrfWcftír 4 fT l}««MflpForm
^ E<nd<n9;^^«]er y A P'cr-ft«-i
«1 E^ndmjSeurcc ^ •■ PHfrrncfa
8 B«non O Aps cci'ig
P1 CKtskSa 4 3 ftwnlx^l
P ^ FcmJXWv^i^M
ÍH Ch«<kedLmS«a
^ Fc*ml «ru
^ Cc^ciDialog P *? Ferml
2 CombcBet P c* Pr09rjm.CS
Q CentoiT.VnuSu^
f! 0<uCnd'.'»»
ífi D«US<1
2 DMcTmcPfcktr
S D««rtpiyfirt/y
forml SyslcmWndOJrsFerms.Fcrm
ChííOetySejfcheí
:! 7* o f *-
Q DcmamUpOown
O E'wPTOvíd** Cj<Í9rc<uodVA*9« |_J (nrtfwítC)
S«it9roun^mj9«Lj>oUI Tdj
B Evtntiog
PHjuIt
SS FikSj-ucmWctOtcr
l.íicrc -.«ft 'j^n% t 25p«
Flo«kl«j«u^«nd
Cor^o'Tnt
«3 Eotd«f8roiMcrDt»log FofrP'&crdcf$r/l<
5 ^ntOulog Pj9MTolctl
Q GrcupBok R«9*»» T c l «*t l ow»
Q HetyPfovrd** Tos

* KSctoKh
6 tnu^m Tc*1

A \ Texto jtcctjdo jJ

Figura 8.5. Entorno de trabajo de Visual Studio.

Vistas del formulario. Normalmente, al crear un nuevo proyecto veremos un único


formulario en vista de diseño, es decir, lo observaremos en su foraia gráfica.
Realizando doble clic sobre cualquier parte de su superficie, contenga esta controles o
no, pasaremos a ver su código fuente. De forma rápida podemos acceder a una u otra
vista a partir de las pestañas denominadas nombre_formuIario.cs (vista de código
fuente) y nombre_formulario.cs[Diseño] (vista de diseño).

Forml.cs Forml.cs [Diseño]

Figura 8.6. Pestañas para acceder a las diferentes vistas de un formulario.

Formulario contenedor de controles. Será el objeto que agrupe a todos los


elementos que tengan alguna relación. Ventana que se irá diseñando y sobre la que se
irán colocando los controles necesarios según la funcionalidad que se quiera dar al
objeto contenedor en la aplicación.
Capítulo 8. Entorno gráfico 323

Cuadro de herramientas. El cuadro de


herramientas no tiene por qué estar visible
cuando se crea un proyecto gráfico. Si a p-
1 • ^ 1 1 !• 1 • t ^ Todos IciWmdowj Fcirm fc. *
simple vista no lo localizamos se ubica en la ..co«ii»tetomun« ^
zona izquierda de la pantalla de aplicación, "> \\
junto a una pestaña denominada Orígenes a chJiLx \\
de datos. Solo debemos hacer clic sobre el ch«k,diifl6o» \\
1 T-'1 1 1 ^ CcinbcBoi
nombre para que se muestre. El cuadro de a D.teT«T,eP«vcf ^
herramientas contiene todos los controles ^
UnlcUbel
aceptados por C# que pueden incluirse en a *'""y,''""J
una ventana. Para insertar un control en el (.)• M4sl.edTej(tSo*
/
#
formulario solo debemos pinchar en él y a E3 MonthCi1«nd«f ij 'j
continuación hacer clic en la zona del ^ Nctdyuon
LIS NumencUpOown
jl ■
ii G.
formulario donde queramos que se ubique. s PtctuicBox ij H 1
Este puede ser desplazado posteriormente a ^ 11 i
otro lugar o modificado en tamaño y demás K RkhToaBo» /j m|
características visuales. El cuadro de ® jj I,
herramientas estará organizado en jj ^
categorías, agrupando controles en función |
de su uso e incluso podemos localizar un ^McnuJ>bJ^MdchttT.minlus I
* b D«tos I
control de forma rápida escribiendo su t.compoMrt«
nombre en la parte superior (Buscador de
herramientas en la Figura 8.7) Figura 8.7. Cuadro de herramientas.
Explorador de proyectos. Esta zona ya es bien conocida por el lector. En ella se
ubicarán todos los ficheros que fonnan nuestro programa. Cuando creamos
aplicaciones con más de un formulario podremos acceder a uno u otro realizando
doble clic sobre él en esta paleta.
Cuadro de propiedades. El cuadro de
propiedades es de gran importancia ya ,
que pennitirá modifícar el objeto
(control) que hayamos insertado en
nuestro fomiulario. Cuando se inserta E5SS!2SBBBBBBBBIIIIIBiBHBHOn
- ~ Forml S)-stcm.Windo*vs.Forms.Fonn
S)-stcm.Windo*vs.Forms.Form
un nuevo control, este se configura
con sus propiedades con valores por B MaiimumSue
B MaumumSce 0;0
0:0 ^
B MmimumSce
defecto. Los valores predetemhnados B Pfidding
0:0
0:0:0:0
pueden ser modifícados uno a uno en
1 Wirfth
esta paleta o bien a través de código [ Hetghl p
[ J
.
^^ 499

StdrtPodtion
siempre que
^
los atributos sean V.IndowStíte
v:.ndcv«Drf.uttLo«t«o
\^.'mdcwsDeíautlLo<at»©o
Normal
Normal
accesibles de algún modo. Para s VCfttMU
B Est3o de vent^n»
ContrclBox Tnjt ^
modificar algún valor solo debemos sác°"
Sáe A
localizar la propiedad y hacer clic Tjm,ñodticAtrdi.»,pixe(a.
Tamjjno del c(»trol, en pixe^es. \
junto a ella. El cuadro de propiedades \ \
presenta en su parte superior una lista
con todos los controles incluidos en el ~
Figura 8.8. Cuadro de propiedades en
formulario
,. ,
de forma ,que
~
podemos Figura 8.8Visual
Cuadro de propiedades en
Studio.
cambiar de uno a otro de fonna rapida
y modificar así sus propiedades.
324 Programación

El cuadro de propiedades además permitirá al programador visualizar todos los eventos


soportados por un control, además de la validación, configuración y uso de alguno de ellos.

I Propiedad s
Forml SjAjtm.VVindc.vs.Fofms.Form
qS! i

Figura 8.9. Iconos en el cuadro de propiedades que permiten permutar entre vista de propiedades y
eventos.

Son muchos los controles que podemos usar, y a su vez muchas las propiedades exclusivas de
estos. Como programadores, conoceremos aquellas que usemos con frecuencia pero no tenemos
por qué saber las características de todos los objetos y sus atributos. Visual Studio entiende que
un programador no puede o tiene por qué memorizar todos los controles, su funcionalidad y sus
características y por eso, en la parte inferior del cuadro de propiedades observaremos como
aparece una pequeña descripción de la propiedad que se encuentre seleccionada en ese momento.

8.4.1.3. CONTROLES
En el cuadro de herramientas veremos una categoría catalogada con el nombre controles
comunes que agrupa los controles que suelen ser más usados en aplicaciones. Entre estos
controles encontramos;
■ Button (Botón). Control que tras ser pulsado provoca la ejecución de una acción.
■ CheckBox (Casilla de verificación). Elemento de un fonnulario diseñado para
presentarse en dos estados, activado y desactivado según el usuario pulse sobre él.
Activar una casilla de verificación suele conllevar la posterior ejecución de una
acción, cambio de característica, etc. Suelen usarse en sentencias alternativas de
modo que según la casilla se baya activado o no se realiza una acción u otra.
■ ComboBox (Lista desplegable). Control que muestra una lista de opciones y pennite
al usuario escoger una entre ellas. El objeto se denomina lista desplegable debido a
que veremos una única opción a la vez y para visualizar el resto debemos hacer clic
en el cuadro de control.
■ Label (Etiqueta). Objeto que permite visualizar un texto en pantalla. Básicamente su
función es esa, visualizar un texto aclaratorio sobre algún componente concreto del
formulario.
■ ListBox (Cuadro de lista). En esencia es similar al combobox ya que muestra una
lista de elementos, la diferencia está en su visualización. Veremos más de un ítem a la
vez y podremos seleccionar más de uno.
■ RadioButton (Botón de opción). Encontraremos grupos de estos controles de fonna
que solo podremos seleccionar uno a la vez en el grupo. Son similares a las casillas de
verificación debido a que son objetos que poseen dos estados, activado y
desactivado y según estos el programador podrá realizar una acción, pero se
diferencian cuando se encuentran agrupados con elementos del mismo tipo de modo
Capítulo 8. Entorno gráfico 325

que podemos seleccionar tantas casillas de verificación como queramos y un único


botón de opción en el grupo.
TextBox (Cuadro de texto). Los cuadros de texto se utilizan con frecuencia ya que
son el modo en el que podemos introducir información en nuestra aplicación. Son
objetos rectangulares preparados para que el usuario pueda hacer clic sobre ellos e
introducir información.

Button CheckBox Q ComboBox


ListBox 0 RadioButton
@ TextBox
Etiquetas

form2

ComboBox
LabeH

Casillas de
verificación

Cuadro de texto

Figura 8.10. Controles comunes en un proyecto visual.

Seguiremos analizando otros conti'oles, por lo pronto conocer estos nos permitirá realizar
aplicaciones de cierta envergadura.

8.4.1.4. DESARROLLO DE UNA PEQUEÑA APLICACION DE EJEMPLO


Para comenzar a usar Visual Studio vamos a desarrollar una pequeña aplicación. En primer
lugar vamos a definir su visualización. Esta debe ser similar a la que se muestra en la Figura
8.11.

Formi 1 ■=■ I n) |»iii^¡jg|' Antes de nada debemos seguir los pasos del
Apartado 8.4.1 para crear un nuevo proyecto.
Calculadora Le daremos el nombre Conversor.

Tras generarse el nuevo proyecto veremos


un formulario en pantalla que usaremos para ir
' ' agregando los controles que necesitaremos.
Analizando el entomo gi^áfíco de ejemplo
Figura 8.11. Entorno gráfico de la aplicación de deducimos que:
ejemplo propuesta.
■ Los textos Calculadora, Euros
Etiquetas Cuadros de texto
y Dólares son etiquetas.
■ Junto a Euros y Dólares
encontramos dos cuadros en los
que podrá escribir el usuario, Calculadora

son cuadros de texto. Eixos I

■ En la parte inferior tenemos que


incluir un botón.
Una vez tenemos claro los componentes
que se deben incluir, comenzamos el
proceso de configuración gráfica de la Figura 8.12. Análisis de los elementos del
programa.
aplicación.
Podemos empezar insertando el control que queramos, para seguir un orden vamos a insertar
controles según se encuentran ubicados de arriba abajo en el formulario.
1. Insertamos la primera etiqueta. Para ello localizaremos el objeto Label en el cuadro
de herramientas y haremos clic sobre él.
2. Una vez seleccionado el control Label dirigimos el cursor hacia el formulario y
hacemos clic en la zona donde queramos ubicarlo.
3. La etiqueta aparece con el texto Labell. Este debe ser sustituido por Calculadora.
Para ello debemos acceder al cuadro de propiedades (activar la vista de propiedades
en caso de que no esté visible - Figura 8.9). La propiedad que decide que se muestra
en la etiqueta se llama Text.
4. Localizamos la propiedad Text. A su lado debe aparecer el texto Labell. Para
cambiarlo por Calculadora solo debemos hacer clic en él, borrar Label 1 y escribir
Calculadora. Tras pulsar ENTER el nuevo texto se mostrará en la etiqueta.
5. El texto de la etiqueta posee unas características que pueden cambiarse a partir del
conjunto de propiedades denominado Font. Si hacemos clic sobre el signo más junto
a Font se expandirán todas las opciones de fuente; slze para modificar el tamaño de la
letra, ñame para establecer el nombre de la fuente del texto, bold para establecer el
texto en negrita, etc. Podremos cambiar el color del texto a partir de ForeColor.
6. Ultimamos la posición del componente, para ello colocaremos el cursor sobre él,
pulsaremos el botón izquierdo del ratón, manteniéndolo presionado mientras
movemos el objeto. Lo soltaremos en la posición adecuada.
7. A continuación podemos insertar las etiquetas Euros y Dólares siguiendo los pasos 1
a 6.

8. El siguiente componente que podemos incluir es el cuadro de texto que se encuentra


junto a la etiqueta Euros. Localizaremos este control en el cuadro de herramientas,
haremos clic sobre él y acto seguido clic en la zona del formulario donde queramos
ubicarlo.
9. Las etiquetas son objetos que no serán usados normalmente en el código fuente del
programa, sin embargo, objetos como cuadros de texto o botones si serán
referenciados. Así, debemos asignar un nombre, como cuando creábamos variables a
Capítulo 8. Entorno gráfico 327

estos componentes (de hecho estos elementos son variables de las clases TextBox y
Button). Para asignar un nombre a un control debemos localizar la propiedad namen.
10. Accedemos al cuadro de propiedades, localizamos la propiedad ñame y clic en el
cuadro que se encuentra justo al lado. Por defecto, los objetos de tipo textBox se
denominan textBox 1, textBox2, etc., podemos usar otra nomenclatura que nos resulte
más representatix a y que nos ayude a identificar más fácilmente los cuadros de texto
de nuestra aplicación. Ya que es un cuadro de texto, vamos a comenzar el nombre del
control con las letras txt seguidas de un nombre representativo relacionado con la
información que va a tratar. Llamaremos a nuestro cuadro de texto situado jimto a la
etiqueta Euros con el nombre txtEuros.
11. Si el tamaño del cuadro por defecto nos parece inadecuado, podemos modificarlo
desde el entorno gráfico, pinchando y arrastrando uno de los pequeños anclajes
situados a cada lado del cuadro. O bien es posible establecer un valor para el ancho y
el alto del componente mediante el uso del grupo de propiedades Size entre las que se
encuentran Width (ancho) y Height (alto).
12. Insertaremos el siguiente cuadro de texto siguiendo los pasos 8 a 11. Llamaremos al
cuadro de texto txtDoIares.

13. Para finalizar incluiremos el botón en el fomiulario. Ya sabemos cómo colocarlo


puesto que el proceso se ha repetido en varias ocasiones a la hora de agregar las
etiquetas y cuadros de texto a nuestro proyecto.
14. Una vez se ha colocado el botón, podemos modificar su posición y tamaño como se
ha explicado para etiquetas y cuadros de texto, ya sea de forma manual o a través de
las propiedades descritas.
15. Cambiaremos el nombre del componente por btnConvertir (btn debido a que es un
botón).
16. Seguidos todos los pasos la aplicación estará finalizada, visualmente hablando, solo
queda darle funcionalidad.
A continuación, veamos qué hará nuestra aplicación. El usuario escribirá una cantidad en
euros, tal que cuando haga clic en el botón Convertir en el cuadro de texto Dolares aparecerá la
cantidad de dólares equivalente a los euros escritos, teniendo en cuenta que 1 euro = 1,3194
dólares.

Según este comportamiento, tenemos claro que;


■ La acción de hacer clic conlleva a la ejecución de un conjunto de instrucciones que
como resultado mostrará la cantidad de dólares equivalentes a un número
determinado de euros.
■ El hecho de hacer clic se puede catalogar como evento.
■ Las instrucciones que se ejecutarán en consecuencia al evento clic será la respuesta a
este evento.

Para codificar el evento clic podemos seguir uno de estos dos pasos:
■ Elacer doble clic sobre el botón. Esto provocará que se muestre el código fuente
asociado al fonnulario con el cursor activo en el método para el evento clic (ya que
este es el predeterminado para este tipo de control).
■ Seleccionar el botón, mostrar los eventos en el cuadro de propiedades, localizar el
evento clic y hacer doble clic sobre él. Obtendremos el mismo resultado que en el
punto anterior.
Acabamos de acceder al método de nuestro botón btnConvertir asociado al evento de hacer
clic con el ratón, a continuación solo debemos escribir las líneas de código que se deberán
ejecutar cuando este se dé.
Dada que la operación es una conversión en la que tenemos la cantidad de euros y teniendo en
cuenta que 1 euro equivale a 1,3194, al hacer clic en el botón:
1. Multiplicaremos los euros escritos en txtEuros por la cantidad 1,3194.
2. Mostraremos esta cantidad en el cuadro de texto txtDolares.
El código resultante es el que se muestra a continuación:
private void btnConvertir Click(object sender, EventArgs e)
{
txtDolares.Text = (int.Parse(txtEuros.Text)
* 1.3194).ToString O ;

■ Los nombres asociados a los eventos comienzan con el nombre del componente,
seguido del nombre del evento y una lista de parámetros.
■ Un cuadro de texto recibe o muestra información de tipo texto, así, a la hora de
recoger una cantidad de este debemos realizar una operación de conversión
(int.parseO).
■ Igualmente, la información que debamos mostrar en un cuadro de texto debe ser de
tipo String, de forma que a la hora de modificar el valor de un cuadro el nuevo valor
debe ser de tipo texto( de ahí la necesidad de usar el método ToStringO en el
ejemplo).
■ La propiedad que almacena el texto escrito en un textBox en Text. Usaremos la
sintaxis cuadro.Text.
Llegados a este punto nuestra aplicación está finalizada. Solo debe hacer clic sobre el botón
de ejecución. Pasados unos segundos veremos el fonnulario. Probaremos su funcionalidad
escribiendo en el cuadro de texto euros una cantidad válida y pulsando el botón convertir. Hasta
ahora no hemos tratado errores de ejecución, si no escribiéramos ningún valor en el cuadro de
texto euros o el valor no fuera un número válido, el programa exteriorizaría un código de error
cerrando la aplicación. Vamos a controlar que al menos el usuario escriba un valor en el cuadro
euros, tal que si está vacío muestre un mensaje de error. El código debe quedar algo así como:
Si(cuadro de texto euros vacio){
Mensaje "El cuadro esta vacio";
}
Sino{
cuadroDolares=euros*1,3194;
}
1. Si nos encontramos en la vista de diseño haremos doble clic sobre el botón convertir.
Desde la vista de código solo debemos localizar el evento clic del botón.
Capítulo 8. Entorno gráfico 329

2. La propiedad Text del cuadro de texto euros refiere una variable de tipo String.
Debemos saber si esta variable está vacía o no cuando se pulsa el botón. Para ello, los
objetos String disponen del método Compárelo. Claramente se debe crear una
sentencia altemati\ a doble.

3. La sentencia //"podría ser similar a esta: if(txtEuros.Text.CompareTo("")=0){...}.


Realizamos la acción indicada en el paso 2, si el contenido del cuadro de texto euros
(txlEuros) comparado con la cadena vacía ("") resulta ser el mismo, es decir, NO se
ha escrito nada en el cuadro de texto euros, se ejecutarán las instrucciones ubicadas
entre las llaves del if.
4. Para mostrar un mensaje de error, advertencia, etc., C# cuenta con una serie de clases
y métodos estáticos, una de ellas es MessageBox. Esta clase mostrará un cuadro de
diálogo típico de Windows configurable por el programador al usar su método
Sho>v().
5. Para finalizar la sentencia alternativa, si la cadena no estaba vacía suponemos que se
ha escrito un valor válido en el cuadro de texto y se puede realizar la operación de
conversión, así la línea referida a sino se mostraría como sigue: else {
txtDoIares.Text =(int.Parse(txtEuros.Text)* 1.3194).ToStringO;}
prívate void btnConvertir_Click(object sender, EventArgs e) i
{ i
if (txtEuros.Text.CompareTo("") == 0) ;
{ I
MessageBox .Show ("Error, el cuadro de texto euros está vacio", !
"Error cuadro vacio", MessageBoxButtons.OK); ;

else
{
txtDoIares.Text = (int.Parse(txtEuros.Text) * 1.3194).ToString();

ACTIVIDAD 8.1

Modifica el programa ejemplo realizado en el Apartado 8.4.1.4 de forma que el programa


compruebe qué conversión debe realizar y la ejecute. Por ejemplo, si el cuadro de texto euros
está en blanco comprobará si el de dólares contiene algún valor. En caso afirmativo la conversión
será de dólares a euros. Si el cuadro dólares esta en blanco y el de euros no, la conversión será
de euros a dólares. Controlar posibles fallos que se puedan ocasionar.

8.4.1.5. EVENTOS Y PROPIEDADES MÁS COMUNES

Entre las propiedades más comunes a todos los controles encontramos:


■ Ñame. Nombre del control. Esta propiedad debe tener algún valor obligatoriamente
ya que es la forma en la que se referencia un objeto en el fonnulario.
■ Location. Posición o localización del objeto. Confonna el grupo de propiedades X,Y
y Loeked. Mediante las propiedades X e Y se indicará la localización del control.
normalmente referida a la esquina superior izquierda de este. La propiedad locked
determina si el control puede moverse o cambiarse de tamaño en el diseño'.
Visible. Establece si el control es visible o no en un momento determinado de la
ejecución del programa. Acepta los valores true o false.

NOTA: MessageBox es una clase que genera cuadros de diálogo propios de j


Windows. Existen otros controles que ya estudiaremos usados para generar euadros de :
diálogo del tipo abrir fichero, imprimir, fuente, etc. '
ShowO es el método que usaremos para visualizar y configurar las características de
un cuadro de diálogo. Solemos usar la sobrecarga del método con tres parámetros tal que:
■ Primer parámetro: Especifica el texto que queremos se visualice en el
cuadro de diálogo.
■ Segundo parámetro: Especifica el título de la ventana del nuevo cuadro de
diálogo. Se ubica en la barra de título como ocurre con el resto de ventanas de
los sistemas operativos gráficos.
■ Tercer parámetro: Establece el tipo y número de botones de acción que
deben incluirse en el cuadro de diálogo. Para definir esta característica
usaremos la clase MessageBoxButtons. Podremos usar: OK (visualiza solo el
botón de aceptar), OKCancel (visualiza los botones de aceptar y cancelar),
YesNo (botones si y no), etc.
MessageBox.Show("Error,el cuadro de texto euros está vacio","Erroí.cuadro vacio", MessageBoxButtons.OK);

£norcuddrov»dol

Error, d cuadro de texto euros está vacio

Figura 8.13. Cuadros de diálogo con MessageBox.

BackColor. Color de fondo del componente.


Font. Grupo de propiedades que establecen la fuente del objeto. Podremos establecer
nombre de la fuente (Ñame), tamaño de la letra (Size), si debe verse en negrita
(Bold), etc.
ForeColor. Color de primer plano del componente. Normalmente establece el color
del texto de este.

' Aunque a simple vista esta característica pueda parecer algo absurda tiene su sentido, ya que en
ocasiones realizaremos aplicaciones con multitud de controles que podemos desplazar de fonna
involuntaria. Al establecer Locked a true el control queda bloqueado y estático en la posición establecida
por el programador.
Capítulo 8. Entorno gráfico 331

■ Text. Determina el texto que debe verse sobre el control. En controles, tipo cuadros
de texto establece el texto que por defecto debe estar escrito sobre el componente
cuando se ejecute la aplicación.
■ Enabled. Permite habilitar o deshabilitar un control. Cuando un componente está
deshabilitado es visible pero no acepta ningún tipo de interacción con el usuario.
■ Tabindcx. Determina el número de orden del control a la hora de usar la tecla TAB.
Normalmente en una aplicación gráfica podremos desplazamos a través de ella
gracias al uso de la tecla TAB en caso de que el ratón no funcione adecuadamente.
Según se pulsa esta tecla se activa un componente u otro de la aplicación. El orden
que establece qué control se visualiza primero cuando pulsamos TAB, cuál es el
siguiente, etc., se indica en esta propiedad. Este valor es inicializado por defecto en
Visual Studio en función del orden de creación de los objetos.
■ Size. Conjunto de propiedades que penniten establecer las dimensiones de un control.
Se compone de las propiedades Width y Height.
Propiedades comunes del objeto Forni:
■ IsMdiContainer. Esta propiedad booleana pemiite configiorar im formulario
principal y el resto como secundarios, tal que hace las veces de contenedor del resto.
■ Opacity. Para indicar el grado de opacidad del formulario.
■ MinimizcBox. Propiedad booleana que permite hacer visible o no el botón minimizar
ubicado en la barra de título.

■ MaximizeBox. Propiedad booleana similar a MinimizeBox pero referida al botón


maximizar.

■ Icón. Permite indicar qué icono se debe usar como imagen representativa de la
aplicación. El icono se muestra en la barra de título en la esquina superior izquierda,
junto al menú de control.
" ControlBox. Propiedad referida al menú de control. A través de ella podemos
prescindir o no de este elemento típico de una ventana.
■ HelpButton. Botón de ayuda. Permite hacer visible este botón en la barra de título
del formulario.

■ StartPosition. Establece la posición inicial del formulario. Podemos usar como


posición inicial la establecida en la propiedad Location, optar por centrar la ventana,
etc.

ACTIVIDAD 8.2

Echa un vistazo a la MSDN de Microsoft y localiza información sobre otras propiedades


relacionadas con el control especial fonnulario. Si lo prefieres puedes acceder a Visual Studio,
hacer clic sobre cualquier zona vacía del formulario y observar sus propiedades y su significado.
Modifica el control cambiando los valores de estas características.

Propiedades earaeterísticas de cuadros de texto:


■ TextAlign. Establece la alineación del texto incluido en el cuadro de texto.
■ CharacterCasing. Propiedad por la que se puede convertir el texto, una vez escrito, a
mayúsculas o minúsculas.
■ MaxLength. Establece el número de caracteres máximo que se pueden escribir en el
cuadro de texto.

■ Multiline. Controla si el cuadro de texto puede estar fonnado por más de una línea.
Útil cuando deseamos introducir cuadros de comentario o similar en nuestro
formulario.

■ PasswodChar. Establece el carácter que se debe mostrar en caso de que el cuadro de


texto sea usado para escribir contraseñas.
■ ReadOnly. Establece el cuadro de texto como de solo lectura, es decir, solo
podremos leer su contenido en ningún caso modificarlo.
Propiedades comunes en casillas de verificación y botones de opción:
■ Checked. Indica si el botón de radio o casilla de verificación está activada o no.
■ CheckState. Usado en casillas de verificación indican su estado.

Propiedades comunes en cuadros de lista y listas desplegables:


■ Serte. Establece si los elementos de una lista están o no ordenados.

■ Items. Colección que almacena los ítems y elementos de la lista.


Para finalizar el apartado, son muchos los eventos que es capaz de detectar un control
concreto. A continuación veamos algunos de ellos que pueden sernos de utilidad. Recordamos
que en la paleta de propiedades debemos hacer clic en el botón adecuado para mostrar los
eventos asociados al objeto seleccionado.
■ Click. Se produce cuando se hace clic sobre el componente.
■ Move. Se produce cuando se mueve el control.
■ Resize. Se produce cuando cambia el tamaño del control.
■ Enter. Se produce cuando un objeto obtiene el foco, es decir, es el control activo en
ese momento.

■ Leave. Se produce cuando el objeto que tiene el foco deja de ser el objeto activo,
pierde el foco.
■ PropiedadChanged. Existe un conjunto de eventos que se producen cuando una
característica concreta del control es modificada, por ejemplo, CursorChanged,
EnabledChanged, etc. En PropiedadChanged, la palabra Propiedad es sustituida
por una propiedad válida del objeto tratado.
■ MouseDown. Se produce cuando se pulsa el botón del ratón sobre un control, se
refiere a la pulsación del botón izquierdo del ratón.
■ MouseUp. Se produce cuando soltamos el botón izquierdo del ratón sobre un control
determinado.

■ MouseHover. Se produce cuando el ratón se encuentra inmóvil sobre un control.


■ MouseLeave. Se produce cuando el ratón se encuentra fuera de la superficie de un
control.
Capítulo 8. Entorno gráfico 333

■ MouseMove. Se produce cuando el ratón se encuentra en movimiento sobre la


superncic de un control.
■ KeyDown. Se produce cuando el usuario pulsa una tecla sin soltar esta.
■ KeyUp. Es el evento producido al levantar el dedo de la tecla que anteriormente
estaba presionada.
■ KeyPress. Este ex ento se refiere a la pulsación de una tecla, el efecto de pulsar una
tecla, es decir pulsamos y soltamos esta.

ACTIVTd^AD 8.3
Realiza un formulario como el que se muestra en la Figura 8.14. Asocia a cada botón el
código fuente y características según se indica.
pt* Forml

Tamaño dd te>3o

Cok)r del texlo

[ 1 Cursiva
Ver texlo
i.j Subrayado

Figura 8.14. Formulario de la actividad 8.3.

1. Botón Aumentar, nombre btnAumentar.


2. Botón Reducir, nombre btnReducir.

3. Botones de color de fondo del fonnulario, nombres btnRojo, btnVerde y btnAzul.


4. Lista desplegable {comboBox). Rellenar la lista^ con valores numéricos (12, 14, 16, etc.),
nombre IstTamaTexto.

5. Botones de color del texto de la etiqueta 9, nombres btnRojoTexto, btnVerdeTexto y


btnAzuITextp.
Los textos "Tamaño del texto" y "Color del texto" son etiquetas que no precisan
configuración.
6. Botón que permite ocultar la etiqueta 9, nombre btnOcultarXexto.
7. Botón que pemiite visualizar la etiqueta 9, nombre btnVerTexto.

^ Al hacer clic sobre la lista en vista de diseño veremos una pequeña flecha en la zona superior derecha
del control. Al hacer clic sobre ella encontraremos el texto Editar elementos. Este enlace visualizará un
cuadro de diálogo donde se irán insertando los elementos de la lista.
8. Casillas de verificación, sus nombres serán vNegrita, vCursiva y vSubrayado.
9. Etiqueta a la que modificaremos determinadas propiedades a través de los botones
descritos. Su nombre es IbITexto.

10. Botón de cerrar formulario, nombre btnCerrar.


Asociaremos a cada botón un evento Click, a la lista dcsplcgable el evento
SelectedIndexChanged (debido a que se cambiará el tamaño del texto cuando cambie el ítem
seleccionado de la lista) y a las casillas de verificación el evento Click.
El código asociado a cada componente es;
1.this.Width = this.Width + 50;

2. this.Width = this.Width - 50;

3. this.BackColor = Color.Red; (botón rojo)|this.BackColor = Color.Creen; (botÓn


verde) I this.BackColor = Color.Blue; (BotÓn azul).
4. A la hora de modificar la fuente es necesario asigna un Nuevo objeto de tipo Font con las
nuevas earacteristicas.

IblTexto.Font = new ;
Font(IblTexto.Font.FontFamily,IstTamaTexto.Selectedlndex+'F'); '•
5. IblTexto.ForeColor = Color.Red; (botón roJo) | IblTexto.ForeColor
Color.Creen; (botón verde)|IblTexto.ForeColor = Color.Blue; (Botón azul).
6. IblTexto.Visible = false;

7. IblTexto.Visible = true;
8. A cada casilla:

if (vNegrita.Checked)
{
IblTexto.Font = new Font(IblTexto.Font,
IblTexto.Font.Style | FontStyle.Bold);

IblTexto.Font = new Font(IblTexto.Font,


IblTexto.Font.Style " FontStyle.Bold);

El símbolo barra (|) concatena propiedades mientras que ('^) excluye propiedades.
Para vCursiva la enumeración FontStyle dispone del valor Italic, así, en lugar de escribir
FontStyle.Bold escribiremos FontStyle.Italic. En el caso de texto subrayado usaremos el valor
FontStyIe.Underline).
10. this.cióse O;
Debes realizar un nuevo proyecto, generar la vista de diseño tal y como se indica, dar
funcionalidad a la aplicación y analizar todo el proceso. Observa cada línea de código, en ellas se
modifican propiedades de controles vistas en el apartado, es importante que se entienda con
claridad cada una de las operaciones realizadas y el código fuente de la aplicación.
Capitulo 8. Entorno gráfico 335

ACTIVIDAD 8.4 ACTIVIDAD 8.5

Realiza una pequeña ealeuladora. Debe Modifica la calculadora de la Actividad


mostrar el siguiente aspecto: 8.4 de forma que solo incluya un cuadro
de texto y en él se vayan mostrando
Calculadora i^ operando!, operando2 y el resultado.

Operando 1

Operando 2

Resultado

d][J][E EDGII
H B H BCE
LD CE CE I I

Figura 8.15. Calculadora básica.

8.4.1.6. CONTENEDORES

Los contenedores son un tipo de control diseñado para contener a un conjimto de


componentes como su nombre indica. Encontraremos la categoría Contenedores en el cuadro de
herramientas. Se definen como contenedores: FlowLayoutPanel, GroupBox, Panel,
SpIitContainer, TabControI y TableLayoutPanel. Recordamos que el objeto Form que hasta
ahora estamos usando también puede definirse como contenedor de controles.
■ FlowLayoutPanel. Panel que usaremos para agmpar controles que organiza
horizontal o verticalmente de forma dinámica. Este tipo de contenedor consigue que
sus controles se adapten adecuadamente a los cambios de tamaño de forma que si el
Panel aumenta también lo harán los controles para adaptarse a la nueva dimensión.
■ GroupBox. Este contenedor muestra un marco al que se le puede agregar rm texto
opcionalmente. Suele usarse con casillas de verificación y botones de opción.
■ Panel. Es el contenedor más básico. Posee propiedades que proporcionan efectos
visuales bastante interesantes.

■ SpIitContainer. Contenedor utilizado para dividir el espacio de la aplicación en dos


partes redimensionables.
■ TabControI. Este componente es típico en cuadros de diálogo tipo propiedades de
ficheros o carpetas, características de fuente en editores de texto, opciones, etc.,
donde se visualizan diferentes pestañas tal que cada una de ellas, en fiinción de su
uso, contiene mayor o menor número de controles, siendo estos de diferentes tipos o
no.

■ TableLayoutPanel. Contenedor parecido a FlowLayoutPanel, la diferencia estriba en


que en este tipo se estructura el contenido en una cuadrícula compuesta de filas y
columnas.

A la hora de usar TabControI cada pestaña es considerada como una página (TabPage). Para
modificar cada página de este contenedor es preciso acceder a la propiedad TabPages y hacer
clic en el botón con el texto (...)junto a Colección.

Editor de la colecdón TabPage

Propiedades de tabPagel:

l|tabPage2
B m
^ Accesibilidad
0 AccessibleOescript
AccessibleName
AccessibleRole Defauh
a Aparíencta
BackColcr | |Transparent
Backgroundimage
| |(ninguno)
Backgroundlmagel Tile
BordcfStyle None
Cursor Default
h Font Microsoft Sans Serif; 8,1
ForeCoior ControIText
PJghtToLeft No
Text tabPagel

Figura 8.16. Opciones de páginas en el contenedor TabControI.

Supongamos que queremos realizar la aplicación con las siguientes características visuales:

Datos personales ¡ Datos profesionates i Datos personales ; Datos prtrfesionales i

Tíulaaón académica

Cargo en la empresa

Email corporativo

O Empleado cualificado

I Aceptar |[ Aplicar 1| Cancel ]

Figura 8.17. Pestaña Datos personales. Figura 8.18. Pestaña Datos profesionales.

1. Insertaremos en nuestro formulario un contenedor TabControI adaptando su


dimensión a la del formulario.
Capitulo 8. Entorno gráfico 337

2. Tras seleccionar el eonlencdor localizainos la propiedad TabPages y accedemos al


cuadro de diálogo de modificación de cada página.
3. Modificamos el nombre de la pestaña mediante el uso de la propiedad Text.
4. Configuramos las opciones que deseemos de cada pestaña y cerramos el cuadro de
diálogo Editor de colección TabPage.
5. Activamos la primera de las pestañas e insertamos en ella los controles que se
muestran en la Figura 8.17.
6. Realizamos la misma operación con la pestaña Datos profesionales.
7. A partir de aquí podemos codificar el funcionamiento de nuestra aplicación.

8.4.1.7. MENÚS

Insertar un menú en Visual Studio es tan sencillo como acceder al cuadro de herramientas y
localizar la categoría Menús y barras de herramientas. Existen diferentes tipos:
■ ContcxtMcnuStrip. Menú contextual. Sus opciones varían en función del lugar
donde se hace clic con el botón derecho del ratón.

■ MenuStrip. Menú ubicado normalmente en la parte superior, bajo la barra de título,


que contendrá todas las opciones que podrá ejecutar la aplicación.
" StatusStrip. Representa la barra de estados.
■ TooIStrip. Barra de herramientas en la que incluiremos botones que ejecutarán las
acciones más comunes del programa.
■ TooIStripContainer. Herramienta que proporciona paneles para cada lado del
formulario además de un panel en la zona central donde podremos ubicar otros
controles. Podremos ubicar más de una barra de herramientas y no solo en la parte
superior.
Los elementos que incluiremos en un control MenuStrip son de tipo TooIStripMenuItem. Si
estamos diseñando una barra de heraamientas cada uno de sus controles se incluyen en una
colección de Items de tipo TooIStripButton, ToolStripLabel, etc. Algo similar sucede con los
menús contextúales de fonna que cada opción del menú es un TooIStripMenúItem. Veamos un
pequeño ejemplo. Supongamos que deseamos realizar una aplicación con el entorno gráfico que
se muestra en la Figura 8.19.

éi Archivo Ayuda En la parte superior se incluye una barra de


mcnús. Como elementos principales de este
Archivo y Ayuda O. Al hacer clic en
I Comen¿ar)03 i
I
Archivo encontraremos los submenús Nuevo
y Salir.
I Bajo la barra de menús incluiremos una
I barra de herramientas © con dos botones a
I los que se les asignará iconos. Existen sitios
web que distribuyen libremente iconos para
' uso en aplicaciones,imo de ellos es:
Figura 8.19. Aplicación de ejemplo con menús.
http://www.iconlet.com/.
Para finalizar, se incluye un cuadro de texto multilínea en el interior del formulario. Podemos
convertir un cuadro de texto predeterminado para recibir una sola línea en multilínea haciendo
clic en la flecha que se visualiza en la parte superior derecha cuando se selecciona este. Se
asociará un menú contextual al cuadro de texto con las opciones Cortar, Copiar y Pegar.
Los diferentes menús se añaden al formulario como cualquier otro control visto. Empecemos
por la barra de menú:
1. Clic en el grupo de la barra de herramientas Menús y barras de herramientas.
2. Clic sobre MenuStrip.
3. Clic en el formulario. El menú se coloca de fonna inmediata en la parte superior del
formulario.

4. Veremos el texto Escriba aquí tal que podemos configurar directamente sobre el
componente los diferentes menús y submenús que contendrá.

Forml r^ir^ií
:;cr:';.3

Figura 8.20. Diseño de una barra de menús.


5. Introduciremos el texto Archivo en el primer [-^
recuadro. A continuación podemos continuar '["ArThi^cTI Ayuda
diseñando un nuevo menú o submenú. Bajo '
Nuevo
Archivo debemos incluir los menús Nuevo y
Salir mientras que junto a Archivo se agregará
Ayuda. La barra de menús debe visualizarse como —
se observa en la Figura 8.21. Figura
Figura 8.21.
8.21. MenuStrip.
MenuStrip.
6. Los elementos visuales ya se han introducido, a
continuación vamos a dedicar un tiempo a iad ^
configurar las propiedades de estos. Al insertar un
control de tipo MenuStrip en la parte inferior de la | Síi.r
vista de diseño observaremos este, de forma que ¡
sea más sencilla su selección. Haremos clic en él. /

7. Entre las propiedades localizaremos ñame y /


cambiaremos el nombre del componente a .
barraMenus. I
8. A continuación buscaremos la propiedad Items y Ei menuStripi
El menuS'.ripl
haremos clic sobre el botón de tres puntos (...)
junto a Colección. Visualizaremos las opciones Figura
Figura 8.22.
8.22. Referencia
Referencia al
al
control MenuStrip en la parte
para configurar
°
cada elemento del menú (Figura control MenuStrip en la parte
inferior de la vista de diseño.
0.23).
9. En la parte superior podemos agregar nuevos elementos al menú. Además de objetos
tipo Menúltem se pueden agregar cuadros de texto y listas desplegables. Podemos
modificar el orden de aparición de los diferentes menús a partir de los botones de
flecha arriba y abajo situados a la derecha de la lista miembros, o eliminar alguno de
Capítulo 8. Entorno gráfico

ellos usando el botón sobre el que se dibuja una x. Seleeeionaremos


archivoToolStripMenuItem para modificar sus características.
Editor de la colección de dementes

Seteecicnar demento y «gregario•la siguiente lista: barraMenus

B Menuftem " [ Agreg'ür s


Miembros 1 AJIowltemReordef False
AJIowMerge True
ContextMenuStrip (ninguno)
B «rchivoToclStnpMenuIlem
Enabled True
B «>'udaTootStnpMenu&em 1
ImeMode NoCcntrol
Mdñ'lndowüstltern (ninguno)
1 ShcwhemToorTips False
Tabinda 0
TabStop False
Visible True
1 a Datos
> (ApplicationSettings)
(DataSindings)
i^ Tag
1 a DiscAo
1
1
1 barraMenus

Figura 8.23. Editor de elementos de un control MenuStrip.

10. Cambiaremos su nombre por menuArchivo a través de la propiedad ñame.


11. Si el texto Archivo no nos parece apropiado para este elemento podemos modificarlo
a través de la propiedad Text.
12. La propiedad Enable será de utilidad ya que en función de la acción que realicemos
en la aplicación nos interesará que algunos menús estén accesibles o no, así
estableceremos el valor de Enable a true o false según el caso. Igualmente podremos
jugar con la propiedad Visible para pennitir que un menú esté visible o no en un
momento concreto de ejecución.
Una vez hemos acabado de diseñar nuestro menú, pasaremos a configurar la barra de
herramientas.

1. Clic en la barra de herramientas sobre Menús y barras de herramientas en caso de


que no estén visibles los controles de este grupo.
2. Clic sobre ToolStrip.
3. Clic sobre el fonnularío. La nueva barra de herramientas se ubicará justo bajo la barra
de menús. Igualmente veremos en la parte inferior un icono que referencia esta, por
defecto tomará el nombre de toolStripl si es nuestra primera barra de herramientas.
4. El control ToolStrip aparece en blanco, A'thivo Ayud» _
al igual que hemos hecho con la barra [E30
de menús debemos ir completando uno
a uno los botones o controles que
queremos incluir.
5. En la barra de herramientas veremos
una pequeña lista O que permite (El barraMenus S tootStrípl

seleccionar cada componente a insertar. Figura 8.24. Barra de herramientas


Clic en ella. (ToolStrip).
6. Nuestro ToolStrip estará formado exclusivamente de botones. Clic en la lista y clic en
Button. Seguidamente veremos que se ha añadido un objeto listo para ser
configurado.
7. Repetimos el paso 6 debido a que son dos los botones que incorpora nuestra barra de
herramientas.

Figura 8.25. Barra de herramientas de la aplicación de ejemplo.


8. Seleccionar toolStripl para cambiar el nombre del control. Cambiamos el valor de la
propiedad ñame por barraHerramientas.
9. Al seleccionar la barra de herramientas, se observa
en la parte superior derecha una pequeña flecha (la ® Tareas de looistrip
veremos también al seleccionar la barra de — Incrustar en TcclStrtpContamer
menús). Si hacemos clic sobre ella accederemos insertar elementes estándar
de forma directa a la configuración de los ítems de RenderModc
RenderMode: '^ ManagerRenderMode

Doclc [Top
10. Dock modifica la ubicación de la barra de Gripst>'ie: [w^ie "g'
herramientas, pudiéndose colocar arriba, abajo o a Editar elementos...

ambos lados.
Figura 8.26. Cuadro de tareas
11. Clic en Editar elementos para configurar los de ToolStrip.
botones de la barra.
12. Nuevamente veremos el editor de colección de elementos, usado en las opciones de la
barra de menús y otros controles en apartados anteriores. Clic sobre el primer botón y
escribiremos botonNuevo en la propiedad ñame.
13. A continuación vamos a asignar un icono al botón. Localizamos la propiedad Image
y clic sobre el botón de tres puntos (...) situado junto a ella.
14. Si hemos descargado o creado algún icono personalizado para nuestra aplicación, en
la nueva ventana solo tendremos que buscar este e incluirlo para ser asignado al
botón. Tras hacer clic en Aceptar el elemento de la barra de herramientas se verá
modificado con el nuevo elemento gráfico.
15. Realizamos la misma operación sobre el siguiente botón de la barra de herramientas.
Finalmente se visualizará como se observa en la Figura 8.27.

Archivo Ayuda

Figura 8.27. Aplicación con barra de menús y herramientas configurada.


Para finalizar el ejemplo solo debemos incluir un menú contextual y asociarlo al cuadro de
texto. Insertamos una etiqueta con el nombre Comentarios y un cuadro de texto multilínea. Una
vez los controles hayan sido colocados debemos introducir el menú contextual.
1. Clic en el cuadro de herramientas sobre Menús y barras de herramientas en caso de
que este grupo no esté visible.
Capítulo 8. Entorno gráfico

2. Clic en ContextMenuStrip.
3. Clic en cualquier zona del formulario. En la parte inferior será insertado el nuevo
control.

4. A la hora de agregar elementos al menú la acción será similar a la de agregar


elemento a una barra de menús. Observamos como lo hicimos algo más arriba cuando
insertamos en la aplicación el control MenuStrip. El menú contextual tendrá las
opciones Cortar, Copiar y Pegar.
5. Modificamos el nombre del menú contextual haciendo clic sobre él y modificando la
propiedad nanie. Su nuevo identificador será menuContextual.
6. Para asociar un menú contextual a un objeto debemos seleccionar este primero.
Haremos clic sobre el cuadro de texto y entre sus propiedades localizaremos la que
aparece con el nombre ContextMenuStrip. Esta propiedad permite que cuando el
usuario haga clic con el botón derecho del ratón sobre el control se muestre un menú.
En la lista junto a ContextMenuStrip seleccionamos menuContextual.
Llegados a este punto ya tenemos nuestro fomiulario perfectamente diseñado gráficamente
para aceptar órdenes. Para proporcionar funcionalidad a cada elemento de menú,submenú, botón
de barra de herramienta o menú contextual, debemos seleccionar este previamente y en el cuadro
propiedades visualizar los eventos asociados.
Al hacer doble elle sobre el evento se mostrará la vista de código fuente con el cursor en la
función de evento asociada. Solo debemos escribir el código que queramos se ejecute cuando ese
evento se dé. Podemos acceder al código de evento de forma rápida haciendo doble clic sobre el
componente.

Por ejemplo, si queremos asociar al submenú Salir el método CloseQ; solo debemos hacer
doble che sobre el evento Click y escribir entre sus llaves thls.CloseO;

ACTIVIDAD 8.6

Realiza una aplicación que muestre el siguiente aspecto:


, El menú Archivo tiene el submenú Salir
TarruAo ► » 12 i ,
■ - £n.io ~ r, j4 que cerrara la aplicación.
El menú Fuente contiene los siguientes
submenús:

■ Tamaño. Muestra un conjunto de


Archn-o Furntc
tamaños que modificará el
□ a y tamaño del texto de la etiqueta.
■ Estilo. Contiene los submenús
TamaAo » j J2 Negrita, Cursiva y Subrayado.
Realizarán las operaciones como
indican sus identificadores sobre
la etiqueta.
■ Letra. Contiene los submenús
Figura 8.28. interfaz gráfica de la Actividad 8.6. Arial, Times New Román y
Courier New.
Al seleccionar un tipo de letra el texto de la etiqueta se modificará para mostrar este.
La barra de herramientas contiene tres botones, Negrita, Cursiva y Subrayado.
Para finalizar se incluye un menú contextual sobre la etiqueta que permita modificar el
tamaño del texto.

Configura el aspecto gráfico y su funcionalidad.

8.4.1.8. PROYECTOS CON MÚLTIPLES FORMULARIOS

Normalmente crearemos aplicaciones formadas por más de un formulario. La fomia más fácil
de hacerlo es crear una nueva aplicación gráfica y a partir de ella ir agregando nuevos
formularios según vayamos necesitando como se explicó en el Apartado 8.4.1.1. AGREGAR
NUEVOS FORMULARIOS.

En este tipo de programas, uno de los formularios será considerado como el principal, siendo
el que se visualiza nada más se ejecuta la aplicación. Podemos establecer cualquier fomiulario
como principal modificando el código de la función Main en el fichero Program.cs.

static void Main{)

Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new FormlO);

Solo debemos cambiar el nombre del objeto en la función Run. Si el fonnulario inicial debe
ser Form2, en lugar de escribir new FormlQ, escribiremos new FormlQ. Al cerrar el fonnulario
principal se dará por finalizada la aplicación.
A la hora de mostrar un formulario usaremos el método ShowQ, mientras que si deseamos
realizar la operación contraria escribiremos CIoseQ o HideO- Véase el siguiente ejemplo.
Disponemos de una aplicación con la siguiente interfaz gráfica:

0^ Forml [- u ■, -lil^ ¡|||||^ Form2

Nue/a Mostrar personas Salir


Nueva Persona
Nombre Isabel

Apeilldos Oreliana

Telefono

Guardar

Figura 8.29. Forml o formulario principal de la Figura 8.30. Form2. Accesible desde Forml haciendo
aplicación. menú Nueva.
Capítulo 8. Entomo gráfico 343

Se codificará una dase persona que contiene


infonnación sobre nombre, apellidos y telefono.
Mostrar Personas Desde Fonnl, al hacer clic en Nueva aparecerá
, Jtmencí.Gnía
Fonn2 que permitirá añadir una nueva persona a
CacíiTo.H&man
Oreflana.babd
la lista (array de tipo persona). En Form2
'OreSana.babd encontramos cuadros de texto donde especificar
los datos que necesita la clase y el botón
Guardar. Al pulsar en Guardar se creará un
nuevo objeto persona y se añadirá esta a la lista.
Además Form2 se ocultará.

Al hacer clic en Mostrar personas desde la


barra de menús de Forml, visualizaremos Fonn3
y a su vez un cuadro de lista donde cada item
Figura 8.31. FormS, accesible desde el representará una persona insertada en la lista de
formulario principal al hacer clic en el menú personas que mantiene la aplicación.
Mostrar personas.
En este último formulario podremos eliminar o modificar aquellas personas insertadas con
anterioridad en la lista.

Para poder aeceder a los métodos desarrollados en cada fonnulario desde el resto estos deben
estar perfectamente conectados. Comunicaremos nuestros formularios del siguiente modo:

Formulario principal (Forml) - Declaración de variables


Form2 FormNueva=null; //Referencia a Form2 (Insertar personas)
Form3 FormMostrar=null; //Referencia a FormS (Mostrar personas)
Persona[] personas; //Array que almacena objetos de tipo Persona
const int MAX = 100; //Control del número máximo de personas en el array
int numPersonas; //Total de personas insertadas hasta un momento concreto

Menú Nuevo Menú Mostrar

/*Si el formulario aún no se ha usado


su referencia será nuil con lo que
debemos crearlo*/
if (FormNueva == nuil) FormMostrar = new FormS();
( FormMostrar.Show(this);
FormNueva = new Form2(); FormMostrar.FormPadre(this);

/*En esta occasion siempre creamos la


//Mostramos el formulario variable FormMostrar ya que la forma
FormNueva.Show(); en que usamos esta es diferente, lo
veremos a continuación*/
/*Método que permitirá comunicar Form2
con Forml*/
FormNueva.FormPadre(this);

Método FormPadreO en Form2 y FormS. Previamente ambos declaran una variable de tipo
Forml: Forml padre;
public void FormPadre(Forml padre)
{
this.padre = padre;
Mediante estas líneas de código los tres formularios quedan perfectamente enlazados.
FormZ

Forml padre;

public void FormPadre(Forml padre)


{
Form2 FormNueva=null; ^ this.padre = padre;
FormNueva=new Form2(); }

FormNueva.FormPadre(this);

FormB FormMostrar=null;

FormMostrar=new Form3();
Forml padre;
FormMostrar.FormPadre(this);
public void FormPadre(Forml padre)
{
this.padre = padre;
}

Figura 8.32. Esquema de comunicación de varios formularios a través de creación de objetos.


A partir de las variables FormNueva, FormMostrar y padre accedemos desde otros
formularios a los métodos y variables públicas de aquellos que la han declarado.
A la hora de cerrar Form2 usaremos el método HideQ mientras que cerraremos Form3 con el
uso de CloseO. Existe diferencia entre ambos. El método HideQ oculta el fonnulario pero no
elimina su referencia en memoria. La variable formulario se crea una vez y se mantiene aunque
se use el método Hide tantas veces como se quiera. Por eso, en el código de Forml referido al
manú Nuevo escribíamos el código;
— — — — — -— I

if (ForitiNueva == nuil) '


{ i
FormNueva = new Form2(); i

Si es la primera vez que entramos en esta opción crearemos la variable, en otro caso nos
limitaremos a mostrar el formulario.
El método CloseQ es diferente ya que elimina la referencia del objeto en memoria. Si
creamos la variable Form3 y posteriormente usamos CloseQ, necesitaremos volver a crear la
variable Form3 nuevamente antes de hacer ninguna operación sobre ella. Por eso en el menú
nuevo no se incluyen sentencias altemativas, siempre se ejecuta la línea FormMostrar = new
FormBQ;
El código de Forml podría ser similar al que se muestra a continuación.
i

public partial class Forml : Form


{
//Declaración de variables para acceder a formularios
Form2 FormNueva=null;
Form3 FormMostrar=null;
Capítulo 8. Entorno gráfico 345

//Lista de personas junto a variables necesarias para su control


Persona[ ] personas;
const int MAX = 100;
int numPersonas;

public Forml()
{
/*E1 constructor del formulario crea el array de persona e inicializa
además de los componentes del formulario la variable numPersonas a
cero*/
personas = new Persona[MAX];
numPersonas = 0;
In i t ia1 izeComponent();

//Evento Click para la opción de menú nueva


prívate void nuevaToolStripMenuItem_Click(object sender, EventArgs e)
{
if (FormNueva == nuil)
{
FormNueva = new Form2();
}
FormNueva.Show();
FormNueva.FormPadre(this);
)

//Evento Click para la opción de menú mostrar


prívate voíd mostrarPersonasToolStripMenuItem_Click(object sender
, EventArgs e)
(
FormMostrar = new Form3();
FormMostrar.Show(thís);
FormMostrar.FormPadre(thís);
}

//Evento Click para la opción de menú salir


prívate void salirToolStripMenuItem Click(object sender, EventArgs e)
(
this.Cióse();
}

//Método que devuelve el total de personas que puede incluir el array


publíc ínt TotalPersonas()
{
return personas.Length;
)

//Método que devuelve el número de personas incluidas en la lista


piiblíc ínt NumeroPersonas()
(
return numPersonas;
/*Creainos un indizador que permita acceder desde Forml en otros
formularios a la lista de personas.*/
public Persona this[int pos]
{
set //Se inserta un elemento siempre que la lista no esté llena
{
if (pos < MAX)
{
personas[pos] = valué;
numPersonas++;
}
else
{ /*En caso de que la lista esté llena se mostrará un mensaje
indicando que no se ha producido la inserción*/
MessageBox.Show("Error, imposible agregar más personas"
,"Error en insercción",MessageBoxButtons.OK);
}
}
get //Se devolverá la persona que se encuentre en la posición pos
{
if (pos < personas.Length)
{
return personas[pos];
}
else //Si la posición no es correcta devolveremos nuil
{
return nuil;

La clase Persona puede codificarse como sigue;


string nombre; ; pv
string apellidos; 1 {
string telefono; ;

public Persona O I
{ I
this.nombre = ■
this.apellidos = 1
this.telefono = ;
} ' ;

public Persona(string nombre ;I >


/String apellidos) ; pi
^ .
this.nombre = nombre;
i1 {
this.apellidos = apellidos; ;
this.telefono = 1

public Persona(string nombre


/String apellidos, string telefono)

this.nombre = nombre;
this.apellidos = apellidos;
this.telefono = telefono;
Capítulo 8. Entorno gráfico 347

return telefono;

telefono = valué;

public stiring ToStringO


{
string cadena =
cadena = apellidos + + nombre;
return cadena;

Para finalizar se muestra el código de los formularios Form2 y FormS.


public partial class Form2 : Form
{
Forml padre;
public Form2()
{
Ini ti a1izeComponent();

public void FormPadre(Forml padre)

this.padre = padre;

P^ivate void. buttonl_Click(object sender, EventArgs e)

/*Creamos una nueva persona a partir de los datos escritos en los


cuadros de texto del formulario*/
Persona nueva = new Persona (txtNombre.Text
,txtApellidos.Text, txtTelefono.Text);

/*Hacemos uso del indizador de forma que insertamos a la nueva persona


en la primera posición libre del array. Seguidamente se oculta el
fo rmu1a rio*/
padre[padre.NumeroPersonas()]=nueva;
this.Hide();

public partial class Form3 : Foi


{
Forml padre = nuil;
public Form3()
{
InitializeComponent();
/*A la hora de asociar este formulario con Forml o formulario principal
inicializamos el cuadro de lista con las personas ya almacenadas en el
array personas declarado y creado en Forml. Para agregar un nuevo
elemento a una lista debemos usar la colección Items seguida del método
AddO .*/
public void FormPadre(Forml padre)
{
this.padre = padre;
int i = O;
Persona aux;

while((aux=padre[i])!=null)
{
listaPersona.Items.Add(padre[i].ToString());

prívate void button3_Click(object sender, EventArgs e)

this.Glose();

ACTIVIDAD 8.7

Los botones Eliminar y Modificar de Form3 no se han codificado. Completa la aplicación


codificando los eventos Click de estos controles de forma que al hacer clic en Eliminar se
elimine el elemento seleccionado de la lista del array de personas y al hacer clic sobre Modificar
se muestre el Fomi2 de fonna que podamos cambiar los datos de la persona seleccionada. Añade
un botón modificar a Form2, recuerda que debe estar deshabilitado cuando accedamos al
formulario desde el menú Nueva.

Hasta el momento hemos estado usando múltiples formularios de forma que todos ellos se
han estado mostrando de forma no modal. El uso de accesos no modales conlleva a que una vez
abierta la ventana esta pierde el foco si hacemos clic sobre otro formulario también visible.
Las ventanas no modales se abren a través del método ShowQ, mientras que los fonnularios
modales usarán el método ShowDialogQ. Cuando se realiza una llamada al método
ShowDialogO el código que sigue no se ejecutará hasta que no se haya cerrado la ventana,
además, ningún otro formulario será accesible.
Para que la ventana se cierre la propiedad DialogResult del formulario debe tener un valor
distinto de cero. Se asignan valores a esta propiedad cuando se hace clic sobre alguno de los
botones asociados al formulario. Serán estos botones los que de forma automática modifican esta
característica.

Resumiendo, un formulario modal será similar a los cuadros de diálogo propios de


aplicaciones como cuadro propiedades, fuente, etc. Estas ventanas no permiten continuar con
la aplicación mientras se encuentran abiertas. Normalmente, un formulario modal evalúa el valor
devuelto por ShowDialog y en función de este realiza unas operaciones u otras.
Capítulo 8. Entorno gráfico 349

Véase un pequeño ejemplo. Hemos desarrollado una aplicación de dos formularios sencillos.

<':í Forml |<=> |pQ^I«£3wl

FoonJaito 2

Figura 8.33. Formulario principal de la aplicación, Figura 8.34. Formulario Form2.


denominado Forml.

Configuramos los botones de Fonn2 para que al ser pulsados generen un resultado válido para
el método ShovvDialog(), además de las operaciones que se codifiquen en zona de desarrollo.
1. Seleccionamos el botón Mostrar.

2. Localizamos el cuadro de Propiedades la propiedad DlalogResult.


3. Al hacer clie en ella veremos una lista de opciones, seleccionamos OK.
4. Acabamos de indicar que al hacer clic sobre el botón mostrar se devolverá OK
(equivale a un valor numérico).
Realizamos la misma operación para el botón Cerrar. En esta ocasión daremos el valor
Cancel a su propiedad DlalogResult.
A continuación haremos doble clic sobre el botón Formulario! de Forml e introduciremos el
siguiente código fuente:
----- - —

Form2 formulario = new Form2(); i


if (formulario.ShowDialog() == DialogResult.OK) i

MessageBox.Show("Has hecho clic en Mostrar"); I


} i
else 1
{ i
MessageBox.Show ("Cancelaste"); i
} i
La línea if(formularío.ShowDialogO = DiaíogResult.OK){...} refleja la esencia de este
tipo de acceso a fomiularios. Al ejecutar ShowDialog sobre formulario estamos pidiendo que se
visualice FonTi2 de forma que hasta que no sea cerrado no se continúe procesando el resto del
código. Cuando Fomi2 se cierra se compruebra el valor que ShowDialog ha devuelto. En caso de
haber hecho clic sobre Mostrar el valor devuelto será OK, contenido en la propiedad
DialogResult y se visualizará el mensaje "Has hecho clic en Mostrar". En cualquier otro caso el
mensaje mostrado será "Cancelaste".
Para finalizar, existe otro modo de tratar proyectos con múltiples formularios, y es a través de
la creación de proyectos MDI (Interfaz de Múltiples Documentos). En este tipo de
aplicaciones una de las ventanas se considera contenedor o formulario padre tal que el resto,
formularios hijos, se visualizan en su interior. Para crear proyectos MDI:
1. Creamos un proyecto de aplicación para Windows tal y como hemos estado haciendo
hasta ahora.

2. Seleccionamos el fonnulario que el proyecto crea por defecto y establecemos el valor


de la propiedad IsMdiContainer a truc.
3. Visualmente el formulario cambia, el fondo se oscurece, mostrándose acorde a la
función de contenedor de otras ventanas que va a desempeñar.
4. Normalmente, el formulario MDI principal incluirá las barras de menús, herramientas
y estado. A través de ellas accedemos al resto de formularios secundarios. Así, vamos
a insertar una pequeña barra de menús con las opciones Hijol e Hijo2(Véase 8.4.1.6.
MENÚS).
5. A continuación pasamos a agregar un formulario secundario. La creación de este
nuevo objeto de la clase Form se lleva a cabo como hasta ahora, clic con el botón
derecho sobre el proyecto en el explorado de soluciones, agregar y Windows Form.
Lo llamaremos FormHijol.
6. Asignamos el evento Click al menú Hijol y escribimos el siguiente código fuente:
1 prívate void hijolToolStripMenuItem_Click(object sender, EventArgs e) ;
i ^
¡ FormHijol formHl = new FormHijol();
i
;
1 formHl.MdiParent = this; ;
1 formHl.Show(); I
I I

; } ;

7. El código es similar al usado en primera instancia para comunicar proyectos con


múltiples formularios, la diferencia estriva en el uso de la propiedad MdiParent por
la que se indica al formulario secundario cuál es el formulario padre (contenedor).
8. Para dar funcionalidad al menú Hijo2 repetiremos los pasos 5 a 7.
Existen bastantes referencias web que ayudarán al lector en la creación de aplicaciones MDI.
Se aconseja echar un vistazo a http://msdn.microsoft.com/es-es/library/.

I NOTA: La creación de formularios secundarios no es necesaria, es posible generar


! estos mediante código exclusivamente (Form formHljo=new FormO;). Así podríamos
í crear una aplicación MDI con un único formulario (formulario padre).

ACTIVIDAD 8.8

Desarrolla xm pequeño editor de texto. Debe tener un menú con las opciones:
Archivo: Nuevo, Salir.
■ Al hacer clic en Nuevo se mostrará un nuevo formulario con un cuadro de texto
donde escribiremos este.

■ Salir cerrará la aplicación.


Edición: Cortar, Copiar y Pegar.(Genera además un menú contextual con estas opciones).
■ Cortar. Realizará la acción de cortar sobre el texto que se haya seleccionado de un
formulario generado a partir del submenú nuevo.
■ Copiar. Similar a Cortar pero en esta ocasión realizaremos la orden de Copiar.
■ Pegar. Usaremos el texto del portapapeles y lo colocaremos sobre el cuadro de texto
del formulario activo.
Capítulo 8. Entorno gráfico 351

Fuente. Muestra un formulario donde dispondremos de la lista Tipo de letra, lista Tamaño,
lista Estilos (negrita, cursiva y subrayado) y lista Color del texto. Al cerrar el formulario el texto
del cuadro de texto activo se verá modifcado por las modificaciones realizadas en el formulario
Fuente.

Además la aplicación contará con una barra de herramientas con las opciones Nuevo, Lista
Fuente y Lista Tamaño.
Realiza un proyecto MDl bien estructurado que funcione tal como se indica.

8.4.2. PROYECTOS GRÁFICOS EN NETBEANS


La creación de proyectos gráficos en Netbeans es muy similar a la vista en Visual Studio.
Encontraremos diferencias en nombres de controles, propiedades, eventos o métodos pero la
esencia de creación de un proyecto gráfico en Java con el uso de Netbeans es la que hemos
estudiado en C# bajo Visual Studio.

8.4.2.1. PROYECTOS GRÁFICOS EN NETBEANS

Netbeans no distingue a la hora de generar un proyecto entre proyectos gráficos o de consola,


será el usuario quién determine de que tipo será im proyecto después de haber sido creado,
mediante la inserción de un fonnulario o no y la codificación de su método principal.
Así, a la hora de crear una aplicación gráfica, comenzaremos creando una aplicación tal y
como hemos estudiado en el Apartado 1.9.3. CREAR UN NUEVO PROYECTO EN
NETBEANS 7.3 del Capítulo 1 y posterionnente añadiremos un formulario a esta.
Para agregar un formulario a un proyecto en Netbeans:
1. En el explorador de proyectos expandiremos este de forma que veamos la carpeta
Source Packages y en su interior el paquete por defecto.
2. Clic con el botón derecho del ratón sobre el paquete y New. Clic en JFrame Forra...
3. Escribimos el nombre del fonnulario y Finish.
Podemos añadir tantos fonnularios como deseemos. Al igual que en Visual Studio podemos
configurar interfaces de múltiples fonnularios. En Netbeans la selección del fonnulario que debe
ejecutarse en primer lugar no se realiza de forma automática, seremos nosotros los
programadores quienes debamos detallar que formulario debe iniciarse al ejecutar la aplicación.
Así, una vez creado el fonnulario debemos instanciar este y hacerlo visible. Veremos esto
más adelante.

Se podrá probar la interfaz de cada fonnulario y su funcionalidad si lo seleccionamos y en el


menú contextual o en la barra de herramientas pulsamos el botón de ejecutar (Opción Run o
flecha de color verde).

8.4.2.2. ENTORNO DE TRABAJO

Al insertar un objeto visual en nuestra aplicación, si está seleccionada, el entomo de trabajo


varía mostrando nuevas paletas que permitirán configurar el aspecto gráfico y características de
los controles que podremos usar.
Netbeans se mostrará como se observa en la Figura 8.35.
I Sara I Po^Ti rtiB., Síd S — — il^iii» ♦»*
; ! ^'"^•c*»VF"»'W*'*íe'oficsr.:*r*»tfM5«tl»yxíRir«^fror««ariei:f*ru

pTrim*] ■ Propcrtwft ■
j tt5C*t»!i^ Bnárig t*rf** Coót
i
Oe<MnCle»eOp«r«tion OJT.OTí.CVOZ
en

* ' o^tv^rvnm*
*.-**>«0a'C9
•)-»04TecS<Ae«>^
UCLgr»»^ 0~*C?íí5*5}

Figura 8.35. Entorno de trabajo en Netbeans.

■ Navegador. Permite explorar los objetos incluidos en un fichero .java. Si el código


fuente refiere una aplicación en modo consola, sin entorno gráfico, el navegador
mostrará las funciones creadas y variables. Si el objeto activo es un fonnulario en su
vista gráfica en el navegador observaremos los controles que se han insertado hasta el
momento en él.

■ Objetos abiertos. En la zona superior de la interfaz se irán colocando pestañas por


cada uno de los formularios o archivos de código fuente abiertos. Podemos acceder a
uno u otro haciendo clic en la pestaña que le corresponda.
■ Vista de diseño. Es la vista predeterminada o vista por defecto de un fonnulario o
contenedor. En ella podremos insertar controles de forma rápida y sencilla. Es posible
visualizar un formulario en vista de diseño o vista de código fuente, para acceder a
una u otra sólo tendremos que hacer clic sobre Desing (Diseño) o Source (Código).
■ Paleta. La paleta es el equivalente a cuadro de herramientas usado en Visual studio.
En ella se tendrá acceso a todos los controles que pueden insertarse en un formulario
Java.

■ Propiedades. Refiere el cuadro de propiedades desde el que podremos modificar las


características de un control o el propio formulario en Netbeans.
A la hora de crear aplicaciones con formularios el proceso será igual al ya visto;
1. Añadir un nuevo formulario.
2. Añadir controles a este según necesidades.
3. Seleccionar los controles añadidos y modificar sus características si la funcionalidad
de la aplicación así lo precisa.
Capítulo 8. Entorno gráfico

4. Configurar eventos o sucesos que de algún modo podrían bombardear la aplicación


dando respuesta mediante acciones.

8.4.2.3. CONTROLES

Antes de comenzar a hablar de controles Java es preciso que hablemos de las bibliotecas que
usa el lenguaje para desarrollo de Interfaces de Usuario Gráficas (GUIs). Si observamos el
cuadro de herramientas o paleta, veremos diferentes categorías, entre las que se distinguen
SWING y AVVT.
AWT (Abstract Windowing Toolkit) contempla el primer conjunto de clases que
refereneian componentes gráficos en Java, primer kit de herramientas gráficas. Swing aparece en
la versión 2.0 y mejora el aspecto de los controles y su funcionalidad. Hoy se siguen incluyendo
ambas bibliotecas ya que en determinados dispositivos o aplicaciones se hace más eficiente el
uso de una u otra.

Los controles Java equivalentes a los que hemos estudiado en Visual Studio son:
AWT SWING Visual Studio (C#)

Labe! I Label JLabel 1 label Label Label

TextFieId íáB TextFieId JTextFieId 1 i Text Field


TextBox M TextBox
TextArea Text Area JScrollPane Í^Text Area
Button QkI Button JButton loFI Button Button p) Button

CtieckBox Checkbox JChechBox E- Chedc Box CheckBox El CheckBox


Choise Cholee JComboBox 03 Combo Box ComboBox ^ ComboBox
List m List JScrollPane List LIstBox 10 ListBox
JRadioButton Radio Button RadioButton © RadioButíon

Los primeros controles gráficos (AWT) no eran demasiado bonitos, similares a los que se
usaban en páginas web de la época en las que se incluían fonnularios. Los controles swing
mejoran el aspecto gráfico de las aplicaciones Java.

buttoni

AWT í SWING

Figura 8.36. Diferencia visual entre controles AWT y SWING.

8.4.2.4. EVENTOS Y PROPIEDADES MÁS COMUNES EN CONTROLES JAVA


Teniendo en cuenta que ya hemos estudiado los controles C# y que Java es un lenguaje de
programación muy parecido, veamos qué propiedades en Java son equivalentes a las ya vistas en
C# y especifiquemos alguna que pueda semos de interés y únicas de objetos Java.
Equivalencia entre propiedades de objetas gráf^os C#}^aya
Propiedades C# Propiedades Java __
Ñame ñame. Aunque la propiedad establece el nombre del control no tiene el
mismo significado que en C#. Este nombre identifica al objeto, sin
embargo, el nombre de la variable que referencia el tipo de objeto no varía.
Para establecer un nuevo nombre a un control debemos pinchar en él con el
botón derecho^1 ratón y hacer clic en^Change Variable Ñame...
Locatíon No podremos modificar la localización desde el cuadro de propiedades.
Para poder establecer una nueva posición se usa el modificador
setLocation(int x,int y). Este método, aplicado sobre un control,
modificará la distancia de la esquina superior izquierda del control con
respecto al borde superior e izquierdo del objeto contenedor que lo incluye.
El método recibe dos números enteros, los nuevos valores x e y.
Visible visible
BackColor background
Font Pennite modificar tipo de letra, tamaño y_estil^o.
ForeCore foreground
Text ¡Jabel. Cuando usamos cuadros^texto la_ propiedad se denomina text
Enabled enabled. En Java se muestra una casilla de verificación. Al hacer clic sobre
i ella activamos la propiedad, es decir, indicamos que enabled sea true.
: Nonnalmente las propiedades de tipo boolean se muestran en el cuadro de
; herramientas mediante una carifia de verificación.
I Propiedades Híorizontal Size y Vertical Size^
iconimage ^ / f
Propiedades relacionadas con cuadros de texto:
■ editable. Establece si puede escribirse o no en el cuadro de texto.
■ border. Permite establecer el tipo de borde del control.
■ disabledTextColor. Color del texto ubicado en el cuadro cuando este se encuentra
deshabilitado, es decir, la propiedad enabled es false.
■ margin. Establece la distancia entre el borde y el texto.
Propiedades relacionadas con casillas de verificación:
■ selected. Permite que una casilla de verificación aparezca seleccionada o no al
mostrarse en pantalla.
Propiedades relacionadas con botones de opción:
■ selected. Permite que el programador elija si el botón de opción se encuentra
seleccionado o no cuando comience la aplicación.
Por defecto los botones de opción no pertenecen a un único grupo cuando se insertan, es
decir, todos los botones pueden ser seleccionados, algo inusual en este tipo de objetos. Para
agrupar un conjunto de botones de opción debemos haber insertado previamente un Button
Group. Este control no cuenta con interfaz gráfica pero puede ser seleccionado a través de la
ventana Navigator. Una vez hemos agregado un Button Group, debemos seleccionar uno a uno
los botones de opción creados, e ir modificando la propiedad buttonGroup en cada uno de ellos.
Capítulo 8. Entorno gráfico

La propiedad ButtonGroup muestra una lista de grupos de botones de opción, solo debemos
eseoger aquel al que se desea pertenezea el nuevo botón de opción.
Propiedades relacionadas eon cuadros de lista desplegable:
■ selectedIndex. Devuelve o establece la posición del elemento de la lista que debe
estar seleccionado.

■ selecteditem. Parecida a la anterior, ambas propiedades se encuentran enlazadas, de


forma que el modificar una de ellas la otra se verá también afectada. Establece o
determina el elemento de la lista seleccionado. Si tenemos una lista de tres elementos
y en selectediten escribo el nombre del segundo la propiedad selectedindex
cambiará, ya que el nuevo valor seleccionado.
■ model. Permite establecer los elementos que formarán la lista. Estos se colocan
separados por comas.
Propiedades relacionadas eon cuadros de lista (List):
■ mode!. Determina los elementos que forman la lista. Al igual que en las listas
desplegables se colocan separados por comas.
■ selectionMode. Establece el modo en que pueden ser seleccionados los componentes
de la lista. Recordamos que en un listBox podemos seleccionar más de un elemento a
la vez. Los valores entre los que elegir son:
—> SINGLE. Sólo se permite seleccionar un elemento de la lista.
^ MULTIPLE_INTERVAL. Podemos seleccionar más de un elemento,
podiendo estos no estar adyacentes, es decir, podemos usar tanto la tecla
CTRL como SHIFT para realizar la selección.
—> SINGLE_INTERVAL. Pemiite seleccionar más de un elemento pero todos
ellos tienen que estar continuos.
■ selectedindex. Pemiite modificar o mostrar el índice del elemento de la lista que está
seleccionado.

■ selected Valué. Similar al anterior pero se indica el valor a seleccionar.


■ selectionBackground. Establece el color de fondo utilizado para visualizar
elementos seleccionados.
Propiedades relacionadas con botones:
■ ¡con. Pemiite asignar un icono a un botón tal que podamos verlo en lugar del típico
texto o ambos.

■ buttonGroup. Podemos asociar un botón a un Button Group y alterar así su


comportamiento.
■ iconTextGap. En caso de que hayamos determinado que el botón contenga un icono
y un texto en esta propiedad se indicará el espacio entre ambos.
■ pressedicon. Icono que se mostrará cuando el botón sea presionado.
Al igual que en C# son muchas las características que se pueden configurar, en este apartado
hemos estudiado solo algunas, dejamos al lector que explore los controles Java y empiece a
356 Programación

desenvolverse en este IDE (Netbeans). Si bien es cierto, en Java quizas sea mas comoda la
configuración de propiedades de controles a través de código fuente, además, muchas
características no pueden ser cambiadas como no sea a través de la vista de código.
Desde la ventana de propiedades accedemos a los eventos que pueden aplicarse a cada uno de
los controles incorporados en nuestra aplicación. Para visualizar estos solo debemos hacer clic
sobre Events.

Al igual que hacíamos con las propiedades veamos las equivalencias de eventos entre los
lenguajes de programación estudiados.

Equivalencia entre eventos de C# y Java

Eventos C# Eventos Java

Click actionPerformed

Move componentMoved
•iini

Enter

MouseDown

MouseUp
MouseHover

MouseMove

KeyDown keyPressed
KeyUp KeyReleased

ACTIVIDAD 8.9

Es interesante estudiar cómo gestiona Java los eventos, además de profundizar en la gestión
que también realiza C#. En este libro hemos estudiado tan solo el uso de eventos como
respuestas a iteraciones del usuario con nuestras aplicaciones y se usan eventos característicos
sin saber de dónde proceden.
Usa la web o documentación aportada por el profesor y realiza un pequeño documento donde
describas la gestión de eventos tanto en Java y C# así como cuál sería la codificación de una
pequeña aplicación con eventos desarrollando los listeners oportunos.

8.4.2.5. EJEMPLO SENCILLO DE APLICACION JAVA

En este apartado vamos a desarrollar la aplicación de ejemplo vista en C# del Apartado


8.4.1.4. DESARROLLO DE UNA PEQUEÑA APLICACIÓN DE EJEMPLO para así estudiar
de forma práctica los componentes gráficos de Java y observar diferencias con C#.
Capítulo 8. Entorno gráfico 357

Fofml Comenzaremos creando una aplicación en


Netbeans y agregaremos un JFrame a ella que
Calculadora
estructuraremos como en la Figura 8.37. La
inserción de controles en él se hace de forma
similar a la vista en Visual Studio (clic en el
componente y a continuación clic en el
fonnulario).
Figura 8.37. Conversor Euros/Dólares.

El formulario Java se verá como se muestra en la Figura


j■ L"' El código de la aplicación es muy sencillo.
Calculadora debemos esperar a que el usuario haga
clic en el botón y a continuación recoger lo
Eufos j I Dólares j que haya escrito en el cuadro de textos Euros,
I Convenir j pasar esta cantidad a Dólares, y mostrarla en el
cuadro con el mismo nombre.

Figura 8.38. Conversor Euros/Dólares desarrollado


Básicamente el código es el que se
con controles swing de Java. visualiza a continuación:

prívate void btnConvertirActionPerformed (java.awt.event.ActionEvent evt) {


double euros = Double . parseDouble (txtEuros . getText () ) ;
doiible resulConvertir=euros*l. 3194;
txtDolares . setText (Double . toString (resulConvertir) ) ;
)

■ Los eventos en Java se denominan a partir del nombre del control que los provoca y
el evento en sí: btnConvertir (botón) ActionPerformed (Evento),
btnConvertirActionPerformed.

■ Java no soporta en sus clases la codificación de propiedades de forma que cada vez
que se debe modificar o seleccionar el valor de un atributo de una clase se deben usar
métodos modificadores o selectores. Por ejemplo, en C#, a la hora de cambiar el texto
de un cuadro de texto escribíamos cuadro.Text ya que Text era una propiedad de la
clase, en Java es necesario crear un método, así tenemos cuadro.setText(valor) y
cuadro.getTextO-
■ La converisión de tipos se realiza a través de las clases envolventes. Así, el típico
double.Parse se modifica a Double.parseDouble.
■ A la hora de escribir en un cuadro de texto usaremos el método setText al que
debemos pasar como parámetro una cadena de caracteres (variable String). En
nuestro ejemplo debemos escribir un valor numérico, así este debe ser convertido.
Para ello volvemos a hacer uso de las clases envolventes y el método toStringO-
A continuación debemos controlar que el cuadro txtEuros no este vacío ya que esto
provocaría una excepción. Para ello vamos a usar una sentencia alternativa tal que si no se ha
escrito nada en el cuadro de textos muestre un cuadro de diálogo.
C# dispone de una clase, MessageBox que genera de forma automática cuadros de diálogo de
advertencia, en Java debemos "fabricar" estos.
1. Activamos la vista de diseño del fonnulario.
En la paleta de herramientas desplegamos la categoría Swing Windows.
Clic en Dialog. A continuación haremos clic sobre cualquier zona del formulario.
Veremos que en el Navegador aparece bajo Other Components el componente
JDialog.
Cambiamos el nombre al cuadro de diálogo haciendo clic con el botón derecho del
ratón sobre él en el Navegador. El nuevo nombre será CuadroMensaje.
El formulario ha sido ocultado y en su lugar vemos un panel vacío. Para que simule
un cuadro de mensaje debemos agregar al menos una etiqueta con el texto de error a
mostrar y un botón que permita cerrarlo.
Agregamos una etiqueta a la que asignaremos el texto Debes escribir una cantidad
numérica en el cuadro euros. Podemos modificar el panel de fonna que en la
propiedad title se especifique un título al cuadro de diálogo, por ejemplo Error en la
conversión.

Agregamos un botón al que asignaremos el texto Aceptar. Además, asignaremos


código fuente antes de volver al formulario principal. Haremos doble clic sobre él y
en el evento ActionPerformed escribiremos la instrucción:
CuadroMensaje.disposeO;
Una vez hemos acabado de configurar nuestro cuadro de diálogo debemos hacer
referencia a él desde el formulario principal, tal que si no se escribe nada en el cuadro
euros este se haga visible. Modificamos el evento ActionPerfonned del botón
convertir como sigue:
if(txtEuros.getTexto .compareTo("")==0){
CuadroMensaje.setVisible(true);
else{
double euros-Double.parseDouble(txtEuros.getText());
doiible resulConvertir=euros*l.3194;
txtDolares.setText(Double.toString(resulConvertir));

Ya que Java no soporta propiedades"sí sé desea acceder al atributo visible de un formulario


debe hacerse a través de los métodos setVisible o getVisible.
9. Al ejecutar la aplicación y no escribir nada en el cuadro de texto euros se mostrará un
cuadro de diálogo, ahora bien, este casi no será visible, a través de código debemos
establecer las dimensiones de nuestro objeto. Modificamos el código tal que
finalmente se muestre como se ve a continuación:
— — — —• — — I
if(txtEuros.getText().compareTo("")==0){ i
CuadroMensaje.setSize(400, 200); ;
CuadroMensaje.setVisible(true); 1
} ;
else{ ;
double euros=Double.parseDouble(txtEuros.getText());
—— i-"-/ lui-'-Lc uijx«=i ''
;
double resulConvertir=euros*l.3194;
double rpic:n 1 p/-xr^TTo-4- !
txtDolares.setText(Double.toString(resulConvertir));
Capítulo 8. Entorno gráfico 359

ACTIVIDAD 8.10

Añade a la actividad de ejemplo una nueva funcionalidad de fonna que la conversión pueda
realizarse en ambos sentidos. Si el cuadro euros está relleno se realizará la conversión a dólares.
Si el cuadro dólares está relleno, la conversión se realizará a euros. En caso de que ambos
cuadros estén vacíos o llenos se mostrará un cuadro de diálogo alertando de esta situación ya que
no se podrá realizar ninguna operación hasta que solo uno de ellos tenga un valor.

8.4.2.6. CONTENEDORES EN JAVA

Los contenedores como ya hemos estudiado son elementos gráficos que agrupan a un
conjunto de controles. En Java encontramos dos tipos de contenedores:
■ Superiores: JFrame, JDialog y JApplet. En Netbeans se localizan en el cuadro de
herramientas bajo la categoría Swing Windows.
■ Intermedios. JPanel, JSplitPane, JscrollPane, JToolBar o JInteraalFrame. Estos se
encuentran en la categoría Swing Containers en el cuadro de herramientas^.
Los contenedores superiores pueden incluir contenedores intermedios y están diseñados para
albergar menús o barras de herramientas además de disponer un aspecto similar a ventanas del
sistema operativo (poseen barra de título, botones de acción maximizar, minimizar o cerrar y
menú de control).
A la hora de diseñar una aplicación gráfica Java debemos:
1. Crear y configurar un contenedor superior. Para ello establecemos el tamaño del
contenedor a partir del método setSizeQ o propiedad correspondiente en el cuadro de
propiedades, y posterionnente hacemos este visible (selVisibleQ) al arrancar el
programa.

2. Incluimos un contenedor intennedio a través del método addQ.


3. Agregamos los controles que necesitaremos en nuestra aplicación.
Netbeans realiza las acciones 1 y 2 de forma automática cuando agregamos im Formulario a
nuestro proyecto. Observar el navegador y desplegar el objeto Form para ver los elementos que
incluye.
Así, si necesitamos crear ima aplicación con más de un formulario podemos optar por generar
un único JFrame e ir añadiendo paneles (contenedores intennedios) a este e ir haciéndolos
visibles en función de las necesidades.

■ Jframe. Representa el fonnulario o ventana. Es el elemento principal de una


aplicación gráfica Java. Es el equivalente al objeto Form visto en C#.
■ JDialog. Este contenedor también es considerado como elemento principal, pudiendo
ser usado de fonna independiente en la aplicación (al igual que el JFrame) aunque
nonnalmente se usa para el diseño de cuadros de diálogo tipo advertencia,
modificación de propiedades, etc. Estará incluido en alguno de los fonnularios ya
existente de fonna que se mostrará al producirse algún evento. Se pueden configurar
para que sean modales o no.

^ Existen algunos otros contenedores que no se han mencionado.


NOTA: Convertiremos un JDialog en modal mediante el uso de la función
setModalQ-

■ JApplet. Diseñado para ser visualizado en intemet, embebido en código HTML.


■ JPanel. Contenedor usado para agrupar controles que tienen cierta relación en la
aplicación. Podría decirse que son pseudo-ventanas incluidas en objetos JFrame de
forma que podemos desarrollar una interfaz de múltiples formularios.
■ JSplitPane. Se usa para dividir dos componentes. Si insertamos el contenedor
observaremos una pequeña barra vertical entre ambos.
■ JScrollPane. Asociado a controles cuya área es extensa y no pueden hacerse visibles
por completo en el contenedor principal. JScrollPane proporciona una barra de
desplazamiento vertical y horizontal que pennite acceder a todas las zonas del
control.

■ JToolBar. Barra de herramientas. Diseñado para contener botones u otros controles


que permiten el acceso rápido por parte del usuario a acciones comunes en la
aplicación.
■ JlnternalFrame.Ventanas o frame internos. Podemos desarrollar aplicaciones MDI
similares a las que hemos realizado en C# a partir de este contenedor.
8.4.2.7. MENÚS EN JAVA
Una vez tenemos nuestros objetos contenedores diseñados podemos incluir menús que
faciliten la interfaz. Para ello, en el cuadro o paleta de herramientas haremos clic en la categoría
Swing menus. Para insertar un menú:
1. Insertamos la barra de menú(Menú Bar).
2. La barra de menús incluye dos elementos, para añadir más menús principales
usaremos el control Menú.

3. Si queremos agregar submenús a nuestros menús principales podremos incluir


nuevos Menus o usaremos los controles Menú Item, Menú Item / CheckBox o
Menú Item / RadioButton.
4. Si lo deseamos podemos incluir separadores conceptuales entre elementos del
menú a partir de Separator.
Así, los elementos de menús de los que disponemos son:
■ Menú Bar(JMenuBar). Barra de herramientas. Es el objeto contenedor de menús.
■ Menú (JMenu). Representa cada elemento del menú principal o secundario que
agrupar otros menús.

■ Menú Item (JMenuIntem). Representa una opción concreta del menú que al ser
pulsada generará una acción. Encontraremos varios tipos de menús finales. Menú
Item CheckBox (JCheckBoxMenuItem) y Menú Item RadioButton
(JRadioButtonMenuItem).
y Capítulo 8. Entorno gráfico

Scparator (JSeparator). Linea divisoria. Permite dar un determinado aspecto visual


y separa las opciones en función de la acción que desempeñan.
361

Barra de menus(Menú Bar)

jusb&caoo p-
l^bniefiús(Ménu)
Menú ttpo boton de
opción(Menú
iift il: tadioButton) 7

Menú tipo casillas de


, verificación(Menú
CheckBox)

Figura 8.39. Esquema de elementos de diseño de menús en Java.

Además, podemos incluir menús contextúales o Popup Menú. Vamos a realizar a


continuación una pequeña aplicación que utilice los elementos Java estudiados hasta ahora.
Imaginemos que nos piden desarrollar un programa que presenta la siguiente interfaz gráfica:
l^'.l Editor de textos vl.O 1. Creamos un nuevo proyecto y agregamos
Archivo Editar Fuente Ayuda a este un fomiulario (JFrame).
u3 Qj 12) 2. Agregamos una barra de menús a este.
Clic en la paleta de herramientas,
categoria Swing Menus y a continuación
Mena Bar.

3. Clic en el formulario para dar orden de


incluir la nueva barra. Esta se coloca en la
parte superior de la ventana.
4. Modificamos el nombre de los menús que
incluye y agregaremos dos más. Clic en el
primer menú (JMenul) y el cuadro de
propiedades modificamos el valor de la
Figura 8.40. Interfaz gráfica de la aplicación de
propiedad Text al Archivo.
ejemplo.
5. Realizamos la misma operación con el siguiente menú (jMenu2). Le daremos el
nombre de Editar.

6. De los dos nuevos menús a agregar, uno de ellos cuenta con submenús y el otro
no, para añadir el menú que contiene más ítems haremos clic en Menú y a
continuación clic en la barra de menús. Veremos el nuevo elemento con el nombre
jMenu3, modificamos este por Fuente. Repetiremos la operación y añadiremos el
último componente de nuestra barra. Lo llamaremos Ayuda.
7. A continuación pasamos a ir configurando las opciones internas de cada menú.
Usaremos Menú Item de forma que el proceso será repetitivo pero no
complicado. Clic en Menú Item, colocamos el cursor sobre archivo y al visualizar
este con un recuadro punteado y anaranjado volvemos a hacer clic. Acabamos de
asignar un submenú al menú Archivo. Lo llamaremos Nuevo.
8. Repetiremos el punto 7 hasta completar el menú de la aplicación, tal que:
■ Archivo contiene los submenús: Nuevo y Salir.
■ Editar contiene los submenús: Cortar, Copiar, Pegar y Seleccionar todo.
■ Fuente contiene los submenús: Tipo de letra. Tamaño de Letra y Estilo.
■ Ayuda no contiene submenús.
9. Sería interesante modificar los nombres de las variables que representan cada
elemento de la barra de menús. Podríamos haber realizado esta operación al
mismo tiempo que hemos ido insertando un nuevo menú. Recordamos que
debemos hacerlo a través del navegador de objetos. Para el ejemplo se ha llamado
a cada menú de igual modo que el texto que visualizan.
10. El navegador de objetos se muestra tal y como se observa en la Figura 8.41.
;iMenu4[jfjenu]-Navigator a I B j^ Agregamos adeiuás uua pequeña barra de
heiTamientas en la que incluiremos los botones
9 BJfarne] guardar, cortar, copiar y pegar. Clic en Swlng
barraMenus [J-lenuBar] ® . •' t* i t»
0-^ Ardiwo [JMenu] Containers y a continuación Tool Bar.
• Nuevo [JJ4enuItem]
-[—"i Salir [3MenuItem] 12. Clic en el formulario. El contenedor no se ajusta
automáticamente ni en tamaño ni posición de forma
1
- Copiar [JMenuItem]
- p-"| Pegar [JMenuItem] que
^ debemos moverlo
i• • a 1 la zona superior y
; a íj Fuente [jMenii] posteríoimente redimonsionario.
'F1 TipoLeIra [Jlteiultetn] ^ .
; - TamaLeíra [JI4enuItem] 13. Clic en Swing Controls y Button. Seguidamente clic
s ^1 en el contenedor ToolBar que acabamos de añadir.
Aparece el primer botón de nuestra barra.
14. Modificamos el botón para que se muestre un icono''.
Clic en él y localizamos la propiedad ¡con. Pulsamos
Figura 8.41. Navegador de objetos , botón oara seleccionar el icono asociado. Este será
del formulario con la barra de menús ,, , ^ ,
configurada. el boton guardar.

"Es preciso que dispongamos de una serie de icono para agregar a nuestra aplicación. Los que vamos a
usar son los tipicos de guardar, cortar, copiar y pegar.
Capítulo 8. Entorno gráfico 363

Borramos el texto de la propiedad Text para que solo sea visible la imagen sobre él.
15. Agregamos los tres botones restantes cambiando su aspecto como en el ptmto 14.
Los botones referencian las órdenes de cortar, copiar y pegar.
16. Cuando el usuario haga clic en el menú Nuevo debe mostrarse un panel^ en el que
incluiremos un Text Area. Clic en Swing Containers y Panel. A continuación clic
en el formulario. Redimensionamos el panel para que ocupe toda la zona
disponible de la ventana.
1 7. Insertamos en Text Area en el Panel. Igualmente redimensionamos este para que
ocupe todo el panel.
18. Desde el menú Fuente vamos a poder cambiar características de la letra.
Agregaremos tres cuadros de diálogo que se mostrarán al seleccionar una u otra
opción del menú.
19. Para insertar un nuevo cuadro de diálogo clic en Swing Windows y a
continuación en Dialog. El proceso de creación de im cuadro de diálogo ya se ha
explicado de forma que en función de la información que ya tenemos del proceso
vamos a desarrollar los cuadros tal que se visualicen del siguiente modo:

Tamauo Estilo
Tipo de letra
|Arial rn Negrita
jrimes New Román
Courier New []]Cursiva
Comic Sans
Verdaña n Subrayada
Aceptar
Aceptar

Figura 8.43. Cuadro de diálogo


Figura 8.42. Cuadro de diálogo para modificar el tamaño del Figura 8.44. Cuadro de diálogo
para el tipo de letra. texto. para modificar el estilo.

20. Llegados a este punto damos por finalizada la interfaz gráfica. Comenzamos a
codificar cada una de las opciones. Básicamente usaremos métodos que permitan
hacer visibles cuadros de diálogo y paneles (setVisible) y modificaremos las
características de nuestro texto.

21. Para asociar el evento click a cada elemento del menú sólo debemos hacer doble
clic sobre él. En caso de necesitar otro evento usaremos el cuadro de propiedades
tras activar Event.
~ ...... — - — - —... 1

//Constructor del formulario principal


initComponents(); I
PanelNuevo.setVisible(false); |
//La variable texto hará las veces de portapapeles I
texto=""; i
A continuación se irá indicando el código fuente para cada objeto, es decir cada finición de
evento asociada.

^ Utilizaremos paneles para ver su funcionalidad aunque podría usarse Frame o Infernal Erame y así
generaríamos una interfaz MDI.
//Código menú Archivo Nuevo
PanelNuevo.setVisible(true);

//Código menú Archivo -> Salir


System.exit(0);

//Código del menú Editar Cortar y botón cortar de la barra de herramientas


texto=jTextAreal.getSelectedText(); /*getSelectedText() devuelve el texto
seleccionado del cuadro de texto*/
jTextAreal.replaceSelection("");

//Código del menú Editar -> Copiar y botón copiar de la barra de herramientas
texto=jTextAreal.getSelectedText();

//Código del menú Editar -> Pegar y botón pegar de la barra de herramientas
jTextAreal.replaceSelection(texto);

//Código del menú Editar -> Seleccionar todo


jTextAreal.selectAll();

//Código del menú Fuente Tipo de letra


DialogoLetra.setSize(200, 200);
DialogoLetra.setVisible(true);
//Código del menú Fuente Tamaño de letra
DialogoTama.setSize(100, 150);
DialogoTama.setVisible(true);
//Codigo del menú Fuente Estilo
DialogoEstilo.setSize(150,200);
DialogoEstilo.setVisible(true);
//Botón Aceptar del cuadro de diálogo Tipo de Letra

/*Ya que solo cambiaremos la letra y usaremos el objeto Font debemos mantener
el tamaño y el estilo*/
int tama=jTextAreal.getFont().getSizeO;
int estilo=jTextAreal.getFont().getStyleO;
String letra="";
//La nueva letra será la seleccionada de entre los elementos de la lista
String letra=jListl.getSelectedValue ().toStringO;
jTextAreal.setFont(new Font(letra,estilo,tama));
DialogoLetra.dispose();
//Botón Aceptar del cuadro de diálogo Tamaño de letra
int tama=Integer.parseint(jComboBoxl.getSelectedItem().toString());
int estilo=jTextAreal.getFont().getStyle();
String letra=jTextAreal.getFont().getFontName();
jTextAreal.setFont(new Font(letra,estilo,tama));
DialogoTama.dispose();

ACTIVIDAD 8.11

Finaliza el editor de texto escribiendo el código para el botón Aceptar del cuadro de diálogo
Estilos.

ACTIVIDAD 8.12

Nuestro editor puede optimizarse. Configurarlo de forma que los menús estén habilitados o no
en función del momento de ejecución en el que nos encontremos. Por ejemplo, si aún no se ha
i Capitulo 8. Entorno gráfico 365

pulsado el botón Nuevo y no tenemos un cuadro de texto en pantalla no podemos hacer clic en
ninguna de las opeiones de los menús Editar y Fuente.

ACTIVIDAD 8.13

Mejora la aplicación de fomia que añadas un nuevo submenú a Archivo. Este se denominará
Guardar. Usa un array de objetos que almacene los textos creados. Para ello, cada vez que se
pulse Archivo -> Nuevo se debe comprobar si existe un documento escrito y si se desea guardar
este. Agregar además un submenú Cerrar al menú Archivo tal que al hacer clic sobre él se cierre
el panel acti\ o y se pregunte al usuario si se desea almacenar el texto.

Sabías que...
Los entornos de desarrollo Visual Studio y Netbeans proporcionan atajos al
programador para facilitar su trabajo a la hora de picar código. Si hacemos clic con el
botón derecho del ratón sobre cualquier zona de la vista de código en Netbeans veremos
el menú Inserí Cede... Este permite generar código deforma automática:
II Si hacemos clic en Constructor el IDE generará
Generate

Constructor ^ó^igo necesario para el constructor de la clase, así


' Logger... como las opciones Getter... y Setter... generará los

Setter selectores y modificadores de la propiedad o


Getter and Setter... propiedades que escojamos. Aunque observamos la I
equalsO and hashCodeQ-.- opción Add Property... esta no es una Propiedad como
toStringO- • ■
veíamos en C#, se refiere a la creación de un atributo y
Override Method...
Override Method... métodos get y set de este.
Add Property...
U En C# la opción para insertar código
Figura 8.45.
8.45. Opciones
Opciones de
de predeterminado a partir del menú contextual es Insertar
generación de código
automático.
automático. fragmento de código...

COMPRUEBA TU APRENDIZAJE
1. ¿En qué consiste la programación dependiente de eventos?
2. ¿Qué entiendes por formulario? ¿Y control?
3. Define evento.

4. ¿Qué tipo de controles usamos normalmente en aplicaciones gráficas C# y


Java? ¿Qué pasos seguiremos a groso modo a la hora de crear una aplicación
gráfica?
5. ¿Para qué usamos las propiedades Visible (visible en Java) y Ñame (ñame en
Java)? ¿Qué diferencia existe entre la propiedad Ñame y ñame en ambos
lenguajes?
6. ¿Cuándo codificaremos el evento Click en C#? ¿Y el evento Leave?
7. ¿Qué tipo de objetos contenedores encontramos en C#? ¿Y en Java?
8. Básicamente, ¿cómo debemos proceder a la hora de generar aplicaciones con
varios formularios? ¿Cómo controlamos el acceso a cada uno de ellos? ¿Cómo
se procede en Visual Studio y en Netbeans?
9. ¿Qué es una ventana modal? ¿Cómo configuramos ventanas modales en C# y
Java?

10. ¿Qué se entiende por Interfaz de Múltiples Documentos (MDI)? ¿Cómo se


genera en C#?

ACTIVIDADES DE AMPLIACION
1. Busca información en la web o bibliografía aportada por el profesor en la que
se explique cómo desarrollar aplicaciones MDI en Java. Crea una pequeña
aplicación en la que al menos dispongas de un formulario padre y un
formulario hijo.
2. Crea una pequeña aplicación gráfica en Java tal que veamos la pantalla
dividida en dos zonas. En la superior veremos opciones relacionadas con
características de ordenadores, por ejemplo, cuadro de lista para escoger el tipo
de microprocesador, botones de opción para poder escoger el tipo de memoria
RAM del PC, capacidad de disco, etc. En esta veremos un botón de añadir de
forma que al hacer clic sobre él crearemos un objeto llamado PC con las
características establecidas (usar la clase Equipo creada en la Actividad de
Ampliación 11 del Capítulo 7). En la otra zona encontraremos los botones de
Añadir, Eliminar y modificar (usaremos la clase LlstaEquipos de la Actividad
de Ampliación 12 del Capítulo 7).
3. Desarrolla el Editor de ejemplo visto en el capítulo usando C#. Mejora su
funcionalidad agregando más funciones al editor.
4. Crea una aplicación en C# tal que incluyamos en ella tres clases llamadas
Triángulo, Rectángulo y Circulo. La aplicación estará compuesta por tres
botones con los textos epecificados relacionados con cada una de las figuras
geométricas. Cada vez que se pulse uno de los botones debemos mostrar en
pantalla el objeto en una posición aleatoria.
5. Usar la clase persona creada en la Actividad de Ampliación 8 del Capítulo 7,
ampliar esta de forma que almacene sexo, dirección, código postal, provincia y
población. Desarrollar un entorno gráfico en C# tal que permita dar valores e
instanciar tantas personas como se desee (usaremos un array de personas para
almacenar estas). La interfaz debe estar formada por un menú con las opciones
nueva, guardar y mostrar. Al hacer clic sobre nueva se verá una nueva
ventana tal que en ella se incluyan los controles necesarios para recoger
Capítulo 8. Entorno gráfico 367

infonnación de una persona. La opción guardar insertará la nueva persona en


el array comprobando antes que la información escrita sea correcta y se
encuentre dentro de los límites del dato que contiene. Si algún dato no es
correcto se hará saber al usuario y no se añadirá ninguna persona. Para finalizar
mostrar visualizará uno debajo de otro los datos relacionados con todas las
personas almacenadas. Desarrollar la interfaz gráfica en C# con todos aquellos
componentes que necesitéis.
6. Desarrolla la Actividad 5 en Java.

7. Crea una pequeña aplicación en C# de forma que incluya un cuadro de texto y


un botón. Al hacer clic sobre él será modificado el título de la ventana por el
texto escrito en el cuadro de texto.

8. Crea una pequeña aplicación en la que se incluirá una etiqueta. Desarrolla el


evento correspondiente con el texto adecuado en Java para que al mover el
ratón también lo haga el label.
9. Realiza una calculadora similar a la estándar de Windows en C#.
CAPITULO 9

HERENCIA,POLIMORFISMO E INTEREACES

CONTENIDOS OBJETIVOS

Espacios de nombres y Saber qué es un espacio de


paquetes. nombres o paquete y su utilidad.
Herencia y polimorfismo. Conocer el concepto de herencia
Clases abstractas y métodos y los elementos principales que lo
rodean.
virtuales.
Entender qué se entiene por
Interfaces.
polimorfismo, qué son funciones
virtuales y clases abstractas.
Saber qué es una interfaz y su
utilidad.

RESUMEN DEL CAPITULO

En el Capítulo 6 iniciábamos el estudio de la programación orientada a objetos, en


este profundizamos en ella mediante conceptos como herencia y polimorfismo. Se
pondrán en práctica estos conceptos para obtener aplicaciones más complejas y
versátiles, implementándose además interfaces.
370 Programación

9.1. INTRODUCCIÓN
Hasta ahora hemos introducido la programación orientada a objetos y sus conceptos
esenciales, de forma que sabemos qué es una clase, las partes que la forman, y hemos estudiado
peculiaridades además de usado diferentes tipos de clases e instancias de estas.
En este capítulo seguimos estudiando conceptos fundamentales de la programación orientada
a objetos como son la herencia y el polimorfismo. Además, comenzaremos definiendo los
conceptos de paquete y espacio de nombre que se han ido usando a lo largo del libro pero no se
han presentado formalmente.

9.2. ESPACIOS DE NOMBRES O PAQUETES


Los espacios de nombre (C#) o paquetes (Java) se usan para organizar los nuevos tipos de
datos creados por el usuario. De este modo el acceso es más sencillo e incluso podernos tener
más de una clase con el mismo nombre siempre que se encuentre en diferentes espacios o
paquetes.

9.2.1. DECLARACIÓN DE ESPACIOS DE NOMBRES


Para crear un nuevo espacio de nombres en C# usamos la palabra reservada namespace
seguida del nombre del espacio.

namespace espaciof
Tipojiatol

Tipo_daton;

Tipo_datol...Tipo_daton representa el conjunto de clases incluidas en el espacio.


namespace tienda{
class Libro{

class Pelicula{

class Socios{

class Proveedores{

¡ NOTA: En un espacio de nombres podemos incluir clases, enumeraciones, interfaces


I y estructuras. Además, podemos definir un espacio de nombre en otro ya existente.

A la hora de acceder a un elemento de un espacio de nombres concreto usaremos el operador


punto. Así, si tenemos el espacio de nombres tienda y queremos acceder al tipo Película
escribiremos tienda.Pelicula.
Capítulo 9. Herencia, polimorfismo e interfaces

Debido a que podemos estructurar los espacios de nombres tal que podemos declarar unos
dentro de otros, en ocasiones, el acceso a un tipo es una tarea algo tediosa, ya que debemos
escribir todos los espacios hasta llegar al tipo concreto:
espacio 1.espacio l_l.espaciol_12.tipo.método.
Si tux iéramos que escribir esta línea repetida veces modificando el método a usar en cada
una, configuraríamos un código fuente dificil de leer y complejo. Para evitar esto, C# utiliza la
palabra reservada using seguida del nombre de espacio: using namespace; de forma que
podemos usar todos los tipos incluidos en él sin necesidad de estar escribiendo este
continuamente.

Agregaremos a nuestro código tantas sentencias using como espacios de nombre queramos
usar. Estas se colocarán nonnalmente al comienzo del fichero para evitar posibles errores.
A la hora de usar using debemos prestar especial atención a la casuística de que en dos
espacios incluidos en un programa se defina un tipo con el mismo nombre, por ejemplo,
imaginemos que tenemos los espacios de nombre espacio_A y espacio_B y en ambos tengamos
declarada una clase llamada Dato. ¿Qué ocurriría si declaráramos una variable de tipo Dato en
nuestro código?
using espacio_A; \
using espacio_B; I
t
I

namespace espacio C{ '


class Tipo{ ¡
I
I

Dato var; I

En este caso, tal y como se ha planteado el código, el compilador no sabría si la variable tipo
Dato referencia a la clase Dato de espacio_A o espacio_B. Así, es necesario escribir el nombre
completo de acceso al tipo: espacio_A.Dato var o espacio_B.Dato var.
C# nos pemiite la creación de alias para estos casos, tal que podemos dar im pseudo-nombre a
una de las clases Dato en uno de los espacios de nombres y así pueda ser perfectamente
identificada.

i using alias=nombre_completo_deljipo;
using espacio_A;
using espacio_B;
using Dato_A=espacio_A.Dato;

namespace espacio C{
class Tipo{

Dato A var;
9.2.2. PAQUETES

En Java los paquetes al igual que espacios de nombre se encargan de agaipar clases. Si
observamos el directorio donde se almacena nuestro proyecto en Netbeans veremos en la carpeta
src una subcarpeta por cada paquete creado.
Indicamos que una clase pertenece a un paquete a través de la palabra reservada package al
comienzo de la clase.

package nombre_del_paquete; i
En Java un paquete puede contener subpaquetes estableciéndose una estructura jerárquica a la
que se accede nuevamente a través del operador punto. Así, si necesitamos incluir una clase de
un paquete concreto indicaremos el nombre de este seguido de la clase separadas ambas partes
por el símbolo punto.

nombre_paquete.clase i
Al igual que ocurría con los espacios de nombres, cuando usamos paquetes podemos declarar
tipos con el mismo nombre pertenecientes a diferentes paquetes, solo debemos tener precaución a
la hora de usarlos.

Para facilitar el acceso a los tipos de datos declarados en un paquete usamos la sentencia
import justo después del nombre del paquete, import permite incluir en nuestro proyecto una
clase concreta o todas las clases pertenecientes a un paquete en función de la forma en que
usemos la palabra reservada.
import nombre_paquete.tipo_dato;(1)
import nombre paquete.*;(2)
■ En la primera sentencia (1), agregamos solo el tipo de datos con el nombre tipo_dato.
■ En la segunda sentencia (2), agregamos todos los tipos de datos declarados en el
paquete con el nombre nombrejpaquete.
; package ejemplo; ;
; iirport System.io.*; //Importamos todas las clases de io incluido en System. ;
import java.net.Socket; //Importamos solo la clase Socket incluida en net. j
En Netbeans agregamos paquetes con facilidad siguiendo los pasos que se indican a
continuación:

1. En el explorador de proyectos clic con el botón derecho del ratón sobre el proyecto al
que queramos agregar el nuevo paquete.
2. En el menú contextual, clic en New y a continuación Java Package.
3. Escribimos el nombre del paquete y clic en Finish.
El nuevo paquete se muestra de color gris ya que aún no contiene ningún tipo.

9.3. HERENCIA Y POLIMORFISMO


Los términos de herencia y polimorfismo son de gran importancia en programación orientada
a objetos.
■ Herencia. La herencia entre clases es similar a la herencia entre padres e hijos en la
vida real. Un hijo puede heredar de un padre su color de ojos, algunos gestos, su
Capítulo 9. Herencia, polimorfismo e interfaces 373

fonna de andar, etc., una clase, se dice que hereda de otra cuando adquiere de forma
implícita características (color de ojos) y métodos (gestos, forma de andar) de esta. La
herencia pemiite jerarquizar nuestro grupo de clases y aprovechar propiedades y
métodos sin tener que volver a crearlas o implementarlas.
■ Polimorfismo. El paso del mismo mensaje a varias clases da como resultado
diferentes respuestas. Básicamente así podemos definir polimorfismo, imaginemos
varias clases que disponen de un método con el mismo nombre, cada método realiza
una función diferente aunque se llamen igual. Si ejecutamos el método de una clase
este dará una respuesta diferente que si la ejecución se produce desde otra clase. El
polimorfismo cobra gran importancia si se une a la herencia, en los siguientes
apartados entenderemos por qué.

9.3.1. HERENCIA

Cuando estudiamos la herencia debemos distinguir entre dos tipos de clases: clases base y
clases derivadas.

■ Clase base. Clase de la que se hereda. En la jerarquía es la que se encuentra más


arriba siendo de la que se aprovecha su funcionalidad y características. Suelen ser
denominadas también clase padre o superclase.
■ Clase derivada. La clase derivada es la que hereda. Es la clase que aprovecha la
funcionalidad. A su vez pueden ser clases base.
Existen diferentes tipos de herencia:
■ Herencia simple. En la herencia simple cada clase deriva de una única clase padre.
■ Herencia múltiple. Tipo de herencia soportada por lenguajes como C-H- en la que
una clase puede derivar de más de ima clase padre. C# y Java no soportan este tipo de
herencia, para simularla usan las denominadas interfaces.

Figura 9.1. Herencia simple. Figura 9.2. Herencia múltiple.

Supongamos que nos piden el desarrollo de im software para una empresa en la que trabajan
diferentes tipos de empleados, podríamos establecer el esquema que se muestra en la Figura 9.3.
La empresa tiene dados de alta a ima serie de empleados, todos ellos tienen en común que poseen
un nombre, un DNI, apellidos y un sueldo base, así, se ha creado ima clase llamada Empleados
que englobe todas esas características comunes a todos los empleados de la empresa.
A continuación, la empresa está formada por empleados con titulación que trabajan en
diferentes departamentos (contabilidad, infonnática, administración, etc.), y empleados sin
titulación que trabajan a pie de calle en plena obra. Por este motivo hemos creado una clase
llamada Cualificados y otra Obreros. Además, cada departamento dispone de un Jefe de
departamento que también debe ser teniendo en cuenta como clase independiente ya que posee
propiedades y funciones añadidas, así tendremos la clase JefeDepartamento.
A la hora de afrontar un problema de este tipo siempre debemos buscar similitudes,
propiedades y métodos similares de forma que vayamos agrupando estos y formando ciases base
o derivadas.

Empleados
- Nombre
-Apellidos
-Sueldo base
<-Dm j

Cualificados Obreros
-Titulación -DestinoTrabajo
- Plus - Horas extra
• Departamento - Precio hora extra

JefePepartamento
-TotaITra baja doresACa rgo
- Proyectos

)
Figura 9.3. Esquema de clases del software de una empresa.
La sintaxis básica de definición de herencia es la siguiente:

c# Java ;
class Base{ ciass Base{ ;

} } i
class Derivada:Base{ class Derivada exíends Base{ ;

\l... } j
Usamos el símbolo dos puntos (:) para Usamos la palabra reservada extends para
establecer la herencia con una clase base. establecer la relación de herencia.
Debemos tener en cuenta que:
■ Una clase derivada solo puede accede a los miembros publíc y protected de la clase
base, los miembros prívate nunca son accesibles, al menos de forma directa, ya que
sí podremos acceder a ellos a través de métodos públicos o protegidos que los usen.
■ La clase derivada incluirá los miembros definidos en la clase base.
Según esto, nuestra definición de clases para las clases definidas en la Figura 9.3 será la
siguiente:

c# ; Java
class Empleados{ 1 class Empleados{
string nombre, apellidos; ¡ String nombre, apellidos;
; doiíble sueldoBase;
double sueldoBase;
String DNI;
string DNI;

i }
Capítulo 9. Herencia, polimorfismo e ¡nterfaces

class Cualificados extends Empleados{


class Cua1 ificados:Empleados i String titulación;
string titulación; double plus;
dotible plus; String departamento;
string departamento;

class Obreros extends Empleados{


class Obre ros:Empleados{ String destinoTrabajo;
string destinoTrabajo; int horasExtra;
int horasExtra; double precioHoraExtra;
doulDle precioHoraExtra;

class JefeDepartamento
class Je fe Departamento:Cualificados{ extends Cualificados {
int totalTrabajadoresACargo; int totalTrabajadoresACargo;
string []proyectos; String []proyectos;
double plus; double plus;

ACTIVIDAD 9.1

Realiza un nuevo proyecto en el que incluyas las clases: FiguraGeometrica, Rectángulo,


Cuadrado, Circulo y Rombo, tal que las cuatro últimas clases deriven de FiguraGeométrica. En
FiguraGeometrica incluiremos los atributos X e Y relacionados con la posición del objeto. Las
demás clases deben incluir las propiedades necesarias en función del tipo de figura, por ejemplo,
la clase Circulo incluirá atributos del tipo coordenada x e y del centro y radio. Agregar los
métodos área y perímetro.

9.3.1.1. CONSTRUCTORES EN CLASES DERIVADAS

Cuando una clase hereda de otra esta adquiere los atributos de la clase base y estos deben ser
inicializados. Si los atributos de la clase base son privados no son accesibles desde la clase que
hereda pero sí de algún modo podemos hacer un llamamiento a estos a través de sus métodos
constructores.

Así, a la hora de crear el constructor de una clase derivada realizamos una llamada al
constructor de la clase base, esto no es necesario cuando la clase padre no cuenta con
constructores o usa el constructor por defecto. Resumiendo:
■ Cuando declaremos una instancia de la clase derivada se llamará de forma automática
al constructor de la clase base.

■ Si la clase base tiene definido un constructor es necesario que la clase derivada haga
referencia a este en su constructor.

■ En caso de que la clase base no defina constructores no será necesario hacer


referencia a esta en el constructor de la clase derivada.

■ A la hora de crear el constructor de la clase derivada incluiremos tanto parámetros


como sean necesarios para inicializar tanto la propia clase como la clase base.
Supongamos que en nuestro ejemplo, la clase Empleados tiene definido el siguiente
1

constructor :

class Empleados!
string nombre, apellidos;
double sueldoBase;
string DNI;

piiblic Empleados(string nom, string apell, double sueldo, string DNI) {


this.nombre=nom;
this.apellidos=apell;
this.sueldoBase=sueldo;
this.DNI=DNI;

A la hora de defmir la clase Cualificados y Obreros debemos realizar un llamamiento a este


ya que los atributos de Empleados deben ser inicializados. En C# modificaremos la cabera del
construetor mientras que en Java haremos referencia a la clase base en el interior de este.

class Base{
tipo atributol;
tipo atributol;

piiblic Base(tipo parí, tipo par2){


atributol=parl;
atributo2=par2;
}

class Derivada:Base{
tipo atributoS;

public Derivadaftipo parí, tipo parí, tipo par3):base(parl,par2){


atributo3=par3;
}

class Cualificados:Empleados{
string titulación;
double plus;
string departamento;

public Cualificados(string nom, string apel, double sueldo,string DNI,


string titulo, double plus, string dep):base(nom,apel,sueldo, DNI)(
this.titulacion=titulo;
this.plus=plus;

'Se define el código en C#


Capítulo 9. Herencia, polimorfismo e interfaces 377

this.departamento=dep;

class Base{
tipo atributo1;
tipo atribiitoZ;

piiblic Base(tipo parí, tipo par2){


atributo1 —parí;
atributoZ=par2;
}

class Derivada:Base{
tipo atributo3;

public Derivada(tipo parí, tipo par2, tipo par3){


super(parl,par2);
atributo3=par3;
}

class Cualificados extends Empleados{


String titulación;
double plus;
String departamento;

public Cualificados(String nom, String apel, double sueldo,String DNI


,String titulo, double plus, String dep){
super(nom,apel,sueldo,DNI)
this.titulacion=titulo;
this.plus=plus;
this.departamento=dep;

En los ejemplos observamos que usamos la palabra reservada base en C# para hacer
referencia al constructor de la superclase mientras que en Java usamos la palabra reservada
super. C# sigue en este aspecto la sintaxis usada por C-H-. En ambos lenguajes, al hacer la
llamada al constructor de la clase base se pasa como parámetros las variables anteriormente
definidas en la cabecera del método (parámetros del constmctor que deriva). Además, usamos
tantos parámetros como variables necesitemos inicializar contando clase base y clase derivada.
A la hora de establecer jerarquías en la que prima la herencia, es posible realizar asignaciones
entre clases base y derivada. Esto es muy útil combinado con la implementación del
polimorfismo. Así, cuando comencemos a instanciar las diferentes clases podemos asignar a una
clase base cualquiera de sus clases hijas, no al contrario.
Empleados empleado;
Cualificados maria=new Cualificados("maria"
,"Pérez",1000.00,"44.233.350","Técnico informática".
500, "informática");
empleado=maria;

9.3.1.2. REDEFINICIÓN DE MÉTODOS


Podemos incluir el mismo método tanto en clases heredadas como derivadas de forma que
estaremos redefiniendo el método. Cuando se redefíne un miembro en la clase derivada este se
oculta en la clase base, es decir, cuando llamamos al método estaremos usando siempre el
método redefínido en la clase derivada. La redefínición consiste en la creación de métodos con el
mismo nombre y mismo número y tipo de argumentos.
I class Empleados{ ;
I string nombre, apellidos; •
I double sueldoBase; :
j string DNI; ;
i public Empleados(string nom, string apell, doiible sueldo, string DNI) { j
; this.nombre=nom; ;
; this.apellidos=apell; ;
I this.sueldoBase=sueldo; ¡
; this.DNI=DNI; i

piiblic string Información (){


return "Nombre:"+nombre+" Apellidos:"+apellidos;

class Cualificados:Empleados{
string titulación;
double plus;
®tring departamento;

public Cualificados(string nom, string apel, double sueldo,string DNI,


string titulo, double plus, string dep):base(nom,apel,sueldo,DNI){
this.titulacion=titulo;
this.plus=plus;
this.departamento=dep;

public string Información(){


return "Titulación;"+titulacion+" Departamento:"+departamento;

En el ejemplo la clase Cuah'fícádos hereda de Empleados y tanto una como otra definen un
método llamado InformacionQ, más correctamente podemos decir que la clase Empleados
redefíne el método Información y oculta este método en la clase Empleados de forma que cuando
realicemos la llamada al método desde una instancia de Cualificados se ejecutará el código de
esta misma clase y no el definido en Empleados.
Capítulo 9. Herencia, polimorfismo e Interfaces

Si queremos acceder desde la clase derivada a alguno de los métodos ocultos debemos
hacerlo mediante el uso de la palabra reservada base en C# o super en Java. Así, si quisiéramos
usar los datos que devuelve el método InformacionQ en la clase Empleados:

class Empleados{

piiblic string Información () {


return "Nombre:"+ nombre+" Apellidos:"+apellidos;

class Cua1 ificados:Empleados {

public string Información () {


string info=base.Información ();
info=info+"Titulación:"+titulacion+" Departamento:"+departamento;
return info;

class Empleados!

public String Informacion () {


return "Nombre:"+nombre+" Apellidos:"+apellidos;

class Cualificados extends Empleados!

public String Información!)!


String info=super.Información !);
i nfo=info+"Titulación:"+titulacion+" Departamento:"+departamento;
return info;

Debemos tener en cuenta que:


Empleados juan=new Empleados("Juan","Maclas",1000.00,"48.123.454");
Cualificados miguel=new Cualificados("Migue",Jiménez",1100.00,
"29.323.222","Administrativo",300.00, "Administración");
Cualificados maria=new Cualificados("Maria","Hernández",1100.00,
"44.234.501",Informática",400,"Informática");
juan .Informacion O ; //Accede al método Información!) de Empleados.
miguel.Informacion O ; /*Oculta el método Información!) de Empleados
y ejecuta sólo el de Cualificados*/

Empleados aux=maria;
aux.Informacion O ; /*En C# Ejecuta el método información de la clase base, ya
que aunque al objeto aux se le tiaya asignado maria de tipo
Cualificados el objeto es de tipo Empleados. Si
codificáramos el problema en Java ejecutarla el método de
la clase Cualificados.*/
NOTA: Aunque es opcional en C# se usa la palabra new delante de los miembros
redefínidos:

new public string InformacionO {...}

9.3.1.3. OPERADORES 18, AS E INSTANCEOF

Como ya hemos estudiado, podemos crear clases que deriven unas de otras y realizar
asignaciones entre ellas, es decir, podemos crear una instancia de la clase base y asignar a esta
una instancia de una clase derivada. Esto es interesante cuando necesitamos mantener gran
cantidad de objetos de forma que podemos crear un array de la clase base y asignar a cada casilla
un objeto de la clase derivada que deseemos.
Empleados []listaEmpleados=new Empleados[100]; •
listaEmpleados[0]=new Cualificados (...) ; j
listaEmpleados[1]=new Obreros (...) ; ;
listaEmpleados[2]=new Obreros (...) ; ;

listaEmpleados[99]=new Cualificados (...); I


A la hora de usar este tipo de datos compuesto, en más de una ocasión necesitaremos saber a
qué clase pertenece un elemento concreto ubicado en una de sus posiciones para así realizar una
u otra operación con él, es en este punto donde entran enjuego los operadores is, as e instanceof
(este último ya se estudiado en el Capítulo 7). Los operadores is y as son operadores de C#
mientras que instanceof es un operador de Java, en ambos lenguajes podremos hacer
conversiones de tipo usando casting.
■ Los operadores is e instanceof se usan para comprobar si un objeto pertene a una
clase concreta.

■ El operador as convierte un objeto al tipo de dato (clase) indicado. Si la conversión


no es posible devuelve nuil. Su funcionalidad es bastante similar a la realización de
casting sobre el objeto, la diferencia se encuentra en que si no se puede realizar el
casting se produce un error de ejecución (excepción).
' — — — — I

/*Comprobamos si listaEmpleados[O] es un objeto de la clase Cualificados, en j


caso afirmativo se muestra por pantalla el texto "¿A qué departamento j
perteneces?*/ |
if (listaEmpleados[O] is Cualificados)] ;
Consolé.WriteLine("¿A qué departamento perteneces?"); ;

/*Igual al ejemplo anterior*/ ;


xf (listaEmpleados[O] instanceof Cualificados)[ ;
System.out.println("¿A qué departamento perteneces?"); :

/*Creamos un objeto auxiliar del tipo que queremos comprobar y realizamos una
conversión mediante as: convertimos listaEmpleados[O] al tipo de datos
Cualificados si es posible y asignamos el resultado a aux. Si se produce fallo
ya que listaEmpleados[O] puede no ser un objeto de Cualificados, aux será
igual a nuil, en otro caso aux recogerá el objeto Cualificados concreto*/
Cualificados aux=listaEmpleados[O] as Cualificados;
if(aux!=null){
Consolé.WriteLine("¿A qué departamento perteneces?")y }
Capítulo 9. Herencia, polimorfismo e interfaces

ACTIVIDAD 9.2

Crea un pequeño programa que use la jerarquía de clases definida en la Actividad 9.1 de
fonna que manten un array de FigurasGeométricas al que vayas asignando las diferentes figuras
representadas por las clases Rectángulo, Círculo, Rombo y Triángulo. Incluye una función que se
encargue de contar el número de figuras geométricas de cada tipo incluidas en el array, la salida
de este método podría ser:
2 Círculos; 1 Triángulo; 4 Rombos; 10 Rectángulos

9.3.2. POLIMORFISMO

Definíamos polimorfismo de forma que indicábamos que podíamos mantener diferentes


clases tal que a todas podríamos mandar el mismo mensaje y obtendríamos respuestas diferentes
a estos. Esto es posible debido a que llamamos de igual modo a métodos ubicados en las
diferentes clases de forma que cada uno de ellos está codificado de modo distinto. Cuando
realizamos la llamada o el paso de mensaje especificamos el mismo nombre de método pero cada
uno realiza una función que nada tiene que ver con la del resto.

NOTA: Es importante entender que el compilador decidirá qué función usará en


tiempo de ejecución no durante la compilación.

A la hora de redefmir métodos en el Apartado 9.3.1.2 estudiábamos que:

■ Al definir un método de igual modo en una clase derivada y una clase base, el método
de la clase base se oculta, de modo que cuando un objeto de la clase derivada usaba el
método siempre se hacía referencia a su método redefinido y no al de la clase base.

■ Si creamos una variable de la clase base y a esta le asignamos un objeto de la clase


derivada, en C#, el método redefinido que siempre se ejecuta es el de la clase base, ya
que la definición del objeto refiere a esta.

Nonmalmente, la redefinición de métodos se realiza junto a la herencia de forma que podamos


mantener en nuestros programas estructuras complejas basadas en la clase base de forma que a
cada elemento podamos asignar las diferentes clases hijas y usar en tiempo de ejecución el
método redefinido de cada clase derivada según corresponda. Hasta ahora, según hemos visto, en
C# esto no es posible, ya que al defmir un objeto de la clase base, aunque a este le asignemos xma
clase hija, los métodos que se ejecutan corresponden a la clase base. Véase la Figura 9.4.

Para conseguir que en tiempo de ejecución en C# se ejecuten las fimciones propias del objeto
asignado debemos usar las llamadas funciones virtuales.
NOTA: En C# es obligatoria la definición de funciones virtuales mientras que en Java
toda función que se sobrescribe o se redefine es tomada como virtual. Normalmente,justo
antes de la definición de una función redefinida en Java en una clase derivada
encontraremos el texto @override. Este es indicativo y se usa para comunicar al
compilador que el método que sigue sobrescribe un método de la clase base de forma que
si a la hora de definirlo se ha producido algún error léxico el compilador lance un fallo de
compilación. Si no se especifica @override no se lanzarán fallos de compilación en caso
de errores en la definición y será tomado como otro método de la clase.

Array Empleados Método IniormacionO

Obreros
Cualificados
Cualificados

Obreros

'■ — - ' 1 .. — Método InformacionO de Empicados

Figura 9.4. Representación gráfica de un array de Empleados en C# al que asignamos a cada casilla un
objeto de la clase derivada. Hasta el momento, tal y como hemos definido los métodos a la hora de
ejecutar la función lnformacion() en cada casilla se ejecutará la definición dada en Empleados y no en cada
objeto derivado.
Para definir en C# una función de la clase base como virtual será necesario incluir la palabra
virtual en su cabecera, mientras que en las funciones de las clases derivadas redefinidas
antepondremos la palabra reservada override. Aunque es obvio decir que es necesario que los
métodos definidos como virtuales deben ser a su vez públicos.

class Base{

public virtual tipo nombre método (parámetros) {

class Derivada:Base{

public override tipo nombrejnétodo(parámetros){


Capítulo 9. Herencia, polimorfismo e interfaces 383

class Empleados{

virtual public string Información()


{
return "Nombre:" + this.nombre + " Apellidos:" + apellidos;

class Cual ificados:Empleados{

override public string Información()


(
return "Titulación:" + this.titulación +
" Departamento:" + this.departamento;

NOTA: El orden de las palabras virtual y override puede alterarse con respecto a la
palabra public. Así, es igual escribir virtual public / override public que public virtual
/ public override.

class Base{

public tipo nombrejnétodo(parámetros){

class Derivada extends Base{


■•'•V í/''".?-.
: i m'"
Vi iriA--
L
class Empleados{

public String Información() {


return "Nombre : "+this . nombre+" Apellidos : "+this . apellidos;
}
}
class Cualificados extends Empleados {

@Override
public String Información() {
return "Titulación: "+this . titulaciont
" Departamento:"+this.departamento;

Al usar la palabra virtual en C# cuando definimos una estructura de datos de tipo Empleados
(clase base) a la que asignamos objetos de tipo Cualificados u Obreros (clases derivadas) la
ejecución del método InformacionQ referirá al método correspondiente a la clase asignada en
cada momento y será en tiempo de ejecución cuando el compilador decida qué método debe
utilizar. Recordamos que en Java el proceso se realiza de forma automática ya que la redefinición
lleva implícito el concepto de virtualización.
ACTIVIDAD 9.3

Agrega a las clases definidas en la Actividad 9.1 un método llamado arca() tal que calcule el
área de cada figura geométrica. Modifica el programa diseñado en la Actividad 9.2 de fomia que
además muestre por pantalla el área de cada una de las figuras geométricas almacenadas en el
array.

9.4. CLASES ABSTRACTAS


En muchas ocasiones la herencia se usa para mantener tipos de datos compuestos en los que
cada elemento del tipo es a su vez de un tipo de dato diferente. Imaginemos que tenemos una
aplicación en la que hemos definido las clases Empleados, Cualificados y Obreros de fonna
independiente tal que no hereden unas de otras. Si quisiéramos mantener un tipo de datos
compuesto como es un array en el que pudiéramos almacenar en cada casilla Empleados,
Cualificados y Obreros, no podríamos, ya que a la hora de definir un array este sólo puede ser de
un tipo de datos concreto:

Empleados []listaEmpleados;
Cualificados []listaEmpleados,•
Obreros []listaEmpleados;

Sin embargo, si creamos una estructura de clases jerárquica en la que tenemos una clase base
y otras clases derivadas que heredan de esta, podemos crear un array de la clase base y
posteriormente asignar a cada casilla un objeto de la clase derivada, de forma que indirectamente
estamos construyendo un array de diferentes tipos de datos: Empleados, Cualificados y Obreros:
i Empleados []listaEmpleados=new Empleados[10]; j
I listaEmpleados[O]=new Cualificados (...) ; '
listaEmpleados[1]=new Obreros (...); i
En ocasiones, la clase Base apenas contiene información, y se usa básieamente para poder
crear las estructuras que acabamos de explicar, de forma que no es necesario instanciar esta, ya
que se diseña principalmente para agrupar a un conjunto de clases.
Estas clases base, que no necesitan ser instanciadas se declaran como clases abstractas.
Cuando definimos una clase abstracta:
" No podremos crear instancias de la clase.
■ Una clase abstracta puede contener descriptores de acceso (en C#, get y set) y
métodos abstractos.
■ Las clases derivadas de clases abstractas deben incluir la implementación de todos los
métodos abstractos heredados y descriptores(en C#).
Para definir una clase como abstracta usamos la palabra reservada abstract tanto en Java
como C#.

C# : Java !
i

abtract class Empleados{

} i
Capitulo 9. Herencia, polimorfismo e interfaces 385

Como hemos comentado, además de clases abstractas podemos definir métodos abstractos.
Un método abstracto sólo puede incluirse en clases que a su vez son abstractas y es
implícitamente un método virtual.
Al igual que las clases abstractas no penniten la instanciación de objetos, un método abstracto
no permite la inclusión de código en él, es decir, son métodos que no contienen cuerpo ya que
están destinados para ser implementados en las clases derivadas. Así, a la hora de declarar un
método abstracto en una clase abstracta será definido del siguiente modo:

abstract class Base{ i

abstract piiblic tipo nombrejnétodo(parámeti-os); I

La implementación del método se realizará en las clases derivadas de Base:

class Derivada:Base{

overridepublic tipo nombre_método(parámetros){

class Derivada extends Base{

@override
public tipo nombrejnétodo(parámetros){

En nuestro ejemplo, si cambiamos la clase base usando el modificador abstract el código se


mostraría como sigue.
//Código C#
abstract class Empleados{

abstract ptablic string Información();


}
class Cualificados:Empleados{

override public string Información()


{
return "Titulación:" + this.titulación +
" Departamento:" + this.departamento;

//Código Java
abstract class Empleados{

abstract piablic string Información ();


class Cualificados extends Empleados{

@override
public string Información()
{
return "Titulación:" + this.titulación +
" Departamento:" + this.departamento;

ACTIVIDAD 9.4

Modifica las clases creadas en la Actividad 9.1 de fonna que la clase base sea abstracta.
Además, el método area() añadido en la Actividad 9.3 también debe ser abstracto.

9.5. INTERFACES
Una interfaz es una definición de un conjunto de métodos para los que no se realiza
implementación, es similar a una clase abstracta compuesta exclusivamente de métodos
abstractos.

Una clase puede implementar una o varias interfaces. La implementación de una interfaz
consiste en el desarrollo de los métodos definidos en la interfaz.
En muchas ocasiones se usan interfaces para de algún modo representar herencia múltiple en
nuestros programas ya que lenguajes como C# y Java no la soportan. La sintaxis para la creación
de interfaces es similar en C# y Java:
modificador ínterface nombre{

tipo nombre(parámetros);
tipo nombre_2(parámetros);

■ modificador. Cuaíquierá de íós modificadores usados en las definiciones de clases:


public, prívate, protected, etc.
■ nombre. Nombre asignado a la interfaz, puede ser cualquier identificador válido.
■ tipo nombre (parámetros). Cada método de la interfaz que será implementado en
otras clases.

Además es posible la herencia entre interfaz de forma que podemos disponer de interfaces
base e interfaces derivadas. La definición de herencia para interfaces es similar a la vista para
clase con la diferencia de la palabra reservada interface.
C# .... J
modificador interface¡Derivada:IBase{

}
Java

modificador interface¡Derivada extends¡Base{


Capítulo 9. Herencia, polimorfismo e interfaces

A la hora de implcmentar una interfaz en Java usaremos la palabra reservada ímplements


mientras que en C# usaremos la misma sintaxis que cuando definimos la herencia. Una clase
podrá implementar más de una interfaz o puede derivar de al menos una clase de forma que
podremos nombrar a cada una de ellas separando estas por comas.

class nombreClase:nombreInterface, nombreInterface_2,...,nombreIníerfazN{

public tipo nombve(pavámetros){

class nombreClase ímplements nombreinterface, nombreInterface_2,...,nombreInteTfazN{

public tipo nombre(parámetros){

Supongamos que nuestra empresa emplea a personas que aún están estudiando, becarios, tal
que nuestro esquema de clases se mostraría como en la Figura 9.5.

Estudiantes Empleados
ExamenesO - Nombre
Universidad!) -Apellidos
NotaMediaPracticasO -Sueldo base
-DNl

Becarios Cualificados Obreros


Carrera Titulación -DestinoTrabajo
Curso Plus - Horas extra
Departamento Departamento - Precio hora extra

JefeDepartarnento
-TotalTrabajadoresACargo
- Proyectos
- Plus

Figura 9.5. Nuevo esquema de clases para nuestro programa ejemplo.

Dado este nuevo esquema de clases la implementación podría ser la siguiente:


/*Código Java, sólo se representan las clases Estudiantes, Becarios y
Empleados, las demás clases serán definidas como hasta ahora*/
interface lEstudiantes{
public void Examenes O;
public void Universidad O;
public double NotaMediaPracticas(double []notas);
}
class Empleados{
String nombre;
String apellidos;
double sueldo;
String DNI;

piiblic Empleados(String nom, String apell, double sueldo, String DNI) {


this.nombre=nom;
this.apellidos=apell;
this.sueldo=sueldo;
this.DNI=DNI;
}
}
class Becarios extends Empleados implements lEstudiantes{
String carrera;
int curso;
String departamento;

pviblic Becarios(String nom, String apell, double sueldo, String DNI


,String carrera,int curso, String dep) {
super(nom,apell,sueldo, DNI);
this.carrera=carrera;
this.curso=curso;
this.departamento=dep;

0Override
public void Examenes O{
System.out.println("Los exámenes debido a las prácticas
comenzarán el 15 de septiembre");
}

0Override
public void UniversidadO {
if (this.departamento.compareTo("Informática")==0) {
System.out.println("Universidad de la Rabida");
else if(this.departamento.compareTo("Recursos Humanos")==0) {
System.out.println("Universidad de la Merced");

else{
System.out.println("Universidad del Carmen");

0Override
double NotaMediaPracticas(double []notas) {
double resultado=0.0;
/*Se definen tres notas para el alumno: adquisición de conceptos
teóricos, prácticos y proyectos*/
for (int i=0;i<3;i++){
resultado+=notas[i];
}
return resultado;
Capítulo 9. Herencia, poUmorfismo e Interfaces 389

A la hora de definir una interfaz podemos usar como nombre cualquier identificador válido.
Se aconseja para diferencias clases e interfaces, comenzar estas últimas con la letra I seguida del
nombre de interfaz que deseemos.
En C# el código será bastante similar a diferencia de la sintaxis del lenguaje:
interface lEstudiantes í
l i
public void Examenes O; ¡
public void Universidad O ; 1
public double NotaMediaPracticas(double[] notas); I
) i
class Empleados I
{ i
string nombre; ;
string apellidos; 1
double sueldo; ¡
string DNI; '
pxiblic Empleados(string nom, string apell, double sueldo, string DNI) 1

this.nombre = nom;
this.apellidos = apell;
this.sueldo = sueldo;
this.DNI=DNI;
)
}
class Becarios : Empleados, lEstudiantes
{
string carrera;
int curso;
string departamento;

public Becarios(string nom, string apell, double sueldo, string DNI


,string carrera,int curso, string dep)
:base(nom,apell,sueldo, DNI) {

this.carrera=carrera;
this.curso=curso;
this.departamento=dep;
}

public void Examenes O {


Consolé.WriteLine("Los exámenes debido a las
prácticas comenzarán el 15 de septiembre");
}

ptablic void Universidad(){


if (this.departamento.Compárelo("Informática")==0){
Consolé.WriteLine ("Universidad de la Rabida");
}
else if(this.departamento.Compárelo("Recursos Humanos")==0){
Consolé.WriteLine("Universidad de la Merced");
)
else {
Consolé.WriteLine ("Universidad del Carmen");
}
public douisle NotaMediaPracticas(double []notas){
double resultado=0.0;
/*Se definen tres notas para el alumno: adquisición de conceptos
teóricos, prácticos y proyectos*/
for (int i=0;i<3;i++){
resultado+=notas[i];
}
return resultado;

COMPRUEBA TU APRENDIZAJE
1. ¿Qué es un espacio de nombres?
2. ¿Qué elementos podemos incluir en un espacio de nombres? ¿Cómo usamos un espacio
de nombres? ¿Y un paquete?
3. ¿Qué entiendes por herencia? ¿Qué diferencia existe entre una clase base y una clase
derivada? ¿Qué tipos de herencia hemos contemplado en el capítulo?
4. ¿Cómo se deben tratar los constructores cuando una clase deriva de otra que tiene
implementado un constructor con parámetros? Pon ejemplos en Java y C#.
5. ¿Qué es la redefmición de métodos?
6. ¿Para qué se usan los operadores is, as e instanceof?
7. Define polimorfismo. ¿Para qué se crean métodos virtuales en C#?
8. ¿Qué es una clase abstracta? ¿Cuáles son sus características?
9. ¿Qué entiendes por interfaz? ¿Cuál es su utilidad?
10. ¿Qué utilidad encontramos en el uso de clases abstractas si además se crean métodos
virtuales?

ACTIVIDADES DE AMPLIACION
1. Desarrolla el siguiente diagrama de clases tanto en Java como C#. Configura cada clase
en función de las características reales de cada objeto. Incluye todos los componentes en
un espacio de nombre y paquete denominado seres.

Figura 9.6. Diagrama de clases Actividad de ampliación 1.


Capítulo 9. Herencia, polimorfismo e interfaces

2. Crea una aplicación que utilice el diagrama de clases de la Actividad de ampliación 1.


Configura un array de tipo Servivo. Añade una variable que cuente las instancias que se
van realizando de cada clase derivada. El programa podrá agregar un objeto derivado de
ScrVivo, modificar, mostrar o eliminar contenido del array.
3. Configura una interfaz gráfica que permita gestionar las operaciones planteadas en la
Actividad 2.

4. Configura un método denominado seAlimenta() en cada clase tal que devuelva una
cadena de caracteres donde se indique el tipo básico de alimentación de cada ser vivo.
5. Crea una estructura de clases similar a la que se muestra en la Figura 9.7. Configúrense
propiedades y métodos, como desee el lector.

Figura 9.7. Diagrama de clases Actividad de ampliación 5.

6. Desarrolla una aplicación gráfica que use las clases creadas en la Actividad 5. La
aplicación debe ser MDI, disponer de un menú y opciones que visualizarán formularios
para:

■ Añadir productos.
■ Modificar productos existentes.
■ Abrir una cuenta corriente.

■ Abrir una tarjeta y asociar esta a una cuenta.

7. Imagina que tienes que iinplementar una aplicación para un videoclub. En él tendremos
películas y música. Los dos tipos de elementos que se pueden alquilar tienen en común
una serie de características, como número identificativo, precio, etc. Desarrolla un
diagrama de clases que se ajuste a las necesidades del negocio que se quiere informatizar.
Siempre atender a la realidad, a fin de cuentas se pretende captar esta.
8. Crea una interfaz gráfica para usar las clases creadas en la Actividad 7. El objetivo es
desarrollar una aplicación que pennita gestionar el videoclub teniendo en cuenta todas las
actividades que en él se realizan.
9. Dada la siguiente implementación el programa da error, ¿por qué? Justifica tu respuesta.
abstrae class Vehículos{

public void darAlta(){

Vehiculos nuevo=new Vehículos();

10. Crea una clase llamada alumno y haz que esta implemente el método coinpareTo() de la
interfaz Comparable en Java tal que puedan compararse dos alumnos. A la hora de
comprobar si dos alumnos son iguales se tienen en cuenta su nombre y apellidos tal que
devolvemos -1 si alfabéticamente el nombre y apellidos está antes del nombre y apellidos
que realiza la comparación, O si son iguales y 1 si es mayor.
11. Crea una interfaz denominada sonidos. En ella tendremos un método llamado
sonidoProducidoQ- La función sonidoProducido devuelve una cadena de caracteres con el
sonido que realiza un objeto concreto. Crea una estructura de clases ta! que se disponga
de una superclase llamada IntrumentosMusicales de la que derivan InstrumentosViento e
InstrumentoPercusion. Haz que las clases implementen la interfaz sonidos.
12. Desarrolla un esquema de clases tal que dispongas de una superclase llamada Animal de
la que deriven perro y gato. Haz que las clases implemente la interfaz realizada en la
Actividad 11.
CAPITULO 10

FLUJOS DE ENTRADA/SALIDA Y CONTROL DE


EXCEPCIONES

CONTENIDOS OBJETIVOS

Ficheros y directorios. Conocer los tipos de ficheros que


Tipos de ficheros y
existen y las operaciones que
podemos realizar sobre ellos.
operaciones.
Saber qué clases gestionan, tanto
Flujos de datos base e
en Java como en C# los flujos de
intermedios.
datos y hacer uso de ellas.
Clases para la gestión de flujos
Entender el término excepción,
de datos en C# y Java.
las clases de excepciones en Java y
Excepciones. C# así como la gestión que estos
lenguajes realizan de ellas.

RESUMEN DEL CAPITULO

Llegados a este punto hemos estudiado la gran mayoría de elementos que fonnan
programas estructurados u orientados a objetos. Parece oportuno que en este Capítulo se
estudien los flujos de E/S que permitan al menos almacenar la información usada en
nuestras aplicaciones, además de la gestión de errores en tiempo de ejecución.
10.1. INTRODUCCION
Hasta ahora hemos estado usando estructuras de datos, variables, ciases y hemos estado
manipulando información que una vez finalizada la ejecución del software desaparece de
memoria, ya que esta es almacenada en memoria principal durante el tiempo que se está
ejecutando el programa.
Llegados a este punto se hace necesario el estudio de nuevas estructuras o métodos que nos
permitan conseguir que la información no se volatilice, siendo almacenada en dispositivos de
almacenamiento secundario tales como discos duros extemos o internos, memorias USB, etc.
Así, en este capítulo vamos a estudiar las clases que se usan tanto en C# como en Java para la
gestión de ficheros, el control de los errores que estos lenguajes mantienen durante el proceso de
lectura/escritura u otros procesos, así como otros tipos de flujos de entrada/salida de información.

10.2. FICHEROS Y DIRECTORIOS


Recordamos que un fichero o archivo no es más que una secuencia de bits organizada de un
modo determinado y almacenada en un dispositivo de almacenamiento secundario tipo disco
duro, CD/DVD, memorias USB, etc. Será el programa que genera el fichero quien sea capaz de
traducirlo y dar sentido a la combinación de bits, es decir, a la hora de diseñar un programa y
almacenar información en un fichero será el programador quien dicte las normas sobre cómo se
introduce la información en él tal que solo ese programa será capaz de descifrarlo.
Los directorios se encargarán de agrupar los diferentes archivos siguiendo determinados
criterios, estos criterios inducidos a veces por el sistema operativo o programa que los usa, y
otras veces por la persona que los crea.
A la hora de trabajar con archivos, debemos tener en cuenta que:
■ La información no es más que un conjunto de bits, O y 1.
■ Los bits se agrupan formando los denominados bytes o palabras.
■ A su vez los tipos de datos usados, int, double, etc., estarán fonnados por un
conjunto de bytes,formando lo que se denomina campo.
■ Los campos se agrupan formando registros de información.
■ Un archivo estará a su vez constituido por un conjunto de registros, de fonna que
todos ellos poseen la misma estructura.
Véase la Figura 10.1.
Las características principales de un fichero son:
■ Se ubican en soportes o dispositivos de almacenamiento secundario de forma que la
información permanece una vez que la aplicación que los usa se cierra.
" Podemos usar la información en diferentes equipos ya que esta se puede duplicar o
mover a otra unidad, memoria USB,ordenador a través de red, etc.
■ Posibilita la independencia de la información respecto a los programas ya que no
necesitamos estar ejecutando la aplicación para que la infonnación exista.
Capitulo 10. Flujos de Entrada/Salida y control de excepciones

necesitaremos ejecutar el programa para poder modificar o realizar alguna operación


sobre ella.

Permiten almacenar gran cantidad de infonnación.

iTciOioioioni 1101011101 loToioiTüroioioioioioimoioionoi 110101010010101011111010


1110110101011010loioioioioioioioionoii loioioiooioioioiiifioioiiioiioioioiioioi
OIOIOIOIOIOIOIOIOIIÍU 1101010100101010111110101110110101011(510101010101010101010
1101110101010010loiola 1110101110110101011010101010101010101(11011011101010100101
0101111101011101 lOlOlQl lOlOlOlOlOlOlOlOlOlOlOllOlllOlOlOlOOlíílOlOlllllOlOlllOll
0101011010101010101010M1010110111010101001010101111101011101\01010110101010101
010101010101101 lioioioimioioioill 1101011101101010110101010101^1010101010110111
010101001010101111101011M110101011010101010101010101010110111(?1010100101010111
1101011101 loioioiioioioioipioioioioioioiioiiioioio \

Registros

10010101 01111101011101 1010101101010101

01010101 OlOlOllOimOlO ^ 10100101010111

I Cinta Cumbreras

Campos [

Figura 10.1. Estructura de un fichero organizado en registros que guardan información reiativa a número
de orden, nombre y primer apellido.

10.2.1. CLASIFICACIÓN DE LOS FICHEROS SEGÚN SU


ORGANIZACIÓN
Podemos clasificar los ficheros siguiendo diferentes criterios, uno de ellos es en función de la
organización de la infonnación. Las principales organizaciones son:
■ Secuencial.

■ Aleatoria o directa.

■ Secuencial indexada.

10.2.1.1. FICHEROS SECUENCIALES

En los ficheros secuenciales los registros ocupan posiciones consecutivas y solo es posible
acceder a ellos comenzando desde el principio y recorriendo estos de uno en uno.
Además, solo es posible realizar una operación de lectura o escritura a la vez, es decir, cuando
el fichero está siendo leído no es posible realizar una operación de escritura así como cuando el
fichero está siendo escrito no puede llevarse a cabo ninguna operación de lectura.
Las actualizaciones de este tipo de ficheros se realizan mediante la creación de nuevos en los
que se incluyen los registros existentes más las actualizaciones. Véase la Figura 10.2.
Para leer el Registro N antes debe haber recorrido y leído
los N-1 registros anteriores

Registro 1 Registro 2 Registro 3 Registro 4 Registro N

Posición de lectura

Registro 1 Registro2 Registros i Registro4

Posición de escritura

Figura 10.2. Acceso de lectura y escritura en ficheros secuenciales.

10.2.1.2. FICHEROS ALEATORIOS

En este tipo de ficheros el acceso es directo, es decir, se accede a un registro concreto


indicando su posición o lugar relativo ocupado dentro del conjunto de posibles posiciones.
Gracias a esta organización los registros pueden ser leídos o escritos en cualquier orden, ya que
cada uno se identifica por su posición y a la hora de realizar una operación sobre el solo se tiene
que colocar el manejador de fichero justo antes de donde se encuentre este (en caso de que sea
una operación de lectura) o justo en la posición donde queramos se escriba el nuevo conjunto de
datos.

Este tipo de ficheros se caracterizan por la rapidez con la que se accede a los datos.

Para leer el Registro 3 solo tenemos que colocar el


puntero de lectura/escritura justo antes de esta posición.

Registro 1 ; Registro2 : Registros Registro4 Registro N

Posición de
lectura/escritura

Figura 10.3. Acceso de lectura/escritura en ficheros aleatorio.

10.2.1.3. FICHEROS SECUENCIALES INDEXADOS

En este tipo de ficheros cada registro es identificado de manera única a partir de un campo
llamado campo clave. Estos campos se utilizan para formar índices que relacionan campo clave
con posición del registro en el fichero.
Este tipo de organización permite el acceso secuencial y aleatorio al fichero, tal que:
■ En primer lugar se busca de forma secuencial el campo clave del registro que se
necesita.
Capitulólo. Flujos de Entrada/Salida y control de excepciones 397

■ Cuando se conoce el campo clave del registro, el acceso a este es directo, ya que solo
tenemos que acceder a la posición que dicho campo clave o índice indique.
Los índices se encentran ordenados para conseguir un acceso más rápido y en ocasiones
además del fichero de datos, el conjunto de índices se encuentra almacenado en tm fichero
igualmente.

J 1 Registro 1
i/ 2 Registro 2
Campos clave 3
\ 4 Registro 4

N Registro N

Figura 10.4. Estructura de un fichero organizado secuencial indexado.

Normalmente, hoy día, cuando se crean aplicaciones que gestionan grandes cantidades de
datos, estos son almacenados en bases de datos a las que accede el programa, pocas veces
encontraremos esta información almacenada en ficheros.

Si usamos ficheros en nuestro programas, estos serán de tipo texto o binario y el acceso
preferido es el secuencial, ya que aunque es algo más lento no complica demasiado la
programación.

10.3. OPERACIONES CON FICHEROS


Cuando usamos ficheros en nuestros programas son básicamente tres las operaciones que
realizaremos sobre ellos:

■ Apertura. Debemos abrir el fichero tal que en el proceso se indique qué tipo de
acceso vamos a realizar sobre él. Al abrir un fichero relacionamos un objeto de
nuestro programa con un archivo almacenado en disco a través de su nombre e
indicamos el modo en que vamos a operar sobre él.
■ Operaciones de lectura y escritura. Leeremos la infonnación del fichero prestando
especial atención en la posición que se encuentra el manejador de archivos. Este
indica el punto desde el que empezar a leer y si nos encontramos en el final del
fichero esta operación no sería posible.
■ Cierre. El fichero queda liberado y finaliza el proceso de almacenamiento de la
información, es decir, se tennina de escribir en el dispositivo físico la información
guardada aún en el buffer\

Zona de disco destinada al almacenamiento temporal de información.


10.3.1. APERTURA DE FICHEROS

A la hora de abrir un fichero es necesario indicar para qué vamos a usarlo, es decir, si solo
vamos a realizar operaciones de lectura sobre un archivo su modo de apertura será diferente a si
vamos a realizar operaciones de lectura y escritura.
Los modos de apertura son los siguientes:
■ Lectura. Vamos a realizar solo operaciones de lectura sobre el fichero.
■ Escritura. Realizaremos operaciones de escritura. En caso de que el fichero que se
quiera escribir ya exista este será borrado.
■ Añadir. Parecido al de Escritura ya que realizaremos esta operación sobre el fichero
con la diferencia de que si este existe con anterioridad no será eliminado.
■ Lectura/Escritura. Realizaremos operaciones de lectura/escritura en el fichero.

10.3.2. OPERACIONES DE LECTURA/ESCRITURA EN FICHEROS


A la hora de leer o escribir en un fichero, ya sea este secuencial o aleatorio, siempre seguimos
la misma secuencia de operaciones. Veamos en este apartado esta secuencia de pasos.

Lectura secuencial
//Variable de tipo Fichero
Fichero/;

//Apertura delfichero
/Abrir/lectura);

Mientras nofinal defichero hacer


fLeer(registro);
operaciones con registro leído;
finMientras

//Cierre delfichero
fCerrarO;
Lectura aleatoria
//Vaiable de tipo Fichero
Fichero/;

//Apertura delfichero
f.A brir(lectura);

Mientras condición según programa hacer //No siempre usamos una sentencia repetitiva
Situar el puntero delficherojusto antes del registro requerido
fLeer(registro);
operaciones con registro leído;
finMientras

f:Q?rrfí(0_i //_ Cierre delfichero


Capitulólo. Flujos de Entrada/Salida y control de excepciones 399

En el pscudocódigo suponemos que realizamos la lectura de toda la información almacenada


en el fiehero.

Escritura secuencial

//Variable de tipo Fichero


Fichero/;

//Apertura de!fichero
/Abrir(lectura);

Mientras existan datos a escribir


Configurar registros a partir de los datos
/.Escribir(Registro);
finMientras

//Cierre delfichero
fCerrarQ;
Escritura aleatoria
//Vaiable de tipo Fichero
Fichero/;

//Apertura delfichero
/A brir(lectura); ■

Mientras se deseen escribir datos hacer -vffl!.'


Situar el puntero delfichero en la situación requerida
/Escribir(registro);
operaciones con registro leído;
finMientras

f:Q?rrPJ)()i/L delfichero

10.4. FLUJOS DE ENTRADA/SALIDA DE DATOS


Cuando realizamos operaciones de entrada/salida tanto en C# como Java usamos los
denominados flujos de datos. El flujo representa una secuencia de bytes entre ima fuente y un
destino de infonnación. Así, a la hora de escribir o leer datos en o de un fichero haremos uso de
un flujo, de fomia que cuando queramos escribir ubicaremos el dato en el flujo y este lo
redirigirá hacia el dispositivo correspondiente y a la inversa, si deseamos leer infonnación, el
dispositivo ubicará la información en el flujo para que llegue al objeto del programa que debe
procesarla.
El flujo actúa como interfaz entre el dispositivo y clase asociada tal que:
■ Proporciona una mayor flexibilidad.
■ No solo nos permite realizar operaciones con ficheros ya que mediante flujos
podemos enviar información a través de la red, a la pantalla, al teclado, etc.
■ Pemiiten los accesos aleatorios y secuenciales.
■ La información que se intercambia entre el dispositivo y la clase relacionada puede
ser de tipo texto, binaria, líneas, etc.
Básicamente, a la hora de enviar o recibir información a o desde el dispositivo elegido,
usaremos flujos de bytes. Estos flujos serán representados por clases tanto en C# como Java.

FUENTE... FLUJO ...DESTINO

Dispositivo o clase Dispositivo o ciase

Figura 10.5. Representación gráfica del uso de flujos en el acceso a la información almacenada en
diferentes dispositivos.

Existen diferentes tipos de flujos:


■ Flujos base. Trabajan directamente con el dispositivo físico, disco, memoria,
conexión de red, etc.
■ Flujos intermedios^. Son flujos que no trabajan con los dispositivos de fonna directa
sino que envuelven a otros flujos que sí lo hacen. Suelen proporcionar un mayor
número de funciones para operar con la información. Así, el flujo intermedio
envuelve a algún flujo base de forma que los datos se envían al flujo intennedio, este
a su vez al flujo base y este último al dispositivo físico.

Flujo intermedio^
iyfelba^''
Cadena de
...DESTINO
caracteres

Figura 10.6. Representación gráfica del uso de flujos intermedios y base.

El acceso a un flujo de datos se puede realizar de forma secuencial o aleatoria como veíamos
al estudiar los ficheros (al hablar de flujos generalizamos a movimiento de bytes entre otros
dispositivos). Este dispondrá de un mecanismo por el que se pueda extraer o añadir información.
Por defecto, la primera operación a realizar sobre el flujo de byte se realiza al comienzo, es decir,
cuando abrimos el flujo el puntero o manejador de flujo se coloca al principio de fonna que si
realizamos una operación de lectura o escritura, esta se realizará sobre los primeros datos.
A partir de la primera operación las siguientes operaciones se llevan a cabo a partir de la
última posición accedida, en caso de que el acceso sea secuencial. Si el acceso es aleatorio, como
ya hemos estudiado, podemos modificar la posición a partir de la que operar con los datos.
Véase las siguientes figuras.

^ Podríamos decir que el flujo intermedio procesa la información mientras que el base realiza
básicamente la función de envío de bytes de un lugar a otro.
Capitulo 10. Flujos de Entrada/Salida y control de excepciones

Tras la primera operación el puntero


pasa a colocarse aquí. La siguiente
Puntero o
operación se realiza desde este punto.
manejador de flujo
antes de realizar la
primera operación
l^Ll-

Figura 10.7. Acceso secuencial a un flujo, concretamente acceso para lectura.

Queremos leer la posición 4.


Colocamos el puntero justo antes de
Puntero o
esta y realizamos la operación.
manejador de flujo
antes de realizar la
primera operación

Figura 10.8. Acceso aleatorio a un flujo para la lectura de bytes.

10.4.1. CLASES QUE REFIEREN FLUJOS EN C#


En C# todos las clases que trabajan con flujos de datos se encuentran en el espacio de nombre
System.IO. Entre ellas, la clase más importante o de las más importantes, es Stream. Esta es ima
clase abstracta que proporciona los mecanismos necesarios para leer o escribir bytes. Además,
podremos disponer de los tipos BinaryReader/BinaryWriter y TextReader/TextWriter que
penmitirán almacenar la infonnación en formato binario o texto respectivamente.

Stream BinaryWriter

TextReader

StringWriter

StreamWriter

Figura 10.9. Información extraida de las clases del Framework .NET.


10.4.1.1. CLASE STREAM

Stream es la clase base para todas las secuencias o movimiento de bytes, ya se produzcan en
un fichero, en una tubería de comunicación entre procesos o un proceso de comunicación entre
diferentes ordenadores a través de TCP/IP.

La clase Stream posee multitud de clases que derivan de ella, en la Figura 10.9 solo hemos
incluido algunas de ellas.
A continuación vamos a estudiar los métodos que usa la clase para la lectura o escritura de
bytes. Es importante entender que la clase solo escribe o lee bytes, es decir, no sabe cómo
procesar los tipos primitivos del lenguaje tales como int, double, char, etc.
■ int Read(byte[] buffer, int Inicio, int totalBytes) . Operación de lectura. Leerá
tantos bytes como indique el parámetro totalBytes (siempre que sea posible, es decir,
no se haya alcanzado el final del flujo) comenzando en la posición indicada con el
parámetro inicio. Los bytes leídos serán almacenados en parámetro buffer que no es
más que un array de bytes. Devuelve el total de bytes leídos, cero en caso de que
hayamos llegado al final del fichero.
■ int ReadByteQ. Lee un byte de la secuencia y lo devuelve como número entero sin
signo. En caso de llegar al final del fichero el valor devuelto será -1.
■ void Write (byte[] buffer, int inicio, int totalBytes). Escribe el total de bytes
especificado en el parámetro totalBytes. La información es obtenida desde el buffer
y empezamos a escribir desde la posición establecida por el parámetro inicio.
■ void WriteByte (byte valué). Escribe el valor pasado como parámetro (el byte) en el
flujo.
■ long Seek (long inicio, SeekOrigin origen). Método necesario en accesos aleatorios.
Posiciona el puntero en la posición correcta que debe ser leída o escrita. El parámetro
origen determina un lugar de referencia, es decir, el principio del fichero
(SeekOrigin.Begin), el final del fichero (SeekOrigin.End) o la posición actual
(SeekOrigin.Current), mientras que inicio indica el número de posiciones que
saltaremos desde la posición de referencia.
■ CanRead. Propiedad que devuelve verdadero o falso en función de si el flujo puede
ser leído o no.

■ CanWrite. Propiedad que devuelve verdadero o false en función de si el flujo puede


ser escrito o no.

■ CanSeek. Propiedad que determina si el flujo admite acceso aleatorio o no.


Las clases que derivan de Stream tales como BufferedStream o FileStream (véase Figura
10.9) heredan estos métodos con lo que a través de ellas abriremos flujos de datos y utilizaremos
las operaciones Read o Write para la lectura o escritura de información. La clase FileStream es
la que usaremos para la lectura/escritura de ficheros.

10.4.1.2. CLASE FILESTREAM

Clase derivada de Stream que permite operaciones sobre flujos que almacenan o leen los
datos desde ficheros.
Capitulólo. Flujos de Entrada/Salicia y control de excepciones 403

El proceso de apertura del fíehero o flujo que se va a manipular se realiza en el momento en


que se erea una instaneia del objeto. Así, a la hora de crear una variable de tipo FileStream, en el
constructor debemos:

■ Indicar el nombre del fíehero a abrir.

■ Establecer el modo en el que el sistema operativo debe abrir el fichero, es decir, si


existe en modo añadir infonnación (Append), si existe en modo machacar el fichero
(Create), si no existe crear este (OpenOrCreate), etc. El modo se especifica a partir
de la enumeración FileMode.

■ Establecer el acceso al fichero. Este parámetro es opcional. Se configura a partir de la


enumeración FileAccess y sus opciones son Read, Write y ReadWrite.

ACTI^D^AD 10.1
Accede al Framework .NET y observa los diferentes modos y accesos de FileStream.

I NOTA: Las operaciones de lectura o escritura en un flujo de bytes conllevan a que se


produzcan errores en tiempo de ejecución o excepciones. Es posible que el fichero no
! exista, no pueda ser leído, etc. Todos estos problemas se detectan durante el tiempo en
¡ que el usuario está haciendo uso de nuestra aplicación. Las excepciones de entrada/salida
se ubiean en lOException. Veremos esto en el apartado dedicado a excepciones en este
mismo capítulo.

Debemos tener en euenta que la clase FileStream sigue leyendo im flujo de bytes, es decir, no
reconoce los tipos de datos primitivos del lenguaje (será necesario realizar operaciones de
traducción a bytes en cada lectura o escritura para que la información pueda ser interpretada o
escrita).
El proceso a seguir en la lectura o escritura de un archivo a partir de FileStream será el
siguiente:
■ Abrir el fichero. Para ello tenemos que crear una variable de tipo FileStream y en el
constructor indicar el nombre del fichero y el modo de apertura de este, tal que:
—>■ Abrir para lectura -> FileMode.Open.
Abrir para escribir sin sobrescribir el fichero -> FileMode.Append.
-> Abrir para crear el fichero en caso de que no exista y si existe lo sobrescriba
-> FileMode.Create.

Si queremos abrir el fichero para poder leer y escribir sobre él además del modo indicaremos
el acceso FileAccess.ReadWrite.

■ Leer o escribir el o en el fichero. Usando los métodos vistos en el Apartado


10.4.1.1.

Cerrar el fichero. Uso del método CloseQ.


ACTIVIDAD 10.2

Busca en el Framework .NET la clase File. Redacta un pequeño documento donde incluyas
los métodos más característicos. Busca un ejemplo donde se use de forma conjunta FilcStream y
File.

//Proceso de escritura secuencial


using System.IO;

public void EscribirFicheroByte(string nombreFichero)


{
FileStream fichero = new FileStream (nombreFichero
,FileMode.Create, FileAccess.Write);
string cadena="Hola mundo";
char [] caracteres=cadena.ToCharArray();
foreach(char c in caracteres)
{
byte []buffer = BitConverter.GetBytes(c);
fichero.Write(buffer. O, buffer.Length);
}
fichero.Glose ();

NOTA: A la hora de poner en práctica los ejemplos mostrados o actividades debemos


tener en cuenta que el fichero a leer o escribir debe estar ubicado junto al fichero ejecutable
que genera VisualStudio, este se encuentra en la carpeta Debug, ubicada a su vez en bin
dentro de la carpeta del proyecto. Si el fichero no es localizado de produce una excepción
del tipo FileNotFoundException.

//Proceso de lectura secuencial


using System.IO;

public string LeerFichero(string nombreFichero)


{
FileStream fichero = new FileStream(nombreFichero
,FileMode.Open, FileAccess.Read);
String cadenaLeida="";
int tama = sizeof(char);
byte []buffer=new byte[tama];
/*Permite el uso de métodos que obtienen cadenas a partir de una secuencia
de bits*/
UTFBEncoding aux = new UTFSEncoding(true);

while (fichero.Read(buffer. O, tama) > 0)


{
cadenaLeida = cadenaLeida + aux.GetString(buffer);
}
fichero.Glose();
return cadenaLeida;
Capítulo 10. Flujos de Entrada/Salida y control de excepciones

NOTA: La lectura de un fichero secuencia! puede realizarse de dos formas:


■ Realizar una primera lectura, en la condición del while comprobar si el
número de bytes leídos es mayor que cero y continuar con otra lectura.
■ Realizar la operación de lectura en la misma condición del while.

Para finalizar mostraremos como se realiza el acceso aleatorio.

//Proceso de lectura aleatoria


System.10;

static public char LeerFicheroAleatorio(string nombreFichero)


{
FileStream fichero = new FileStream(nombreFichero
,FileMode.Open, FileAccess.Read);
char letra;
fichero.Seek(2, SeekOrigin.Begin);
letra=(char)fichero.ReadByte();
fichero.Glose O;
return letra;

ACTIVIDAD 10.3

Realiza un pequeño programa de consola en el que se pida al usuario el nombre de un fichero


y se muestre por pantalla el contenido de este (usa la clase estudiada FileStream).

ACTIVIDAD 10.4

Modifica la Actividad 10.3 de fonna que crees una aplicación gráfica con un único
formulario. En la ventana debes incluir un cuadro de texto para especificar el nombre del fichero,
un cuadro de texto multilinea donde visualizar el contenido del fichero y un botón que de la
orden de apertura y lectura del mismo.

ACTIVIDAD 10.5

Realiza un fomiulario en el que incluyas un cuadro de lista editable y un botón. Al pulsar el


botón los Ítems seleccionados de la lista deben ser escritos en el fichero ítems.dat.

10.4.1.3. USO DE FLUJOS INTERMEDIOS

En C# podemos realizar operaciones de entrada/salida de bajo y alto nivel. Las operaciones de


bajo nivel son aquellas en las que se trabaja con información sin formato (clases que se usan:
Stream, BinaryReader o BinaryWriter), mientras que en las de alto nivel, en lugar de hablar de
transmisión de bytes transmitimos número enteros (int), reales (double), caracteres (char), etc.
(clases usadas: TextReader o TextWriter).
A su vez, los objetos Stream o FileStream operan directamente con el flujo o secuencia. En
este apartado vamos a trabajar con clases que no operan de forma directa con el flujo, es decir, a
la hora de ser construidas reciben como parámetro una clase que representa la secuencia de bytes,
por ejemplo:
FileStream flujoDatos = new FileStream(nombreFichero ;
,FileMode.Open, FileAccess.Read); !
BinaryReader fichero = new BinaryReader(flujoDatos); ;

Las siguientes serían clases que implementan flujos intermedios de alto nivel para trabajar
con ficheros de texto:

■ StreamReader/StreamWriter. Proporcionan métodos que permiten leer o escribir


caracteres. La codificación predeterminada es la UTF-8 aunque se puede modificar.
■ StringReader/StringWriter. Proporcionan métodos que permiten leer o escribir
cadenas de caracteres.

Las siguientes serían clases que implementan flujos intennedios de alto nivel para trabajar
con ficheros binarios:

■ BinaryReader/BínaryWriter. Proporcionan métodos que permiten la lectura y


escritura de archivos binarios.

NOTA: A la hora de usar BinaryReader y leer un fichero binario el método de lectura


usado en función del tipo leído lanzará una excepción de tipo EndOfStreamException al
llegar al final del fichero. En los objetos StreamReader el final del fichero viene
determinado por la propiedad EndüfStream.

//Método para escribir en un fichero mediante BinaryWriter


public void EscribirBinario(string nombreFichero)
{
FileStream flujo = new FileStream(nombreFichero
,FileMode.Create, FileAccess.Write);
BinaryWriter fichero = new BinaryWriter(flujo);
string cadena = "Adiós mundo";
fichero.Write(cadena);
fichero.Glose();
flujo.Glose();
}

static public string LeerBinario(string nombreFichero)


{
FileStream flujo = new FileStream(nombreFichero
,FileMode.Open, FileAccess.Read);
BinaryReader fichero = new BinaryReader(flujo);
string cadena = fichero.ReadString();
fichero.Glose();
flujo.Glose();
return cadena;

ACTIVIDAD 10.6

Realiza una función que permita la lectura desde un fichero de texto y otra que pennita la
escritura usando las clases StreamReader y StreamWriter. Usa las msdn en caso de dudas.

ACTIVIDAD 10.7

Realiza un programa que cuente el niimero de palabras de un fichero de texto dado.


Capítulo 10. Flujos de Entrada/Salida y control de excepciones 407

ACTIVIDAD 10.8

Codifica un programa que lea un fichero de texto y contabilice el número de veces que
aparece cada una de las letras del alfabeto en él.

10.5. PROCESO DE SERIALIZACIÓN EN 0#


Hasta ahora hemos realizado operaciones de entrada/salida de datos en ficheros y estos datos
eran datos simples o primitivos. Hemos estudiado como realizar las operaciones oportunas ya sea
en modo byte o texto. ¿Qué ocurre si en lugar de una variable de un tipo de datos simples
deseamos almacenar tipos de datos compuesto o de mayor complejidad creados por nosotros
mismos?

Para almacenar objetos es necesario realizar lo que se denomina como serialización. La


serialización es el proceso por el que una instancia de una clase es covertida a un formato legible
capaz de ser transmitido mediante un flujo de datos. Básicamente el objeto es traducido a un
conjunto de bytes de forma que pueda ser almacenado en un fichero u otro elemento.
El proceso inverso se denominaría deserialización y convierte la información extraída de un
flujo al objeto con su formato original.
En C#, para poder escribir objetos en ficheros u otros dispositivos, se serializa la clase a la
que pertenece este. La serialización es bastante sencilla ya que solo debemos añadir al comienzo
de esta la marca [Serializable].
Supongamos que hemos codificado una clase llamada Persona mediante las siguientes líneas:
class Persona I
I

{ i
string nombre; ¡
string apellidos; ;
I
t
I

//El contructor está sobrecargado I


public Personal) j
{ i
this.nombre = ;
this.apellidos = 1

public Persona(string nombre, string apellidos)


this.nombre = nombre;
this.apellidos = apellidos;

//Propiedades Nombre y Apellidos


public string Nombre

return nombre;

nombre = valué;
public string Apellidos

return apellidos;

apellidos = valué;

Para indicar que la clase es serializable solo debemos escribir la marca [Serializabie]:
[Serializable]
class Persona
{
string nombre;
string apellidos;

A continuación, la operación de escritura del objeto se realiza en los siguientes pasos:


1. Creación de la instancia del Objeto
2. Declaración de una variable de tipo IFormatter
3. Apertura del flujo de datos
4. Uso del método Serialize de IFormatter

5. Cierre del flujo de datos.


i Persona person = new Persona("José","Cumbreras"); ;
! IFormatter formateador = new BinaryFormatter() ; !
i FileStream flujo = new FileStream("ListaPersonas.bin" ;
' ,FileMode.OpenOrCreate, FileAccess.Write); |
i formateador. Serialize(flujo, persona); ;
I flujo.Glose O ; ;
'

IFormatter proporciona la funcionalidad necesaria para que el proceso de señalización se


produzca.
La operación de lectura o deserialización se realizaría como sigue:
1. Creación de un objeto IFormatter.
2. Creación de la variable de flujo de datos.
3. Uso del método Deseríalize asignando el objeto que devuelve a una variable del tipo
previa la realización de un casting o conversión ya que el objeto se devuelve de fonna
general como tipo Object.
4. Cierre del flujo de datos.

Será necesario añadir using System.Runtime.Serialization.Formatters.Binary;


Capitulo 10. Flujos de Entrada/Salida y control de excepciones

IFormatter formateador = new BinaryFormatter();


FileStream flujo = new FileStream("ListaPersonas.bin"
,FileMode.Open, FileAccess.Read, FileShare.Read);
Persona persona = (Persona) formateador.Deserialize(flujo);
stream.Cióse();

ACTIVIDAD 10.9

Realiza una pequeña aplicación de consola tal que use la clase Persona (amplia su
funcionalidad y atributos). Al comenzar las personas almacenadas en el fichero que indiquemos
son leídas y guardadas en un array de tipo Personas. Si no hubiera almacenada aún ninguna
persona en el fichero el array no contendrá datos. Veremos un pequeño menú con las opciones
insertar, modificar, eliminar, buscar persona y mostrar (todas las operaciones asociadas a los
elementos del menú se realizan sobre el array). Al cerrar el programa el fichero inicial será
sobrescrito con la nueva lista de personas.

ACTIVIDAD 10.10

Desanolla una aplicación gráfica basada en la Actividad 10.9. Configura la aplicación con
tantos fomiularios como se desee y los controles que se creen oportunos para que se puedan
realizar las operaciones indicadas.

10.6. FLUJOS DE DATOS EN JAVA


Hasta ahora hemos estado usando en Java flujos de datos estándares. Cuando hemos realizado
operaciones de entrada/salida en dispositivos tales como teclado o pantalla (dispositivos de
entrada y salida entandar) se ha hecho uso de flujo o secuencia de bj^es.
Recordamos System.in o System.out seguidos de métodos del tipo readQ o println(). Sin bien
es cierto, sobre todo hemos usado métodos del flujo de salida ya que para la obtención de la
infonnación usábamos objetos que capturaban la secuencia de bytes y eran capaces de aportar
mayo funcionalidad (combinación de flujos base e intermedio):
— —

InputStreamReader flujo=new InputStreamReader(System.in); ;


BufferedReader teclado=new BufferedReader(flujo); ;
Al igual que ocurría en C#, en Java encontramos'clases que permiten la escritura/lectura de
fon-na binaria o texto, en las Figuras 10.10 y 10.11 observamos un esquema con todas ellas.

FileInputStream
DatalnputStream
|
FilterlnputStream
InputStream BufferedInputStream

0H1SSI

OutputStream
StnngBufferlnputStream j
ObjectOutputStream 1

Figura 10.10. Clases destinadas a trabajar con flujos binarios.


InputStreamReader FileReader
Reader
FilterReader

StringReader

BufferedWriter

OutputStreamWriter FileWriter

Writer FilterWriter

StringWriter

PrintWriter

Figura 10.11. Clases'* destinadas a trabajar con flujos de tipo texto.


En Java no es necesario establecer modo de acceso ya que la propia clase dctennina si se
realizará una lectura o escritura. Las clases que contienen la palabra Input se refieren a la lectura
de datos mientras que aquellas que contienen la palabra Output indican escritura.
Es posible determinar si el fichero es sobrescrito o no, en una operación o flujo de escritura,
en el constructor de la clase que usemos para la secuencia de bytes. Así, si utilizamos la clase
FileOutputStream para escribir información, en el constructor de la clase podemos indicar solo
el nombre del fichero o este seguido de true. En el segundo de los casos indicamos que la
apertura se realiza para añadir información.
//Apertura sobreescribiendo el fichero si existe ;
FileOutputStreain flujoSalida = new FileOutputStream(nombreFichero); I

//Apertura agregand información al fichero en caso de que este exista ;


FileOutputStream flujoSalida = new FileOutputStream(nombreFichero, true); 1
Veamos un ejemplo de escritura en un fichero usando flujos binarios:
public void EscribirFicheroBinario(String nombreFichero)
throws FileNotFoundException, lOExceptionl
FileOutputStream flujo=new FileOutputStream(nombreFichero,true);
String cadena="Hola mundo";
byte[]buffer=cadena.getBytes();
flujo.write(buffer. O, buffer.length);
flujo.cióse ();

NOTA: Todas las clases vistas se incluyen en el paquete java.io. Debemos usar j
sentencias import para hacer uso de ellas, ya sea de forma individual: import j
: java.io.FileOutputStream; o conjunta: importjava.io.*; |
I I
Es necesario, ya sea en la cabecera o a través de sentencias try...catch que ya veremos los
posibles errores de ejecución, de ahí que la declaración contenga la línea:

Existen otras clases que no se han incluido en la Figura. Acceder a la API de Java para obicner todo el
listado.
Capitulo 10. Flujos de Entrada/Salida y control de excepciones

throws FileNotFoundException, lOException

Desde ella se controla el error en tiempo de ejecución producido por no localizar el fichero
(FileNotFoundException) y los errores en operaciones de escritura/lectura de datos
(lOException).
A la hora de realizar una operación de lectura secuencial desde un fichero, el proceso es
similar al visto en C#, cambian las clases que usamos para ello.
public static void LeerFicheroBinario(String nombreFichero)
throws FileNotFoundException, lOException{
FileInputStream flujo=new FileInputStream(nombreFichero);
byte []buffer=new byte[10];
while(flujo.read(buffer)>0){
String cadena = new String(buffer) ,•
System.out.println(cadena);
)
flujo.cióse();

Los flujos en modo texto taíes como íñputStrea"mRéa"dér/OÜ^^^^ impíementan


los métodos que se indican a continuación.
InputStreamReader
■ closeO- Cierra el flujo.
■ getEncodingO. Devuelve la codificación usada.
■ readO- Lee el siguiente carácter.
■ read(char [] buffer, int inicio, int tamaño). Lee el conjunto de caracteres indicado
por tamaño desde la posición inicio y los almacena en buffer.
■ readyO- Detemiina si el flujo está listo para ser leído.
OutputStreamWriter
■ cIoseQ- Cierra el flujo.
■ flusbQ. Limpia el flujo. En ocasiones es necesario limpiar el flujo de datos ya que
permanece en él información escrita anteriormente que no queremos que se siga
usando.

■ getEncodingQ. Al igual que en la clase InputStreamReader devolverá la codificación


usada.

■ write(char [] buffer, int inicio, int tamaño). Envía al flujo de datos la información
almacenada en el array de caracteres buffer. Es posible indicar dónde comenzar a
enviar a través de inicio y la longitud de los datos, tamaño. Existen sobrecargas para
este método tal que podemos enviar una cadena de caracteres write(String cadena,
int inicio, int tamaño) o bien un solo carácter expresado como valor del código
usado, write(int carácter).
Las clases FileReader y FileWriter heredan de estas tal que utilizan estos métodos.
A la hora de usar estas clases debemos prestar atención a sus constructores, ya que en el caso
de InputStreamReader y OutputStreamWriter se precisa un flujo base de tipo InputStream o
derivado de este como FileInputStream. Si optamos por el uso de InputStream recordad que es
una clase abstracta y no puede ser instanciada. Véase el siguiente ejemplo. En las clases
FileReader y Filewriter será necesario usar una variable de tipo File.
public void copiar(String nombreFicheroOrigen,String nombreFicheroDestino)
throws FileNotFoundException, IOException{
int letra;

/*Se establece desde donde se leerá la información (InputStream


,flujo de entrada)*/
InputStream flujoEntrada=new FileInputStream(nombreFicher oOr igen);
InputStreamReader lectura=new InputStreamReader(flujoEntrada);

/*Se establece hacia donde va la información (OutputStream


,flujo de salida)*/
OutputStream flujoSalida=new FileOutputStream(nombreFicheroDestino);
OutputStreamWriter escritura=new OutputStreamWriter(flujoSalida);

//Leemos desde ficheroOrigen y escribimos en ficheroDestino


while(lectura.ready())(
letra=lectura.read();
escritura.write(letra);
}
lectura.cióse();
flujoEntrada.cióse();
escritura.cióse O;
flujoSalida.close();

Es posible usar 'flújos"ínténnediós'"q^^^^ directamente con los tipos primitivos del


lenguaje como DatalnputStream y DataOutputStream.
public void almacenarNumeros(String nombreFichero)
throws FileNotFoundException, IOException{
BufferedReader teclado=new BufferedReader
(new InputStreamReader(System.in));

OutputStream flujoSalida=new FileOutputStream(nombreFichero);


DataOutputStream escritura=new DataOutputStream(flujoSalida);

double numReal;
System.out.println("Escribe número real y pulsa ENTER.
-1 para finalizar");
do{
numReal=Double.parseDouble(teclado.readLine());
if(numReal!=-l){
escritura.writeDouble(numReal);
}
jwhile(numReal!=-l);

escritura.cióse O ;
flujoSalida.cióse();
teclado.cióse();
}
void leerNumeros(String nombreFichero)
throws FileNotFoundException, IOException{
InputStream flujoEntrada=new FileInputStream(nombreFichero);
DatalnputStream lectura=new DatalnputStream(flujoEntrada);
try{
double numReal;
while(true){
Capítulo 10. Flujos de Entrada/Salida y control de excepciones 413

numReal=lectura.readDouble();
System.out.println(numReal);

catch(EOFException ex){
lectura.cióse O;
flujoEntrada.cióse();
)

Al llegar al final del fichero, si usamos DataínputStream en conjunto con alguno de los
métodos readXXX (readlnt(), readDouble(), etc.) el programa lanzará la excepción
EOFExcption. En el ejemplo se ha optado por configurar un bucle infinito que finalizará cuando
se produzca el error de ejecución tal que en ese momento se cerrarán los flujos abiertos.
El método readLine() de DatalnpuStream, en cambio, devuelve nuil si no existe más
información a leer de fonna que podemos usar esto para finalizar el bucle:
p

i while ((cadena = lectura.readLine())!= nuil) {...} j


Existen clases que además permiten acceso aleatorio a "la información como
RandomAccessFile. Véase el siguiente ejemplo.
public static void leeAleatorio(String nombreFichero)
throws FileNotFoundException, lOExceptionf
RandomAccessFile lectura = new RandomAccessFile(nombreFichero, r ),
char letra;

//Colocamos el puntero justo antes de la posición a leer


lectura.seek(6);
letra=lectura.readChar();
System.out.println(letra);
lectura.closeO ;
.J
"Éi constructor de Rand'ort^c¿Vs"s]ñle precisa""qiie"s"e""e's"tábre"zcá'é^^ en que será accedido
el fichero, así, en el ejemplo se observa "r", en new RandomAccessFUe(...)- Existe otros
modos como "w" o "rw"(véase la API).
Para finalizar el apartado, veamos el objeto File que representa tanto ficheros como
directorios. Este contiene los siguientes métodos y propiedades:
- pathSeparator. Propiedad de tipo String. Representa el carácter separador en la ruta
al fichero o directorio. Este varía en función del sistema operativo.
■ canExecuteO / canReadQ / canWriteO- Se usan para averiguar si el fichero es
ejecutable, puede ser leído o escrito respectivamente.
■ createNewFlIeO. Crea un fichero vacío.
■ deleteQ. Borra un fichero o directorio.
■ exists(). Devolverá verdadero o falso en función de si el fichero existe o no.
■ getAbsolutePath(). Devuelve una cadena de caracteres con la ruta absoluta de acceso
al fichero o directorio.
■ getPathQ. Devuelve una cadena de caracteres con la ruta o path para llegar al fichero
o directorio.
414 Programación

isFileO- Determina si el objeto es un fichero.


isDirectoryO- Determina si el objeto es un directorio.
listo. Devuelve un array de String con los nombres de los ficheros de un directorio
(el objeto que hace la llamada a listQ).
listFilesQ. Devuelve un array de tipo File en el cada casilla incluyen un fichero o
directorio de la ruta especificada.
mkdir(). Crea un directorio.

ACTIVIDAD 10.11

Realiza un programa en el que crees un directorio vacío y en él copies los ficheros ubicados
en otro directorio.

10.7. PROCESO DE SERIALIZACIÓN EN JAVA


A la hora de trabajar con objetos y flujos de datos, es necesario que sean transformados en
bytes para que sean enviados, y como ya sabemos, es aquí donde se debe aplicar la serialización
del objeto.
En Java, para que un objeto pueda ser escrito en un flujo es necesario que iinplcmente la
interfaz Serializable.

class Persona in^lements Serializable{

A la hora de escribir o leer un objeto usaremos las clases ObjectOutputStream e


ObjectInputStream, teniendo en cuenta que en la lectura debemos realizar un casting.
public void escribirPersona(String nombreFichero
,Persona [] listaPersonas) throws FileNotFoundException, IOException(
ObjectOutputStream escribir=
new ObjectOutputStream(new FileOutputStream(nombreFichero));
for(int i=0;i<listaPersonas.length;i++)

escribir.writeObject(listaPersonas[i]);

escribir.cióse();

public void leerPersona(String nombreFichero)


throws lOException, ClassNotFoundException{
ObjectInputStream leer=
new ObjectInputStream(new FileInputStream(nombreFichero));
Persona aux;
do{
aux=(Persona)leer.readObject();
if (aux!=null){
System.out.println(aux.nombre);
}
}while(aux!=null);
leer.cióse ();
Capítulo 10. Flujos de Entrada/Salida y control de excepciones

10.8. OTROS FLUJOS DE DATOS EN JAVA


Como ya se ha comentado en más de una ocasión, el flujo de datos permite el envío o
recepción de datos entre objetos de nuestro programa y dispositivos tales como disco, memoria,
interfaz de red, etc. En este capítulo nos centramos en la comunicación con ficheros y directorios
almacenados en diferentes dispositivos secundarios y nos parece oportuno finalizar el estudio de
flujos presentando un ejemplo de comunicación entre otros dispositivos.
La siguiente aplicación está compuesta de dos clases ejecutables, una ellas llamada Servidor y
otra denominada Cliente. El objetivo es que ambas clases puedan ser ejecutadas en diferentes
ordenadores tal que se comuniquen y envíen infonnación a través de la red. Para ellos se usan
dos clases Java, ServerSocket y Socket que generan procesos servidores (a espera de que los
clientes se conecten a él para poder darles servicio) y procesos clientes. Utilizaremos las clases
de flujo DatalnputStream y DataOutputStream para la recepción y envío de la información.
package redes;

import java.io.DatalnputStream;
import java.io.DataOutputStream;
import java.io.lOException;
import java.net.ServerSocket;
import java.net.Socket;

public class Servidor {

public static void main(String[] args) throws lOException {


//Declaración de variables para conexión en red
ServerSocket servidor=new ServerSocket(1234);
Socket cliente=null;

//Declaración de flujos
DatalnputStream leer=null;
DataOutputStream escribir=null;

/♦Declaración de variables donde se almacenarán datos enviados y


recibidos*/
String cadenaLeida;
String cadenaMayusculas;

/*E1 método acceptO hace que el servidor este a la escucha o espera de


que un cliente se quiera comunicar con él*/
cliente=servidor.accept() ;
/ Se crean los flujos intermedios a partir de los flujos base que
proporciona el cliente. getInputStream y getOutputStream refieren
la secuencia de bytes procedente del cliente*/
DatalnputStream (cliente. getInputStream ()) ;
escribir—new DataOutputStream (cliente. getOutputStream () ) ;
System.out.println ("Recibiendo información. . .");
cadenaLeida=leer.readUTF() ; //Método para leer cadenas del flujo
cadenaMayusculas=cadenaLeida.toUpperCase O ;
System. out.println ("Cadena enviada "+cadenaMayusculas+". . .") ;
escribir.writeUTF(cadenaMayusculas); /*Método para escribir una cadena
en el flujo*/
//Cerramos todos lo flujos abiertos
leer.cióse();
escribir.cióse();
cliente.cióse();
servidor.cióse();

package redes;

import java.io.DatalnputStream;
import java.io.DataOutputStream;
import java.io.lOException;
import java.net.Socket;

public class Cliente {

piablic static void main (String[] args) throws lOException {


/*Se crea el cliente. Al especificar una ip y un Puerto nuestro cliente
buscará en el equipo concreto un puerto abierto con ese número.
Si existe el servidor acaba de recibir un cliente con lo que se
empiezan a ejecutar las sentencias a partir de acceptO*/
Socket cliente=new Socket("127.O.O.1",1234);

//Abrimos los flujos para el envió de información


DatalnputStream leido=new DatalnputStream(cliente.getinputstream());
DataOutputStream escrito=
new DataOutputStream(cliente.getOutputStreamO);

/*Declaración de la cadena a enviar y la variable donde almacenar la


que se reciba*/
String cadena="este texto está en minúsculas";
cadenaLeida;

//Escribimos en el flujo, es decir, enviamos la información


escrito.writeUTF(cadena);
//Leemos lo que el servidor nos envia y lo almacenamos en cadenaLeida
cadenaLeida=leido.readUTF();
System.out.println(cadenaLeida);

//Cerramos flujos y socket abiertos


leido.cióse();
escrito.cióse();
cliente.cióse();

10.9. CUADROS DE DIÁLOGO EN ENTORNO GRÁFICO


PARA ACCESO A FICHEROS
Tanto en Java conao C# existen elementos gráficos que permiten facilitar el acceso a
directorios y ficheros, cuadros de diálogos perfectamente configurados como los habituales en
los sistemas operativos más usados.
Capítulo 10. Flujos de Entrada/Salida y control de excepciones 417

Abrir
: * * Ba>fcetfc« » DocwncnSos «

^ Í4M«vé

Biblioteca Docome- Ofoéjrper Cvmu


'
» OecwTwWei •|«r' I x.:^ a.- !utKMfcetn

* Mwr>«i«rp«U

■C 'r>««tot
j.
j Bibiioteca Oocumemos
C^frw per Csrp^t *■ *lh' ' 1l1
11 ñ •
mM—
ir,-.-
Aácibtjit'df ho< V«fi«n Cue
qaed«w
W

. )
• OecwiHf** ll
tm Ir«t*9*rv«t |
B 'TWrvM#! 6afr»«S«Mm««« V«*w«n Cu* JUAepUy M|
et 0cw9nflr'

O'vpe «n «I ^ 0<Mra<c<«

iji iiii Ijt lii: Ov^ en d hogar


ji habd
Cqurpo Abro i |. CeooÉw
• ^ Atd
: P*n«i d« control

¿_P»p«lefa de wtic^e_

lownuewooro^, I Agtotof j { Conato' |

Figura 10.12. Cuadros de diálogo Buscar carpeta (FolderBrowserDialog), Abrir (OpenFileDialog) y


Guardar (SaveFileDialog).

Lid Abrir

Buscaren: [fgg Dccumenís ~3 CSj


di Access Devdioper Extanslons (¡¿ Alcohol 120% Drú'erGentus (d
Id AdcWn Expresa lifAItova Id Fax d
id Adobe Id Autopia/Menú Designar Im Fonts m
Id AdcbeStoc<Photos a Bluetooth d gegl-0.0 id
Iji Album graduabón Id Downioads Id archivos de origen de datos ld

Figura 10.13. Panel Abrir/guardar archivos (File Chooser) en Java.

La inclusión de estos elementos en el formulario es similar a la de cualquier otro control, solo


debemos disponer de algún menú, botón u otro elemento en la aplicación que nos permita
acceder a ellos.

En C#, el método que pennite visualizar los diferentes cuadros de diálogos es ShowDialogO-
Este no pennite que se realice ninguna otra operación hasta que la ventana se cierra, es decir, los
cuadros de diálogos pasan a ser modales.
En Java, debido a que tenemos un único cuadro para las funciones de abrir, guardar y buscar
archivo, disponemos de los métodos showOpenDlalogO, showSaveDialogQ y showDialogO-
Botones del cuadro de herramienta referentes a los cuadros de dialogo en C#
en FolderBrowserDialog [ ^ OpenFileDialog I SI SaveFileDialog
Botón del cuadro de herramientas referente al cuadro de diálogo en Java
Combinaremos estos cuadros de diálogos con los flujos y archivos vistos en el capítulo.
Veamos los siguientes ejemplos.

/*A1 hacer clic en el botón se abrirá un cuadro de diálogo


FolderBrowserDialog. Se obtiene el nombre de todos los ficheros y estos se
muestran en una lista*/
prívate void buscar_Click(object sender, EventArgs e)
{
//Si pulsamos Aceptar en el cuadro de diálogo
if (elegirCarpeta.ShowDialog() == DialogResult.OK)
{
string cadena = elegirCarpeta.SelectedPath; /*Obtiene la ruta
seleccionada*/
string[] ficheros = Directory.GetFiles(cadena); /*Obtiene los nombres
de todos los ficheros y directorios
de la carpeta seleccionada*/
foreach(string cad in ficheros)//Agregamos elementos a la lista
{
lista.Items.Add(cad);

//Uso del cuadro de diálogo Abrir


/*Nuestra aplicación dispone de un menú abrir que pulsaremos para acceder a un
fichero. Al hacer clic sobre él se muestra el cuadro de diálogo y al Aceptar
el fichero elegido se abre y se muestra en un cuadro de texto multilinea*/
private void menuAbrir Click(object sender, EventArgs e)
{
//Si se ha pulsado Aceptar en el cuadro de dialogo...
if (abrir.ShowDialog() == DialogResult.OK)
{
//Creamos los flujos base e intermedio para leer el fichero
FileStream flujo = nuil;
BinaryReader fichero = nuil;
try
{
string nombreFichero = abrir.FileName;
flujo = new FileStream(nombreFichero, FileMode.Open);
fichero = new BinaryReader(flujo);
string cadenaLeida;
/*Mientras podamos leer, se lee y se almacena la cadena en
cadenaLeida*/
while ((cadenaLeida = fichero.ReadString()) != nuil)
{/*La linea leida de fichero se añade al cuadro de texto
multilinea*/
cuadro.AppendText(cadenaLeida);
}
}//Al llegar al final se lanza la excepción EndOfStreamException
catch (EndOfStreamException ex)
{
fichero.Glose ();
flujo.Glose O;

/*Para finalizar imaginemos que tenemos una aplicación que realiza la función
de guardar entre otras, vamos a codificar un menú tal que recoja la
Capitulólo. Flujos de Entrada/Salida y control de excepciones 419

información de un cuadro de texto y la almacene en el fichero seleccionado.*/


prívate void menuGuardar_Click(object sender, EventArgs e)
{
if (guardar.ShowDialog() == DialogResult.OK)
{
string nombreFichero = guardar.FileName;
FileStream flujo = new FileStream(nombreFichero, FileMode.Create);
BinaryWriter fichero = new BinaryWriter(flujo) ,•
fichero.Write(cuadro.Text);
fichero.Glose O;
flujo.Glose O;

Los códigos escritos son bastante simples, se pueden modificar para controlar posibles errores
de ejecución por parte del usuario. En los cuadros de diálogo Abrir y Guardar se puede
configurar la propiedad FileName de antemano, de fonua que se muestre en el cuadro nombre el
texto relacionado con esta propiedad. Posterionuente, como hemos visto, FileName nos servirá
para saber qué fichero se ha escogido.
La propiedad Filter se usa para configurar el cuadro de texto tipo en estos cuadros de
diálogo. En el cuadro tipo solemos especificar la extensión de los ficheros que nuestra aplicación
puede leer o guardar. Por ejemplo podemos asignar a la aplicación el valor: Archivos
b¡narios|*.dat|Archivos de texto |*.txt; de forma que estaremos indicando que se visualicen en
el cuadro de diálogo los ficheros con estas extensiones. El formato para el valor de Filter es:

Filter = "Descripción del tipo de archivo\extensión\siguiente descripción\extensión..."


Existen otros tipos de cuadros de diálogo como CoIorDlalog que muestra el cuadro de
diálogo para escoger un color. FontDíalog muestra el cuadro de diálogo fuente o tipo de letra.

ACTIVIDAD 10.12

En Java la fonua de operar con cuadros de diálogos es similar a como hemos visto en C#.
Crea una pequeña aplicación gráfica con un menú y las opciones guardar y abrir. Usa los
controles que creas oportunos tal que cuando se pulse en guardar se visualice un cuadro de
dialogo de guardar y, posterionuente cuando el usuario pulse en Aceptar, se extraiga la
infonuación de algún otro control y se almacene en el fichero seleccionado en el cuadro de
diálogo. El menú abrir realizará la operación inversa.

10.10. EXCEPCIONES
Cuando se diseñan programas, se configuran de forma que se tienen en cuenta todos los
posibles en-ores que se pueden producir durante la ejecución del mismo, sin embargo, si
desarrollamos programas complejos es muy probable que se produzcan errores en tiempo de
ejecución.
Cuando escribimos código fuente podemos observar si se producen errores de compilación
gracias a las nuevas interfaces de desarrollo en modo gráfico o bien el programa no podrá ser
ejecutado hasta que el error sea corregido. Este tipo de errores se denominan errores de
compilación mientras que a los errores que se producen en tiempo de ejecución se les denomina
Excepción.
Al producirse un error en tiempo de ejecución, si este no es controlado, el programa finaliza
de forma brusca, en este apartado vamos a estudiar el modo de asegurar que nuestra aplicación,
pese a estos tipos de errores, funcione adecuadamente y en caso de que se produzcan fallos
resolver estos de un modo u otro, o al menos aportar una vía alternativa de ejecución ante los
fallos ocasionados.

Así, a partir de ahora, se diseñarán las aplicaciones de forma que si el código presenta una
excepción, esta será tratada en otra zona del código fuente siempre que la excepción producida
haya sido "nombrada" de algún modo. A continuación estos párrafos comenzarán a cobrar
sentido.

10.10.1. MECANISMOS DE MANEJO DE EXCEPCIONES


Tanto en C# como en Java, cuando se produce un error de ejecución, no puede ser ignorado.
Justo cuando se da el error las aplicaciones lanzan una excepción (thows). A partir de esta la
aplicación deberá:
■ Buscar algún bloque de código que responde a la excepción (manejador de
excepciones).
■ El manejador captura la excepción. En esta parte del código podemos usar las
sentencias vistas en los lenguajes de programación estudiados. El objetivo es absorber
el error para evitar que la aplicación finalice de forma inesperada.

10.10.2. BLOQUE TRY...CATCH


El bloque try...carch^ permitirá implementar lo explicado en el apartado anterior (10.10.1).
Las palabras reservadas que fonna el bloque son muy representativas, podrían interpretarse
como:

intenta ejecutar este códigof

captura el error si existe (codigoError){

}catch(codigoError ex){

Los bloques try...catch además pueden ir acompañados de una sentencia finally. El bloque
de código incluido en el finalIy se ejecuta siempre existan o no errores. Así, con el uso de finally
podemos aseguramos de que los recursos usados sean liberados, los objetos eliminados
correctamente, etc.

Misma sintaxis tanto para C# como Java.


Capítulo 10. Flujos de Entrada/Salida y control de excepciones 421

Podemos incluir tantos bloque eateh como errores queramos capturar. Es fácil encontrar
estructuras como esta:

catch (TipoErrorl exl){

catch(TipoError2 ex2){

catch(TipoErrorN exN){

Bloque controlado
con try ^

Error de tipo 1

Código

Error de tipo 2

Bloque (catch) para procesar error


tipo 1

Bloque (catch) para procesar error L


tipo 2 r

Figura 10.14. Proceso de ejecución en un programa con control de excepciones.


Veamos un pequeño ejemplo. Imaginemos que debemos realizar un programa que se encarga
de dividir un número entre otros. El problema es simple y se solucionaría con el siguiente
código^:
1 static void Main (string[] args)
i {
I doiable dividendo;
i dovible divisor;
; double división;
; Consolé.Write ("Escribe el dividendo:");
i. iF^rse(Consolé.ReadLine O );

^ El código está escrito en C# aunque es bastante similar en Java, la diferencia se encuentra en las
clases diseñadas para la escritura en pantalla y lectura de teclado.
Consolé-Write("Escribe el divisor:");
divisor = double.Parse(Consolé.ReadLineO);
división = dividendo / divisor;
Consolé-WriteLine("El resultado de dividir (0) entre (1) es {2}"
,dividendo, divisor, división)
Consolé.WriteLine("Pulsa una tecla para finalizar el programa");
Consolé.ReadLine();

Mientras escribamos valores numéricos la ejecución será correcta pero, ¿que ocurriría si en
lugar de un número escribiéramos una letra cuando nos solicitan los datos referidos a dividendo y
divisor?

Al ejecutar el programa, si cuando nos piden un dividendo escribimos por ejemplo la letra 'q',
Visual Studio se detiene mostrando un cuadro de diálogo en el que se infonna de que se ha
producido un error no controlado en tiempo de ejecución y el programa debe finalizar. El en'or
concretamente viene definido por la clase FormatException. Este fallo se da cuando intentamos
convertir un valor a un tipo no penuitido (double.parse(Console.ReadLine()), intentamos
convertir un carácter a double y esto no es posible).
Para subsanar el problema cambiamos el código como sigue:
I

static void Main(string[] args) i

double dividendo;
double divisor;
double división;
Consolé.Write("Escribe el dividendo:");
dividendo = double.Parse(Consolé.ReadLineO);
Consolé.Write("Escribe el divisor:");
divisor = double.Parse(Consolé.ReadLineO);
división - dividendo / divisor;
Consolé.WriteLine("El resultado de dividir {0} entre {1} es (2)"
, /-l-ix7-¡Hí=nHo.
dividendo, divisor,
Hivi c: nr. div
H-itr isión);

Consolé.WriteLine("Pulsa una tecla paraí finalizar el programa");


Consolé.ReadLine();
¡

(FormatException error)

Consolé.WriteLine("No ha sido posible continuar con el programa,


se ha producido un error en la introducción de datos.
rrur cu j-o — .

Código de error:{O}",error.Message);
Consolé.ReadLine();

Recordamos que a la Wra de' hacer uña división podemos dividir por cualquier número
siempre que este no sea cero, en cuyo caso el resultado de la división es infinito. ¿Qué ocurrirá
en nuestro programa si asignamos a divisor el valor O?
En este caso, C# en las actuales versiones controla este tipo de error indicando que el
resultado de dividir entre cero es infinito, no se produce un cierre inesperado. Si este mismo
código lo ejecutáramos en Java se observaría algo similar, eso sí, en lugar de mostrar el texto el
resultado es infinito se visualizaría el resultado es Infinity. A continuación se indica el código y
excepciones Java.
Capítulo 10. Flujos de Entrada/Salida y control de excepciones

NOTA: Al realizar una lectura de teclado en Java se deben usar flujos y capturarse
posibles excepciones de E/S. Por este motivo se añade un catch para lOException.

public static void main(String[] args) {


try
{
BufferedReader teclado=
new BufferedReader(new InputStreamReader(System.in));
double dividendo;
double divisor;
double división;
System.out.print("Escribe el dividendo:");
dividendo = Double.parseDouble(teclado.readLine());
System.out.print("Escribe el divisor:");
divisor = Double.parseDouble(teclado.readLineO );
división = dividendo / divisor;
System.out.print("El resultado de dividir "+dividendo+
" entre " + divisor + " es " + división);
System.out.print("Pulsa una tecla para finalizar
el programa");
}
catch (lOException error){
System.out.print("No ha sido posible continuar con el programa,
se ha producido un error al leer los datos. Código de error:"
+ error.getMessageO );
}
catch(NumberFormatException error){
System.out.println("No ha sido posible continuar con el programa,
se ha producido un error en la conversión. Número no válido.
Código de error:" + error.getMessage());

Como decíamos con anterioridad, no es necesario incluir el bloque fínally. Si se añade, debido
a que tengamos componentes en nuestro programa que deban ser tratados de un modo concreto,
debiendo controlar su finalización, este se incluirá detrás de todos los bloque catch.
En nuestro ejemplo vamos a agregar un pequeño bloque finally. Este no tiene demasiada
utilidad pero aclarará su funcionamiento al lector.
— 1

static void Main(string[] args) i

double dividendo;
double divisor;
dot±)le división;
Consola.Write("Escribe el dividendo:");
dividendo = double.Parse(Consolé.ReadLine());
Consolé.Write("Escribe el divisor:");
divisor = double.Parse(Consolé.ReadLineO );
división = dividendo / divisor;
Consolé.WriteLine("El resultado de dividir {0} entre {1} es {2}"
, dividendo, divisor, división);
}
catch (FormatException error)
{
Consolé.WriteLine("No ha sido posible continuar con el programa.
se ha producido un error en la introducción de datos. Código de
error:{0}", error.Message);
}
finally
{
Consolé.WriteLine("Pulsa una tecla para finalizar el programa");
Consolé.ReadLine();

Se produzca o no error la aplicación finaliza con el mensaje "Pulsa una tecla para finalizar el
programa".

10.10.3. JERARQUÍA DE ERRORES


Tanto en Java como C# existe una superclase de la que derivan todos las clases que registran
todos los posibles tipos de error. En ambos lenguajes esta clase base es denominada Exception'.
Veamos los esquemas donde se observan claramente las jerarquías.

Throwable =^4 Error


NoClassDefFoundError
f-'1^F¡leNotFoundException
íi*! EOFException j
VirtualMachineError
UTFDataFormatException

ClassNotFoundException

lOException
Exception
W ArithmeticException I
RuntimeException CassCastException i
P IndexOutBoundException

Figura 10.15, Jerarquía de clases de error en Java. Existen otras muchas, véase la API de Java.

Exception
-i>>| FileNotFoundException I

tp="^LpivÍdeByZeroException
=>-- EndOfStreamException
|
L^'|^ExceptÍo^=^^ -íí'^TFDataFormatException
NuIIReferenceException

\ SystemException IndexOutOfRangeException

Figura 10.16. Jerarquía de clases de error en C#. Al igual que en la Figura 10.15 se han omitido algunas
para no hacer extenso el gráfico. Véase el framework .NET.

^ En Java esta deriva de Throwable.


Capitulólo. Flujos de Entrada/Salida y control de excepciones 425

La clase Exception, tanto en Java como C#, dispone de una serie de métodos y propiedades
que son usados en las clases que derivan de ella.
Propiedades de Exception en C#;
■ Message. Propiedad. Devuelve una cadena de caracteres con un mensaje descriptivo
del error.

■ InnerException. En caso de que una excepción sea producida a causa de otra esta
propiedad devolverá esta. Devuelve un objeto de tipo Exception o derivado.
■ Source. Almacena o devuelve información relativa al componente que causó la
excepción. Es una propiedad.
■ HelpLink. Devuelve una cadena de caracteres con la URL donde se puede localizar
información relativa al error producido.
Métodos de la clase Exception en Java;
■ getMessageO. Método que devuelve una cadena de caracteres descriptiva del tipo de
error producido.

■ getCauseQ. Método que devuelve un objeto de tipo Throwable en caso de que la


excepción haya sido provocada por otra.
10.10.4. CREANDO NUESTRAS PROPIAS EXCEPCIONES
Aunque existen muchos tipos de objeto derivados de Exception, son muchos los programas
que se pueden realizar y muchas las posibles causas de error que se pueden plantear. En
ocasiones, no es posible, aún teniendo muchas clases de error, controlar todo aquello que
necesitamos.

En este apartado vamos a estudiar cómo crear nuestras propias clases de error y cómo lanzar
estas en detenninados momentos del programa.
10.10.4.1. CREACIÓN DE CLASES DE ERROR EN C#
Las clases de error son similares a cualquiera de las clases que hemos estado desarrollando
hasta ahora, con diferencia de la fimción para la que se diseñan. En C# las clases de error deben
derivar de Exception o AppIicatlonException, normalmente suelen derivar de la segunda de
ellas.

Así, a la hora de crear nuestra propia clase de error declararemos esta como sigue:
class nombre_clase:ApplicationException{ '.1 ' :
i Í.ÍV''- i. ■ i

En su interior básicamente configuraremos eY código de descriptivo de este.


Supongamos que estamos desarrollando un software que tiene asegurado el acceso a través de
login y password. En caso de que a la hora de hacer login un usuario este no introduce los datos
correctamente, ya sea usuario incorrecto o password inválida debe producirse un error. En primer
lugar vamos a desarrollar la clase de error que se lanzará cuando se dé la situación planteada.
1. Crearemos una nueva clase en nuestro proyecto.
426 Programación

2. Modificaremos el encabezamiento para que la nueva clase derive de


ApplicationException o una subclase más especializada.
3. Configuraremos una variable donde almacenar un mensaje descriptivo del error
producido.
4. Configuraremos al menos un método constructor que inicialice el mensaje de error.
5. Sobrescribiremos la propiedad Message para que devuelva el código de error
planteado en el punto 3.
class ErrorLoginException:ApplicationException

string mensaje;

public ErrorLoginException (string mensaje)

this.mensaje = mensaje;

public string Menssage

return this.mensaje;

Acabamos de crear una nueva"¿lasé"de eiTÓr"Err^^^^ Ahora solo queda hacer uso de ella
en el programa principal.

10.10.4.2. CREACIÓN DE CLASES DE ERROR EN JAVA


El proceso de creación de clases de error en Java es muy similar, la diferencia se encuentra en
la sintaxis del lenguaje. Así, si queremos crear una clase de error en Java adaptada al problema
presentado en el Apartado 10.10.4.2:
1. Añadimos una nueva clase a nuestro proyecto.
2. Derivamos la clase de Exception o subclase de esta más especializada.
3. Configuramos en la clase definida una variable donde almacenar el código de error o
texto descriptivo.
4. Configuramos el constructor de la clase que inicializará la variable que mantiene el
mensaje de error.
5. Sobrescribimos el método getMessage para que devuelva el error producido en
nuestra clase y no en la clase de la que se deriva.
public class ErrorLoginException extends Exception {
String mensaje;

public ErrorLoginException (String mensaje){


this.mensaje=mensaje;
Capitulólo. Flujos de Entrada/Salida y control de excepciones 427

0Override
public String getMessage(){
return mensaje;

10.10.4.3. LANZANDO EXCEPCIONES

Las clases de error diseñadas en los Apartados 10.10.4.1 se crean con el objetivo de que en el
momento en que un usuario escriba incorrectamente su información de acceso sea lanzada, pero,
¿cómo lanzamos nuestras propias excepciones? Si bien es cierto, hasta el momento, solo
sabemos capturar errores.
Indicamos que se produzca una excepción creando una instancia de esta en la zona crítica del
código, incluyendo este en un bloque try...cateh, y anteponiendo a esta la palabra reservada
throw.

throw new clase_de_error(mensaje);

catch (clase_de_error var_error){

Así, en nuestro ejemplo, deberíamos incluir a la hora de producirse el login de un usuario algo
similar a los que se muestra a continuación:

Código C#
static bool login(string usuario, string password)

static void Main(stringj] args)

if (!login (nombre, passwd))


{
throw new ErrorLoginExoeption ("Usuario o password incorrecto.
Vuelva a intentarlo de nuevo.");

catch (ErrorLoginExoeption error)

Consolé.WriteLine(error.Menssage);

finally
Código Java
static boolean login(String usuario, String password){

public static void main(String[] args) {


try

if(ilogin(nombre,passwd)){
throw new ErrorLoginException ("Usuario o password incorrecto.
Vuelva a intentarlo de nuevo.");
}
}
catch(ErrorLoginException error){
System.out.print(error.getMessage());
}
finally{

NOTA: Podemos lanzar cualquier tipo de excepción con la sintaxis vista, ya sea
creada o no por el usuario.

En muchas ocasiones Java da la posibilidad de omitir los bloques try...catch y añadir una
sentencia throws en la cabecera del método indicando que este es posible que genere o lance un
tipo concreto de excepción.
public static void main(String[] args) throws ErrorLogin, ;
lOException, NumberFormatException{ ;

Esta codificación permite indicar que puede producirse errores de ejecución pero no controla
qué hacer con ellos.

10.10.5. CONSIDERACIONES FINALES


A la hora de realizar aplicaciones en las que se pueden producir fallos debidos al mal uso por
parte del usuario, si estos fallos se detectan a través de sentencias alternativas es conveniente
seguir usando estas sentencias de control antes que crear excepciones, ya que lanzar una
excepción consume tiempo de ejecución.
Cuando creemos excepciones seguiremos la nomenclatura usada en los lenguajes de
programación estudiados, llamamos a estas de forma representativa finalizando el identificador
con la palabra Exception.
Además, siempre que sea posible usaremos las excepciones predefinidas en lugar de crear
nuevas clases de error. En los ejemplos hemos creado clases que derivan de
ApplicationException y Exception, si es posible debemos derivar de la clase más especializada,
es decir, si el error tiene relación con ClassNotFoundException derivaremos de esta en lugar de
Exception o ApplicationException.
Para finalizar, evitar usar bloque catch vacíos o capturar excepciones para las que no tenemos
solución.
Capítulo 10. Flujos de Entrada/Salida y control de excepciones 429

COMPRUEBA TU APRENDIZAJE
1. ¿Qué tipos de ficheros hemos estudiado? ¿Qué caracterizan a cada uno de ellos?
2. ¿Qué operaciones básicas realizamos sobre ficheros?
3. A la hora de abrir un fichero existen diferentes modos,¿cuáles son?
4. ¿Cuáles son los algoritmos de lectura y escritura secuencial y aleatoria?
5. Define flujo. ¿Qué tipos de flujos existen? Explícalos brevemente.
6. ¿Qué se entiende por señalización?
7. ¿Qué es una excepción? ¿Qué mecanismos existen en los lenguajes vistos para controlar
estas?

8. ¿Cómo creamos nuestras propias clases de error?


9. ¿A qué nos referimos cuando decimos que vamos a lanzar una excepción? ¿Cómo
lanzamos excepciones en C# y Java?
10. ¿Qué utilidad tiene la propiedad Message en C# y el método getMessageO en Java?

ACTIVIDADES DE AMPLIACION
1. Amplía la Actividad Ampliación 3 del Capítulo 9 de forma que se de opción al usuario a
almacenar la información en un fichero binario (almacenamos objetos) y cargar datos
desde un fichero.

2. Amplía la funcionalidad de la Actividad de Ampliación 6 del Capítulo 9 de forma que la


infonuación que se gestiona pueda ser almacenada o cargada a o desde un fichero binario.
3. Crea un comando copiaFigura tal que este se ejecute escribiendo; copiaFigura figura
dimensiones. Entre los nombres de figuras permitidos tenemos: círculo, cuadrado,
rectángulo y triángulo. Un ejemplo de uso sería: copiaFigura rectángulo 5 3, siendo 5 la
base del rectángulo y 3 la altura. Para el círculo debemos indicar el radio, el cuadrado el
lado y para el triángulo base y altura. El comando debe guardar la figura con las
dimensiones dadas en un fichero de texto. Para dibujarla podemos utilizar el carácter que
queramos. Para el ejemplo propuesto en el fichero se escribiría algo como:

4. Crea un comando llamado copiarBinario al que pasaremos el nombre de un fichero de


texto origen y el nombre del fichero binario destino (copiarBinario fich.txt fich.bin).
CopiarBinario debe copiar el contenido del fichero de texto en el fichero binario
especificado.
5. Crea un programa que lea el contenido de un directorio dado e indique:
■ Número de directorios que hay.
■ Cuántos ficheros están definidos como solo texto.

■ Cuántos ficheros son ejecutable.


■ Tamaño que ocupa el directorio en memoria.
6. Crea un programa que mueva un fichero de una ubicación a otra, es decir, realice la
ñmción del comando cortar.

7. Crea una pequeña aplicación gráfica en la que se muestre un formulario con dos cuadros
de texto, usuario y password, y un botón, login. Los cuadros de texto no pueden estar
vacíos, si al pulsar el botón alguno no contiene infonnación se lanzará la excepción
VacioException. El cuadro de texto usuario sólo puede contener letras, mayúsculas o
minúsculas, en caso de que contenga otro tipo de símbolo se lanzará la excepción
FormatoErroneoUsuarioException. Para el cuadro password debemos aseguramos de que
se escriban letras, números y alguno de estos símbolos ($, _ % o &), es decir, la
contraseña debe ser una combinación de estos, en caso contrario lanzaremos la excepción
FormatoPasswdErroneoException.
8. Crea un pequeño programa que trate objetos de tipo Casa. Un objeto Casa tiene
propiedades del tipo dirección, portal, piso, localidad, código postal, metros cuadrados,
número de habitaciones, etc., y métodos que modifican estas operaciones, calculan el
precio de venta, etc. La aplicación debe permitir almacenar y cargar estas casas en
ficheros binarios. A la hora de crear una nueva casa se lanzará una excepción en caso de
que alguno de los datos sea incorrecto, esta excepción se llamara NuevaCasaException. Si
cargamos un fichero y este está vacío se debe lanzar la excepción
CasasInexistentesException.
9. En el Capítulo 7 veíamos como ejemplo práctico de uso de arrays el juego del ahorcado.
Añade funcionalidad al programa de forma que almacenemos en un fichero los nombres
de los ganadores así como el número de letras usadas.
10. Realiza un programa que cuente el número de dígitos que contiene un fichero dado por el
usuario.

11. Realiza un programa que muestre, tras leer un fichero dado, el porcentaje de aparición de
cada carácter.

12. Lee un fichero de texto tal que en él se almacenan una serie de operaciones a realizar.
Estas operaciones pueden ser;
■ crear fichero

■ leer directorio

■ contarFicheros directorio

■ EspacioDisponible unidad
El objetivo es que tras leer el fichero se realicen las órdenes especificadas en él.

NOTA: Las actividades pueden realizarse en cualquiera de los lenguajes de j


^ programación estudiados o ambos. !
CAPÍTULO 11

GESTIÓN DE BASES DE DATOS.BASES DE DATOS


ORIENTADAS A OBJETOS

CONTENIDOS / OBJETIVOS
Bases de datos relaciónales. Saber qué es una base de datos
Creación de aplicaciones de acceso relacional y entender el proceso a
a bases de datos relaciónales en seguir para la creación de
C#. aplicaciones que las gestiones en C#.
Bases de datos orientadas a Entender el concepto de base de
objetos en C# y Microsoft SQL datos orientada a objetos.
Server.
Saber cómo configurar bases de
Bases de datos orientadas a datos orientadas a objetos en C# y
objetos en Java y db4o. Java así como las aplicaciones de
conexión que necesitaremos en cada
\ lenguaje e DDE.

RESUMEN DEL CAPÍTULO


En este capítulo se pretende acercar al lector al uso de bases de datos, debido a que en
detenninados programas y según el volumen de infonnación a almacenar, son necesarias.
Comenzando enfocando la programación hacia bases de datos relaciónales para posteriormente
crear modelos de entidades orientados a objetos y gestionar estos en los dos lenguajes de
programación estudiados a lo largo del libro.
11.1. BASES DE DATOS RELACIONALES
Habitualmente solemos trabajar con infomiación que almacenamos en ficheros, sin embargo,
cuando es mucho el volumen de datos a usar se crean bases de datos relaciónales. Una base de
datos relacional organiza la infomiación en tablas relacionadas entre sí. Se aconseja el libro
Gestión de Bases de Datos de esta misma editorial para profundizar en conceptos relacionados
con bases de datos relaciónales. Veamos un pequeño ejemplo.
Imaginemos que tenemos una tienda donde debemos almacenar información relacionada con
nuestros productos, clientes, proveedores y pedidos. De cada grupo de información debemos
guardar:
■ Artículos: Referencia del artículo, referencia del proveedor que lo suministra,
nombre del artículo, nombre de la categoría en la que se encuentra encasillado, precio
y unidades en stock.
■ Clientes: Referencia del cliente, nombre del cliente, dirección, ciudad, código postal,
país y teléfono.
■ Proveedores: Referencia del proveedor, nombre del proveedor, dirección, ciudad,
código postal, país y teléfono.
■ Pedidos: Referencia del pedido, referencia del cliente que hace el pedido, referencia
del artículo solicitado, cantidad, descuento si lo tiene y fecha de pedido.
Después de conocer con exactitud la información a almacenar y las partes que fonnan esta
información, usamos un programa gestor de bases de datos para llevar a cabo la organización y
estructura de componentes de la base de datos.

i NOTA: SGBD (Sistema de Gestión de Bases de Datos). Un SGBD no es más que un


: conjunto de programas que permite almacenar, modificar y extraer infonnación de una
' base de datos, además de proporcionar un conjunto robusto de herramientas para la i
: realización de otro tipo de operaciones sobre los datos. i

Access, aunque no es de los mejores sistemas de gestión, se usa con frecuencia en la actualidad
en pequeñas/medianas empresas, así, vamos a usar esta aplicación de Microsoft para generar
nuestra base de datos ejemplo. En este libro no vamos a enseñar al lector a crear una base de
datos, tan solo mostraremos las tablas y relaciones entre ellas, si el lector desea conocer más
sobre la creación de bases de datos puede hacerse con el libro de esta misma editorial que
comentábamos párrafos más arriba.
Decir que cada grupo de información vendrá definido por un objeto llamado tabla. A su vez,
en cada grupo de datos realizamos subgrupos de información, por ejemplo, de los clientes
almacenamos referencia, nombre, dirección, etc., a cada uno de estos elementos se les denomina
campo. Por último, cuando empezamos a dar un valor exacto a cada campo vamos configurando
los denominados registros. Un registro almacena información relacionada con una entidad
concreta, es decir, cada cliente es un registro y cada cliente da una infonnación diferente a los
campos nombre, dirección, etc.
A su vez, los datos no pueden estar aislados, los clientes deben estar en contacto directo con
los pedidos que realizan, los proveedores con los artículos que proveen, de fonna que se
establecen relaciones entre las tablas o grupos de información más que justificadas.
Capítulo 11. Gestión de bases de datos. Bases de datos orientadas a objetos 433

Finalmente nuestra información debe estar organizada siguiendo el esquema que se muestra
en la Figura 11.1.

Campos

RefProveedor Nombre Te efono

Registros

P- ;
S/ RefPc&do

R<tA/tiCUtO
í RdProveodof X
CjntidJd
Detru<nto
Nombre de ^<r«ed
Oifítcicn X
fccKide pedido
Ciudid X

í RerCi»ent< -
Nombre de Clienti
9 RetArucutc j
RrfPravecdoi 9
Dtfecoon
Nombre de Artin
Ciudad
Nofr.b(e de Catej
Codpottal j Pteoo unidad
P*i>
Unidades en eut
Tfjífftnrt

Figura 11.1. Esquema de relaciones Access entre las tablas que forman la base de datos y elementos
de una tabla relacional.

A partir de ahora, nuestro objetivo es crear aplicaciones tanto en C# como en Java que hagan
las veces de sistemas de gestión tal que actualicen la información de la base de datos, la
modifiquen o eliminen, la visualicen, etc. Así, dispondremos de una base de datos ya creada,
conectaremos con ella, y en la aplicación usaremos las herramientas oportunas para gestionar los
datos.

11.2. CREACIÓN DE UNA APLICACIÓN DE GESTIÓN DE


BASES DE DATOS SENCILLA EN C#
En C# es sencillo el proceso para crear aplicaciones de acceso a datos. Solo se precisa que la
base de datos baya sido creada con anterioridad. Vamos a usar de ejemplo la base de datos
Access del Apartado 11.1.
Para crear una aplicación que acceda a los datos de una base de datos previamente creada y
que realice operaciones sobre ellos, se deben realizar las siguientes operaciones;
1. Crear un nuevo proyecto gráfico(Windows Form).
2. En la zona de la izquierda hacer clic sobre Orígenes de datos para visualizar la paleta
con este nombre.
3. Aún no se ha definido ningún origen de datos, es decir, no hemos conectado la
aplicación con ninguna base de datos, de fonna que la paleta Orígenes de datos
aparecerá vacía con el enlace Agregar nuevo origen de datos... y un texto
representativo sobre la situación actual con respecto a conexión de datos de la
aplicación.
4. Clic en Agregar nuevo origen de datos... Aparece el asistente de configuración de
orígenes de datos.
Asistente para configursción de orígenes de datos ^ . L_¿

Bagir un tipo da origen de datos |

¿De dónde obtendrá b apGcadón los datos? j

Bese dejleto^ , Seivicio Objeto

Le permite cenecterse a une bese de datos y elegir les objetes de base de datos para la aplicación.

Figura 11.2. Asistente de C# para la configuración de orígenes de datos.


5. La opción seleccionada por defecto es Bases de datos. Mantenemos esta y clic en
Siguiente.
6. A continuación debemos elegir un modelo de base de datos. En esta ocasión
seleccionaremos Conjunto de datos en lugar de Entity Data Model. Esta última
generará una estructura de clases a partir de la información. Clic en Siguiente.
¿Qué tipo de modelo de base de datos desea usar?
jj- ^ ^
: Conjunto de| Entity Data
datos j Model

El modelo de base de datos que elija determina los tipos de objetos de datos que utiliza el código de la aplicación.
Se agregará un archivo de conjunto de datos a! proyecto.

Figura 11.3. Elección del modelo de base de datos.

7. Elegir conexión de datos. En esta ventana debemos establecer qué conexión de base
de datos vamos a usar, es decir, se debe indicar qué fichero contiene la base de datos
con la información. Es necesario dar a conocer una serie de características, el nombre
del archivo no es suficiente, así haremos clic en Nueva conexión.
8. En Origen de datos debemos especificar Archivo de base de datos de Microsoft
Access. Existen otros orígenes tales como SQL Server.
9. Clic en examinar para localizar el fichero de base de datos. Seleccionamos nuestra
base de datos tienda. Si hubiéramos establecido un password de acceso a la base de
datos debemos escribir en la zona Conexión con la base de datos. Clic en Siguiente.
Capítulo 11. Gestión de bases de datos. Bases de datos orientadas a objetos

10. Si la base de datos no se encuentra en la carpeta del proyecto se nos alertará de ello y
dará la opción de agregarla a este. Clic en Sí para que añada el archivo de base de
datos al proyecto que va a usarla.
11. Los pasos que se acaban de dar establecen una conexión con una base de datos
representada mediante la denominada cadena de conexión. Esta presenta un nombre
dado por Visual Studio. Si queremos modificar solo tendremos que escribir en el
cuadro de texto. Daremos a la cadena de conexión el nombre conexionTlenda.

12. Clic en Siguiente. Si la conexión con las base de datos se realizó correctamente
veremos una pantalla con las tablas y vistas (consultas) almacenadas.

¿Qué objetos de ia base de datos desea tener en el conjunto de datos?


[Ugl* Tablas I
CUl^ Vistas

Figura 11.4. Elementos de la base de datos conectada.

13. Seleccionamos todas las tablas haciendo clic en la casilla junto a Tablas y
especificamos el nombre del DataSet antes de hacer clic en Finalizar. El DataSet es
el componente que englobará toda la información de la base de datos con la que
conectamos. Estará fonnado por DataTable relacionados cada uno con tina de las
tablas de la base de datos en cuestión.

14. Tras unos segundos, la paleta orígenes de datos cambiará de forma que en ella se
visualizarán todas las tablas de nuestra base de datos.

15. Podemos configurar un formulario para


que muestre una de las tablas
+8
simplemente haciendo clic sobre ella en
A 8^ Datos la ventana orígenes de datos y arrastrar
hacia la ventana. De forma automática
M RefArticuio aparecerá im Datagrid configurado para
M RefPreveedor visualizar los campos de la tabla
M Nombre de Artículo seleccionada junto a una barra de
Nombre de Categoría herramientas que permitirá el
Precio unidad desplazamiento entre registros así como
0 Unidades en existencia la eliminación y anexión de nuevos datos.
> 8*: Pedidos
> 8^; Clientes NOTA: Si Añadimos un
t> 8Íi Pedidos fonnulario por cada tabla y
> 8^! Proveedores conectamos todos ellos a través de
una ventana que los muestre ya hemos
finalizado nuestra primera aplicación
de bases de datos.
Figura 11.5. Paleta orígenes de datos tras la
conexión con la base de datos de nuestra tienda de
ejemplo.
11.3. ELEMENTOS DE UNA BASE DE DATOS EN C#
Al crear nuestro primer formulario a partir de un objeto de base de datos, la vista de diseño se
muestra como sigue en la Figura 11.6.
Forml.cs[Diseño]*

ni? Forml
; N < O de{0} ► H ■!> X y

Nombre de Nombre de
RefArticulo RefProveedor
Artículo Categoría

datos articulosBindingSource sB articulosTableAdapter ¡B tableAdapterManager

M artículosBíndingNavigator

Figura 11.6. Vista de diseño del formulario que visualizará la información referida a la tabla Articules.

Se observan los elementos:

■ datos. Objeto de tipo dataSet (conjunto de datos) que incluye una referencia a los
datos de la base de datos. Al arrastrar una de las tablas al fonnulario de fonna
automática se incluye este elemento, pues contiene totas las tablas de la base de datos
enlazada.

■ articuosBindingNavigator. BindingNavigator es un control del cuadro de


herramientas ubicado en la categoría datos que representa la barra de navegación.
Esta pequeña barra contiene botones que permiten el desplazamiento entre registros
así como el anexado o eliminación.
■ articulosBindingSource. El objeto BindingSource determina el origen real de datos,
es decir, la información se obtiene de la base de datos pero, ¿de qué tabla concreta?
Así este objeto establece la consulta que determina tabla o conjunto de tablas de los
que se va a visualizar la información. Entre las propiedades a configurar para este
objeto tenemos DataSource donde se indica el DataSet a usar y Member donde se
establece la tabla de la base de datos. Al configurar la propiedad Member aparece el
objeto artículosTableAdapter
Capítulo 11. Gestión de bases de datos. Bases de datos orientadas a objetos

■ articulosTableAdapter. Al establecer la propiedad Member en el objeto


BindingSource se genera de forma automática este objeto que aporta mayor
funcionalidad para trabajar con la infonnación.
Cuando establecemos un origen de datos en el cuadro de herramientas observamos una nueva
categoría relacionada con este. En nuestro ejemplo, la categoría se denomina Componentes
BaseDeDatos siendo BaseDeDatos el nombre asignado a nuestro proyecto. En este grupo
encontramos un TableAdapter por cada tabla del origen de datos.

11.4. CREAR UNA APLICACIÓN DE BASE DE DATOS EN


C# PASO A PASO
En el Apartado 11.3 creábamos una aplicación que gestionaba una base de datos de forma
automática, en este vamos a configurar una aplicación y sus formularios incluyendo controles
manualmente y modificando sus propiedades. Partimos de la base de datos de ejemplo Tienda,
con las tablas proveedores, clientes. Artículos y pedidos.
Creamos un nuevo proyecto gráfico y configuramos el primer formulario como se muestra en
la Figura 11.7.
Por lo pronto nuestro menú estará formado por:
Archivo: submenú Salir.

Datos: submenús Tablas.

En Tablas encontramos Clientes, Proveedores,Pedidos y Artículos.


Ayuda.
La barra de herramientas sólo contiene un botón. Salir.
Añadimos el origen de datos como hemos estudiado en el Apartado 11.2.

Archivo Datos Ayuda

&

Figura 11.7. Aplicación de ejemplo.


Al formulario principal añadimos el dataSet que usaremos como origen de datos de toda la
aplicación. Llamaremos al origen de datos TiendaDataSet.
Agregamos nuestro primer fonnulario, al que llamaremos formClientes. Este será accesible
desde el menú Datos -> Tablas -> Clientes.
Diseñaremos este tal y como se muestra en la Figura 11.8.
^ ^° A partir de ahora debemos asignar a cada cuadro de
CLIENTES texto un campo de la tabla clientes para que la infonnación
Reíercnca de Q>erUe i
se visualice a través de ellos y aportar funcionalidad a los
botones para poder desplazamos a lo largo de los registros.
Cudad

Seleccionamos el primer cuadro de texto (Referencia


tJZ del cliente) y accedemos a las propiedades. En el grupo
Datos clic en DataBlnding. Debemos configurar la
I pihiOT I r'ftmor 1 1 | iw ~| proplcdad Text para que esta adopte el valor de los
registros de la tabla clientes. Clic en el valor de Text,
Figura 11.B.^ormulario Clientes
Figura 11.8. Formulario Clientes seleccionar Otros orígenes de datos, a continuación
(Diseño).
Orígenes de datos del proyecto y TíendaDataSet.
Al seleccionar el dataSet aparecerán las tablas que contiene la base de datos, seleccionaremos
la tabla clientes y a continuación el campo que queremos se visualice en el cuadro, es decir
RefCliente.

Visual Studio añade los objetos TíendaDataSet,


__ THVtote T clientesBindíngSource y clíentesTableAdapter. La
^
g;
^ II'RLüiente
Nombre de Cliente ~|
operacióo de asignación de cuadro de texto a campo podría
habcrse rcalízado incluyendo manualmente estos objetos, es
Te ■ decir, agregando el dataSet de nuestro proyecto, configurando
I ^ód postal f el BindingSource para que tome la información de
S ™Lno ~ TíendaDataSet y relacione la tabla clientes y posteriormente
M Pedidos - configurando la propiedad Text a refCliente.
Agregar crigsn de datos del proyecto...
Seleccione un origen de datos en'Otros R63.1ÍZ3.mOS Is míSlTlS. OpCrSClOH SObfC GI TGStO dC CU3.drOS
de texto. Seleccionamos el cuadro Nombre de Cliente y en el
Q Datos (propiedades) clic en Text. Al haberse
Figura 11.9. Selección del origen instanciado un objeto BindingSource, este aparece por defecto
Figura
de datos
de datos para
para el cuadro de texto orígenes de datos, conteniendo acceso a todos los
referencia de cliente. , , ,i i- ..
campos de la tabla clientes.
Así, para indicar que el campo nombre de cliente muestre los nombre de los clientes de la
tabla clientes solo haremos clic en clientesBindíngSource y Nombre de cliente.
Repetimos la operación para todos los cuadros de texto. Si ejecutamos el programa y
accedemos al formulario Clientes veremos en cada cuadro el valor del campo correspondiente
para el primer registro de la tabla. A partir de ahora sólo tendremos que dar funcionalidad a los
botones Primero, Anterior, Siguiente y Último.
..-i a«nt= 1 ^;i3 gj objeto BindingSource dispone de una serie de
Rrferenda de Oerte ^
V»;:<l métodos que nos permiten movemos entre los registros
Nombre
Nombre del
del Oicne
Qierse Affreds
Aíreds Rítefkiae
Puterkute de una tabla. Estos son:
DsBcción 57
Obere Str 57

Cudsd
Código postal
Be^
122S9
MoveFirstO- Desplaza la vista al primer registro de la
íte.™ tabla. Lo usaremos en el botón Primero.
Teléfono
Teléfono C3!>0C7íE2T
C3!>0C7íE21 " " i • l • •
MovePreviousO- Desplaza la vista al registro anterior
Lp""»" i[ ]i I [ !>.■»» i qyg actualmente activo. Lo usaremos en el botón
j\iit0rior
Figura 11.10. Vista del formulario
clientes una vez se ha ejecutado la
aplicación.
Capítulo 11. Gestión de bases de datos. Bases de datos orientadas a objetos

■ MoveNextO- Desplaza la vista al registro siguiente al que está actualmente activo, lo


usaremos en el botón Siguiente.
■ MoveLastO- Para finalizar, este método desplaza la vista al último registro de la
tabla. Lo usaremos en el botón Último.
El formulario presentará el siguiente código fuente:
prívate void btnPrimero_Click(object sender, EventArgs e)

c 1 ientesBindingSource.ttoveFirst();

prívate voíd btnAnterior_Click:(object sender, EventArgs e)


{
clientesBindingSource.MovePrevious();

prívate voíd btnSiguiente_Click(object sender, EventArgs e)


{
clientesBindingSource.MoveNext();

prívate voíd btnUltimo_Click(object sender, EventArgs e)


{
clientesBindingSource.MoveLast();

A continuación vamos a agregar nuevos botones al formulario que permita eliminar y añadir
nuevos registros (los botones se pueden configurar para que en lugar de un texto muestren ima
imagen).
La vista del formulario debe mostrarse de forma similar a la Figura 11.11.
Clientes ^ [ rr. j B Los mótodos dc BiudingSource que
penniten la eliminación e inserción de datos en
Referendfl de Chente EDEa
Referencia de Chente _ la tabla del origen de datos son
Nombredeioente «fredsFüiakide
Nombre dd Chente Affreds Futtefkiste RcmoveCurrent y AddNcw.
Dirección Obeie Str. 57
Obere

Gudad Berfin
Qudad Berfin AddNewQ inserta una nueva fila en la
Código postal
Código postal 12209
12209 I I tabla, eso sí, antes de dejar de usarla debemos
País Alemania
País Alemania I I actuallzar la Infonnaclón para que permanezca
Teléfono 030-0074321 ii t* ^ ' i 'j
en ella. RemoveCurrent eliminara el registro
I 1, i, , 1—^ 1 actual.
^ J Configuraremos los botones Añadir y
Figura 11.11. Formulario dientes modificado. Se Elimina, además del evento FormClosing
han agregado los botones Añadir y Eliminar. como sigue:

NOTA: Existen dos eventos relacionados con el cierre del formulario. El primero de
; ellos es FormClosing. Este se produce antes de cerrar el fonnulario. El otro evento es
FormClosed que se da una vez se ha cerrado el formulario. A la hora de actualizar la
j infonnación de nuestro formulario cliente debemos hacer uso del evento FormClosing, ya
1 que debemos actualizar antes de cerrar la vista.
prívate void btnEliminar Click(object sender, EventArgs e)

clientesBindingSource.RemoveCurrent();

prívate vold btnAnadir Click(object sender, EventArgs e)

clientesBindingSource.AddNew();

prívate vold Clientes_FormClosing(object sender, FormClosingEventArgs e)


{
clientesTableAdapter.Update(neptuno2002DataSet);

Para finalizar nuestro formulario Clientes vamos a agregar un cuadro de texto que pennita
buscar un registro según su posición. La vista del formulario debe ser algo similar a la Figura
11.12.

I-C3 -irB" Al hacer clic en buscar se recogerá el valor


del cuadro de texto desplazándonos al registro
Referencia de dente cuyo número coincida con el especificado.
Nombre del diente
Para realizar esta operación haremos uso de la
propiedad Position. Deben controlarse,
aunque en el ejemplo no se haga ya que nos
Código postd
hemos querido centrar en el uso de la base de
datos, los posibles errores de ejecución,
excepciones que se puedan producir, como un
valor no numérico especificado en el cuadro
de texto o número de registro fuera de rango.
Figura 11.12. Nueva vista del formulario Clientes
con el botón y cuadro de texto buscar. Veamos la codificación del botón Buscar.

prívate void btnBuscar_Click(object sender, EventArgs e)


{
clientesBindingSource.Position = int.Parse(txtBuscar.Text)-1;

ACTIVIDAD 11.1

Añade un nuevo formulario, denomínalo Proveedores. Configura este para que se visualice la
información de los proveedores de nuestra base de datos. Agrega los botones oportunos para el
desplazamiento, eliminación, anexado de nuevos proveedores y búsqueda según índice como
hemos hecho en este apartado para el formulario Clientes. Haz que se muestre al hacer clic en el
menú Datos Tablas -> Proveedores.

ACTIVIDAD 11.2

Repite la Actividad 11.2 para las tablas Pedidos y Artículos.


Capítulo 11 ■ Gestión de bases de datos. Bases de datos orientadas a objetos

11.5. USO DE DATAGRID PARA LA VISUALIZACION DE


DATOS DE BASE DE DATOS EN C#
Muchos programadores optan por visualizar la información en forma de tabla tal y como se
muestra en Access (vista de hoja de datos). Ya hemos visto en el Apartado 11.2 como de forma
automática podemos establecer en un fomiulario la vista de hoja de datos de una tabla a partir de
un objeto DataGrid. Veamos cómo podemos modificar un objeto Datagrid existente o
configurarlo desde el principio.
Para desarrollar el apartado, vamos a crear un nuevo fomiulario en nuestra aplicación de
ejemplo al que denominaremos ClientesDataGrld. Accedemos a él a través de un botón en el
fonnulario Clientes anteriormente creado. Véase la Figura 11.13. Insertamos en el formulario
ClientesDataGrid un control de tipo DataGridView que encontraremos en la categoría Datos
dentro del cuadro de herramientas, véase la Figura 11.14.
'-'r-

y 09 OientesOataGríd

Referencia de Qente

Nombre del Qenle

Código postal

Figura 11.13. Formulario Clientes con el botón _


Tabla en la esquina superior derecha para acceso Figura 11.14. Formulario ClientesDataGrld con el
a formulario con DataGrid. control DataGridView insertado.

1. Renombramos el DataGridView con el nombre ClientesDataGridView.


2. Seleccionamos el DataGridView y hacemos clic en la flecha ubicada en la parte
superior derecha.
3. Configurar la lista Elegir origen de datos de forma que especifiquemos
ClientesBindingsource.
4. De foraia automática el DataGridView se ve modificado, podemos observar en él el
conjunto de campos correspondiente al origen de datos seleccionado.
5. Clic en Editar columnas. Veremos el cuadro de diálogo que nos permitirá modificar
el contenido del DataGrid.

6. La columna refCliente no es necesario que sea visible. Clic en el botón Quitar


previa selección de refCliente.
7. Colocaremos la columna Teléfono justo después de la columna nombre, para ello
hacemos uso de los botones T y >1.
8. Modificamos el nombre de cabecera del campo Cód Cliente por Código cliente, para
ello lo seleccionamos y escribimos el nuevo nombre en la propiedad Header Text.
9. Clic en Aceptar.
"9 ClientesDítaGrid

Tareas de DataGrídView
Nombre de
Gente Elegir origen de datos: clientesBindingSource [vj
Editar columnas...

Columnas seleccionadas: Propiedades de columnas enlazadas

E3 Nombre de Cliente ContextMenuStrip (ninguno)


E3 Dirección MaxInputLcngth 32767
0 Gudad P.eadOnly False
@ Cód postal Resizable Truc
@ País SortMode Automatic
0Teléfono Datos
DataPropcxtyName
Diseño
(Ñame) refCIienteDataGridVIe'AT extí

AutoSizeMode NotSct

(Ñame)
, Indica el nombre utilizado en el código para identificar el
objeto.

:1 subpro<l.iT7:

Figura 11.15. Edición dei controi DataGridView CiientesDataView.

Si lo deseamos, en lugar de visualizar todos los registros podemos ver el resultado de realizar
una consulta sobre ellos. Para ello seguimos los siguientes pasos:
1. Clic para visualizar las tareas de ClieutesDataGridView (flecha en la zona superior
derecha).
2. Clic en Agregar consulta.
3. Aparecerá el cuadro de diálogo Generador de criterios de búsqueda. Por defecto el
dataCridView muestra todos los datos de la tabla, de fonua que la consulta SQL que
se observa en Texto de la consulta en una seuteucia SELECT en la que se incluyen
todos los campos y todos los datos. Clic en Generador de consultas. Vamos a
especificar qué información queremos que se muestre en el DataCridView de
clientes.
4. Para aquellos lectores que estén acostumbrados a realizar consultas en Access la
ventana Generador de consultas les debe resultar familiar ya que es similar a la vista
de diseño de una consulta en Access (aunque los campos y criterios se muestran en
filas en lugar de columnas), en Visual Studio se añade el texto de la consulta por
defecto en la parte inferior, algo que no sucede en la aplicación de Microsoft. Véase
Figura 11.16.
5. Supongamos que sólo queramos mostrar los campos Nombre de Cliente, dirección y
Teléfono de todos los clientes que sean de Madrid. En la zona superior del generador
de consultas se insertan las tablas que van a formar parte de esta, activaremos las
casillas de verificación que coincidan con los campos que coincidan con aquellos que
deban ser visualizados, es decir, en nuestro caso. Nombre cliente. Dirección y
Teléfono.

6. Para establecer los clientes de Madrid debemos incluir el campo ciudad aunque no
visualizaremos este. Clic en la casilla de verificación junto a Ciudad.
Capítulo 11. Gestión de bases de datos. Bases de datos orientadas a objetos

7. En la zona intermedia del cuadro Generador de consultas buscaremos el campo


Ciudad y bajo la columna Filtro para este campo vamos a escribir Madrid.
8. La Ciudad debe estar oculta de fonna que junto a columna, en la fila Resultados
desactivaremos la casilla de verificación.

Generador de consultas

1 I* (Todas las columnas)


^iRefCBente
Nombre de Cliente
5]Dirección
"ZlCiudad

Alias Tabla Resul.. Tipo de orden Criterío de or~. Rl^

[Nombre de C

SELECT RefCíiente,[Nombre de Cuente], Dirección, Ciudad,[Cod postal]. País,Teléfono


FROM Clientes

Ejecutar consulta Aceptar Cancetar

Figura 11.16. Cuadro de diálogo Generador de consultas que permite configurar diferentes vistas a
partir de las tablas aportadas por la base de datos.

9. En la parte inferior, la sentencia SQL debe verse como sigue:


SELECT [Nombre de Cliente], Dirección, Teléfono FROM Clientes WHERE (Ciudad = I
Madrid') _ j
10. Clic en Ejecutar consulta para comprobar que los resultados que obtiene son los
deseados. Clic en Aceptar.
11. En el Generador de criterios de búsqueda antes de hacer clic en Aceptar daremos
nombre a la consulta. Junto al botón de opción Nuevo nombre de consulta
escribimos Madrid como palabra identificativa.
12. Clic en Aceptar.

I NOTA: Si el esquema propuesto presentara fallos agregar todos los campos a la


j consulta especificando la sentencia WHERE para filtrar los datos. A continuación, en el
I DataGridView eliminar las columnas que no se quieran visualizar.

Visual Studio genera una barra de herramientas donde se incluye un botón que permitirá
ejecutar la consulta realizada. Podremos agregar tantos botones como consultas creamos
oportunas.
444 Programación

Tienda

o-J Clientes

Referencia de Cíenle ALFKí

Normre del Qenie /sitreds


CfientesOataGnd
Dirección Obere

Gijdad Berln

Cóvgo postal 12209

País íJemania

Teléfono 03&-007

/na Tn^o Empa... Avda de la Cons . (5)555-4723


rrtoTBO Moreno ... Mataderos 2312 (5)555-3332
Around Ihe Hom 120 Hanover Sq. (71)555-7728
BergiurKÍsanabb... Beryuvsvagen 8 0921-12 34 55
Blauer See Deik .. Forsíertír. 57

BiorKielpéreelfis 24. place IQéber 88.60 15 31

Figura 11.17. Formulario con DataGrid y botón de consulta.


Podemos agregar consultas al componente TableAdapter y posteriomiente generar botones
para que se muestren los resultados estas asociados a cuadros de texto o datagrid.

ACTIVIDAD 11.3

Crea un formulario de consulta para artículos, puedes usar DataGrid, cuadros de texto u otro
control para mostrar la información. Es accesible desde el fonnulario diseñado anteriormente
para los artículos de la base de datos.

ACTIVIDAD 11.4

Crea un consulta a aplicar sobre la tabla artículos y cuyo resultado se muestre en el fonnulario
creado en la Actividad 11.3, en la que se indique que se filtre la infonnación a partir del campo
precio, es decir, solo se muestren los artículos cuyo precio sea inferior a 10 euros.

ACTIVIDAD 11.5

Crea una nueva consulta en la que se visualicen los artículos de la categoría de bebidas.

11.6. BASES DE DATOS ORIENTADAS A OBJETOS


(BDOO)
Decimos que una base de datos orientada a objetos es aquella cuyos datos son objetos
presentando una filosofía diferente a las bases de datos relaciónales que hemos venido usando en
los apartados anteriores.

11.6.1. CARACTERÍSTICAS DE LAS BASES DE DATOS


ORIENTADAS A OBJETOS
Las características de una base de datos orientada a objetos son similares a las vistas en los
lenguajes orientados a objetos:
■ Una base de datos orientada a objetos se diseña como un programa orientado a
objetos pensando en el mundo real, intentan plasmar la realidad de nuestro entorno.
Capítulo 11. Gestión de bases de datos. Bases de datos orientadas a objetos

Cada tabla o entidad que usábamos en nuestras bases de datos relaciónales serán a
partir de ahora objetos de nuestra base de datos.
■ Cada objeto posee un identificador, similar a las claves primarias en las bases de
datos relaciónales, por el que son identificados de forma única.
■ Podemos almacenar objetos complejos sin necesidad de realizar tareas o adaptaciones
especiales.
■ Una base de datos relacional, ya que se encuentra constituida de objetos, aplica
herencia entre ellos, es decir, los objetos pueden heredar unos de otros.
■ El usuario es el encargado de decidir qué elementos fonnarán la base de datos,
atributos y métodos.
■ No será necesario la generación de métodos de acceso a los objetos ya que los
gestores de bases de datos orientadas a objetos (SGBDOO) se encargan de realizar
esta función.

■ Se implementan características de la programación orientada a objetos como


polimorfismo, sobrecarga de funciones, etc.

11.6.2. BASES DE DATOS ORIENTADAS A OBJETOS EN C#

En este apartado seguimos con el uso de Visual Studio y C# para la generación de bases de
datos orientadas a objetos. Vamos a desarrollar un modelo (diagrama de tablas relacionadas entre
sí) a partir de Visual Studio para posteríonnente generar de forma física, real, la base de datos y
hacer uso de ella desde alguna aplicación que conseguiremos.
A la hora de generar bases de datos orientadas a objetos será necesario que hagamos uso de
alguna aplicación de base de datos, y si esta además se integra en Visual Studio nos facilitará
mucho el trabajo. Para entender los apartados posteriores del libro es necesario saber qué vamos
a hacer. Así, a la hora de desarrollar nuestra base de datos orientada a objetos:
1. Debemos tener instalado un gestor de bases de datos que permita almacenar la
información relacionada con entidades, registros, etc. Vamos a instalar Microsoft
SQL Server.

I NOTA: Microsoft SQL Server es un software producido por Microsoft para la gestión ¡
i de bases de datos en el modelo relacional. Es la altemativa que plantea Microsoft a |
I sistemas gestores como Grade o MySQL. Ahora bien, podemos trabajar con objetos I
I usando un almacenamiento en una base de datos relacional, utilizando lo que se i
j denomina un mapeador "objeto-relación" (ORM). El mapeo se produce a nivel de i
i aplicación cliente, no en SQL. ¡

Configuraremos Visual Studio para que podamos acceder a SQL Server desde su
interfaz (automático con la instalación de SQL Express, acceda a la web de Microsoft
y localice infonnación relacionada con el proceso de instalación y configuración).
Crearemos el modelo de entidades.

Desarrollaremos una aplicación que haga uso de la base de datos.


Antes de nada comenzamos la descarga de las herramientas de SQL Server para Visual Studio
desde el sitio web: http://www.microsoft.com/es-es/downIoad/details.aspx?id=36843.
Una vez descargado procedemos a la instalación. Solo tendremos que ejecutar el fichero, el
proceso es automático y sencillo.

11.6.2.1. CREACIÓN DEL MODELO DE ENTIDADES EN C#


Una vez hemos creado un proyecto de consola y hemos instalado adecuadamente SQL Server
Express pasamos a realizar el modelo de entidades. Como su nombre indica esto es simplemente
un modelo de tablas y relaciones, la base de datos no será creada hasta que realicemos otro tipo
de operaciones. Con la creación del modelo obtendremos un fichero SQL para ser ejecutado en
SQL Server Express, tras la ejecución la base de datos sí estará creada. Pero eso será más
adelante, vayamos paso a paso.
1. Clic con el botón secundario del ratón en el proyecto de consola que acabamos de
crear (el proyecto se ha denominado TiendaModelo).
2. Agregar y clic en Clase.
3. En la ventana de anexado de objetos seleccionamos ADO.NET Entity Data Model.
4. Escribimos el nombre ModeloDeTienda y clic en Aceptar.
5. A partir de ahora arranca un pequeño asistente por el que debemos indicar si escoger
el modelo a partir de una base de datos existente o crear este desde cero. Será la
segunda de las opciones la que seleccionaremos. Clic en Modelo vacío y Finalizar.
Un modelo de base de datos constituye el una vista contextual de los elementos que forman
esta, es decir, el modelo vendrá a ser algo más o menos como la estructura de la base de datos. Al
partir de un modelo en blanco debemos añadir todas las tablas y atributos que queramos incluir
en nuestra base de datos.

En la vista de diseño del modelo se hará visible el cuadro de herramientas Entity


Framework. Para insertar una tabla pincharemos y arrastraremos el objeto Entity del cuadro de
herramientas a la zona central. A partir de ahí:
1. Cambiaremos el nombre de la entidad al nombre de la tabla correspondiente en
nuestra base de datos. Seguiremos usando la base de datos con las tablas Proveedores,
Clientes, Artículos y Pedidos. Como primera entidad vamos a configurar la tabla
Artículos. Clic en el cuadro de entidad, en la zona inferior derecha observamos el
cuadro de propiedades del objeto del modelo. Asignamos Artículos a la propiedad
Nombre.

2. Por defecto la entidad incluye un campo clave de tipo numérico. Cambiaremos su


nombre id al de refArticulo. Clic sobre la propiedad id y en el cuadro de propiedades
modificamos la propiedad Nombre con el nuevo valor, refArticulos.
3. Para agregar un nuevo campo a la entidad clic con el botón derecho del ratón sobre
ella. Agregar nuevo y Propiedad escalar.
4. Escribimos como nombre de la nueva propiedad nombreArticuIo.
5. Configuramos la propiedad escalar creada, nombreArticuIo, de forma que asignamos
a longitud máxima el valor 255. Al hacer esta operación estaremos restringiendo el
número de caracteres que se pueden escribir para este campo. El tipo seguirá siendo
Capítulo 11. Gestión de bases de datos. Bases de datos orientadas a objetos

String, si posteriormente insertamos una propiedad tipo precio o cantidad debemos


cambiar el tipo a double o int.
6. Repetir los pasos 1 a 5 para las propiedades nombreCategoria, refProveedor,
precio y cantidadStock.
7. Configuramos cada propiedad Nombre del conjunto de entidades para cada entidad,
de forma que el nombre que especifiquemos aquí es el que usaremos para referir a
todos los registros almacenados de cada tipo de objeto. Para artículos especificaremos
como nombre del conjunto TodosLosArticulos.

ACTIVIDAD 11.6

Configura las entidades Proveedores, Clientes y Pedidos con los campos indicados en el
Apartado 11.1. Configura la propiedad nombre del conjunto de entidades para cada entidad
como sigue: Proveedores TodosLosProveedores, Pedidos TodosLosPedidos y Clientes
-> TodosLosClientes.

Para enlazar las entidades y establecer relaciones entre ellas sólo tendremos que escoger la
henamienta Asociación del cuadro de herramientas Entity Framework.
1. Clic en Asociación. Colocamos el curso sobre el campo de la primera entidad que
queremos relacionar, por ejemplo sobre el campo refProveedores de la tabla
Proveedores.

2. Pinchamos y sin soltar el ratón arrastramos hacia la siguiente tabla, segunda entidad
en la relación. En nuestro ejemplo sobre la entidad Artículos.
3. Colocamos el cursor sobre el campo de la segimda tabla a enlazar y soltamos el ratón;
en nuestro caso, refProveedor de la entidad Artículo.

4. Tras la operación veremos una línea entre ambas entidades. El proceso de asociación
es similar al de establecimiento de relaciones entre tablas de una base de datos de
Access.

Finalmente el diagrama de entidades se verá como en la Figura 11.18.


A continuación sólo queda dar nombre al conjunto de entidades que acabamos de crear en el
modelo de fonna que esta quede definida. Debemos hacer clic sobre cualquier zona vacía del
espacio de trabajo y dirigimos a las propiedades del diagrama. La propiedad que vamos a
modificar se denomina Nombre del contenedor de entidades, le asignaremos el nombre de
EntidadesTienda. Resumiendo, podemos decir que hasta ahora tenemos;
■ Un gmpo de entidades que denominamos EntidadesTienda. Es importante conocer
este dato cuando tengamos que hacer referencia a todo el modelo desde el código
fuente.

■ Unas clases o entidades individuales llamadas Artículos, Proveedores, Clientes y


Pedidos. Cada una de ellas será un posible objeto a almacenar en la base de datos. Es
importante ya que desde el código fuente insertaremos entidades y esto se reduce a la
instanciación de las entidades que forman la base de datos. Para instanciar
necesitaremos conocer el nombre de la clase.
■ A cada grupo de registros de cada tipo, o mejor dicho, cada grupo de entidades se las
denomina además de una fonna característica: Al gmpo de entidades de proveedores
se le llama TodosLosProveedores, al grupo de todos los artículos TodosLosArtículos,
etc. Es necesario tener bien denominados a estos grupos ya que a partir de ellos
realizaremos consultas y extraeremos la información.

- Propiedadev 'Propiedadtrv
Y? rclPfot/eedor yí refArticulo
M NombreProveed.- A nomhrrAtUtulu
^ DirrtíKin ^ NombtrCalpqofia
^ CodigoPo&ial PieCiO

A Ciudad f* CantidadStock
> País A fefProveedor
A Telefono 'Propiedades de nav.^
" Propiedades de nav.. y*!] Piijvrrdtnes
^Artículos Pedido

' Propiedades
yf f«?irii«'n!r
Norr.bie
A Dirección
f* Ciudad
- Propiedades A CodigoPostal
y? rrfPcdido A PO'S
A refArtiCuto Trlí-Ínrvo

f* refCliente ■ Propiedades de nav ,


P cantidad v"^ Pedido
descuento

=■ Propiedades de nav^.
Clientes
^ Artículos

Figura 11.18. Diagrama de entidades.

11.6.2.2. OBTENCIÓN DEL SCRIPT PARA LA CREACIÓN DE LA BASE DE


DATOS EN SOL SERVER EXPRESS
Aunque hemos diseñado el diagrama de tablas y relaciones de nuestra base de datos, este,
hasta ahora, no sirve para mucho ya que el crear el diagrama no indica crear la base de datos. Es
necesario que obtengamos el fichero SQL con la información oportuna para ser ejecutada en un
gestor de base de datos que sí genere esta.
1. Desde el modelo de entidades haremos clic con el botón secundario del ratón desde
cualquier zona vacía. En el menú contextual clic en Generar base de datos desde
modelo...

2. Clic en Nueva conexión.


3. En la lista Origen de datos seleccionamos el origen Microsoft SQL Server. Clic en
Continuar.
4. En Nombre del servidor escribimos .\SQLEXPRESS. Establecemos Tienda como
nombre de la base de datos y continuar.
5. En la ventana principal veremos la cadena de conexión en la zona inferior. Esta
cadena es muy importante ya que establece la conexión con la base de datos. Clic en
Siguiente. Véase la Figura 11.19.
Capítulo 11. Gestión de bases de datos. Bases de datos orientadas a objetos

NOTA: Si durante la conexión se produce algún fallo se debe comprobar que el


servicio Microsoft SQL (SQLEXPRESS) se esté ejecutando correctamente. Para ello
podemos acceder a las herramientas administrativas del sistema operativo y servicios o
bien en Inicio Todos los programas Microsoft SQL Server Herramientas de
configuración Administrador de configuración de SQL Server.

Cadena de conexión de la entidad;

metadata=res://*./TiendaModelo.c5dl|res;/,^/TiendaMcdelo.s5di|
re5://'/TiendaModelo.m5l;provider=System.Data,SqlCiient;provider connection string="data sources:.
\SQLEXPRESS;¡nitia! cataIog=Tienda;integrated
securit)'=True;MültipleActiveResultSets=True;App-EntityFrannework"

Figura 11.19. Cadena de conexión de nuestro modelo de entidades.

6. Pasados unos segundos se visualizará el fichero SQL (script) con las órdenes
necesarias para generar la base de datos correspondiente al modelo creado en el
Apartado 11.6.2.1.
7. Cambiamos el nombre del fichero a ModeloDeTíenda.edmx.sql y clic en Finalizar.

NOTA: Al crear un script SQL antes de ejecutar y crear la base de datos podemos
corregir posibles fallos del modelo.

11.6.2.3. EJECUCIÓN DEL SCRIPT PARA LA CREACIÓN DE LA BASE DE


DATOS

Nonnalmente, nada más finalizar la creación del fichero SQL, será lazada la aplicación SQL
Server que ejecutará el script y generará la base de datos. Aún así, podemos dar orden de ejecutar
el script:
1. Clic con el botón derecho del ratón sobre cualquier zona del script.
2. Clic en Execute SQL.
3. SQL Server se inicia y configura la base de datos relacionada con nuestro modelo.
4. Accedemos al Explorador de servidores (Ver -> Explorador de servidores). Al
desplegar la conexión creada anteriormente, debemos visualizar las tablas
relacionadas con las entidades creadas en nuestro modelo.

i NOTA: Si a la hora de ejecutar la consulta SQL (Script) se produce algún tipo de fallo
' es posible ejecutar esta y conseguir que se generen las tablas a partir del SQL Server
Management studio ubicado en la carpeta de instalación de Visual Studio y accesible
desde el acceso directo en Inicio de este. Desde SQL Server Management veremos las
conexiones creadas y podremos ejecutar el script creando un nuevo fichero SQL y
pegando en él el código SQL creado a partir del modelo de entidades
, (ModeloDeTienda.edmx.sql).
11.6.2.4. USO DE LA BASE DE DATOS

Una vez se han ejecutado los pasos descritos en los Apartados 11.6.2.1 a 11.6.2.3 vamos a
desarrollar una pequeña aplicación de consola que nos permita usar la base de datos de nuestra
tienda.

1. Codificaremos el fichero Programs' de nuestra aplicación. Recordamos que llamamos


al grupo de entidades con el nombre EntidadesTienda. Al conjunto de registros de
una tabla lo llamo TodosLosNombre, siendo Nombre el nombre asignado a cada
tabla y las tablas tenían los nombres Artículos, Proveedores, etc.
2. Escribiremos el siguiente código fuente. Vamos a insertar un registro en la tabla de
Artículos.

EntidadesTienda entidades = new EntidadesTienda(); //(l)

Articulo nuevoProd = new Articulo(); //(2)


nuevoProd.refArticulo = 1;
nuevoProd.nombreArticulo = "Pera de agua";

nuevoProd.precio = 1.3;

entidades.TodosLosArticulos.AddObject(nuevoProd); //(3)

entidades.SaveChangesO ; //(4)

■ //(l). Creamos una instancia del modelo, es decir la variable entidades


representa todas las tablas y elementos del modelo.
■ //(2). Creamos un objeto de tipo Articulo (tabla de artículos).
■ //(3). Agregamos el nuevo artículo al conjunto de artículos (registros),
almacenados en la tabla, TodosLosArticulos. Podemos considerar a Artículo
como un registro de la tabla y TodosLosArticulos como la tabla en sí en un
modelo de bases de datos relacional.

■ //(4). Guardamos los cambios en el modelo de forma que el nuevo registro


permanezca en la base de datos.
Si en el explorador de servidores accedemos a la base de datos y a la tabla artículos
(TodosLosArticulos) al mostrar la vista de datos observaremos la nueva entrada.
Para eliminar un registro de la base de datos debemos crear una variable del tipo de registro a
eliminar con los valores de este y usar el método Remove. El código seria similar al de añadir.

EntidadesTienda entidades = new EntidadesTienda(); //(1)

Articulo prodBorrar = new Articulo(); //(2)


nuevoProd.refArticulo = 1;
nuevoProd.nombreArticulo = "Pera de agua";

'Es posible crear varios proyectos en la aplicación, usar referencias y organizar mucho mejor nuestro
trabajo.
Capitulo 11 ■ Gestión de bases de datos. Bases de datos orientadas a objetos 451

; nuevoProd.precio =

entidades.TodosLosArticulos.Remove(nuevoProd); //(3)

entidades.SaveChangesO; //(4)

■ //(l). Creamos una instancia del modelo para poder acceder a sus elementos.
■ //(2). Se crea una variable de tipo artículo (registro artículo). A la hora de eliminar
debemos indicar qué objeto borrar, por eso cargamos las propiedades de Artículos
con los valores oportunos para posteriormente localizar este registro en la tabla.
■ //(3). Damos la orden de borrar. Pasaremos como parámetro el objeto creado, si este
se encuentra en la tabla será eliminado.

■ //(4). Al igual que al añadir un registro, para que la eliminación perdure debemos
aplicar los cambios a partir del método SaveChanges().
La generación de consultas puede realizarse de diferentes maneras, una de ellas es a través de
la creación de procedimientos almacenados.
1. En el explorador de servidores clic con el botón derecho del ratón en la carpeta
Procedimientos almacenados y Agregar nuevo procedimiento almacenado.
2. Veremos una nueva ventana con la sentencia CREATE PROCEDURE.

3. Llamaremos al procedimiento devolverCategoria. La cabecera del método es ahora


similar a CREATE PROCEDURE dbo.devolverCategoria. El código completo se
muestra a continuación.

CREATE PROCEDURE dbo.devolverCategoria

@nomCategoria nvarchar(255);

AS

SELECT refArticulo, nombreArticulo, nombreCategoria,refProveedor,


precio, cantidadStock
FROM Entidades.TodosLosArticulos
WHERE nombreCategoria = 0nomCategoria
I

4. Para añadir la función al modelo, cíic en él con el botón derecho del ratón en
cualquier zona vacía, en el menú contextual Add e Functionimport.
5. Seleccionamos el procedimiento que acabos de crea y agregamos este.
6. Para hacer uso de la función y modelo creado en el código escribiremos:
: List<Articulo> sqlCategorias = entidades.devolverCategoria("Fruta").ToListO;

Podemos generar la consulta a partir del Query Builder. Clic en la zona en blanco del procedimiento e
Inserí SQL.
11.6.3. GESTOR DE BASE DE DATOS ORIENTADO A OBJETOS
PARA JAVA

Hemos estudiado cómo generar una base de datos de objetos y clases en C# y hemos usado un
gestor que como ya veíamos no es un gestor de bases de datos orientado a objetos, hacíamos uso
de un mapeado. En este apartado sí vamos a usar un gestor de bases de datos orientados a objetos
en combinación con el lenguaje de programación Java. No existe gran variedad de SGBDOO en
relación con los gestores de bases de datos relaciónales, algunos de ellos son; db4o,
Objetivity/DB o EyeDB. En este libro hemos optado por el uso de db4o, SGBDOO con licencia
GPL.

NOTA: Existe una versión de db4o para la plataforma .NET, es posible descargarla
desde su web oficial, sin embargo no es compatible con la versión 2012 de Visual Studio.
I La última versión de db4o para .NET requiere la versión 2010 de Visual Studio.

Podemos descargar el gestor tanto para usar con Java como con cualquiera de los lenguajes de
la plataforma .NET desde su web oficial http://www.db4o.com/.
Ya que vamos a trabajar en Java, en el apartado Download de la web de db4o
seleccionaremos el botón de opción relacionado con la versión db4o 8.0 para Java (en el
momento de edición del libro esta es la que está vigente) y haremos clic en Download.
Tras finalizar la descarga instalamos el gestor. El siguiente paso será la configuración de
Netbeans para usar db4o a partir de este IDE.
11.6.3.1. CONFIGURAR EL PLUGIN DB40 EN NETBEANS Y AGREGAR
LIBRERÍA DB40 A NUESTRO PROYECTO
Antes de comenzar a crear un nuevo proyecto y objetos de la base de datos vamos a
configurar el plugin db4 para NetBeans. Seguiremos estos pasos:
1. Antes de nada se debe proceder a la descarga del plugin, accedemos a la dirección
web: https://code.google.eom/p/db4o-netbeans/downloads/list.
2. Clic en el enlace db4o-netbeans_0.6.0_NB6.zip (hasta el momento es el último
plugin de db40). Para versiones de NetBeans 6.0 en adelante.
3. El fichero es un .ZIP, lo descomprimimos en una carpeta y accedemos ejecutamos
NetBeans.

4. Una vez en el IDE, clic en Tool y plugins.


5. Aparecerá el cuadro de diálogos donde se muestran plugins instalados, disponibles y
se permitirá localizar plugin a través de la red. Clic en la pestaña Downloaded.
6. Clic en el botón Add plugins... Aparecerá una ventana del explorador de Windows.
7. Buscamos el directorio donde hemos almacenado previamente los ficheros
descomprimidos y añadimos todos los ficheros con extensión .nbm que veamos.
8. Una vez los plugin aparecen en la lista bajo el botón Add plugin, clic en Install.
9. Tras unos segundos el proceso de instalación habrá finalizado.
Capítulo 11. Gestión de bases de datos. Bases de datos orientadas a objetos

AGREGANDO LIBRERIA DB40

1. Tools y Ant Librarles.


2. New Librarle nombre db4o

3. Seleccionamos la nueva librería para configurarla. Estando activada la librería y


pestaña CLASSPATH clic en Add Jar/Folder.
4. En la carpeta descomprimida de db4o para Java seleccionamos la carpeta lib, fichero
db4o*-all-java5.jar y añadir Jar/forlder. Podemos añadir las carpetas o jar
coiTespondientes en las pestañas Sources y Javadoc.
5. Clic en OK

6. Añadimos a nuestro proyecto la librería. Clic con el botón derecho en la carpeta


librarles del proyecto. Añadir Librery. Clic en db4o y add library.
7. Ya podemos usar la API de bd4o.
11.6.3.2. CREACIÓN Y CONFIGURACIÓN DE UN NUEVO PROYECTO DE
BASE DE DATOS

Lo primero que debemos hacer es generar un nuevo proyecto, este puede ser en modo consola
o gráfico, para nuestras explicaciones se ha optado por el uso de un proyecto visual.
Además, debemos indicar que usaremos db4o, así se debe incluir la librería db4o al proyecto.
1. Creamos un nuevo proyecto en modo gráfico al que llamaremos BasesDeDatosOO.
2. En el explorador de proyecto, clic con el botón derecho del ratón en la carpeta
librarles para el proyecto que acabamos de generar.
3. Clic en Add Library.
4. En el cuadro de diálogo debemos localizar la librería db4o. Clic en ella y clic en add
Library.

11.6.3.3. CONFIGURACIÓN DE LAS CLASES DE NUESTRA BASE DE DATOS


Antes de comenzar a usar el gestor y establecer relaciones entre objetos y demás operaciones
debemos crear las clases que usaremos en nuestra base de datos. Recordamos en el ejemplo de
gestión de nuestra tienda que disponíamos de cuatro tablas: Proveedores, Clientes, Pedidos y
Artículos, vamos a codificar cada una de ellas como un objeto independiente que tendrá como
propiedades los campos que veíamos en las bases de datos relaciónales. Recordad que siempre se
debe incluir un campo identificativo.
1. Clic con el botón secundario del ratón sobre el paquete de nuestra aplicación y New
Java Class.

2. Llamaremos a la clase Proveedores y escribiremos el código que se muestra a


continuación.

public class Proveedores { j


prívate int refProveedor; I
prívate Strlng nombreProveedor; ¡
prívate Strlng dirección; ;
prívate Strlng ciudad; j
prívate String codPostal;
prívate String pais;
prívate String telefono;

publlc Proveedores(Int referencia, String nombreProveedor){


thls.refProveedor=referencia;
thls.nombreProveedor=nombreProveedor;

publlc Proveedores(Int refProveedor, String nombreProveedor, String


dirección, String ciudad, String codPostal, String pais, String telefono) (
thls.refProveedor = refProveedor;
thls.nombreProveedor = nombreProveedor;
thls.dirección = dirección;
thls.ciudad = ciudad;
thls.codPostal = codPostal;
thls.pais = pais;
thls.telefono = telefono;
}

@Overrlde
publlc String toString() {
return "Proveedores{" + "refProveedor=" + refProveedor +
", nombreProveedor=" + nombreProveedor + ", direccion="
+ dirección + ", ciudad=" + ciudad + ", codPostal=" + codPostal
+ ", pais=" + pais + ", telefono=" + telefono +

Int getRefProveedor() { pübiic vold setCiudad(String ciudad) {


return refProveedor; thls.ciudad = ciudad;
} }
publlc vold setRefProveedor(Int publlc String getCodPostal() {
refProveedor) { return codPostal;
thls.refProveedor }
^ = refProveedor; publlc vold setCodPostal
(String codPostal) {
publlc String getNombreProveedor() thls.codPostal = codPostal;
}
^fiturn nombreProveedor; publlc String getPaisO {
return pais;
}
publlc void setNombreProveedor publlc vold setPais(String pais) {
(String nombreProveedor) { thls.pais = pais;
thls.nombreProveedor )
^ = nombreProveedor; publlc String getTelefono () {
return telefono;
publlc String getDireccion() { }
return dirección; publlc vold setTelefono
(String telefono) (
thls.telefono = telefono;
publlc vold setDireccion
(String dirección) {
thls.dirección = dirección;
}
publlc String getCiudadO {
return ciudad;
Capítulo 11. Gestión de bases de datos. Bases de datos orientadas a objetos

3. A continuación creamos la clase Artículos. Esta es similar a proveedores, en cuanto a


tipos de métodos a introducir. Los campos de Artículos serán: refartículo (int),
refroveedor (String), nombreArticulo (String), nombreCategoria (String), precio
(double) y unidadesExistencia (int).
4. Realizamos la operación para generar la clase Cliente, esta estará compuesta de los
campos: refCliente (int), nombreCliente (String), dirección (String), ciudad (String),
codPostal^ (String), pais (String) y telefono (String).
5. Para finalizar generaremos la clase Pedidos. Los campos que se deben incluir son:
refPedido (int), refCliente (int), refArticulo (int), cantidad (int), descuento (double) si
lo tiene y fechaPedido (Date).
Ya que estamos configurando una aplicación gráfica vamos a eliminar la clase principal
generada por defecto y a agregar un JFrame al que denominaremos Inicio.

NOTA: Al eliminar la clase principal generada por defecto con el nuevo proyecto
debemos especificar qué clase vamos a usar a partir de ahora como primera clase.
Accedemos a las propiedades del proyecto haciendo clic con el botón derecho sobre su
nombre en el explorador de proyectos y Properties. En la categoría Run cuadro de texto
Main.class especificamos el nombre del nuevo formulario (Inicio).

11.6.3.4. CONFIGURACIÓN DE DE VARIABLE DE ACCESO A LA BASE DE


DATOS

A la hora de trabajar con bases de datos, básicamente siempre se repetirá la misma secuencia
de pasos:
Paso 1: Abrir la base de datos.

Paso 2: Realizar operaciones sobre los datos.


Paso 3: Cerrar la base de datos.

Una vez se han configurado los objetos que vamos a almacenar en nuestra base de datos
debemos crear esta o abrirla en caso de que haya sido creada con anterioridad (la operación de
apertura conlleva una creación de la base de datos en caso de que esta no exista).
En db4o la operación de apertura se realiza a partir del método Db4oEmbedded.opeiiFUe.
Este método devuelve un objeto de tipo ObjectContainer que representará nuestra conexión a la
base de datos durante toda la aplicación.
El método openFlIe precisa de dos parámetros. El primero de ellos se refiere al tipo de
configuración que podemos obtener a partir de Db4oEmbedded.NewConfigurationO y el
segundo el nombre db4o que vamos a dar a nuestro fichero de base de datos.
Así, en nuestro proyecto, vamos a crear una variable estática que pueda ser usada en cualquier
fonnulario y que identifique la base de datos que llamaremos Tienda.db4o. Esta variable será
creada como atributo de la clase Inicio como se muestra a continuación:

^ Aunque los campos codPostal y teléfono se usan para almacenar números no vamos a realizar
operaciones aritméticas con estos, de forma que es conveniente declararlos como cadenas de caracteres ya
que así ocuparán menos espacio en memoria.
import com.db4o.Db4oEnibedded;
import com.db4o.0bjectContainer;

piiblic class Inicio extends javax .swing.JFrame {


public static ObjectContainer bsTienda;

piiblic static void main(String args[]) {

java.awt.EventQueue.invokeLater(new RunnableO {
public void run () {
bsTienda=
Db4oEmbedded.openFile(Db4oEmbedded.newConfiguration()
,"Tienda.db4o");
new Inicio().setVisible(true);

Debemos aseguramos de que la base de datos es cerrada correctamente, así, vamos a añadir el
método de cierre en el evento closing para el formulario inicial tal que al finalizar la aplicación la
base de datos también sea cerrada.
~ —

prívate void formWindowClosing(java.awt.event.WindowEvent evt) { ;


bsTienda.cióse O; '

11.6.3.5. INSERTAR DATOS EN LA BASE DE DATOS TIENDA

Ya que estamos configurando una base de datos de objetos, a la hora de insertar un elemento
en la base de datos es necesario crear una instancia de la clase que se va a agregar. Vamos a
comenzar a añadir objetos de tipo Proveedores a nuestra conexión. Para ello, vamos a configurar
un formulario como se muestra en la Figura 11.20.

Referenda de proveedor

Nombre de proveedor f"


Direcdón

Códiqo Postal

Telefono

Insertar

Figura 11.20. Vista del formulario para insertar proveedores.


Capitulo 11. Gestión de bases de datos. Bases de datos orientadas a objetos

MenuTlenda [JMenuBar]
Archivo Insertar Ver Buscar Ayuda 1
É Archivo [3Menu]
i ■t^ Salir [JMenuItem]
B Insertar [JMenu]
"0 MenuProveedores [JMenuItem]
Ver [JMenu]
i MenuVerProveedores [JMenuItem]
0 Busrar [JMenu]
1 MenuBuscarRefProveedor [JMenuItem]
i i - -1^ MenuBuscarProveedoresQudad [JMenuItem]
' ^ Ayuda [JMenu]

Figura 11.21. Vista del formulario principal.


Figura 11.21 bis. Intems de la barra de menús.
Al hacer clic en el botón Insertar se agregará un nuevo proveedor a la base de datos. Vamos a
agregar el siguiente código al control:
(

prívate void btnInsertarActionPerformed (java. awt.event. ActionEvent evt) { ;


Proveedores nuevo; ¡
//Recogemos los datos de los cuadros de textos 1
int ref=Integer .parseint (tJiis.txtReferencia.getText O ); Í
String nom=this.txtNombre.getText() ; 1
String direccion=this.txtDireccion.getText() ; ¡
String ciudad=this.txtCiudad.getText() ; ;
String codPostal=this.txtCodPostal.getText() ; ¡
String pais=tJiis . txtPais . getText () ; ;
String telefono=this . txtTelefono. getText () ; >'
I
I

//Creamos un Nuevo proveedor a partir de los datos recogidos de los |


cuadros de texto 1I

nuevo=new Proveedores (ref, nom, dirección, ciudad, codPostal,pais, telefono) ;


I
I
I

//Almacenamos el nuevo proveedor en la base de datos ¡


Inicio.bsTienda.store(nuevo) ; ¡
I
t
I

//Preparamos los cuadros para que se puedan insertar más registros 1


this.txtReferencia.setText("") ; j
this.txtNombre.setText("") ; 1
this . txtDireccion. setText (""),* 1
this.txtCiudad.setText("") ; |
this.txtCodPostal.setText("") ; ¡
this.txtPais.setText("") ; 1
this.txtTelefono.setText("") ; !
} i
La función store(Objeto) se encargará de almacenar las diferentes instancias de objetos en la
base de datos que hace la llamada al método.

ACTIVIDAD 11.7

Crea un fonnulario de inserción para los objetos restantes Clientes, Pedidos y Artículos.
NOTA: La actualización de registros de la base de datos se lleva a cabo mediante la
extracción del registro en cuestión, modificación de sus propiedades y posterior
almacenamiento.

11.6.3.6. VISUALIZAR EL CONTENIDO DE LA BASE DE DATOS

La visualización del contenido de la tabla se lleva a cabo a través de la realización de una


consulta a la base de datos. Se hará uso del método queryByExample(Objeto) correspondiente
al objeto ObjectConteiner o variable que permite el acceso a la base de datos. El método
devuelve el conjunto de registros resultado de la consulta, más concretamente una variable de
tipo ObjectSet.
Con el fin de entender el funcionamiento de queryByexampIe(Objeto) crearemos dos nuevos
formularios, uno de ellos mostrará todos los registros almacenados en la tabla de proveedores y
el otro visualizará aquellos registros cuya ciudad coincida con la que se especifique en un cuadro
de texto. Veamos las interfaces gráficas a usar.

[ Refere... Nombre Direcdón Código... Ciudad Teléfono

Referenoa Notrdx'e Drecoón Códgo... Dudad Pats Teléfono ;

Figura 11.23. Vista del formulario para ver los


proveedores de la ciudad especificada.
Figura 11.22. Vista del formulario para ver
todos los proveedores, accesible desde el menú
ver -> Proveedores.

Ambos formularios contienen un objeto Jtable para la visualización de la infonnación.


El código a especificar para visualizar todo el conjunto de proveedores es el siguiente:
/*E1 conjunto de proveedores es cargado al iniciar el formulario por eso se
incluye en el método constructor*/
public VerProveedores() {
initComponents();

Proveedores auxProv;
ObjectSet GrupoDeProveedores =
Inicio.bsTienda.queryByExample(new Proveedores(O,nuil)); //(I)
DefaultTableModel modelo =
(DefaultTableModel) tablaProveedores.getModel();
while (GrupoDeProveedores.hasNext()){ //(2)
auxProv=(Proveedores) GrupoDeProveedores.next(); //(3)
String []fila=new String[7];
fila[0] = String.valueOf(auxProv.getRefProveedor O);
fila[1]=auxProv.getNombreProveedor();
fila[2]=auxProv.getDireccion();
fila [3]=auxProv.getCodPostal();
Capítulo 11 ■ Gestión de bases de datos. Bases de datos orientadas a objetos

fila[4]=auxProv.getCiudad();
fila[5]=auxProv.getPais();
fila[6]=auxProv.getTelefono();
modelo.addRow(fila);

//(l). Ejecutamos el método queryByExample. Este requiere que se pase como


parámetro el objeto que debemos buscar para visualizar. Si introducimos como
parámetro un objeto vacio, queryByExample devolverá todos los objetos del tipo
especificado almacenados en la base de datos. Al escribir queryByExample(new
Proveedores(0,null)); estamos indicando que devuelva cualquier objeto de tipo
proveedores almacenado en la base de datos. El objeto que se obtiene tras ejecutar la
función es de tipo ObjectSet, en este ejemplo, un conjunto de instancias de la clase
proveedores.
//(2). El método hasNextQ devuelve un valor booleano que determina si existen o no
elementos en el conjunto de registros. Normalmente este método se usa en
colecciones y permite recorrer una colección completamente.
//(3). El método nextQ devuelve el siguiente proveedor u objeto en el conjunto de
registros extraídos por la función queryByExaple. Podríamos decir que es la lectura
del siguiente elemento.
Las siguientes líneas se refieren al objeto Table de forma que se configura un array de
cadenas que contiene cada valor del registro leído para posteriormente ser añadido a
la tabla mediante el método addRowO.

prívate void btnCiudadActionPerformed(java.awt.event.ActionEvent evt) {


Proveedores auxProv;
ObjectSet GrupoDeProveedores =
Inicio.bsTienda.queryByExample(new
Proveedores(O,nuil,nuil,txtCiudad.getText(),nuil,nuil,nuil));

DefaultTableModel modelo =
(DefaultTableModel) tablaProveedores.getModel();

while (GrupoDeProveedores.hasNext()){
auxProv=(Proveedores) GrupoDeProveedores.next();
String []fila=new String[7];
fila[0] = String.valueOf(auxProv.getRefProveedor O);
fila[1]=auxProv.getNombreProveedor();
fila[2]=auxProv.getDireccion();
fila[3]=auxProv.getCodPostal();
fila[4]=auxProv.getCiudad();
fila[5]=auxProv.getPais();
fila[6]=auxProv.getTelefono O ;
modelo.addRow(fila);
La línea
— — — — - -------- -- -- -- -- -- -- -- -- - — — — -— — ^

ObjectSet GrupoDeProveedores = ■
Inicio.bsTienda.queryByExample(new I
Proveedores(O,nuil,nuil,txtCiudad.getText(),nuil, nuil,nuil)); ;

Obtiene los proveedores de la base de datos cuyo campo ciudad sea igual a
txtCiudad.getTextO, es decir, a la ciudad que el usuario especifique en el cuadro de texto
txtCiudad.

ACTIVIDAD 11.8

Crea los formularios que desees para visualizar Clientes, Artículos y Pedidos. Puedes
desarrollar diferentes interfaces para la realización de consultas en las que tengas en cuenta
diferentes criterios, por ejemplo proveedores con código postal X, Artículos con precio inferior a
10 euros, etc.

11.6.3.7. ELIMINACiÓN DEL CONTENIDO DE LA BASE DE DATOS

Al igual que las operaciones vistas en los anteriores apartados, la eliminación es una
operación sencilla en el tipo de base de datos que estamos tratando.
Para eliminar un registro de nuestra base de datos debemos:
1. Buscar el registro o conjunto de registros en la base de datos. Para ello haremos uso
del método queryByExample.
2. Usar la función next para apuntar al registro a eliminar.
3. Usar la función deiete incluyendo entre paréntesis el objeto obtenido a partir del
método next, es decir objeto a borrar.

public void EliminarProveedor(String nombre){


ObjectSet conjuntoDeProveedores =
bsTienda.queryByExample(new Proveedores(O,nombre);
Proveedores auxProv = conjuntoDeProveedores.next();
bsTienda.deiete(auxProv);

ACTIVIDAD 11.9

Añade un formulario llamado eliminarProveedores en el que incluyas una lista con todos los
campos de la tabla Proveedores, un cuadro de texto, un botón consultar y un objeto Jtable de
forma que el usuario pueda seleccionar un campo de la lista, escribe un valor en el cuadro de
texto y al pulsar el botón se muestre en el Jtable los registros que cumplen la condición
especificada. Inserta un botón eliminar tal que al hacer clic sobre el registro obtenido, este sea
eliminado.
Capítulo 11. Gestión de bases de datos. Bases de datos orientadas a objetos

COMPRUEBA TU APRENDIZAJE
1. ¿Qué entiendes por base de datos relacional? ¿Qué elementos la forman?
2. ¿Qué pasos hemos de seguir para conectar nuestro proyecto con una base de datos
relacional en C#?

3. ¿Qué utilidad presenta un objeto de tipo DataSet? ¿Y un objeto BindingSource o


TableAdapter? Recordad que son objetos propios de C#.
4. ¿Qué entiendes por base de datos orientada a objetos?
5. ¿Cuáles son las características de una base de datos orientada a objetos?
6. ¿Cómo se genera un modelo de entidades en C#? ¿Qué características deben configurarse
para posterionnente usar en el código fuente?
7. Una vez se genera el modelo de entidades, ¿ya está creada la base de datos? En caso de
que respondas negativamente explica qué se debe hacer acto seguido.
8. ¿Qué es db4o? ¿Qué debemos hacer para usar esta librería en Netbeans, concretamente en
nuestro proyecto?
9. ¿Cómo accedemos a las entidades de nuestra base de datos orientada a objetos en Java y
C#?

10. ¿Qué tipo de operaciones realizamos en bases de datos orientadas a objetos? ¿Qué
métodos usamos en C# y Java?

ACTIVIDADES DE AMPLIACION
1. Supongamos que hemos diseñado en papel, siguiendo las instrucciones del módulo Bases
de Datos, una base de datos relacional en la que encontramos Persona tal que de ellas
derivan las clases Estudiante, Becario y Trabajador. Desarrolla la base de datos en Access
y posterionnente crea en Visual Studio ima aplicación que inserte, elimine, modifique,
busque, etc., los datos de esta base de datos relacional.
2. Crea en Netbeans, con el uso de db4o una aplicación que gestione la base de datos de la
Actividad de Ampliación 1 como una base de datos orientada a objetos.
3. Desarrolla en C# el modelo de clases de la base de datos de las Actividades de
Ampliación 1 y 2 tal que configures cada propiedad concienzudamente. Crea una péqueña
aplicación de consola en C# que la gestione.
4. Crea una nueva base de datos orientada a objetos en la que gestiones la información
relacionada con un zoo no demasiado grande. Debes incluir personas, tal que tenemos
trabajadores del zoo, usuarios (clientes, personas que acceden al zoo) y socios. Además,
los animales se clasifican en acuáticos, terrestres y voladores. Debes clasificar cada
animal del zoo en una de estas categorías. Realiza la codificación en Java y C#.
5. Tras aseguramos que la base de datos de animales del zoo está abierta, contiene
infonnación,^ realiza una serie de consultas sobre ellas, por ejemplo, algún animal
proviene de África, cuantos tipos de monos existen y en qué se diferencian, etc.
Desarrolla una base de datos para gestionar el servicio de comedor de un colegio. Es
necesario apuntar a cada niño o niña que se queda a comer en el comedor del centro así
como qué ha comido. Se deben generar al menos las clases niño y comida. Una vez que el
modelo y las clases de la base de datos estén bien diseñados, crea una aplicación que
realice todas las operaciones necesaria sobre la base de datos: insertar alumno, modificar
alumno, eliminar alumno, establecer comida para cada niño, etc.
RESUMEN DEL CAPITULO

No podemos finalizar el estudio de lenguajes de programación como C# y Java sin


usar algunas de sus colecciones para el almacenamiento de objetos. Aunque hemos
estudiado modelos de bases de datos orientadas a objetos en el Capítulo 11 y el
almacenamiento persistente a través de ellas, las colecciones representan otra opción,
junto al almacenamiento en ficheros binarios de tratamiento de información.
12.1. COLECCIONES
Los datos que se encuentran relacionados entre sí se pueden tratar con más eficacia si se
agrupan en una colección. Con el uso de colecciones en lugar de crear código independiente para
cada objeto individualmente se usa el mismo código para procesar todos los elementos de la
colección.

Aunque no hemos definido colecciones si hemos comenzado a usar su esencia en algunas


actividades, cuando creábamos listas de objetos y desarrollábamos funciones relacionadas con la
inserción, eliminación, modificación, etc., de los objetos de dicha estructura.
Existen diferentes tipos de colecciones, estas se generan a partir de las colecciones propias de
datos que conocemos, tales como tablas, pilas, colas o listas.

12.2. COLECCIONES EN C#
Todas las colecciones que vamos a ver en este lenguaje de programación derivan de
ICollection, IList o IDictionary. A su vez IList e IDictionary derivan de ICollection de forma
que ya sea directa o indirectamente todas las colecciones derivarán de ICollection. Entre los tipos
de colecciones que implementan ICollection, IList o IDictionary encontramos:
■ Array.
■ ArrayList y List.
■ Hashtable y Dictionary.
■ SortedLlst y SortedDictionary.
■ Queue.
■ Stack.

12.2.1. ARRAY
La clase Array no se encuentra en el espacio de nombre System.Collection (la localizamos en
System), pero aún asi, es considerada como una colección ya que implementa IList. Proporciona
métodos para la creación, manipulación, búsqueda y ordenación de arrays o matrices, se usa
como clase base para todas las matrices.
En una Array:
■ Un elemento del Array es un valor concreto del Array.
■ La longitud es el número de elementos que puede contener.
■ El rango establece el número de dimensiones, por ejemplo si rango es igual a dos
tendremos un Array de dos dimensiones.
■ El limite inferior del Array en una dimensión es el índice inicial del Array para esa
dimensión.

■ El limite superior del Array en una dimensión es el índice del último elemento en esa
posición.
Capítulo 12. Colecciones 465

Básicamente, el objeto Array proporciona mayor funcionalidad a los tipos de datos


compuestos array ya vistos en este libro. Véase el siguiente ejemplo.
static void Main(stringí] args) i
{ i
int[] numeres = new int[] { 2,1,7,4,9,8,10}; j
int[] copiaNumeros=new int[10]; !
Array.Sort(numeres); i
Array.Cepy(numeres, cepiaNumeres, 3); I
) j
■ Array.SortO. Método que permite ordenar un array dado. En el ejemplo ordena un
anay de números enteros.
■ Array.CopyO- Permite copiar un array dado en otro.
Podemos usar la clase Array y todos sus métodos sobre los tipos de datos primitivos. Si
queremos que nuestros propios tipos de datos puedan ser ordenados, copiados, etc., a partir de la
clase Array, será necesario adaptar alguna que otra función en nuestra clase para que sean
tratados como los tipos básicos. Por ejemplo, supongamos que hemos creado una clase
denominada Persona y en una aplicación queremos desarrollar un Array de este tipo, si
quisiéramos usar el método Sort sobre el array de Personas creado no podríamos ya que no existe
en la clase un método por el que se indique a Sort el modo de ordenación, es decir, el método
Sort de Array realiza la ordenación comparando cada elemento del array, realiza una ordenación
QuictSort, si no indicamos en Persona como comparar dos objeto Persona no podremos ordenar
el array. Veamos un pequeño ejemplo.
class Persona public string Apellidos
{
string nombre;
string apellidos;
return apellidos;
public Persona O
{
nombre =
apellidos = apellidos = valué;
)
public Persona(string nombre,
string apellidos) public string ToStringO
{ {
this.nombre = nombre; return "Nombre:" + nombre +
this.apellidos = apellidos; " Apellidos:" + apellidos;
} }
public string Nombre }//Fin clase Persona

return nombre;

nombre = valué;

Persona}] listaPersonas = new Persona[3];


listaPersonas[0] = new Persona("Isabel", "Jiménez");
listaPersonas[1] = new Persona("Juan", "Aznareta");
; listaPersonas[2] = new Persona("Marta", "Garrido");
i_ Array.Sort(listaPersonas); //ERROR
Si en la función Main de nuestro proyecto usamos Array.Sort tal y como se muestra algo
más arriba, el compilador lanzará un error antes de que podamos ejecutar el programa, debido a
que listaPersonas es un array de Personas y esta clase no implementa ningún método de
comparación que pueda usar Sort para ordenar un array de este tipo. La clase Persona debe
implementar el método CompareTo o Comparer de las interfaces ¡Comparable e IComparer,
Sort usará uno de estos métodos para comparar los elementos del Array.
Así, la clase debe ser modificada como sigue;
class Persona:IComparable

pxiblic int CompareTo(Object b)


{
Persona aux = (Persona)b;
int compara = this.apellidos.CompareTo(aux.Apellidos)
return compara;

■ Indicamos que vamos a implementar la interfaz IComparable: class


PersonarIComparable.
■ Implementamos el método CompareTo para la clase Persona. El método CompareTo
pasa como parámetro una variable de tipo Object de forma que así tenemos que
establecer la cabecera aunque vayamos a usarlo con el tipo Persona. En el cuerpo del
método crearemos una variable auxiliar a la que asignaremos el objeto convertido a
Persona y posteríonnente se realizará la comparación oportuna. El método
CompareTo debe devolver un valor entero. En nuestro código comparamos los
apellidos de cada persona.
Otra posible modificación consiste en la implementación de la interfaz IComparer en lugar de
IComparable, en este caso el código debe adaptarse del siguiente modo:
— 1
class Persona:IComparer i

public int Compare(Object a, Object b) I

Persona auxA = (Persona)a; |


Persona auxB = (Persona)b; i
return a.Apellidos.CompareTo(b.Apellidos); •

Para finalizar, veamos algunos de los métodos y propiedades característicos de la clase Array:
■ Length. Propiedad. La hemos usado con frecuencia. Devuelve el número total de
elementos de todas las dimensiones del Array. Encontraremos otra propiedad similar
a esta, LongLength. La primera de ellas devuelve el número como entero de 32 bits
mientras que la segunda devuelve el valor como entero largo(64 bits).
■ Rank. Propiedad. Devuelve el número de dimensiones o rango del Array.
■ CloneO- Método. Crea una copia del /Vrray que hace la llamada al método.
Capítulo 12. Colecciones 467

■ ClearQ. Método. "Limpia el Array". Establece al valor por defecto predeterminado


cada casilla del Array, es decir, si el Array es de tipo numérico modifica todas sus
casillas a cero, si es de tipo bool a false y si es de algún tipo de objeto a nuil.
■ CopyO- Método. Copia un conjunto de elementos especificado de un Array a otro
realizando las conversiones de tipo necesarias.
■ EqualsQ. Determina si dos Arrays son iguales.
■ FindO- Método. Localiza un objeto en el Array que cumplas con unas características.
Existen variaciones a este método tales como FindAlI, Findindex, FindLast, etc.
■ GetLowerBoundO- Método. Devuelve el límite inferior de la dimensión que se
indique.
■ GetUpperBoundO. Método. Devuelve el límite superior de la dimensión
especificada entre los paréntesis.
■ GetValueO / SetValueO- Métodos. Devuelve o establece el valor de una posición
concreta en el Array.
■ ResizeQ. Método. Cambia el tamaño de una matriz.
■ ReverseO- Método que invierte el contenido de una matriz.
■ SortO- Método que ordena el Array que hace uso de él.

12.2.2. ARRAYLIST Y LIST


Los objetos ArrayList y List mejoran el objeto Array visto en el apartado anterior. Entre las
propiedades que los caracterizan encontramos:
■ Capacidad variable. Un objeto de tipo Array es de tamaño fijo, mientras que los
objetos ArrayList y List aumentan su capacidad automáticamente si las necesi es
de la aplicación así lo requiere.
■ Insercción o eliminación de intervalos de elementos. ArrayList y List aportan
métodos que penniten agregar, insertar y eliminar intervalos de elementos, en e
objeto Array sólo podíamos obtener o establecer un único valor.
■ Incorporación del método Synchronized.
■ Límite inferior siempre cero. En un objeto de tipo Array podemos establecer cuál es
el índice inferior mientras que en los objetos tipo ArrayList y List este siempre es
cero.

■ Una única dimensión. Un objeto Array puede representar rma matriz de rMgo mayor
a uno mientras que los objetos de tipo ArrayList y List representarán siempre
matrices de una única dimensión.

12.2.2.1. ARRAYLIST
Un objeto de tipo ArrayList implementa la interfaz IList. Básicamente representa un array de
una única dimensión que aumenta dinámicamente en función de las necesidades de la aplicación
que lo usa.
NOTA: Ya que ArrayList está incluida en el espacio de nombres Collections es
necesario añadir en la parte superior de nuestras aplicaciones using System.Collections.

Los métodos son similares a los estudiados para un objeto de tipo Array. Veamos un
ejemplo sencillo.
static public int menú()
{
int opcion;
Consolé.WriteLine("1. Insertar nuevo numero");
Consolé.WriteLine("2. Eliminar un número");
Consolé.WriteLine("3. Buscar numero");
Consolé.WriteLine("4. Búsqueda binaria");
Consolé.WriteLine("5. Mostrar números");
Consolé.WriteLine("6. Salir");
Consolé.WriteLine("Opcion:");
opcion = int.Parse(Consolé.ReadLineO);
return opcion;

static public int menú()


{
ArrayList números = n
new ArrayList O; //(l)
int opcion;
int numero, posición;
do
{
opcion = menú O;
switch (opcion)

case 1: Consolé.WriteLine("Numero:");
numero = int.Parse(Consolé.ReadLine());
números.Add(numero); //(2)
break;
case 2: Consolé.WriteLine("Numero:");
numero = int.Parse(Consolé.ReadLineO);
números.Remove(numero); //(3)
break;
case 3: Consolé.WriteLine("Numero:");
numero = int.Parse(Consolé.ReadLine());
//(4)
Consolé.WriteLine(números.Contains(numero)?
"Número encontrado en el Array":"Número inexistente");
break;
case 4; Consolé.WriteLine("Numero:");
numero = int.Parse(Consolé.ReadLineO);
números.SortO ; //(5)
Consolé.WriteLine(((posicion=numeros.BinarySearch(numero))
!=-l)?"Número encontrado en la posición"+posicion
:"Número no encontrado");
break;
case 5;
foreach (int num in números)

Consolé.WriteLine("Numero:{O}",num);

break;

} while (opcion != 6);


■ //(l). Creamos una instancia del objeto ArrayList. Podemos especificar en el
constructor una cantidad o capacidad predetenninada.
■ //(2). Uso del método Add que permite añadir nuevos elementos a la lista. Estos
elementos(números)serán añadidos al final del ArrayList.
■ //(3). Uso del método Remove del objeto ArrayList. Este busca y eliminar el objeto
pasado como parámetro (siempre que exista).
■ //(4). Uso del método Contaíns. Este método permite averiguar si un objeto está
almacenado o no en la lista. Devuelve el valor verdadero o falso en función de si se
encuentra o no el elemento buscado.

■ //(5). Uso del método Sort. Este ordena la lista de objetos. Una línea más abajo se
hace uso además del método BinarySearch que busca un objeto en un ArrayList
ordenado.

El ejemplo que acabamos de realizar es muy sencillo y se expone para entender


definitivamente el uso del ArrayList. A continuación se presenta un ejemplo algo más complejo
en el que intervienen diferentes clase. La clase Proveedores almacena la información referida a
un único proveedor. Implementa IComparable ya que necesitaremos establecer un criterio de
ordenación para el método Sort que posteriormente usaremos.
La clase ListaProveedores mantendrá una lista de objetos Proveedores. Se han añadido
métodos a esta que encapsulan la variable principal y sus características y métodos.
Para finalizar, la clase Programa mantiene la zona ejecutable que a partir de un menú lanzará
los métodos desarrollados en ListaProveedores.
Este conjunto de clases, así como la organización de los elementos y su funcionalidad, es
típica cuando trabajamos con conjuntos de datos, ya sean listas, pilas, colas, etc.

//Proveedores.es \
class Proveedores:IComparable 1
{ i
int refProveedor; 1
string nombre; ;
string dirección; ;
string codigoPostal; 1
string ciudad; i
string pais; 1
string telefono; I

public Proveedores(int refProveedor) 1


this.refProveedor = refProveedor; !
} :
public Proveedores(int refProveedor, string nombre |
^ ,string dirección, string telefono) ;
this.refProveedor = refProveedor; i
this.nombre = nombre; ¡
this.dirección = dirección; í
this.telefono = telefono; i
public int RefProveedor
{
get {return refProveedor;) public string Ciudad
}
public string Nombre get {return ciudad; }
{ set {ciudad = valué; }
get {return nombre;}
}
set {nombre = valué;) public string Pais
}
{
public string Dirección get {return pais;)
{ set {pais = valué;}
get {return dirección;}
)
set {dirección = valué;)
piiblic string Telefono
}
{
public string CodigoPostal get {return telefono; ]
{ set {telefono = valué;
get {return codigoPostal; }
set {codigoPostal = valué;}

public string ToStringO


{
return refProveedor + + nombre + "\t" + dirección + "(" + ciudad
+ + codigoPostal + "\t" + telefono;
}
public int Compárelo(Object obj)
{
Proveedores aux = (Proveedores)obj;
return nombre.Compárelo(aux.nombre);

//ListaProveedores.es
^Is-Ss ListaProveedores
{
ArrayList listaProveedores; //Usamos un objeto ArrayList para almacenar
//Proveedores
public ListaProveedores()

^ listaProveedores = new ArrayList();


//Método privado usado por el método Insertar
prívate Proveedores nuevoProveedor(int RefProveedor)
{
Proveedores nuevo = new Proveedores(RefProveedor);
Consolé.WriteLine("Nombre:");
nuevo.Nombre = Consolé.ReadLine();
Consolé.WriteLine("Dirección:");
nuevo.Dirección = Consolé.ReadLine();
Consolé.WriteLine("Ciudad:");
nuevo.Ciudad = Consolé.ReadLine();
Consolé.WriteLine("Código postal:");
nuevo.CodigoPostal = Consolé.ReadLine();
Consolé.WriteLine("Pais:");
nuevo.Pais = Consolé.ReadLine();
Consolé.WriteLine("Teléfono:");
nuevo.Telefono = Consolé.ReadLine();
return nuevo;
Capítulo 12. Colecciones 471

public void Insertar(int refProveedor)

Proveedores nuevo = nuevoProveedor(refProveedor);


//Usamos la propiedad Add. Insetamos al final
listaProveedores.Add(nuevo);
}
public void Eliminar(int refProveedor)
{
/*Localizamos la referencia y si existe eliminamos la posición que
ocupa*/
int pos = BuscarPosicion(refProveedor);
if (pos != -1)
{
listaProveedores.RemoveAt(pos);
}
)
//Método de búsqueda según referencia
public int BuscarPosicion(int refProveedor)
{
int pos=-l;
int i = 0;
while (pos == -1 && i < listaProveedores.Count)
{
if (((Proveedores)listaProveedores[i]).RefProveedor
== refProveedor)

pos = i;

return pos;
}
//Método de búsqueda según nombre
public int BuscarNombre(string nombre)
{
int pos = -1;
int i = 0;
while (pos == -1 && i < listaProveedores.Count)
{
if (((Proveedores)listaProveedores[i]).Nombre.CompareTo(nombre)

pos = i;

return pos;
}
public void OrdenarListaO
{
listaProveedores.Sort();
}
public void Mostrar O
{
foreach (Proveedores proveedor in listaProveedores)

Consolé.WriteLine(proveedor.ToString());
piiblic int UltimaReferencia ()
{
if (listaProveedores.Count > 0)
{
return
((Proveedores)listaProveedores[listaProveedores.Count-1]).RefProveedor;

return 0;

public int TotalProveedoresO

return listaProveedores.Count;
}
public string this[int pos]

if (pos != -1)

return ((Proveedores)listaProveedores[pos]).ToStringO;

return "Proveedor no encontrado";

//Program.es "
static public int menú()
{
int opcion;
Consolé.WriteLine("1. Insertar nuevo proveedor");
Consolé.WriteLine("2. Eliminar proveedor por referencia");
Consolé.WriteLine("3. Buscar proveedor por nombre");
Consolé.WriteLine("4. Ordenar proveedores por nombre );
Consolé.WriteLine("5. Mostrar proveedores");
Consolé.WriteLine("6. Salir");
Consolé.WriteLine("Opcion:");
opcion = int.Parse(Consolé.ReadLineO);
return opcion;
}
static void Main(string[] args)

ListaProveedores proveedores = new ListaProveedores();


int opcion;
int referencia, posición;
string nombre;
do

opcion = menú();
switch (opcion)
{
case 1: referencia = proveedores.UltimaReferencia();
proveedores.Insertar (referencia__+__l)_;_
break;
case 2: Consolé.WriteLine("Indica referencia a eliminar:");
referencia = int.Parse (Consolé.ReadLine());
proveedores.Eliminar(referencia);
break;
case 3: Consolé.WriteLine("Qué nombre de proveedor
desea buscar?:");
nombre = Consolé.ReadLine();
posición = proveedores.BuscarNombre(nombre);
Consolé.WriteLine(proveedores[posición]);
break;
case 4: proveedores.OrdenarLista();
break;
case 5: proveedores.Mostrar();
break;
)
Consolé.WriteLine("Pulse la tecla ENTER para continuar");
Consolé.ReadLine();
Consolé.Clear();
} while (opcion != 6);

NOTA: Aunque en ninguno de los ejemplos hemos usado el método Insert para
insertar elementos en la lista sería interesante hacer uso de él ya que una lista puede ser
borrada desde cualquier posición y un elemento puede ser insertado en cualquier lugar.

12.2.2.2. LIST

Podemos decir que la clase List es el equivalente genérico de ArrayList, es decir, a la hora de
definir un objeto List indicamos el tipo de datos que va a contener. Al usar un ArrayList no
indicamos en primera instancia qué tipo de elementos contiene, con lo que se pueden producir
errores en tiempo de ejecución al intentar realizar conversiones imposibles. La clase List es
similar a ArrayList con la diferencia que indicamos el tipo de datos del objeto que se va a
almacenar en él. List implementa la interfaz IList mediante im array cuyo tamaño aumenta
dinámicamente como ya hemos explicado (Apartado 12.2.2).
-— ,

//Definición de una lista de números reales tipo float ¡


List<float> listaReales = new List<float>(); i
Si para la lista creada realizáramos la siguiente operación el compilador lanzaría un error
durante la compilación, no permitiría la ejecución del programa.
------------ I

listaReales.Add(5.4); //ERROR, no se puede convertir double a float í


//La mejor coincidencia del método es Add(float) I
Véase el siguiente ejemplo de uso de List sobre la clase Persona que implementa a su vez
¡Comparable para definir el método Compárelo.
static void Main(string[] args)
{
List<Persona> listaPersonas = new List<Persona>();
listaPersonas.Add(new Persona("Isabel", "Jiménez"));
listaPersonas.Add(new Persona("Juan Manuel", "Orellana"));
listaPersonas.Add(new Persona("José", "Cumbreras"));

Consolé.WriteLine("Personas en la lista...");
mostrar(listaPersonas);

listaPersonas.RemoveAt(1);
listaPersonas.Sort();

Consolé.WriteLine("\nLista ordenada...");
mostrar(listaPersonas);

listaPersonas.Reverse();
Consolé.WriteLine("\nLista invertida...");
mostrar(listaPersonas);

Consolé.ReadLine();

ACTIVIDAD 12.1

Modifica la Actividad 6.4 del Capítulo 6 de forma que uses un Objeto Array, ArrayList o List.

ACTIVIDAD 12.2

Modifica la Actividad 6.5 del Capítulo 6 en función de lo que acabas de realizar en la


Actividad 12.1 de este mismo capítulo.

12.2.3. HASHTABLE

Una colección de tipo Hashtable estará formada por pares del tipo clave - valor. En este tipo
de colección los elementos no se encuentran identificados por su posición en la lista sino por un
elemento denominado clave que los diferencia de forma única. Así, vamos a tener objetos en el
Hashtable a los que asociaremos una clave única que usaremos para su identificación.
Es interesante su uso ya que gracias a esta colección podremos realizar búsquedas en función
de un dato representativo del objeto valor que se almacena, por ejemplo, si la clave es la
referencia de un proveedor es más sencillo encontrar este entre todos los objetos de la colección.
La clave puede ser cualquier tipo de objeto, al igual que el valor, eso sí, la clave no acepta
valores vacíos o nuil, a diferencia de los elementos valor.
Son interesantes las propiedades Keys y Valúes que permiten acceder a todas las claves y
valores de una colección tipo Hashtable.
Véase el siguiente ejemplo para entender su uso. Imaginemos que en esta ocasión disponemos
de una clase llamada Clientes con los atributos y métodos que se indican.
• — - -- — — -- — —I

//Clientes.es ;
class Clientes I
{ i
string referencia; ;
string nombre; 1
string dirección;
string codigoPostal;
string ciudad;
string pais;
string telefono;

public Clientes(string referencia)


{
this.referencia = referencia;
}
//Se codifican las propiedades para cada atributo
public string Referencia {...}
public string Nombre {...}
public string Dirección {...}
public string CodigoPostal {...}
public string Ciudad {...}
pviblic string Pais (...)
public string Telefono (...)

/*Sobrecargamos el método ToString para visualizar posteriormente cada


cliente*/
public string ToString()
{
return "Nombre:" + nombre + " Dirección:" + dirección +
" Código Postal:" + codigoPostal + " Ciudad:" + ciudad +
" Pais:" + pais + " Teléfono:" + telefono;

//Program.es
static public int menú()
(
int opcion;
Consolé.WriteLine("1. Insertar
Insertar nuevo el
nuevo cliente");
Consolé.WriteLine("2. Eliminar cliente por referencia");
Consolé.WriteLine("3. Buscar cliente por
pe referencia");
Consolé.WriteLine ("4. Mostrar clientes");
clientes"
Consolé.WriteLine("5. Salir");
Consolé.WriteLine("Opcion:");
opcion = int.Parse(Consolé.ReadLineO);
return opcion;
í

static public Clientes nuevoCliente(string RefCliente)


Clientes nuevo = new Clientes(RefCliente);
Consolé.WriteLine("Nombre:");
nuevo.Nombre = Consolé.ReadLine();
Consolé.WriteLine("Dirección:");
nuevo.Dirección = Consolé.ReadLine();
Consolé.WriteLine("Ciudad:");
nuevo.Ciudad = Consolé.ReadLine();
Consolé.WriteLine("Código postal:");
nuevo.CodigoPostal = Consolé.ReadLine();
Consolé.WriteLine("Pais:");
nuevo.Pais = Consolé.ReadLine();
Consolé.WriteLine("Teléfono:");
nuevo.Telefono = Console.ReadLine();
return nuevo;
static void Main(string[] args)
{
Hashtable colección = new Hashtable();
int opcion;
string referencia;
do
{
opcion = menú O;
switch (opcion)
{
case 1: Consolé.WriteLine("Referencia ;
referencia=Console.ReadLine();
Clientes nuevoC = nuevoCliente(referencia);
//(1)
colección.Add(referencia, nuevoC);
break;
case 2: Consolé.WriteLine("Referencia:");
referencia = Consolé.ReadLine();
//(2)
colección.Remove(referencia);
break;
case 3: Consolé.WriteLine("Referencia:");
referencia = Consolé.ReadLine();
Consolé.WriteLine(((Clientes)colección[referencia])
.ToString()); //(3)
break;
case 4: Consolé.WriteLine("CLIENTES:");
foreach(Clientes cliente in colección.Valúes) //(4)
Consolé.WriteLine(cliente.ToString());

break;

} while (opcion != 5);

//(l). La creación de un nuevo cliente se realiza especificando una referencia o clave


(key). El método Add solicitará que indiquemos clave y valor u objeto a insertar en la
colección. Así observamos colección.Add(referencia,nuevoC);
//(2). La eliminación se hace a través de la clase, mucho más sencilla ya que esta
representa a un valor concreto y suele ser incluso parte de él. En nuestro ejemplo sólo
debemos especificar la referencia a eliminar y se sacará el valor correspondiente a la
clase de la colección.
//(3). Es posible acceder a cualquier objeto (valor) a partir de los operadores []. De
este modo podremos modificar u obtener el valor de un elemento concreto de la
colección. Buscamos este no por su posición, sino por su referencia ya que entre los
corchetes debemos especificar la clave no un número de orden
(colección[referencia]).
//(4). A la hora de recorrer la colección y mostrar su contenido debemos tener en
cuenta que cada elemento de esta está formado por un par clave-valor de forma que
en el foreach no podemos usar directamente la variable colección, debemos incluir la
propiedad Valúes para que recoja todos los valores de la colección y puedan ser
procesados de forma independiente.
12.2.4. DICTIONARY

Al igual que ocurría con ArrayList y List, Dictionary es el equivalente genérico de Hashtable.
Así, podremos usar los métodos vistos a diferencia que de antemano establecemos el tipo de
datos que vamos a almacenar en la colección.
Si para el ejemplo visto en el Apartado 12.2.3 usáramos un objeto de tipo Dictionary sólo
tendríamos que modificar la variable de tipo Hashtable por la que sigue:
Dictionary<string,Clientes> colección = new Dictionary<string,Clientes> (); |
)

El resto del código no tiene que ser modificado ya que los métodos son iguales en Hashtable y
Dictionary.
A la hora de definir un objeto de tipo Dictionary se deben indicar los tipos de datos tanto de la
clave como del valor, de ahí que en la declaración se observe: Dictionary<string,Clientes>.
Acabamos de establecer que la clave asociada a cada valor a de ser de tipo string mientras que
cada valor será una instancia de la clase Clientes.

12.2.5. SORTEDLIST Y SORTEDDICTIONARY

Avanzamos en las colecciones y llegamos al objeto SortedList. Un objeto SortedList es una


colección fonnada por pares clave-valor ordenada por clave como indica su nombre (Sorted),
podiendo tenerse acceso a cada objeto a través de la clave o la posición que ocupa. Aunamos
características de List y Hashtable.
SortedDictionary representa la clase genérica de SortedList. Tanto una como otra colección
mantiene los mismos métodos.

Al ser una lista ordenada por clave, a la hora de insertar un objeto mediante el método Add
este lo coloca en la posición adecuada para mantener el orden de claves, así:
SortedList listaOrdenada = new SortedList O;
listaOrdenada.Add(1,'m');
listaOrdenada.Add(3, 'h');
listaOrdenada.Add(2, 'a');

dará como resultado la lista:

a - h

Un ejemplo de SortedDictionary podría ser:


SortedDictionary<string, Persona> listaOrdenadaPersonas
= new SortedDictionary<string, Persona>();

1istaOrdenadaPersonas.Add("Jiménez",new Persona("Isabel","Jiménez"));
listaOrdenadaPersonas.Add("López", new Persona("Pablo", "López"));
listaOrdenadaPersonas.Add("Aznar", new Persona("Inma", "Aznar"));
listaOrdenadaPersonas.Add("Márquez", new Persona("José Antonio", "Márquez"));

foreach (Persona person in listaOrdenadaPersonas.Valúes)


{
Consolé.WriteLine(person.ToString());

Consolé.ReadLine();
12.2.6. QUEUE

El objeto Queue de System.Collection representa un tipo de colección que posee un


funcionamiento predeterminado. A la hora de añadir o eliminar no podemos hacer la operación
desde cualquier posición. Esta clase representa una cola de objetos en la que el primero en entrar
en la cola es el primero en salir, es decir, las inserciones de elementos se realizarán al final de la
cola mientras las eliminaciones serán siempre al principio.
Son propios de este objeto los métodos:
■ Peek. Devuelve el objeto situado al principio de la cola sin eliminar este.
■ Dequeue. Extrae y devuelve el primer elemento de la cola.
■ Enqueue. Agrega un objeto al final de la cola.
Podemos usar el objeto con tipos genéricos, es decir, son validas las definiciones:
Queue colaEntradas = new Queue();
Queue<float> colaNumerosReales = new Queue<float>();

12.2.7. STACK
El objeto Stack representa una colección tipo LIFO, último en entrar primero en salir. Es una
colección genérica (System.Collection.Generic) cuyos métodos más significativos son:
■ Peek. Devuelve el objeto situado al inicio sin eliminarlo.
■ Pop. Quita y devuelve el objeto que se ubica al principio de la lista.
■ Push. Inserta un objeto al principio de la lista.

Figura 12.1. Queue.

Figura 12.2. Stack.

ACTIVIDAD 12.3

Realiza una aplicación que controle la lista de personas que accede a un cine a recoger
entradas para ver una película, así como la venta de entradas. El programa parte de una cola de x
personas, cada una de ellas al llegar a la taquilla (ser la primera persona de la cola) pide un
número de entradas, la aplicación mostrará localidades libres con el fin de que la persona
concreta elija los asientos. Al finalizar la operación se indica precio, lista de asientos ocupados y
se saca a la persona de la lista. La aplicación finalizará cuando se vacíe la cola de personas o no
queden más entradas que vender.

ACTIVIDAD 12.4

Como actividad de investigación y ampliación se propone programar la Actividad 12.3


usando procesos. Supongamos que cada persona es un proceso que llega a ima cola en la que
espera su tumo para ser atendida. Busca en bibliografia aportada por el profesor o en la Web
información sobre programación concurrente en C#(Threads).

12.3. COLECCIONES EN JAVA


Java, al igual que C#, dispone de una serie de clase que conforman colecciones de datos.
Todas las colecciones, como ya se ha estudiado, realizan las operaciones de inserción,
eliminación, acceso, búsqueda o visualización de los objetos de la colección. En Java
encontramos las siguientes colecciones:
■ ArrayList.
■ HashMap.
■ HashSet.

■ Hashtable.

■ LinkedList.

■ Properties.
■ Stack.

■ TreeMap.
■ TreeSet.

■ Vector.

■ WeakHashMap.
Las colecciones Java se encuentran en la librería java.util.

12.3.1. ARRAYLIST

Objeto similar en funcionalidad al ArrayList visto en Java, sin embargo, podemos definir este
de forma genérica o no según nos interese.
- n

ArrayList array=new ArrayList(); !


ArrayList<Integer> arrayEnteros=new ArrayList<Integer>(); I
En caso de usar la primera definición podemos configurar una colección de diferentes tipos de
datos, como ocurría en C#:

array.add(2); ■
array.add(9.6); i
array.add("Hola"); I

Los métodos de interés de esta colección son:

■ add(Object). Añade un elemento a la colección. El método está sobrecargado de


fonna que se permite insertar un objeto en la posición indicada.
■ contains(Object). Comprueba si el objeto pasado como parámetro se encuentra o no
en la lista.

■ get(int). Obtiene el objeto que se encuentra en la posición pasada como parámetro.


■ indexOf(Object). Inverso a get. Devuelve la posición del objeto pasado como
parámetro.
■ isEmptyO- Devuelve un valor booleano que detennina si el array esta vacio o no.
■ remove(Objeto). Eliminar la primera ocurrencia del objeto pasado como parámetro.
El método presenta una sobrecarga en la que se pasa como parámetro la posición del
objeto a eliminar, devuelve el objeto.
■ set(int. Objeto). Sobrescribe el elemento que se encuentra en la posición int por el
nuevo Objeto pasado como parámetro.
■ sizeQ. Devuelve e número de elementos de la lista.
■ toArrayO. Devuelve un array de objetos (Object []) tal que en cada casilla se inserta
un objeto del ArrayList.
public static void main(String[] args){
try{
ArrayList arrayPrimitivos=new ArrayListO;
BufferedReader teclado=
new BufferedReader(new InputStreamReader(System.in));
int opcion=0;
int cuentaEnteros=0, cuentaString=0;

while(opcion != 3){
System.out.println("¿Qué deseas añadir?");
System.out.println("1. Numero entero");
System.out.println ("2. Cadena");
System.out.println("3. Salir");
opcion=Integer.parseint(teclado.readLine());
switch(opcion){
case 1:
arrayPrimitivos.add(Integer.parseint(teclado.readLine()));
break;
case 2;
arrayPrimitivos.add(teclado.readLine());
break;

while (!arrayPrimitivos.isEmpty()){
Object e1emento=arrayPrimitivo3.remove(O);
if(elemento instanceof Integer){
cuentaEnteros++;
}
else if (elemento instanceof String){
cuentaString++;
Capítulo 12. Colecciones 481

System.out.printf("Enteros:%d - Cadenas:%d"
,cuentaEnteros, cuentaString);
}
catch(NumberFormatException error){
System.out.println ("Error en la conversión de tipos.
Código de error+" + error.getMessage());
}
catch(lOException error){
System.out.println ("Error en la entrada estándar de datos.
Código de error+" + error.getMessage());

12.3.2. VECTOR

Un objeto de tipo Vector es similar a ArrayList aunque presenta mayor número de métodos.
Vector representa un array de objetos que aumenta o disminuye de fonna dinámica en función de
las operaciones que se realizan sobre los datos.
Entre sus métodos destacados:

■ firstElementO. Devuelve el primer elemento del vector.


■ lastEIementoQ. Devuelve el último elemento del vector.
■ capacityO- Devuelve la capacidad actual del vector.
■ setS¡ze(¡nt). Determina un nuevo tamaño para el vector de forma que si el nuevo
valor es mayor al actual los nuevos elementos se inicializan a nuil mientras que, en
caso contrario, si el nuevo tamaño es inferior al actual los elementos sobrantes se
eliminan.

Además se siguen manteniendo métodos del tipo add,remove, etc., vistos en el ArrayList.

ACTIVIDAD 12.5

Busca información en la web o bibliografía aportada por el profesor de las otras colecciones
Java enunciadas. Observa diferencias y similitudes con sus homólogas en C#.

ACTIVIDAD 12.6

Escoge alguna de las eoleeeiones vistas en la Actividad 12.5 y realiza un pequeño programa
donde hagas uso de ella.

COMPRUEBA TU APRENDIZAJE
1. ¿Qué es una colección?
2. ¿Qué tipos de colecciones hemos estudiado en este capítulo?
3. ¿Qué método utiliza un ArrayList en C# para añadir im objeto a la colección? ¿Y para
eliminarlo?

4. ¿Qué queremos decir con la frase "List es el equivalente genérico de ArrayList"?


5. ¿Qué caracteriza a una colección HashTable tanto en Java como C#?
6. ¿Qué estamos haciendo cuando escribimos esta línea de código en C#?
7. ¿Cuál es el funcionamiento de una colección Queue? ¿Y una colección Stack? ¿Qué
métodos utilizan para la inserción y eliminación de elementos?
8. ¿En qué se diferencia la colección ArrayList de C# con la colección ArrayList de Java?
9. ¿Cuáles son los métodos principales de una clase Vector en Java?
10. ¿Para qué crees que se usa la clase SortedList?

ACTIVIDADES DE AMPLIACION
Imagina que necesitas mantener una lista ordenada de vehículos. Cada vehículo se
enumera e identifica con una clave del tipo vXXXX, por ejemplo, vOOOl, v0120, etc.
De cada coche vamos a almacenar su matrícula, modelo, tipo, número de puertas, color,
potencia y observaciones. Crea un programa de consola con las clases necesarias para
cumplir las especificaciones dadas.
Usa las clases codificadas en la Actividad de Ampliación 1 y desarrolla una interfaz
gráfica que permita insertar un nuevo coche, visualizar un coche según una
característica dada (por ejemplo matrícula), etc. Complica la aplicación tanto como
desees suponiendo que estas diseñando la aplicación de un concesionario de vehículos.
Añade mayor funcionalidad a la Actividad de Ampliación 2 de fonna que cuando se
cierre el programa los vehículos sean almacenado en un fichero binario. Además,
incluiremos una nueva opción por la que se pueda cargar la información de un conjunto
X de vehículos a partir de un fichero binario.
Para asegurar nuestros ficheros de vehículos vamos a establecer una cabecera en ellos
cada vez que sean guardados, es decir, todos los ficheros de la aplicación desarrollada
en las actividades anteriores comenzarán con el texto: Ap. Vehiculos_v.01.
Autor:Nombre_lector.
Genera una aplicación que permita almacenar objetos de tipo Juguetes. El objetivo es
tener contabilizados y registrados todos los juguetes de un aula de infantil en una
guardería. Así, todos los objetos con los que se distraen los niños se denominarán
juguetes pero se distinguen grupos como: muñecas, carros, puzles, etc., (agregar
aquellos que el lector desees, el objetivo es generar un esquema jerárquico de herencia).
La aplicación permite; dar de alta un juguete (insertarlo en la lista), dar de baja un
juguete (en caso de que se haya roto o este deteriorado), modificar estado de un juguete
(tal que podamos especificar el estado físico en el que se encuentra el juguete), buscar y
listar el total de juguetes dados de alta. Usa alguna de las colecciones vista en el
capítulo.
Realiza una aplicación que gestione la entrada de una cola de personas a un concierto de
rock. Antes de que se abran las puertas del concierto, cada persona a cogido la vez de
forma que sabe justo detrás de que otra persona debe entrar. Al acceder a la puerta de
acceso al concierto deben indicar su DNI, primer apellido y número de entrada. La
aplicación almacenará los DNIs, primer apellido y número de entrada en un fichero de
texto. Además, debe contarse el número total de personas que han accedido al recinto.
Capitulo 12. Colecciones 483

Crea una aplicación gráfica que simule una libreta de notas. El objetivo es que la
persona inserte notas en la aplicación y vaya eliminando estas en función de si realizan
o no las tareas. Además, se debe tener opción a dar énfasis a determinadas tareas, es
decir, asociar a cada tarea un atributo que determine el grado de importancia de la tarea
en cuestión. Si esta es muy importante se visualizará cada vez que se acceda al
programa. Es conveniente mantener un fichero donde almacenar las notas pendientes.
Programación
Este texto tiene una orientación fundamentalmente
práctica; para ello, se ha intercalado un buen núme
ro de actividades entre los contenidos teóricos para
asimilar las ideas y dinamizar el desarrollo de las clases.
Además al final del libro se incluyen Actividades de am
pliación y Comprueba tu aprendizaje.
Los objetivos principales que se alcanzarán estudiando
este libro serán los siguientes:
• Conceptos básicos relacionados con el software, como
qué se entiende por programa o algoritmo y qué diferen
cia existe entre programa y proceso, lenguaje de pro
gramación, clasificación de los lenguajes de programa
ción, compilación del software o entornos de desarrollo
preparados para la creación de software. Centraremos
nuestra atención en lenguajes como C# y Java y los IDE
Visual Studio y Netbeans.
Q.^ • Metodología de la programación. Antes de comenzar a
< < programar en C# o Java se realizarán pequeños algorit
O) O
mos al mismo tiempo que se estudian los componentes
■o >, principales de un programa estructurado. Creación de
diagramas de flujo y pseudocódigos.
o < • Una vez se conocen los elementos que componen un
t Q programa estructurado se pasarán a codificar estos en
OJ
(/) C# y Java. Variables, identificadores, tipos de datos
O)
Q
simples y compuestos, sentencias de control alternati
vas o bucles, etc., consiguiendo construir aplicaciones
funcionales.
• Programación orientada a objetos. Los conceptos prin
cipales que conforman este paradigma de la programa
ción. Clases, objetos, herencia, polimorfismo, interfaz,
etc.
• Desarrollo de aplicaciones de consola y aplicaciones
gráficas. Se profundizará en los elementos que forman
una aplicación gráfica, así como en la programación
orientada a eventos.
• Flujos de datos. Programación de aplicaciones.que ges
tionan el flujo de información entre diferentes tipos de
dispositivos, sobre todo entre el software y un dispositi
vo de almacenamiento elegido.
» Gestión de errores tanto en la compilación como en
tiempo de ejecución (excepciones).
• Desarrollo de aplicaciones de gestión de bases de da
tos relaciónales o bases de datos orientadas a objetos
(BDOO) en C# y Java.
> Colecciones típicas de C# y Java como ArrayList, List,
etc.

ISBN 978-84-1545-259-1

www.garceta.es
grupo editorial

9 788415 452591
Programación
Este texto tiene una orientación fundamentalmente
práctica; para ello, se ha intercalado un buen núme
ro de actividades entre los contenidos teóricos para
asimilar las ideas y dinamizar el desarrollo de las clases.
Además al final del libro se incluyen Actividades de am
pliación y Comprueba tu aprendizaje.
Los objetivos principales que se alcanzarán estudiando
este libro serán los siguientes:
• Conceptos básicos relacionados con el software, como
qué se entiende por programa o algoritmo y qué diferen
cia existe entre programa y proceso, lenguaje de pro
gramación, clasificación de los lenguajes de programa
ción, compilación del software o entornos de desarrollo
preparados para la creación de software. Centraremos
nuestra atención en lenguajes como C# y Java y los IDE
Visual Studio y Netbeans.
Q.^ • Metodología de la programación. Antes de comenzar a
< < programar en C# o Java se realizarán pequeños algorit
O) O
mos al mismo tiempo que se estudian los componentes
■o >, principales de un programa estructurado. Creación de
diagramas de flujo y pseudocódigos.
o < • Una vez se conocen los elementos que componen un
t Q programa estructurado se pasarán a codificar estos en
OJ
(/) C# y Java. Variables, identificadores, tipos de datos
O)
Q
simples y compuestos, sentencias de control alternati
vas o bucles, etc., consiguiendo construir aplicaciones
funcionales.
• Programación orientada a objetos. Los conceptos prin
cipales que conforman este paradigma de la programa
ción. Clases, objetos, herencia, polimorfismo, interfaz,
etc.
• Desarrollo de aplicaciones de consola y aplicaciones
gráficas. Se profundizará en los elementos que forman
una aplicación gráfica, así como en la programación
orientada a eventos.
• Flujos de datos. Programación de aplicaciones.que ges
tionan el flujo de información entre diferentes tipos de
dispositivos, sobre todo entre el software y un dispositi
vo de almacenamiento elegido.
» Gestión de errores tanto en la compilación como en
tiempo de ejecución (excepciones).
• Desarrollo de aplicaciones de gestión de bases de da
tos relaciónales o bases de datos orientadas a objetos
(BDOO) en C# y Java.
> Colecciones típicas de C# y Java como ArrayList, List,
etc.

ISBN 978-84-1545-259-1

www.garceta.es
grupo editorial

9 788415 452591

También podría gustarte