Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Introcsharp Version099zz PDF
Introcsharp Version099zz PDF
Introduccin a la programacin
con C#
Este texto ha sido escrito por Nacho Cabanes. Si quiere conseguir la ltima
versin, estar en mi pgina web:
www.nachocabanes.com
Este texto se distribuye "tal cual", sin garanta de ningn tipo, implcita ni explcita.
Aun as, mi intencin es que resulte til, as que le rogara que me comunique
cualquier error que encuentre.
Contenido
Contenido _________________________________________________________________________________ 2
0. Conceptos bsicos sobre programacin ____________________________________________ 9
0.1. Programa y lenguaje _______________________________________________________________________________ 9
0.4. Pseudocdigo_______________________________________________________________________________________ 14
5.4. Valor devuelto por una funcin. El valor "void" _____________________________________________ 160
Ap1.2. Unidades de medida empleadas en informtica (2): los bits ____________________________ 386
Ap5.2. Cambios de apariencia. Casillas de texto para sumar dos nmeros ___________________ 424
Estas rdenes se le deben dar en un cierto lenguaje, que el ordenador sea capaz
de comprender.
PRINT "Hola"
Otros lenguajes, como Pascal, nos obligan a ser algo ms estrictos y detallar
ciertas cosas como el nombre del programa o dnde empieza y termina ste, pero,
a cambio, hacen ms fcil descubrir errores (ya veremos por qu):
program Saludo;
begin
write('Hola');
end.
#include <stdio.h>
int main()
{
printf("Hola");
return 0;
}
En C# hay que dar todava ms pasos para conseguir lo mismo, porque, como
veremos, cada programa ser "una clase":
Como se puede observar, a medida que los lenguajes evolucionan, son capaces de
ayudar al programador en ms tareas, pero a la vez, los programas sencillos se
vuelven ms complicados. Afortunadamente, no todos los lenguajes siguen esta
regla, y algunos se han diseado de forma que las tareas simples sean (de nuevo)
sencillas de programar. Por ejemplo, para escribir algo en pantalla usando el
lenguaje Python haramos:
print("Hello")
Por el contrario, los lenguajes de bajo nivel son ms cercanos al ordenador que a
los lenguajes humanos. Eso hace que sean ms difciles de aprender y tambin
que los fallos sean ms difciles de descubrir y corregir, a cambio de que podemos
optimizar al mximo la velocidad (si sabemos cmo), e incluso llegar a un nivel de
control del ordenador que a veces no se puede alcanzar con otros lenguajes. Por
ejemplo, escribir Hola en lenguaje ensamblador de un ordenador equipado con
el sistema operativo MsDos y con un procesador de la familia Intel x86 sera algo
como
dosseg
.model small
.stack 100h
.data
saludo db 'Hola',0dh,0ah,'$'
.code
main proc
mov ax,@data
mov ds,ax
mov ah,9
mov dx,offset saludo
int 21h
mov ax,4C00h
int 21h
main endp
end main
(Nota: los colores de los ejemplos anteriores son una ayuda que nos dan algunos
entornos de programacin, para que nos sea ms fcil descubrir ciertos errores).
Por ejemplo, en el caso de Windows (y de MsDos), y del programa que nos saluda
en lenguaje Pascal, tendramos un fichero fuente llamado SALUDO.PAS. Este
fichero no servira de nada en un ordenador que no tuviera un compilador de
Pascal. En cambio, despus de compilarlo obtendramos un fichero SALUDO.EXE,
capaz de funcionar en cualquier otro ordenador que tuviera el mismo sistema
operativo, aunque dicho ordenador no tenga un compilador de Pascal instalado.
Eso s, no funcionara en otro ordenador que tuviera un sistema operativo distinto
(por ejemplo, Linux o Mac OS X).
Los intrpretes siguen siendo muy frecuentes hoy en da. Por ejemplo, en un
servidor web es habitual crear programas usando lenguajes como PHP, ASP o
Python, y que estos programas no se conviertan a un ejecutable, sino que sean
analizados y puestos en funcionamiento en el momento en el que se solicita la
correspondiente pgina web.
Actualmente existe una alternativa ms, algo que parece intermedio entre un
compilador y un intrprete. Existen lenguajes que no se compilan a un ejecutable
para un ordenador concreto, sino a un ejecutable "genrico", que es capaz de
funcionar en distintos tipos de ordenadores, a condicin de que en ese ordenador
exista una "mquina virtual" capaz de entender esos ejecutables genricos. Esta
es la idea que se aplica en Java: los fuentes son ficheros de texto, con extensin
".java", que se compilan a ficheros ".class". Estos ficheros ".class" se podran llevar
a cualquier ordenador que tenga instalada una "mquina virtual Java" (las hay para
la mayora de sistemas operativos).
Esta misma idea se sigue en el lenguaje C#, que se apoya en una mquina virtual
llamada "Dot Net Framework" (algo as como "plataforma punto net"): los
programas que creemos con herramientas como Visual Studio sern unos
ejecutables que funcionarn en cualquier ordenador que tenga instalada dicha
"plataforma .Net", algo que suele ocurrir en las versiones recientes de Windows y
que se puede conseguir de forma un poco ms artesanal en plataformas Linux y
Mac, gracias a un "clon" de la "plataforma .Net" que es de libre distribucin,
conocido como "proyecto Mono".
Ejercicios propuestos
0.4. Pseudocdigo
A pesar de que los lenguajes de alto nivel se acercan al lenguaje natural (ingls),
que nosotros empleamos, es habitual no usar ningn lenguaje de programacin
concreto cuando queremos plantear iniciamente los pasos necesarios para
resolver un problema, sino emplear un lenguaje de programacin ficticio, no tan
estricto, muchas veces incluso en espaol. Este lenguaje recibe el nombre de
pseudocdigo.
Por ejemplo, un algoritmo que controlase los pagos que se realizan en una tienda
con tarjeta de crdito, escrito en pseudocdigo, podra ser:
Ejercicios propuestos
Proceso EjemploDeSuma
Escribir 2+3
FinProceso
La mayora de los compiladores actuales permiten dar todos estos pasos desde un
nico entorno, en el que escribimos nuestros programas, los compilamos, y los
depuramos en caso de que exista algn fallo.
Esto escribe "Hola" en la pantalla. Pero hay muchas "cosas raras" alrededor de ese
"Hola", de modo vamos a comentarlas antes de proseguir, aunque muchos de los
detalles los aplazaremos para ms adelante. En este primer anlisis, iremos desde
dentro hacia fuera:
public static void Main() : Main indica cual es "el cuerpo del programa", la
parte principal (un programa puede estar dividido en varios fragmentos,
como veremos ms adelante). Todos los programas tienen que tener un
bloque "Main". Los detalles de por qu hay que poner delante "public static
void" y de por qu se pone despus un parntesis vaco los iremos
aclarando ms tarde. De momento, deberemos memorizar que sa ser la
forma correcta de escribir "Main".
Como se puede ver, mucha parte de este programa todava es casi un "acto de fe"
para nosotros. Debemos creernos que "se debe hacer as". Poco a poco iremos
detallando el por qu de "public", de "static", de "void", de "class"... Por ahora nos
limitaremos a "rellenar" el cuerpo del programa para entender los conceptos
bsicos de programacin.
}
}
En este caso, yo comentar los pasos necesarios para usar Linux Mint (en su
versin 17 Cinnamon) como entorno de desarrollo:
de texto llamada "Filtro rpido", en la que podemos teclear "mcs" para que nos
aparezca directamente nuestro compilador Mono:
En este editor podemos teclear nuestro programa, que inicialmente se ver con
letras negras sobre fondo blanco:
En esa "pantalla negra" ya podemos teclear las rdenes necesarias para compilar y
probar el programa:
Si alguno de los pasos ha fallado, tendrs que comprobar si has dado los pasos
anteriores de forma correcta y si tu fuente est bien tecleado.
http://www.mono-project.com/
Mono est listo para usar. En nuestro men de Inicio deberamos tener una nueva
carpeta llamada "Mono x.x.x for Windows", y dentro de ella un acceso a "Mono-
x.x.x Command Prompt":
Quiz se nos lleve a una carpeta que est dentro de "Documents and settings" o
quiz (algo habitual en las ltimas versiones) a alguna en la que no tengamos
permiso para escribir, como "Windows\System32".
En ese caso, podemos crear una carpeta en nuestro escritorio, llamada (por
ejemplo) "Programas", y comenzar por desplazarnos hasta ella, tecleando
cd \users\yo\desktop\programas
(Esa sera la forma de hacerlo para un usuario llamado "Yo" en un sistema con
Windows 7 o Windows 8; los detalles exactos dependern del nombre del usuario
y de la versin de Windows empleada).
Para crear un programa, el primero paso ser teclear el "fuente". Para ello
podemos usar cualquier editor de texto. En este primer fuente, usaremos
simplemente el "Bloc de notas" de Windows. Para ello, tecleamos:
notepad ejemplo01.cs
Aparecer la pantalla del "Bloc de notas", junto con un aviso que nos indica que no
existe ese fichero, y que nos pregunta si deseamos crearlo. Lo razonable es
responder que s:
Guardamos los cambios, salimos del "Bloc de notas" y nos volvemos a encontrar
en la pantalla negra del smbolo del sistema. Nuestro fuente ya est escrito y
guardado. El siguiente paso es compilarlo. Para eso, tecleamos
mcs ejemplo01.cs
mono ejemplo01.exe
Si en nuestro ordenador est instalado el "Dot Net Framework" (algo que debera
ser cierto en las ltimas versiones de Windows, y que no ocurrir en Linux ni Mac
OsX), quiz no sea necesario decir que queremos que sea Mono quien lance
nuestro programa, y podamos ejecutarlo directamente con su nombre:
ejemplo01
Pero en ocasiones puede ocurrir que el ejecutable sea para una versin de la
plataforma ".Net" distinta de la que tenemos instalada. En ese caso, tendremos
que lanzarlo usando Mono, o bien deberemos descargar e instalar la
correspondiente versin de ."Net" (es una descarga gratuita desde la pgina web
de Microsoft):
Hay un posible problema que se debe tener en cuenta: algunos de estos entornos
de desarrollo muestran el resultado de nuestro programa y luego regresan al
editor tan rpido que no da tiempo a ver los resultados. Una solucin provisional
puede ser aadir "System.Console.ReadLine()" al final del programa, de modo que
se quede parado hasta que pulsemos Intro:
(Nota: no debers entregar tus programas con ese "ReadLine" al final: es una
ayuda para que compruebes que estn funcionando correctamente desde ciertos
entornos, pero no debera formar parte de un programa finalizado, porque no es
parte de la lgica del programa).
Ejercicios propuestos:
(1.4.1) Crea un programa que diga el resultado de sumar 118 y 56.
(1.4.2) Crea un programa que diga el resultado de sumar 12345 y 67890.
Operador Operacin
+ Suma
- Resta, negacin
* Multiplicacin
/ Divisin
% Resto de la divisin ("mdulo")
Revisin 0.99zz Pgina 34
Introduccin a la programacin con C#, por Nacho Cabanes
Ejercicios propuestos:
(1.5.1.1) Haz un programa que calcule el producto de los nmeros 12 y 13.
(1.5.1.2) Un programa que calcule la diferencia (resta) entre 321 y 213.
(1.5.1.3) Un programa que calcule el resultado de dividir 301 entre 3.
(1.5.1.4) Un programa que calcule el resto de la divisin de 301 entre 3.
Por eso necesitaremos reservar zonas de memoria a las que datemos un nombre
y en las que guardemos los datos con los que vamos a trabajar y tambin los
resultados temporales. A estas "zonas de memoria con nombre" les llamaremos
variables.
Como primer ejemplo, vamos a ver lo que haramos para sumar dos nmeros
enteros que fijsemos en el programa.
El primer tipo de datos que usaremos sern nmeros enteros (sin decimales), que
se indican con "int" (abreviatura del ingls "integer"). Despus de esta palabra se
indica el nombre que tendr la variable:
int primerNumero;
Esa orden reserva espacio para almacenar un nmero entero, que podr tomar
distintos valores, y al que nos referiremos con el nombre "primerNumero".
int primerNumero;
...
primerNumero = 234;
Hay que tener en cuenta que esto no es una igualdad matemtica, sino una
"asignacin de valor": el elemento de la izquierda recibe el valor que indicamos a
la derecha. Por eso no se puede hacer 234 = primerNumero, y s se puede
cambiar el valor de una variable tantas veces como queramos
primerNumero = 234;
primerNumero = 237;
Tambin podemos dar un valor inicial a las variables ("inicializarlas") antes de que
empiece el programa, en el mismo momento en que las definimos:
(esta lnea reserva espacio para dos variables, que usaremos para almacenar
nmeros enteros; una de ellas se llama primerNumero y tiene como valor inicial
234 y la otra se llama segundoNumero y tiene como valor inicial 567).
Despus ya podemos hacer operaciones con las variables, igual que las hacamos
con los nmeros:
Ejercicio propuesto (1.6.2.1): Ampla el ejercicio 1.6.1.1, para que las tres
variables n1, n2, n3 estn declaradas en la misma lnea y tengan valores iniciales.
System.Console.WriteLine(3+4);
pero si se trata de una variable es idntico (sin comillas, para que el compilador
analice su valor de antes de escribir):
System.Console.WriteLine(suma);
Ya sabemos todo lo suficiente para crear nuestro programa que sume dos
nmeros usando variables:
{
public static void Main()
{
int primerNumero;
int segundoNumero;
int suma;
primerNumero = 234;
segundoNumero = 567;
suma = primerNumero + segundoNumero;
Ejercicios propuestos:
(1.6.3.1) Crea un programa que calcule el producto de los nmeros 121 y 132,
usando variables.
(1.6.3.2) Crea un programa que calcule la suma de 285 y 1396, usando variables.
(1.6.3.3) Crea un programa que calcule el resto de dividir 3784 entre 16, usando
variables.
(1.6.3.4) Ampla el ejercicio 1.6.2.1, para que se muestre el resultado de la
operacin n1+n2*n3.
1.7. Identificadores
Los nombres de variables (lo que se conoce como "identificadores") pueden estar
formados por letras, nmeros o el smbolo de subrayado (_) y deben comenzar por
letra o subrayado. No deben tener espacios intermedios. Tambin hay que
recordar que las vocales acentuadas y la ee son problemticas, porque no son
letras "estndar" en todos los idiomas, as que no se pueden utilizar como parte de
un identificador en la mayora de lenguajes de programacin.
PrimerNumero = 234;
primernumero = 234;
int primerNumero;
Ejercicios propuestos:
(1.7.1) Crea un programa que calcule el producto de los nmeros 87 y 94, usando
variables llamadas "numero1" y "numero2".
(1.7.2) Intenta crear una nueva versin del programa que calcula el producto de
los nmeros 87 y 94, usando esta vez variables llamadas "1numero" y "2numero".
(1.7.3) Intenta crear una nueva versin del programa que calcula el producto de
los nmeros 87 y 94, usando esta vez variables llamadas "numero 1" y "numero 2".
(1.7.4) Crea una nueva versin del programa que calcula el producto de los
nmeros 87 y 94, usando esta vez variables llamadas "nmero1" y "nmero2".
1.8. Comentarios
Podemos escribir comentarios, que el compilador ignorar, pero que pueden ser
tiles para nosotros mismos, haciendo que sea ms fcil recordar el cometido un
fragmento del programa ms adelante, cuando tengamos que ampliarlo o
corregirlo.
/* Esto
es un comentario que
ocupa ms de una lnea
*/
Tambin es posible declarar otro tipo de comentarios, que comienzan con doble
barra y terminan cuando se acaba la lnea (estos comentarios, claramente, no
podrn ocupar ms de una lnea). Son los "comentarios de una lnea" o
"comentarios al estilo de C++" (a diferencia de los "comentarios de mltiples
lneas" o "comentarios al estilo de C" que ya hemos visto):
En este texto, a partir de ahora los fuentes comenzarn con un comentario que
resuma su cometido, y en ocasiones incluirn tambin comentarios intermedios.
Ejercicios propuestos:
(1.8.1) Crea un programa que convierta una cantidad prefijada de metros (por
ejemplo, 3000) a millas. La equivalencia es 1 milla = 1609 metros. Usa comentarios
donde te parezca adecuado.
Si queremos que sea el usuario de nuestro programa quien teclee los valores,
necesitamos una nueva orden, que nos permita leer desde teclado. Pues bien, al
igual que tenemos System.Console.WriteLine ("escribir lnea"), tambin existe
System.Console.ReadLine ("leer lnea"). Para leer textos, haramos
texto = System.Console.ReadLine();
pero eso ocurrir un poco ms adelante, cuando veamos cmo manejar textos. De
momento, nosotros slo sabemos manipular nmeros enteros, as que
deberemos convertir ese dato a un nmero entero, usando Convert.ToInt32:
Un ejemplo de programa que sume dos nmeros tecleados por el usuario sera:
Ejercicios propuestos:
(1.9.1) Crea un programa que calcule el producto de dos nmeros introducidos
por el usuario.
(1.9.2) Crea un programa que calcule la divisin de dos nmeros introducidos por
el usuario, as como el resto de esa divisin.
(1.9.3) Suma tres nmeros tecleados por usuario.
(1.9.4) Pide al usuario una cantidad de "millas nuticas" y muestra la equivalencia
en metros, usando: 1 milla nutica = 1852 metros.
segundoNumero = Convert.ToInt32(Console.ReadLine());
suma = primerNumero + segundoNumero;
Ejercicios propuestos:
(1.10.1) Crea una nueva versin del programa que calcula el producto de dos
nmeros introducidos por el usuario (1.9.1), empleando "using System". El
programa deber contener un comentario al principio, que recuerde cual es su
objetivo.
(1.10.2) Crea una nueva versin del programa que calcula la divisin de dos
nmeros introducidos por el usuario, as como el resto de esa divisin (1.9.2),
empleando "using System". Debers incluir un comentario con tu nombre y la
fecha en que has realizado el programa.
Ejercicios propuestos:
(1.11.1) El usuario teclear dos nmeros (a y b), y el programa mostrar el
resultado de la operacin (a+b)*(a-b) y el resultado de la operacin a2-b2.
Ambos resultados se deben mostrar en la misma lnea.
(1.11.2) Pedir al usuario un nmero y mostrar su tabla de multiplicar,
usando {0},{1} y {2}. Por ejemplo, si el nmero es el 3, debera escribirse
algo como
3x0=0
3x1=3
3x2=6
3 x 10 = 30
(1.11.3) Crear una variante del programa anterior, que pide al usuario un
nmero y muestra su tabla de multiplicar. Esta vez no debers utilizar {0},
{1}, {2}, sino "Write".
(1.11.4) Crea un programa que convierta de grados Celsius (centgrados) a
Kelvin y a Fahrenheit: pedir al usuario la cantidad de grados centgrados y
usar las siguiente tablas de conversin: kelvin = celsius + 273 ; fahrenheit
= celsius x 18 / 10 + 32. Emplea "Write" en vez de "{0}" cuando debas
mostrar varios datos en la misma lnea.
2. Estructuras de control
Casi cualquier problema del mundo real que debamos resolver o tarea que
deseemos automatizar supondr tomar decisiones: dar una serie de pasos en
funcin de si se cumplen ciertas condiciones o no. En muchas ocasiones, adems
esos pasos debern ser repetitivos. Vamos a ver cmo podemos comprobar si se
cumplen condiciones y tambin cmo hacer que un bloque de un programa se
repita.
if (condicin) sentencia;
Es decir, debe empezar con la palabra "if", la condicin se debe indicar entre
parntesis y a continuacin se detallar la orden que hay que realizar en caso de
cumplirse esa condicin, terminando con un punto y coma.
// Ejemplo_02_01_01a.cs
// Condiciones con if
// Introduccin a C#, por Nacho Cabanes
using System;
Console.WriteLine("Introduce un nmero");
numero = Convert.ToInt32(Console.ReadLine());
if (numero>0) Console.WriteLine("El nmero es positivo.");
}
}
Este programa pide un nmero al usuario. Si es positivo (mayor que 0), escribe en
pantalla "El nmero es positivo."; si es negativo o cero, no hace nada.
Si la orden "if" es larga, se puede partir en dos lneas para que resulte ms legible:
if (numero>0)
Console.WriteLine("El nmero es positivo.");
Ejercicios propuestos:
(2.1.1.1) Crea un programa que pida al usuario un nmero entero y diga si es par
(pista: habr que comprobar si el resto que se obtiene al dividir entre dos es cero:
if (x % 2 == 0) ).
(2.1.1.2) Crea un programa que pida al usuario dos nmeros enteros y diga cul es
el mayor de ellos.
(2.1.1.3) Crea un programa que pida al usuario dos nmeros enteros y diga si el
primero es mltiplo del segundo (pista: igual que antes, habr que ver si el resto
de la divisin es cero: a % b == 0).
// Ejemplo_02_01_02a.cs
// Condiciones con if (2): Sentencias compuestas
// Introduccin a C#, por Nacho Cabanes
using System;
{
int numero;
Console.WriteLine("Introduce un nmero");
numero = Convert.ToInt32(Console.ReadLine());
if (numero > 0)
{
Console.WriteLine("El nmero es positivo.");
Console.WriteLine("Recuerde que tambin puede usar negativos.");
} // Aqu acaba el "if"
} // Aqu acaba "Main"
} // Aqu acaba "Ejemplo06
Ejercicios propuestos:
(2.1.2.1) Crea un programa que pida al usuario un nmero entero. Si es mltiplo
de 10, informar al usuario y pedir un segundo nmero, para decir a
continuacin si este segundo nmero tambin es mltiplo de 10.
Operador Operacin
< Menor que
> Mayor que
<= Menor o igual que
>= Mayor o igual que
== Igual a
!= No igual a (distinto de)
Revisin 0.99zz Pgina 49
Introduccin a la programacin con C#, por Nacho Cabanes
// Ejemplo_02_01_03a.cs
// Condiciones con if (3): "distinto de"
// Introduccin a C#, por Nacho Cabanes
using System;
Console.WriteLine("Introduce un nmero");
numero = Convert.ToInt32(Console.ReadLine());
if (numero != 0)
Console.WriteLine("El nmero no es cero.");
}
}
Ejercicios propuestos:
(2.1.3.1) Crea un programa que multiplique dos nmeros enteros de la siguiente
forma: pedir al usuario un primer nmero entero. Si el nmero que se que teclee
es 0, escribir en pantalla "El producto de 0 por cualquier nmero es 0". Si se ha
tecleado un nmero distinto de cero, se pedir al usuario un segundo nmero y se
mostrar el producto de ambos.
(2.1.3.2) Crea un programa que pida al usuario dos nmeros enteros. Si el
segundo no es cero, mostrar el resultado de dividir entre el primero y el segundo.
Por el contrario, si el segundo nmero es cero, escribir "Error: No se puede dividir
entre cero".
2.1.4. if-else
Podemos indicar lo que queremos que ocurra en caso de que no se cumpla la
condicin, usando la orden "else" (en caso contrario), as:
// Ejemplo_02_01_04a.cs
// Condiciones con if (4): caso contrario ("else")
// Introduccin a C#, por Nacho Cabanes
using System;
Console.WriteLine("Introduce un nmero");
numero = Convert.ToInt32(Console.ReadLine());
if (numero > 0)
Console.WriteLine("El nmero es positivo.");
else
Console.WriteLine("El nmero es cero o negativo.");
}
}
// Ejemplo_02_01_04b.cs
// Condiciones con if (5): caso contrario, sin "else"
// Introduccin a C#, por Nacho Cabanes
using System;
Console.WriteLine("Introduce un nmero");
numero = Convert.ToInt32(Console.ReadLine());
if (numero > 0)
Console.WriteLine("El nmero es positivo.");
if (numero <= 0)
Console.WriteLine("El nmero es cero o negativo.");
}
}
Podemos enlazar varios "if" usando "else", para decir "si no se cumple esta
condicin, mira a ver si se cumple esta otra":
// Ejemplo_02_01_04c.cs
// Condiciones con if (6): condiciones encadenadas
// Introduccin a C#, por Nacho Cabanes
using System;
Console.WriteLine("Introduce un nmero");
numero = Convert.ToInt32(Console.ReadLine());
if (numero > 0)
Console.WriteLine("El nmero es positivo.");
else
if (numero < 0)
Console.WriteLine("El nmero es negativo.");
else
Console.WriteLine("El nmero es cero.");
}
}
Ejercicio propuesto:
(2.1.4.1) Mejora la solucin al ejercicio 2.1.3.1, usando "else".
(2.1.4.2) Mejora la solucin al ejercicio 2.1.3.2, usando "else".
Operador Significado
&& Y
|| O
! No
As, un programa que dijera si dos nmeros introducidos por el usuario son cero,
podra ser:
// Ejemplo_02_01_05a.cs
// Condiciones con if enlazadas con &&
// Introduccin a C#, por Nacho Cabanes
using System;
Ejercicios propuestos:
(2.1.5.1) Crea un programa que pida al usuario un nmero entero y responda si es
mltiplo de 2 o de 3.
(2.1.5.2) Crea un programa que pida al usuario un nmero entero y responda si es
mltiplo de 2 y de 3 simultneamente.
(2.1.5.3) Crea un programa que pida al usuario un nmero entero y responda si es
mltiplo de 2 pero no de 3.
(2.1.5.4) Crea un programa que pida al usuario un nmero entero y responda si no
es mltiplo de 2 ni de 3.
(2.1.5.5) Crea un programa que pida al usuario dos nmeros enteros y diga si
ambos son pares.
(2.1.5.6) Crea un programa que pida al usuario dos nmeros enteros y diga si (al
menos) uno es par.
Revisin 0.99zz Pgina 53
Introduccin a la programacin con C#, por Nacho Cabanes
(2.1.5.7) Crea un programa que pida al usuario dos nmeros enteros y diga si uno
y slo uno es par.
(2.1.5.8) Crea un programa que pida al usuario dos nmeros enteros y diga "Uno
de los nmeros es positivo", "Los dos nmeros son positivos" o bien "Ninguno de
los nmeros es positivo", segn corresponda.
(2.1.5.9) Crea un programa que pida al usuario tres nmeros y muestre cul es el
mayor de los tres.
(2.1.5.10) Crea un programa que pida al usuario dos nmeros enteros y diga si son
iguales o, en caso contrario, cul es el mayor de ellos.
En el caso del lenguaje C#, este riesgo no existe, porque la "condicin" debe ser
algo cuyo resultado sea "verdadero" o "falso" (lo que pronto llamaremos un dato
de tipo "bool"), de modo que obtendramos un error de compilacin "Cannot
implicitly convert type 'int' to 'bool'" (no puedo convertir un "int" a "bool"). Es el caso
del siguiente programa:
// Ejemplo_02_01_06a.cs
// Condiciones con if: comparacin incorrecta
// Introduccin a C#, por Nacho Cabanes
using System;
Console.WriteLine("Introduce un nmero");
numero = Convert.ToInt32(Console.ReadLine());
if (numero = 0)
Nota: en lenguajes como C y C++, en los que s existe este riesgo de asignar un
valor en vez de comparar, se suele recomendar plantear la comparacin al revs,
colocando el nmero en el lado izquierdo, de modo que si olvidamos el doble
signo de "=", obtendramos una asignacin no vlida y el programa no compilara:
if (0 == numero) ...
Ejercicios propuestos:
(2.1.6.1) Crea una variante del ejemplo 02_01_06a, en la que la comparacin de
igualdad sea correcta y en la que las variables aparezcan en el lado derecho de la
comparacin y los nmeros en el lado izquierdo.
Es decir:
El inicio o el final del programa se indica dentro de un crculo.
Eso s, hay que tener en cuenta que sta es una notacin anticuada, y que no
permite representar de forma fiable las estructuras repetitivas que veremos
dentro de poco, por lo que su uso actual es muy limitado.
Ejercicios propuestos:
(2.1.7.1) Crea el diagrama de flujo para el programa que pide dos nmeros al
usuario y dice cul es el mayor de los dos.
(2.1.7.2) Crea el diagrama de flujo para el programa que pide al usuario dos
nmeros y dice si uno de ellos es positivo, si lo son los dos o si no lo es ninguno.
(2.1.7.3) Crea el diagrama de flujo para el programa que pide tres nmeros al
usuario y dice cul es el mayor de los tres.
y equivale a decir "si se cumple la condicin, toma el valor valor1; si no, toma el
valor valor2". Un ejemplo de cmo podramos usarlo sera para calcular el mayor
de dos nmeros:
numeroMayor = a>b ? a : b;
if ( a > b )
numeroMayor = a;
else
numeroMayor = b;
// Ejemplo_02_01_08a.cs
// El operador condicional
// Introduccin a C#, por Nacho Cabanes
using System;
int a, b, mayor;
mayor = a>b ? a : b;
Un segundo ejemplo, que sume o reste dos nmeros segn la opcin que se
escoja, sera:
// Ejemplo_02_01_08b.cs
// El operador condicional (2)
// Introduccin a C#, por Nacho Cabanes
using System;
Ejercicios propuestos:
(2.1.8.1) Crea un programa que use el operador condicional para mostrar un el
valor absoluto de un nmero de la siguiente forma: si el nmero es positivo, se
mostrar tal cual; si es negativo, se mostrar cambiado de signo.
(2.1.8.2) Usa el operador condicional para calcular el menor de dos nmeros.
2.1.9. switch
Si queremos ver varios posibles valores, sera muy pesado tener que hacerlo con
muchos "if" seguidos o encadenados. La alternativa es emplear la orden "switch",
cuya sintaxis es
switch (expresin)
{
case valor1: sentencia1;
break;
case valor2: sentencia2;
sentencia2b;
break;
case valor3:
goto case valor1;
...
case valorN: sentenciaN;
break;
default:
otraSentencia;
break;
}
Es decir:
Tras la palabra "switch" se escribe la expresin a analizar, entre parntesis.
Despus, tras varias rdenes "case" se indica cada uno de los valores
posibles.
Los pasos (porque pueden ser varios) que se deben dar si la expresin tiene
un cierto valor se indican a continuacin, terminando con "break".
Si hay que hacer algo en caso de que no se cumpla ninguna de las
condiciones, se detalla despus de la palabra "default".
Si dos casos tienen que hacer lo mismo, se aade "goto case" a uno de
ellos para indicarlo.
Vamos a ver un ejemplo, que diga si el smbolo que introduce el usuario es una
cifra numrica, un espacio u otro smbolo. Para ello usaremos un dato de tipo
"char" (carcter), que veremos con ms detalle en el prximo tema. De momento
nos basta que deberemos usar Convert.ToChar si lo leemos desde teclado con
ReadLine, y que le podemos dar un valor (o compararlo) usando comillas simples:
// Ejemplo_02_01_09a.cs
// La orden "switch" (1)
// Introduccin a C#, por Nacho Cabanes
using System;
switch (letra)
{
case ' ': Console.WriteLine("Espacio.");
break;
case '1': goto case '0';
case '2': goto case '0';
case '3': goto case '0';
case '4': goto case '0';
case '5': goto case '0';
case '6': goto case '0';
case '7': goto case '0';
case '8': goto case '0';
case '9': goto case '0';
case '0': Console.WriteLine("Dgito.");
break;
default: Console.WriteLine("Ni espacio ni dgito.");
break;
}
}
}
Cuidado quien venga del lenguaje C: en C se puede dejar que un caso sea
manejado por el siguiente, lo que se consigue si no se usa "break", mientras que
C# siempre obliga a usar "break" o "goto" al final de cada cada caso (para evitar
errores provocados por una "break" olvidado) con la nica excepcin de que un
caso no haga absolutamente nada excepto dejar pasar el control al siguiente caso,
y en ese caso se puede dejar totalmente vaco:
// Ejemplo_02_01_09b.cs
// La orden "switch" (variante sin break)
// Introduccin a C#, por Nacho Cabanes
using System;
switch (letra)
{
case ' ': Console.WriteLine("Espacio.");
break;
case '1':
case '2':
case '3':
case '4':
case '5':
Revisin 0.99zz Pgina 61
Introduccin a la programacin con C#, por Nacho Cabanes
case '6':
case '7':
case '8':
case '9':
case '0': Console.WriteLine("Dgito.");
break;
default: Console.WriteLine("Ni espacio ni dgito.");
break;
}
}
}
// Ejemplo_02_01_09c.cs
// La orden "switch" con cadenas de texto
// Introduccin a C#, por Nacho Cabanes
using System;
Console.WriteLine("Introduce tu nombre");
nombre = Console.ReadLine();
switch (nombre)
{
case "Juan": Console.WriteLine("Bienvenido, Juan.");
break;
case "Pedro": Console.WriteLine("Que tal estas, Pedro.");
break;
default: Console.WriteLine("Procede con cautela,
desconocido.");
break;
}
}
}
Ejercicios propuestos:
2.2.1. while
while (condicin)
sentencia;
Un ejemplo que nos diga si cada nmero que tecleemos es positivo o negativo, y
que termine cuando tecleemos el nmero 0, podra ser:
Revisin 0.99zz Pgina 63
Introduccin a la programacin con C#, por Nacho Cabanes
// Ejemplo_02_02_01_01a.cs
// La orden "while": mientras...
// Introduccin a C#, por Nacho Cabanes
using System;
while (numero != 0)
{
if (numero > 0) Console.WriteLine("Es positivo");
else Console.WriteLine("Es negativo");
Ejercicios propuestos:
(2.2.1.1.1) Crea un programa que pida al usuario su contrasea (numrica).
Deber terminar cuando introduzca como contrasea el nmero 1111, pero
volvrsela a pedir tantas veces como sea necesario.
(2.2.1.1.2) Crea un "calculador de cuadrados": pedir al usuario un nmero y
mostrar su cuadrado. Se repetir mientras el nmero introducido no sea cero
(usa "while" para conseguirlo).
(2.2.1.1.3) Crea un programa que pida de forma repetitiva pares de nmeros al
usuario. Tras introducir cada par de nmeros, responder si el primero es mltiplo
del segundo.
(2.2.1.1.4) Crea una versin mejorada del programa anterior, que, tras introducir
cada par de nmeros, responder si el primero es mltiplo del segundo, o el
segundo es mltiplo del primero, o ninguno de ellos es mltiplo del otro.
// Ejemplo_02_02_01_02a.cs
// Contar con "while"
// Introduccin a C#, por Nacho Cabanes
using System;
while (n < 6)
{
Console.WriteLine(n);
n = n + 1;
}
}
}
Ejercicios propuestos:
(2.2.1.2.1) Crea un programa que escriba en pantalla los nmeros del 1 al 10,
usando "while".
(2.2.1.2.2) Crea un programa que escriba en pantalla los nmeros pares del 26 al
10 (descendiendo), usando "while".
(2.2.1.2.3) Crea un programa calcule cuantas cifras tiene un nmero entero
positivo (pista: se puede hacer dividiendo varias veces entre 10).
(2.2.1.2.4) Crea el diagrama de flujo y la versin en C# de un programa que d al
usuario tres oportunidades para adivinar un nmero del 1 al 10.
do
sentencia;
while (condicin);
Al igual que en el caso anterior, si queremos que se repitan varias rdenes (es lo
habitual), deberemos encerrarlas entre llaves.
Como ejemplo, vamos a ver cmo sera el tpico programa que nos pide una clave
de acceso y no nos deja entrar hasta que tecleemos la clave correcta:
// Ejemplo_02_02_02a.cs
// La orden "do..while" (repetir..mientras)
// Introduccin a C#, por Nacho Cabanes
using System;
do
{
Console.Write("Introduzca su clave numrica: ");
clave = Convert.ToInt32(Console.ReadLine());
if (clave != valida)
Console.WriteLine("No vlida!");
}
while (clave != valida);
Console.WriteLine("Aceptada.");
}
}
Como veremos con detalle un poco ms adelante, si preferimos que la clave sea
un texto en vez de un nmero, los cambios al programa son mnimos, basta con
usar "string" e indicar su valor entre comillas dobles:
// Ejemplo_02_02_02b.cs
// La orden "do..while" (2)
// Introduccin a C#, por Nacho Cabanes
using System;
string clave;
do
{
Console.Write("Introduzca su clave: ");
clave = Console.ReadLine();
if (clave != valida)
Console.WriteLine("No vlida!");
}
while (clave != valida);
Console.WriteLine("Aceptada.");
}
}
Ejercicios propuestos:
(2.2.2.1) Crear un programa que pida nmeros positivos al usuario, y vaya
calculando y mostrando la suma de todos ellos (terminar cuando se teclea un
nmero negativo o cero).
(2.2.2.2) Crea un programa que escriba en pantalla los nmeros del 1 al 10,
usando "do..while".
(2.2.2.3) Crea un programa que escriba en pantalla los nmeros pares del 26 al 10
(descendiendo), usando "do..while".
(2.2.2.4) Crea un programa que pida al usuario su identificador y su contrasea
(ambos numricos), y no le permita seguir hasta que introduzca como
identificador "1234" y como contrasea "1111".
(2.2.2.5) Crea un programa que pida al usuario su identificador y su contrasea, y
no le permita seguir hasta que introduzca como nombre "Pedro" y como
contrasea "Peter".
2.2.3. for
sta es la orden que usaremos habitualmente para crear partes del programa que
se repitan un cierto nmero de veces. El formato de "for" es
As, para contar del 1 al 10, tendramos "1" como valor inicial, "<=10" como
condicin de repeticin, y el incremento sera de 1 en 1. Es muy habitual usar la
letra "i" como contador cuando se trata de tareas muy sencillas, as que el valor
inicial sera "i=1", la condicin de repeticin sera "i<=10" y el incremento sera
"i=i+1":
La orden para incrementar el valor de una variable ("i = i+1") se puede escribir de
la forma abreviada "i++", como veremos con ms detalle en el prximo tema, de
modo que la forma habitual de crear el contador anterior sera
En general, ser preferible usar nombres de variable ms descriptivos que "i". As,
un programa que escribiera los nmeros del 1 al 10 podra ser:
// Ejemplo_02_02_03a.cs
// Uso bsico de "for"
// Introduccin a C#, por Nacho Cabanes
using System;
Ejercicios propuestos:
(2.2.3.1) Crea un programa que muestre los nmeros del 10 al 20, ambos
incluidos.
(2.2.3.2) Crea un programa que escriba en pantalla los nmeros del 1 al 50 que
sean mltiplos de 3 (pista: habr que recorrer todos esos nmeros y ver si el resto
de la divisin entre 3 resulta 0).
(2.2.3.1.3) Crea un programa que muestre los nmeros del 100 al 200 (ambos
incluidos) que sean divisibles entre 7 y a la vez entre 3.
(2.2.3.4) Crea un programa que muestre la tabla de multiplicar del 9.
(2.2.3.5) Crea un programa que muestre los primeros ocho nmeros pares: 2 4 6 8
10 12 14 16 (pista: en cada pasada habr que aumentar de 2 en 2, o bien mostrar
el doble del valor que hace de contador).
(2.2.3.6) Crea un programa que muestre los nmeros del 15 al 5, descendiendo
(pista: en cada pasada habr que descontar 1, por ejemplo haciendo i=i-1, que se
puede abreviar i--).
for ( ; ; )
Tambin se puede crear un bucle sin fin usando "while" y usando "do..while", si se
indica una condicin que siempre vaya a ser cierta, como sta:
while (1 == 1)
Ejercicios propuestos:
(2.2.4.1) Crea un programa que contenga un bucle sin fin que escriba "Hola " en
pantalla, sin avanzar de lnea.
(2.2.4.2) Crea un programa que contenga un bucle sin fin que muestre los
nmeros enteros positivos a partir del uno.
// Ejemplo_02_02_05a.cs
// "for" anidados
// Introduccin a C#, por Nacho Cabanes
using System;
(Es decir: tenemos varias tablas, del 1 al 5, y para cada tabla queremos ver el
resultado que se obtiene al multiplicar por los nmeros del 1 al 10).
Ejercicios propuestos:
(2.2.5.1) Crea un programa escriba 4 veces los nmeros del 1 al 5, en una misma
lnea, usando "for": 12345123451234512345.
(2.2.5.2) Crea un programa escriba 4 veces los nmeros del 1 al 5, en una misma
lnea, usando "while": 12345123451234512345.
(2.2.5.3) Crea un programa que, para los nmeros entre el 10 y el 20 (ambos
incluidos) diga si son divisibles entre 5, si son divisibles entre 6 y si son divisibles
entre 7.
// Ejemplo_02_02_06a.cs
// "for" anidados (2)
// Introduccin a C#, por Nacho Cabanes
using System;
Console.WriteLine();
}
}
Revisin 0.99zz Pgina 70
Introduccin a la programacin con C#, por Nacho Cabanes
Ejercicios propuestos:
(2.2.6.1) Crea un programa que escriba 4 lneas de texto, cada una de las cuales
estar formada por los nmeros del 1 al 5.
(2.2.6.2) Crea un programa que pida al usuario el ancho (por ejemplo, 4) y el alto
(por ejemplo, 3) y escriba un rectngulo formado por esa cantidad de asteriscos:
****
****
****
// Ejemplo_02_02_07a.cs
// "for" que usa "char"
// Introduccin a C#, por Nacho Cabanes
using System;
// Ejemplo_02_02_07b.cs
// "for" que descuenta
// Introduccin a C#, por Nacho Cabanes
using System;
Ejercicios propuestos:
(2.2.7.1) Crea un programa que muestre las letras de la Z (mayscula) a la A
(mayscula, descendiendo).
(2.2.7.2) Crea un programa que muestre 5 veces las letras de la L (mayscula) a la
N (mayscula), en la misma lnea.
// Ejemplo_02_02_08a.cs
// Reutilizacion incorrecta de la variable de un "for"
// Introduccin a C#, por Nacho Cabanes
using System;
}
}
}
// Ejemplo_02_02_08b.cs
// Intento de reutilizacion incorrecta de la variable
// de un "for": no compila
// Introduccin a C#, por Nacho Cabanes
using System;
Esta idea sea puede aplicar a cualquier fuente que contenga un "for". Por ejemplo,
el fuente 2.2.6a, que mostraba varias tablas de multiplicar, se podra reescribir de
forma ms segura as:
// Ejemplo_02_02_08c.cs
// "for" anidados, variables en "for"
// Introduccin a C#, por Nacho Cabanes
using System;
Console.WriteLine();
}
}
}
Ejercicios propuestos:
(2.2.8.1) Crea un programa que escriba 6 lneas de texto, cada una de las cuales
estar formada por los nmeros del 1 al 7. Debes usar dos variables llamadas
"linea" y "numero", y ambas deben estar declaradas en el "for".
(2.2.8.2) Crea un programa que pida al usuario el ancho (por ejemplo, 4) y el alto
(por ejemplo, 3) y escriba un rectngulo formado por esa cantidad de asteriscos,
como en el ejercicio 2.2.6.2. Debers usar las variables "ancho" y "alto" para los
datos que pidas al usuario, y las variables "filaActual" y "columnaActual"
(declaradas en el "for") para el bloque repetitivo.
Por ejemplo, el siguiente fuente puede parecer correcto, pero si lo miramos con
detenimiento, veremos que la orden "Console.WriteLine" del final, aunque est
tabulada ms a la derecha, no forma parte de ningn "for", de modo que no se
repite, y no se dejar ningn espacio en blanco entre una tabla de multiplicar y la
siguiente, sino que slo se escribir una lnea en blanco al final, justo antes de
terminar el programa:
// Ejemplo_02_02_09a.cs
// "for" anidados de forma incorrecta, sin llaves
// Introduccin a C#, por Nacho Cabanes
using System;
Por eso, una alternativa recomendable es incluir siempre las llaves, aunque
inicialmente esperemos repetir slo una una orden:
Revisin 0.99zz Pgina 74
Introduccin a la programacin con C#, por Nacho Cabanes
// Ejemplo_02_02_09b.cs
// "for" anidados, variables en "for", llaves "redundantes"
// Introduccin a C#, por Nacho Cabanes
using System;
Console.WriteLine();
}
}
}
Ejercicios propuestos:
(2.2.9.1) Crea un programa que pida un nmero al usuario y escriba los mltiplos
de 9 que haya entre 1 ese nmero. Debes usar llaves en todas las estructuras de
control, aunque slo incluyan una sentencia.
(2.2.9.2) Crea un programa que pida al usuario dos nmeros y escriba sus
divisores comunes. Debes usar llaves en todas las estructuras de control, aunque
slo incluyan una sentencia.
// Ejemplo_02_02_10a.cs
// "for" interrumpido con "break"
// Introduccin a C#, por Nacho Cabanes
using System;
}
}
}
1 2 3 4
Es una orden que se debe tratar de evitar, porque puede conducir a programas
difciles de leer, en los que no se cumple la condicin de repeticin del bucle, sino
que se interrumpe por otros criterios. Como norma general, es preferible
reescribir la condicin del bucle de otra forma. En el ejemplo anterior, bastara que
la condicin fuera "contador < 5". Un ejemplo ligeramente ms complejo podra
ser mostrar los nmeros del 105 al 120 hasta encontrar uno que sea mltiplo de
13, que no se mostrar. Lo podramos hacer de esta forma (poco correcta):
// Ejemplo_02_02_10b.cs
// "for" interrumpido con "break" (2)
// Introduccin a C#, por Nacho Cabanes
using System;
// Ejemplo_02_02_10c.cs
// alternativa a un "for" interrumpido con "break"
// Introduccin a C#, por Nacho Cabanes
using System;
int contador=105;
Ejercicios propuestos:
(2.2.10.1) Crea un programa que pida al usuario dos nmeros y escriba su mximo
comn divisor (pista: una solucin lenta pero sencilla es probar con un "for" todos
los nmeros descendiendo a partir del menor de ambos, hasta llegar a 1; cuando
encuentres un nmero que sea divisor de ambos, interrumpes la bsqueda).
(2.2.10.2) Crea un programa que pida al usuario dos nmeros y escriba su mnimo
comn mltiplo (pista: una solucin lenta pero sencilla es probar con un "for"
todos los nmeros a partir del mayor de ambos, de forma creciente; cuando
encuentres un nmero que sea mltiplo de ambos, interrumpes la bsqueda).
// Ejemplo_02_02_11a.cs
// "for" interrumpido con "continue"
// Introduccin a C#, por Nacho Cabanes
using System;
1 2 3 4 6 7 8 9 10
// Ejemplo_02_02_11b.cs
// Alternativa a "for" interrumpido con "continue"
// Introduccin a C#, por Nacho Cabanes
using System;
Ejercicios propuestos:
(2.2.11.1) Crea un programa que escriba los nmeros del 20 al 10, descendiendo,
excepto el 13, usando "continue".
(2.2.11.2) Crea un programa que escriba los nmeros pares del 2 al 106, excepto
los que sean mltiplos de 10, usando "continue".
// Ejemplo_02_02_12a.cs
// "for" y "while" equivalente
// Introduccin a C#, por Nacho Cabanes
using System;
Console.WriteLine();
int n=1;
Revisin 0.99zz Pgina 78
Introduccin a la programacin con C#, por Nacho Cabanes
while (n<=10)
{
Console.Write("{0} ", n);
n++;
}
}
}
// Ejemplo_02_02_12b.cs
// "for" y "while" equivalente... con "continue"
// Introduccin a C#, por Nacho Cabanes
using System;
Console.WriteLine();
int n=1;
while (n<=10)
{
if (n == 5)
continue;
Console.Write("{0} ", n);
n++;
}
}
}
En este caso, el "for" muestra todos los valores menos el 5, pero en el "while" se
provoca un bucle sin fin y el programa se queda "colgado" tras escribir el nmero
4, porque cuando se llega al nmero 5, la orden "continue" hace que dicho valor
no se escriba, pero que tampoco se incremente la variable, de modo que nunca se
llega a pasar del 5.
Ejercicios propuestos:
(2.2.12.1) Crea un programa que escriba los nmeros del 100 al 200, separados
por un espacio, sin avanzar de lnea, usando "for". En la siguiente lnea, vuelve a
escribirlos usando "while".
Revisin 0.99zz Pgina 79
Introduccin a la programacin con C#, por Nacho Cabanes
(2.2.12.2) Crea un programa que escriba los nmeros pares del 20 al 10,
descendiendo, excepto el 14, primero con "for" y luego con "while".
Respuesta: escribe 5, porque no hay llaves tras el "for", luego slo se repite
la orden "if".
El formato de "goto" es
goto donde;
donde:
// Ejemplo_02_03a.cs
// "for" y "goto"
// Introduccin a C#, por Nacho Cabanes
using System;
salida:
Console.Write("Fin del programa");
}
}
i vale 0 y j vale 0.
i vale 0 y j vale 2.
i vale 0 y j vale 4.
i vale 0 y j vale 6.
i vale 0 y j vale 8.
i vale 0 y j vale 10.
i vale 0 y j vale 12.
i vale 0 y j vale 14.
i vale 0 y j vale 16.
i vale 0 y j vale 18.
i vale 0 y j vale 20.
i vale 1 y j vale 0.
i vale 1 y j vale 2.
i vale 1 y j vale 4.
i vale 1 y j vale 6.
Fin del programa
Ejercicios propuestos:
(2.3.1) Crea un programa que escriba los nmeros del 1 al 10, separados por un
espacio, sin avanzar de lnea. No puedes usar "for", ni "while", ni "do..while", slo
"if" y "goto".
Por su parte, un bucle "while" se vera como una condicin que hace que algo se
repita (una flecha que vuelve hacia atrs, al punto en el que se comprobaba la
condicin):
Pedir nmero n1
Pedir nmero n2
n1>n2?
si no
Decir "n1 es Decir "n2 es
mayor" mayor"
Abrir fichero
Mientras haya datos en fichero
Leer dato
Mostrar dato
Cerrar fichero
2.5. foreach
Nos queda por ver otra orden que permite hacer cosas repetitivas: "foreach" (se
traducira "para cada"). La veremos ms adelante, cuando manejemos estructuras
de datos ms complejas, que es en las que la nos resultar til para extraer los
datos de uno en uno. De momento, el nico dato compuesto que hemos visto (y
todava con muy poco detalle) es la cadena de texto, "string", de la que podramos
obtener las letras una a una con "foreach" as:
// Ejemplo_02_05a.cs
// Primer ejemplo de "foreach"
// Introduccin a C#, por Nacho Cabanes
Revisin 0.99zz Pgina 85
Introduccin a la programacin con C#, por Nacho Cabanes
using System;
Ejercicios propuestos:
(2.5.1) Crea un programa que cuente cuantas veces aparece la letra 'a' en una
frase que teclee el usuario, utilizando "foreach".
Ejercicios propuestos:
(2.6.1) Crear un programa que d al usuario la oportunidad de adivinar un nmero
del 1 al 100 (prefijado en el programa) en un mximo de 6 intentos. En cada
pasada deber avisar de si se ha pasado o se ha quedado corto.
(2.6.2) Crear un programa que descomponga un nmero (que teclee el usuario)
como producto de su factores primos. Por ejemplo, 60 = 2 2 3 5
Revisin 0.99zz Pgina 86
Introduccin a la programacin con C#, por Nacho Cabanes
Precio? 44
Pagado? 100
Su cambio es de 56: 50 5 1
Precio? 1
Pagado? 100
Su cambio es de 99: 50 20 20 5 2 2
(2.6.9) Crea un programa que "dibuje" un cuadrado formado por cifras sucesivas,
con el tamao que indique el usuario, hasta un mximo de 9. Por ejemplo, si desea
tamao 5, el cuadrado sera as:
11111
Revisin 0.99zz Pgina 87
Introduccin a la programacin con C#, por Nacho Cabanes
22222
33333
44444
55555
// Ejemplo_02_07a.cs
// Excepciones (1)
// Introduccin a C#, por Nacho Cabanes
using System;
try
{
Console.WriteLine("Introduzca el primer numero");
numero1 = Convert.ToInt32( Console.ReadLine() );
// Ejemplo_02_07b.cs
// Excepciones (2)
// Introduccin a C#, por Nacho Cabanes
using System;
{
int numero1, numero2, resultado;
try
{
Console.WriteLine("Introduzca el primer numero");
numero1 = Convert.ToInt32( Console.ReadLine() );
catch (FormatException)
{
Console.WriteLine("No es un nmero vlido");
}
catch (DivideByZeroException)
{
Console.WriteLine("No se puede dividir entre cero");
}
}
}
Como se ve en este ejemplo, si no vamos a usar detalles adicionales del error que
ha afectado al programa, no necesitamos declarar ninguna variable de tipo
Exception: nos basta con construcciones como "catch (FormatException)" en vez de
"catch (FormatException e)".
Ejercicios propuestos:
(2.7.1) Crea un programa que pregunte al usuario su edad y su ao de nacimiento.
Si la edad que introduce no es un nmero vlido, mostrar un mensaje de aviso.
Lo mismo ocurrir si el ao de nacimiento no es un nmero vlido.
Como se puede observar en esta tabla, el tipo de dato ms razonable para guardar
edades sera "byte", que permite valores entre 0 y 255, y ocupa 3 bytes menos que
un "int".
// Ejemplo_03_01_01a.cs
// Tipos de nmeros enteros
// Introduccin a C#, por Nacho Cabanes
using System;
Ejercicios propuestos:
(3.1.1.1) Calcula el producto de 1.000.000 por 1.000.000, usando una variable
llamada "producto", de tipo "long". Prueba tambin a calcularlo usando una
variable de tipo "int".
// Ejemplo_03_01_02a.cs
// Conversiones para otros tipos de nmeros enteros
// Introduccin a C#, por Nacho Cabanes
using System;
Ejercicios propuestos:
(3.1.2.1) Pregunta al usuario su edad, que se guardar en un "byte". A
continuacin, le debers decir que no aparenta tantos aos (por ejemplo, "No
aparentas 20 aos").
(3.1.2.2) Pide al usuario dos nmeros de dos cifras ("byte"), calcula su
multiplicacin, que se deber guardar en un "ushort", y muestra el resultado en
pantalla.
(3.1.2.3) Pide al usuario dos nmeros enteros largos ("long") y muestra su suma,
su resta y su producto.
a = a + 1;
Pues bien, en C# (y en otros lenguajes que derivan de C, como C++, Java y PHP),
existe una notacin ms compacta para esta operacin, y para la opuesta (el
decremento):
// Ejemplo_03_01_03a.cs
// Incremento y decremento
// Introduccin a C#, por Nacho Cabanes
using System;
b = a++;
As, si "a" vala 2, lo que esta instruccin hace es dar a "b" el valor de "a" y
aumentar el valor de "a". Por tanto, al final tenemos que b=2 y a=3
(postincremento: se incrementa "a" tras asignar su valor).
En cambio, si escribimos
b = ++a;
Ejercicios propuestos:
(3.1.3.1) Crea un programa que use tres variables x,y,z. Sus valores iniciales sern
15, -10, 2.147.483.647. Se deber incrementar el valor de estas variables. Qu
valores esperas que se obtengan? Contrstalo con el resultado obtenido por el
programa.
(3.1.3.2) Cul sera el resultado de las siguientes operaciones? a=5; b=++a; c=a++;
b=b*5; a=a*2; Calclalo a mano y luego crea un programa que lo resuelva, para
ver si habas hallado la solucin correcta.
Revisin 0.99zz Pgina 95
Introduccin a la programacin con C#, por Nacho Cabanes
// Ejemplo_03_01_04a.cs
// Operaciones abreviadas
// Introduccin a C#, por Nacho Cabanes
using System;
Ejercicios propuestos:
(3.1.4.1) Crea un programa que use tres variables x,y,z. Sus valores iniciales sern
15, -10, 214. Debers incrementar el valor de estas variables en 12, usando el
formato abreviado. Qu valores esperas que se obtengan? Contrstalo con el
resultado obtenido por el programa.
(3.1.4.2) Cul sera el resultado de las siguientes operaciones? a=5; b=a+2; b-=3;
c=-3; c*=2; ++c; a*=b; Crea un programa que te lo muestre.
a = b = c = 1;
// Ejemplo_03_01_05a.cs
// Asignaciones mltiples
// Introduccin a C#, por Nacho Cabanes
using System;
// Ejemplo_03_02_01a.cs
// Nmeros reales (1: float)
// Introduccin a C#, por Nacho Cabanes
Revisin 0.99zz Pgina 97
Introduccin a la programacin con C#, por Nacho Cabanes
using System;
float f1 = 2, f2 = 3;
float divisionF;
Ejercicios propuestos:
(3.2.1.1) Crea un programa que muestre el resultado de dividir 3 entre 4 usando
nmeros enteros y luego usando nmeros de coma flotante.
(3.2.1.2) Cul sera el resultado de las siguientes operaciones, usando nmeros
reales? a=5; a/=2; a+=1; a*=3; --a;
// Ejemplo_03_02_02a.cs
// Nmeros reales (2: double)
// Introduccin a C#, por Nacho Cabanes
using System;
float x = 2;
Otra forma alternativa es forzar una "conversin de tipos", como veremos dentro
de muy poco.
As, podemos crear un programa que pida al usuario el radio de una circunferencia
(que ser un nmero entero) para mostrar la longitud de la circunferencia (cuyo
valor ser 2 * PI * radio) podra ser:
// Ejemplo_03_02_02a.cs
// Nmeros reales: valor inicial de un float
// Introduccin a C#, por Nacho Cabanes
using System;
Console.WriteLine("Introduce el radio");
radio = Convert.ToInt32(Console.ReadLine());
Console.WriteLine("La longitud de la circunferencia es");
Console.WriteLine(2 * pi * radio);
}
}
Ejercicios propuestos:
(3.2.2.1) Crea un programa que muestre el resultado de dividir 13 entre 6 usando
nmeros enteros, luego usando nmeros de coma flotante de simple precisin y
luego con nmeros de doble precisin.
(3.2.2.2) Calcula el rea de un crculo, dado su radio, que ser un nmero entero
(rea = pi * radio al cuadrado)
// Ejemplo_03_02_03a.cs
Revisin 0.99zz Pgina 100
Introduccin a la programacin con C#, por Nacho Cabanes
using System;
Ejercicios propuestos:
(3.2.3.1) Calcula el volumen de una esfera, dado su radio, que ser un nmero de
doble precisin (volumen = pi * radio al cubo * 4/3)
(3.2.3.2) Crea un programa que pida al usuario a una distancia (en metros) y el
tiempo necesario para recorrerla (como tres nmeros: horas, minutos, segundos),
y muestre la velocidad, en metros por segundo, en kilmetros por hora y en millas
por hora (pista: 1 milla = 1.609 metros).
(3.2.3.3) Halla las soluciones de una ecuacin de segundo grado del tipo y = Ax 2 +
Bx + C. Pista: la raz cuadrada de un nmero x se calcula con Math.Sqrt(x)
(3.2.3.4) Si se ingresan E euros en el banco a un cierto inters I durante N aos, el
dinero obtenido viene dado por la frmula del inters compuesto: Resultado = e
Revisin 0.99zz Pgina 101
Introduccin a la programacin con C#, por Nacho Cabanes
(1+ i)n Aplicarlo para calcular en cuanto se convierten 1.000 euros al cabo de 10
aos al 3% de inters anual.
(3.2.3.5) Crea un programa que muestre los primeros 20 valores de la funcin y =
x2 - 1
(3.2.3.6) Crea un programa que "dibuje" la grfica de y = (x-5)2 para valores de x
entre 1 y 10. Deber hacerlo dibujando varios espacios en pantalla y luego un
asterisco. La cantidad de espacios depender del valor obtenido para "y".
(3.2.3.7) Escribe un programa que calcule una aproximacin de PI mediante la
expresin: pi/4 = 1/1 - 1/3 + 1/5 - 1/7 + 1/9 - 1/11 + 1/13 ... El usuario deber
indicar la cantidad de trminos a utilizar, y el programa mostrar todos los
resultados hasta esa cantidad de trminos. Debes hacer todas las operacion con
"double".
// Ejemplo_03_02_04a.cs
// Nmeros reales: typecast
// Introduccin a C#, por Nacho Cabanes
using System;
Console.WriteLine("Introduce el radio");
radio = Convert.ToDouble(Console.ReadLine());
longitud = 2 * pi * radio;
Revisin 0.99zz Pgina 102
Introduccin a la programacin con C#, por Nacho Cabanes
Su resultado sera:
Introduce el radio
2,3456789
La longitud de la circunferencia es
14,7383356099727
Y con simple precisin
14,73834
Y como nmero entero
14
Ejercicios propuestos:
(3.2.4.1) Crea un programa que calcule la raz cuadrada del nmero que
introduzca el usuario. La raz se deber calcular como "double", pero el resultado
se mostrar como "float"
(3.2.4.2) Crea una nueva versin del un programa que calcula una aproximacin
de PI mediante la expresin: pi/4 = 1/1 - 1/3 + 1/5 - 1/7 + 1/9 - 1/11 + 1/13 (...) con
tantos trminos como indique el usuario. Debes hacer todas las operacion con
"double", pero mostrar el resultado como "float".
Una forma de conseguirlo es crear una cadena de texto a partir del nmero,
usando "ToString". A esta orden se le puede indicar un dato adicional, que es el
formato numrico que queremos usar, por ejemplo: suma.ToString("0.00")
// Ejemplo_03_02_05a.cs
// Formato de nmeros reales
// Introduccin a C#, por Nacho Cabanes
using System;
Console.WriteLine( numero.ToString("N1") );
Console.WriteLine( numero.ToString("N3") );
Console.WriteLine( numero.ToString("0.0") );
Console.WriteLine( numero.ToString("0.000") );
Console.WriteLine( numero.ToString("#.#") );
Console.WriteLine( numero.ToString("#.###") );
}
}
12,3
12,340
12,3
12,340
12,3
12,34
Ejercicios propuestos:
(3.2.5.1) El usuario de nuestro programa podr teclear dos nmeros de hasta 12
cifras significativas. El programa deber mostrar el resultado de dividir el primer
nmero entre el segundo, utilizando tres cifras decimales.
(3.2.5.2) Crea un programa que use tres variables x,y,z. Las tres sern nmeros
reales, y nos bastar con dos cifras decimales. Se deber pedir al usuario los
valores para las tres variables y mostrar en pantalla el valor de x 2 + y - z (con
exactamente dos cifras decimales).
(3.2.5.3) Calcula el permetro, rea y diagonal de un rectngulo, a partir de su
ancho y alto (permetro = suma de los cuatro lados; rea = base x altura; diagonal,
usando el teorema de Pitgoras). Muestra todos ellos con una cifra decimal.
(3.2.5.4) Calcula la superficie y el volumen de una esfera, a partir de su radio
(superficie = 4 * pi * radio al cuadrado; volumen = 4/3 * pi * radio al cubo). Usa
datos "doble" y muestra los resultados con 5 cifras decimales.
// Ejemplo_03_02_06a.cs
// De decimal a hexadecimal y binario
// Introduccin a C#, por Nacho Cabanes
using System;
Su resultado sera:
f7
11110111
Revisin 0.99zz Pgina 105
Introduccin a la programacin con C#, por Nacho Cabanes
(Si quieres saber ms sobre el sistema hexadecimal, mira los apndices al final de
este texto)
Ejercicios propuestos:
(3.2.6.1) Crea un programa que pida nmeros (en base 10) al usuario y muestre su
equivalente en sistema binario y en hexadecimal. Debe repetirse hasta que el
usuario introduzca el nmero 0.
(3.2.6.2) Crea un programa que pida al usuario la cantidad de rojo (por ejemplo,
255), verde (por ejemplo, 160) y azul (por ejemplo, 0) que tiene un color, y que
muestre ese color RGB en notacin hexadecimal (por ejemplo, FFA000).
(3.2.6.3) Crea un programa para mostrar los nmeros del 0 a 255 en hexadecimal,
en 16 filas de 16 columnas cada una (la primera fila contendr los nmeros del 0 al
15 decimal-, la segunda del 16 al 31 decimal- y as sucesivamente).
// Ejemplo_03_02_06b.cs
// De hexadecimal y binario a decimal
// Introduccin a C#, por Nacho Cabanes
using System;
Que mostrara:
19 26 13 12 201
Ejercicios propuestos:
(3.2.6.4) Crea un programa que pida nmeros binarios al usuario y muestre su
equivalente en sistema hexadecimal y en decimal. Debe repetirse hasta que el
usuario introduzca la palabra "fin".
char letra;
letra = 'a';
Para leer valores desde teclado, lo podemos hacer de forma similar a los casos
anteriores: leemos toda una frase (que debera tener slo una letra) con ReadLine
y convertimos a tipo "char" usando Convert.ToChar:
letra = Convert.ToChar(Console.ReadLine());
As, un programa que de un valor inicial a una letra, la muestre, lea una nueva letra
tecleada por el usuario, y la muestre, podra ser:
// Ejemplo_03_03_01a.cs
// Tipo de datos "char"
// Introduccin a C#, por Nacho Cabanes
using System;
letra = 'a';
Console.WriteLine("La letra es {0}", letra);
Revisin 0.99zz Pgina 107
Introduccin a la programacin con C#, por Nacho Cabanes
Ejercicios propuestos
(3.3.1.1) Crea un programa que pida una letra al usuario y diga si se trata de una
vocal.
(3.3.1.2) Crea un programa que muestre una de cada dos letras entre la que teclee
el usuario y la "z". Por ejemplo, si el usuario introduce una "a", se escribir
"aceg...".
(3.3.1.3) Crea un programa que pida al usuario el ancho (por ejemplo, 4) y el alto
(por ejemplo, 3) y una letra (por ejemplo, X) y escriba un rectngulo formado por
esa cantidad de letras:
XXXX
XXXX
XXXX
Secuencia Significado
\a Emite un pitido
\b Retroceso (permite borrar el ltimo carcter)
\f Avance de pgina (expulsa una hoja en la impresora)
\n Avanza de lnea (salta a la lnea siguiente)
\r Retorno de carro (va al principio de la lnea)
\t Salto de tabulacin horizontal
\v Salto de tabulacin vertical
Revisin 0.99zz Pgina 108
Introduccin a la programacin con C#, por Nacho Cabanes
// Ejemplo_03_03_02a.cs
// Secuencias de escape
// Introduccin a C#, por Nacho Cabanes
using System;
Juguemos mas:
otro salto
Comillas dobles: " y simples ', y barra \
ruta = @"c:\datos\ejemplos\curso\ejemplo1"
Ejercicio propuesto
(3.3.2.1) Crea un programa que pida al usuario que teclee cuatro letras y las
muestre en pantalla juntas, pero en orden inverso, y entre comillas dobles. Por
ejemplo si las letras que se teclean son a, l, o, h, escribira "hola".
As, un ejemplo que diera un valor a un "string", lo mostrara (entre comillas, para
practicar las secuencias de escape que hemos visto en el apartado anterior) y
leyera un valor tecleado por el usuario podra ser:
// Ejemplo_03_04a.cs
// Uso basico de "string"
// Introduccin a C#, por Nacho Cabanes
using System;
if (frase == "Hola!")
Console.WriteLine("Hola a ti tambin! ");
}
}
Ejercicios propuestos:
(3.4.1) Crear un programa que pida al usuario su nombre, y le diga "Hola" si se
llama "Juan", o bien le diga "No te conozco" si teclea otro nombre.
(3.4.2) Crear un programa que pida al usuario un nombre y una contrasea. La
contrasea se debe introducir dos veces. Si las dos contraseas no son iguales, se
avisar al usuario y se le volvern a pedir las dos contraseas.
bool encontrado;
encontrado = true;
Este tipo de datos har que podamos escribir de forma sencilla algunas
condiciones que podran resultar complejas. As podemos hacer que ciertos
fragmentos de nuestro programa no sean "if ((vidas == 0) || (tiempo == 0) ||
((enemigos == 0) && (nivel == ultimoNivel)))" sino simplemente "if
(partidaTerminada) ..."
A las variables "bool" tambin se le puede dar como valor el resultado de una
comparacin:
// Ejemplo bsico
partidaTerminada = false;
if (vidas == 0) partidaTerminada = true;
// Notacin alternativa, sin usar "if"
partidaTerminada = vidas == 0;
// Ejemplo ms desarrollado
if (enemigos ==0) && (nivel == ultimoNivel)
partidaTerminada = true;
else
partidaTerminada = true;
// Notacin alternativa, sin usar "if"
partidaTerminada = (enemigos ==0) && (nivel == ultimoNivel);
// Ejemplo_03_05a.cs
// Variables bool
// Introduccin a C#, por Nacho Cabanes
using System;
if (esCifra)
Console.WriteLine("Es una cifra numrica.");
else if (esVocal)
Console.WriteLine("Es una vocal.");
else
Console.WriteLine("Es una consonante u otro smbolo.");
}
}
Ejercicios propuestos:
(3.5.1) Crea un programa que use el operador condicional para dar a una variable
llamada "iguales" (booleana) el valor "true" si los dos nmeros que ha tecleado el
usuario son iguales, o "false" si son distintos.
(3.5.2) Crea una versin alternativa del ejercicio 3.5.1, que use "if" en vez del
operador condicional.
(3.5.3) Crea un programa que use el operador condicional para dar a una variable
llamada "ambosPares" (booleana) el valor "true" si dos nmeros introducidos por
el usuario son pares, o "false" si alguno es impar.
(3.5.4) Crea una versin alternativa del ejercicio 3.5.3, que use "if" en vez del
operador condicional.
int[] ejemplo;
Cuando sepamos cuantos datos vamos a guardar (por ejemplo 4), podremos
reservar espacio con la orden "new", as:
ejemplo[0] = 15;
// Ejemplo_04_01_01a.cs
// Primer ejemplo de tablas (arrays)
// Introduccin a C#, por Nacho Cabanes
Revisin 0.99zz Pgina 113
Introduccin a la programacin con C#, por Nacho Cabanes
using System;
Ejercicios propuestos:
(4.1.1.1) Un programa que pida al usuario 4 nmeros, los memorice (utilizando un
array), calcule su media aritmtica y despus muestre en pantalla la media y los
datos tecleados.
(4.1.1.2) Un programa que pida al usuario 5 nmeros reales (pista: necesitars un
array de "float") y luego los muestre en el orden contrario al que se introdujeron.
// Ejemplo_04_01_02a.cs
// Segundo ejemplo de tablas: valores iniciales con []
// Introduccin a C#, por Nacho Cabanes
using System;
Ejercicios propuestos:
(4.1.2.1) Un programa que almacene en una tabla el nmero de das que tiene
cada mes (supondremos que es un ao no bisiesto), pida al usuario que le indique
un mes (1=enero, 12=diciembre) y muestre en pantalla el nmero de das que
tiene ese mes.
// Ejemplo_04_01_03a.cs
// Tercer ejemplo de tablas: valores iniciales con llaves
// y recorrido con "for"
// Introduccin a C#, por Nacho Cabanes
using System;
Revisin 0.99zz Pgina 115
Introduccin a la programacin con C#, por Nacho Cabanes
En este caso, que slo sumbamos 5 nmeros, no hemos escrito mucho menos,
pero si trabajsemos con 100, 500 o 1000 nmeros, la ganancia en comodidad s
que sera evidente.
// Ejemplo_04_01_03b.cs
// Cuarto ejemplo de tablas: introducir datos repetitvos
// Introduccin a C#, por Nacho Cabanes
using System;
Ejercicios propuestos:
(4.1.3.1) Crea un programa que pida al usuario 6 nmeros enteros cortos y luego
los muestre en orden inverso (pista: usa un array para almacenarlos y "for" para
mostrarlos).
(4.1.3.2) Crea un programa que pregunte al usuario cuntos nmeros enteros va a
introducir (por ejemplo, 10), le pida todos esos nmeros, los guarde en un array y
finalmente calcule y muestre la media de esos nmeros.
(4.1.3.3) Un programa que pida al usuario 10 reales de doble precisin, calcule su
media y luego muestre los que estn por encima de la media.
(4.1.3.4) Un programa que almacene en una tabla el nmero de das que tiene
cada mes (de un ao no bisiesto), pida al usuario que le indique un mes (ej. 2 para
febrero) y un da (ej. el da 15) y diga qu nmero de da es dentro del ao (por
ejemplo, el 15 de febrero sera el da nmero 46, el 31 de diciembre sera el da
365).
(4.1.3.5) A partir del ejercicio anterior, crea otro que pida al usuario que le indique
la fecha, formada por da (1 al 31) y el mes (1=enero, 12=diciembre), y como
respuesta muestre en pantalla el nmero de das que quedan hasta final de ao.
(4.1.3.6) Un programa que pida 10 nombres y los memorice (pista: esta vez se
trata de un array de "string"). Despus deber pedir que se teclee un nombre y
dir si se encuentra o no entre los 10 que se han tecleado antes. Volver a pedir
otro nombre y a decir si se encuentra entre ellos, y as sucesivamente hasta que se
teclee "fin".
(4.1.3.7) Un programa que prepare espacio para guardar un mximo de 100
nombres. El usuario deber ir introduciendo un nombre cada vez, hasta que se
pulse Intro sin teclear nada, momento en el que dejarn de pedirse ms nombres
y se mostrar en pantalla la lista de los nombres que se han introducido.
(4.1.3.8) Un programa que reserve espacio para un vector de 3 componentes, pida
al usuario valores para dichas componentes (por ejemplo [2, -5, 7]) y muestre su
mdulo (raz cuadrada de la suma de sus componentes al cuadrado).
(4.1.3.9) Un programa que reserve espacio para dos vectores de 3 componentes,
pida al usuario sus valores y calcule la suma de ambos vectores (su primera
componente ser x1+y1, la segunda ser x2+y2 y as sucesivamente).
(4.1.3.10) Un programa que reserve espacio para dos vectores de 3 componentes,
pida al usuario sus valores y calcule su producto escalar (x1y1+ x2y2+x3y3).
(4.1.3.11) Un programa que pida al usuario 4 nmeros enteros y calcule (y
muestre) cul es el mayor de ellos. Nota: para calcular el mayor valor de un array,
hay que comparar cada uno de los valores que tiene almacenados el array con el
que hasta ese momento es el mximo provisional. El valor inicial de este mximo
provisional no debera ser cero (porque el resultado sera incorrecto si todos los
nmeros son negativos), sino el primer elemento del array.
Para ver si un dato existe, habr que recorrer todo el array, comparando el valor
almacenado con el dato que se busca. Puede interesarnos simplemente saber si
est o no (con lo que se podra interrumpir la bsqueda en cuanto aparezca una
primera vez) o ver en qu posiciones se encuentra (para lo que habra que recorrer
todo el array). Si el array estuviera ordenado, se podra buscar de una forma ms
rpida, pero la veremos ms adelante.
Para poder aadir un dato al final de los ya existentes, necesitamos que el array
no est completamente lleno, y llevar un contador de cuntas posiciones hay
ocupadas, de modo que seamos capaces de guardar el dato en la primera posicin
libre.
Para insertar un dato en una cierta posicin, los que queden detrs debern
desplazarse "hacia la derecha" para dejarle hueco. Este movimiento debe empezar
desde el final para que cada dato que se mueve no destruya el que estaba a
continuacin de l. Tambin habr que actualizar el contador, para indicar que
queda una posicin libre menos.
Si se quiere borrar el dato que hay en una cierta posicin, los que estaban a
continuacin debern desplazarse "hacia la izquierda" para que no queden
huecos. Como en el caso anterior, habr que actualizar el contador, pero ahora
para indicar que queda una posicin libre ms.
// Ejemplo_04_01_04a.cs
Revisin 0.99zz Pgina 118
Introduccin a la programacin con C#, por Nacho Cabanes
using System;
// Mostramos el array
for (i=0; i<cantidad; i++)
Console.Write("{0} ",datos[i]);
Console.WriteLine();
// Buscamos el mximo
int maximo = datos[0];
for (i=1; i<cantidad; i++)
if (datos[i] > maximo)
maximo = datos[i];
Console.WriteLine("El mximo es {0} ", maximo);
{
Console.WriteLine("Insertando 30 en la posicin 3");
int posicionInsertar = 2;
for (i=cantidad; i>posicionInsertar; i--)
datos[i] = datos[i-1];
datos[posicionInsertar] = 30;
cantidad++;
}
10 15 12
15 encontrado en la posicin 2
El mximo es 15
Aadiendo 6 al final
10 15 12 6
Borrando el segundo dato
10 12 6
Insertando 30 en la posicin 3
10 12 30 6
Este programa "no dice nada" cuando no se encuentra el dato que se est
buscando. Se puede mejorar usando una variable "booleana" que nos sirva de
testigo, de forma que al final nos avise si el dato no exista (no sirve emplear un
"else", porque en cada pasada del bucle "for" no sabemos si el dato no existe, slo
sabemos que no est en la posicin actual).
Ejercicios propuestos:
(4.1.4.1) Crea una variante del ejemplo anterior (04_01_04a) que pida al usuario el
dato a buscar, avise si ese dato no aparece, y que diga cuntas veces se ha
encontrado en caso contrario.
(4.1.4.2) Crea una variante del ejemplo anterior (04_01_04a) que aada un dato
introducido por el usuario al final de los datos existentes.
(4.1.4.3) Crea una variante del ejemplo anterior (04_01_04a) que inserte un dato
introducido por el usuario en la posicin que elija el usuario. Debe avisar si la
posicin escogida es incorrecta (porque est ms all del final de los datos).
(4.1.4.4) Crea una variante del ejemplo anterior (04_01_04a) que borre el dato que
se encuentre en la posicin que elija el usuario. Debe avisar si la posicin escogida
no es vlida.
4.1.5. Constantes
En ocasiones, manejaremos valores que realmente no van a variar. Es el caso del
tamao mximo de un array. En esos casos, por legibilidad y por facilidad de
mantenimiento del programa, puede ser preferible no usar el valor numrico, sino
una variable. Dado que el valor de sta no debera cambiar, podemos usar la
palabra "const" para indicar que debe ser constante, y el compilador no permitir
que la modifiquemos por error. Adems, por convenio, para que sea fcil
distinguir una constante de una variable, se suele escribir su nombre totalmente
en maysculas:
As, una nueva versin del fuente del apartado 4.1.3 (b), usando una constante
para la capacidad del array podra ser:
// Ejemplo_04_01_05a.cs
// Quinto ejemplo de tablas: constantes
// Introduccin a C#, por Nacho Cabanes
using System;
Ejercicios propuestos:
(4.1.5.1) Crea un programa que contenga un array con los nombres de los meses
del ao. El usuario podr elegir entre verlos en orden natural (de Enero a
Diciembre) o en orden inverso (de Diciembre a Enero). Usa constantes para el
valor mximo del array en ambos recorridos.
// Ejemplo_04_02a.cs
// Array de dos dimensiones "rectangulares" (estilo Pascal)
// Introduccin a C#, por Nacho Cabanes
using System;
Este tipo de tablas de varias dimensiones son las que se usan tambin para
guardar matrices, cuando se trata de resolver problemas matemticos ms
complejos que los que hemos visto hasta ahora. Si ya has estudiado la teora de
matrices, ms adelante tienes algunos ejercicios propuestos para aplicar esos
conocimientos al uso de arrays bidimensionales.
La otra forma de tener arrays multidimensionales son los "arrays de arrays", que,
como ya hemos comentado, y como veremos en este ejemplo, pueden tener
elementos de distinto tamao. En ese caso nos puede interesar saber su longitud,
para lo que podemos usar "a.Length":
// Ejemplo_04_02b.cs
// Array de arrays (array de dos dimensiones al estilo C)
// Introduccin a C#, por Nacho Cabanes
Revisin 0.99zz Pgina 123
Introduccin a la programacin con C#, por Nacho Cabanes
using System;
} // Fin de "Main"
}
0123456789
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
2 3 4 5 6 7 8 9 10 11 12 13
Ejercicios propuestos:
(4.2.1) Un programa que pida al usuario dos bloques de 10 nmeros enteros
(usando un array de dos dimensiones). Despus deber mostrar el mayor dato
que se ha introducido en cada uno de ellos.
(4.2.2) Un programa que pida al usuario dos bloques de 6 cadenas de texto.
Despus pedir al usuario una nueva cadena y comprobar si aparece en alguno
de los dos bloques de informacin anteriores.
(4.2.3) Un programa que pregunte al usuario el tamao que tendrn dos bloques
de nmeros enteros (por ejemplo, uno de 10 elementos y otro de 12). Luego
Revisin 0.99zz Pgina 124
Introduccin a la programacin con C#, por Nacho Cabanes
pedir los datos para ambos bloques de datos. Finalmente deber mostrar el
mayor dato que se ha introducido en cada uno de ellos.
Los datos que forman un "struct" pueden ser pblicos o privados. Por ahora, a
nosotros nos interesar que sean accesibles desde el resto de nuestro programa,
por lo que siempre les aadiremos delante la palabra "public" para indicar que
queremos que sean pblicos.
Ya desde el cuerpo del programa, para acceder a cada uno de los datos que
forman el registro, tanto si queremos leer su valor como si queremos cambiarlo,
se debe indicar el nombre de la variable y el del dato (o campo) separados por un
punto:
// Ejemplo_04_03_01a.cs
// Registros (struct)
// Introduccin a C#, por Nacho Cabanes
using System;
persona.nombre = "Juan";
persona.inicial = 'J';
persona.edad = 20;
persona.nota = 7.5f;
Console.WriteLine("La edad de {0} es {1}",
persona.nombre, persona.edad);
}
}
Ejercicios propuestos:
(4.3.1.1) Crea un "struct" que almacene datos de una cancin en formato MP3:
Artista, Ttulo, Duracin (en segundos), Tamao del fichero (en KB). Un programa
debe pedir los datos de una cancin al usuario, almacenarlos en dicho "struct" y
despus mostrarlos en pantalla.
// Ejemplo_04_03_02a.cs
// Array de struct
// Introduccin a C#, por Nacho Cabanes
using System;
personas[0].nombre = "Juan";
personas[0].inicial = 'J';
personas[0].edad = 20;
personas[0].nota = 7.5f;
Console.WriteLine("La edad de {0} es {1}",
Revisin 0.99zz Pgina 126
Introduccin a la programacin con C#, por Nacho Cabanes
personas[0].nombre, personas[0].edad);
personas[1].nombre = "Pedro";
Console.WriteLine("La edad de {0} es {1}",
personas[1].nombre, personas[1].edad);
}
}
La edad de Juan es 20
La edad de Pedro es 0
Ejercicios propuestos:
(4.3.2.1) Amplia el programa del ejercicio 4.3.1.1, para que almacene datos de
hasta 100 canciones. Deber tener un men que permita las opciones: aadir una
nueva cancin, mostrar el ttulo de todas las canciones, buscar la cancin que
contenga un cierto texto (en el artista o en el ttulo).
(4.3.2.2) Crea un programa que permita guardar datos de "imgenes" (ficheros de
ordenador que contengan fotografas o cualquier otro tipo de informacin grfica).
De cada imagen se debe guardar: nombre (texto), ancho en pxeles (por ejemplo
2000), alto en pxeles (por ejemplo, 3000), tamao en Kb (por ejemplo 145,6). El
programa debe ser capaz de almacenar hasta 700 imgenes (deber avisar cuando
su capacidad est llena). Debe permitir las opciones: aadir una ficha nueva, ver
todas las fichas (nmero y nombre de cada imagen), buscar la ficha que tenga un
cierto nombre.
// Ejemplo_04_03_03a.cs
// Registros anidados
// Introduccin a C#, por Nacho Cabanes
using System;
struct tipoPersona
{
public string nombre;
public char inicial;
public fechaNacimiento diaDeNacimiento;
public float nota;
}
persona.nombre = "Juan";
persona.inicial = 'J';
persona.diaDeNacimiento.dia = 15;
persona.diaDeNacimiento.mes = 9;
persona.nota = 7.5f;
Console.WriteLine("{0} naci en el mes {1}",
persona.nombre, persona.diaDeNacimiento.mes);
}
}
Ejercicios propuestos:
(4.3.3.1) Amplia el programa 4.3.2.1, para que el campo "duracin" se almacene
como minutos y segundos, usando un "struct" anidado que contenga a su vez
estos dos campos.
Vamos a repasar todas esas posibilidades, junto con la de formar una cadena a
partir de otras si las unimos con el smbolo de la suma (+), lo que llamaremos
"concatenar" cadenas. Un ejemplo que nos pidiese nuestro nombre y nos
saludase usando todo ello podra ser:
// Ejemplo_04_04_01a.cs
// Cadenas de texto (1: manejo bsico)
// Introduccin a C#, por Nacho Cabanes
using System;
if (nombre == "Alberto")
Console.WriteLine("Dices que eres Alberto?");
else
Console.WriteLine("As que no eres Alberto?");
Ejercicios propuestos:
(4.4.1.1) Crea un programa que te pida tu nombre y lo escriba 5 veces.
(4.4.1.2) Crea un programa que pida al usuario su nombre. Si se llama como t
(por ejemplo, "Nacho"), responder "Bienvenido, jefe". En caso contrario, le
saludar por su nombre.
(4.4.1.3) Un programa que pida tu nombre, tu da de nacimiento y tu mes de
nacimiento y lo junte todo en una cadena, separando el nombre de la fecha por
una coma y el da del mes por una barra inclinada, as: "Juan, nacido el 31/12".
(4.4.1.4) Crea un programa que pida al usuario dos nmeros enteros y despus
una operacin que realizar con ellos. La operacin podr ser "suma", "resta",
multiplicacin" y "divisin", que tambin se podrn escribir de forma abreviado
con los operadores matemticos "+", "-", "*" y "/". Para multiplicar tambin se
podr usar una "x", minscula o mayscula. A continuacin se mostrar el
resultado de esa operacin (por ejemplo, si los nmeros son 3 y 6 y la operacin es
"suma", el resultado sera 9). La operacin debes tomarla como una cadena de
texto y analizarla con un "switch".
// Ejemplo_04_04_02a.cs
// Cadenas de texto (2: acceder a una letra)
// Introduccin a C#, por Nacho Cabanes
using System;
Ejercicios propuestos:
(4.4.2.1) Crea un programa que pregunte al usuario su nombre y le responda cul
es su inicial.
// Ejemplo_04_04_03a.cs
// Cadenas de texto (3: longitud)
// Introduccin a C#, por Nacho Cabanes
using System;
{
Console.WriteLine("La letra {0} es {1}", i, saludo[i]);
}
}
}
Ejercicios propuestos:
(4.4.3.1) Un programa que te pida tu nombre y lo muestre en pantalla separando
cada letra de la siguiente con un espacio. Por ejemplo, si tu nombre es "Juan",
debera aparecer en pantalla "J u a n".
(4.4.3.2) Un programa que pida una frase al usuario y la muestra en orden inverso
(de la ltima letra a la primera).
(4.4.3.3) Un programa que pida al usuario una frase, despus una letra y
finalmente diga si aparece esa letra como parte de esa frase o no.
(4.4.3.4) Un programa capaz de sumar dos nmeros enteros muy grandes (por
ejemplo, de 30 cifras), que se debern pedir como cadena de texto y analizar letra
a letra.
(4.4.3.5) Un programa capaz de multiplicar dos nmeros enteros muy grandes
(por ejemplo, de 30 cifras), que se debern pedir como cadena de texto y analizar
letra a letra.
saludo = frase.Substring(0,4);
// Ejemplo_04_04_04a.cs
// Cadenas de texto (4: substring)
// Introduccin a C#, por Nacho Cabanes
using System;
Ejercicios propuestos:
(4.4.4.1) Un programa que te pida tu nombre y lo muestre en pantalla separando
cada letra de la siguiente con un espacio, similar al 4.4.3.1, pero esta vez usando
"Substring". Por ejemplo, si tu nombre es "Juan", debera aparecer en pantalla "J u
a n".
(4.4.4.2) Un programa que te pida tu nombre y lo muestre en pantalla como un
tringulo creciente. Por ejemplo, si tu nombre es "Juan", debera aparecer en
pantalla:
J
Ju
Jua
Juan
De forma similar, LastIndexOf ("ltima posicin de") indica la ltima aparicin (es
decir, busca de derecha a izquierda).
if (nombre.Contains("Juan")) ...
// Ejemplo_04_04_05a.cs
// Cadenas de texto (5: substring)
// Introduccin a C#, por Nacho Cabanes
using System;
Ejercicios propuestos:
(4.4.5.1) Un programa que pida al usuario 10 frases, las guarde en un array, y
luego le pregunte textos de forma repetitiva, e indique si cada uno de esos textos
aparece como parte de alguno de los elementos del array. Terminar cuando el
texto introducido sea "fin".
(4.4.5.2) Crea una versin del ejercicio 4.4.5.1 en la que, en caso de que alguno de
los textos aparezca como subcadena, se avise adems si se encuentra
exactamente al principio.
// Ejemplo_04_04_06a.cs
// Cadenas de texto (6: manipulaciones diversas)
// Introduccin a C#, por Nacho Cabanes
using System;
Y su resultado sera
Ejercicios propuestos:
(4.4.6.1) Una variante del ejercicio 4.4.5.2, que no distinga entre maysculas y
minsculas a la hora de buscar.
(4.4.6.2) Un programa que pida al usuario una frase y elimine todos los espacios
redundantes que contenga (debe quedar slo un espacio entre cada palabra y la
siguiente).
Afortunadamente, C# nos permite hacerlo con Split, que crea un array a partir de
los fragmentos de la cadenam usando el separador que le indiquemos, as:
// Ejemplo_04_04_07a.cs
// Cadenas de texto: partir con "Split"
// Introduccin a C#, por Nacho Cabanes
using System;
}
}
Fragmento 0 = uno
Fragmento 1 = dos
Fragmento 2 = tres
Fragmento 3 = cuatro
// Ejemplo_04_04_07b.cs
// Cadenas de texto: partir con "Split" - 2
// Introduccin a C#, por Nacho Cabanes
using System;
Ejercicios propuestos:
(4.4.7.1) Un programa que pida al usuario una frase y muestre sus palabras en
orden inverso.
(4.4.7.2) Un programa que pida al usuario varios nmeros separados por espacios
y muestre su suma.
if (frase.CompareTo("hola") > 0)
Console.WriteLine("La frase es mayor que hola");
// Ejemplo_04_04_08a.cs
// Cadenas de texto y comparacin alfabtica
// Introduccin a C#, por Nacho Cabanes
using System;
string frase;
Si tecleamos una palabra como "gol", que comienza por G, que alfabticamente
est antes de la H de "hola", se nos dir que esa palabra es menor:
Si escribimos "hOLa", que coincide con "hola" salvo por las maysculas, una
comparacin normal nos dir que es mayor (las maysculas se consideran
"mayores" que las minsculas), y una comparacin sin considerar maysculas o
minsculas nos dir que coinciden:
Ejercicios propuestos:
(4.4.8.1) Un programa que pida al usuario dos frases y diga cual sera la "mayor"
de ellas (la que aparecera en ltimo lugar en un diccionario).
(4.4.8.2) Un programa que pida al usuario cinco frases, las guarde en un array y
muestre la "mayor" de ellas.
que hacamos en ciertas ocasiones con los Arrays), y se pueden convertir a una
cadena "convencional" usando "ToString":
// Ejemplo_04_04_09a.cs
// Cadenas modificables con "StringBuilder"
// Introduccin a C#, por Nacho Cabanes
using System;
using System.Text; // Usaremos un System.Text.StringBuilder
string cadenaNormal;
cadenaNormal = cadenaModificable.ToString();
Console.WriteLine("Cadena normal a partir de ella: {0}",
cadenaNormal);
}
}
Ejercicios propuestos:
(4.4.9.1) Un programa que pida una cadena al usuario y la modifique, de modo
que todas las vocales se conviertan en "o".
(4.4.9.2) Un programa que pida una cadena al usuario y la modifique, de modo
que las letras de las posiciones impares (primera, tercera, etc.) estn en
minsculas y las de las posiciones pares estn en maysculas, mostrando el
resultado en pantalla. Por ejemplo, a partir de un nombre como "Nacho", la
cadena resultante sera "nAcHo".
(4.4.9.3) Crea un juego del ahorcado, en el que un primer usuario introduzca la
palabra a adivinar, se muestre sta oculta con guiones (-----) y el programa acepte
las letras que introduzca el segundo usuario, cambiando los guiones por letras
correctas cada vez que acierte (por ejemplo, a---a-t-). La partida terminar cuando
se acierte la palabra por completo o el usuario agote sus 8 intentos.
// Ejemplo_04_05a.cs
// Ejemplo de "foreach"
// Introduccin a C#, por Nacho Cabanes
using System;
Ejercicios propuestos:
(4.5.1) Un programa que pida tu nombre y lo muestre con un espacio entre cada
par de letras, usando "foreach".
(4.5.2) Un programa que pida al usuario una frase y la descomponga en
subcadenas separadas por espacios, usando "Split". Luego debe mostrar cada
subcadena en una lnea nueva, usando "foreach".
(4.5.3) Un programa que pida al usuario varios nmeros separados por espacios y
muestre su suma (como el del ejercicio 4.4.7.2), pero empleando "foreach".
No debera resultar difcil. Vamos a ver directamente una de las formas en que se
podra plantear y luego comentaremos alguna de las mejoras que se podra
(incluso se debera) hacer.
Por otra parte, para revisar todas las fichas existentes, recorreremos desde la
posicin 0 hasta la n-1, haciendo algo como
// Ejemplo_04_06a.cs
// Tabla con muchos struct y men para manejarla
// Introduccin a C#, por Nacho Cabanes
using System;
struct tipoFicha {
public string nombreFich; // Nombre del fichero
public long tamanyo; // El tamao en KB
};
do {
// Menu principal, repetitivo
Console.WriteLine();
Console.WriteLine("Escoja una opcin:");
Console.WriteLine("1.- Aadir datos de un nuevo fichero");
Console.WriteLine("2.- Mostrar los nombres de todos los ficheros");
Console.WriteLine("3.- Mostrar ficheros por encima de un cierto
tamao");
Console.WriteLine("4.- Ver datos de un fichero");
Console.WriteLine("5.- Salir");
fichas[i].nombreFich, fichas[i].tamanyo);
break;
(Como quiz hayas notado, este fuente, que es un poco ms largo que los
anteriores, abre las llaves al final de cada lnea -estilo Java- y usa tabulaciones de 2
espacios, en vez de 4, para ocupar menos espacio en papel y caber en el ancho de
una pgina; recuerda que puedes usar estilo Java si lo prefieres, pero en general el
fuente ser ms legible con tabulaciones de 4 espacios en vez de 2).
Este programa funciona, y hace todo lo que tiene que hacer, pero es mejorable.
Por supuesto, en un caso real es habitual que cada ficha tenga que guardar ms
informacin que slo esos dos apartados de ejemplo que hemos previsto esta vez.
Si nos muestra todos los datos en pantalla y se trata de muchos datos, puede
ocurrir que aparezcan en pantalla tan rpido que no nos d tiempo a leerlos, as
que sera deseable que parase cuando se llenase la pantalla de informacin (por
ejemplo, una pausa tras mostrar cada 25 datos). Por descontado, se nos pueden
ocurrir muchas ms preguntas que hacerle sobre nuestros datos. Y adems,
cuando salgamos del programa se borrarn todos los datos que habamos
tecleado, pero eso es lo nico "casi inevitable", porque an no sabemos manejar
ficheros.
Ejercicios propuestos:
(4.6.1) Un programa que pida el nombre, el apellido y la edad de una persona, los
almacene en un "struct" y luego muestre los tres datos en una misma lnea,
separados por comas.
Existen ligeras mejoras (por ejemplo, cambiar uno de los "for" por un "while", para
no repasar todos los datos si ya estaban parcialmente ordenados), as como
mtodos claramente ms efectivos, pero ms difciles de programar, alguno de los
cuales comentaremos ms adelante.
Mtodo de burbuja
(Intercambiar cada pareja consecutiva que no est ordenada)
(Nota: algunos autores hacen el bucle exterior creciente y otros decreciente, as:)
Seleccin directa
(En cada pasada busca el menor, y lo intercambia al final de la pasada)
Nota: el smbolo "<>" se suele usar en pseudocdigo para indicar que un dato es
distinto de otro, de modo que equivale al "!=" de C#. La penltima lnea en C#
saldra a ser algo como "if (menor !=i)"
Insercin directa
(Comparar cada elemento con los anteriores -que ya estn ordenados- y
desplazarlo hasta su posicin correcta).
(Es mejorable, no intercambiando el dato que se mueve con cada elemento, sino
slo al final de cada pasada, pero no entraremos en ms detalles).
// Ejemplo_04_07a.cs
// Ordenaciones simples
// Introduccin a C#, por Nacho Cabanes
using System;
// BURBUJA
// (Intercambiar cada pareja consecutiva que no est ordenada)
// Para i=1 hasta n-1
// Para j=i+1 hasta n
// Si A[i] > A[j]
// Intercambiar ( A[i], A[j])
Console.WriteLine("Ordenando mediante burbuja... ");
for(i=0; i < n-1; i++)
{
foreach (int dato in datos) // Muestro datos
Console.Write("{0} ",dato);
Console.WriteLine();
{
datoTemporal = datos[i];
datos[i] = datos[j];
datos[j] = datoTemporal;
}
}
}
Console.Write("Ordenado:");
// SELECCIN DIRECTA:
// (En cada pasada busca el menor, y lo intercambia al final de la
pasada)
// Para i=1 hasta n-1
// menor = i
// Para j=i+1 hasta n
// Si A[j] < A[menor]
// menor = j
// Si menor <> i
// Intercambiar ( A[i], A[menor])
Console.WriteLine("Ordenando mediante seleccin directa... ");
int[] datos2 = {5, 3, 14, 20, 8, 9, 1};
for(i=0; i < n-1; i++)
{
foreach (int dato in datos2) // Muestro datos
Console.Write("{0} ",dato);
Console.WriteLine();
int menor = i;
for(j=i+1; j < n; j++)
if (datos2[j] < datos2[menor])
menor = j;
if (i != menor)
{
datoTemporal = datos2[i];
datos2[i] = datos2[menor];
datos2[menor] = datoTemporal;
}
}
Console.Write("Ordenado:");
// INSERCION DIRECTA:
// (Comparar cada elemento con los anteriores -que ya estn
ordenados-
// y desplazarlo hasta su posicin correcta).
// Para i=2 hasta n
// j=i-1
// mientras (j>=1) y (A[j] > A[j+1])
// Intercambiar ( A[j], A[j+1])
// j = j - 1
Console.WriteLine("Ordenando mediante insercin directa... ");
Revisin 0.99zz Pgina 147
Introduccin a la programacin con C#, por Nacho Cabanes
j = i-1;
while ((j>=0) && (datos3[j] > datos3[j+1]))
{
datoTemporal = datos3[j];
datos3[j] = datos3[j+1];
datos3[j+1] = datoTemporal;
j--;
}
}
Console.Write("Ordenado:");
}
}
Y su resultado sera:
Ejercicios propuestos:
(4.7.1) Un programa que pida al usuario 6 nmeros en coma flotante y los muestre
ordenados de menor a mayor. Escoge el mtodo de ordenacin que prefieras.
(4.7.2) Un programa que pida al usuario 5 nombres y los muestre ordenados
alfabticamente (recuerda que para comparar cadenas no podrs usar el smbolo
">", sino "CompareTo").
(4.7.3) Un programa que pida al usuario varios nmeros, los vaya aadiendo a un
array, mantenga el array ordenado continuamente y muestre el resultado tras
aadir cada nuevo dato (todos los datos se mostrarn en la misma lnea,
separados por espacios en blanco). Terminar cuando el usuario teclee "fin".
(4.7.4) Amplia el ejercicio anterior, para aadir una segunda fase en la que el
usuario pueda "preguntar" si un cierto valor est en el array. Como el array est
ordenado, la bsqueda no se har hasta el final de los datos, sino hasta que se
encuentre el dato buscado o un un dato mayor que l.
Una vez que los datos estn ordenados, podemos buscar uno concreto dentro de
ellos empleando la bsqueda binaria: se comienza por el punto central; si el valor
buscado es mayor que el del punto central, se busca esta vez slo en la mitad
superior (o en la inferior), y as sucesivamente, de modo que cada vez se busca
entre un conjunto de datos que tiene la mitad de tamao que el anterior. Esto
puede suponer una enorme ganancia en velocidad: si tenemos 1.000 datos, una
bsqueda lineal har 500 comparaciones como media, mientras que una
bsqueda lineal har 10 comparaciones o menos. Se podra implementar as:
// Ejemplo_04_07b.cs
// Bsqueda binaria
// Introduccin a C#, por Nacho Cabanes
using System;
// Y comenzamos a buscar
int valorBuscado = 1001;
Console.WriteLine("Buscando si aparece {0}...", valorBuscado);
int limiteInferior = 0;
int limiteSuperior = 999;
bool terminado = false;
while(! terminado)
{
int puntoMedio = limiteInferior+(limiteSuperior-limiteInferior) /
2;
// Aviso de dnde buscamos
Console.WriteLine("Buscando entre pos {0} y {1}, valores {2} y
{3},"+
" centro {4}:{5}",
limiteInferior, limiteSuperior,
datos[limiteInferior], datos[limiteSuperior],
puntoMedio, datos[puntoMedio]);
// Compruebo si hemos acertado
if (datos[puntoMedio] == valorBuscado)
{
Console.WriteLine("Encontrado!");
terminado = true;
}
// O si se ha terminado la bsqueda
else if (limiteInferior == limiteSuperior-1)
{
Console.WriteLine("No encontrado");
terminado = true;
}
// Si no hemos terminado, debemos seguir buscando en una mitad
if ( datos[puntoMedio] < valorBuscado )
limiteInferior = puntoMedio;
else
limiteSuperior = puntoMedio;
}
}
Revisin 0.99zz Pgina 150
Introduccin a la programacin con C#, por Nacho Cabanes
Ejercicios propuestos:
(4.7.5) Realiza una variante del ejercicio 4.7.4, que en vez de hacer una bsqueda
lineal (desde el principio), use "bsqueda binaria": se comenzar a comparar con el
punto medio del array; si nuestro dato es menor, se vuelve a probar en el punto
medio de la mitad inferior del array, y as sucesivamente.
// Ejemplo_04_07c.cs
// Array.Sort
// Introduccin a C#, por Nacho Cabanes
using System;
Ejercicios propuestos:
(4.7.6) Crea una variante del ejercicio 4.7.3, pero usando esta vez Array.Sort para
ordenar: un programa que pida al usuario varios nmeros, los vaya aadiendo a
un array, mantenga el array ordenado continuamente y muestre el resultado tras
aadir cada nuevo dato (todos los datos se mostrarn en la misma lnea,
separados por espacios en blanco). Terminar cuando el usuario teclee "fin".
Si, por el contrario, todo ha sido correcto, la ventana que nos muestra la ejecucin
de nuestro programa se quedar pausada para que podamos comprobar los
resultados:
Nos aparecer una ventana de la que nos interesan dos casillas: la que permite
escribir el comando para compilar y la del comando para ejecutar.
C:\Windows\Microsoft.NET\Framework\v2.0.50727\csc "%f"
Esto habra sido muy trabajoso para compilar desde lnea de comando nuestros
primeros programas, pero una vez que usamos editores ms avanzados, slo hay
que configurarlo una vez en Geany y se utilizar automticamente cada vez que
creemos un fuente en C# desde ese editor.
En este caso, en la casilla "Ejecutar" bastar con escribir "%e.exe", para que se
lance directamente el ejecutable recin creado:
"%e.exe"
Nota: Las comillas dobles que rodean a "%e.exe" (y a "%f") permiten que se lance
correctamente incluso si hubiramos escrito algn espacio en el nombre del
fichero (costumbre poco recomendable para un fuente de un programa).
Por supuesto, existen otros muchos editores gratuitos, que puedes utilizar sin
gastar dinero y que harn tu rutina de programador mucho ms cmoda que con
el Bloc de Notas. Van desde editores sencillos como Notepad2 o Scite hasta
entornos integrados, que usaremos dentro de poco para programas de mayor
tamao, en los que intervendrn varios ficheros fuente de forma simultnea. Es el
caso de Visual Studio, Sharp Develop y MonoDevelop (o su versin modernizada y
ampliada, Xamarin Studio), que veremos un poco ms adelante, cuando nuestros
programas lleguen a ese nivel de complejidad.
Por ejemplo, podramos crear una funcin llamada "Saludar", que escribiera varios
mensajes en la pantalla:
Ahora desde dentro del cuerpo de nuestro programa, podramos "llamar" a esa
funcin:
// Ejemplo_05_02a.cs
// Funcion "Saludar"
// Introduccin a C#, por Nacho Cabanes
using System;
LeerDatosDeFichero();
Revisin 0.99zz Pgina 157
Introduccin a la programacin con C#, por Nacho Cabanes
do {
MostrarMenu();
opcion = PedirOpcion();
switch( opcion ) {
case 1: BuscarDatos(); break;
case 2: ModificarDatos(); break;
case 3: AnadirDatos(); break;
...
Ejercicios propuestos:
(5.2.1) Crea una funcin llamada "BorrarPantalla", que borre la pantalla dibujando
25 lneas en blanco. Crea tambin un "Main" que permita probarla.
(5.2.2) Crea una funcin llamada "DibujarCuadrado3x3", que dibuje un cuadrado
formato por 3 filas con 3 asteriscos cada una. Incluye un "Main" para probarla.
(5.2.3) Descompn en funciones la base de datos de ficheros (ejemplo 04_06a), de
modo que el "Main" sea breve y ms legible (Pista: las variables que se compartan
entre varias funciones debern estar fuera de todas ellas, y debern estar
precedidas por la palabra "static").
Por ejemplo, si escribimos en pantalla nmeros reales con frecuencia, nos puede
resultar til crear una funcin auxiliar que nos los muestre con el formato que nos
interese (que podra ser con exactamente 3 decimales). Lo podramos hacer as:
EscribirNumeroReal(2.3f);
(recordemos que el sufijo "f" sirve para indicar al compilador que trate ese nmero
como un "float", porque de lo contrario, al ver que tiene cifras decimales, lo
tomara como "double", que permite mayor precisin... pero a cambio nosotros
// Ejemplo_05_03a.cs
// Funcion "EscribirNumeroReal"
// Introduccin a C#, por Nacho Cabanes
using System;
x= 5.1f;
Console.WriteLine("El primer numero real es: ");
EscribirNumeroReal(x);
Console.WriteLine(" y otro distinto es: ");
EscribirNumeroReal(2.3f);
}
De modo que un programa completo de ejemplo para una funcin con dos
parmetros podra ser:
// Ejemplo_05_03b.cs
// Funcion "EscribirSuma"
// Introduccin a C#, por Nacho Cabanes
using System;
{
Console.Write( a+b );
}
Ejercicios propuestos:
(5.3.1) Crea una funcin "DibujarCuadrado" que dibuje en pantalla un cuadrado
del ancho (y alto) que se indique como parmetro. Completa el programa con un
Main que permita probarla.
(5.3.2) Crea una funcin "DibujarRectangulo" que dibuje en pantalla un rectngulo
del ancho y alto que se indiquen como parmetros. Incluye un Main para probarla.
(5.3.3) Crea una funcin "DibujarRectanguloHueco" que dibuje en pantalla un
rectngulo hueco del ancho y alto que se indiquen como parmetros, formado por
una letra que tambin se indique como parmetro. Completa el programa con un
Main que pida esos datos al usuario y dibuje el rectngulo.
Pero eso no es lo que ocurre con las funciones matemticas que estamos
acostumbrados a manejar: s devuelven un valor, que es el resultado de una
operacin (por ejemplo, la raz cuadrada de un nmero tiene como resultado otro
nmero).
De igual modo, para nosotros tambin ser habitual crear funciones que realicen
una serie de clculos y nos "devuelvan" (return, en ingls) el resultado de esos
clculos, para poderlo usar desde cualquier otra parte de nuestro programa. Por
ejemplo, podramos crear una funcin para elevar un nmero entero al cuadrado
as:
resultado = Cuadrado( 5 );
// Ejemplo_05_04a.cs
// Funcion "Cuadrado"
// Introduccin a C#, por Nacho Cabanes
using System;
numero= 5;
resultado = Cuadrado(numero);
Console.WriteLine("El cuadrado del numero {0} es {1}",
numero, resultado);
Console.WriteLine(" y el de 3 es {0}", Cuadrado(3));
}
Ejercicios propuestos:
(5.4.1) Crea una funcin "Cubo" que calcule el cubo de un nmero real (float) que
se indique como parmetro. El resultado deber ser otro nmero real. Prueba esta
funcin para calcular el cubo de 3.2 y el de 5.
(5.4.2) Crea una funcin "Menor" que calcule el menor de dos nmeros enteros
que recibir como parmetros. El resultado ser otro nmero entero.
(5.4.3) Crea una funcin llamada "Signo", que reciba un nmero real, y devuelva un
nmero entero con el valor: -1 si el nmero es negativo, 1 si es positivo o 0 si es
cero.
(5.4.4) Crea una funcin "Inicial", que devuelva la primera letra de una cadena de
texto. Prueba esta funcin para calcular la primera letra de la frase "Hola".
(5.4.5) Crea una funcin "UltimaLetra", que devuelva la ltima letra de una cadena
de texto. Prueba esta funcin para calcular la ltima letra de la frase "Hola".
(5.4.6) Crea una funcin "MostrarPerimSuperfCuadrado" que reciba un nmero
entero y calcule y muestre en pantalla el valor del permetro y de la superficie de
un cuadrado que tenga como lado el nmero que se ha indicado como parmetro.
Vamos a ver el uso de variables locales con un ejemplo. Crearemos una funcin
que calcule la potencia de un nmero entero (un nmero elevado a otro), y el
cuerpo del programa que la use. La forma de conseguir elevar un nmero a otro
ser a base de multiplicaciones, es decir:
3 elevado a 5 = 3 3 3 3 3
// Ejemplo_05_05a.cs
// Ejemplo de funcin con variables locales
// Introduccin a C#, por Nacho Cabanes
using System;
En este caso, las variables "temporal" e "i" son locales a la funcin "potencia": para
"Main" no existen. Si en "Main" intentramos hacer i=5; obtendramos un
mensaje de error. De igual modo, "num1" y "num2" son locales para "Main": desde
la funcin "potencia" no podemos acceder a su valor (ni para leerlo ni para
modificarlo), slo desde "Main". Este ejemplo no contiene ninguna variable global.
Ejercicios propuestos:
(5.5.1) Crea una funcin "PedirEntero", que reciba como parmetros el texto que
se debe mostrar en pantalla, el valor mnimo aceptable y el valor mximo
aceptable. Deber pedir al usuario que introduzca el valor tantas veces como sea
necesario, volvrselo a pedir en caso de error, y devolver un valor correcto.
Prubalo con un programa que pida al usuario un ao entre 1800 y 2100.
(5.5.2) Crea una funcin "EscribirTablaMultiplicar", que reciba como parmetro un
nmero entero, y escriba la tabla de multiplicar de ese nmero (por ejemplo, para
el 3 deber llegar desde "3x0=0" hasta "3x10=30").
(5.5.3) Crea una funcin "EsPrimo", que reciba un nmero y devuelva el valor
booleano "true" si es un nmero primo o "false" en caso contrario.
(5.5.4) Crea una funcin "ContarLetra", que reciba una cadena y una letra, y
devuelva la cantidad de veces que dicha letra aparece en la cadena. Por ejemplo, si
la cadena es "Barcelona" y la letra es 'a', debera devolver 2 (porque la "a" aparece
2 veces).
(5.5.5) Crea una funcin "SumaCifras" que reciba un numero cualquiera y que
devuelva como resultado la suma de sus dgitos. Por ejemplo, si el nmero fuera
123 la suma sera 6.
(5.5.6) Crea una funcin "Tringulo" que reciba una letra y un nmero, y escriba un
"tringulo" formado por esa letra, que tenga como anchura inicial la que se ha
indicado. Por ejemplo, si la letra es * y la anchura es 4, debera escribir
****
***
**
*
// Ejemplo_05_06a.cs
// Dos variables locales con el mismo nombre
// Introduccin a C#, por Nacho Cabanes
using System;
n vale 5
Ahora n vale 5
Por qu? Sencillo: tenemos una variable local dentro de "cambiaN" y otra dentro
de "Main". El hecho de que las dos variables tengan el mismo nombre no afecta al
funcionamiento del programa, siguen siendo distintas, porque cada una est en
un bloque ("mbito") distinto.
// Ejemplo_05_06b.cs
// Una variable global
// Introduccin a C#, por Nacho Cabanes
using System;
static int n = 7;
}
Revisin 0.99zz Pgina 166
Introduccin a la programacin con C#, por Nacho Cabanes
// Ejemplo_05_07a.cs
// Modificar una variable recibida como parmetro - acercamiento
// Introduccin a C#, por Nacho Cabanes
using System;
n vale 5
El valor recibido vale 5
y ahora vale 10
Ahora n vale 5
Esto se debe a que, si no indicamos otra cosa, los parmetros "se pasan por
valor", es decir, la funcin no recibe los datos originales, sino una copia de ellos. Si
modificamos algo, estamos cambiando una copia de los datos originales, no
dichos datos.
// Ejemplo_05_07b.cs
// Modificar una variable recibida como parmetro - correcto
// Introduccin a C#, por Nacho Cabanes
using System;
n vale 5
El valor recibido vale 5
y ahora vale 10
Ahora n vale 10
El hecho de poder modificar valores que se reciban como parmetros abre una
posibilidad que no se podra conseguir de otra forma: con "return" slo se puede
devolver un valor de una funcin, pero con parmetros pasados por referencia
podramos devolver ms de un dato. Por ejemplo, podramos crear una funcin
que intercambiara los valores de dos variables:
tienen valor inicial. Por ejemplo, una funcin que devuelva la primera y segunda
letra de una frase sera as:
// Ejemplo_05_07c.cs
// Parmetros "out"
// Introduccin a C#, por Nacho Cabanes
using System;
Si pruebas este ejemplo, vers que no compila si cambias "out" por "ref", a no ser
que des valores iniciales a "letra1" y "letra2".
Ejercicios propuestos:
(5.7.1) Crea una funcin "Intercambiar", que intercambie el valor de los dos
nmeros enteros que se le indiquen como parmetro. Crea tambin un programa
que la pruebe.
(5.7.2) Crea una funcin "Iniciales", que reciba una cadena como "Nacho Cabanes"
y devuelva las letras N y C (primera letra, y letra situada tras el primer espacio),
usando parmetros por referencia. Crea un "Main" que te permita comprobar que
funciona correctamente.
// Ejemplo_05_08a.cs
// Funcin tras Main
// Introduccin a C#, por Nacho Cabanes
using System;
Tambin, una forma simple de obtener un nico nmero "casi al azar" entre 0 y
999 es tomar las milsimas de segundo de la hora actual:
// Ejemplo_05_09_01a.cs
// Nmeros al azar
// Introduccin a C#, por Nacho Cabanes
using System;
Ejercicios propuestos:
(5.9.1.1) Crea un programa que imite el lanzamiento de un dado, generando un
nmero al azar entre 1 y 6.
(5.9.1.2) Crea un programa que genere un nmero al azar entre 1 y 100. El
usuario tendr 6 oportunidades para acertarlo.
(5.9.1.3) Mejora el programa del ahorcado (4.4.9.3), para que la palabra a adivinar
no sea tecleada por un segundo usuario, sino que se escoja al azar de un "array"
de palabras prefijadas (por ejemplo, nombres de ciudades).
(5.9.1.4) Crea un programa que genere un array relleno con 100 nmeros reales al
azar entre -1000 y 1000. Luego deber calcular y mostrar su media.
(5.9.1.5) Crea un programa que "dibuje" asteriscos en 100 posiciones al azar de la
pantalla. Para ayudarte para escribir en cualquier coordenada, puedes usar un
array de dos dimensiones (con tamaos 24 para el alto y 79 para el ancho), que
primero rellenes y luego dibujes en pantalla.
// Ejemplo_05_09_02a.cs
// Ejemplo de funciones trigonometricas
// Introduccin a C#, por Nacho Cabanes
using System;
Ejercicios propuestos:
(5.9.2.1) Crea un programa que halle (y muestre) la raz cuadrada del nmero que
introduzca el usuario. Se repetir hasta que introduzca 0.
(5.9.2.2) Crea un programa que halle cualquier raz (de cualquier orden) de un
nmero. El usuario deber indicar el nmero (por ejemplo, 2) y el ndice de la raz
(por ejemplo, 3 para la raz cbica). Pista: hallar la raz cbica de 2 es lo mismo que
elevar 2 a 1/3.
(5.9.2.3) Haz un programa que resuelva ecuaciones de segundo grado, del tipo ax2
+ bx + c = 0. El usuario deber introducir los valores de a, b y c. Se deber crear
una funcin "CalcularRaicesSegundoGrado", que recibir como parmetros los
coeficientes a, b y c (por valor), as como las soluciones x1 y x2 (por referencia).
Deber devolver los valores de las dos soluciones x1 y x2. Si alguna solucin no
existe, se devolver como valor 100.000 para esa solucin. Pista: la solucin se
calcula con
x = -b raz (b2 4ac) / (2a)
(5.9.2.4) Haz un programa que pida al usuario 5 datos numricos enteros, los
guarde en un array, pida un nuevo dato y muestre el valor del array que se
encuentra ms cerca de ese dato, siendo mayor que l, o el texto "Ninguno es
mayor" si ninguno lo es.
(5.9.2.5) Crea un programa que pida al usuario 5 datos numricos reales, los
guarde en un array, pida un nuevo dato y muestre el valor del array que se
encuentra ms cerca de ese dato en valor absoluto (es decir, el ms prximo, sea
mayor que l o menor que l).
(5.9.2.6) Crea una funcin "Distancia", que calcule la distancia entre dos puntos
(x1,y1) y (x2,y2), usando la expresin d = raz [ (x1-x2)2 + (y1-y2)2 ].
Revisin 0.99zz Pgina 173
Introduccin a la programacin con C#, por Nacho Cabanes
(5.9.2.7) Crea un programa que pida al usuario un ngulo (en grados) y muestre su
seno, coseno y tangente. Recuerda que las funciones trigonomtricas esperan que
el ngulo se indique en radianes, no en grados. La equivalencia es que 360 grados
son 2*PI radianes.
(5.9.2.8) Crea un programa que muestre los valores de la funcin y = 10 *
seno(x*5), para valores de x entre 0 y 72 grados.
(5.9.2.9) Crea un programa que "dibuje" la grfica de la funcin y = 10 * seno(x*5),
para valores de x entre 0 y 72 grados. Para ayudarte para escribir en cualquier
coordenada, puedes usar un array de dos dimensiones, que primero rellenes y
luego dibujes en pantalla (mira el ejercicio 5.9.1.5).
(5.9.2.10) Crea un programa que "dibuje" un crculo dentro de un array de dos
dimensiones, usando las ecuaciones x = xCentro + radio * coseno(ngulo), y =
yCentro + radio * seno(ngulo). Si tu array es de 24x79, las coordenadas del centro
seran (12,40). Recuerda que el ngulo se debe indicar en radianes (mira el
ejercicio 5.9.1.5 y el 5.9.2.9).
5.10. Recursividad
La recursividad consiste en resolver un problema a partir de casos ms simples del
mismo problema. Una funcin recursiva es aquella que se "llama a ella misma",
reduciendo la complejidad paso a paso hasta llegar a un caso trivial.
El factorial de 1 es 1:
1! = 1
n! = n (n-1)!
// Ejemplo_05_10a.cs
// Funciones recursivas: factorial
// Introduccin a C#, por Nacho Cabanes
using System;
Ejercicios propuestos:
(5.10.1) Crea una funcin que calcule el valor de elevar un nmero entero a otro
nmero entero (por ejemplo, 5 elevado a 3 = 5 3 = 5 5 5 = 125). Esta funcin se
debe crear de forma recursiva. Piensa cul ser el caso base (qu potencia se
puede calcular de forma trivial) y cmo pasar del caso "n-1" al caso "n" (por
ejemplo, si sabes el valor de 54, cmo hallaras el de 55 a partir de l).
(5.10.2) Como alternativa, crea una funcin que calcule el valor de elevar un
nmero entero a otro nmero entero de forma NO recursiva (lo que llamaremos
"de forma iterativa"), usando la orden "for".
(5.10.3) Crea un programa que emplee recursividad para calcular un nmero de la
serie Fibonacci (en la que los dos primeros elementos valen 1, y para los restantes,
cada elemento es la suma de los dos anteriores).
(5.10.4) Crea un programa que emplee recursividad para calcular la suma de los
elementos de un vector de nmeros enteros, desde su posicin inicial a la final,
usando una funcin recursiva que tendr la apariencia: SumaVector(v, desde,
hasta). Nuevamente, piensa cul ser el caso base (cuntos elementos podras
sumar para que dicha suma sea trivial) y cmo pasar del caso "n-1" al caso "n" (por
ejemplo, si conoces la suma de los 6 primeros elementos y el valor del sptimo
elemento, cmo podras emplear esta informacin para conocer la suma de los 7
primeros).
(5.10.5) Crea un programa que emplee recursividad para calcular el mayor de los
elementos de un vector. El planteamiento ser muy similar al del ejercicio anterior.
(5.10.6) Crea un programa que emplee recursividad para dar la vuelta a una
cadena de caracteres (por ejemplo, a partir de "Hola" devolvera "aloH"). La funcin
recursiva se llamar "Invertir(cadena)". Como siempre, analiza cul ser el caso
base (qu longitud debera tener una cadena para que sea trivial darle la vuelta) y
cmo pasar del caso "n-1" al caso "n" (por ejemplo, si ya has invertido las 5
primeras letras, que ocurrira con la de la sexta posicin).
(5.10.7) Crea, tanto de forma recursiva como de forma iterativa, una funcin diga
si una cadena de caracteres es simtrica (un palndromo). Por ejemplo,
"DABALEARROZALAZORRAELABAD" es un palndromo.
(5.10.8) Crear un programa que encuentre el mximo comn divisor de dos
nmeros usando el algoritmo de Euclides: Dados dos nmeros enteros positivos m
y n, tal que m > n, para encontrar su mximo comn divisor, es decir, el mayor
entero positivo que divide a ambos: - Dividir m por n para obtener el resto r (0 r
< n) ; - Si r = 0, el MCD es n.; - Si no, el mximo comn divisor es MCD(n,r).
(5.10.9) Crea dos funciones que sirvan para saber si un cierto texto es subcadena
de una cadena. No puedes usar "Contains" ni "IndexOf", sino que debes analizar
letra a letra. Una funcin debe ser iterativa y la otra debe ser recursiva.
(5.10.10) Crea una funcin que reciba una cadena de texto, y una subcadena, y
devuelva cuntas veces aparece la subcadena en la cadena, como subsecuencia
formada a partir de sus letras en orden. Por ejemplo, si recibes la palabra "Hhoola"
y la subcadena "hola", la respuesta sera 4, porque se podra tomar la primera H
con la primera O (y con la L y con la A), la primera H con la segunda O, la segunda
H con la primera O, o bien la segunda H con la segunda O. Si recibes "hobla", la
respuesta sera 1. Si recibes "ohla", la respuesta sera 0, porque tras la H no hay
ninguna O que permita completar la secuencia en orden.
(5.10.11) El algoritmo de ordenacin conocido como "Quicksort", parte de la
siguiente idea: para ordenar un array entre dos posiciones "i" y "j", se comienza
por tomar un elemento del array, llamado "pivote" (por ejemplo, el punto medio);
luego se recoloca el array de modo que los elementos menores que el pivote
queden a su izquierda y los mayores a su derecha; finalmente, se llama de forma
recursiva a Quicksort para cada una de las dos mitades. El caso base de la funcin
recursiva es cuando se llega a un array de tamao 0 1. Implementa una funcin
que ordene un array usando este mtodo.
ls l *.cs
En este caso, la orden sera "ls", y las dos opciones (argumentos o parmetros) que
le indicamos son "-l" y "*.cs".
dir *.cs
Pues bien, estas opciones que se le pasan al programa se pueden leer desde C#.
Se hace indicando un parmetro especial en Main, un array de strings:
Environment.Exit(1);
Es decir, entre parntesis indicamos un cierto cdigo, que suele ser (por convenio)
un 0 si no ha habido ningn error, u otro cdigo distinto en caso de que s exista
algn error.
Este valor se podra comprobar desde el sistema operativo. Por ejemplo, en MsDos
y Windows se puede leer desde un fichero BAT o CMD usando "IF ERRORLEVEL",
as:
{
...
return 0;
}
// Ejemplo_05_11a.cs
// Parmetros y valor de retorno de "Main"
// Introduccin a C#, por Nacho Cabanes
using System;
if (args.Length == 0)
{
Console.WriteLine("No ha indicado ningn parmetro!");
Environment.Exit(1);
}
return 0;
}
}
Ejercicios propuestos:
(5.11.1) Crea un programa llamado "suma", que calcule (y muestre) la suma de dos
nmeros que se le indiquen como parmetros en lnea de comandos. Por
ejemplo, si se teclea "suma 2 3" deber responder "5", si se teclea "suma 2"
responder "2" y si se teclea nicamente "suma" deber responder "no hay
suficientes datos" y devolver un cdigo de error 1.
(5.11.2) Crea una calculadora bsica, llamada "calcula", que deber sumar, restar,
multiplicar o dividir los dos nmeros que se le indiquen como parmetros.
Ejemplos de su uso sera "calcula 2 + 3" o "calcula 5 * 60".
(5.11.3) Crea una variante del ejercicio 5.11.2, en la que Main devuelva el cdigo 1
si la operacin indicada no es vlida o 0 cuando s sea una operacin aceptable.
(5.11.4) Crea una variante del ejercicio 5.11.3, en la que Main devuelva tambin el
cdigo 2 si alguno de los dos nmeros con los que se quiere operar no tiene un
valor numrico vlido.
Esta descomposicin no debe ser arbitraria. Por ejemplo, ser deseable que cada
bloque tenga unas responsabilidades claras, y que cada bloque no dependa de los
detalles internos de otros bloques.
Una forma de "descubrir" los objetos que forman parte de un programa es partir
de la descripcin del problema, y subrayar los nombres con un color y los verbos
con otro color.
Y cuando comenzamos una partida, veremos una pantalla como sta, que vamos a
analizar con ms detalle:
A partir de esa descripcin, podemos buscar los nombres (puede ayudar si los
subrayamos con un rotulador de marcar), que indicarn los objetos en los que
podemos descomponer el problema, y los verbos (con otro rotulador de marcar,
en otro color), que indicarn las acciones que puede realizar cada uno de esos
objetos.
Esa serie de objetos, con sus relaciones y sus acciones, se puede expresar
mediante un "diagramas de clases", que en nuestro caso podra ser as
(simplificado):
Algunos de los detalles que se pueden leer de ese diagrama (y que deberan
parecerse bastante a la descripcin inicial) son:
Nosotros no vamos a hacer proyectos tan grandes (al menos, no todava), pero s
empezaremos a crear proyectos sencillos en los que colaboren varias clases, que
permitan sentar las bases para proyectos ms complejos, y tambin entender
algunas peculiaridades de los temas que veremos a continuacin, como el manejo
de ficheros en C#.
Como curiosidad, cabe mencionar que en los proyectos grandes es habitual usar
herramientas grficas que nos ayuden a visualizar las clases y las relaciones que
existen entre ellas, como hemos hecho para el Space Invaders. Tambin se puede
dibujar directamente en papel para aclararnos las ideas, pero el empleo de
herramientas informticas tiene varias ventajas adicionales:
aplicacin, la secuencia de acciones que se debe seguir, las clases que la van a
integrar (que es lo que a nosotros nos interesa en este momento), etc.
Disponemos de herramientas gratuitas como ArgoUML, multiplataforma, que
permite crear distintos tipos de diagramas UML y que permite generar el cdigo
correspondiente al esqueleto de nuestras clases, o Dia, tambin multiplataforma,
pero de uso ms genrico (para crear diagramas de cualquier tipo) y que no
permite generar cdigo por ella misma pero s con la ayuda de Dia2code.
En los prximos ejemplos partiremos de una nica clase en C#, para ampliar
posteriormente esa estructura e ir creando proyectos ms complejos.
Ejercicio propuesto:
(6.1.1) Piensa en un juego que conozcas, que no sea demasiado complejo
(tampoco demasiado simple) y trata de hacer una descripcin como la anterior y
una descomposicin en clases.
Vamos a completar un programa de prueba que use un objeto de esta clase (una
"Puerta"), muestre su estado, la abra y vuelva a mostrar su estado:
// Ejemplo_06_02a.cs
// Primer ejemplo de clases
// Introduccin a C#, por Nacho Cabanes
using System;
Console.WriteLine("Valores iniciales...");
p.MostrarEstado();
Console.WriteLine();
Console.WriteLine("Vamos a abrir...");
p.Abrir();
p.MostrarEstado();
}
Este fuente ya no contiene una nica clase (class), como todos nuestros ejemplos
anteriores, sino dos clases distintas:
(Nota: al compilar, puede que obtengas algn "Aviso" -warning- que te dice que
has declarado "alto", "ancho" y "color", pero no las ests utilizando; no es
importante por ahora, puedes ignorar ese aviso).
Valores iniciales...
Ancho: 0
Alto: 0
Color: 0
Abierta: False
Vamos a abrir...
Ancho: 0
Alto: 0
Color: 0
Abierta: True
Se puede ver que en C# (pero no en todos los lenguajes), las variables que forman
parte de una clase (los "atributos") tienen un valor inicial predefinido: 0 para los
nmeros, una cadena vaca para las cadenas de texto, "false" para los datos
booleanos.
Vemos tambin que se accede a los mtodos y a los datos precediendo el nombre
de cada uno por el nombre de la variable y por un punto, como hacamos con los
registros (struct).
Aun as, en nuestro caso no podemos hacer directamente "p.abierta = true" desde
el programa principal, por dos motivos:
Por ejemplo, para conocer y modificar los valores del "ancho" de una puerta,
podramos crear un mtodo LeerAncho, que nos devolviera su valor, y un mtodo
CambiarAncho, que lo reemplazase por otro valor. No hay un convenio claro sobre
cmo llamar a a estos mtodos en espaol, por lo que es frecuente usar las
palabras inglesas "Get" y "Set" para leer y cambiar un valor, respectivamente. As,
crearemos funciones auxiliares GetXXX y SetXXX que permitan acceder al valor de
los atributos (en C# existe una forma alternativa de hacerlo, usando
"propiedades", que veremos ms adelante):
As, una nueva versin del programa, que incluya ejemplos de Get y Set, podra
ser:
// Ejemplo_06_02b.cs
// Clases, get y set
// Introduccin a C#, por Nacho Cabanes
using System;
Console.WriteLine("Valores iniciales...");
p.MostrarEstado();
Console.WriteLine();
Console.WriteLine("Vamos a abrir...");
p.Abrir();
p.SetAncho(80);
p.MostrarEstado();
Revisin 0.99zz Pgina 190
Introduccin a la programacin con C#, por Nacho Cabanes
Ejercicios propuestos:
(6.2.1) Crea una clase llamada Persona, en el fichero "persona.cs". Esta clase
deber tener un atributo "nombre", de tipo string. Tambin deber tener un
mtodo "SetNombre", de tipo void y con un parmetro string, que permita
cambiar el valor del nombre. Finalmente, tambin tendr un mtodo "Saludar",
que escribir en pantalla "Hola, soy " seguido de su nombre. Crea tambin una
clase llamada PruebaPersona. Esta clase deber contener slo la funcin Main,
que crear dos objetos de tipo Persona, les asignar un nombre a cada uno y les
pedir que saluden.
(6.2.2) Tras leer la descripcin de Space Invaders que vimos en el apartado
anterior, crea una clase Juego,que slo contenga un mtodo Lanzar, void, sin
parmetros, que escriba en pantalla "Bienvenido a Console Invaders. Pulse Intro
para salir" y se parar hasta que el usuario pulse Intro. Prepara tambin un Main
(en la misma clase), que cree un objeto de la clase Juego y lo lance.
(6.2.3) Para guardar informacin sobre libros, vamos a comenzar por crear una
clase "Libro", que contendr atributos "autor", "titulo", "ubicacion" (todos ellos
strings) y mtodos Get y Set adecuados para leer su valor y cambiarlo. Prepara
tambin un Main (en la misma clase), que cree un objeto de la clase Libro, d
valores a sus tres atributos y luego los muestre.
(6.2.4) Crea una clase "Coche", con atributos "marca" (texto), "modelo" (texto),
"cilindrada" (nmero entero), potencia (nmero real). No hace falta que crees un
Main de prueba.
Para compilar un programa formado por varios fuentes, basta con indicar los
nombres de todos ellos. Por ejemplo, con Mono sera
En ese caso, el ejecutable obtenido tenda el nombre del primero de los fuentes
(fuente1.exe). Podemos cambiar el nombre del ejecutable con la opcin "-out" de
Mono:
// Puerta.cs
// Clases, get y set
// Introduccin a C#, por Nacho Cabanes
using System;
// Ejemplo_06_03a.cs
// Usa la clase Puerta
// Introduccin a C#, por Nacho Cabanes
using System;
Console.WriteLine("Valores iniciales...");
p.MostrarEstado();
Console.WriteLine();
Console.WriteLine("Vamos a abrir...");
p.Abrir();
p.SetAncho(80);
p.MostrarEstado();
}
Y lo compilaramos con:
Aun as, para estos proyectos formados por varias clases, lo ideal es usar algn
entorno ms avanzado, como SharpDevelop (que adems es gratuito) o
VisualStudio (del que existe una versin gratuita, conocida como Express), que nos
Revisin 0.99zz Pgina 193
Introduccin a la programacin con C#, por Nacho Cabanes
permitan crear todas las clases con comodidad, saltar de una clase a clase otra
rpidamente, que marquen dentro del propio editor la lnea en la que estn los
errores...
Cuando hagamos doble clic en nuestro nuevo icono, veremos la pantalla principal
de SharpDevelop, que nos muestra la lista de los ltimos proyectos ("soluciones")
que hemos realizado, y nos permite crear uno nuevo:
En nuestro caso, comenzaremos por crear una "Nueva solucin", desde el men
"File", en la opcin "New Solution":
Se nos mostrar los tipos de proyectos para los que se nos podra crear un
esqueleto vaco que despus iramos rellenando:
Podemos cambiar el idioma del interfaz, para que todos los mens y opciones
aparezcan en espaol. Lo haremos desde "Options" (opciones), al final del men
"Tools" (herramientas):
(Si la ventana de nuestro programa se cierra tan rpido que no tenemos tiempo de
leerla, nos puede interesar aadir provisionalmente una lnea ReadLine() al final
del fuente, para que ste se detenga hasta que pulsemos la tecla Intro).
Normalmente, el tipo de elemento que nos interesar ser una clase, cuyo nombre
deberemos indicar:
y obtendramos un nuevo esqueleto vaco (esta vez sin "Main"), que deberamos
completar.
Nuestro programa, que ahora estara formado por dos clases, se compilara y se
ejecutara de la misma forma que cuando estaba integrado por una nica clase.
Si nuestro equipo es moderno, posiblemente nos permitir usar Visual Studio con
una cierta fluidez. Instalar y emplear este entorno es muy similar a lo que hemos
visto. La versin Express, que es gratuita para uso personal, se puede descargar
de:
http://www.visualstudio.com/es-es/products/visual-studio-express-vs
La pantalla principal, una vez completada la instalacin, debera ser similar a sta
(para Visual Studio 2013; puede ser ligeramente distinta en otras versiones o si
escogemos otras paletas de colores):
Ejercicio propuesto:
(6.3.1) Crea un proyecto con las clases Puerta y Ejemplo_06_03a. Comprueba que
todo funciona correctamente.
(6.3.2) Modifica el fuente del ejercicio 6.2.1 (clase Persona), para dividirlo en dos
ficheros: Crea una clase llamada Persona, en el fichero "persona.cs". Esta clase
deber tener un atributo "nombre", de tipo string. Tambin deber tener un
mtodo "SetNombre", de tipo void y con un parmetro string, que permita
cambiar el valor del nombre. Finalmente, tambin tendr un mtodo "Saludar",
que escribir en pantalla "Hola, soy " seguido de su nombre. Crea tambin una
clase llamada PruebaPersona, en el fichero "pruebaPersona.cs". Esta clase deber
contener slo la funcin Main, que crear dos objetos de tipo Persona, les asignar
un nombre y les pedir que saluden.
(6.3.3) Crea un proyecto a partir de la clase Libro (ejercicio 6.2.3). El "Main" pasar
a una segunda clase llamada "PruebaDeLibro" y desaparecer de la clase Libro.
(6.3.4) Ampla el esqueleto del ConsoleInvaders (ejercicio 6.2.2): crea un proyecto
para Visual Studio o SharpDevelop. Adems de la clase "Juego", crea una clase
"Bienvenida" y una clase "Partida". El mtodo "Lanzar" de la clase Juego, ya no
escribir nada en pantalla, sino que crear un objeto de la clase "Bienvenida" y lo
lanzar y luego un objeto de la clase "Partida" y lo lanzar. El mtodo Lanzar de la
clase Bienvenida escribir en pantalla "Bienvenido a Console Invaders. Pulse Intro
para jugar". El mtodo Lanzar de la clase Partida escribir en pantalla "sta sera la
pantalla de juego. Pulse Intro para salir" y se parar hasta que el usuario pulse
Intro.
(6.3.5) Ampla el esqueleto del ConsoleInvaders (ejercicio 6.3.4): El mtodo Lanzar
de la clase Bienvenida escribir en pantalla "Bienvenido a Console Invaders. Pulse
Intro para jugar o ESC para salir". Puedes comprobar si se pulsa ESC con
"ConsoleKeyInfo tecla = Console.ReadKey(); if (tecla.Key == ConsoleKey.Escape)
salir = true;". El cdigo de la tecla Intro es " ConsoleKey.Enter". Tambin puedes
usar "Console.Clear();" si quieres borrar la pantalla. Aade un mtodo "GetSalir" a
la clase Bienvenida, que devuelva "true" si el usuario ha escogido Salir o "false" si
ha elegido Jugar. El mtodo Lanzar de la clase Juego repetir la secuencia
Bienvenida-Partida hasta que el usuario escoja Salir.
(6.3.6) Ampla el esqueleto del ConsoleInvaders (ejercicio 6.3.5): Crea una clase
Nave, con atributos "x" e "y" (nmeros enteros, "x" de 0 a 1023 e "y" entre 0 y 767,
pensando en una pantalla de 1024x768), e imagen (un string formado por dos
caracteres, como "/\"). Tambin tendr un mtodo MoverA(nuevaX, nuevaY) que lo
mueva a una nueva posicin, y un mtodo Dibujar, que muestre esa imagen en
pantalla (como esta versin es para consola, la X tendr que rebajarse para que
tenga un valor entre 0 y 79, y la Y entre 0 y 24). Puedes usar
Console.SetCursorPosition(x,y) para situarte en unas coordenadas de pantalla.
Crea tambin clase Enemigo, con los mismos atributos. Su imagen podra ser "][".
El mtodo Lanzar de la clase Partida crear una nave en las coordenadas (500,
600) y la dibujar, crear un enemigo en las coordenadas (100, 80) y lo dibujar, y
finalmente esperar a que el usuario pulse Intro para terminar la falsa sesin de
juego.
(6.3.7) Crea un proyecto a partir de la clase Coche (ejercicio 6.2.4): adems de la
clase Coche, existir una clase PruebaDeCoche, que contendr la funcin "Main",
que crear un objeto de tipo coche, pedir al usuario su marca, modelo, cilindrada
y potencia, y luego mostrar en pantalla el valor de esos datos.
6.4. La herencia
Vamos a ver ahora cmo definir una nueva clase de objetos a partir de otra ya
existente. Por ejemplo, vamos a crear una clase "Porton" a partir de la clase
"Puerta". Un portn tendr las mismas caractersticas que una puerta (ancho, alto,
color, abierto o no), pero adems se podr bloquear, lo que supondr un nuevo
atributo y nuevos mtodos para bloquear y desbloquear:
// Porton.cs
// Clase que hereda de Puerta
// Introduccin a C#, por Nacho Cabanes
using System;
Revisin 0.99zz Pgina 205
Introduccin a la programacin con C#, por Nacho Cabanes
bool bloqueada;
Con "public class Porton: Puerta" indicamos que Porton debe "heredar" todo lo
que ya habamos definido para Puerta. Por eso, no hace falta indicar nuevamente
que un Portn tendr un cierto ancho, o un color, o que se puede abrir: todo eso
lo tiene por ser un "descendiente" de Puerta.
No tenemos por qu heredar todo tal y como era; tambin podemos "redefinir"
algo que ya exista. Por ejemplo, nos puede interesar que "MostrarEstado" ahora
nos diga tambin si la puerta est bloqueada. Para eso, basta con volverlo a
declarar y aadir la palabra "new" para indicar al compilador de C# que sabemos
que ya existe ese mtodo y que sabemos seguro que lo queremos redefinir (si no
incluimos la palabra "new", el compilador mostrar un "warning", pero dar el
programa como vlido; ms adelante veremos que existe una alternativa a "new" y
en qu momento ser adecuado usar cada una de ellas.
Puedes observar que ese "MostrarEstado" no dice nada sobre el ancho ni el alto
del portn. En el prximo apartado veremos cmo acceder a esos datos.
// Ejemplo_06_04a.cs
Revisin 0.99zz Pgina 206
Introduccin a la programacin con C#, por Nacho Cabanes
using System;
Console.WriteLine("Valores iniciales...");
p.MostrarEstado();
Console.WriteLine();
Console.WriteLine("Vamos a abrir...");
p.Abrir();
p.SetAncho(80);
p.MostrarEstado();
Console.WriteLine();
Console.WriteLine("Ahora el portn...");
Porton p2 = new Porton();
p2.SetAncho(300);
p2.Bloquear();
p2.MostrarEstado();
}
Y su resultado sera:
Valores iniciales...
Ancho: 0
Alto: 0
Color: 0
Abierta: False
Vamos a abrir...
Ancho: 80
Alto: 0
Color: 0
Abierta: True
Ahora el portn...
Portn.
Bloqueada: True
Ejercicios propuestos:
(6.4.1) Crea un proyecto con las clases Puerta, Portn y Ejemplo_06_04a. Prueba
que todo funciona correctamente.
(6.4.2) Crea una variante ampliada del ejercicio 6.3.2. En ella, la clase Persona no
cambia. Se crear una nueva clase PersonaInglesa, en el fichero
"personaInglesa.cs". Esta clase deber heredar las caractersticas de la clase
"Persona", y aadir un mtodo "TomarTe", de tipo void, que escribir en pantalla
"Estoy tomando t". Crear tambin una clase llamada PruebaPersona2, en el
fichero "pruebaPersona2.cs". Esta clase deber contener slo la funcin Main, que
crear dos objetos de tipo Persona y uno de tipo PersonaInglesa, les asignar un
nombre, les pedir que saluden y pedir a la persona inglesa que tome t.
(6.4.3) Ampla el proyecto del ejercicio 6.3.3 (Libro): crea una clase "Documento",
de la que Libro heredar todos sus atributos y mtodos. Ahora la clase Libro
contendr slo un atributo "paginas", nmero entero, con sus correspondientes
Get y Set.
(6.4.4) Ampla el esqueleto del ConsoleInvaders (ejercicio 6.3.6): Crea una clase
"Sprite", de la que heredarn "Nave" y "Enemigo". La nueva clase contendr todos
los atributos y mtodos que son comunes a las antiguas (todos los existentes, por
ahora).
(6.4.5) Ampla el proyecto de la clase Coche (ejercicio 6.3.7): Crea una clase
"Vehiculo", de la que heredarn "Coche" y una nueva clase "Moto". La clase
Vehiculo contendr todos los atributos y mtodos que antes estaban en Coche, y
tanto Coche como Moto heredarn de ella.
6.5. Visibilidad
Nuestro ejemplo todava no funciona correctamente: los atributos de una Puerta,
como el "ancho" y el "alto" estaban declarados como "privados" (es lo que se
considera en C# si no decimos lo contrario), por lo que no son accesibles desde
ninguna otra clase, ni siquiera desde Porton.
Hemos manejado con frecuencia la palabra "public", para indicar que algo debe
ser pblico, porque nuestras clases y su "Main" lo han sido siempre hasta ahora.
Nos podramos sentir tentados de declarar como "public" los atributos como el
"ancho" y el "alto", pero esa no es la solucin ms razonable, porque no queremos
que sean accesibles desde cualquier sitio, debemos recordar la mxima de
"ocultacin de detalles", que hace nuestros programas sean ms fciles de
mantener.
Slo querramos que esos datos estuvieran disponibles para todos los tipos de
Puerta, incluyendo sus "descendientes", como un Porton. Esto se puede conseguir
usando otro mtodo de acceso: "protected". Todo lo que declaremos como
"protected" ser accesible por las clases derivadas de la actual, pero por nadie
ms:
Si quisiramos dejar claro que algn elemento de una clase debe ser totalmente
privado, podemos usar la palabra "private", en vez de "public" o "protected". En
general, ser preferible usar "private" a no escribir nada, por legibilidad, para
ayudar a detectar errores con mayor facilidad y como costumbre por si ms
adelante programamos en otros lenguajes, porque puede ocurrir que en otros
lenguajes se considere pblico (en vez de privado) un atributo cuya visibilidad no
hayamos indicados
As, un nico fuente completo que declarase la clase Puerta, la clase Porton a partir
de ella, y que adems contuviese un pequeo "Main" de prueba podra ser:
// Ejemplo_06_05a.cs
// Portn, que hereda de Puerta, con "protected"
// Introduccin a C#, por Nacho Cabanes
using System;
Console.WriteLine("Valores iniciales...");
p.MostrarEstado();
Console.WriteLine();
Console.WriteLine("Vamos a abrir...");
p.Abrir();
p.SetAncho(80);
p.MostrarEstado();
Console.WriteLine();
Console.WriteLine("Ahora el portn...");
Revisin 0.99zz Pgina 209
Introduccin a la programacin con C#, por Nacho Cabanes
// ------------------------------------------
// Puerta.cs
// Clases, get y set
// Introduccin a C#, por Nacho Cabanes
// ------------------------------------------
// Porton.cs
// Clase que hereda de Puerta
// Introduccin a C#, por Nacho Cabanes
bool bloqueada;
Ejercicios propuestos:
(6.5.1) Crea un proyecto a partir del ejemplo 06.05a, en el que cada clase est en
un fichero separado. Como podrs comprobar, ahora necesitars un "using
System" en cada fuente.
(6.5.2) Ampla las clases del ejercicio 6.4.2, creando un nuevo proyecto con las
siguientes caractersticas: La clase Persona no cambia; la clase PersonaInglesa se
modificar para que redefina el mtodo "Saludar", para que escriba en pantalla
"Hi, I am " seguido de su nombre; se crear una nueva clase PersonaItaliana, en el
fichero "personaItaliana.cs", que deber heredar las caractersticas de la clase
"Persona", pero redefinir el mtodo "Saludar", para que escriba en pantalla "Ciao";
crea tambin una clase llamada PruebaPersona3, en el fichero "
pruebaPersona3.cs", que deber contener slo la funcin Main y crear un objeto
de tipo Persona, dos de tipo PersonaInglesa, uno de tipo PersonaItaliana, les
asignar un nombre, les pedir que saluden y pedir a la persona inglesa que
tome t.
(6.5.3) Retoca el proyecto del ejercicio 6.4.3 (Libro): los atributos de la clase
Documento y de la clase Libro sern "protegidos".
(6.5.4) Ampla el esqueleto del ConsoleInvaders (ejercicio 6.4.4): Ampla la clase
Nave con un mtodo "MoverDerecha", que aumente su X en 10 unidades, y un
"MoverIzquierda", que disminuya su X en 10 unidades. Necesitars hacer que esos
atributos sean "protected". El mtodo Lanzar de la clase Partida no esperar hasta
el usuario pulse Intro sin hacer nada, sino que ahora usar un do-while que
Revisin 0.99zz Pgina 211
Introduccin a la programacin con C#, por Nacho Cabanes
compruebe si pulsa ESC (para salir) o flecha izquierda o flecha derecha (para
mover la nave: sus cdigos son ConsoleKey.LeftArrow y ConsoleKey. RightArrow).
Si se pulsan las flechas, la nave se mover a un lado o a otro (con los mtodos que
acabas de crear). Al principio de cada pasada del do-while se borrar la pantalla
("Console.Clear();").
(6.5.5) Mejora el proyecto de la clase Coche (ejercicio 6.4.5): todos los atributos
sern "protegidos" y los mtodos sern "pblicos".
public Puerta()
{
ancho = 100;
alto = 200;
color = 0xFFFFFF;
abierta = false;
}
Un programa de ejemplo que usara estos dos constructores para crear dos
puertas con caractersticas iniciales distintas podra ser:
// Ejemplo_06_06a.cs
// Constructores
// Introduccin a C#, por Nacho Cabanes
using System;
Console.WriteLine("Valores iniciales...");
p.MostrarEstado();
Console.WriteLine();
Console.WriteLine("Vamos a abrir...");
p.Abrir();
p.MostrarEstado();
Console.WriteLine();
Console.WriteLine("Para la segunda puerta...");
p2.MostrarEstado();
}
// ------------------------------------------
public Puerta()
{
ancho = 100;
alto = 200;
color = 0xFFFFFF;
abierta = false;
}
ancho = an;
alto = al;
color = 0xFFFFFF;
abierta = false;
}
~Puerta()
{
// Liberar memoria
// Cerrar ficheros
}
Ejercicios propuestos:
(6.6.1) Ampliar las clases del ejercicio 6.5.2, para que todas ellas contengan
constructores. Los constructores de casi todas las clases estarn vacos, excepto
del de PersonaInglesa, que prefijar su nombre a "John". Crea tambin un
constructor alternativo para esta clase que permita escoger cualquier otro
nombre.
(6.6.2) Ampla el proyecto del ejercicio 6.5.3 (Libro): la clase Libro tendr un
constructor que permita dar valores al autor, el ttulo y la ubicacin.
(6.6.3) Ampla el esqueleto del ConsoleInvaders (ejercicio 6.5.4): La clase Enemigo
tendr un constructor, sin parmetros, que prefijar su posicin inicial. El
constructor de la clase Nave recibir como parmetros las coordenadas X e Y
iniciales, para que se puedan cambiar desde el cuerpo del programa. Elimina las
variables xNave e yNave de la clase Partida, que ya no sern necesarias.
(6.6.4) Mejora el proyecto de la clase Coche (ejercicio 6.5.5): aade un atributo
"cantidadDeRuedas" a la clase Vehiculo, junto con sus Get y Set. El constructor de
la clase Coche le dar el valor 4 y el constructor de la clase Moto le dar el valor 2.
Ejercicios propuestos:
(6.7.1) A partir de las clases del ejercicio 6.6.1, aade a la clase "Persona" un nuevo
mtodo Saludar, que reciba un parmetro, que ser el texto que debe decir esa
persona cuando salude.
(6.7.2) Ampla el proyecto del ejercicio 6.6.2 (Libro): la clase Libro tendr un
segundo constructor que permita dar valores al autor y el ttulo, pero no a la
ubicacin, que tomar el valor por defecto "No detallada".
(6.7.3) Ampla el esqueleto del ConsoleInvaders (6.6.3): La clase Nave tendr un
segundo constructor, sin parmetros, que prefijar su posicin inicial a (500,600).
La clase Enemigo tendr un segundo constructor, con parmetros X e Y, para
poder colocar un enemigo en cualquier punto desde Main.
(6.7.4) Crea dos nuevos mtodos en la clase Vehiculo (ejercicio 6.6.4): uno llamado
Circular, que fijar su "velocidad" (un nuevo atributo) a 50, y otro Circular(v), que
fijar su velocidad al valor que se indique como parmetro.
// Ejemplo_06_08a.cs
// Orden de llamada a los constructores
// Introduccin a C#, por Nacho Cabanes
using System;
// ------------------
{
public Animal()
{
Console.WriteLine("Ha nacido un animal");
}
}
// ------------------
// ------------------
// ------------------
Ha nacido un animal
Ha nacido un animal
Ha nacido un gato
Ha nacido un gato siams
Ha nacido un animal
Ha nacido un perro
Ha nacido un animal
Ha nacido un gato
Ejercicios propuestos:
(6.8.1) Crea un nico fuente que contenga las siguientes clases:
// Ejemplo_07_01a.cs
// Mtodos "static"
// Introduccin a C#, por Nacho Cabanes
using System;
Hardware.BorrarPantalla();
Console.WriteLine("Borrado!");
}
}
Desde una funcin "static" no se puede llamar a otras funciones que no lo sean.
Por eso, como nuestro "Main" debe ser static, deberemos siempre elegir entre:
// Ejemplo_07_01b.cs
// Alternativa a 07_01a, sin mtodos "static"
// Introduccin a C#, por Nacho Cabanes
using System;
Ejercicios propuestos:
(7.1.1) Ampla el ejemplo 07_01a con un funcin "static" llamada
"EscribirCentrado", que escriba centrado horizontalmente el texto que se le
indique como parmetro.
(7.1.2) Ampla el ejemplo 07_01b con un funcin llamada "EscribirCentrado", que
escriba centrado horizontalmente el texto que se le indique como parmetro. Al
contrario que en el ejercicio 7.1.1, esta versin no ser "static".
Revisin 0.99zz Pgina 220
Introduccin a la programacin con C#, por Nacho Cabanes
(7.1.3) Crea una nueva versin del ejercicio 5.2.3 (base de datos de ficheros,
descompuesta en funciones), en la que los mtodos y variables no sean "static".
Deberemos usar "new" dos veces: primero para reservar memoria para el array, y
luego para cada uno de los elementos. Por ejemplo, podramos partir del ejemplo
del apartado 6.8 y crear un array de 5 perros as:
// Ejemplo_07_02a.cs
// Array de objetos
// Introduccin a C#, por Nacho Cabanes
using System;
// ------------------
// ------------------
public Perro()
{
Console.WriteLine("Ha nacido un perro");
}
}
Ha nacido un animal
Ha nacido un perro
Ha nacido un animal
Ha nacido un perro
Ha nacido un animal
Ha nacido un perro
Ha nacido un animal
Ha nacido un perro
Ha nacido un animal
Ha nacido un perro
Ejercicio propuesto:
(7.2.1) Crea una versin ampliada del ejercicio 6.8.1 (clase Trabajador y
relacionadas), en la que no se cree un nico objeto de cada clase, sino un array de
tres objetos.
(7.2.2) Ampla el proyecto Libro (ejercicio 6.7.2), de modo que permita guardar
hasta 1.000 libros. Main mostrar un men que permita aadir un nuevo libro o
ver los datos de los ya existentes.
(7.2.3) Ampla el esqueleto del ConsoleInvaders (6.7.3), para que haya 10 enemigos
en una misma fila (todos compartirn una misma coordenada Y, pero tendrn
distinta coordenada X). Necesitars un nuevo constructor en la clase Enemigo, que
reciba los parmetros X e Y.
Un ejemplo ms detallado:
// Ejemplo_07_02b.cs
Revisin 0.99zz Pgina 222
Introduccin a la programacin con C#, por Nacho Cabanes
using System;
// ------------------
// ------------------
// ------------------
// ------------------
Ha nacido un animal
Ha nacido un perro
Ha nacido un animal
Ha nacido un gato
Ha nacido un animal
Ha nacido un gato
Ha nacido un gato siams
Ha nacido un animal
Ha nacido un perro
Ha nacido un animal
Ha nacido un perro
Ha nacido un animal
Ha nacido un perro
Ha nacido un animal
Ha nacido un perro
Ha nacido un animal
Ejercicios propuestos:
(7.2.4) A partir del ejemplo 07.02b y del ejercicio 6.8.1 (clase Trabajador y
relacionadas), crea un array de trabajadores en el que no sean todos de la misma
clase.
(7.2.5) Ampla el proyecto Libro (ejercicio 7.2.2), de modo que permita guardar
1000 documentos de cualquier tipo. A la hora de aadir un documento, se
preguntar al usuario si desea guardar un documento genrico o un libro, para
usar el constructor adecuado.
(7.2.6) Ampla el esqueleto del ConsoleInvaders (7.2.3), para que haya tres tipos de
enemigos, y un array que contenga 3x10 enemigos (3 filas, cada una con 10
enemigos de un mismo tipo, pero distinto del tipo de los elementos de las otras
filas). Cada tipo de enemigos ser una subclase de Enemigo, que se distinguir por
usar una "imagen" diferente. Puedes usar la "imagen" que quieras (siempre que
sea un string de letras, como "}{" o "XX"). Si estas imgenes no se muestran
Revisin 0.99zz Pgina 224
Introduccin a la programacin con C#, por Nacho Cabanes
// Ejemplo_07_03a.cs
// Array de objetos, sin "virtual"
// Introduccin a C#, por Nacho Cabanes
using System;
miPerro.Hablar();
miGato.Hablar();
miAnimal.Hablar();
misAnimales[0].Hablar();
misAnimales[1].Hablar();
misAnimales[2].Hablar();
}
}
// ------------------
// ------------------
// ------------------
(Recuerda que, como vimos en el apartado 6.4, ese "new" que aparece en "new
void Hablar" sirve simplemente para anular un "warning" del compilador, que dice
algo como "ests redifiniendo este mtodo; aade la palabra new para indicar que
eso es lo que deseas". Equivale a un "s, s que estoy redefiniendo este mtodo").
Guau!
Miauuu
Estoy comunicndome...
Estoy comunicndome...
Estoy comunicndome...
Estoy comunicndome...
miPerro.Hablar();
miGato.Hablar();
miAnimal.Hablar();
Revisin 0.99zz Pgina 226
Introduccin a la programacin con C#, por Nacho Cabanes
misAnimales[0].Hablar();
misAnimales[1].Hablar();
misAnimales[2].Hablar();
// Ejemplo_07_03b.cs
// Array de objetos, con "virtual"
// Introduccin a C#, por Nacho Cabanes
using System;
miPerro.Hablar();
miGato.Hablar();
miAnimal.Hablar();
misAnimales[0].Hablar();
misAnimales[1].Hablar();
misAnimales[2].Hablar();
}
}
// ------------------
// ------------------
// ------------------
Guau!
Miauuu
Estoy comunicndome...
Guau!
Miauuu
Estoy comunicndome...
Nota: Esto que nos acaba de ocurrir va a ser frecuente. Si el compilador nos avisa
de que deberamos aadir la palabra "new" porque estamos redefiniendo algo...
es habitual que realmente lo que necesitemos sea "virtual" en la clase base y
"override" en las clases derivada, especialmente si no se trata de un programa
trivial, sino que vamos a tener objetos de varias clases coexistiendo en el
programa, y especialmente si todos esos objetos van a ser parte de un nico array
o estructura similar (como las "listas", que veremos ms adelante).
Ejercicios propuestos:
(7.3.1) Crea una versin ampliada del ejercicio 6.5.1 (Persona, PersonaInglesa, etc),
en la que se cree un nico array que contenga personas de varios tipos.
(7.3.2) Crea una variante del ejercicio 7.2.2 (array de Trabajador y derivadas), en la
que se cree un nico array "de trabajadores", que contenga un objeto de cada
clase, y exista un mtodo "Saludar" que se redefina en todas las clases hijas,
usando "new" y probndolo desde "Main".
(7.3.3) Crea una variante del ejercicio anterior (7.3.2), que use "override" en vez de
"new".
(7.3.4) Ampla el proyecto Libro (ejercicio 7.2.5): tanto la clase Documento como la
clase Libro, tendrn un mtodo ToString, que devuelva una cadena de texto
formada por ttulo, autor y ubicacin, separados por guiones. Crea una clase
Articulo, que aada el campo "procedencia". El cuerpo del programa permitir
aadir Artculos o Libros, no documentos genricos. El mtodo ToString deber
mostrar tambin el nmero de pginas de un libro y la procedencia de un artculo.
La opcin de mostrar datos llamar a los correspondientes mtodos ToString.
Recuerda usar "virtual" y "override" si en un primer momento no se comporta
como debe.
(7.3.5) Ampla el esqueleto de ConsoleInvaders (7.2.6) para que muestre las
imgenes correctas de los enemigos, usando "virtual" y "override". Adems, cada
tipo de enemigos debe ser de un color distinto. (Nota: para cambiar colores
puedes usar Console.ForegroundColor = ConsoleColor.Green;). La nave que
maneja el usuario debe ser blanca.
Por ejemplo, podemos hacer que un Gato Siams hable igual que un Gato normal,
pero diciendo "Pfff" despus, as:
// Ejemplo_07_04a.cs
// Ejemplo de clases: Llamar a la superclase
// Introduccin a C#, por Nacho Cabanes
using System;
miGato.Hablar();
Console.WriteLine(); // Linea en blanco
miGato2.Hablar();
}
// ------------------
// ------------------
// ------------------
{
public new void Hablar()
{
base.Hablar();
Console.WriteLine("Pfff");
}
}
Su resultado sera
Miauuu
Miauuu
Pfff
Ejercicios propuestos:
(7.4.1) Crea una versin ampliada del ejercicio 7.3.3, en la que el mtodo "Hablar"
de todas las clases hijas se apoye en el de la clase "Trabajador".
(7.4.2) Crea una versin ampliada del ejercicio 7.4.1, en la que el constructor de
todas las clases hijas se apoye en el de la clase "Trabajador".
(7.4.3) Refina el proyecto Libro (ejercicio 7.3.4), para que el mtodo ToString de la
clase Libro se apoye en el de la clase Documento, y tambin lo haga el de la clase
Articulo.
(7.4.4) Ampla el esqueleto de ConsoleInvaders (7.3.5) para que en Enemigo haya
un nico constructor que reciba las coordenadas X e iniciales. Los constructores de
los tres tipos de enemigos deben basarse en ste.
// Ejemplo_07_05a.cs
// Primer ejemplo de uso de "this": parmetros
// Introduccin a C#, por Nacho Cabanes
using System;
// ------------------
En muchos casos, podemos evitar este uso de "this". Por ejemplo, normalmente es
preferible usar otro nombre en los parmetros:
Pero "this" tiene tambin otros usos. Por ejemplo, podemos crear un constructor
a partir de otro que tenga distintos parmetros:
// Ejemplo_07_05b.cs
// Segundo ejemplo de uso de "this": constructores
// Introduccin a C#, por Nacho Cabanes
using System;
// ------------------
private int x;
private int y;
private string texto;
La palabra "this" se usa mucho tambin para que unos objetos "conozcan" a los
otros. Por ejemplo, en un juego de 2 jugadores, podramos tener una clase
Jugador, heredar de ella las clases Jugador1 y Jugador2, que sern muy parecidas
entre s, y nos podramos sentir tentados de hacer que la clase Jugador tenga un
"adversario" como atributo, y que el Jugador1 indique que su adversario es de tipo
Jugador2, y lo contrario para el otro jugador, as:
// Ejemplo_07_05c.cs
// Dos clases que se usan mutuamente: recursividad indirecta
// Introduccin a C#, por Nacho Cabanes
using System;
// ------------------
// ------------------
{
public Jugador1()
{
adversario = new Jugador2();
}
}
// ------------------
// Ejemplo_07_05d.cs
// Dos clases que se usan mutuamente: "Main" coordina
// Introduccin a C#, por Nacho Cabanes
using System;
// ------------------
}
}
Otra alternativa es que un Jugador le pueda decir a otro "yo soy tu adversario", y
ese "yo" equivaldr a un "this". En general, eso simplificar el programa principal, a
cambio de complicar ligeramente las clases auxiliares:
// Ejemplo_07_05e.cs
// Dos clases que se usan mutuamente: "this"
// Introduccin a C#, por Nacho Cabanes
using System;
// ------------------
public Jugador()
{
}
// Y yo soy su adversario
adversario.SetAdversario( this );
}
Hay que recordar que, en general, cuando una clase contiene a otras, la clase
contenedora sabe los detalles de las clases contenidas (la "casa" conoce a sus
"puertas"), pero las clases contenidas no saben nada de la clase que las contiene
(las "puertas" no saben otros detalles de la "casa" a la que pertenecen). Si
queremos que se puedan comunicar con la clase contenedora, deberemos usar
Revisin 0.99zz Pgina 236
Introduccin a la programacin con C#, por Nacho Cabanes
"this" para que la conozcan, en vez de crear una nueva clase contenedora con
"new", o provocaremos una recursividad indirecta sin fin, como en el primer
ejemplo.
Ejercicios propuestos:
(7.5.1) Crea una versin ampliada del ejercicio 7.4.2, en la que el constructor sin
parmetros de la clase "Trabajador" se apoye en otro constructor que reciba como
parmetro el nombre de esa persona. La versin sin parmetros asignar el valor
"Nombre no detallado" al nombre de esa persona.
(7.5.2) Crea una clase Puerta con un ancho, un alto y un mtodo "MostrarEstado"
que muestre su ancho y su alto. Crea una clase Casa, que contenga 3 puertas y
otro mtodo "MostrarEstado" que escriba "Casa" y luego muestre el estado de sus
tres puertas.
(7.5.3) Crea una clase Casa, con una superficie (por ejemplo, 90 m 2) y un mtodo
"MostrarEstado" que escriba su superficie. Cada casa debe contener 3 puertas. Las
puertas tendrn un ancho, un alto y un mtodo "MostrarEstado" que muestre su
ancho y su alto y la superficie de la casa en la que se encuentran. Crea un
programa de prueba que cree una casa y muestre sus datos y los de sus tres
puertas.
(7.5.4) Ampla el esqueleto de ConsoleInvaders (7.4.4), de modo que el constructor
sin parmetros de la clase Nave se apoye en el constructor con parmetros de la
misma clase, prefijando unas coordenadas que te parezcan las ms adecuadas.
que estamos (por ejemplo, una Matriz) y recibira dos datos de ese mismo tipo
como parmetros:
return nuevaMatriz;
}
Desde "Main", calcularamos una matriz como suma de otras dos haciendo
simplemente
Eso s, debes tener presente que "no todo se puede redefinir". Por ejemplo,
puedes pensar en sobrecargar los operadores [ y ] para acceder a un cierto
elemento de la matriz, pero C# no permite redefinir cualquier operador, y los
corchetes estn entre los que no podremos cambiar. En su lugar, deberas crear
mtodos Get para acceder a una posicin y mtodos Set para cambiar el valor de
una posicin.
Ejercicios propuestos:
(7.6.1) Desarrolla una clase "Matriz", que represente a una matriz de 3x3, con
mtodos para indicar el valor que hay en una posicin, leer el valor de una
posicin, escribir la matriz en pantalla y sumar dos matrices. (Nota: en C# puedes
sobrecargar el operador "+", pero no el operador "[]", de modo que tendrs que
crear mtodos "get" y "set" para leer los valores de posiciones de la matriz y para
cambiar su valor).
(7.6.2) Si has estudiado los "nmeros complejos", crea una clase "Complejo", que
represente a un nmero complejo (formado por una parte "real" y una parte
"imaginaria"). Incluye un constructor que reciba ambos datos como parmetros.
Crea tambin mtodos "get" y "set" para leer y modificar los valores de dichos
datos, as como mtodos que permitan saber los valores de su mdulo y su
argumento. Crea un mtodo que permita sumar un complejo a otro, y redefine el
operador "+" como forma alternativa de realizar esa operacin.
(7.6.3) Crea una clase "Fraccion", que represente a una fraccin, formada por un
numerador y un denominador. Crea un constructor que reciba ambos datos como
parmetros y otro constructor que reciba slo el numerador. Crea un mtodo
Escribir, que escriba la fraccin en la forma "3 / 2". Redefine los operadores "+", "-",
"*" y "/" para que permitan realizar las operaciones habituales con fracciones.
(7.6.4) Crea una clase "Vector3", que represente a un vector en el espacio de 3
dimensiones. Redefine los operadores "+" y "-" para sumar y restar dos vectores,
"*" para hallar el producto escalar de dos vectores y "%" para calcular su producto
vectorial.
(7.7.1) Crea un proyecto "Space Invaders" con todas las clases que vimos al
principio del tema 6. El proyecto no ser jugable, pero deber compilar
correctamente.
(7.7.2) Crea un proyecto "PersonajeMovil", que ser un esqueleto sobre el que se
podra ampliar para crear un juego de plataformas. Existir una pantalla de
bienvenida, una pantalla de crditos y una partida, en la que el usuario podr
mover a un personaje (que puede ser representado por una letra X). Las teclas de
movimiento quedan a tu criterio, pero podran ser "wasd" o las flechas del cursor.
Si quieres que el movimiento sea ms suave, puedes investigar sobre
Console.Readkey (que posiblemente habrs usado ya en el proyecto
ConsoleInvaders) como alternativa a Console.ReadLine.
(7.7.3) Crea un proyecto "Laberinto", a partir del proyecto "PersonajeMovil",
aadiendo tres enemigos que se muevan de lado a lado de forma autnoma y un
laberinto de fondo, cuyas paredes no se puedan "pisar" (la clase Laberinto puede
tener mtodos que informen sobre si el jugador podra moverse a ciertas
coordenadas X e Y). Si el personaje toca a un enemigo, acabar la partida y se
regresar a la pantalla de bienvenida.
(7.7.4) Crea un proyecto "Laberintos", a partir del proyecto "Laberinto", aadiendo
premios que recoger, tres vidas para nuestro personaje y varios laberintos
distintos que recorrer.
(7.7.5) Crea un proyecto "SistemaDomotico", que simule un sistema domtico para
automatizar ciertas funciones en una casa: apertura y cierre de ventanas y
puertas, encendido de calefaccin, etc. Adems de esos elementos fsicos de la
casa, tambin existir un panel de control, desde el que el usuario podra controlar
el resto de elementos, as como programar el comportamiento del sistema (por
8. Manejo de ficheros
8.1. Escritura en un fichero de texto
Cuando queramos manipular un fichero, siempre deberemos hacerlo en tres
pasos:
Abrir el fichero.
Leer datos de l o escribir datos en l.
Cerrar el fichero.
// Ejemplo_08_01a.cs
// Escritura en un fichero de texto
// Introduccin a C#, por Nacho Cabanes
using System;
using System.IO; // Para StreamWriter
fichero = File.CreateText("prueba.txt");
fichero.WriteLine("Esto es una lnea");
fichero.Write("Esto es otra");
fichero.WriteLine(" y esto es continuacin de la anterior");
fichero.Close();
}
}
// Ejemplo_08_01b.cs
// Escritura en un fichero de texto, con constructor
// Introduccin a C#, por Nacho Cabanes
using System;
using System.IO; // Para StreamWriter
Existe otra sintaxis, ms compacta, que nos permite olvidarnos (hasta cierto
punto) de cerrar el fichero, empleando la orden "using" para delimitar el bloque
que lee datos del fichero, de esta forma:
// Ejemplo_08_01c.cs
// Escritura en un fichero de texto, con "using"
// Introduccin a C#, por Nacho Cabanes
using System;
using System.IO; // Para StreamWriter
Ejercicios propuestos:
(8.1.1) Crea un programa que vaya leyendo las frases que el usuario teclea y las
guarde en un fichero de texto llamado "registroDeUsuario.txt". Terminar cuando
la frase introducida sea "fin" (esa frase no deber guardarse en el fichero).
(8.1.2) Crea una versin de la base de datos de ficheros (ejercicio 5.2.3), de modo
que todos los datos se vuelquen a un fichero de texto al terminar la ejecucin del
programa.
(8.1.3) Amplia el proyecto Libro (ejercicio 7.7.8), de modo que todos los datos se
vuelquen a un fichero de texto al terminar la ejecucin del programa.
// Ejemplo_08_02a.cs
// Lectura de un fichero de texto
// Introduccin a C#, por Nacho Cabanes
using System;
using System.IO; // Para StreamReader
fichero = File.OpenText("prueba.txt");
linea = fichero.ReadLine();
Console.WriteLine( linea );
Console.WriteLine( fichero.ReadLine() );
fichero.Close();
}
}
// Ejemplo_08_02b.cs
// Lectura de un fichero de texto, con constructor
Revisin 0.99zz Pgina 244
Introduccin a la programacin con C#, por Nacho Cabanes
using System;
using System.IO; // Para StreamReader
// Ejemplo_08_02c.cs
// Lectura de un fichero de texto, con "using"
// Introduccin a C#, por Nacho Cabanes
using System;
using System.IO; // Para StreamReader
Ejercicios propuestos:
(8.2.1) Crea un programa que lea las tres primeras lneas del fichero creado en el
ejercicio 8.1.1 y las muestre en pantalla. Usa OpenText para abrirlo.
(8.2.2) Crea una versin alternativa del ejercicio 8.2.1, usando el constructor de
StreamReader.
(8.2.3) Crea una versin alternativa del ejercicio 8.2.2, empleando la sintaxis
alternativa de "using".
// Ejemplo_08_03a.cs
// Lectura de un fichero de texto completo
// Introduccin a C#, por Nacho Cabanes
using System;
using System.IO;
public class Ejemplo_08_03a
{
public static void Main()
{
StreamReader fichero;
string linea;
fichero = File.OpenText("prueba.txt");
do
{
linea = fichero.ReadLine();
if (linea != null)
Console.WriteLine( linea );
}
while (linea != null);
fichero.Close();
}
}
Ejercicios propuestos:
(8.3.1) Crea una variante del ejemplo 08_03a, empleando un constructor en vez de
"File.OpenText".
(8.3.2) Crea una variante del ejemplo 08_03a, empleando "using" en vez de "Close".
(8.3.3) Prepara un programa que pregunte un nombre de fichero y muestre en
pantalla el contenido de ese fichero, haciendo una pausa despus de cada 24
lneas, para que d tiempo a leerlo. Cuando el usuario pulse la tecla Intro, se
mostrarn las siguientes 24 lneas, y as sucesivamente hasta que termine el
fichero.
(8.3.4) Ampla la base de datos de ficheros (ejercicio 8.1.2), de modo que los datos
se lean desde fichero (si existe) en el momento de lanzar el programa (puedes usar
try-catch para que el programa no falle en el momento inicial, en el que quiz
// Ejemplo_08_04a.cs
// Aadir a un fichero de texto
// Introduccin a C#, por Nacho Cabanes
using System;
using System.IO;
fichero = File.CreateText("prueba2.txt");
fichero.WriteLine("Primera lnea");
fichero.Close();
fichero = File.AppendText("prueba2.txt");
fichero.WriteLine("Segunda lnea");
fichero.Close();
}
}
// Ejemplo_08_04b.cs
// Aadir a un fichero de texto, con constructor y "using"
// Introduccin a C#, por Nacho Cabanes
using System;
using System.IO;
Ejercicios propuestos:
(8.4.1) Un programa que pida al usuario que teclee frases, y las almacene en el
fichero "log.txt", que puede existir anteriormente (y que no deber borrarse, sino
aadir al final de su contenido). Cada sesin acabar cuando el usuario pulse Intro
sin teclear nada.
Debemos recordar que, como la barra invertida que se usa en sistemas Windows
para separar los nombres de los directorios, coincide con el carcter de control
que se usa en las cadenas de C y los lenguajes que derivan de l, deberemos
escribir dichas barras invertidas repetidas, as:
Como esta sintaxis puede llegar a resultar incmoda, en C# existe una alternativa:
podemos indicar una arroba (@) justo antes de abrir las comillas, y entonces no
ser necesario delimitar los caracteres de control:
Ejercicios propuestos:
(8.5.1) Crea un programa que pida al usuario pares de nmeros enteros y escriba
su suma (con el formato "20 + 3 = 23") en pantalla y en un fichero llamado
"sumas.txt", que se encontrar en un subdirectorio llamado "resultados". Cada vez
// Ejemplo_08_06a.cs
// Saber si un fichero existe
// Introduccin a C#, por Nacho Cabanes
using System;
using System.IO;
Ejercicios propuestos:
(8.6.1) Mejora el ejercicio 8.3.4 para que compruebe antes si el fichero existe, y
muestre un mensaje de aviso en caso de que no sea as.
(8.6.2) Mejora el ejemplo 08_06a para que no use "while (true)", sino una variable
booleana de control.
Por ello, una forma ms eficaz de comprobar si ha existido algn tipo de error es
comprobar las posibles "excepciones", con las que ya tuvimos un contacto al final
del tema 2.
Tpicamente, los pasos que puedan ser problemticos irn dentro del bloque "try"
y los mensajes de error y/o acciones correctoras estarn en el bloque "catch". As,
un primer ejemplo, que mostrara todo el contenido de un fichero de texto y que
en caso de error se limitara a mostrar un mensaje de error y a abandonar el
programa, podra ser:
// Ejemplo_08_07a.cs
// Excepciones y ficheros (1)
// Introduccin a C#, por Nacho Cabanes
using System;
using System.IO;
try
{
fichero = File.OpenText(nombre);
do
{
linea = fichero.ReadLine();
if (linea != null)
Console.WriteLine( linea );
Revisin 0.99zz Pgina 250
Introduccin a la programacin con C#, por Nacho Cabanes
}
while (linea != null);
fichero.Close();
}
catch (Exception exp)
{
Console.WriteLine("Ha habido un error: {0}", exp.Message);
return;
}
}
}
El fichero existe y es de slo lectura (se lanzar una excepcin del tipo
"IOException").
La ruta del fichero es demasiado larga (excepcin de tipo
"PathTooLongException").
El disco puede estar lleno (IOException).
As, dentro de cada bloque "catch" podramos indicar una excepcin ms concreta
que una simple "Exception", de forma que el mensaje de aviso sea ms concreto, o
que podamos dar pasos ms adecuados para solucionar el problema:
// Ejemplo_08_07b.cs
// Excepciones y ficheros (2)
// Introduccin a C#, por Nacho Cabanes
using System;
using System.IO;
string linea;
try
{
fichero = File.CreateText(nombre);
fichero.WriteLine( linea );
fichero.Close();
}
catch (PathTooLongException e)
{
Console.WriteLine("Nombre demasiado largo!");
}
catch (IOException e)
{
Console.WriteLine("No se ha podido escribir!");
Console.WriteLine("El error exacto es: {0}", e.Message);
}
}
}
Hay que recordar que, como la consola se comporta como un fichero de texto
(realmente, como un fichero de entrada y otro de salida), se puede usar
"trycatch" para comprobar ciertos errores relacionados con la entrada de datos,
como cuando no se puede convertir un dato a un cierto tipo (por ejemplo, si
queremos esperamos que introduzca un nmero, pero, en lugar de ello, tecleado
un texto).
Ejercicios propuestos:
(8.7.1) Un programa que pida al usuario el nombre de un fichero de origen y el de
un fichero de destino, y que vuelque al segundo fichero el contenido del primero,
convertido a maysculas. Se debe controlar los posibles errores, como que el
fichero de origen no exista, o que el fichero de destino no se pueda crear.
(8.7.2) Un programa que pida al usuario un nmero, una operacin (+, -, *, /) y un
segundo nmero, y muestre el resultado de la correspondiente operacin. Si se
teclea un dato no numrico, el programa deber mostrar un aviso y volver a
pedirlo, en vez de interrumpir la ejecucin.
(8.7.3) Un programa que pida al usuario repetidamente pares de nmeros y la
operacin a realizar con ellos (+, -, *, /) y guarde en un fichero "calculadora.txt" el
resultado de dichos clculos (con la forma "15 * 6 = 90"). Debe controlar los
posibles errores, como que los datos no sean numricos, la divisin entre cero, o
que el fichero no se haya podido crear.
// Ejemplo_08_09a.cs
// Ficheros binarios: lectura de un byte con FileStream
// Introduccin a C#, por Nacho Cabanes
using System;
using System.IO;
try
{
fichero = File.OpenRead(nombre);
unDato = (byte) fichero.ReadByte();
Console.WriteLine("El byte leido es un {0}",
unDato);
fichero.Close();
}
catch (Exception e)
{
Console.WriteLine("Error: "+e.Message);
return;
}
}
}
// Ejemplo_08_09b.cs
// Ficheros binarios: lectura de un byte con FileStream, constructor
// Introduccin a C#, por Nacho Cabanes
using System;
using System.IO;
try
{
FileStream fichero = new FileStream(nombre, FileMode.Open);
byte unDato = (byte) fichero.ReadByte();
Console.WriteLine("El byte leido es un {0}",
unDato);
fichero.Close();
}
catch (Exception e)
{
Console.WriteLine("Error: "+e.Message);
return;
}
}
}
// Ejemplo_08_09c.cs
// Ficheros binarios: lectura de un byte con FileStream, using
// Introduccin a C#, por Nacho Cabanes
using System;
using System.IO;
try
{
using (FileStream fichero = new FileStream(
nombre, FileMode.Open))
{
byte unDato = (byte) fichero.ReadByte();
Console.WriteLine("El byte leido es un {0}",
unDato);
}
}
catch (Exception e)
Revisin 0.99zz Pgina 255
Introduccin a la programacin con C#, por Nacho Cabanes
{
Console.WriteLine("Error: "+e.Message);
return;
}
}
}
Los otros modos de apertura posibles (cuyos nombre en ingls son casi
autoexplicativos) son: Append (aade al final o crea si no existe), Create (crea el
fichero o lo sobreescribe si ya exista), CreateNew (crea si no exista, o falla si
existe), Open (abre si existe o falla si no existe), OpenOrCreate (abre si existe o lo
crea si no existe), Truncate (abre un fichero que debe existir y lo vaca).
Ejercicios propuestos:
(8.9.1) Abre un fichero con extensin EXE (cuyo nombre introducir el usuario) y
comprueba si realmente se trata de un ejecutable, mirando si los dos primeros
bytes del fichero son un 77 (que corresponde a una letra "M", segn el estndar
que marca el cdigo ASCII) y un 90 (una letra "Z"), respectivamente.
(8.9.2) Abre una imagen BMP (cuyo nombre introducir el usuario) y comprueba si
realmente se trata de un fichero en ese formato, mirando si los dos primeros
bytes del fichero corresponden a una letra "B" y una letra "M", respectivamente.
(8.9.3) Abre una imagen GIF y comprueba si sus tres primeros bytes son las letras
G, I, F.
// Ejemplo_08_10a.cs
// Ficheros binarios: lectura completa de un FileStream
// Introduccin a C#, por Nacho Cabanes
using System;
using System.IO;
La segunda forma de leer hasta el final del fichero es comprobar si el dato ledo es
un -1. Cuando esto ocurra, ser que seal de que se ha llegado al final del fichero.
El fuente correspondiente podra ser:
// Ejemplo_08_10b.cs
// Ficheros binarios: lectura completa de un FileStream
// Introduccin a C#, por Nacho Cabanes
using System;
using System.IO;
Ejercicios propuestos:
(8.10.1) Crea un programa que compruebe si un fichero (cuyo nombre introducir
el usuario) contiene una cierta letra (tambin escogida por el usuario).
(8.10.2) Crea un programa que cuente la cantidad de vocales que contiene un
fichero binario.
(8.10.3) Crea un programa que diga si un fichero (binario) contiene una cierta
palabra que introduzca el usuario.
(8.10.4) Crea un programa que extraiga a un fichero de texto todos los caracteres
alfabticos (cdigos 32 a 127, adems del 10 y el 13) que contenga un fichero
binario.
Para eso, dentro de la clase "FileStream" tenemos mtodo "Read", que nos
permite leer una cierta cantidad de datos desde el fichero. Le indicaremos en qu
array guardar esos datos, a partir de qu posicin del array debe introducir los
datos (no la posicin en el fichero, sino en el array, de modo que casi siempre ser
0, es decir, al principio del array), y qu cantidad de datos se deben leer. Nos
devuelve un valor, que es la cantidad de datos que se han podido leer realmente
(porque puede ser menos de lo que le hemos pedido, si hay un error de lectura o
si hemos llegado al final del fichero).
// Ejemplo_08_11a.cs
// Ficheros binarios: lectura por bloques de un FileStream
// Introduccin a C#, por Nacho Cabanes
using System;
using System.IO;
Ejercicios propuestos:
(8.11.1) Abre un fichero con extensin EXE y comprobar si realmente se trata de
un ejecutable, mirando si los dos primeros bytes del fichero corresponden a una
letra "M" y una letra "Z", respectivamente. Debes leer ambos bytes a la vez, usando
un array.
(8.11.2) Abre una imagen BMP (cuyo nombre introducir el usuario) y comprueba
si realmente se trata de un fichero en ese formato, mirando si los dos primeros
bytes del fichero corresponden a una letra "B" y una letra "M", respectivamente.
Usa "Read" y un array.
(8.11.3) Abre una imagen GIF y comprueba si sus tres primeros bytes son las letras
G, I, F. Guarda los 3 datos en un array.
(8.11.4) Abre una imagen en formato BMP y comprueba si est comprimida,
mirando el valor del byte en la posicin 30 (empezando a contar desde 0). Si ese
valor es un 0 (que es lo habitual), indicar que el fichero no est comprimido.
Debers leer toda la cabecera (los primeros 54 bytes) con una sola orden.
Para ello, tenemos el mtodo "Seek". A este mtodo se le indican dos parmetros:
la posicin a la que queremos saltar, y el punto desde el que queremos que se
cuente esa posicin (desde el comienzo del fichero SeekOrigin.Begin-, desde la
posicin actual SeekOrigin.Current- o desde el final del fichero SeekOrigin.End-).
La posicin es un Int64, porque puede ser un nmero muy grande e incluso un
nmero negativo (si miramos desde el final del fichero, porque desde l habr que
retroceder).
// Ejemplo_08_12a.cs
// Ficheros binarios: posicin
// Introduccin a C#, por Nacho Cabanes
using System;
using System.IO;
fichero.Close();
}
}
(Nota: existe una propiedad "CanSeek" que nos permite saber si el fichero que
hemos abierto permite realmente que nos movamos a unas posiciones u otras).
Ejercicios propuestos:
(8.12.1) Abre un fichero con extensin EXE y comprueba si su segundo byte
corresponde a una letra "Z", sin leer su primer byte.
(8.12.2) Abre una imagen en formato BMP y comprueba si est comprimida,
mirando el valor del byte en la posicin 30 (empezando a contar desde 0). Si ese
Revisin 0.99zz Pgina 260
Introduccin a la programacin con C#, por Nacho Cabanes
// Ejemplo_08_13a.cs
// Ficheros binarios: lectura de un short, con BinaryReader
// Introduccin a C#, por Nacho Cabanes
using System;
using System.IO;
Si preferimos hacer esta lectura byte a byte, podremos usar tanto un FileStream
como un BinaryReader, pero deberemos recomponer ese "short", teniendo en
cuenta que, en la mayora de sistemas actuales, en primer lugar aparece el byte
menos significativo y luego el byte ms significativo, de modo que el dato sera
byte1 + byte2*256, as:
// Ejemplo_08_13b.cs
// Ficheros binarios: lectura de un short, byte a byte
// Introduccin a C#, por Nacho Cabanes
using System;
using System.IO;
// Ejemplo_08_13c.cs
// Ficheros binarios: lectura de un short, con BinaryReader, using
// Introduccin a C#, por Nacho Cabanes
using System;
using System.IO;
Ejercicios propuestos:
(8.13.1) Abre un fichero con extensin EXE y comprueba si comienza con el entero
corto 23117.
(8.13.2) Abre una imagen en formato BMP y comprueba si comienza con el entero
corto 19778.
// Ejemplo_08_13d.cs
// Ficheros binarios: Seek, con BinaryReader
// Introduccin a C#, por Nacho Cabanes
using System;
using System.IO;
Ejercicios propuestos:
(8.13.3) El alto de un fichero BMP es un entero de 32 bits que se encuentra en la
posicin 22. Ampla el ejemplo 08_13d, para que muestre tambin el alto de ese
fichero BMP.
Un fichero BMP est compuesto por las siguientes partes: una cabecera de
fichero, una cabecera del bitmap, una tabla de colores y los bytes que
definirn la imagen.
Reservado 6-7
Reservado 8-9
Inicio de los datos de la imagen 10-13
Tamao de la cabecera de bitmap 14-17
Anchura (pxeles) 18-21
Altura (pxeles) 22-25
Nmero de planos 26-27
Tamao de cada punto 28-29
Compresin (0=no comprimido) 30-33
Tamao de la imagen 34-37
Resolucin horizontal 38-41
Resolucin vertical 42-45
Tamao de la tabla de color 46-49
Contador de colores importantes 50-53
Por tanto, ser fcil hacer que se nos muestren algunos detales, como su ancho,
su alto, la resolucin y si la imagen est comprimida o no:
// Ejemplo_08_14a.cs
// Informacin de un fichero BMP, con BinaryReader
// Introduccin a C#, por Nacho Cabanes
using System;
using System.IO;
if (! File.Exists( nombre) )
{
Console.WriteLine("No encontrado!");
}
else
{
BinaryReader fichero = new BinaryReader(
File.Open(nombre, FileMode.Open));
// A continuacin: alto
int alto = fichero.ReadInt32();
Console.WriteLine("Alto: {0}", alto);
fichero.Close();
}
}
}
}
// Ejemplo_08_14b.cs
// Informacin de un fichero BMP, con FileStream
// Introduccin a C#, por Nacho Cabanes
using System;
using System.IO;
if (! File.Exists( nombre) )
{
Console.WriteLine("No encontrado!");
}
else
{
FileStream fichero = File.OpenRead(nombre);
int tamanyoCabecera = 54;
byte[] cabecera = new byte[tamanyoCabecera];
if (cantidadLeida != tamanyoCabecera)
{
Console.WriteLine("No se ha podido leer la cabecera");
}
else
{
// Analizo los dos primeros bytes
char marca1 = Convert.ToChar( cabecera[0] );
char marca2 = Convert.ToChar( cabecera[1] );
Ejercicios propuestos:
(8.14.1) Localiza en Internet informacin sobre el formato de imgenes PCX. Crea
un programa que diga el ancho, alto y nmero de colores de una imagen PCX.
(8.14.2) Localiza en Internet informacin sobre el formato de imgenes GIF. Crea
un programa que diga el subformato, ancho, alto y nmero de colores de una
imagen GIF.
Vamos a ver un ejemplo que junte todo ello: crearemos un fichero, guardaremos
datos, lo leeremos para comprobar que todo es correcto, aadiremos al final, y
volveremos a leer:
// Ejemplo_08_15a.cs
// Ficheros binarios: escritura en FileStream
// Introduccin a C#, por Nacho Cabanes
using System;
using System.IO;
byte[] datos;
nombre = "datos.dat";
datos = new byte[TAMANYO_BUFFER];
try
{
// Primero creamos el fichero, con algun dato
fichero = File.Create( nombre );
fichero.Write(datos, 0, TAMANYO_BUFFER);
fichero.Close();
}
catch (Exception e)
{
Console.WriteLine("Problemas: "+e.Message);
return;
}
}
}
(Nota: existe una propiedad "CanWrite" que nos permite saber si se puede escribir
en el fichero).
Si queremos que escribir datos bsicos de C# (float, int, etc.) en vez de un array de
bytes, podemos usar un "BinaryWriter", que se maneja de forma similar a un
"BinaryReader", con la diferencia de que no tenemos mtodos WriteByte,
WriteString y similares, sino un nico mtodo "Write", que se encarga de escribir el
dato que le indiquemos, sea del tipo que sea:
// Ejemplo_08_15b.cs
// Ficheros binarios: escritura en BinaryWriter
// Introduccin a C#, por Nacho Cabanes
using System;
using System.IO;
Console.WriteLine("Creando fichero...");
// Primero vamos a grabar datos
try
{
ficheroSalida = new BinaryWriter(
File.Open(nombre, FileMode.Create));
unDatoByte = 1;
unDatoInt = 2;
unDatoFloat = 3.0f;
unDatoDouble = 4.0;
unDatoString = "Hola";
ficheroSalida.Write(unDatoByte);
ficheroSalida.Write(unDatoInt);
ficheroSalida.Write(unDatoFloat);
ficheroSalida.Write(unDatoDouble);
ficheroSalida.Write(unDatoString);
ficheroSalida.Close();
}
catch (Exception e)
{
Console.WriteLine("Problemas al crear: "+e.Message);
return;
}
try
{
ficheroEntrada = new BinaryReader(
File.Open(nombre, FileMode.Open));
unDatoByte = ficheroEntrada.ReadByte();
Console.WriteLine("El byte leido es un {0}",
unDatoByte);
unDatoInt = ficheroEntrada.ReadInt32();
Console.WriteLine("El int leido es un {0}",
unDatoInt);
unDatoFloat = ficheroEntrada.ReadSingle();
Console.WriteLine("El float leido es un {0}",
unDatoFloat);
unDatoDouble = ficheroEntrada.ReadDouble();
Console.WriteLine("El double leido es un {0}",
unDatoDouble);
unDatoString = ficheroEntrada.ReadString();
Console.WriteLine("El string leido es \"{0}\"",
unDatoString);
Console.WriteLine("Volvamos a leer el int...");
ficheroEntrada.BaseStream.Seek(1, SeekOrigin.Begin);
unDatoInt = ficheroEntrada.ReadInt32();
Console.WriteLine("El int leido es un {0}",
unDatoInt);
ficheroEntrada.Close();
}
catch (Exception e)
{
Console.WriteLine("Problemas al leer: "+e.Message);
return;
}
}
}
En este caso hemos usado "FileMode.Create" para indicar que queremos crear el
fichero, en vez de abrir un fichero ya existente. Los modos de fichero que
podemos emplear en un BinaryReader o en un BinaryWriter son los siguientes:
Revisin 0.99zz Pgina 270
Introduccin a la programacin con C#, por Nacho Cabanes
Ejercicios propuestos:
(8.15.1) Crea una copia de un fichero EXE. La copia debe tener el mismo nombre y
extensin BAK.
(8.15.2) Crea un programa que "encripte" el contenido de un fichero BMP,
volcando su contenido a un nuevo fichero, en el que intercambiar los dos
primeros bytes. Para desencriptar, bastar con volver a intercambiar esos dos
bytes, volcando a un tercer fichero.
Una vez que hayamos indicado que queremos leer y escribir del fichero, podremos
movernos dentro de l con "Seek", leer datos con "Read" o "ReadByte", y grabar
datos con "Write" o "WriteByte":
// Ejemplo_08_16a.cs
// Lectura y escritura en fichero binario
// Introduccin a C#, por Nacho Cabanes
Revisin 0.99zz Pgina 271
Introduccin a la programacin con C#, por Nacho Cabanes
using System;
using System.IO;
nombre = "datos.dat";
datos = new byte[TAMANYO_BUFFER];
try
{
int posicion = 0;
fichero.Seek(2, SeekOrigin.Begin);
int nuevoDato = fichero.ReadByte();
Console.WriteLine("El tercer byte es un {0}", nuevoDato);
fichero.Seek(2, SeekOrigin.Begin);
fichero.WriteByte( 4 );
fichero.Seek(2, SeekOrigin.Begin);
nuevoDato = fichero.ReadByte();
Console.WriteLine("Ahora el tercer byte es un {0}", nuevoDato);
fichero.Close();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
return;
}
}
}
Ejercicios propuestos:
(8.16.1) Crea un programa que "encripte" el contenido de un fichero BMP,
intercambiando los dos primeros bytes (y modificando el mismo fichero). Para
desencriptar, bastar con volver a intercambiar esos dos bytes.
Revisin 0.99zz Pgina 272
Introduccin a la programacin con C#, por Nacho Cabanes
9. Persistencia de objetos
9.1. Por qu la persistencia?
Una forma alternativa de conseguir que la informacin que manipula un programa
est disponible para una ejecucin posterior es no guardarla en un "fichero
convencional", sino pedir al sistema que se conserve el estado de los objetos que
forman el programa.
// Ejemplo_09_01a.cs
// Primer acercamiento a la persistencia
// Introduccin a C#, por Nacho Cabanes
using System;
using System.IO;
int numero;
{
Persist01 ejemplo = new Persist01();
ejemplo.SetNumero(5);
Console.WriteLine("Valor: {0}", ejemplo.GetNumero());
ejemplo.Guardar( "ejemplo.dat" );
ejemplo2.Cargar( "ejemplo.dat" );
Console.WriteLine("Y ahora: {0}", ejemplo2.GetNumero());
}
}
Valor: 5
Valor 2: 0
Y ahora: 5
Pero esta forma de trabajar se complica mucho cuando nuestro objeto tiene
muchos atributos, y ms an si se trata de bloques de objetos (por ejemplo, un
"array") que a su vez contienen otros objetos. Por eso, existen maneras ms
automatizadas y que permiten escribir menos cdigo.
Ejercicios propuestos:
(9.1.1) Ampla la clase Persona (ejercicio 6.2.1), para que permita guardar su
estado y recuperarlo posteriormente.
[Serializable]
public class Persist02
En segundo lugar, como vamos a sobrescribir todo el objeto, en vez de slo los
atributos, ahora los mtodos "Cargar" y "Guardar" ya no pueden pertenecer a esa
misma clase: debern estar en una clase auxiliar, que se encargue de salvar los
datos y recuperarlos. En este primer ejemplo, nos limitaremos a declararlos
"static" para que sea Main el que se encargue de esas tareas:
// Ejemplo_09_02a.cs
// Ejemplo bsico de persistencia
// Introduccin a C#, por Nacho Cabanes
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
[Serializable]
public class Persist02
{
int numero;
Valor: 5
Valor 2: 0
Y ahora: 5
Ejercicios propuestos:
(9.2.1) Crea una variante del ejercicio 9.1.1, que use serializacin para guardar y
recuperar los datos.
[Serializable]
public class ClaseAGuardar
{
Ejemplo e;
Y crear una segunda clase, que sea la encargada de guardar y recuperar los datos:
formatter.Serialize(stream, objeto);
stream.Close();
}
De modo que un nico fuente que contuviera estas tres clases podra ser:
// Ejemplo_09_03a.cs
// Ejemplo de persistencia
// Introduccin a C#, por Nacho Cabanes
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
// ---------------------------------------------
// La clase "de prueba"
[Serializable]
public class Ejemplo
{
int numero;
// ---------------------------------------------
// La clase "que realmente se va a guardar"
[Serializable]
public class ClaseAGuardar
{
Ejemplo e;
// ---------------------------------------------
// La clase "encargada de guardar"
// ---------------------------------------------
// Y el programa de prueba
s.Guardar(guardador);
// Ejemplo_09_03b.cs
// Ejemplo de persistencia
// Introduccin a C#, por Nacho Cabanes
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
// ---------------------------------------------
// Las dos clases "de prueba": una contiene a otra
[Serializable]
public class MiniEjemplo
{
int dato;
// -----
[Serializable]
public class Ejemplo
{
int numero;
float numero2;
MiniEjemplo[] mini;
public Ejemplo()
{
mini = new MiniEjemplo[100];
for (int i=0; i<100; i++)
{
Revisin 0.99zz Pgina 280
Introduccin a la programacin con C#, por Nacho Cabanes
// ---------------------------------------------
// La clase "que realmente se va a guardar"
[Serializable]
public class ClaseAGuardar
{
Ejemplo e;
// ---------------------------------------------
// La clase "encargada de guardar"
{
IFormatter formatter = new BinaryFormatter();
Stream stream = new FileStream(nombre,
FileMode.Create, FileAccess.Write, FileShare.None);
formatter.Serialize(stream, objeto);
stream.Close();
}
// ---------------------------------------------
// Y el programa de prueba
Valor: 5
Valor 2: 0
Y ahora: 5
dato 50: 500
Ejercicios propuestos:
(9.3.1) Crea una variante del ejercicio 9.2.1, que use una clase auxiliar para la
serializacin.
// Ejemplo_09_04a.cs
// Ejemplo de persistencia (XML)
// Introduccin a C#, por Nacho Cabanes
using System;
using System.IO;
using System.Runtime.Serialization;
// Para la siguiente lnea, puede ser necesario aadir a las referencias
// del proyecto "System.Runtime.Serialization.Formatters.Soap.dll"
using System.Runtime.Serialization.Formatters.Soap;
// ---------------------------------------------
// Las clases "de prueba"
[Serializable]
public class MiniEjemplo
{
int dato;
[Serializable]
public class Ejemplo
{
int numero;
float numero2;
MiniEjemplo[] mini;
public Ejemplo()
Revisin 0.99zz Pgina 283
Introduccin a la programacin con C#, por Nacho Cabanes
{
mini = new MiniEjemplo[100];
for (int i=0; i<100; i++)
{
mini[i] = new MiniEjemplo();
mini[i].SetDato( i*2 );
}
}
// ---------------------------------------------
// La clase "que realmente se va a guardar"
[Serializable]
public class ClaseAGuardar
{
Ejemplo e;
// ---------------------------------------------
// La clase "encargada de guardar"
nombre = nombreFich;
}
// ---------------------------------------------
// Y el programa de prueba
Dentro de esta opcin, deberemos buscar dentro de las referencias que ya estn
previstas para nuestra versin de .Net (por ejemplo, la 4.5) el nombre
"System.Runtime.Serialization.Formatters.Soap" y despus aceptar los cambios:
<SOAP-ENV:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-
ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-
ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:clr="http://schemas.microsoft.com/soap/encoding/clr/1.0" SOAP-
ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<a1:ClaseAGuardar id="ref-1"
xmlns:a1="http://schemas.microsoft.com/clr/assem/persist05%2C%20Version%3D0.0
.0.0%2C%20Culture%3Dneutral%2C%20PublicKeyToken%3Dnull">
<e href="#ref-3"/>
</a1:ClaseAGuardar>
<a1:Ejemplo id="ref-3"
xmlns:a1="http://schemas.microsoft.com/clr/assem/persist05%2C%20Version%3D0.0
.0.0%2C%20Culture%3Dneutral%2C%20PublicKeyToken%3Dnull">
<numero>5</numero>
<numero2>0</numero2>
<mini href="#ref-4"/>
</a1:Ejemplo>
<SOAP-ENC:Array id="ref-4" SOAP-ENC:arrayType="a1:MiniEjemplo[100]"
xmlns:a1="http://schemas.microsoft.com/clr/assem/persist05%2C%20Version%3D0.0
.0.0%2C%20Culture%3Dneutral%2C%20PublicKeyToken%3Dnull">
<item href="#ref-5"/>
<item href="#ref-6"/>
<item href="#ref-7"/>
...
<item href="#ref-102"/>
<item href="#ref-103"/>
<item href="#ref-104"/>
</SOAP-ENC:Array>
<a1:MiniEjemplo id="ref-5"
xmlns:a1="http://schemas.microsoft.com/clr/assem/persist05%2C%20Version%3D0.0
.0.0%2C%20Culture%3Dneutral%2C%20PublicKeyToken%3Dnull">
<dato>0</dato>
</a1:MiniEjemplo>
<a1:MiniEjemplo id="ref-6"
xmlns:a1="http://schemas.microsoft.com/clr/assem/persist05%2C%20Version%3D0.0
.0.0%2C%20Culture%3Dneutral%2C%20PublicKeyToken%3Dnull">
<dato>2</dato>
</a1:MiniEjemplo>
...
<a1:MiniEjemplo id="ref-104"
xmlns:a1="http://schemas.microsoft.com/clr/assem/persist05%2C%20Version%3D0.0
.0.0%2C%20Culture%3Dneutral%2C%20PublicKeyToken%3Dnull">
<dato>198</dato>
</a1:MiniEjemplo>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Ejercicios propuestos:
(9.4.1) Crea una variante del ejercicio 9.3.1, que guarde los datos en formato XML.
(9.4.2) Crea una versin ampliada de la "base de datos de ficheros" (ejemplo
04_06a) para que use objetos en vez de struct, y que guarde los datos (en formato
binario) usando persistencia.
(9.4.3) Aade al ejercicio de los trabajadores (6.8.1) la posibilidad de guardar sus
datos en formato XML.
primera base de datos, no pretendemos que sea perfecta, sino sencilla, as que
apenas guardaremos tres datos de cada amigo: el nombre, la direccin y la edad.
Para crear la base de datos que contiene todo usaremos "create database",
seguido del nombre que tendr la base de datos:
En nuestro caso, nuestra base de datos almacenar una nica tabla, que
contendr los datos de nuestros amigos. Por tanto, el siguiente paso ser decidir
qu datos concretos ("campos") guardaremos de cada amigo. Deberemos pensar
tambin qu tamao necesitaremos para cada uno de esos datos, porque al
gestor de bases de datos habr que drselo bastante cuadriculado. Por ejemplo,
podramos decidir lo siguiente:
Cada gestor de bases de datos tendr una forma de llamar a esos tipos de datos.
Por ejemplo, es habitual tener un tipo de datos llamado "VARCHAR" para
referirnos a texto hasta una cierta longitud, y varios tipos de datos numricos de
distinto tamao. Si usamos "INT" para indicar que nos basta con un entero no muy
grande, la orden necesaria para crear esta tabla sera:
Este formato nos obliga a indicar valores para todos los campos, y exactamente en
el orden en que se disearon. Si no queremos introducir todos los datos, o
queremos hacerlo en otro orden, o no recordamos con seguridad el orden, hay
otra opcin: detallar tambin en la orden "insert" los nombres de cada uno de los
campos, as:
que, con nuestros datos, dara como resultado (en el intrprete de comandos de
algunos gestores de bases de datos, como MySQL):
+--------+-----------+------+
| nombre | direccion | edad |
+--------+-----------+------+
| juan | su casa | 25 |
| pedro | su calle | 23 |
+--------+-----------+------+
Si queremos ver slo ciertos campos, detallamos sus nombres, separados por
comas:
y obtendramos
+--------+-----------+
| nombre | direccion |
+--------+-----------+
| juan | su casa |
| pedro | su calle |
+--------+-----------+
Normalmente no querremos ver todos los datos que hemos introducido, sino slo
aquellos que cumplan cierta condicin. Esta condicin se indica aadiendo un
apartado WHERE a la orden "select", as:
+--------+-----------+
| nombre | direccion |
+--------+-----------+
| juan | su casa |
+--------+-----------+
A veces no querremos comparar con un texto exacto, sino slo con parte del
contenido del campo (por ejemplo, porque slo sepamos un apellido o parte del
nombre de la calle). En ese caso, no compararamos con el smbolo "igual" (=), sino
que usaramos la palabra "like", y para las partes que no conozcamos usaremos el
comodn "%", como en este ejemplo:
que nos dira el nombre y la direccin de nuestros amigos llamados que viven en
calles que contengan la palabra "calle", precedida por cualquier texto (%) y con
cualquier texto (%) a continuacin:
+--------+-----------+
| nombre | direccion |
+--------+-----------+
| pedro | su calle |
+--------+-----------+
En nuestro caso, para acceder a SQLite desde C#, tenemos disponible alguna
adaptacin de la biblioteca original. Una de ellas es System.Data.SQLite, que se
puede descargar de http://system.data.sqlite.org/ y tienes un pequeo proyecto de
ejemplo listo para usar desde Visual Studio en para
http://nachocabanes.com/csharp
Con esta biblioteca, los pasos a seguir para crear una base de datos y guardar
informacin en una base de datos de SQLite seran:
// EjemploSQLite1.cs
// Ejemplo de acceso a bases de datos con SQLite (1)
// Introduccin a C#, por Nacho Cabanes
using System;
// Es necesario aadir la siguiente DLL a la "referencias" del proyecto
using System.Data.SQLite;
// Creamos la tabla
string creacion = "create table personas ("
+" nombre varchar(20),direccion varchar(40),edad int);";
SQLiteCommand cmd = new SQLiteCommand(creacion, conexion);
cmd.ExecuteNonQuery();
Console.WriteLine("Creada.");
}
}
mono ejemploSQLite.exe
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\csc ejemploSQLite.cs
/r:System.Data.SqLite.dll
Este fichero DLL es de 32 bits, de modo que si usamos una versin de Windows 64
bits puede no funcionar correctamente. La solucin, si compilamos desde lnea de
comandos, es indicar que es para "plataforma x86", aadiendo la opcin
"/platform:x86", as:
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\csc ejemploSQLite.cs
/r:System.Data.SqLite.dll /platform:x86
Si usamos Visual Studio o SharpDevelop, tendramos que aadir ese fichero DLL a
las "referencias" de nuestro proyecto. Lo podemos hacer desde la ventana del
"Explorador de soluciones", pulsando el botn derecho sobre "References",
"Agregar referencia" y escogiendo el fichero System.Data.SQLite.DLL desde la
pestaa "Examinar":
Para mostrar los datos, el primer y ltimo paso seran casi iguales, pero no los
intermedios:
// EjemploSQLite2.cs
// Ejemplo de acceso a bases de datos con SQLite (2)
// Introduccin a C#, por Nacho Cabanes
using System;
Revisin 0.99zz Pgina 295
Introduccin a la programacin con C#, por Nacho Cabanes
Que mostrara:
Nota: en este segundo ejemplo, mostrbamos dato[0] (el primer dato, el nombre)
y dato [2] (el tercer dato, la edad) pero no el dato intermedio, dato[1] (la direccin).
Ejercicios propuestos:
(10.3.1) Crea una versin de la base de datos de ficheros (ejemplo 04_06a) que
realmente guarde en una base de datos (de SQLite) los datos que maneja. Deber
guardar todos antes de terminar cada sesin de uso, y volverlos a cargar al
comienzo de la siguiente.
Por una parte, podemos tener bloques de informacin claramente distintos. Por
ejemplo, en una base de datos que guarde la informacin de una empresa
tendremos datos como los artculos que distribuimos y los clientes que nos los
compran, y estos dos bloques de informacin no deberan guardarse en una
misma tabla.
Por otra parte, habr ocasiones en que veamos que los datos, a pesar de que se
podran clasificar dentro de un mismo "bloque de informacin" (tabla), seran
redundantes: existira gran cantidad de datos repetitivos, y esto puede dar lugar a
dos problemas:
Espacio desperdiciado.
Posibilidad de errores al introducir los datos, lo que dara lugar a
inconsistencias:
Veamos un ejemplo:
+---------+-----------+-----------+
| nombre | direccion | ciudad |
+---------+-----------+-----------+
| juan | su casa | alicante |
| alberto | calle uno | alicante |
| pedro | su calle | alicantw |
+---------+-----------+-----------+
Por otra parte, hemos tecleado mal uno de los datos: en la tercera ficha no hemos
indicado "alicante", sino "alicantw", de modo que si hacemos consultas sobre
personas de Alicante, la ltima de ellas no aparecera. Al teclear menos, es
tambin ms difcil cometer este tipo de errores.
La forma de crear la tabla con esos dos campos y con esa clave primaria sera:
Mientras que la tabla de personas sera casi igual al ejemplo anterior, pero
aadiendo un nuevo dato: el cdigo de la ciudad
direccion varchar(40),
edad decimal(3),
codciudad varchar(3)
);
Para introducir datos, el hecho de que exista una clave primaria no supone ningn
cambio, salvo por el hecho de que no se nos permitira introducir dos ciudades
con el mismo cdigo:
Cuando queremos mostrar datos de varias tablas a la vez, deberemos hacer unos
pequeos cambios en las rdenes "select" que hemos visto:
Por eso, una consulta bsica sera algo parecido (slo parecido) a:
Pero esto todava tiene problemas: estamos combinando TODOS los datos de la
tabla de personas con TODOS los datos de la tabla de ciudades, de modo que
obtenemos 3x3 = 9 resultados:
+---------+-----------+-----------+
| nombre | direccion | nombre |
+---------+-----------+-----------+
| juan | su casa | alicante |
| pedro | su calle | alicante |
| alberto | calle uno | alicante |
| juan | su casa | barcelona |
| pedro | su calle | barcelona |
| alberto | calle uno | barcelona |
| juan | su casa | madrid |
| pedro | su calle | madrid |
Revisin 0.99zz Pgina 299
Introduccin a la programacin con C#, por Nacho Cabanes
Pero esos datos no son reales: si "juan" vive en la ciudad de cdigo "a", slo
debera mostrarse junto al nombre "alicante". Nos falta indicar esa condicin: "el
cdigo de ciudad que aparece en la persona debe ser el mismo que el cdigo que
aparece en la ciudad", as:
+---------+-----------+-----------+
| nombre | direccion | nombre |
+---------+-----------+-----------+
| juan | su casa | alicante |
| alberto | calle uno | barcelona |
| pedro | su calle | madrid |
+---------+-----------+-----------+
Ese s es el resultado correcto. Cualquier otra consulta que implique las dos tablas
deber terminar comprobando que los dos cdigos coinciden. Por ejemplo, para
ver qu personas viven en la ciudad llamada "madrid", haramos:
+--------+-----------+------+
| nombre | direccion | edad |
+--------+-----------+------+
| pedro | su calle | 23 |
+--------+-----------+------+
Y para saber las personas de ciudades que comiencen con la letra "b", usaramos
"like":
+---------+-----------+-----------+
| nombre | direccion | nombre |
+---------+-----------+-----------+
| alberto | calle uno | barcelona |
+---------+-----------+-----------+
Revisin 0.99zz Pgina 300
Introduccin a la programacin con C#, por Nacho Cabanes
Si en nuestra tabla puede haber algn dato que se repita, como la direccin,
podemos pedir un listado sin duplicados, usando la palabra "distinct":
// EjemploSQLite3.cs
// Ejemplo de acceso a bases de datos con SQLite (3)
// Introduccin a C#, por Nacho Cabanes
using System;
// Es necesario aadir la siguiente DLL a la "referencias" del proyecto
using System.Data.SQLite;
// E insertamos datos
Console.WriteLine(" Introduciendo ciudades");
string insercion = "insert into ciudades values "
+"('a', 'alicante');";
cmd = new SQLiteCommand(insercion, conexion);
int cantidad = cmd.ExecuteNonQuery();
if (cantidad < 1)
Console.WriteLine("No se ha podido insertar");
Revisin 0.99zz Pgina 301
Introduccin a la programacin con C#, por Nacho Cabanes
Console.WriteLine("Datos:");
// Leemos los datos de forma repetitiva
while (datos.Read())
{
// Y los mostramos
Console.WriteLine(" {0} - {1} - {2}",
Convert.ToString(datos[0]), Convert.ToString(datos[1]),
Convert.ToString(datos[2]));
}
Su resultado sera:
Ejercicios propuestos:
(10.4.1) Mejora el ejercicio 10.3.1 para que, adems de el nombre del fichero y su
tamao, guarde una categora (por ejemplo, "utilidad" o "vdeo"). Estas categoras
estarn almacenadas en una segunda tabla.
Esto borrara todas las personas llamadas "juan" que estn almacenadas en la
tabla "personas".
Para modificar datos de una tabla, el formato habitual es "update tabla set
campo=nuevoValor where condicion".
(al igual que habamos visto para "select" y para "delete", si no indicamos la parte
del "where", los cambios se aplicarn a todos los registros de la tabla).
Ejercicios propuestos:
(10.5.1) Crea una versin del ejercicio 10.5.1 que no guarde todos los datos al salir,
sino que actualice con cada nueva modificacin: inserte los nuevos datos inme-
diatamente, permita borrar un registro (reflejando los cambios inmediatamente) y
modificar los datos de un registro (dem).
+--------+---------+---------+
| 5/2 | 5 div 2 | 5 mod 2 |
+--------+---------+---------+
| 2.5000 | 2 | 1 |
+--------+---------+---------+
10.7 Grupos
Puede ocurrir que no nos interese un nico valor agrupado para todos los datos
(el total, la media, la cantidad de datos), sino el resultado para un grupo de datos.
Por ejemplo: saber no slo la cantidad de clientes que hay registrados en nuestra
base de datos, sino tambin la cantidad de clientes que viven en cada ciudad.
+----------+------+
| count(*) | edad |
+----------+------+
| 1 | 22 |
| 1 | 23 |
| 1 | 25 |
+----------+------+
Pero podemos llegar ms all: podemos no trabajar con todos los grupos posibles,
sino slo con los que cumplen alguna condicin.
La condicin que se aplica a los grupos no se indica con "where", sino con "having"
(que se podra traducir como "los que tengan..."). Un ejemplo:
select count(*), edad from personas group by edad having edad > 24;
que mostrara
+----------+------+
| count(*) | edad |
+----------+------+
| 1 | 25 |
+----------+------+
En el lenguaje SQL existe mucho ms que lo que hemos visto aqu, pero para
nuestro uso desde C# y SQLite debera ser suficiente.
Ejercicios propuestos:
(10.7.1) Crea una versin del ejercicio 10.4.1 que permita saber cuntos ficheros
hay pertenecientes a cada categora.
// AgendaSQLite.cs
// Ejemplo de acceso a bases de datos con SQLite: agenda
// Introduccin a C#, por Nacho Cabanes
using System;
using System.IO; // Para File.Exists
// Constructor
public AgendaSQLite()
{
AbrirBD();
}
// Y creamos la tabla
string creacion = "CREATE TABLE persona "
+ "(nombre VARCHAR(30), direccion VARCHAR(40), "
+ " edad INT );";
SQLiteCommand cmd = new SQLiteCommand(creacion, conexion);
cmd.ExecuteNonQuery();
}
else
{
// Si ya existe, abrimos
conexion = new SQLiteConnection(
"Data
Source=agenda.sqlite;Version=3;New=False;Compress=True;");
conexion.Open();
}
}
try
{
insercion = "INSERT INTO persona " +
"VALUES ('"+nombre+"','"+direccion+"',"+edad+");";
cmd = new SQLiteCommand(insercion, conexion);
cantidad = cmd.ExecuteNonQuery();
if (cantidad < 1)
return false; // Si no se ha podido insertar
}
Revisin 0.99zz Pgina 307
Introduccin a la programacin con C#, por Nacho Cabanes
catch (Exception e)
{
return false; // Si no se ha podido insertar (codigo repetido?)
}
return true; // Si todo ha ido bien, devolvemos true
}
~AgendaSQLite()
{
conexion.Close();
}
// ------------------------------------------------------------------
do
{
Console.WriteLine("Escoja una opcin...");
Console.WriteLine("1.- Aadir");
Console.WriteLine("2.- Ver todos");
Console.WriteLine("3.- Buscar");
Console.WriteLine("0.- Salir");
opcion = Console.ReadLine();
switch (opcion)
{
case "1":
Console.Write("Nombre? ");
string n = Console.ReadLine();
Console.Write("Direccin? ");
string d = Console.ReadLine();
Console.Write("Edad? ");
int e = Convert.ToInt32(Console.ReadLine());
agenda.InsertarDatos(n, d, e);
break;
case "2":
Console.WriteLine(agenda.LeerTodosDatos());
break;
case "3":
Console.Write("Texto a buscar? ");
string txt = Console.ReadLine();
Console.WriteLine(agenda.LeerBusqueda(txt));
break;
}
} while (opcion != "0");
Ejercicios propuestos:
(10.8.1) Ampla este ejemplo (AgendaSQLite), para que se pueda borrar un dato a
partir de su nombre (que debe coincidir exactamente).
(10.8.2) Ampla el ejemplo 10.8.1, para que se pueda modificar un registro.
(10.8.3) Ampla el ejemplo 10.8.2, para que permita exportar los datos a un fichero
de texto (que contenga el nombre, la direccin y la edad correspondientes a cada
registro en lneas separados), o bien se pueda importar datos desde un fichero de
texto (aadiendo al final de los existentes).
(10.8.4) Ampla el ejemplo 10.8.2 con una tabla de ciudades, que se deber pedir (y
mostrar) de forma independiente al resto de la direccin.
Por ejemplo, existe un "DataGridView", que nos permite recorrer los datos en una
vista de tabla, y poder hacer modificaciones sobre ellos:
Este tipo de variables son sencillas de usar y rpidas... si slo vamos a manejar
estructuras de datos que no cambien, pero resultan poco eficientes si tenemos
estructuras cuyo tamao no sea siempre el mismo.
La solucin real suele ser crear estructuras dinmicas, que puedan ir creciendo o
disminuyendo segn nos interese. En los lenguajes de programacin "clsicos",
como C y Pascal, este tipo de estructuras se tienen que crear de forma
bsicamente artesanal, mientras que en lenguajes modernos como C#, Java o las
ltimas versiones de C++, existen esqueletos ya creados que podemos utilizar con
facilidad.
Las pilas. Una "pila de datos" se comportar de forma similar a una pila de
libros: podemos apilar cosas en la cima, o extraer de la cima. Se supone
que no se puede tomar elementos de otro sitio que no sea la cima, ni
dejarlos en otro sitio distinto. De igual modo, se supone que la pila no tiene
un tamao mximo definido, sino que puede crecer arbitrariamente.
Las colas. Una "cola de datos" se comportar como las del cine (en teora):
la gente llega por un sitio (la cola) y sale por el opuesto (la cabeza). Al igual
Este tipo de estructuras se suele denotar tambin usando las siglas "LIFO" (Last In
First Out: lo ltimo en entrar es lo primero en salir).
Para utilizar la clase "Stack" y la mayora de las que veremos en este tema,
necesitamos incluir en nuestro programa una referencia a "System.Collections".
As, un ejemplo bsico que creara una pila, introdujera tres palabras y luego las
volviera a mostrar sera:
// Ejemplo_11_02a.cs
// Ejemplo de clase "Stack" (Pila)
// Introduccin a C#, por Nacho Cabanes
using System;
Revisin 0.99zz Pgina 312
Introduccin a la programacin con C#, por Nacho Cabanes
using System.Collections;
yo
soy
Hola,
Como se puede ver en este ejemplo, no hemos indicado que sea una "pila de
strings", sino simplemente "una pila". Por eso, los datos que extraemos son
"objetos", que deberemos convertir al tipo de datos que nos interese utilizando un
"typecast" (conversin forzada de tipos), como en palabra = (string)
miPila.Pop();
"Peek", que mira el valor que hay en la cima, pero sin extraerlo.
"Clear", que borra todo el contenido de la pila.
"Contains", que indica si un cierto elemento est en la pila.
"GetType", para saber de qu tipo son los elementos almacenados en la
pila.
"ToString", que devuelve el elemento actual convertido a un string.
"ToArray", que devuelve toda la pila convertida a un array.
"GetEnumerator", que permite usar "enumeradores" para recorrer la pila,
una funcionalidad que veremos con algn detalle ms adelante.
Tambin tenemos una propiedad "Count", que nos indica cuntos
elementos contiene.
Ejercicios propuestos:
(11.2.1) Crea un programa que pida al usuario 5 nmeros enteros y luego los
muestre en orden contrario, utilizando una pila.
(11.2.2) Crea un programa que pida al usuario el nombre de un fichero de texto y
muestre en orden inverso las lneas que lo forma, empleando una pila.
(11.2.3) Crea una clase que imite el comportamiento de una pila, pero usando
internamente un array (si no lo consigues, no te preocupes; en un apartado
posterior veremos una forma de hacerlo).
(11.2.4) La "notacin polaca inversa" es una forma de expresar operaciones que
consiste en indicar los operandos antes del correspondiente operador. Por
ejemplo, en vez de "3+4" se escribira "3 4 +". Es una notacin que no necesita
parntesis y que se puede resolver usando una pila: si se recibe un dato numrico,
ste se guarda en la pila; si se recibe un operador, se obtienen los dos operandos
que hay en la cima de la pila, se realiza la operacin y se apila su resultado. El
proceso termina cuando slo hay un dato en la pila. Por ejemplo, "3 4 +" se
convierte en: apilar 3, apilar 4, sacar dos datos y sumarlos, guardar 7, terminado.
Implemntalo y comprueba si el resultado de "3 4 6 5 - + * 6 +" es 21.
// Ejemplo_11_03a.cs
// Ejemplo de clase "Queue" (Cola)
// Introduccin a C#, por Nacho Cabanes
using System;
using System.Collections;
que mostrara:
Hola,
soy
yo
Al igual que ocurra con la pila, la implementacin de una cola que incluye C# es
ms avanzada que eso, con mtodos similares a los de antes:
"Peek", que mira el valor que hay en la cabeza de la cola, pero sin extraerlo.
"Clear", que borra todo el contenido de la cola.
"Contains", que indica si un cierto elemento est en la cola.
"GetType", para saber de qu tipo son los elementos almacenados en la
cola.
"ToString", que devuelve el elemento actual convertido a un string.
"ToArray", que devuelve toda la pila convertida a un array.
"GetEnumerator", que permite usar "enumeradores" para recorrer la cola,
una funcionalidad que veremos con algn detalle ms adelante.
Al igual que en la pila, tambin tenemos una propiedad "Count", que nos
indica cuntos elementos contiene.
Ejercicios propuestos:
(11.3.1) Crea un programa que pida al usuario 5 nmeros reales de doble preisin,
los guarde en una cola y luego los muestre en pantalla.
(11.3.2) Crea un programa que pida frases al usuario, hasta que introduzca una
frase vaca. En ese momento, mostrar todas las frases que se haban introducido.
(11.3.3) Crea un programa que lea el contenido de un fichero de texto, lo
almacene lnea por lnea en una cola, luego muestre este contenido en pantalla y
finalmente lo vuelque a otro fichero de texto.
En el caso de C#, no tenemos ninguna clase "List" que represente una lista
genrica, pero s dos variantes especialmente tiles: una lista a cuyos elementos se
puede acceder como a los de un array ("ArrayList") y una lista ordenada
("SortedList").
11.4.1. ArrayList
En un ArrayList, podemos aadir datos en la ltima posicin con "Add", insertar en
cualquier otra con "Insert", recuperar cualquier elemento usando corchetes, o
incluso ordenar toda la lista con "Sort". Vamos a ver un ejemplo de la mayora de
sus posibilidades:
// Ejemplo_11_04_01a.cs
// Ejemplo de ArrayList
// Introduccin a C#, por Nacho Cabanes
using System;
using System.Collections;
// Buscamos un elemento
Console.WriteLine( "La palabra \"yo\" est en la posicin {0}",
miLista.IndexOf("yo") );
// Ordenamos
miLista.Sort();
// Invertimos la lista
miLista.Reverse();
Contenido actual:
Hola,
soy
yo
La segunda palabra es: soy
Contenido tras insertar:
Hola,
Como estas?
soy
yo
La palabra "yo" est en la posicin 3
Revisin 0.99zz Pgina 317
Introduccin a la programacin con C#, por Nacho Cabanes
Casi todo debera resultar fcil de entender, salvo quiz el smbolo ~. Esto se debe
a que BinarySearch devuelve un nmero negativo cuando el texto que buscamos
no aparece, pero ese nmero negativo tiene un significado: es el "valor
complementario" de la posicin del dato inmediatamente mayor (es decir, el dato
cambiando los bits 0 por 1 y viceversa). En el ejemplo anterior, "posicin" vale -2, lo
que quiere decir que el dato no existe, y que el dato inmediatamente mayor est
en la posicin 1 (que es el "complemento a 2" del nmero -2, que es lo que indica
la expresin "~posicin"). En el apndice 3 de este texto hablaremos de cmo se
representan internamente los nmeros enteros, tanto positivos como negativos, y
entonces se ver con detalle en qu consiste el "complemento a 2".
Veremos los operadores a nivel de bits, como ~, en el tema 13, que estar
dedicado a otras caractersticas avanzadas de C#.
Ejercicios propuestos:
(11.4.1.1) Crea un programa que lea el contenido de un fichero de texto, lo
almacene lnea por lnea en un ArrayList, y luego pregunte de forma repetitiva al
usuario qu lnea desea ver. Terminar cuando el usuario introduzca "-1".
(11.4.1.2) Crea un programa que lea el contenido de un fichero de texto, lo
almacene lnea por lnea en un ArrayList, y luego pregunte de forma repetitiva al
usuario qu texto desea buscar y muestre las lneas que contienen ese texto.
Terminar cuando el usuario introduzca una cadena vaca.
(11.4.1.3) Crea un programa que lea el contenido de un fichero de texto, lo
almacene lnea por lnea en un ArrayList, lo ordene y lo muestre ordenado en
pantalla.
(11.4.1.4) Crea un programa que lea el contenido de un fichero de texto, lo
almacene lnea por lnea en un ArrayList, luego muestre en pantalla las lneas
Revisin 0.99zz Pgina 318
Introduccin a la programacin con C#, por Nacho Cabanes
impares (primera, tercera, etc.) y finalmente vuelque a otro fichero de texto las
lneas pares (segunda, cuarta, etc.).
(11.4.1.5) Crea una nueva versin de la "bases de datos de ficheros" (ejemplo
04_06a), pero usando ArrayList en vez de un array convencional.
11.4.2. SortedList
En un SortedList, los elementos estn formados por una pareja: una clave y un
valor (como en un diccionario: la palabra y su definicin). Se puede aadir
elementos con "Add", o acceder a los elementos mediante su ndice numrico (con
"GetKey") o mediante su clave (con corchetes), como en este ejemplo:
// Ejemplo_11_04_02a.cs
// Ejemplo de SortedList: Diccionario esp-ing
// Introduccin a C#, por Nacho Cabanes
using System;
using System.Collections;
Su resultado sera
Ejercicios propuestos:
(11.4.2.1) Crea un programa que, cuando el usuario introduzca el nombre de un
nmero del 1 al 10 en ingls (por ejemplo, "two"), diga su traduccin en espaol
(por ejemplo, "dos").
(11.4.2.2) Crea un traductor bsico de C# a Pascal, que tenga las traducciones
almacenadas en una SortedList (por ejemplo, "{" se convertir a "begin", "}" se
convertir a "begin", "WriteLine" se convertir a "WriteLn", "ReadLine" se convertir
a "ReadLn", "void" se convertir a "procedure" y "Console." se convertir a una
cadena vaca. salo para convertir un abrir un fichero que contenga un fuente en
C# y mostrar su equivalente (que no ser perfecto) en Pascal.
// Ejemplo_11_05a.cs
// Ejemplo de Tabla Hash: Diccionario de informtica
// Introduccin a C#, por Nacho Cabanes
using System;
using System.Collections;
// Ejemplo_11_05b.cs
// Ejemplo de Tabla Hash: Diccionario de informtica
// Introduccin a C#, por Nacho Cabanes
using System;
using System.Collections;
Una tabla hash tiene una cierta capacidad inicial, que se ampla automticamente
cuando es necesario. Como la tabla hash es mucho ms rpida cuando est
bastante vaca que cuando est casi llena, podemos usar un constructor
alternativo, en el que se le indica la capacidad inicial que queremos, si tenemos
una idea aproximada de cuntos datos vamos a guardar:
Ejercicios propuestos:
(11.5.1) Crea una versin alternativa del ejercicio 11.4.2.1, pero que tenga las
traducciones almacenadas en una tabla Hash.
(11.5.2) Crea una versin alternativa del ejercicio 11.4.2.2, pero que tenga las
traducciones almacenadas en una tabla Hash.
// Ejemplo_11_06a.cs
// Ejemplo de Enumeradores en una Tabla Hash
// Introduccin a C#, por Nacho Cabanes
using System;
using System.Collections;
cuyo resultado es
Contenido:
pc = personal computer
byte = 8 bits
kilobyte = 1024 bytes
Para las colecciones "normales", como las pilas y las colas, el tipo de Enumerador a
usar ser un IEnumerator, con un campo Current para saber el valor actual:
// Ejemplo_11_06b.cs
// Ejemplo de Enumeradores en una pila (Stack)
// Introduccin a C#, por Nacho Cabanes
using System;
using System.Collections;
while ( miEnumerador.MoveNext() )
Console.WriteLine("{0}", miEnumerador.Current);
}
}
que escribira
Contenido:
yo
soy
Hola,
http://msdn.microsoft.com/es-es/library/system.collections(en-us,VS.71).aspx#
// Ejemplo_11_07a.cs
// Ejemplo de clase "Pila" basada en un array
// Introduccin a C#, por Nacho Cabanes
using System;
// Constructor
public PilaString()
{
posicionPila = 0;
datosPila = new string[MAXPILA];
}
} // Fin de la clase
Ejercicios propuestos:
(11.7.1) Usando esta misma estructura de programa, crea una clase "Cola", que
permita introducir datos (nmeros enteros) y obtenerlos en modo FIFO (el primer
dato que se introduzca debe ser el primero que se obtenga). Debe tener un
mtodo "Encolar" y otro "Desencolar".
(11.7.2) Crear una clase "ListaOrdenada", que almacene un nico dato (no un par
clave-valor como los SortedList). Debe contener un mtodo "Insertar", que aadir
un nuevo dato en orden en el array, y un "Extraer(n)", que obtenga un elemento de
la lista (el nmero "n"). Deber almacenar "strings".
(11.7.3) Crea una pila de "doubles", usando internamente un ArrayList en vez de
un array.
(11.7.4) Crea una cola que almacene un bloque de datos (struct, con los campos
que t elijas) usando un ArrayList.
(11.7.5) Crea una lista ordenada (de "strings") usando un ArrayList.
En ocasiones puede ser interesante algo un poco ms rgido, que con las ventajas
de un ArrayList (crecimiento dinmico, mltiples mtodos disponibles) est
adaptado a un tipo de datos, y no necesite una conversin de tipos cada vez que
extraigamos un dato.
using System.Collections.Generic;
Con slo estos dos cambios, el ejemplo de uso de ArrayList que vimos en el
apartado 11.4.1 funcionara perfectamente:
Revisin 0.99zz Pgina 326
Introduccin a la programacin con C#, por Nacho Cabanes
// Ejemplo_11_08a.cs
// Ejemplo de List<string>
// Introduccin a C#, por Nacho Cabanes
using System;
using System.Collections.Generic;
// Buscamos un elemento
Console.WriteLine( "La palabra \"yo\" est en la posicin {0}",
miLista.IndexOf("yo") );
// Ordenamos
miLista.Sort();
// Invertimos la lista
miLista.Reverse();
Console.WriteLine( frase );
No slo tenemos listas. Por ejemplo, tambin existe un tipo "Dictionary", que
equivale a una tabla Hash, pero en la que las claves y los valores no tienen por qu
ser strings, sino el tipo de datos que nosotros decidamos. Por ejemplo, podemos
usar una cadena como clave, pero un nmero entero como valor obtenido:
As, con un diccionario que tenga tanto claves string como valores string,
podramos crear una versin alternativa del ejemplo 11_05b. Los nicos cambios
seran una declaracin parecida a la anterior, el "using" correcto, y cambiar
Contains por ContainsKey:
// Ejemplo_11_08b.cs
// Ejemplo de Dictionary
// Introduccin a C#, por Nacho Cabanes
using System;
using System.Collections.Generic;
if (miDiccio.ContainsKey("pc"))
Console.WriteLine( "El significado de PC es: {0}",
miDiccio["pc"]);
else
Console.WriteLine( "No existe la palabra PC");
}
}
Ejercicios propuestos:
(11.8.1) Crea una nueva versin de la "bases de datos de ficheros" (ejemplo
04_06a), pero usando List en vez de un array convencional.
(11.8.2) Crea un programa que lea todo el contenido de un fichero, lo guarde en
una lista de strings y luego lo muestre en orden inverso (de la ltima lnea a la
primera).
int *posicion;
posicion = №
mcs unsafe1.cs
unsafe1.cs(15,31): error CS0227: Unsafe code requires the `unsafe' command
line
option to be specified
Compilation failed: 1 error(s), 0 warnings
Por tanto, deberemos compilar con la opcin /unsafe como forma de decir al
compilador "s, s que este programa tiene zonas no seguras, pero aun as quiero
compilarlo":
// Ejemplo_11_09_03a.cs
// Primer ejemplo de punteros
// Introduccin a C#, por Nacho Cabanes
using System;
// Damos un valor a x
x = 2;
// punteroAEntero ser la direccin de memoria de x
punteroAEntero = &x;
// Los dos estn en la misma direccin:
Console.WriteLine("x vale {0}", x);
Console.WriteLine("En punteroAEntero hay un {0}", *punteroAEntero);
x vale 2
En punteroAEntero hay un 2
x vale 5
En punteroAEntero hay un 5
Ejercicios propuestos:
(11.9.3.1) Crea un programa que intercambie la posicin de memoria en la que se
encuentran dos variables y que luego muestre sus contenidos en pantalla, para
comprobar que no son los mismos que al principio.
// Ejemplo_11_09_04a.cs
// Segundo ejemplo de punteros
// Introduccin a C#, por Nacho Cabanes
using System;
Incrementar(&i);
}
// Y mostramos el resultado
Console.WriteLine (i);
}
}
// Ejemplo_11_09_05a.cs
// Tercer ejemplo de punteros: stackalloc
// Introduccin a C#, por Nacho Cabanes
using System;
// Rellenamos el array
for (int i = 0; i < tamanyoArray; i++)
{
datos[i] = i*10;
}
// Mostramos el array
for (int i = 0; i < tamanyoArray; i++)
{
Console.WriteLine(datos[i]);
}
}
}
Ejercicios propuestos:
(11.9.5.1) Crea una programa que pida al usuario 5 strings, los almacene usando
"stackalloc" y luego los muestre en orden inverso.
// Ejemplo_11_09_06a.cs
// Cuarto ejemplo de punteros: aritmtica de punteros
// Introduccin a C#, por Nacho Cabanes
using System;
Ejercicios propuestos:
(11.9.6.1) Crea una programa que pida al usuario 4 nmeros reales, los almacene
usando "stackalloc" y luego los recorra incrementando el puntero a partir de la
posicin del primer elemento.
// Ejemplo_11_09_07a.cs
// Quinto ejemplo de punteros: fixed
// Introduccin a C#, por Nacho Cabanes
using System;
*posicionDato);
}
Dentro de ese tipo de datos DateTime, tenemos las herramientas para saber el da
(Day), el mes (Month) o el ao (Year) de una fecha, entre otros. Tambin podemos
calcular otras fechas sumando a la actual una cierta cantidad de segundos
(AddSeconds), das (AddDays), etc. Un ejemplo bsico de su uso sera:
// Ejemplo_12_01a.cs
// Ejemplo bsico de manejo de fechas
// Introduccin a C#, por Nacho Cabanes
using System;
Algunos de las propiedades ms tiles de este tipo de datos son: Now (fecha y
hora actual de este equipo), Today (fecha actual); Day (da del mes), Month
(nmero de mes), Year (ao); Hour (hora), Minute (minutos), Second (segundos),
Millisecond (milisegundos); DayOfWeek (da de la semana: su nombre en ingls,
que se puede convertir en un nmero del 0-domingo- al 6-sbado- si se fuerza su
tipo a entero, anteponindole "(int)"); DayOfYear (da del ao).
Para calcular nuevas fechas, podemos usar mtodos que generan un nuevo
DateTime, como: AddDays (que aparece en el ejemplo anterior), AddHours,
AddMilliseconds, AddMinutes, AddMonths, AddSeconds, AddHours, o bien un Add
Revisin 0.99zz Pgina 337
Introduccin a la programacin con C#, por Nacho Cabanes
ms genrico (para sumar una fecha a otra) y un Subtract tambin genrico (para
restar una fecha de otra).
// Ejemplo_12_01b.cs
// Diferencia entre dos fechas
// Introduccin a C#, por Nacho Cabanes
using System;
TimeSpan diferencia =
fechaActual.Subtract(fechaNacimiento) ;
// Ejemplo_12_01c.cs
// Pausas
// Introduccin a C#, por Nacho Cabanes
using System;
using System.Threading;
Ejercicios propuestos:
(12.1.1) Crea una versin mejorada del ejemplo 12_01a, que muestre el nombre
del mes (usa un array para almacenar los nombres y accede a la posicin
correspondiente), la hora, los minutos, los segundos y las dcimas de segundo (no
las milsimas). Si los minutos o los segundos son inferiores a 10, debern
mostrarse con un cero inicial, de modo que siempre aparezcan con dos cifras.
(12.1.2) Crea un reloj que se muestre en pantalla, y que se actualice cada segundo
(usando "Sleep"). En esta primera aproximacin, el reloj se escribir con
"WriteLine", de modo que aparecer en la primera lnea de pantalla, luego en la
segunda, luego en la tercera y as sucesivamente (en el prximo apartado veremos
cmo hacer que se mantenga fijo en unas ciertas coordenadas de la pantalla).
(12.1.3) Crea un programa que pida al usuario su fecha de nacimiento, y diga de
qu da de la semana se trataba.
(12.1.4) Crea un programa que muestre el calendario del mes actual (pista:
primero debers calcular qu da de la semana es el da 1 de este mes). Deber ser
algo como:
Mayo 2015
lu ma mi ju vi s do
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28
// Ejemplo_12_02a.cs
// Ms posibilidades de "System.Console"
// Introduccin a C#, por Nacho Cabanes
using System;
{
int posX, posY;
Console.ForegroundColor = ConsoleColor.Blue;
Console.SetCursorPosition(10, 15);
Console.Write("Pulsa 1 o 2: ");
ConsoleKeyInfo tecla;
do
{
tecla = Console.ReadKey(false);
}
while ((tecla.KeyChar != '1') && (tecla.KeyChar != '2'));
(Nota: no todas las posibilidades que se aplican en este ejemplo estn disponibles
en la plataforma .Net 1.x, sino a partir de la versin 2).
KeyChar, que representa el carcter que se escribira al pulsar esa tecla. Por
ejemplo, podramos hacer if (tecla.KeyChar == '1') ...
Key, que se refiere a la tecla (porque hay teclas que no tienen un carcter
visualizable, como F1 o las teclas de cursor). Por ejemplo, para comprobar
la tecla ESC podramos hacer if (tecla.Key == ConsoleKey.Escape) ... .
Algunos de los cdigos de tecla disponibles son:
o Teclas de edicin y control como, como: Backspace (Tecla
RETROCESO), Tab (Tecla TAB), Clear (Tecla BORRAR), Enter (Tecla
ENTRAR), Pause (Tecla PAUSA), Escape (Tecla ESC (ESCAPE)),
Spacebar (Tecla BARRA ESPACIADORA), PrintScreen (Tecla IMPR
PANT), Insert (Tecla INS (INSERT)), Delete (Tecla SUPR (SUPRIMIR))
Revisin 0.99zz Pgina 341
Introduccin a la programacin con C#, por Nacho Cabanes
Console.Readkey hace que el programa se quede parado hasta que se pulse una
tecla. Si queremos hacer algo mientras que el usuario no pulse ninguna tecla,
podemos emplear Console.KeyAvailable para comprobar si ya se ha pulsado
alguna tecla que haya que analizar, como en este ejemplo, que permite mover un
smbolo a izquierda y derecha, pero muestra una animacin simple si no pulsamos
ninguna tecla:
// Ejemplo_12_02b.cs
// No bloquear el programa con Console.Readkey
// Introduccin a C#, por Nacho Cabanes
Revisin 0.99zz Pgina 342
Introduccin a la programacin con C#, por Nacho Cabanes
using System;
using System.Threading;
do
{
Console.Clear();
Console.SetCursorPosition(posX, posY);
Console.Write( simbolos[ simboloActual ]);
Thread.Sleep(500);
if (Console.KeyAvailable)
{
ConsoleKeyInfo tecla = Console.ReadKey(true);
if (tecla.Key == ConsoleKey.RightArrow) posX++;
if (tecla.Key == ConsoleKey.LeftArrow) posX--;
if (tecla.Key == ConsoleKey.Escape) terminado = true;
}
simboloActual++;
if (simboloActual > 3) simboloActual = 0;
}
while ( ! terminado );
}
}
Al igual que en este ejemplo, ser recomendable hacer una pequea pausa entre
una comprobacin de teclas y la siguiente, con Thread.Sleep, tanto para que la
animacin no sea demasiado rpida como para no hacer un consumo muy alto de
procesador para tareas poco importantes.
Los colores que tenemos disponibles (y que se deben escribir precedidos con
"ConsoleColor") son: Black (negro), DarkBlue (azul marino), DarkGreen (verde
oscuro) DarkCyan (verde azulado oscuro), DarkRed (rojo oscuro), DarkMagenta
(fucsia oscuro o prpura), DarkYellow (amarillo oscuro u ocre), Gray (gris),
DarkGray (gris oscuro), Blue (azul), Green (verde), Cyan (aguamarina o verde
azulado claro), Red (rojo), Magenta (fucsia), Yellow (amarillo), White (blanco).
Ejercicios propuestos:
(12.2.1) Crea un programa que muestre una "pelota" (la letra "O") rebotando en
los bordes de la pantalla. Para que no se mueva demasiado rpido, haz una pausa
de 50 milisegundos entre un "fotograma" y otro.
(12.2.2) Crea una versin de la "base de datos de ficheros" (ejemplo 04_06a) que
use colores para ayudar a distinguir los mensajes del programa de las respuestas
del usuario, y que no necesite pulsar Intro tras escoger cada opcin.
(12.2.3) Crea un programa que permita "dibujar" en consola, moviendo el cursor
con las flechas del teclado y pulsando "espacio" para dibujar un punto o borrarlo.
(12.2.4) Crea una versin del programa de "dibujar" en consola (12.2.3), que
permita escribir ms caracteres (por ejemplo, las letras), as como mostrar ayuda
(pulsando F1), guardar el contenido de la pantalla en un fichero de texto (con F2) o
recuperarlo (con F3).
(12.2.5) Crea una versin mejorada del programa 12.1.2 (mostrar el reloj
actualizado en pantalla, que lo dibuje siempre en la esquina superior derecha de la
pantalla).
// Ejemplo_12_03a.cs
// Crear un directorio
// Introduccin a C#, por Nacho Cabanes
using System.IO;
(la clase Directory est declarada en el espacio de nombres System.IO, por lo que
deberemos aadirlo entre los "using" de nuestro programa).
// Ejemplo_12_03b.cs
// Lista de ficheros en un directorio
// Introduccin a C#, por Nacho Cabanes
using System;
using System.IO;
listaFicheros = Directory.GetFiles(miDirectorio);
foreach(string fichero in listaFicheros)
Console.WriteLine(fichero);
}
}
// Ejemplo_12_03c.cs
// Lista de ficheros en un directorio
// Introduccin a C#, por Nacho Cabanes
using System;
using System.IO;
// Ejemplo_12_03d.cs
// Lista detallada de ficheros en un directorio
// Introduccin a C#, por Nacho Cabanes
using System;
using System.IO;
}
}
Ejercicios propuestos:
(12.3.1) Crea un programa que muestre en pantalla el contenido de un fichero de
texto, cuyo nombre escoja el usuario. Si el usuario no sabe el nombre, podr
pulsar "Intro" y se le mostrar la lista de ficheros existentes en el directorio actual ,
para luego volver a preguntarle el nombre del fichero.
(12.3.2) Crea un programa que cree un fichero de texto a partir del contenido de
todos los ficheros de texto existentes en la carpeta actual.
(12.3.3) Crea un programa que permita "pasear" por la carpeta actual, al estilo del
antiguo "Comandante Norton": mostrar la lista de ficheros y subdirectorios de la
carpeta actual, y permitir al usuario moverse hacia arriba o abajo dentro de la
lista usando las flechas del cursor. El elemento seleccionado se mostrar en color
distinto del resto.
(12.3.4) Mejora el ejercicio 12.3.3 para que muestre directorios (en primer lugar) y
ficheros (a continuacin), y permita entrar a un directorio si se pulsa Intro sobre l
(en ese momento, se actualizar la lista de ficheros y directorios, para mostrar el
contenido del directorio al que se ha accedido).
(12.3.5) Mejora el ejercicio 12.3.4 para que contenga dos paneles, uno al lado del
otro, cada uno de los cuales podr estar mostrando el contenido de un directorio
distinto. Si se pulsa el "tabulador", cambiar el panel activo.
(12.3.6) Mejora el ejercicio 12.3.5, para que se pueda "seleccionar un fichero"
pulsando "Espacio" o "Insert". Los ficheros seleccionados se mostrarn en un color
distinto. Se podrn deseleccionar volviendo a pulsar "Espacio" o "Insert". Si se
pulsa F5, los ficheros seleccionados en la carpeta actual del panel actual se
copiarn a la carpeta del otro panel. Mientras se estn copiando, el programa
debe mostrar una "barra de progreso" de color amarillo, que indicar el porcentaje
de ficheros que ya se han copiado.
proc.WaitForExit();
// Ejemplo_12_04a.cs
// Lanzar otro proceso y esperar
// Introduccin a C#, por Nacho Cabanes
using System;
using System.Diagnostics;
Ejercicios propuestos:
(12.4.1) Mejora el ejercicio 12.3.3 (la versin bsica del programa "tipo
Comandante Norton") para que, si se pulsa Intro sobre un cierto fichero, lance el
correspondiente proceso.
(12.4.2) Aplica esta misma mejora al ejercicio 12.3.5 (la versin con dos paneles del
programa "tipo Comandante Norton").
(12.4.3) Crea un programa que mida el tiempo que tarda en ejecutarse un cierto
proceso. Este proceso se le indicar como parmetro en la lnea de comandos.
// Ejemplo_12_05a.cs
// Informacin sobre el sistema
// Introduccin a C#, por Nacho Cabanes
using System;
using System.Diagnostics;
Ejercicios propuestos:
(12.5.1) Mejora el ejercicio 12.4.2 (la versin ms completa del programa "tipo
Comandante Norton") para que se pueda pasar a "navegar" por otra unidad de
disco. Si se pulsa Alt+F1, se mostrar la lista de unidades lgicas, el usuario podr
escoger una de ellas, y esa pasar a ser la unidad activa en el panel izquierdo. Si se
pulsa Alt+F2, se realizar el mismo proceso, pero cambiar la unidad activa en el
panel derecho.
Como primer ejemplo, vamos a ver cmo podramos recibir una pgina web (por
ejemplo, la pgina principal de "www.nachocabanes.com"), lnea a lnea como si se
tratara de un fichero de texto (StreamReader), y mostrar slo las lneas que
contengan un cierto texto (por ejemplo, la palabra "Pascal"):
// Ejemplo_12_06a.cs
// Ejemplo de descarga y anlisis de una web:
// Muestra las lneas que contienen "Pascal"
// Introduccin a C#, por Nacho Cabanes
using System;
using System.IO; // Para Stream
using System.Net; // Para System.Net.WebClient
int contador = 0;
while ( (linea=lector.ReadLine()) != null )
{
contador ++;
if (linea.IndexOf("Pascal") >= 0)
Console.WriteLine("{0}: {1}", contador, linea);
}
conexion.Close();
}
}
Otra posibilidad que tampoco es complicada (aunque s algo ms que sta ltima)
es la de comunicar dos ordenadores, para enviar informacin desde uno y
recibirla desde el otro. Se puede hacer de varias formas. Una de ellas es usando
directamente el protocolo TCP: emplearemos un TcpClient para enviar y un
TcpListener para recibir, y en ambos casos trataremos los datos como un tipo
especial de fichero binario, un NetworkStream:
// Ejemplo_12_06b.cs
// Ejemplo de envio y recepcin de frases a travs de la red
// Introduccin a C#, por Nacho Cabanes
using System;
using System.IO; // Para Stream
using System.Text; // Para Encoding
using System.Net; // Para Dns, IPAddress
using System.Net.Sockets; // Para NetworkStream
conexion.Write(secuenciaLetras, 0, secuenciaLetras.Length);
conexion.Close();
cliente.Close();
}
cliente.Close();
listener.Stop();
return frase;
}
1
Esperando...
Prueba de texto
Recibido
Enviando... Enviado
Esta misma idea se podra usar como base para programas ms elaborados, que
comunicaran diferentes equipos (en este caso, la direccin no sera "localhost",
sino la IP del otro equipo), como podra ser un chat o cualquier juego multijugador
en el que hubiera que avisar a otros jugadores de los cambios realizados por cada
uno de ellos.
Esto se puede conseguir a un nivel algo ms alto, usando los llamados "Sockets"
en vez de los TcpClient, o de un modo "no fiable", usando el protocolo UDP en vez
de TCP, pero nosotros no veremos ms detalles de ninguno de ambos mtodos.
Ejercicios propuestos:
(12.6.1) Crea un juego de "3 en raya" en red, para dos jugadores, en el que cada
jugador escoja un movimiento, pero ambos vean un mismo estado del tablero.
(12.6.2) Crea un programa bsico de chat, en el que dos usuarios puedan
comunicarse, sin necesidad de seguir turnos estrictos.
(12.6.3) Crea un programa que monitorice cambios en una pgina web,
comparando el contenido actual con una copia guardada en fichero. Deber
mostrar en pantalla un mensaje que avise al usuario de si hay cambios o no.
(12.6.4) Crea un programa que descargue todo un sitio web, partiendo de la
pgina que indique el usuario, analizando los enlaces que contiene y descargando
de forma recursiva las pginas que corresponden a dichos enlaces. Slo deber
procesar los enlaces internos (en el mismo sitio web), no las pginas externas
(alojadas en otros sitios web).
(12.6.5) Crea un juego de "barquitos" en red, para dos jugadores, en el que cada
jugador decida dnde quiere colocar sus barcos, y luego cada uno de ellos
"dispare" por turnos, escogiendo una casilla (por ejemplo, "B5"), y siendo avisado
de si ha acertado ("impacto") o no ("agua"). Los barcos se podrn colocar en
horizontal o en vertical sobre un tablero de 8x8, y sern: 4 de longitud 1, 3 de
longitud 2, 2 de longitud 3 y uno de longitud 4. Cada jugador ver el estado de su
propio tablero y los impactos que ha conseguido en el tablero del otro jugador. El
juego terminar cuando uno de los jugadores hunda toda la flota del otro.
(12.6.6) Mejora el juego de "barquitos" (12.6.5) para que el aviso de impacto sea
ms detallado ("tocado" o "hundido", segn el caso).
(12.6.7) Mejora el juego de "barquitos" completo (12.6.6) para que un jugador
pueda decidir aplazar la partida, y entonces el resultado quede guardado en
ficheo, y en la siguiente sesin se reanude el juego en el punto en el que qued.
La idea detrs de ese "using" es que puede ocurrir que distintos programadores
en distintos puntos del mundo (o incluso en distintos grupos de un mismo gran
proyecto) creen funciones o clases que se llamen igual, y, si se mezclan fuentes de
distintas procedencias, esto podra dar lugar a programas que no compilaran
correctamente, o, peor an, que compilaran pero no funcionaran de la forma
esperada.
Por eso, se recomienda usar "espacios de nombres", que permitan distinguir unos
de otros. Por ejemplo, si yo quisiera crear mi propia clase "Console" para el acceso
a la consola, o mi propia clase "Random" para manejo de nmeros aleatorios, lo
razonable es crear un nuevo espacio de nombres, de forma que cualquier usuario
de esas clase pueda simultanear su uso con el de las incluidas en la plataforma
.Net.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
}
}
}
using System;
namespace PruebaDeNamespaces
{
class Program
{
public static void Main(string[] args)
{
Console.WriteLine("Hello World!");
// Ejemplo_13_01a.cs
// Ejemplo de uso de "namespaces"
// Introduccin a C#, por Nacho Cabanes
using System;
namespace ConsolaAmpliada
{
public class Console
{
public static void WriteLine(string texto)
{
System.Console.ForegroundColor = ConsoleColor.Blue;
System.Console.WriteLine("Mensaje: "+texto);
}
}
}
Como se puede ver, este ejemplo tiene dos clases Console, y ambas tienen un
mtodo WriteLine. Una es la original de C#, que invocaramos con
"System.Console". Otra es la que hemos creado para el ejemplo, que escribe un
texto modifica y en color (ayudndose de System.Console), y que llamaramos
mediante "ConsolaAmpliada.Console". El resultado es que podemos tener dos
Revisin 0.99zz Pgina 355
Introduccin a la programacin con C#, por Nacho Cabanes
clases Console accesibles desde el mismo programa, sin que existan conflictos
entre ellas. El resultado del programa sera:
Hola
Mensaje: Hola otra vez
// Ejemplo_13_02a.cs
// Operaciones a nivel de bits
// Introduccin a C#, por Nacho Cabanes
using System;
El resultado es:
La variable a vale 67
y b vale 33
El complemento de a es: -68
El producto lgico de a y b es: 1
Su suma lgica es: 99
Su suma lgica exclusiva es: 98
Desplacemos a a la izquierda: 134
Desplacemos a a la derecha: 33
Para comprobar que es correcto, podemos convertir al sistema binario esos dos
nmeros y seguir las operaciones paso a paso:
67 = 0100 0011
33 = 0010 0001
0000 0001 = 1
Despus hacemos su suma lgica, sumando cada bit, de modo que 1+1 = 1, 1+0 =
1, 0+0 = 0
0110 0011 = 99
La suma lgica exclusiva devuelve un 1 cuando los dos bits son distintos: 1^1 = 0,
1^0 = 1, 0^0 = 0
0110 0010 = 98
Desplazar los bits una posicin a la izquierda es como multiplicar por dos:
Desplazar los bits una posicin a la derecha es como dividir entre dos:
0010 0001 = 33
x += 2;
x <<= 2;
x &= 2;
x |= 2;
...
Ejercicios propuestos
(13.2.1) Crea un programa que lea un fichero de texto, encripte cada lnea
haciendo un XOR de los caracteres que la forman con un cierto dato prefijado (y
pequeo, como 3, por ejemplo) y vuelque el resultado a un nuevo fichero.
13.3. Enumeraciones
Cuando tenemos varias constantes, cuyos valores son nmeros enteros, hasta
ahora estamos dando los valores uno por uno, as:
Hay una forma alternativa de hacerlo, especialmente til si son nmeros enteros
consecutivos. Se trata de enumerarlos:
(Al igual que las constantes de cualquier otro tipo, se puede escribir en maysculas
para recordar "de un vistazo" que son constantes, no variables)
Si queremos que los valores no sean exactamente estos, podemos dar valor a
cualquiera de las contantes, y las siguientes irn aumentando de uno en uno. Por
ejemplo, si escribimos
// Ejemplo_13_03a.cs
// Ejemplo de enumeraciones
// Introduccin a C#, por Nacho Cabanes
using System;
y su resultado ser:
Nosotros hemos usado enumeraciones muchas veces hasta ahora, sin saber
realmente que lo estbamos haciendo. Por ejemplo, el modo de apertura de un
fichero (FileMode) es una enumeracin, por lo que escribimos FileMode.Open.
Tambin son enumeraciones los cdigos de color de la consola (como
ConsoleColor.Red) y las teclas de la consola (como ConsoleKey.Escape).
Nota: las enumeraciones existen tambin en otros lenguajes como C y C++, pero la
sintaxis es ligeramente distinta: en C# es necesario indicar el nombre de la
enumeracin cada vez que se usen sus valores (como en diasSemana.MIERCOLES),
mientras que en C se usa slo el valor (MIERCOLES).
Ejercicios propuestos
(13.3.1) Crea una versin de la base de datos de ficheros con colores (ejercicio
12.2.2) en la que las opciones sean parte de una enumeracin.
13.4. Propiedades
Hasta ahora estbamos siguiendo la poltica de que los atributos de una clase sean
privados, y se acceda a ellos a travs de mtodos "get" (para leer su valor) y "set"
(para cambiarlo). En el caso de C#, existe una forma alternativa de conseguir el
mismo efecto, empleando las llamadas "propiedades", que tienen una forma
abreviada de escribir sus mtodos "get" y "set":
// Ejemplo_13_04a.cs
// Ejemplo de propiedades (1)
// Introduccin a C#, por Nacho Cabanes
using System;
// -------------------------
{
get
{
return anchura;
}
set
{
anchura = value;
}
}
// -------------------------
// El "Main" de prueba
public static void Main()
{
EjemploPropiedades ejemplo
= new EjemploPropiedades();
ejemplo.SetAltura(5);
Console.WriteLine("La altura es {0}",
ejemplo.GetAltura() );
ejemplo.Anchura = 6;
Console.WriteLine("La anchura es {0}",
ejemplo.Anchura );
}
}
Una curiosidad: si una propiedad tiene un "get", pero no un "set", ser una
propiedad de slo lectura, no podremos hacer cosas como "Anchura = 4", porque
el programa no compilara. De igual modo, se podra crear una propiedad de slo
escritura, definiendo su "set" pero no su "get".
Ejercicios propuestos
(13.4.1) Crea una nueva versin del ejercicio de la clase Persona (6.7.1), en la que
el "nombre" sea una propiedad, con sus correspondientes "get" y "set".
orden breve si todos los caracteres de una cadena son numricos, o si empieza
por mayscula y el resto son minsculas, etc.
// Ejemplo_13_05a.cs
// Ejemplo de expresiones regulares
// Introduccin a C#, por Nacho Cabanes
using System;
using System.Text.RegularExpressions;
class PruebaExprRegulares
{
public static bool EsNumeroEntero(String cadena)
{
Regex patronNumerico = new Regex("[^0-9]");
return !patronNumerico.IsMatch(cadena);
}
if (EsNumeroEntero("1942"))
Console.WriteLine("1942 es un nmero entero");
else
Console.WriteLine("1942 NO es un nmero entero");
if (EsNumeroEntero2("1942"))
Console.WriteLine("1942 es entero (forma 2)");
else
Console.WriteLine("1942 NO es entero (forma 2)");
if (EsNumeroEntero("23,45"))
Console.WriteLine("23,45 es un nmero entero");
else
Console.WriteLine("23,45 NO es un nmero entero");
if (EsNumeroEntero2("23,45"))
Console.WriteLine("23,45 es entero (forma 2)");
else
Console.WriteLine("23,45 NO es entero (forma 2)");
if (EsNumeroConDecimales("23,45"))
Console.WriteLine("23,45 es un nmero con decimales");
else
Console.WriteLine("23,45 NO es un nmero con decimales");
if (EsNumeroConDecimales("23,45,67"))
Console.WriteLine("23,45,67 es un nmero con decimales");
else
Console.WriteLine("23,45,67 NO es un nmero con decimales");
if (EsAlfabetico("hola"))
Revisin 0.99zz Pgina 363
Introduccin a la programacin con C#, por Nacho Cabanes
Console.WriteLine("hola es alfabetico");
else
Console.WriteLine("hola NO es alfabetico");
if (EsAlfanumerico("hola1"))
Console.WriteLine("hola1 es alfanumerico");
else
Console.WriteLine("hola1 NO es alfanumerico");
}
}
Su salida es:
Las expresiones regulares son algo complejo. Por una parte, su sintaxis puede
llegar a ser difcil de seguir. Por otra parte, el manejo en C# no se limita a buscar,
sino que tambin permite otras operaciones, como reemplazar unas expresiones
por otras. Como ver muchos ms detalles podra hacer el texto demasiado
extenso, puede ser recomendable ampliar informacin usando la pgina web de
MSDN (Microsoft Developer Network):
http://msdn.microsoft.com/es-
es/library/system.text.regularexpressions.regex(VS.80).aspx
Ejercicios propuestos
(13.5.1) Crea una funcin que valide cdigos postales espaoles (5 cifras
numricas): devolver true si la cadena recibida como parmetro es un cdigo
postal vlido.
(13.5.2) Crea una funcin que diga si una cadena que se le pase como parmetro
parece un correo electrnico vlido.
// Ejemplo_13_06a.cs
// Operador coma
// Introduccin a C#, por Nacho Cabanes
using System;
i vale 0 y j vale 1
i vale 1 y j vale 3
i vale 2 y j vale 5
i vale 3 y j vale 7
i vale 4 y j vale 9
i vale 5 y j vale 11
// Ejemplo_13_07a.cs
// Ejemplo de uso de "var"
// Introduccin a C#, por Nacho Cabanes
Revisin 0.99zz Pgina 365
Introduccin a la programacin con C#, por Nacho Cabanes
using System;
var condicion = 5 == 7;
Console.WriteLine("condicion vale {0} y es de tipo {1}",
condicion, condicion.GetType());
var pi = 3.1416;
Console.WriteLine("pi vale {0} y es de tipo {1}",
pi, pi.GetType());
Ejercicios propuestos
(13.7.1) Crea un programa que pida al usuario una cantidad de kilmetros y
muestre su equivalencia en millas. El valor de conversin debe estar en una
variable definida con "var".
(13.7.2) Crea un programa que muestre la primera lnea de un fichero de texto.
Tanto el fichero como la lnea se deben declarar con "var".
Por ejemplo, podemos obtener los nmeros enteros de un array cuyo valor es
mayor que 10 con:
// Ejemplo_13_08a.cs
// Ejemplo bsico de LINQ
// Introduccin a C#, por Nacho Cabanes
using System;
using System.Linq;
var result =
from n in datos
where n > 10
select n;
foreach(int i in result)
Console.Write("{0} ", i);
Console.WriteLine();
}
}
// Ejemplo_13_08b.cs
// Segundo ejemplo de LINQ
// Introduccin a C#, por Nacho Cabanes
using System;
using System.Linq;
using System.Collections.Generic;
var resultado =
from nombre in nombres
orderby nombre
select nombre;
Profundizar en LINQ es algo que queda fuera del propsito de este texto. Si
quieres saber ms, puedes acudir a la propia referencia oficial en lnea, en MSDN:
https://msdn.microsoft.com/es-es/library/bb397926.aspx
Ejercicios propuestos
(13.8.1) Crea un programa que pida al usuario varios nmeros enteros, los guarde
en una lista y luego muestre todos los que sean positivos, ordenados, empleando
LINQ.
(13.8.2) Crea un programa que prepare un array de palabras. Luego, el usuario
debe introducir una palabra, y el programa responder si es palabra est en el
array o no, utilizando LINQ.
Delegados (delegate).
Funciones lambda.
El preprocesador.
Entornos grficos (Windows Forms, WPF).
Uso en servidores Web (ASP.Net).
Para eliminar esos fallos que hacen que un programa no se comporte como
debera, se usan unas herramientas llamadas "depuradores". Estos nos permiten
avanzar paso a paso para ver cmo avanza realmente nuestro programa, y
tambin nos dejan ver los valores de las variables.
Veremos como ejemplo el caso de Visual Studio 2008 Express, pero las diferencias
con versiones posteriores de Visual Studio deberan ser mnimas.
using System;
namespace EjemploDeOperaciones
{
class Program
{
static void Main(string[] args)
{
int a, b, c;
a = 5;
b = a + 2;
b -= 3;
c = -3;
c *= 2;
++c;
a *= b;
}
}
Revisin 0.99zz Pgina 369
Introduccin a la programacin con C#, por Nacho Cabanes
Para avanzar paso a paso y ver los valores de las variables, entramos al men
"Depurar". En l aparece la opcin "Paso a paso por instrucciones" (al que
corresponde la tecla F11):
Si escogemos esa opcin del men o pulsamos F11, aparece una ventana inferior
con la lista de variables, y un nuevo cursor amarillo, que se queda al principio del
programa:
Aqu hemos avanzado desde el principio del programa, pero eso no es algo
totalmente habitual. Es ms frecuente que supongamos en qu zona del programa
se encuentra el error, y slo queramos depurar una zona de programa. La forma
de conseguirlo es escoger otra de las opciones del men de depuracin: "Alternar
puntos de ruptura" (tecla F9). Aparecer una marca granate en la lnea actual
como alternativa, podemos hacer clic con el ratn en el margen izquierdo del
programa, junto a esa lnea):
Si ahora iniciamos la depuracin del programa, saltar sin detenerse hasta ese
punto, y ser entonces cuando se interrumpa. A partir de ah, podemos seguir
depurando paso a paso como antes.
float discriminante;
if (discriminante < 0)
{
x1 = -9999;
x2 = -9999;
}
else if (discriminante == 0)
{
x1 = - b / (2*a);
x2 = -9999;
}
else
{
x1 = (float) ((- b + Math.Sqrt(discriminante))/ 2*a);
x2 = (float) ((- b - Math.Sqrt(discriminante))/ 2*a);
}
}
}
Es decir, si alguna solucin no existe, se devuelve un valor falso, que no sea fcil
que se obtenga en un caso habitual, como -9999.
x2 -1 = 0 x1 = 1, x2 = -1
x2 = 0 x1 = 0, x2 = No existe (solucin nica)
Revisin 0.99zz Pgina 373
Introduccin a la programacin con C#, por Nacho Cabanes
x2 -3x = 0 x1 = 3, x2 = 0
2x2 -2 = 0 x1 = 1, x2 = -1
// Ejemplo_14_03a.cs
// Ejemplo de realizacin de pruebas
// Introduccin a C#, por Nacho Cabanes
using System;
float discriminante;
if (discriminante < 0)
{
x1 = -9999;
x2 = -9999;
}
else if (discriminante == 0)
{
x1 = - b / (2*a);
x2 = -9999;
}
else
{
x1 = (float) ((- b + Math.Sqrt(discriminante))/ 2*a);
x2 = (float) ((- b - Math.Sqrt(discriminante))/ 2*a);
}
}
}
Console.Write("Probando x2 - 1 = 0 ...");
ecuacion.Resolver((float)1, (float) 0, (float) -1,
out soluc1, out soluc2);
if ((soluc1 == 1) && (soluc2 == -1))
Console.WriteLine("OK");
Revisin 0.99zz Pgina 374
Introduccin a la programacin con C#, por Nacho Cabanes
else
Console.WriteLine("Falla");
Console.Write("Probando x2 = 0 ...");
ecuacion.Resolver((float)1, (float)0, (float)0,
out soluc1, out soluc2);
if ((soluc1 == 0) && (soluc2 == -9999))
Console.WriteLine("OK");
else
Console.WriteLine("Falla");
Vemos que en uno de los casos, la solucin no es correcta. Revisaramos los pasos
que da nuestro programa, corregiramos el fallo y volveramos a lanzar las pruebas
automatizadas. En este caso, el problema es que falta un parntesis, para dividir
entre (2*a), de modo que estamos diviendo entre 2 y luego multiplicando por a.
// Ejemplo_14_03b.cs
// Segundo ejemplo de realizacin de pruebas, con Assert
// Introduccin a C#, por Nacho Cabanes
using System;
using System.Diagnostics;
float discriminante;
if (discriminante < 0)
{
x1 = -9999;
x2 = -9999;
}
else if (discriminante == 0)
{
x1 = - b / (2*a);
x2 = -9999;
}
else
{
x1 = (float) ((- b + Math.Sqrt(discriminante))/ 2*a);
x2 = (float) ((- b - Math.Sqrt(discriminante))/ 2*a);
}
}
}
Console.Write("Probando x2 - 1 = 0 ...");
ecuacion.Resolver((float)1, (float) 0, (float) -1,
out soluc1, out soluc2);
Debug.Assert((soluc1 == 1) && (soluc2 == -1));
Console.Write("Probando x2 = 0 ...");
ecuacion.Resolver((float)1, (float)0, (float)0,
out soluc1, out soluc2);
Debug.Assert((soluc1 == 0) && (soluc2 == -9999));
La ventaja de crear bateras de pruebas es que es una forma muy rpida de probar
un programa, por lo que se puede aplicar tras cada pocos cambios para
comprobar que todo es correcto. El inconveniente es que NO GARANTIZA que el
programa sea correcto, sino slo que no falla en ciertos casos. Por ejemplo, si la
batera de pruebas anterior solo contuviera las tres primeras pruebas, no habra
descubierto el fallo del programa.
Por tanto, las pruebas slo permiten asegurar que el programa falla en caso de
encontrar problemas, pero no permiten asegurar nada en caso de que no se
encuentren problemas: puede que aun as exista un fallo que no hayamos
detectado.
Casi todos esos diagramas caen fuera del alcance de este texto: en una
introduccin a la programacin se realizan programas de pequeo tamao, para
los que no es necesaria una gran planificacin.
Aun as, hay un tipo de documentacin que s debe estar presente en cualquier
problema: los comentarios que aclaren todo aquello que no sea obvio.
Por eso, este apartado se va a centrar en algunas de las pautas que los expertos
suelen recomendar para los comentarios en los programas, y tambin veremos
como a partir de estos comentarios se puede generar documentacin adicional de
forma casi automtica.
http://www.variablenotfound.com/2007/12/13-consejos-para-comentar-tu-
cdigo.html
Ojo a las tabulaciones. Hay editores de texto que usan el carcter ASCII (9) y otros,
lo sustituyen por un nmero determinado de espacios, que suelen variar segn las
preferencias personales del desarrollador. Lo mejor es usar espacios simples o
asegurarse de que esto es lo que hace el IDE correspondiente.
5. S correcto
Evita comentarios del tipo "ahora compruebo que el estpido usuario no haya
introducido un nmero negativo", o "este parche corrige el efecto colateral
producido por la pattica implementacin del inepto desarrollador inicial".
Relacionado e igualmente importante: cuida la ortografa.
6. No pierdas el tiempo
No comentes si no es necesario, no escribas nada ms que lo que necesites para
transmitir la idea. Nada de diseos realizados a base de caracteres ASCII, ni
florituras, ni chistes, ni poesas, ni chascarrillos.
Hay incluso quien opina que los comentarios que describen un bloque deberan
escribirse antes de codificarlo, de forma que, en primer lugar, sirvan como
referencia para saber qu es lo que hay que hacer y, segundo, que una vez
codificado stos queden como comentarios para la posteridad. Un ejemplo:
Console.WriteLine("Resultado: " +
new Calculator()
.Set(0)
.Add(10)
.Multiply(2)
.Substract(4)
.Get()
);
Pero en algunos lenguajes modernos, como Java y C#, existe una posibilidad
adicional que puede resultar muy til: usar comentarios que nos ayuden a crear
de forma automtica cierta documentacin del programa.
Esta documentacin tpicamente ser una serie pginas HTML enlazadas, o bien
varios ficheros XML.
Por ejemplo, la herramienta (gratuita) Doxygen genera pginas como sta a partir
de un fuente en C#:
/**
* Personaje: uno de los tipos de elementos graficos del juego
*
* @see ElemGrafico Juego
* @author 1-DAI 2008/09
*/
(...)
Las etiquetas que sugiere Microsoft son <c> <para> <see> <code> <param>
<seealso> <example> <paramref> <summary> <exception> <permission>
<typeparam> <include>* <remarks> <typeparamref> <list> <returns> <value>.
Puedes leer ms sobre el uso que se sugiere para cada una de ellas aqu:
https://msdn.microsoft.com/en-us/library/5ast78ax.aspx
Eso s, suele ocurrir que realmente un texto de 2000 letras que se guarde en el
ordenador ocupe ms de 2000 bytes, porque se suele incluir informacin adicional
sobre los tipos de letra que se han utilizado, cursivas, negritas, mrgenes y
formato de pgina, etc.
Un byte se queda corto a la hora de manejar textos o datos algo ms largos, con lo
que se recurre a un mltiplo suyo, el kilobyte, que se suele abreviar Kb o K.
En teora, el prefijo kilo querra decir "mil", luego un kilobyte debera ser 1000
bytes, pero en los ordenadores conviene buscar por comodidad una potencia de 2
(pronto veremos por qu), por lo que se usa 210 =1024. As, la equivalencia exacta
es 1 Kb = 1024 bytes.
Los Kb eran unidades tpicas para medir la memoria de ordenadores: 640 Kb fue
mucho tiempo la memoria habitual en los primeros IBM PC y equipos similares.
Por otra parte, una pgina mecanografiada suele ocupar entre 2 K (cerca de 2000
letras) y 4 Kb.
Ejercicios propuestos:
(Ap1.1.1) Cuntas letras se podran almacenar en una agenda electrnica que
tenga 32 Kb de capacidad?
(Ap1.1.2) Si suponemos que una cancin tpica en formato MP3 ocupa cerca de
3.500 Kb, cuntas se podran guardar en un reproductor MP3 que tenga 256 Mb
de capacidad?
(Ap1.1.3) Cuntos diskettes de 1,44 Mb haran falta para hacer una copia de
seguridad de un ordenador que tiene un disco duro de 6,4 Gb? Y si usamos
compact disc de 700 Mb, cuntos necesitaramos?
(Ap1.1.4) A cuantos CD de 700 Mb equivale la capacidad de almacenamiento de
un DVD de 4,7 Gb? Y la de uno de 8,5 Gb?
Un bit es demasiado pequeo para un uso normal (recordemos: slo puede tener
dos valores: 0 1), por lo que se usa un conjunto de ellos, 8 bits, que forman un
byte. Las matemticas elementales (combinatoria) nos dicen que si agrupamos los
bits de 8 en 8, tenemos 256 posibilidades distintas (variaciones con repeticin de 2
elementos tomados de 8 en 8: VR2,8):
00000000
00000001
00000010
00000011
Revisin 0.99zz Pgina 386
Introduccin a la programacin con C#, por Nacho Cabanes
00000100
...
11111110
11111111
Por tanto, si en vez de tomar los bits de 1 en 1 (que resulta cmodo para el
ordenador, pero no para nosotros) los utilizamos en grupos de 8 (lo que se conoce
como un byte), nos encontramos con 256 posibilidades distintas, que ya son ms
que suficientes para almacenar una letra, o un signo de puntuacin, o una cifra
numrica o algn otro smbolo.
Por ejemplo, se podra decir que cada vez que encontremos la secuencia 00000010
la interpretaremos como una letra A, y la combinacin 00000011 como una letra B,
y as sucesivamente (aunque existe un estndar distinto, que comentaremos un
poco ms adelante).
Tambin existe una correspondencia entre cada grupo de bits y un nmero del 0
al 255: si usamos el sistema binario de numeracin (que aprenderemos dentro de
muy poco), en vez del sistema decimal, tenemos que:
Aun as, hay un inconveniente con el cdigo ASCII: slo los primeros 127 nmeros
son estndar. Eso quiere decir que si escribimos un texto en un ordenador y lo
llevamos a otro, las letras bsicas (A a la Z, 0 al 9 y algunos smbolos) no
cambiarn, pero las letras internacionales (como la o las vocales con acentos)
puede que no aparezcan correctamente, porque se les asignan nmeros que no
son estndar para todos los ordenadores. Por eso, existen estndares ms
modernos, como UTF-8, que comentaremos en el siguiente apartado.
Revisin 0.99zz Pgina 387
Introduccin a la programacin con C#, por Nacho Cabanes
Nota: Eso de que realmente el ordenador trabaja con ceros y unos, por lo que le
resulta ms fcil manejar los nmeros que son potencia de 2 que los nmeros que
no lo son, es lo que explica que el prefijo kilo no quiera decir "exactamente mil",
sino que se usa la potencia de 2 ms cercana: 210 =1024. Por eso, la equivalencia
exacta es 1 K = 1024 bytes.
Ejercicios propuestos:
(Ap1.2.1) Un nmero entero largo de 64 bits, cuntos bytes ocupa?
(Ap1.2.2) En una conexin de red de 100 Mbits/segundo, cunto tiempo tardara
en enviar 630 Mbytes de datos?
El cdigo ASCII estndar es de 7 bits, lo que hace que cada grupo de bits desde el
0000000 hasta el 1111111 (0 a 127 en decimal) corresponda siempre a la misma
letra.
Por ejemplo, en cualquier ordenador que use cdigo ASCII, la secuencia de bits
1000001 (65 en decimal) corresponder a la letra "A" (a, en maysculas).
Los cdigos estndar "visibles" van del 32 al 127. Los cdigos del 0 al 31 son
cdigos de control: por ejemplo, el caracter 7 (BEL) hace sonar un pitido, el
caracter 13 (CR) avanza de lnea, el carcter 12 (FF) expulsa una pgina en la
impresora (y borra la pantalla en algunos ordenadores), etc.
| 0 1 2 3 4 5 6 7 8 9 |
| 000 NUL SOH STX ETX EOT ENQ ACK BEL BS HT |
| 010 LF VT FF CR SO SI DLE DC1 DC2 DC3 |
| 020 DC4 NAK SYN ETB CAN EM SUB ESC FS GS |
| 030 RS US SP ! " # $ % & ' |
| 040 ( ) * + , - . / 0 1 |
| 050 2 3 4 5 6 7 8 9 : ; |
| 060 < = > ? @ A B C D E |
| 070 F G H I J K L M N O |
| 080 P Q R S T U V W X Y |
| 090 Z [ \ ] ^ _ ` a b c |
| 100 d e f g h i j k l m |
| 110 n o p q r s t u v w |
| 120 x y z { | } ~ |
Hoy en da, casi todos los ordenadores incluyen un cdigo ASCII extendido de 8
bits, que permite 256 smbolos (del 0 al 255), lo que permite que se puedan usar
tambin vocales acentuadas, ees, y otros smbolos para dibujar recuadros, por
ejemplo, aunque estos smbolos que van del nmero 128 al 255 no son estndar,
Ejercicios propuestos:
(Ap2.1) Crea un programa en C# que muestre una tabla ASCII bsica, desde el
carcter 32 hasta el 127, en filas de 16 caracteres cada una.
o ms detallado todava:
Para los ordenadores no es cmodo contar hasta 10. Como partimos de "casillas
de memoria" que estn completamente vacas (0) o completamente llenas (1), slo
les es realmente cmodo contar con 2 cifras: 0 y 1.
Por eso, dentro del ordenador cualquier nmero se deber almacenar como ceros
y unos, y entonces los nmeros se debern desglosar en potencias de 2 (el
llamado "sistema binario"):
13 = 1 8 + 1 4 + 0 2 + 1 1
o, ms detallado:
3 2 1 0
13 = 1 2 + 1 2 + 0 2 + 1 2
Convertir un nmero de decimal a binario resulta algo menos intuitivo. Una forma
sencilla es ir dividiendo entre las potencias de 2, y coger todos los cocientes de las
divisiones:
Si "juntamos" los cocientes que hemos obtenido, aparece el nmero binario que
buscbamos:
00 = 0 01 = 0 10 = 0 11 = 1
0+0 = 0 0+1 = 1 1+0 = 1 1+1 = 10 (en decimal: 2)
Ejercicios propuestos:
(Ap3.1.1) Expresa en sistema binario los nmeros decimales 17, 101, 83, 45.
(Ap3.1.2) Expresa en sistema decimal los nmeros binarios de 8 bits: 01100110,
10110010, 11111111, 00101101
(Ap3.1.3) Suma los nmeros 01100110+10110010, 11111111+00101101.
Comprueba el resultado sumando los nmeros decimales obtenidos en el ejercicio
anterior.
(Ap3.1.4) Multiplica los nmeros binarios de 4 bits 01001011, 10010011.
Comprueba el resultado convirtindolos a decimal.
(Ap3.1.5) Crea un programa en C# que convierta a binario los nmeros (en base
10) que introduzca el usuario.
(Ap3.1.6) Crea un programa en C# que convierta a base 10 los nmeros binarios
que introduzca el usuario.
de modo que
2 1 0
254 = 3 8 + 7 8 + 6 8
o bien
Pero adems existe una correspondencia muy sencilla entre el sistema octal y el
sistema binario: si agrupamos los bits de 3 en 3, el paso de binario a octal es
rapidsimo
de modo que
o bien
Ejercicios propuestos:
(Ap3.2.1) Expresar en sistema octal los nmeros decimales 17, 101, 83, 45.
(Ap3.2.2) Expresar en sistema octal los nmeros binarios de 8 bits: 01100110,
10110010, 11111111, 00101101
(Ap3.2.3) Expresar en el sistema binario los nmeros octales 171, 243, 105, 45.
(Ap3.2.4) Expresar en el sistema decimal los nmeros octales 162, 76, 241, 102.
(Ap3.1.5) Crea un programa en C# que convierta a octal los nmeros (en base 10)
que introduzca el usuario.
(Ap3.1.6) Crea un programa en C# que convierta a base 10 los nmeros octales
que introduzca el usuario.
Revisin 0.99zz Pgina 394
Introduccin a la programacin con C#, por Nacho Cabanes
Pero hay una dificultad: estamos acostumbrados al sistema decimal, con nmeros
del 0 al 9, de modo que no tenemos cifras de un solo dgito para los nmeros 10,
11, 12, 13, 14 y 15, que utilizaremos en el sistema hexadecimal. Para representar
estas cifras usaremos las letras de la A a la F, as:
0 (decimal) = 0 (hexadecimal)
1 (decimal) = 1 (hexadecimal)
2 (decimal) = 2 (hexadecimal)
3 (decimal) = 3 (hexadecimal)
4 (decimal) = 4 (hexadecimal)
5 (decimal) = 5 (hexadecimal)
6 (decimal) = 6 (hexadecimal)
7 (decimal) = 7 (hexadecimal)
8 (decimal) = 8 (hexadecimal)
9 (decimal) = 9 (hexadecimal)
10 (decimal) = A (hexadecimal)
11 (decimal) = B (hexadecimal)
12 (decimal) = C (hexadecimal)
13 (decimal) = D (hexadecimal)
14 (decimal) = E (hexadecimal)
15 (decimal) = F (hexadecimal)
de modo que
1 0
254 = 15 16 + 14 16
o bien
Revisin 0.99zz Pgina 395
Introduccin a la programacin con C#, por Nacho Cabanes
de modo que
o bien
3 2 1 0
254 = 13 16 + 4 16 + 3 16 + 11 16
es decir
3 2 1 0
A2B5 (hexadecimal) = 10 16 + 2 16 + 11 16 + 5 16 = 41653
110010100100100101010100111 =>
0110 0101 0010 0100 1010 1010 0111 = 6524AA7
Ejercicios propuestos:
(Ap3.3.1) Expresa en sistema hexadecimal los nmeros decimales 18, 131, 83, 245.
(Ap3.3.2) Expresa en sistema hexadecimal los nmeros binarios de 8 bits:
01100110, 10110010, 11111111, 00101101
(Ap3.3.3) Expresa en el sistema binario los nmeros hexadecimales 2F, 37, A0,
1A2.
(Ap3.3.4) Expresa en el sistema decimal los nmeros hexadecimales 1B2, 76, E1,
2A.
(Ap3.3.5) Crea un programa en C# que convierta a hexadecimal los nmeros (en
base 10) que introduzca el usuario.
(Ap3.3.6) Crea un programa en C# que convierta a base 10 los nmeros
hexadecimales que introduzca el usuario.
(Ap3.3.7) Crea un programa en C# que convierta a hexadecimal los nmeros
binarios que introduzca el usuario.
Complemento a 1: se cambian los ceros por unos para expresar los nmeros
negativos.
Ejercicio propuesto: convierte los nmeros decimales 13, 34, -13, -34 a sistema
binario, usando complemento a uno para expresar los nmeros negativos. Calcula
(en binario) el resultado de las operaciones 13+34, (-13)+(-34), 13+(-34) y
comprueba que los resultados que se obtienen son los correctos.
Complemento a 2: para los negativos, se cambian los ceros por unos y se suma
uno al resultado.
Ejercicio propuesto (Ap3.4.1): Convierte los nmeros decimales 13, 34, -13, -34 a
sistema binario, usando complemento a dos para expresar los nmeros negativos.
Calcula (en binario) el resultado de las operaciones 13+34, (-13)+(-34), 13+(-34) y
comprueba que los resultados que se obtienen son los correctos.
En general, todos los formatos que permiten guardar nmeros negativos usan el
primer bit para el signo. Por eso, si declaramos una variable como "unsigned", ese
primer bit se puede utilizar como parte de los datos, y podemos almacenar
nmeros ms grandes. Por ejemplo, un "ushort" (enterno corto sin signo) en C#
podra tomar valores entre 0 y 65.535, mientras que un "short" (entero corto con
signo) podra tomar valores entre +32.767 y 32.768.
Tao.SDL es una adaptacin de esta librera, que permite emplearla desde C#. Las
primeras versiones se pueden descargar desde http://www.mono-project.com/Tao y
las versiones ms recientes desde http://sourceforge.net/projects/taoframework/
// sdl01.cs
// Primer acercamiento a SDL
// Introduccin a C#, por Nacho Cabanes
using Tao.Sdl;
using System; // Para IntPtr (puntero: imgenes, etc)
// Inicializamos SDL
Sdl.SDL_Init(Sdl.SDL_INIT_EVERYTHING);
Revisin 0.99zz Pgina 400
Introduccin a la programacin con C#, por Nacho Cabanes
pantallaOculta = Sdl.SDL_SetVideoMode(
anchoPantalla,
altoPantalla,
bitsColor,
flags);
// Dibujamos la imagen
short x = 400;
short y = 300;
short anchoImagen = 50;
short altoImagen = 50;
Sdl.SDL_Rect origen = new Sdl.SDL_Rect(0,0,anchoImagen,altoImagen);
Sdl.SDL_Rect dest = new Sdl.SDL_Rect(x,y,anchoImagen,altoImagen);
Sdl.SDL_BlitSurface(imagen, ref origen, pantallaOculta, ref dest);
// Y esperamos 5 segundos
System.Threading.Thread.Sleep( 5000 );
Puedes descargar este fuente, incluyendo el proyecto de Visual Studio 2010, los
ficheros DLL necesarios y una imagen de ejemplo, desde:
http://www.nachocabanes.com/fich/descargar.php?nombre=SdlBasico.zip
Ejercicios propuestos
(Ap4.2.1) Crea una animacin con al menos 4 fotogramas, utilizando varias
imgenes que se mostrarn de forma repetitiva (por ejemplo, 10 veces, con medio
segundo de pausa entre una imagen y la siguiente).
// sdl02.cs
// Segundo acercamiento a SDL
// Introduccin a C#, por Nacho Cabanes
using Tao.Sdl;
using System; // Para IntPtr (puntero: imgenes, etc)
// Inicializamos SDL
Sdl.SDL_Init(Sdl.SDL_INIT_EVERYTHING);
pantallaOculta = Sdl.SDL_SetVideoMode(
anchoPantalla,
altoPantalla,
bitsColor,
flags);
// Parte repetitiva
bool terminado = false;
short x = 400;
short y = 300;
short anchoImagen = 50;
short altoImagen = 50;
Sdl.SDL_Event suceso;
int numkeys;
byte[] teclas;
do
{
// Comprobamos sucesos
Sdl.SDL_PollEvent(out suceso);
teclas = Sdl.SDL_GetKeyState(out numkeys);
// Borramos pantalla
Sdl.SDL_Rect origen = new Sdl.SDL_Rect(0,0,
anchoPantalla,altoPantalla);
Sdl.SDL_FillRect(pantallaOculta, ref origen, 0);
// Dibujamos en sus nuevas coordenadas
origen = new Sdl.SDL_Rect(0,0,anchoImagen,altoImagen);
Sdl.SDL_Rect dest = new Sdl.SDL_Rect(x,y,
anchoImagen,altoImagen);
Sdl.SDL_BlitSurface(imagen, ref origen,
pantallaOculta, ref dest);
// Y esperamos 20 ms
System.Threading.Thread.Sleep( 20 );
} while (!terminado);
}
}
Ejercicios propuestos
(Ap4.3.1) Ampla el ejemplo 2 de SDL (sdl02.cs) para que al moverse alterne entre
(al menos) dos imgenes, para dar una impresin de movimiento ms real.
Por ejemplo, podemos buscar que el programa anterior quede simplemente as:
// sdl03.cs
// Tercer acercamiento a SDL: clases auxiliares
// Introduccin a C#, por Nacho Cabanes
// Parte repetitiva
bool terminado = false;
short x = 400;
short y = 300;
Revisin 0.99zz Pgina 405
Introduccin a la programacin con C#, por Nacho Cabanes
do
{
// Miramos si se ha pulsado alguna flecha del cursor
if (h.TeclaPulsada(Hardware.TECLA_ARR))
y -= 2;
if (h.TeclaPulsada(Hardware.TECLA_ABA))
y += 2;
if (h.TeclaPulsada(Hardware.TECLA_IZQ))
x -= 2;
if (h.TeclaPulsada(Hardware.TECLA_DER))
x += 2;
if (h.TeclaPulsada(Hardware.TECLA_ESC))
terminado = true;
img.MoverA(x, y);
// Dibujar en pantalla
h.BorrarPantallaOculta();
h.DibujarImagenOculta(img);
h.VisualizarOculta();
// Y esperamos 20 ms
h.Pausa(20);
} while (!terminado);
}
}
Para eso necesitaramos una clase "Hardware", que oculte los detalles del acceso a
la pantalla y al teclado. Podra ser as:
// hardware.cs
// Clases auxiliar Hardware para SDL
// Introduccin a C#, por Nacho Cabanes
IntPtr pantallaOculta;
// Inicializamos SDL
Sdl.SDL_Init(Sdl.SDL_INIT_EVERYTHING);
pantallaOculta = Sdl.SDL_SetVideoMode(
anchoPantalla,
Revisin 0.99zz Pgina 406
Introduccin a la programacin con C#, por Nacho Cabanes
altoPantalla,
bitsColor,
flags);
~Hardware()
{
Sdl.SDL_Quit();
}
// Definiciones de teclas
public static int TECLA_ESC = Sdl.SDLK_ESCAPE;
public static int TECLA_ARR = Sdl.SDLK_UP;
public static int TECLA_ABA = Sdl.SDLK_DOWN;
Revisin 0.99zz Pgina 407
Introduccin a la programacin con C#, por Nacho Cabanes
Y una clase "Imagen" podra ocultar ese "IntPtr", adems de aadirle datos
adicionales, como la posicin en la que se encuentra esa imagen, y otros que ms
adelante nos resultarn tiles, como el ancho y el alto (que nos servirn para
comprobar colisiones):
// imagen.cs
// Clases auxiliar Imagen para SDL
// Introduccin a C#, por Nacho Cabanes
Ejercicios propuestos
(Ap4.4.1) Descompn en clases el ejercicio Ap4.3.1.
// sdl04.cs
// Cuarto acercamiento a SDL: bucle de juego
// Introduccin a C#, por Nacho Cabanes
using Tao.Sdl;
using System; // Para IntPtr (puntero: imgenes, etc)
void inicializar()
{
h = new Hardware(800, 600, 24, pantallaCompleta);
img = new Imagen("personaje.bmp", 50, 50);
x = 400;
y = 300;
void comprobarTeclas()
{
if (h.TeclaPulsada(Hardware.TECLA_ARR))
y -= 2;
if (h.TeclaPulsada(Hardware.TECLA_ABA))
y += 2;
if (h.TeclaPulsada(Hardware.TECLA_IZQ))
x -= 2;
if (h.TeclaPulsada(Hardware.TECLA_DER))
x += 2;
if (h.TeclaPulsada(Hardware.TECLA_ESC))
terminado = true;
img.MoverA(x, y);
}
void comprobarColisiones()
{
// Todavia no comprobamos colisiones con enemigos
// ni con obstaculos
}
void moverElementos()
{
// Todavia no hay ningun elemento que se mueva solo
}
void dibujarElementos()
{
h.BorrarPantallaOculta();
h.DibujarImagenOculta(img);
h.VisualizarOculta();
}
void pausaFotograma()
{
// Esperamos 20 ms
h.Pausa(20);
}
bool partidaTerminada()
{
return terminado;
}
// Bucle de juego
do
{
j.comprobarTeclas();
j.moverElementos();
Revisin 0.99zz Pgina 410
Introduccin a la programacin con C#, por Nacho Cabanes
j.comprobarColisiones();
j.dibujarElementos();
j.pausaFotograma();
} while (!j.partidaTerminada());
}
}
Ejercicios propuestos
(Ap4.5.1) Refina el ejercicio Ap4.4.1, para que tenga un fuente modular, siguiente
un bucle de juego clsico.
SdlTtf.TTF_Init();
El tipo de letra estar accesible a travs de un puntero genrico "IntPtr", igual que
ocurra con las imgenes, por lo que, igual que hicimos con stas, podemos crear
una clase auxiliar, llamada "Fuente":
// fuente.cs
// Clases auxiliar Fuente para SDL
// Introduccin a C#, por Nacho Cabanes
Y ya lo podemos usar desde nuestro juego. Por una parte, cargaremos el tipo de
letra (con el tamao que deseemos) dentro del "inicializar":
void inicializar()
{
h = new Hardware(800, 600, 24, pantallaCompleta);
img = new Imagen("personaje.bmp", 50, 50);
letra = new Fuente("FreeSansBold.ttf", 18);
x = 400;
y = 300;
}
Y escribiremos el texto que nos interese, en las coordenadas que queramos y con
el color que nos apetezca, desde el mtodo "dibujarElementos":
void dibujarElementos()
{
h.BorrarPantallaOculta();
Revisin 0.99zz Pgina 412
Introduccin a la programacin con C#, por Nacho Cabanes
h.DibujarImagenOculta(img);
h.EscribirTextoOculta("Texto de ejemplo",
200, 100, // Coordenadas
255, 0, 0, // Color: rojo
letra);
h.VisualizarOculta();
}
Ejercicios propuestos
(Ap4.6.1) Ampla el ejercicio Ap4.5.1, para que muestre una "puntuacin" en
pantalla (que, por ahora, siempre ser cero).
if (imgPersonaje.ColisionCon(imgEnemigo))
{
terminado = true;
}
if (imgPersonaje.ColisionCon(imgPremio))
{
puntos += 10;
xPremio = (short)generadorAzar.Next(750);
yPremio = (short)generadorAzar.Next(550);
imgPremio.MoverA(xPremio, yPremio);
}
xEnemigo += velocXEnemigo;
yEnemigo += velocYEnemigo;
imgEnemigo.MoverA(xEnemigo, yEnemigo);
// sdl06.cs
// Sexto acercamiento a SDL: colisiones
// Introduccin a C#, por Nacho Cabanes
using Tao.Sdl;
using System; // Para IntPtr (puntero: imgenes, etc)
Fuente letra;
bool terminado = false;
short x, y;
short xEnemigo, yEnemigo, velocXEnemigo, velocYEnemigo;
short xPremio, yPremio;
int puntos;
Random generadorAzar;
void inicializar()
{
h = new Hardware(800, 600, 24, pantallaCompleta);
imgPersonaje = new Imagen("personaje.bmp", 32, 30);
imgEnemigo = new Imagen("enemigo.bmp", 36, 42);
imgPremio = new Imagen("premio.bmp", 36, 14);
letra = new Fuente("FreeSansBold.ttf", 18);
generadorAzar = new Random();
x = 400; y = 300;
xEnemigo = 100; yEnemigo = 100;
velocXEnemigo = 3; velocYEnemigo = -3;
xPremio = (short) generadorAzar.Next(750);
yPremio = (short) generadorAzar.Next(550);
imgPremio.MoverA(xPremio, yPremio);
puntos = 0;
}
void comprobarTeclas()
{
if (h.TeclaPulsada(Hardware.TECLA_ARR))
y -= 4;
if (h.TeclaPulsada(Hardware.TECLA_ABA))
y += 4;
if (h.TeclaPulsada(Hardware.TECLA_IZQ))
x -= 4;
if (h.TeclaPulsada(Hardware.TECLA_DER))
x += 4;
if (h.TeclaPulsada(Hardware.TECLA_ESC))
terminado = true;
imgPersonaje.MoverA(x, y);
}
void comprobarColisiones()
{
// Si toca el premio: puntos y nuevo premio
if (imgPersonaje.ColisionCon(imgPremio))
{
puntos += 10;
xPremio = (short)generadorAzar.Next(750);
yPremio = (short)generadorAzar.Next(550);
imgPremio.MoverA(xPremio, yPremio);
}
void moverElementos()
{
xEnemigo += velocXEnemigo;
yEnemigo += velocYEnemigo;
imgEnemigo.MoverA(xEnemigo, yEnemigo);
// Para que rebote en los extremos de la pantalla
if ((xEnemigo < 10) || (xEnemigo > 750))
velocXEnemigo = (short) (-velocXEnemigo);
if ((yEnemigo < 10) || (yEnemigo > 550))
velocYEnemigo = (short)(-velocYEnemigo);
}
void dibujarElementos()
{
h.BorrarPantallaOculta();
h.DibujarImagenOculta(imgPersonaje);
h.DibujarImagenOculta(imgPremio);
h.DibujarImagenOculta(imgEnemigo);
h.EscribirTextoOculta("Puntos: "+puntos,
200, 100, // Coordenadas
255, 0, 0, // Color: rojo
letra);
h.VisualizarOculta();
}
void pausaFotograma()
{
// Esperamos 20 ms
h.Pausa(20);
}
bool partidaTerminada()
{
return terminado;
}
// Bucle de juego
do
{
j.comprobarTeclas();
j.moverElementos();
j.comprobarColisiones();
j.dibujarElementos();
j.pausaFotograma();
} while (!j.partidaTerminada());
}
}
http://www.nachocabanes.com/fich/descargar.php?nombre=SdlClases04.zip
Ejercicios propuestos
(Ap4.7.1) Ampla el esqueleto Ap4.6.1, para que muestre varios premios en
posiciones al azar. Cada vez que se toque un premio, ste desparecer y se
obtendrn 10 puntos. Al recoger todos los premios, acabar la partida.
(Ap4.7.2) Ampla el ejercicio Ap4.7.1, para que haya un "margen de la pantalla" y
una serie de obstculos, que "nos maten": si el personaje los toca, acabar la
partida.
(Ap4.7.3) Ampla el ejercicio Ap4.7.2, para que el movimiento sea continuo: el
jugador no se mover slo cuando se pulse una flecha, sino que continuar
movindose hasta que se pulse otra flecha (y entonces cambiar de direccin) o
choque con un obstculo (y entonces acabar la partida).
(Ap4.7.4) Ampla el ejercicio Ap4.7.3, para que el jugador sea una serpiente,
formada por varios segmentos; cada vez que se recoga un premio, la serpiente se
har ms larga en un segmento. Si la "cabeza" de la serpiente toca la cola, la
partida terminar.
(Ap4.7.5) Mejora la jugabilidad del ejercicio Ap4.7.4: el jugador tendr 3 vidas, en
vez de una; si se recogen todos los premios, comenzar un nuevo nivel, con mayor
cantidad de premios y de obstculos.
(Ap4.7.6) Aade dificultad creciente al ejercicio Ap4.7.5: cada cierto tiempo
aparecer un nuevo obstculo, y en cada nuevo nivel, el movimiento ser un poco
ms rpido (puedes conseguirlo reduciendo la pausa de final del bucle de juego).
(Ap4.7.7) Aade al ejercicio Ap4.7.6 un tabla de mejores puntuaciones, que se
guardar en fichero y se leer al principio de cada nueva partida.
Los nuevos ficheros que necesitamos son: SDL_image.dll (el principal), libpng13.dll
(para imgenes PNG), zlib1.dll (auxiliar para el anterior) y jpeg.dll (si queremos usar
imgenes JPG).
Revisin 0.99zz Pgina 417
Introduccin a la programacin con C#, por Nacho Cabanes
imagen = SdlImage.IMG_Load("personaje.png");
Ejercicios propuestos
(Ap4.8.1) Mejora el juego de la serpiente, para que tenga una imagen JPG de fondo
de la pantalla, y que los premios sean imgenes PNG con transparencia, a travs
de las que se pueda ver dicho fondo.
Ejercicios propuestos
(Ap4.9.1) Crea una versin del juego de la serpiente en la que la lgica est
repartida entre varias clases que colaborar: JuegoSerpiente (que contendr el
Main), Presentacion, PantallaAyuda, PantallaCreditos, Nivel, Personaje, Premio.
Cuando lanzamos Visual Studio 2010, aparece una pantalla que nos muestra los
ltimos proyectos que hemos realizado y nos da la posibilidad de crear un nuevo
proyecto:
posicin. De igual modo, hacemos clic en Label para indicar que queremos una
etiqueta de teto, y luego clic en cualquier punto de nuestro formulario. Nuestra
ventana debera estar quedando as:
Ahora vamos a hacer que nuestro programa responda a un clic del ratn sobre el
botn. Para eso, hacemos doble clic sobre dicho botn, y aparecer un esqueleto
de programa que podemos rellenar:
Antes de ver con ms detalle por qu ocurre todo esto y mejorarlo, vamos a
comprobar que funciona. Pulsamos el botn de "Iniciar depuracin" en la barra de
herramientas:
Una vez que estn colocados, vamos a cambiar sus propiedades. Sabemos
cambiar el texto de un Label desde cdigo, pero tambin podemos hacerlo desde
el diseador visual. Basta con tener la ventana de "Propiedades", que podemos
obtener al pulsar el botn derecho sobre uno de los componentes o desde el
men "Ver". Ah tenemos la propiedad "Text", que es el texto que cada
componente muestra en pantalla. Haciendo clic en cada uno de ellos, podemos
cambiar el texto de todos (incluyendo el "nombre de la ventana"):
Es muy mejorable, pero algunas de las mejoras son parte de los ejercicios
propuestos.
Ejercicios propuestos:
(Ap5.2.1) Mejora el ejemplo anterior para que las casillas tengan el texto alineado
a la derecha y para que no falle si las casillas estn vacas o contienen texto en
lugar de nmeros.
(Ap5.2.2) Crea una nueva versin del programa, que no solo muestre la suma, sino
tambin la resta, la multiplicacin, la divisin y el resto de la divisin.
(Ap5.2.3) Un programa que muestre una ventana con un recuadro de texto, un
botn y 3 etiquetas. En el recuadro de texto se escribir un nmero (en sistema
decimal). Cada vez que se pulse el botn, se mostrar en las 3 etiquetas de texto el
equivalente de ese nmero en binario, octal y hexadecimal.
(Ap5.2.4) Haz una variante del programa anterior, que no use un botn para
convertir el nmero a binario, octal y hexadecimal. En vez de eso, cada vez que en
el recuadro de texto se pulse una tecla, se mostrar automticamente en las 3
etiquetas de texto el equivalente del nmero actual. Pista: al hacer doble clic sobre
una casilla de texto, aparecer el evento "TextChanged", que es el que se lanza
cuando se modifica el texto que contiene un TextBox.
Y la tercera variante permite indicar adems el que ser el botn por defecto:
Ejercicios propuestos:
(Ap5.3.1) Mejora el ejercicio Ap5.2.1, para que muestre un mensaje de aviso si las
casillas estn vacas o contienen texto en lugar de nmeros.
Vamos a crear en ella una casilla de texto (TextBox) en la que no se podr escribir,
sino que slo se mostrarn resultados, y un botn que har que aparezca la
ventana secundaria, en la que s podremos introducir datos.
Para crear el segundo formulario (la ventana auxiliar), usamos la opcin "Agregar
Windows Forms ", del men "Proyecto":
Nos aparecer una nueva ventana, en su vista de diseo. Aadimos dos casillas de
texto (TextBox), con sus etiquetas aclaratorias (Label), y el botn de Aceptar:
Adems, para que desde la ventana principal se puedan leer los datos de sta,
podemos hacer que sus componentes sean pblicos, o, mejor, crear un mtodo
"Get" que devuelva el contenido de estos componentes. Por ejemplo, podemos
devolver el nombre y el apellido como parte de una nica cadena de texto, as:
public MainForm()
{
InitializeComponent();
ventanaIntro = new Introduccion();
}
Ejercicios propuestos
(Ap5.4.1) Crea un programa que descomponga un nmero como producto de sus
factores primos, usando ventanas.
(Ap5.4.2) Crea un programa que muestre una casilla de texto en la que el usuario
puede introducir frases. Todas estas frases se irn guardando en un fichero de
texto.
(Ap5.4.3) Crea una versin "con ventanas" de la "base de
datos de ficheros" (ejemplo 04_06a).
// Dibujamos
ventanaGrafica.DrawLine(contornoRojo, 200, 100, 300, 400);
ventanaGrafica.FillEllipse(rellenoAzul, new Rectangle(0, 0, 200, 300));
Los mtodos para dibujar lneas, rectngulos, elipses, curvas, etc. son parte de la
clase Graphics. Algunos de los mtodos que sta contiene y que pueden ser tiles
para realizar dibujos sencillos son:
Por otra parte, un ejemplo de cmo mostrar una imagen predefinida podra ser:
Esta imagen debera estar en la carpeta del programa ejecutable (que quiz no sea
la misma que el fuente), y puede estar en formato BMP, GIF, PNG, JPG o TIFF.
http://msdn.microsoft.com/es-es/library/system.drawing.graphics_methods.aspx
Ejercicios propuestos
(Ap5.6.1) Crea una versin del "juego del ahorcado" (ejercicio 5.9.1.2) basada en
Windows Forms, en la que, a medida que el usuario falle, se vaya "dibujando un
patbulo".
ndice alfabtico
- @
-, 34 @, 109, 383
[
--, 94 [,] (arrays), 123
[] (arrays), 113
[Serializable], 275
!
!, 52 \
!=, 49
\, 109
#
^
#, 104
^, 356
%
{
%, 34
%=, 96 { y }, 17, 48
& |
&, 356 |, 356
& (direccin de una variable, 330 ||, 52
&&, 52
~
* ~, 214, 356
*, 34
*=, 96 +
+, 34
, ++, 68, 94
,, 364 +=, 96
. <
.Net, 13 <, 49
<<, 356
<> (Generics), 326
/
/, 34 =
/**, 382
=, 37
//, 42
-=, 96
///, 382
==, 49
/=, 96
: >
>, 49
: (goto), 82
>>, 356
? 0
?, 58
0x (prefijo), 106
A Bucles anidados, 69
bug, 369
Abs, 172 burbuja, 145
Acceso aleatorio, 253 Buscar en un array, 118
Acceso secuencial, 253 Button, 422
Acos, 172 byte, 93, 385
Add (ArrayList), 316
AddDays, 337
Al azar, nmeros, 170
C
Aleatorio, acceso, 253 C, 10
Aleatorios, nmeros, 170 C#, 10
algoritmo, 14 Cadena modificable, 138
alto nivel, 9 Cadenas de caracteres, 128
and, 356 Cadenas de texto, 110
Aadir a un array, 118 Cambio de base, 105
Aadir a un fichero, 247 Campo (bases de datos), 290
Apilar, 312 Carcter, 107
Aplicacin Windows, 420 Carpetas, 248
Append, 271 case, 60
AppendText, 247 Caso contrario, 50
Arco coseno, 172 catch, 88, 250
Arco seno, 172
Arco tangente, 172
args, 178 Ch
Argumentos de un programa, 177 char, 60, 107
Aritmtica de punteros, 334
array, 113
Array de objetos, 221 C
ArrayList, 316
cifrar mensajes, 358
Arrays bidimensionales, 122
Cifras decimales, 103
Arrays de struct, 126
Cifras significativas, 99
arreglo, 113
class, 18
ASCII, 387, 389, 400, 420
Clear, 340
Asignacin de valores, 37
Close, 243
Asignacin en un "if", 54
cdigo mquina, 9
asignaciones mltiples, 96
Cdigos de formato, 103
Asin, 172
Cola, 314
Atan, 172
Colisiones entre imgenes, 413
Atan2, 172
Color de texto, 340
azar, 170
Coma (operador, for), 364
Coma fija, 97
B Coma flotante, 97
Comentarios
BackgroundColor, 340 recomendaciones, 378
bajo nivel, 10 Comentarios de documentacin, 381
base, 230 Comillas (escribir), 109
Base numrica, 105 Comparacin de cadenas, 137
Bases de datos, 292 CompareTo, 137
Bases de datos con SQLite, 289 compiladores, 12
BaseStream, 262 Compilar con mono, 31
BASIC, 9 Complemento, 356
Begin (SeekOrigin), 259 Complemento a 1, 398
Binario, 105, 391 Complemento a 2, 398
BinaryReader, 261 Consola, 340
BinarySearch, 318 Console, 17, 340
BinaryWriter, 269 ConsoleKeyInfo, 341
bit, 386 constantes, 358
Bloque de programa, 70 constructor, 212
BMP (tipo de fichero), 263 Contador, 65
bool, 111 Contains, 132, 313, 320
Booleanos, 111, 121 Contains (Hash), 321
Borrar en un array, 118 ContainsKey, 321
borrar la pantalla, 340 ContainsValue, 320
break, 60 continue, 77
bucle de juego, 409 Convert, 93, 100
bucle sin fin, 69 Convert.ToInt32, 43
Bucles, 63 Convertir a binario, 105
Revisin 0.99zz Pgina 440
Introduccin a la programacin con C#, por Nacho Cabanes