Sie sind auf Seite 1von 162

INDICE

1 Primera entrega (Breve introduccin de aproximacin a C#. Descripcin terica de la


programacin orientada a objetos.).......................................................................................1-
1.1 Primera aproximacin a C#...........................................................................................................1-4
1.2 Mtodo a seguir durante todo el curso..........................................................................................1-4
1.3 Programacin orientada a objetos................................................................................................1-4
1.3.1 Clases y objetos...........................................................................................................................................1-5
1.4 os pilares de la P!!" #ncapsulamiento$ %erencia & polimor'ismo..........................................1-(
! "egunda entrega (Bases generales# introduccin a la tecnologia .$%& ' bases sintacticas de
C#.)........................................................................................................................................!-1
2.1 )ases generales.............................................................................................................................2-14
2.2 #l mundo .*#+.............................................................................................................................2-14
2.3 )ases sint,cticas de C#................................................................................................................. 2-1-
( &ercera entrega (%spacios de nombres) clases) indicadores ' el sistema de tipos de C#.). (-1*
3.1 os espacios de nombres..............................................................................................................3-1.
3.2 as clases" unidades b,sicas de estructuramiento.....................................................................3-21
3.3 /ndicadores" 0ariables & constantes.............................................................................................3-21
3.4 #l sistema de tipos de C#..............................................................................................................3-23
Cuarta entrega (+peradores de C#.)....................................................................................-!,
4.1 !peradores.................................................................................................................................... 4-2(
- .uinta entrega ($uestra primera aplicacin en C## /0ola mundo1.)..............................--(2
-.1 *uestra primera aplicain en C#................................................................................................--31
2 "exta entrega (34todos (15 parte)) sobrecarga de m4todos) argumentos por valor ' por
re6erencia ' m4todos static.).................................................................................................2-!
1.1 Mtodos......................................................................................................................................... 1-42
6.1.1 Sobrecarga de mtodos..............................................................................................................................6-45
6.1.2 Argumentos pasados por valor y por reerenc!a........................................................................................6-46
6.1.3 "todos stat!c............................................................................................................................................6-4#
, "4ptima entrega (Constructores) destructores ' el recolector de basura.).........................,-7
(.1 Constructores................................................................................................................................ (--2
(.2 #l recolector de basura & los destructores..................................................................................(--2
* +ctava entrega (Campos ' propiedades.).............................................................................*-2,
1
..1 Campos.......................................................................................................................................... .-1(
..2 Propiedades................................................................................................................................... .-(2
..3 #jercicio 1..................................................................................................................................... .-(-
#.3.1 $!stas para el ejerc!c!o 1 %&ntrega #'.........................................................................................................#-(5
#.3.2 )esoluc!*n del ejerc!c!o............................................................................................................................#-(5
7 $ovena entrega (Control de 6lujo condicional# i6...else i6...else8 s9itc:.)..........................7-*;
3.1 Control de 'lujo" estructuras condicionales................................................................................3-.2
3.2 /nstruccin i'...else i'...else...........................................................................................................3-.1
3.3 /nstruccin s4itc%........................................................................................................................ 3-.1
3.4 #jercicio 2..................................................................................................................................... 3-.3
1; D4cima entrega (Control de 6lujo iterativo# bucles (6or) 9:ile) do)) instrucciones de salto '
recursividad.)......................................................................................................................1;-*7
12.1 Control de 'lujo" estructuras iterati0as.....................................................................................12-.3
12.2 )ucles 'or.................................................................................................................................... 12-32
12.3 )ucles 'or anidados..................................................................................................................... 12-31
12.4 )ucles 4%ile................................................................................................................................. 12-34
12.- )ucles do..................................................................................................................................... 12-31
12.1 /nstrucciones de salto................................................................................................................. 12-3(
12.( a instruccin brea5.................................................................................................................. 12-3(
12.. a instruccin continue..............................................................................................................12-3.
12.3 6er mardito goto6....................................................................................................................... 12-33
12.12 7ecursi0idad............................................................................................................................. 12-121
12.11 #ercicio 3................................................................................................................................... 12-122
1+.11.1 $!stas para el ejerc!c!o 3 %&ntrega 1+'...................................................................................................1+-1+3
1+.11.2 )esoluc!*n del ejerc!c!o........................................................................................................................1+-1+3
11 <nd4cima entrega (=rra's.).............................................................................................11-1;*
11.1 8rra&s........................................................................................................................................ 11-12.
11.2 8rra&s multidimensionales.......................................................................................................11-112
11.3 8rra&s de arra&s....................................................................................................................... 11-113
1! Duod4cima entrega (>ndi?adores) sobrecarga de operadores ' conversiones de6inidas.)1!-11(
12.1 /ndi9adores................................................................................................................................ 12-113
12.2 :obrecarga de operadores........................................................................................................12-113
12.3 Con0ersiones de'inidas.............................................................................................................12-113
12.4 #jercicio 4.................................................................................................................................. 12-113
2
12.4.1 $!stas para el ejerc!c!o 4 %&ntrega 12'...................................................................................................12-113
12.4.2 )esoluc!*n del ejerc!c!o.........................................................................................................................12-113
12.- #jercicio -.................................................................................................................................. 12-113
1( &rig4sima entrega (%structuras8 3@s sobre las clases8 0erencia e >nter6aces.)............1(-11(
13.1 #structuras................................................................................................................................ 13-113
13.2 as clases en pro'undidad........................................................................................................13-113
13.3 ;erencia.................................................................................................................................... 13-113
13.4 /nter'aces................................................................................................................................... 13-113
13.- #jercicio 1.................................................................................................................................. 13-113
13.5.1 $!stas para el ejerc!c!o 6 %&ntrega 13'...................................................................................................13-113
13.5.2 )esoluc!*n del ejerc!c!o.........................................................................................................................13-113
13.1 #jercico (................................................................................................................................... 13-113
13.6.1 $!stas para el ejerc!c!o ( %&ntrega 13'...................................................................................................13-113
13.6.2 )esoluc!*n del ejerc!c!o.........................................................................................................................13-113
3
1 Primera entrega (Breve introduccin de aproximacin a C#.
Descripcin terica de la programacin orientada a obetos.!
1.1 Primera aproximacin a C#
Antes de nada, quiero que sepas que hasta ahora soy programador de Visual Basic, y la curiosidad me ha
llevado a interesarme por el nuevo C#, de modo que, bsicamente, me vas a acompaar durante todo mi
proceso de aprendi!a"e# $o es que vaya a escribir cosas sin estar seguro de ellas, estoy bien documentado,
sino que puede que encuentres algo de c%digo que, con el tiempo, te des cuenta de que se pod&a haber
me"orado#
'e dir( que, poco a poco, C# ha ido superando con creces todas mis e)pectativas* es un lengua"e moderno,
potente, +le)ible y orientado a ob"etos# $o te puedo decir nada comparndolo con ,ava ni con C--, porque,
bsicamente, tengo muy poquita idea de c%mo son estos lengua"es# $o obstante, s& te puedo decir que, en una
de mis muchas incursiones por la .eb en busca de in+ormaci%n sobre este lengua"e encontr( el siguiente
prra+o*
/0uchos dicen que si ,ava se puede considerar un C-- me"orado en cuestiones de seguridad y portabilidad,
C# debe entenderse como un ,ava me"orado en todos los sentidos* desde la e+iciencia hasta la +acilidad de
integraci%n con aplicaciones tan habituales como 0icroso+t 1++ice o Corel 2ra.#3 45l rinc%n en espaol de C#,
http*66mano.ar#lsi#us#es67csharp68
9or lo poco que yo s( sobre ,ava y C--, y lo que he le&do en diversa documentaci%n, creo que esta
descripci%n se a"usta bastante a la realidad# :o que s& te puedo asegurar con toda certe!a es que C# combina la
rapide! de desarrollo de Visual Basic con la enorme capacidad bruta de C--#
1.2 Mtodo a seguir durante todo el curso
5mpe!aremos con una breve introducci%n a la programaci%n orientada a ob"etos y la tecnolog&a #$5', y
posteriormente iremos ya con la programaci%n en C# propiamente dicha#
;eguramente pienses al principio que todas las e)celencias que te cuento de la programaci%n orientada a
ob"etos vienen a ser una patraa, puesto que al +inal sigues teniendo que programar todo lo que el programa
tiene que hacer# ;in embargo te aconse"o que tengas un poco de paciencia* cuando empecemos a desarrollar
aplicaciones para <indo.s vers que no te engaaba, pues al desarrollar programas para <indo.s es cuando
se ve que casi todo est hecho 4las ventanas, los botones, las ca"as de te)to, cuadros de dilogo ###8 y
solamente hay que usarlo sin ms#
$o obstante he pre+erido de"ar el desarrollo de aplicaciones para <indo.s al +inal, puesto que de lo
contrario, con tantos ob"etos, propiedades y eventos hubiera sido mucho ms complicado hacer que
comprendieras este lengua"e# 9or este motivo, empe!aremos desarrollando pequeos programas de consola
para que puedas irte +amiliari!ando c%modamente con la sinta)is, sin otras distracciones#
1.3 Programacin orientada a objetos
Bien, vamos all# ;i conoces bien la programaci%n orientada a ob"etos, puedes pasar adelante# 2e lo
contrario te recomiendo que hagas una lectura lenta y cuidadosa de lo que viene a continuaci%n, pues es bsico
para despu(s comprender c%mo +unciona el lengua"e C## :os conceptos estn ilustrados con c%digo de C## ;i
no entiendes dicho c%digo no desesperes, ya que el ob"etivo de esta introducci%n es que comprendas dichos
conceptos, y no el c%digo#
:a programaci%n orientada a ob"etos es algo ms que /el =ltimo grito en programaci%n3# $o se trata de una
moda, sino de un modo de traba"o ms natural, que te permite centrarte en solucionar el problema que tienes
que resolver en lugar de tener que andar pensando en c%mo le digo al ordenador que haga esto o lo otro# ;i
>
alguna ve! utili!aste alg=n lengua"e de los del /ao la polca3 me comprenders enseguida# 5l ?@A del c%digo
estaba dedicado a comunicarte con el ordenador 4que si disear la pantalla, que si reservar memoria, que si el
monitor me aguanta esta resoluci%n###8, y el otro 1@A a resolver el problema# Ba no digamos si alguna ve! has
hecho, o intentado, alg=n programa para <indo.s usando C en ba"o nivel# :a programaci%n orientada a ob"etos
4911 en adelante8 te abstrae de muchas de estas preocupaciones para que puedas dedicarte a escribir
realmente el c%digo =til, es decir, resolver el problema y ya est# Veamos un e"emplo muy claro de lo que quiero
decir*
Cmagina hacer un programa que mantenga una base de datos de personas# ;imple y llanamente# DC%mo era
esto antesE F,AG F,A,AG Hecoge los datos, abre el archivo, de+ine la longitud del registro, de+ine la longitud y el
tipo de cada campo, pon cada campo en su sitio, guarda el registro en el lugar del archivo donde le corresponde
y cierra el archivo# 2espu(s, para una b=squeda, recoge los datos a buscar, abre el archivo, busca los datos,
cierra el archivo, presenta los resultados# ;i adems permites modi+icaciones, recoge los nuevos datos, vuelve
a abrir el archivo, guarda los datos modi+icados en el registro que le corresponde, cierra el archivo### 9esado,
DehE Ciertamente# :a mayor parte del tiempo la dedicbamos a comunicarnos con el ordenador# DC%mo ser&a
esto con un lengua"e orientado a ob"etos, como C#E 0ucho ms sencillo# 'enemos un ob"eto 9ersona# 9ara
agregar un registro, sencillamente habr&a que dar los valores a dicho ob"eto y decirle que los guarde# Ba est#
$os da igual c%mo haga el ob"eto 9ersona para guardar# Vemoslo*
Persona.Nombre = Pepe
Persona.Apellido = Pepe (otra vez, hala)
Persona.Direccin = la direccin que sea
Persona.Guardar
DB para buscarE 9ues, por e"emplo*
Persona.uscar(!anolo)
;i lo encuentra, las propiedades $ombre, Apellido y 2irecci%n ya se habr&an rellenado con los datos del tal
0anolo# DC%mo lo ha hecho el ob"eto 9ersonaE FIu( ms daG 5sto es lo verdaderamente =til de la 911, ya que
no tienes que preocuparte de c%mo el ob"eto hace su traba"o# ;i est bien construido y +unciona no tienes que
preocuparte de nada ms, sino simplemente de usarlo seg=n tus necesidades#
;i lo piensas un poco, no se trata de un sistema arbitrario, o de una invenci%n particular de alg=n iluminado#
9ongamos por e"emplo que, en lugar de disear un programa, ests conduciendo un coche# DIu( esperas que
suceda cuando pisas el aceleradorE 9ues esperas que el coche acelere, claro# Ahora bien, c%mo haga el coche
para decirle al motor que aumente de revoluciones te trae sin cuidado# 5n realidad, da igual que haya un
mecanismo mecnico mediante un cable, o un mecanismo electr%nico, o si deba"o del cap% hay un burro y al
pisar el acelerador se introduce una guindilla por el sito que ms le pueda escocer al desdichado animal#
Adems, esto nos lleva a otra gran venta"a* 9or mucho que avance la tecnolog&a, el modo de conducir un coche
siempre es el mismo, ya que lo =nico que cambia es el mecanismo interno, no la inter+a! que te o+rece# 5sto
mismo es aplicable a los ob"etos en programaci%n* por mucho que cambien las versiones de los ob"etos para
hacerlos ms e+icientes, estos siempre o+recern la misma inter+a!, de modo que podrs seguir utili!ndolos sin
necesidad de hacer modi+icaci%n alguna cuando apare!ca una nueva versi%n del ob"eto#
1.3.1 Clases y objetos
Ba hemos visto algunas de las principales venta"as de la 911# Vamos a entrar ahora en ms detalles* qu(
son las clases, qu( son los ob"etos y en qu( se di+erencian#
A menudo es +cil con+undir ambos t(rminos# DAmbas cosas son igualesE $o, ni mucho menos, aunque
estn &ntimamente relacionados# 9ara que pueda haber un ob"eto debe e)istir previamente una clase, pero no al
rev(s# 0e e)plico* la clase es la /plantilla3 en la que nos basamos para crear el ob"eto# Volvamos al e"emplo del
coche* todos ellos tienen una serie de caracter&sticas comunes* todos tienen un motor, ruedas, un volante,
J
pedales, chasis, carrocer&a###K todos +uncionan de un modo parecido para acelerar, +renar, meter las marchas,
dar las luces###K sin embargo, cada uno de ellos es di+erente de los dems, puesto que cada uno es de su
marca, modelo, color, n=mero de bastidor###, propiedades que lo di+erencian de los dems, aunque una o varias
de ellas puedan coincidir en varios coches# 2ir&amos entonces que todos los coches estn basados en una
plantilla, o un tipo de ob"eto, es decir, pertenecen todos a la misma clase* la clase coche# ;in embargo, cada
uno de los coches es un ob"eto de esa clase* todos comparten la /inter+a!3, pero no tienen por qu( compartir los
datos 4marca, modelo, color, etc8# ;e dice entonces que cada uno de los ob"etos es una instancia de la clase a
la que pertenece, es decir, un ob"eto# 5n resumen, la clase es algo gen(rico 4la idea que todos tenemos sobre lo
que es un coche8 y el ob"eto es algo mucho ms concreto 4el coche del vecino, el nuestro, el papam%vil###8#
Veamos c%mo ser&a esto en C## 5l diseo de la clase Coche ser&a algo parecido a esto 4aunque ms ampliado8*
class "oche#
public "oche(strin$ marca, strin$ modelo, strin$ color, strin$ numbastidor)#
this.!arca=marca%
this.!odelo=modelo%
this."olor=color%
this.Numastidor=numbastidor%
&
public double 'elocidad#
$et#
return this.velocidad%
&
&
protected double velocidad=(%
public strin$ !arca%
public strin$ !odelo%
public strin$ "olor%
public strin$ Numastidor%
public void Acelerar(double cantidad)#
)) Aqu* se le dice al motor que aumente las revoluciones pertinentes, +...
"onsole.,rite-ine(./ncrementando la velocidad en #(& 0m)h1, cantidad)%
this.velocidad 2= cantidad%
&
public void Girar(double cantidad)#
)) Aqu* ir*a el cdi$o para $irar
"onsole.,rite-ine(.Girando el coche #(& $rados1, cantidad)%
&
public void 3renar(double cantidad)#
)) Aqu* se le dice a los 4renos que act5en, +...
L
"onsole.,rite-ine(.6educiendo la velocidad en #(& 0m)h1, cantidad)%
this.velocidad 7= cantidad%
&
&
'eamos una clase con un m8todo !ain para ver cmo se comportar*a esta clase9
class :;emplo"ocheApp#
static void !ain()#
"oche !i"oche=ne< "oche(.Peu$eot1, .=(>1, .Azul1,1?@A>BC>1)%
"onsole.,rite-ine(.-os datos de mi coche son91)%
"onsole.,rite-ine(.!arca9 #(&1, !i"oche.!arca)%
"onsole.,rite-ine(.!odelo9 #(&1, !i"oche.!odelo)%
"onsole.,rite-ine(."olor9 #(&1, !i"oche."olor)%
"onsole.,rite-ine(.N5mero de bastidor9 #(&1, !i"oche.Numastidor)%
!i"oche.Acelerar(?(()%
"onsole.,rite-ine(.-a velocidad actual es de #(& 0m)h1,!i"oche.'elocidad)%
!i"oche.3renar(C@)%
"onsole.,rite-ine(.-a velocidad actual es de #(& 0m)h1,!i"oche.'elocidad)%
!i"oche.Girar(A@)%
&
&
5l resultado que aparecer&a en la consola al e"ecutar este programa ser&a este*
-os datos de mi coche son los si$uientes9
!arca9 Peu$eot
!odelo9 =(>
"olor9 Azul
N5mero de bastidor9 ?@A>BC>
/ncrementando la velocidad en ?(( 0m)h
-a velocidad actual es de ?(( 0m)h
6educiendo la velocidad en C@ 0m)h
-a velocidad actual es de D@ 0m)h
Girando el coche A@ $rados
$o te preocupes por no entender todo el c%digo todav&a, pues ya hablaremos largo y tendido de la sinta)is#
;%lo quiero que te +i"es en que en la clase es donde se de+inen todos los datos y se programan todas las
acciones que han de mane"ar los ob"etos de esta clase# :os datos son Velocidad, 0arca, 0odelo, Color y
$umBastidor, y los m(todos son Acelerar, Mirar y Nrenar# ;in embargo, el ob"eto, 0iCoche 4creado en la primera
l&nea del m(todo 0ain8 no de+ine absolutamente nada# ;implemente usa la inter+a! diseada en la clase 4la
O
inter+a! de una clase es el con"unto de m(todos y propiedades que esta o+rece para su mane"o8# 9or lo tanto,
Coche es la clase y 0iCoche un ob"eto de esta clase#
1.4 os pilares de la P!!" #ncapsulamiento$ %erencia y polimor&ismo
A partir de aqu& leers constantes re+erencias al /cliente3# ;i no sabes qu( es yo te lo aclaro* no, no es todo
aquel que va comprar algo# FC(ntrate, hombre, que estamos en programaci%nG Cuando hable del cliente de una
clase me estoy re+iriendo al c%digo que est usando esa clase, es decir, instancindola o invocando m(todos de
la misma, independientemente de si este c%digo +orma parte del mismo programa o de otro distinto, aun escrito
en otro lengua"e# Iu(date con esto porque te vas a hartar de verlo#
DIu( es eso del encapsulamientoE 9odr&amos de+inirlo como la capacidad que tienen los ob"etos de ocultar
su c%digo al cliente y proteger sus datos, o+reciendo =nica y e)clusivamente una inter+a! que garanti!a que el
uso del ob"eto es el adecuado#
:a ocultaci%n del c%digo es algo evidente* cuando se invoca el m(todo Acelerar del ob"eto 0iCoche, lo =nico
que sabemos es que el coche acelerar, pero el c%mo lo haga es algo que no podremos ver desde el cliente# 5n
cuanto a la protecci%n de datos, +&"ate tambi(n en un detalle del e"emplo* no podr&amos modi+icar directamente
el valor de la propiedad Velocidad, dado que est de+inida como propiedad de s%lo lectura# :a =nica +orma de
modi+icar su valor ser&a invocar los m(todos Acelerar y6o Nrenar# 5sta importante caracter&stica asegura que los
datos de los ob"etos pertenecientes a esta clase se van a mane"ar del modo adecuado#
!i"oche.'elocidad=?((% )) :sto provocar*a un error. 'elocidad es de slo lectura
!i"oche.Acelerar(?(()%
"onsole.,rite-ine(!i"oche.'elocidad)%
;i el coche estaba parado antes de invocar el m(todo Acelerar, el programa escribir&a 1@@ en la consola#
Adems de la gran venta"a de la protecci%n de datos nos encontramos con otra no menos estimable* la
portabilidad del c%digo# Pna ve! diseada la clase podremos usarla en tantos programas como la necesitemos,
sin necesidad de volver a escribirla# 9uede que alguno me diga* /bueno, yo ya pod&a usar procedimientos
escritos anteriormente en mis programas hechos en el lengua"e Q3 4donde pone Q p%ngase C, 9ascal, Basic o
$C;P8# Claro que s&, esto pod&a hacerse ya con la programaci%n procedimiental# $o obstante, este modo de
programar conlleva una serie de de+iciencias intr&nsecas* cada +unci%n est completamente aislada de los datos
que vamos a usar con ella, de modo que, por e"emplo, para acelerar habr&a que pasarle no s%lo cunto
queremos acelerar, sino tambi(n la velocidad actual, y dicha +unci%n tendr&a que devolvernos como resultado la
nueva velocidad alcan!ada# 2icho resultado tendr&amos que almacenarlo en una variable que, por decirlo de
alg=n modo, est tambi(n completamente aislada y, adems, desprotegida, pudiendo esta ser modi+icada sin
intenci%n en otra l&nea del programa 4usando por error el operador de asignaci%n R en lugar del de comparaci%n
RR, por e"emplo8, generando as& errores di+&ciles de rastrear 4puesto que la variable no contiene el valor
adecuado8, ya que el compilador lo permite y no arro"a ning=n mensa"e de error# 5sto ser&a imposible con la
propiedad Velocidad del ob"eto coche, pues si se intentara modi+icar directamente en alguna parte el c%digo, el
compilador arro"ar&a un mensa"e de error, avisando de que la propiedad no se puede modi+icar pues es de s%lo
lectura, error que por otro lado es muy +cil de locali!ar 4de hecho te lo locali!a el compilador8# Como ves, la
911 solventa todas estas de+iciencias gracias al encapsulamiento, proporcionndote as& un modo natural,
seguro y sencillo de traba"ar#
1tro de los pilares bsicos de la 911 es la herencia# Mracias a ella podemos de+inir clases nuevas basadas
en clases antiguas, aadi(ndoles ms datos o ms +uncionalidad# 9ara ver esto ms claro sigamos con el
e"emplo del coche# Cmaginemos que la clase Coche o+rece una inter+a! bsica para cualquier tipo de coche# ;in
embargo queremos un coche que, adems de todo lo que tienen los dems coches, es capa! de aparcar (l
solito, sin necesidad de que nosotros andemos haciendo maniobras# D'endr&amos que de+inir otra clase para
incorporar esta nueva capacidadE 9ues no# 9odemos heredar todos los miembros de la clase Coche y despu(s
agregarle lo que deseemos en la nueva clase*
S
class "ocheAparcador9"oche#
public "ocheAparcador(strin$ marca, strin$ modelo, strin$ color, strin$ numbastidor)9 base(marca,
modelo, color, numbastidor) #&
public void Aparcar()#
)) Aqu* se escribe el cdi$o para que el coche aparque solo
"onsole.,rite-ine(.Aparcando el coche de modo automEtico1)%
this.velocidad = (%
&
&
DIu( ha pasadoE D2%nde estn todos los dems miembros de la claseE Aunque pare!ca mentira, estn# :a
clase CocheAparcador ha heredado todos los miembros de su clase base 4Coche8# :o =nico que ha aadido ha
sido el m(todo Aparcar, de modo que cualquier ob"eto de la clase CocheAparcador 4o"o, no de la clase Coche8
tendr todos los miembros de la clase Coche ms el m(todo Aparcar incorporado en la clase derivada
CocheAparcador# DB c%mo se instancian ob"etos de una clase derivadaE 9ues e)actamente igual que si se
instanciara de cualquier otra clase# Vemoslo con el e"emplo anterior, modi+icando ligeramente el m(todo 0ain*
class :;emplo"ocheApp#
static void !ain()#
"ocheAparcador !i"oche=ne< "ocheAparcador(.Peu$eot1, .=(>1, .Azul1,1?@A>BC>1)%
"onsole.,rite-ine(.-os datos de mi coche son91)%
"onsole.,rite-ine(.!arca9 #(&1, !i"oche.!arca)%
"onsole.,rite-ine(.!odelo9 #(&1, !i"oche.!odelo)%
"onsole.,rite-ine(."olor9 #(&1, !i"oche."olor)%
"onsole.,rite-ine(.N5mero de bastidor9 #(&1, !i"oche.Numastidor)%
!i"oche.Acelerar(?(()%
"onsole.,rite-ine(.-a velocidad actual es de #(& 0m)h1,!i"oche.'elocidad)%
!i"oche.3renar(C@)%
"onsole.,rite-ine(.-a velocidad actual es de #(& 0m)h1,!i"oche.'elocidad)%
!i"oche.Girar(A@)%
!i"oche.Aparcar()%
strin$ a="onsole.6ead-ine()%
&
&
:as modi+icaciones sobre el anterior estn en negrilla# Ahora, el resultado en la consola ser&a este*
-os datos de mi coche son los si$uientes9
!arca9 Peu$eot
!odelo9 =(>
?
"olor9 Azul
N5mero de bastidor9 ?@A>BC>
/ncrementando la velocidad en ?(( 0m)h
-a velocidad actual es de ?(( 0m)h
6educiendo la velocidad en C@ 0m)h
-a velocidad actual es de D@ 0m)h
Girando el coche A@ $rados
Aparcando el coche de modo automEtico
Ahora, el ob"eto 0iCoche tiene los mismos miembros que ten&a cuando era de la clase Coche ms el m(todo
Aparcar implementado por la clase derivada CocheAparcador#
B entonces, Dpodr&a construir clases ms comple"as a partir de otras clases ms sencillasE Tombre, este es
el ob"etivo principal de la herencia# $o obstante, C# soporta la herencia simple, pero no la herencia m=ltiple# 9or
lo tanto, en C# podemos construir una clase derivada a partir de otra clase, pero no de varias clases# ;obre este
aspecto, lo ideal para construir una clase coche hubiera sido construir clases ms sencillas 4ruedas, motor,
chasis, carrocer&a, volante, ###8, y despu(s construir la clase coche derivndola de todas ellas*
class "oche96uedas, !otor, "hasis, "arrocer*a, 'olante )):rror. "F no soporta herencia m5ltiple
;in embargo ya digo que esto no es posible en C## Pna clase puede derivarse de otra, pero no de varias# ;&
se puede derivar una clase de otra clase y varias inter+aces, pero de esto hablaremos ms adelante, cuando
tratemos las inter+aces#
5l polimor+ismo es otra de las maravillas que incorpora la 911# DIu( ocurre si, siguiendo con el manido
e"emplo de los coches, cada coche ha de comportarse de un modo di+erente dependiendo de su marca, esto es,
si es un 9eugeot, por e"emplo, el acelerador acciona un cable, pero si es un VolUs.agen, el acelerador acciona
un mecanismo electr%nicoE# Bien, alguien acostumbrado a la programaci%n procedimental dir* /5so est
chupao# Basta con un ;.itch3# Bien, vemoslo*
class "oche#
public "oche(strin$ marca, strin$ modelo, strin$ color, strin$ numbastidor)#
this.!arca=marca%
this.!odelo=modelo%
this."olor=color%
this.Numastidor=numbastidor%
&
public double 'elocidad#
$et#
return this.velocidad%
&
&
protected double velocidad=(%
public strin$ !arca%
1@
public strin$ !odelo%
public strin$ "olor%
public strin$ Numastidor%
public void Acelerar(double cantidad)#
s<itch this.!arca#
case .Peu$eot19
)) Aqu* acciona el mecanismo de aceleracin de los Peu$eot...
"onsole.,rite-ine(.Accionando el mecanismo de aceleracin del Peu$eot1)%
brea0%
case .'ol0s<a$en19
)) Aqu* acciona el mecanismo de aceleracin de los 'ol0s<a$en...
"onsole.,rite-ine(.Accionando el mecanismo de aceleracin del 'ol0s<a$en1)%
brea0%
case .Geat19
)) Aqu* acciona el mecanismo de aceleracin de los Geat...
"onsole.,rite-ine(.Accionando el mecanismo de aceleracin del Geat1)%
brea0%
de4ault9
)) Aqu* acciona el mecanismo de aceleracin por de4ecto...
"onsole.,rite-ine(.Accionando el mecanismo de aceleracin por de4ecto1)%
brea0%
&
"onsole.,rite-ine(./ncrementando la velocidad en #(& 0m)h1)%
this.velocidad 2= cantidad%
&
public void Acelerar(double cantidad)#
)) Aqu* se le dice al motor que aumente las revoluciones pertinentes, +...
"onsole.,rite-ine(./ncrementando la velocidad en #(& 0m)h1, cantidad)%
this.velocidad 2= cantidad%
&
public void Girar(double cantidad)#
)) Aqu* ir*a el cdi$o para $irar
"onsole.,rite-ine(.Girando el coche #(& $rados1, cantidad)%
&
11
public void 3renar(double cantidad)#
)) Aqu* se le dice a los 4renos que act5en, +...
"onsole.,rite-ine(.6educiendo la velocidad en #(& 0m)h1, cantidad)%
this.velocidad 7= cantidad%
&
&
F0uy bienG DB si aparece una marca nueva con un mecanismo di+erente, machoteE V5stoooo, bueno###
pueees### se aade al s.itch y ya est#V FBuena respuestaG 5ntonces, habr&a que buscar el c%digo +uente de la
clase Coche, y hacer las modi+icaciones oportunas, DnoE V9ues s&, claroV Bien# Cmag&nate ahora que la clase
Coche no es una clase en programaci%n, sino una clase de verdad, o sea, coches de verdad# ;i se crea un
nuevo sistema de aceleraci%n, Dtienen que buscar el manual de reparaci%n del coche, modi+icarlo para
contemplar el nuevo sistema y despu(s redistribuirlo otra ve! todo entero a todo el mundoE Claro que no# :o
que se hace es, simplemente, escribir un nuevo manual =nicamente con las innovaciones y distribuir esta parte
a aquellos que lo vayan a necesitar para que se aada a lo que ya e)iste, ni ms ni menos# 9ues esto es, ms o
menos, lo que proporciona el polimor+ismo en la 911# $o es necesario modi+icar el c%digo de la clase original#
;i esta est bien diseada, basta con derivar otra clase de la original y modi+icar el comportamiento de los
m(todos necesarios# Claro, para esto la clase Coche deber&a estar bien construida# Algo como esto*
class "oche#
public "oche(strin$ marca, strin$ modelo, strin$ color, strin$ numbastidor)#
this.!arca=marca%
this.!odelo=modelo%
this."olor=color%
this.Numastidor=numbastidor%
&
public double 'elocidad#
$et#
return this.velocidad%
&
&
protected double velocidad=(%
public strin$ !arca%
public strin$ !odelo%
public strin$ "olor%
public strin$ Numastidor%
public virtual void Acelerar(double cantidad)#
)) Aqu* se le dice al motor que aumente las revoluciones pertinentes, +...
"onsole.,rite-ine(.Accionando el mecanismo de aceleracin por de4ecto1)%
"onsole.,rite-ine(./ncrementando la velocidad en #(& 0m)h1, cantidad)%
12
this.velocidad 2= cantidad%
&
public virtual void Girar(double cantidad)#
)) Aqu* ir*a el cdi$o para $irar
"onsole.,rite-ine(.Girando el coche #(& $rados1, cantidad)%
&
public virtual void 3renar(double cantidad)#
)) Aqu* se le dice a los 4renos que act5en, +...
"onsole.,rite-ine(.6educiendo la velocidad en #(& 0m)h1, cantidad)%
this.velocidad 7= cantidad%
&
&
N&"ate un poquito en los cambios con respecto a la que hab&amos escrito en primer lugar* se ha aadido la
palabra virtual en las declaraciones de los tres m(todos# D9ara qu(E 9ara que las clases derivadas puedan
sobrescribir el c%digo de dichos m(todos en caso de que alguna de ellas lo necesite porque haya cambiado el
mecanismo# N&"ate bien en c%mo lo har&a una clase que sobrescribe el m(todo Acelerar porque utili!a un
sistema distinto al de la clase Coche*
class "ocheAceleradorAvanzado9"oche#
public "ocheAceleradorAvanzado(strin$ marca, strin$ modelo, strin$ color, strin$ numbastidor)9
base(marca, modelo, color, numbastidor) #&
public override void Acelerar(double cantidad)#
)) Aqu* se escribe el nuevo mecanismo de aceleracin
"onsole.,rite-ine(.Accionando el mecanismo avanzado de aceleracin1)%
"onsole.,rite-ine(./ncrementando la velocidad en #(& 0m)h1, cantidad)%
this.velocidad 2= cantidad%
&
&
Ba est# :a clase base queda intacta, es decir, no hay que modi+icar absolutamente nada# :a clase derivada
=nicamente sobrescribe aquello que no le sirve de la clase base, que es en este caso el m(todo acelerar# N&"ate
que para poder hacerlo hemos puesto la palabra override en la declaraci%n del m(todo# 9ero puede que alguno
piense* /Vamos a ver si yo me aclaro# 5n ese caso, en la clase derivada habr&a dos m(todos Acelerar* uno el
derivado y otro el sobrescrito que, adems, tienen los mismos argumentos# DC%mo sabr el compilador cul de
ellos ha de e"ecutarE3 5l compilador siempre e"ecuta el m(todo sobrescrito si el ob"eto pertenece a la clase
derivada que lo sobrescribe# 5s como si eliminara completamente el m(todo virtual de la clase derivada,
sustituy(ndolo por el nuevo# Veamos un e"emplo*
"ocheAceleradorAvanzado !i"oche%
...
!i"oche = ne< "ocheAceleradorAvanzado(.Peu$eot1, .=(>1, .Azul1, .@A>>BCAD>=@1)%
13
!i"oche.Acelerar(?(()%
5n este caso, est muy claro# 5l ob"eto 0iCoche est declarado como un ob"eto de la clase
CocheAceleradorAvan!ado, de modo que al e"ecutar el m(todo acelerar se e"ecutar sin problemas el m(todo
de la clase derivada# 9or lo tanto, la salida por pantalla de este +ragmento ser&a*
Accionando el mecanismo avanzado de aceleracin
/ncrementando la velocidad en ?(( 0m)h
Gin embar$o, este otro e;emplo puede ser mEs con4uso9
"oche !i"oche%
...
!i"oche = ne< "ocheAceleradorAvanzado(.Peu$eot1, .=(>1, .Azul1, .@A>>BCAD>=@1)%
!i"oche.Acelerar(?(()%
Pn momento, un momento# Aqu& el ob"eto 0iCoche est declarado como un ob"eto de la clase Coche y, sin
embargo, se instancia como ob"eto de la clase CocheAceleradorAvan!ado# DCul de los dos m(todos e"ecutar
ahoraE 2e nuevo e"ecutar el m(todo de la clase derivada, como en el caso anterior# D5ntonces, para qu(
diantres has declarado el ob"eto 0iCoche como un ob"eto de la clase CocheE ;encillo* pudiera ser que yo sepa
que voy a necesitar un ob"eto que ser un coche, pero en el momento de declararlo no s( si ser un coche
normal o uno de acelerador avan!ado# 9or este motivo, tengo que declararlo como ob"eto de la clase Coche#
;in embargo, ms adelante sabr( qu( tipo de coche tengo que crear, por lo que instanciar( el que necesite#
Mracias al polimor+ismo no tendr( que preocuparme de decirle que e"ecute un m(todo u otro, ya que el
compilador e"ecutar siempre el que le corresponda seg=n la clase a la que pertene!ca# :a salida por pantalla
en este caso ser&a, por lo tanto, e)actamente la misma que en el caso anterior#
5l polimor+ismo, en resumen, o+rece la posibilidad de que varios ob"etos que comparten la misma inter+a!, es
decir, que estn +ormados por los mismos miembros, se comporten de un modo distinto unos de otros#
Bueno, creo que ya est bien de conceptos# Aunque pare!ca mentira, hoy has dado un paso crucial para
entender y aprender a utili!ar este nuevo lengua"e, dado que en C# todo, hasta los tipos de datos de toda la
vida, son ob"etos 4bueno, todo, lo que se dice todo, no* los punteros no son ob"etos, pero hablaremos de ellos
cuando lleguemos al c%digo inseguro### todo se andar8#
" #egunda entrega (Bases generales$ introduccin a la tecnologia .NE% &
bases sintacticas de C#.!
2.1 'ases generales
Bueno, lamento tener que comunicarte que todav&a no podemos empe!ar con el lengua"e C# propiamente
dicho 4ya me gustar&a, ya8# Antes quiero comentarte un poco c%mo +unciona todo esto, ms que nada para que
te hagas una idea clara de c%mo +uncionar un programa hecho en C##
2.2 #l mundo .(#)
Healmente, el concepto de #$5' es demasiado amplio, as& que tratar( de resumirlo en unas pocas palabras#
Vamos a verK hasta ahora, lo que conocemos de Cnternet y la in+ormtica es que cada uno de los dispositivos,
cada uno de los programas y cada una de las pginas <eb estn completamente separadas del resto desde el
punto de vista +uncional# ;&, s&, hay v&nculos de unas pginas a otras y cosas de estas, pero eso, para que me
entiendas, es como leer un libro y mirar las notas al pie de pginaK para acceder a la +uente de esas notas
tienes que ver otro libro# 5l ob"etivo de la plata+orma #$5' es que todos estos dispositivos y todas estas pginas
traba"en de un modo con"unto, o+reciendo as& una in+ormaci%n mucho ms =til a los usuarios# ;er&a como si un
1>
libro en lugar de tener notas a pie de pgina tuviera dentro de (l todos los otros libros a los que hace re+erencia
y el lector pudiera e)traer de todos ellos cualquier cosa que necesitara, sin necesidad de tener que buscar
manualmente cada uno de ellos# Nascinante, DnoE
#$5' est +ormado por cuatro componentes principales, pero lo que nos interesa aqu& es una parte de la
in+raestructura #$5', en la que estn encuadrados tanto Visual ;tudio#$5' como el #$5' Nrame.orU#
5l #$5' Nrame.orU est compuesto por el C:H 4Common :anguage Huntime8 y la biblioteca de clases
del #$5' Name.orU# 5l C:H es un entorno de e"ecuci%n en el que aplicaciones escritas en di+erentes lengua"es
pueden traba"ar "untas, es decir, puedes crear una clase en C#, derivar de ella otra en C-- e instanciar un
ob"eto de esta en Visual Basic# $o, no me digis que venga ya# 5s cierto# 5sto se consigue a trav(s de lo que
se denomina c%digo gestionado, es decir, el c%digo que se e"ecuta ba"o el C:H est siendo gestionado por (ste,
independientemente del lengua"e en el que se haya escrito# $o, no se trata de un int(rprete, ni de un
intermediario# ;e trata de que el c%digo gestionado asegura que se cumplen una serie de reglas para que todas
las aplicaciones se comporten de un modo uni+orme# DB se pueden crear compiladores de c%digo gestionado
por el C:H para otros lengua"esE ;&, de hecho ya hay varios o, al menos, se ten&a la intenci%n de hacerlos,
adems de los que se incluyen con Visual ;tudio #$5'# 'odo lo que tienen que hacer estos compiladores es
a"ustarse a las reglas del C:; 4Common :anguage ;peci+ication8, pero eso es para los que disean
compiladores, o sea que, de momento, t= y yo podemos olvidarnos del tema#
:a biblioteca de clases del #$5' Nrame.orU es, como su propio nombre indica, una biblioteca de clases###
4Vaya descubrimiento, DehE8 Vaaaale### me e)plicar( me"or# ;i has programado en C-- conocers la 0NC
40icroso+t Noundation Classes8, o la 1<: de Borland 41b"ect <indo.s :ibrary8# ;i no eres programador de C--
y6o no las conoces, pues me has hecho la pueta### A volver a empe!ar# Veamos, la biblioteca de clases del
#$5' Nrame.orU te o+rece un con"unto de clases base com=n para todos los lengua"es de c%digo gestionado# 1
sea, si, por e"emplo, quieres escribir algo en la pantalla, en Visual Basic ser&a as&*
"onsole.,rite-ine(.Al$o1)
:n "F ser*a as*9
"onsole.,rite-ine(.Al$o1)%
:n "22 $estionado ser*a as*9
"onsole99,rite-ine(.Al$o1)
Como ves, es igual 4o casi igual8 en todos los lengua"es# 5n C-- hay que poner ** en lugar de un punto por
una cuesti%n meramente sintctica propia de este lengua"e para separar el nombre de una clase de uno de sus
miembros# 1"o, no quiero decir que todos los lengua"es sean iguales, no, sino que todos usan la misma
biblioteca de clases o, dicho de otro modo, todos usan las mismas clases de base# Ba s(, ya s(* ahora estar(is
pensando que, si esto es as&, todos los lengua"es tienen la misma capacidad# Bueno, pues es cierto, aunque
s%lo relativamente, pues no todos los lengua"es implementan toda la biblioteca de clases completa, ya que
basta con que el compilador se a"uste a los m&nimos que e)ige el C:;#
9or otro lado, la compilaci%n de un programa gestionado por el C:H no se hace directamente a c%digo
nativo, sino a un lengua"e, ms o menos como el ensamblador, llamado 0;C: 40icroso+t Cntermediate
:anguage8# 2espu(s es el C:H el que va compilando el c%digo 0;C: a c%digo nativo usando lo que se llaman
los compiladores ,C' 4,ust Cn 'ime8# 1"o, no se compila todo el programa de golpe, sino que se van compilando
los m(todos seg=n estos se van invocando, y los m(todos compilados quedan en la cach( del ordenador para
no tener que compilarlos de nuevo si se vuelven a usar# Tay tres tipos de ,C', pero ya los trataremos ms
adelante, pues creo que ser ms oportuno# D;e trata entonces de lengua"es compilados o interpretadosE 9ues
me has /pillao3, porque no sabr&a qu( decirte# $o es interpretado porque no se enla!a l&nea por l&nea, y no es
compilado porque no se enla!a todo completo en el momento de e"ecutar# :lmalo )#
1J
2.3 'ases sint*cticas de C#
Ahora s&, ahora por +in empe!amos a ver algo de C## Agrrate bien, que despegamos#
;i vienes de programar en otros lengua"es basados en el C, como C-- o ,ava, o incluso en el propio C, te
sentirs c%modo enseguida con C#, ya que la sinta)is es muy parecida# ;i vienes de programar en otros
lengua"es de alto nivel, como Visual Basic, 2elphi, 9A;CA: o C1B1:, por e"emplo, o incluso si no conoces
ning=n lengua"e de programaci%n, no te de"es asustar# :eer y entender programas escritos en alguno de estos
=ltimos lengua"es es algo bastante +cil, incluso si no hab&as programado nunca# ;in embargo, leer programas
hechos en C, C--, ,ava o C# puede resultar muy intimidatorio al principio# 5ncontrars llaves por todas partes,
corchetes, par(ntesis, operadores e)traos que nunca viste en otros lengua"es, como W, WW, X, XX, G, GR, R R, YY,
ZZ, interrogantes, dos puntos, punto y coma y cosas as&# Cncluso vers que algunas veces la misma instrucci%n
parece hacer cosas completamente distintas 4aunque en realidad no es as&8# Ba no te quiero contar cuando
escribas tu primer programa en C#* posiblemente tengas ms errores que l&neas de programa# ;in embargo,
repito, no te de"es asustar# Aunque es un poco con+uso al principio vers c%mo te acostumbras pronto# Adems,
el editor de Visual ;tudio#$5' te ayuda mucho a la hora de escribir c%digo, ya que cuando detecta que +alta
algo o hay alguna incoherencia te subraya la parte err%nea en ro"o o en a!ul, tal y como hace el 0;V<ord, por
e"emplo, cuando escribes una palabra que no tiene en el diccionario ortogr+ico, o cuando lo escrito es
incorrecto gramaticalmente#
Como dec&a, la sinta)is de C# es muy parecida a la de C, C-- y, sobre todo, ,ava# 9ara disear este
lengua"e, 0icroso+t ha decidido que todo lo que se pudiera escribir como se escribe en C era me"or no tocarlo, y
modi+icar o aadir =nicamente aquellas cosas que en C no tienen una relativa equivalencia# As&, por e"emplo,
declarar una variable o un puntero en C# se escribe igual que en C*
int a%
intH pA%
$o obstante, hay que prestar atenci%n especial a que, aunque un c%digo sea sintcticamente id(ntico,
semnticamente puede ser muy distinto, es decir* mientras en C la una variable de tipo int es eso y nada ms,
en C# una variable de tipo int es en realidad un ob"eto de la clase ;ystem#Cnt32 4ya di"imos en la introducci%n
que en C# todo es un ob"eto salvo los punteros8# 5n resumen, las di+erencias ms importantes entre C y C# no
suelen ser sintcticas sino sobre todo semnticas#
Bien, una ve! aclarado todo esto, podemos seguir adelante# 9rimera premisa* en C# todas las instrucciones
y declaraciones deben terminar con K 4punto y coma8, salvo que haya que abrir un bloque de c%digo# ;i
programas en 9ascal o 0odula2 dirs* /Tombre, claro3, pero si programas en Visual Basic no te olvides del
punto y coma, pecadorrrrrr# D9or qu(E 9orque, al contrario que en Visual Basic, aqu& puedes poner una
instrucci%n que sea muy larga en varias l&neas sin poner ning=n tipo de signo especial al +inal de cada una# 5s
cuesti%n de cambiar el chip# N&"ate en esta simulaci%n*
A = !etodo(ar$umento?, ar$umentoD, ar$umento=, ar$umentoA
ar$umento@, ar$umento>, ar$umentoC, ar$umentoB)%
5l compilador entiende que todo +orma parte de la misma instrucci%n hasta que encuentre un punto y coma#
DB qu( es un bloque de c%digoE 9ues vamos con ello# Pn bloque de c%digo es una parte del mismo que est
[encerrado[ dentro de alg=n conte)to espec&+ico, como una clase, un m(todo, un bucle### Veamos un e"emplo
muy signi+icativo# 5l siguiente +ragmento de c%digo es una +unci%n escrita en Visual Basic*
Public 3unction :s!a+orIue"ero(numero as /nte$er) as oolean
/4 numero J ( Khen
:s!a+orIue"ero = Krue
:nd /4
1L
:nd 3unction
5n esta +unci%n podemos encontrar dos bloques de c%digo* el primero de ellos es todo el que est dentro del
conte)to de la +unci%n, es decir, entre la l&nea donde se declara la +unci%n 49ublic Nunction 5s0ayorIueCero###8
y la l&nea donde termina dicha +unci%n 45nd Nunction8# 5l otro bloque est dentro del conte)to del C+, y est
compuesto por la =nica l&nea que hay entre el principio de dicho conte)to 4C+ numero Z @ 'hen8 y la que indica el
+inal del mismo 45nd C+8# 9or lo tanto, como puedes ver, en Visual Basic cada bloque empie!a y termina de un
modo distinto, dependiendo de qu( tipo de bloque sea, lo cual hace que su legibilidad sea muy alta y sencilla#
Veamos ahora su equivalente en C# 4y no te preocupes si no entiendes el c%digo, que todo llegar8*
public bool :s!a+orIue"ero(int numero)
#
i4 (numeroJ()
#
return true%
&
return 4alse%
&
5n este caso, los bloques de c%digo estn muy claramente delimitados por las llaves, pero como puedes
apreciar, ambos bloques estn delimitados del mismo modo, es decir, ambos se delimitan con llaves# Adems,
+&"ate en que detrs de la l&nea en que se declara el m(todo no est escrito el punto y coma, igual que en el i+, lo
cual quiere decir que la llave de apertura del bloque correspondiente se pod&a haber escrito a continuaci%n, y no
en la l&nea siguiente# ;eg=n est escrito, es +cil determinar cules son las llaves de apertura y cierre de un
bloque y cules las del otro# ;in embargo, si hubi(semos quitado las tabulaciones y colocado la llave de
apertura en la misma l&nea, esto se habr&a complicado algo*
bool :s!a+orIue"ero(int numero) #
i4 (numeroJ() #
return true%
&
return 4alse%
&
;i, adems, dentro de este m(todo hubiera tres bucles +or anidados, un s.itch, dos bucles <hile y cuatro o
cinco i+, unos dentro de otros, con alg=n que otro else y else i+, pues la cosa se puede convertir en un galimat&as
de dimensiones ol&mpicas# 2e ah& la importancia de tabular correctamente el c%digo en todos los lengua"es,
pero especialmente en los lengua"es basados en C, como el propio C, C--, ,ava y C#, ya que as& ser +cil ver
d%nde empie!a y d%nde termina un bloque de c%digo# 2igo esto porque, a pesar de que Visual ;tudio#$5' pone
todas las tabulaciones de modo automtico, siempre puede haber alguno que las quite porque no le pare!can
=tiles# F$1 IPC'5; :A; 'ABP:ACC1$5;G DC%moE DIue podr&a haber abreviado mucho el c%digo en este
e"emploE ;&, ya lo s(# 9ero entonces no habr&amos visto bien lo de los bloques# Pn poco de paciencia,
hombre###
:os programas escritos en C# se organi!an en clases y estructuras, de modo que todo el c%digo que
escribas debe ir siempre dentro de una clase o bien de una estructura, salvo la directiva using# 9or eso las
+unciones ahora se llaman m(todos, porque sern m(todos de la clase donde las pongas, y las variables y
1O
constantes 4dependiendo de d%nde se declaren8 pueden ser propiedades de la clase# :os que no sepis qu( es
una +unci%n, una variable o una constante no os preocup(is, que lo veremos a su debido tiempo#
5n cada aplicaci%n que escribas en C# debes poner un m(todo llamado 0ain, que adems ha de ser public
y static 4veremos estos modi+icadores ms adelante8# $o importa en qu( clase de tu aplicaci%n escribas el
m(todo 0ain, pero qu(date con la copla* en todo programa escrito en C# debe haber un m(todo 0ain, pues
ser el que busque el C:H para e"ecutar tu aplicaci%n# A partir de aqu&, lo ms aconse"able es escribir el m(todo
0ain en una clase que se llame igual que el programa ms las letras App# 9or e"emplo, si es una calculadora, lo
ms recomendable es situar el m(todo 0ain en una clase que se llame CalculadoraApp# Ahora bien, recuerda
que esto no te lo e)ige el compilador, as& que si pones el m(todo 0ain en cualquier otra clase el programa
+uncionar#
1tra cosa importante a tener en cuenta es que C# distingue las may=sculas de las min=sculas, de modo que
una variable que se llame /$ombre3 es distinta de otra que se llame /nombre3, y un m(todo que se llame /Abrir3
ser distinto de otro que se llame /abrir3# Adaptarte a esto ser lo que ms te cueste si eres programador de
Visual Basic# $o obstante, vers que tiene algunas venta"as#
C# soporta la sobrecarga de m(todos, es decir, que puedes escribir varios m(todos en la misma clase que
se llamen e)actamente igual, pero recuerda que la lista de argumentos ha de ser di+erente en cada uno de ellos,
ya se di+erencien en el n=mero de argumentos o bien en el tipo de dato de dichos argumentos# 5sto es algo que
Visual Basic#$5' tambi(n soporta 4por +in8, pero no suced&a as& en las versiones anteriores de dicho lengua"e#
'ambi(n soporta la sobrecarga de operadores y conversiones de+inidas por el usuario# 5sto quiere decir que
cuando disees una clase puedes modi+icar el comportamiento de varios de los operadores del lengua"e para
que hagan cosas distintas de las que se esperan, y quiere decir tambi(n que si usas una clase diseada por
otro programador, uno o varios operadores pueden estar sobrecargados, por lo que es conveniente revisar la
documentaci%n de dicha clase antes de empe!ar a usarla, no sea que le intentes sumar algo, por e"emplo, y te
haga cualquier cosa que no te esperas# 2e todos modos, cuando lleguemos al tema de la sobrecarga de
operadores te dar( algunos conse"os sobre cundo es apropiado usar esta t(cnica y cundo puede ser
contraproducente#
5n C# no e)isten archivos de cabecera ni m%dulos de de+inici%n, as& que, si programabas en C o C--,
puedes olvidarte de la directiva #include cuando cuente tres* uno###dos###tres FBAG ;i programabas en 012P:A
2, puedes aplicarte el cuento con el NH10 ### C091H', aunque esta ve! no voy a contar# ;i programabas en
otro lengua"e no me preguntes, que no tengo ni idea# ;i no sab&as programar en ning=n lengua"e, me"or que no
te olvides de nada, que si no la liamos# 5n lugar de esto tenemos algo mucho ms +cil y mane"able* los
espacios de nombres, de los cuales hablaremos en la pr%)ima entrega#
9ara terminar, puedes poner los comentarios a tu c%digo de dos +ormas* 66 indica que es un comentario de
una sola l&nea# 6\ ### comentario ### \6 es un comentario de una o varias l&neas# 1bserva el e"emplo*
)) :sto es un comentario de una 5nica l*nea
)H :sto es un comentario que consta de
varias l*neas H)
' %ercera entrega (Espacios de nombres( clases( indicadores & el
sistema de tipos de C#.!
3.1 os espacios de nombres
:os espacios de nombres son un modo sencillo y muy e+ica! de tener absolutamente todas las clases
per+ectamente organi!adas, tanto las que proporciona el #$5' Nrame.orU como las que podamos escribir
nosotros# 9odemos verlo verdaderamente claro con echar un simple vista!o al e)plorador de ob"etos de Visual
;tudio#$5' 4men= Ver###1tras ventanas###5)aminador de ob"etos, o bien la combinaci%n de teclas Ctrl-Alt-,8#
1S

:o que tenemos a la i!quierda es toda la biblioteca de clases del #$5' Nrame.orU# Como ves estn
completamente organi!adas en rbol, de modo que toda ella est +uertemente estructurada# Adems, +&"ate bien
en la rama que est parcialmente desplegada# $o se trata de nada que est( dentro de la biblioteca de clases
del #$5' Nrame.orU, sino de una aplicaci%n diseada en C## 9or lo tanto, como ves, nosotros tambi(n
podemos de+inir nuestros propios espacios de nombres#
:as venta"as principales de estos espacios de nombres son su +uerte estructuraci%n y, sobre todo, la
posibilidad de usar varias clases distintas con el mismo nombre en un mismo programa si los espacios de
nombres son di+erentes# $o, no es el mismo perro con distinto collar# 5s relativamente +cil que varios
+abricantes de so+t.are den el mismo nombre a sus clases pues, al +in y al cabo, solemos basarnos en nuestro
idioma para nombrarlas# ;in embargo es mucho menos probable que los espacios de nombres coincidan, sobre
todo si se tiene la precauci%n de seguir las recomendaciones de 0icroso+t, que consisten en comen!ar por
llamar al espacio de nombres igual que se llama la compa&a, ms luego lo que sea# 9or e"emplo, si mi
compa&a se llama $C;P, y escribo un espacio de nombres con clases que reali!an comple"os clculos para la
navegaci%n espacial, mi espacio de nombres podr&a llamarse $C;P$avegacion5spacial# ;i, despu(s, CB0
desarrolla una biblioteca similar, su espacio de nombres se llamar&a CB0$avegacion5spacial 4venga, hombre,
(chame una mano### imag&nate que los de CB0 hablan espaol8# Aunque el nombre de mis clases coincida en
gran n=mero con los de las clases de CB0, cualquier desarrollador podr&a utili!ar las dos sin problemas gracias
a los espacios de nombres#
2e+inir un espacio de nombres es de lo ms sencillo*
namespace N/GLNave$acion:spacial#
)) Aqu* van las clases del espacio de nombres
1?
&
9or otro lado, ten presente que dentro de un mismo proyecto podemos de+inir tantos espacios de nombres
como necesitemos#
B si de+inir un espacio de nombres es sencillo, usarlo es ms sencillo a=n*
N/GLNave$acion:spacial."lase ob;eto = ne< N/GLNave$acion:spacial."lase(ar$umentos)%
5+ectivamente, se coloca primero el nombre del espacio de nombres y despu(s, separado por un punto, el
miembro de este espacio de nombres que vayamos a usar# $o obstante, dado que los espacios de nombres
estn estructurados en rbol, pudiera ser que llegar a alg=n miembro requiera escribir demasiado c%digo, pues
hay que indicar toda la ruta completa*
N/GLNave$acion:spacial.Propulsion."ombustibles.MPB ob;eto = ne<
N/GLNave$acion:spacial.Propulsion."ombustibles.MPB (ar$umentos)%
Ciertamente, escribir chori!os tan largos s%lo para decir que quieres usar la clase ,9S puede resultar muy
inc%modo# 9ara situaciones como esta C# incorpora la directiva using# 9ara que os hagis una idea, ser&a como
cuando pon&amos 9A'T R lista de rutas en nuestro vie"o y querido 0;V21;# DIu( ocurr&aE 9ues cuando
escrib&amos el nombre de un archivo e"ecutable primero lo buscaba en el directorio donde estbamos
posicionados# ;i no lo encontraba aqu& revisaba todas las rutas que se hab&an asignado al 9A'T# ;i lo
encontraba en alguna de estas rutas lo e"ecutaba directamente, y si no lo encontraba nos saltaba un mensa"e
de error D1s acordis del mensa"e de errorE /Comando o nombre de archivo incorrecto3 4"e "e, qu( tiempos
aquellos###8 Bueno, a lo que vamos, no me voy a poner nostlgico ahora### Bsicamente, eso mismo hace la
directiva using con los espacios de nombres* si utili!amos un nombre que no se encuentra en el espacio de
nombres donde lo queremos usar, el compilador revisar todos aquellos que se hayan especi+icado con la
directiva using# ;i lo encuentra, pues qu( bien, y si no lo encuentra nos lan!a un mensa"e de error# Iu( te
parece, tanto <indo.s, tanto #$5', tanta nueva tecnolog&a### Fy resulta que seguimos como en el 21;G Nuera
de bromas, quiero recalcar que no equivale a la directiva #include de C, ni mucho menos# :a directiva #include
signi+icaba que &bamos a usar +unciones de un determinado archivo de cabecera# ;i no se pon&a, las +unciones
de dicho archivo, simplemente, no estaban disponibles# ;in embargo podemos usar cualquier miembro de los
espacios de nombres sin necesidad de poner ninguna directiva using# 5spero que haya quedado claro# Vamos
con un e"emplo# :o que hab&amos puesto antes se podr&a haber hecho tambi(n de esta otra +orma*
usin$ N/GLNave$acion:spacial.Propulsion."ombustibles%
...
MPB ob;eto = ne< MPB (ar$umentos)%
2e todos modos, no puedes usar la directiva using donde y como te de la gana# Ni"o que los programadores
de Visual Basic se han /colao3# :a directiva using tampoco equivale al bloque <ith de Visual Basic pues,
sencillamente, no es un bloque# ;olamente puedes ponerla, o bien al principio del programa, con lo cual
a+ectar&a a todos los espacios de nombres que de+inas en dicho programa, o bien dentro de los espacios de
nombres, pero siempre antes de cualquier de+inici%n de miembro de dicho espacio de nombres, con lo cual
a+ectar&a solamente a los miembros del espacio de nombres donde la has puesto# Veamos un e"emplo*
usin$ G+stem."onsole%

namespace :spacio?#
...
,rite-ine(.Nola1)%
2@
...
&
namespace :spacioD#
...
,rite-ine(.Nola otra vez1)
...
&
o bien*
namespace :spacio?#
usin$ G+stem."onsole%
...
,rite-ine(.Nola1)%
...
&
namespace :spacioD#
...
,rite-ine(.Nola otra vez1) )) Aqu* saltar*a un error. usin$ solo es e4ectivo para :spacio?
...
&
5n el primer caso no saltar&a ning=n error, ya que <rite:ine es un m(todo static de la clase ;ystem#Console,
y using a+ecta a los dos espacios de nombres 45spacio1 y 5spacio28 al estar escrito al principio del programa#
;in embargo, en el segundo e"emplo el compilador nos avisar&a de que no encuentra <rite:ine en ninguno de
los espacios de nombres, dado que using s%lo es e+ectivo dentro de 5spacio1 al estar escrito dentro de (l# 9or
cierto, los tres puntos signi+ican que por ah& hay ms c%digo, obviamente#
DB qu( pasa si tengo dos clases que se llaman igual en distintos espacios de nombresE D$o puedo poner
using para abreviarE 5n este caso, lo me"or ser&a utili!ar los alias, los cuales se de+inen tamb(n con using*
usin$ N/GL = N/GLNave$acion:spacial% )) A partir de aqu*, N/GL equivale a N/GLNave$acion:spacial
usin$ /! = /!Nave$acion:spacial% )) A partir de aqu*, /! equivale a /!Nave$acion:spacial
...
N/GL.!odulo-unar modulo = ne< N/GL.!odulo-unar()%
/!.!odulo-unar moduloD = ne< /!.!odulo-unar()%
...
;e ve bien claro* el ob"eto modulo pertenecer a la clase 0odulo:unar del espacio de nobres
$C;P$avegacion5spacial, mientras que el ob"eto modulo2 pertenecer a la clase 0odulo:unar tambi(n, pero
esta ve! del espacio de nombres CB0$avegacion5spacial#
9ara terminar ya con esto, que sepas que puedes poner tantas directivas using como estimes oportunas
siempre que cumplas las reglas de colocaci%n de las mismas#
21
3.2 as clases" unidades b*sicas de estructuramiento
Como di"e en la entrega anterior, todo programa en C# se organi!a en clases y estructuras# :as clases son,
por lo tanto, la base +undamental de cualquier programa escrito en este lengua"e# Veamos c%mo se construye
una clase*
class Nombre"lase#
)) Aqu* se codi4ican los miembros de la clase
&
Como puedes apreciar, es muy simple# Basta con poner la palabra class seguida del nombre de la clase y, a
continuaci%n, poner el signo de apertura de bloque [][ para empe!ar a codi+icar sus miembros# 5l +in de la clase
se marca con el signo de cierre de bloque [^[# 9ero, claro, no todas las clases tienen por qu( ser igualmente
accesibles desde otra aplicaci%n# 0e e)plico* puede que necesites una clase que s%lo se pueda usar por c%digo
que pertene!ca al mismo ensamblado# 5n este caso, bastar&a con poner el modi+icador de acceso internal
delante de la palabra class o bien no poner nada, pues internal es el modi+icador de acceso por de+ecto para las
clases*
internal class Nombre"lase#
)) Aqu* se codi4ican los miembros de la clase
&
;i lo que quieres es una clase que sea accesible desde otros ensamblados, necesitars que sea p=blica,
usando el modi+icador de acceso public*
public class Nombre"lase#
)) Aqu* se codi4ican los miembros de la clase
&
Ah, y no os apur(is, que ya trataremos los ensamblados ms adelante 4mucho ms adelante8#
3.3 +ndicadores" ,ariables y constantes
:os indicadores representan un determinado espacio de memoria reservado para almacenar un valor
determinado, sea del tipo que sea 4despu(s hablaremos de los tipos en C#, pues creo que es me"or hacerlo
cuando sepas para qu( sirven8# 9or e"emplo, si quiero reservar memoria para almacenar el nombre de un
cliente puedo declarar un indicador que se llame $ombre# Al hacer esto, el compilador reservar un espacio de
memoria para que se pueda almacenar el dato# 5ste ser&a un caso t&pico de indicador variable, ya que su valor
puede ser modi+icado una o varias veces durante la e"ecuci%n de un programa 4ten en cuenta que antes de
e"ecutar el programa no sabremos nada sobre el cliente8# 9ara declararlo hay que colocar previamente el tipo y
despu(s el nombre del indicador# Vemoslo*
class Gestor"lientesApp#
public static void !ain()#
strin$ Nombre% )) Declaracin de la variable nombre, que es de tipo strin$

"onsole.,rite(OP"mo se llama el clienteQ O)%
Nombre = "onsole.6ead-ine()%
"onsole.,rite-ine(O!i cliente se llama #(&O, Nombre)%
22
&
&
5n este sencillo programa, el compilador reservar memoria para la variable $ombre# 5n la e"ecuci%n del
mismo primero preguntar&a por el nombre del cliente y, despu(s de haberlo escrito nosotros, nos dir&a c%mo se
llama# Algo as& 4/0i cliente se llama Antonio3 es lo que hemos escrito nosotros durante la e"ecuci%n del
programa8*
P"mo se llama el clienteQ Antonio
!i cliente se llama Antonio
2ate cuenta que para que el programa nos pueda decir c%mo se llama el cliente no hemos usado el nombre
literal 4Antonio8, ni la posici%n de memoria donde estaba este dato, sino simplemente hemos usado el indicador
variable que hab&amos de+inido para este prop%sito# 2e aqu& en adelante, cuando hable de variables me estar(
re+iriendo a este tipo de indicadores#
'ambi(n podemos iniciali!ar el valor de una variable en el momento de declararla, sin que esto suponga un
obstculo para poder modi+icarlo despu(s*
int num=?(%
2e otro lado tenemos los indicadores constantes 4constantes en adelante8# 'ambi(n hacen que el
compilador reserve un espacio de memoria para almacenar un dato, pero en este caso ese dato es siempre el
mismo y no se puede modi+icar durante la e"ecuci%n del programa# Adems, para poder declararlo es necesario
saber previamente qu( valor ha de almacenar# Pn e"emplo claro ser&a almacenar el valor de pi en una constante
para no tener que poner el n=mero en todas las partes donde lo podamos necesitar# ;e declaran de un modo
similar a las variables, aunque para las constantes es obligatorio decirles cul ser su valor, y este ha de ser
una e)presi%n constante# Basta con aadir la palabra const en la declaraci%n# Vamos con un e"emplo*
usin$ G+stem%
namespace "ircun4erencia?#
class "ircun4erenciaApp#
public static void !ain()#
const double P/==.?A?@RD>% )) :sto es una constante
double 6adio=A% )) :sto es una variable

"onsole.,rite-ine(O:l per*metro de una circun4erencia de radio #(& es #?&O, 6adio,
DHP/H6adio)%

"onsole.,rite-ine(O:l Erea de un c*rculo de radio #(& es #?&O, 6adio,
P/H!ath.Po<(6adio,D))%
&
&
&
:a salida en la consola de este programa ser&a la siguente*
23
:l per*metro de una circun4erencia de radio A es D@,?=DCA(B
:l Erea de un c*rculo de radio A es @(,D>@AB?>
Como ves, en lugar de poner 2\3#1>1J?2L\Hadio donde damos la circun+erencia hemos puesto 2\9C\Hadio,
puesto que el valor constante por el que debemos multiplicar 4el valor de pi en este caso8 lo hemos almacenado
en una constante, haciendo as& el c%digo ms c%modo y +cil de leer#
:os indicadores, al igual que las clases, tambi(n tienen modi+icadores de acceso# ;i se pone, ha de
colocarse en primer lugar# ;i no se pone, el compilador entender que es private# 2ichos modi+icadores son*

012CNCCA2
1H
C1091H'A0C5$'1
public Tace que el indicador sea accesible desde otras clases#
protected Tace que el indicador sea accesible desde otras clases derivadas de
aquella en la que est declarado, pero no desde el cliente
private Tace que el indicador solo sea accesible desde la clase donde est
declarado# 5ste es el modi+icador de acceso por omisi%n#
internal Tace que el indicador solo sea accesible por los miembros del ensambla"e
actual#
Pn caso de variable con nivel de acceso protected, por e"emplo, ser&a*
protected int 'ariable%
1tro asunto importante a tener en cuenta es que, cuando se declara un indicador dentro de un bloque que
no es el de una clase o estructura, este indicador ser siempre privado para ese bloque, de modo que no ser
accesible +uera del mismo 4no te preocupes mucho si no acabas de entender esto# :o vers mucho ms claro
cuando empecemos con los distintos tipos de bloques de c%digo# 2e momento me basta con que tengas una
vaga idea de lo que quiero decir8#
3.4 #l sistema de tipos de C#
5l sistema de tipos suele ser la parte ms importante de cualquier lengua"e de programaci%n# 5l uso correcto
de los distintos tipos de datos es algo +undamental para que una aplicaci%n sea e+iciente con el menor consumo
posible de recursos, y esto es algo que se tiende a olvidar con demasiada +recuencia# 'odo tiene su e)plicaci%n*
antiguamente los recursos de los equipos eran muy limitados, por lo que hab&a que tener mucho cuidado a la
hora de desarrollar una aplicaci%n para que esta no sobrepasara los recursos disponibles# Actualmente se
produce el e+ecto contrario* los equipos son muy rpidos y potentes, lo cual hace que los programadores se
rela"en, a veces demasiado, y no se preocupen por economi!ar medios# 5sta tendencia puede provocar un
e+ecto demoledor* aplicaciones terriblemente lentas, inestables y muy poco e+icientes#
Bien, despu(s del serm%n, vamos con el meollo de la cuesti%n# Actualmente, muchos de los lengua"es
orientados a ob"etos proporcionan los tipos agrupndolos de dos +ormas* los tipos primitivos del lengua"e, como
n=meros o cadenas, y el resto de tipos creados a partir de clases# 5sto genera muchas di+icultades, ya que los
tipos primitivos no son y no pueden tratarse como ob"etos, es decir, no se pueden derivar y no tienen nada que
ver unos con otros# ;in embargo, en C# 4ms propiamente en #$5' Nrame.orU8 contamos con un sistema de
tipos uni+icado, el C'; 4Common 'ype ;ystem8, que proporciona todos los tipos de datos como clases
derivadas de la clase de base ;ystem#1b"ect 4incluso los literales pueden tratarse como ob"etos8# ;in embargo,
el hacer que todos los datos que ha de mane"ar un programa sean ob"etos puede provocar que ba"e el
rendimiento de la aplicaci%n# 9ara solventar este problema, #$5' Nrame.orU divide los tipos en dos grandes
grupos* los tipos valor y los tipos re+erencia#
2>
Cuando se declarara variable que es de un tipo valor se est reservando un espacio de memoria en la pila
para que almacene los datos reales que contiene esta variable# 9or e"emplo en la declaraci%n*
int num =?(%
;e est reservando un espacio de 32 bits en la pila 4una variable de tipo int es un ob"eto de la clase
;ystem#Cnt328, en los que se almacena el 1@, que es lo que vale la variable# 5sto hace que la variable num se
pueda tratar directamente como si +uera de un tipo primitivo en lugar de un ob"eto, me"orando notablemente el
rendimento# Como consecuencia, una variable de tipo valor nunca puede contener null 4re+erencia nula8#
DC%moE DIue qu( es eso de una pilaE FVayaG, tienes ra!%n# 'engo la mala costumbre de querer construir la
casa por el te"ado# 2("ame que te cuente algo de c%mo se distribuye la memoria y luego sigo#
2urante la e"ecuci%n de todo programa, la memoria se distribuye en tres bloques* la pila, el mont%n
4traducci%n libre o, incluso, /libertina3 de heap8 y la memoria global# :a pila es una estructura en la que los
elementos se van apilando 4por eso, curiosamente, se llama pila8, de modo que el =ltimo elemento en entrar en
la pila es el primero en salir 4estructura :CN1, o sea, :ast Cn Nirst 1ut8# A ver si me e)plico me"or* Cuando haces
una invocaci%n a un m(todo, en la pila se van almacenando la direcci%n de retorno 4para que se pueda volver
despu(s de la e"ecuci%n del m(todo8 y las variables privadas del m(todo invocado# Cuando dicho m(todo
termina las variables privadas del mismo se quitan de la pila ya que no se van utili!ar ms y, posteriormente, la
direcci%n de retorno, ya que la e"ecuci%n ha retornado# 5l mont%n es un bloque de memoria contiguo en el cual
la memoria no se reserva en un orden determinado como en la pila, sino que se va reservando aleatoriamente
seg=n se va necesitando# Cuando el programa requiere un bloque del mont%n, este se sustrae y se retorna un
puntero al principio del mismo# Pn puntero es, para que me entiendas, algo que apunta a una direcci%n de
memoria# :a memoria global es el resto de memoria de la mquina que no est asignada ni a la pila ni al
mont%n, y es donde se colocan el m(todo main y las +unciones que (ste invocar# DValeE ;eguimos#
5n el caso de una variable que sea de un tipo re+erencia, lo que se reserva es un espacio de memoria en el
mont%n para almacenar el valor, pero lo que se devuelve internamente es una re+erencia al ob"eto, es decir, un
puntero a la direcci%n de memoria que se ha reservado# $o te alarmes* los tipos re+erencia son punteros de tipo
seguro, es decir, siempre van a apuntar a lo que se espera que apunten# 5n este caso, evidentemente, una
variable de un tipo re+erencia s& puede contener una re+erencia nula 4null8#
5ntonces, si los tipos valor se van a tratar como tipos primitivos, Dpara qu( se han liado tanto la manta a la
cabe!aE 9ues porque una variable de un tipo valor +uncionar como un tipo primitivo siempre que sea
necesario, pero podr +uncionar tambi(n como un tipo re+erencia, es decir como un ob"eto, cuando se necesite
que sea un ob"eto# Pn e"emplo claro ser&a un m(todo que necesite aceptar un argumento de cualquier tipo* en
este caso bastar&a con que dicho argumento +uera de la clase ob"ectK el m(todo mane"ar el valor como si +uera
un ob"eto, pero si le hemos pasado un valor int, este ocupa =nicamente 32 bits en la pila# Tacer esto en otros
lengua"es, como ,ava, es imposible, dado que los tipos primitivos en ,ava no son ob"etos#
Aqu& tienes la tabla de los tipos que puedes mane"ar en C# 4me"or dicho, en todos los lengua"es basados en
el C:;8, con su equivalente en el C'; 4Common 'ype ;ystem8#

)E#*+EN DE, #I#%E+- DE %IP.#
'ipo C'; Alias
C#
2escripci%n Valores que acepta
;ystem#1b"ec
t
ob"ect Clase base de todos los tipos del C'; Cualquier ob"eto
;ystem#;tring string Cadenas de caracteres Cualquier cadena
;ystem#;Byte sbyte Byte con signo 2esde V12S hasta 12O
;ystem#Byte byte Byte sin signo 2esde @ hasta 2JJ
2J
;ystem#Cnt1L short 5nteros de 2 bytes con signo 2esde V32#OLS hasta 32#OLO
;ystem#PCnt1
L
ushort 5nteros de 2 bytes sin signo 2esde @ hasta LJ#J3J
;ystem#Cnt32 int 5nteros de > bytes con signo 2esde V2#1>O#>S3#L>S
hasta 2#1>O#>S3#L>O
;ystem#PCnt3
2
uint 5nteros de > bytes sin signo 2esde @ hasta
>#2?>#?LO#2?J
;ystem#CntL> long 5nteros de S bytes con signo 2esde
V?#223#3O2#@3L#SJ>#OOJ#S@S
hasta
?#223#3O2#@3L#SJ>#OOJ#S@O
;ystem#PCntL
>
ulong 5nteros de S bytes sin signo 2esde @
Tasta
1S#>>L#O>>#@O3#O@?#JJ1#L1
J
;ystem#Char char Caracteres Pnicode de 2 bytes 2esde @ hasta LJ#J3J
;ystem#;ingle +loat Valor de coma +lotante de > bytes 2esde 1,J5V>J hasta
3,>5-3S
;ystem#2oubl
e
double Valor de coma +lotante de S bytes 2esde J5V32> hasta
1,O5-3@S
;ystem#Boole
an
bool Verdadero6+also true % +alse
;ystem#2eci
mal
decim
al
Valor de coma +lotante de 1L bytes
4tiene 2SV2? d&gitos de precisi%n8
2esde 15V2S hasta O,?5-2S
Aunque ya lo has visto antes, a=n no lo hemos e)plicado* para declarar una variable de uno de estos tipos
en C# hay que colocar primero el tipo del C'; o bien el alias que le corresponde en C#, despu(s el nombre de
la variable y despu(s, opcionalmente, asignarle su valor*
G+stem./nt=D num=?(%
int num=?(%
:a variable num ser&a de la clase ;ystem#Cnt32 en ambos casos* en el primero hemos usado el nombre de la
clase tal y como est en el C';, y en el segundo hemos usado el alias para C## $o olvides que la asignaci%n
del valor en la declaraci%n es opcional# 5n todos los lengua"es que cumplen las especi+icaciones del C:; se
usan los mismos tipos de datos, es decir, los tipos del C';, aunque cada lengua"e tiene sus alias espec&+icos#
9or e"emplo, la variable num de tipo int en Visual Basic ser&a*
Dim num As G+stem./nt=D = ?(
Dim num As /nte$er = ?(
5n cualquier caso, y para cualquier lengua"e que cumpla las especi+icaciones del C:;, los tipos son los
mismos#
;i no has programado nunca es posible que a estas alturas tengas un importante "aleo mental con todo esto#
D9ara qu( tantos tiposE D5s que no es lo mismo el n=mero 1@@ en una variable de tipo int que en una de tipo
2L
byte, o short, o long, o decimalE DIu( es eso de la coma +lotanteE DIu( es eso de las cadenasE DIu( son los
caracteres unicodeE FIu( me ests contandoooooG Bueno, tratar( de irte dando respuestas, no te preocupes#
'odos los tipos son necesarios en aras de una mayor e+iciencia# Healmente, podr&amos ahorrarnos todos los
tipos num(ricos y quedarnos, por e"emplo, con el tipo 2ecimal, pero si hacemos esto cualquier n=mero que
quisi(ramos meter en una variable ocupar&a 1L bytes de memoria, lo cual supone un enorme desperdicio y un
e)cesivo consumo de recursos que, por otra parte, es absolutamente innecesario# ;i sabemos que el valor de
una variable va a ser siempre entero y no va a e)ceder de, por e"emplo, 1@#@@@, nos bastar&a un valor de tipo
short, y si el valor va a ser siempre positivo, nos sobra con un tipo ushort, ya que estos ocupan =nicamente 2
bytes de memoria, en lugar de 1L como las variables de tipo decimal# 9or lo tanto, no es lo mismo el n=mero
1@@ en una variable de tipo short que en una de otro tipo, porque cada uno consume una cantidad di+erente de
memoria# 5n resumen* hay que a"ustar lo m)imo posible el tipo de las variables a los posibles valores que
estas vayan a almacenar# 0eter valores pequeos en variables con mucha capacidad es como usar un almac(n
de 2@@ metros cuadrados s%lo para guardar una pluma# D9ara qu(, si basta con un pequeo estucheE 9ara
asignar un valor num(rico a una variable num(rica basta con igualarla a dicho valor*
int num=?(%
Pn tipo que admite valores de coma +lotante admite valores con un n=mero de decimales que no est +i"ado
previamente, es decir, n=meros enteros, o con un decimal, o con dos, o con die!### 9or eso se dice que la coma
es +lotante, porque no est siempre en la misma posici%n con respecto al n=mero de decimales 4el separador
decimal en el c%digo siempre es el punto8#
double num=?(.C@%
double num=?(.C@(B%
:as cadenas son una consecuci%n de caracteres, ya sean num(ricos, al+ab(ticos o al+anum(ricos# A ver si
me e)plico me"or# :a e)presi%n 1 - 2 dar&a como resultado 3, ya que simplemente hay que hacer la suma# ;in
embargo, la e)presi%n /13 - /23 dar&a como resultado /123, ya que ni el uno ni el dos van a ser considerados
n=meros sino cadenas de caracteres al estar entre comillas# 'ampoco el resultado se considera un n=mero, sino
tambi(n una cadena, es decir, el resultado de unir las dos anteriores o, lo que es lo mismo, la concatenaci%n de
las otras cadenas 4/13 y /238# 9or lo tanto, cuando se va a asignar un valor literal a una variable de tipo string hay
que colocar dicho literal entre comillas*
strin$ mensa;e = .uenos d*as1%
:os caracteres unicode es un con"unto de caracteres de dos bytes# DIue te has quedado igualE Vaaaaale,
voooooy# Tasta hace relativamente poco en occidente se estaba utili!ando el con"unto de caracteres A$;C, que
constaba de 2JL caracteres que ocupaban un byte# DIu( pasabaE Iue este con"unto de caracteres se
quedaba muy corto en oriente, por lo que ellos usaban el con"unto unicode, que consta de LJ#J3L caracteres# :o
que se pretende con #$5' es que, a partir de ahora, todos usemos el mismo con"unto de caracteres, es decir, el
con"unto unicode# 9or eso, todas las variables de tipo char almacenan un carcter unicode#
DB las +echasE 9ara las +echas tambi(n hay una clase, aunque en C# no hay ning=n alias para estos datos#
5s la clase ;ystem#2ate'ime*
G+stem.DateKime 4echa%
Pna ve! conocido todo esto, es importante tambi(n hablar de las conversiones# A menudo necesitars
e+ectuar operaciones matemticas con variables de distintos tipos# 9or e"emplo, puede que necesites sumar una
variable de tipo int con otra de tipo double e introducir el valor en una variable de tipo decimal# 9ara poder hacer
esto necesitas convertir los tipos# 9ues bien, para convertir una e)presi%n a un tipo de+inido basta con poner
delante de la misma el nombre del tipo entre par(ntesis# 9or e"emplo, 4int8 1@#OS devolver&a 1@, es decir, 1@#OS
como tipo int# ;i ponemos 4int8 >#J \ 3 el resultado ser&a 12, ya que 4int8 a+ecta =nicamente al valor >#J, de modo
que lo convierte en > y despu(s lo multiplica por 3# ;i, por el contrario, usamos la e)presi%n 4int8 4>#J \ 38, el
resultado ser&a 13, ya que en primer lugar hace la multiplicaci%n que est dentro del par(ntesis 4cuyo resultado
2O
es 13#J8 y despu(s convierte ese valor en un tipo int# Tay que tener un cuidado especial con las conversiones*
no podemos convertir lo que nos de la gana en lo que nos apete!ca, ya que algunas converisones no son
vlidas* por e"emplo, no podemos convertir una cadena en un tipo num(rico*
int a = (int) cadena% )) :rror. Lna cadena no se puede convertir a n5mero
9ara este caso necesitar&amos hacer uso de los m(todos de conversi%n que proporcionan cada una de las
clases del #$5' Nrame.orU para los distintos tipos de datos*
int a = G+stem./nt=D.Parse(cadena)% )) As* s*
Bueno, creo que ya podemos dar por concluido el sistema de tipos#
/ Cuarta entrega (.peradores de C#.!
4.1 !peradores
:os operadores sirven, como su propio nombre indica, para e+ectuar operaciones con uno o ms parmetros
4sumar, restar, comparar###8 y retornar un resultado# ;e pueden agrupar de varios modos, pero yo te los voy a
agrupar por primarios, unitarios y binarios# Aqu& tienes una tabla con los operadores de C#, y luego te los
e)plico todos con calma*
1peradores 2escripci%n 'ipo Asociativida
d
4e)presi%n8
ob"eto#miembro
m(todo4argumento, argumento, ###8
array_indice`
var--, varVV
ne.
typeo+
si!eo+
checUed, unchecUed
-
V
G
7
--var, VVvar
4conversi%n8 var
\, 6
A
-, V
YY, ZZ
Y, Z, YR, ZR, is, RR, GR
Control de precedencia
Acceso a miembro de ob"eto
5numeraci%n de argumentos
5lemento de un array
9ostincremento y
postdecremento
Creaci%n de ob"eto
Hecuperaci%n de tipo 4re+le)i%n8
Hecuperaci%n de tamao
Comprobaci%n de
desbordamiento
1perando en +orma original
Cambio de signo
$ot l%gico
Complemento bit a bit
9reincremente y predecremento
Conversi%n de tipos
0ultiplicaci%n, divisi%n
Hesto de divisi%n
;uma, resta
9rima
rio
9rima
rio
9rima
rio
9rima
rio
9rima
rio
9rima
rio
9rima
rio
9rima
rio
9rima
rio
Pnitar
io
Pnitar
io
Pnitar
io
$inguna
$inguna
$inguna
$inguna
$inguna
$inguna
$inguna
$inguna
$inguna
$inguna
$inguna
$inguna
$inguna
$inguna
$inguna
C!quierda
C!quierda
C!quierda
C!quierda
C!quierda
2S
X
a
W
XX
WW
E *
R, \R, 6R, AR, -R, VR, YYR, ZZR, XR,
aR, WR
2espla!amiento de bits
Helacionales
A$2 a nivel de bits
Q1H a nivel de bits
1H a nivel de bits
A$2 l%gico
1H l%gico
IP5;'C1$
2e asignaci%n
Pnitar
io
Pnitar
io
Pnitar
io
Binari
o
Binari
o
Binari
o
Binari
o
Binari
o
Binari
o
Binari
o
Binari
o
Binari
o
Binari
o
Binari
o
Binari
o
C!quierda
C!quierda
C!quierda
C!quierda
C!quierda
C!quierda
2erecha
5stn puestos en orden de precedencia, es decir, en el caso de haber una e)presi%n con varios de ellos, se
e"ecutan por ese orden# Ba te e)plicar( esto con ms detalles#
:os operadores primarios son operadores de e)presi%n# Vamos siguiendo la tabla y te cuento de qu( van*
5n el caso de /4e)presi%n83, los operadores son realmente los par(ntesis# ;irven para modi+icar la
precedencia# DIu( es esoE 'ranquilo, ms adelante#
5n /ob"eto#miembro3, el operador es el punto# ;irve para especi+icar un miembro de una clase 4sea una
variable, una propiedad o un m(todo8#
5n /m(todo4argumento, argumento, ###83, los operadores vuelven a ser los par(ntesis# 5n este caso, sirven
para especi+icar la lista de argumentos de un m(todo#
5n array_&ndice`, los operadores son los corchetes# ;irven para indicar el elemento de un array o un
indi!ador# DIu( son los arrays y los indi!adoresE Calma, lo de"amos para ms adelante#
2?
:os operadores de incremento 4--8 y decremento 4VV8 sirven para incrementar o disminuir el valor de una
variable en una unidad# 9or e"emplo*
num22%
har que num incremente su valor en una unidad, es decir, si val&a 1@ ahora vale 11# :os operadores de
incremento y decremento se pueden poner delante 4preincremento % predecremento8 o bien detrs
4postincremento % postdecremento8, teniendo comportamientos distintos# 0e e)plico* si hay un postincremento
o postdecremento, primero se toma el valor de la variable y despu(s se incrementa o decrementa# 5n caso
contrario, si lo que hay es un preincremento o un predecremento, primero se incrementa o decrementa la
variable y despu(s se toma el valor de la misma# 5n una l&nea como la anterior esto no se ve claro, porque,
adems, el resultado ser&a el mismo que poniendo --num# ;in embargo, veamos este otro e"emplo 4num vale
1@8*
a = 22num%
b = a7 7%
2espu(s de e"ecutar la primera l&nea, tanto a como num valdr&an 11, ya que el preincremento hace que
primero se incremente num y despu(s se tome su valor, asignndolo as& a la variable a# Ahora bien, despu(s de
e"ecutar la segunda l&nea, b valdr 11, y a valdr 1@# D9or qu(E 9orque el postdecremento de a hace que
primero se asigne su valor actual a b y despu(s se decremente el suyo propio#
5l operador /ne.3 sirve para instanciar ob"etos# A estas alturas nos hemos hartado de verlo, y como vamos a
seguir hartndonos no me voy a enrollar ms con (l#
5l operador /typeo+3 es un operador de re+le)i%n, y la re+le)i%n es la posibilidad de recuperar in+ormaci%n de
un tipo determinado en tiempo de e"ecuci%n# 0s adelante hablaremos largo y tendido sobre la re+le)i%n# 2e
momento me basta con que sepas que typeo+ devuelve el tipo de un ob"eto#
/si!eo+3 devuelve el tamao en bytes que ocupa un tipo determinado# Ahora bien, solamente se puede utili!ar
si!eo+ con tipos valor y en conte)tos de c%digo inseguro# Aunque no vamos a e)plicar ahora este tipo de
conte)to, s& puedes ver c%mo +unciona en e"emplo [1peradores9rimarios[, que se incluye con esta entrega#
/checUed3 y /unchecUed3 sirven para controlar si una e)presi%n provoca o no desbordamiento# 0e e)plico
con un e"emplo* sabemos que las variables de tipo byte pueden almacenar valores entre @ y 2JJ# ;i escribimos
el siguiente c%digo*
b+te i=D@=%
chec0ed #i2=?(%&
"onsole.,rite-ine(i)%
5l programa se compila, pero al e"ecutar se produce un error de desbordamiento, ya que la variable i es de
tipo byte y no puede almacenar valores mayores que 2JJ# ;in embargo, si cambiamos checUed por unchecUed*
b+te i=D@=%
unchec0ed #i2=?(%&
"onsole.,rite-ine(i)%
5l programa no producir&a error de desbordamiento, ya que unchecUed hace que se omitan estos errores# 5n
su lugar, i toma el valor truncado, de modo que despu(s de esa l&nea valdr&a O# Pn asunto importante en el que
quiero que te +i"es* checUed y unchecUed son bloques de c%digo, como se deduce al ver que est escrito con
llaves, de modo que puedes incluir varias l&neas en un mismo bloque checUed o unchecUed#
;&, s&, ya s( que no entiendes eso del valor truncado# Veamos* las variables num(ricas tienen un rango de
datos limitado, es decir, una variable de tipo byte, por e"emplo, no puede almacenar valores menores que @ ni
3@
mayores que 2JJ# Cuando se trunca el valor, lo que se hace es, para que me entiendas, colocar seguidos en
una lista todos los valores que la variable acepta, empe!ando de nuevo por el primero cuando el rango acaba, y
despu(s se va recorriendo esta lista hasta terminar la operaci%n de suma# Como dicho as& resulta bastante
ininteligible, +&"ate en la siguiente tabla y lo vers enseguida*
5n la primera +ila estn la lista de valores que acepta la variable, y en negrilla el valor que contiene# Como
ves, a continuaci%n del =ltimo que acepta vuelve a estar el primero# Al sumarle 1@ 4segunda +ila8 es como si se
+ueran contando valores posibles hacia adelante, de modo que i ahora vale O# 1tro e"emplo usando el tipo sbyte
4que acepta valores entre V12S y 12O8*
sb+te i=?D>%
unchec0ed #i2=?(%&
"onsole.,rite-ine(i)%
2e modo que i valdr&a V12@ despu(s de la suma#
:os operadores unitarios - y V sirven sencillamente para mantener o cambiar el signo de un operando# ;i se
desea mantener el signo de un operando sin cambios, el - se puede omitir# 9or e"emplo*
int i=?(%
int b=7i%
"onsole.,rite-ine(O'alor de i9 #(& 'alor de b9 #?&O, i, b)%
:a variable i valdr&a 1@, y la variable b valdr&a V1@# 9or lo tanto, la salida en la consola ser&a*
Valor de i* 1@ Valor de b* V1@
5l operador unitario G es un not l%gico, es decir, invierte el valor de un dato de tipo boolean# 5n el siguiente
e"emplo, i valdr&a true y b valdr&a +alse*
bool i=true%
bool b=Si%
5l operador unitario 7 es de complemento a nivel de bits, o sea, que devuelve el valor complementario al
operando al que a+ecta# 9ara entender esto usaremos una variable de tipo int y escribiremos tanto su valor
como el de su complementario en he)adecimal*
uint i=?(%
"onsole.,rite-ine(O'alor de i9 #(9TB& 'alor de Ui9 #?9TB&O, i, Ui)%
:a salida en la consola ser&a la siguiente*
Valor de i* @@@@@@@A Valor de 7i* NNNNNNNJ
Como sabes, el n=mero he)adecimal A equivale al 1@ en base decimal, por eso escribe A como valor de i# 5l
n=mero NNNNNNNJ es el complementario en he)adecimal de 1@, y en base decimal equivale a >2?>?LO2SJ#
Veamos estos n=meros en c%digo binario 4que es como se almacenan en la memoria8*
A9 (((( (((( (((( (((( (((( (((( (((( ?(?(
3333333@9 ???? ???? ???? ???? ???? ???? ???? (?(?
As& lo ves per+ectamente* el operador 7 ha cambiado todos los bits, pondiendo un 1 donde hab&a un @ y un @
donde hab&a un uno# DB por qu( ha rellenado tantos n=merosE ;ab&a que me preguntar&as eso# 0uy simple*
DCunto espacio se reserva en memoria para una variable de tipo intE > bytes, DnoE# 9ues bien,
independientemente del valor de la variable, esta siempre ocupa > bytes 4 32 bits8, de modo que si, por e"emplo,
31
le asignamos 1@ 4que en binario es 1@1@8 tendr que colocar 2S ceros delante para ocupar los 2S bits que le
+altan# 5l operador 7 solamente es aplicable a variables de tipo int, uint, long y ulong#
5n el operador 4conversion8, lo que ha de ir entre par(ntesis es el tipo al que se quiere convertir 4int8, 4uint8,
4long8### Ba lo e)plicamos con anterioridad cuando hablamos del sistema de tipos#
:os opeardores \ y 6 son, respectivamente, para multiplicar y dividir# 5s muy sencillo# ;i, por e"emplo,
tenemos la siguiente e)presi%n* >\L62, el resultado ser&a el que se supone* 12#
5l operador A devuelve el resto de una divisi%n# 9or e"emplo, S A 3 devolver&a 2#
:os operadores - y b 4binarios8 son para sumar o restar# >-OV3 devolver&a S#
:os operadores YY y ZZ e+ect=an un despla!amiento de bits hacia la i!quierda o hacia la derecha# Ba s( que
esto de los bits puede que resulte algo con+uso para alguno, as& que me e)tender( un poquito# Veamos el
siguiente e"emplo, tambi(n usando n=meros he)adecimales*
int i=?@%
int b%
int c%
"onsole.,rite-ine(O'alor de i9 #(9T&O, i)%
b = i JJ ?%
"onsole.,rite-ine(O:;ecutado b = i JJ ?%O)%
"onsole.,rite-ine(O'alor de b9 #(9T&O, b)%
c = i VV ?%
"onsole.,rite-ine(O:;ecutado c = i VV ?%O)%
"onsole.,rite-ine(O'alor de c9 #(9T&O, c)%
Veamos la salida en la consola y despu(s la e)aminamos*
'alor de i9 3
:;ecutado b = i JJ ?%
'alor de b9 C
:;ecutado b = i VV ?%
'aor de b9 ?:

Variable Valor he)# Valor binario
i @@@@@@@N @@@@ @@@@ @@@@ @@@@ @@@@ @@@@ @@@@ 1111
b @@@@@@@O @@@@ @@@@ @@@@ @@@@ @@@@ @@@@ @@@@ @111
c @@@@@@15 @@@@ @@@@ @@@@ @@@@ @@@@ @@@@ @@@1 111@
Como puedes apreciar, a la variable b le asignamos lo que vale i despla!ando sus bits hacia la derecha en
una unidad# 5l 1 que hab&a ms a la derecha se pierde# 5n la variable c hemos asignado lo que val&a i
despla!ando sus bits hacia la i!quierda tambi(n en una unidad# Como ves, el la parte derecha se rellena el
hueco con un cero#
32
:os operadores relacionales Y 4menor que8, Z 4mayor que8, YR 4menor o igual que8, ZR 4mayor o igual que8,
is, RR 4igual que8, GR 4distinto de8 establecen una comparaci%n entre dos valores y devuelven como resultado un
valor de tipo boolean 4true o +alse8# Veamos un e"emplo*
int i%
int b%
"onsole.,rite(O:scribe el valor de i9 O)%
i=/nt=D.Parse("onsole.6ead-ine())%
"onsole.,rite(O:scribe el valor de b9 O)%
b=/nt=D.Parse("onsole.6ead-ine())%

"onsole.,rite-ine(OiVb devuelve9 #(&O, (iVb))%
"onsole.,rite-ine(OiV=b devuelve9 #(&O, (iV=b))%
"onsole.,rite-ine(OiJb devuelve9 #(&O, (iJb))%
"onsole.,rite-ine(OiJ=b devuelve9 #(&O, (iJ=b))%
"onsole.,rite-ine(Oi==b devuelve9 #(&O, (i==b))%
"onsole.,rite-ine(OiS=b devuelve9 #(&O, (iS=b))%
:a salida de estas l&neas de programa ser&a la siguiente 4en ro"o est lo que se ha escrito durante la
e"ecuci%n de las mismas8*
:scribe el valor de i9 D
:scribe el valor de b9 =
iVb devuelve9 Krue
iV=b devuelve9 Krue
iJb devuelve9 3alse
iJ=b devuelve9 3alse
i==b devuelve9 3alse
iS=b devuelve9 Krue
5l resultado es muy obvio cuando se trata con n=meros 4o, me"or dicho, con tipos valor8# ;in embargo, DIu(
ocurre cuando utili!amos variables de un tipo re+erenciaE
"ircun4erencia c = ne< "ircun4erencia(A)%
"ircun4erencia d = ne< "ircun4erencia(A)%
"onsole.,rite-ine(Oc==d devuelve9 #(&O, (c==d))%
5l resultado de comparar cRRd ser&a Nalse# ;&, s&, Nalse# A pesar de que ambos ob"etos sean id(nticos el
resultado es, insisto, Nalse# D9or qu(E 9orque una variable de un tipo re+erencia no retorna internamente un
dato espec&+ico, sino un puntero a la direcci%n de memoria donde est almacenado el ob"eto# 2e este modo, al
comparar, el sistema compara los punteros en lugar de los datos del ob"eto, y, por lo tanto, devuelve Nalse,
puesto que las variables c y d no apuntan a la misma direcci%n de memoria# 9ara eso tendr&amos que utili!ar el
m(todo 5quals heredado de la clase ob"ect 4en C#, todas las clases que construyas heredan automticamente
los miembros de la clase base ;ystem#1b"ect8, as&*
33
"ircun4erencia c = ne< "ircun4erencia(A)%
"ircun4erencia d = ne< "ircun4erencia(A)%
"onsole.,rite-ine(Oc.:quals(d) devuelve9 #(&O, c.:quals(d))%
Ahora, el resultado s& ser&a 'rue#
5l operador is devuelve un valor boolean al comparar si un ob"eto 4de un tipo re+erencia8 es compatible con
una clase# 9or e"emplo*
"ircun4erencia c=ne< "ircun4erencia()%
"onsole.,rite-ine(O:l resultado de c is "ircun4erencia es9 #(&O, (c is "ircun4erencia))%
:a salida de estas dos l&neas ser&a la que sigue*
5l resultado de c is Circun+erencia es* 'rue
Al decir si el ob"eto es compatible con la clase me re+iero a que se pueda convertir a esa clase# 9or otro lado,
si el ob"eto contiene una re+erenica nula 4null8 o si no es compatible con la clase, el operador is retornar +alse#
:os operadores X 4and a nivel de bits8, W 4or a nivel de bits8 y a 4)or Vo e)clusivoV a nivel de bits8 hacen una
comparaci%n binaria 4bit a bit8 de dos n=meros devolviendo el resultado de dicha comparaci%n como otro
n=mero# Vamos con un e"emplo para que veas que no te engao*
int i=?(%
int b=C%
int res%
res = i W b%
"onsole.,rite-ine(O#(& W #?& retorna9 Decimal9 #D& NeXadecimal9 #=9T&O, i, b, res, res)%
res = (i Y b)%
"onsole.,rite-ine(O#(& Y #?& retorna9 Decimal9 #D& NeXadecimal9 #=9T&O, i, b, res, res)%
res = (i Z b)%
"onsole.,rite-ine(O#(& Z #?& retorna9 Decimal9 #D& NeXadecimal9 #=9T&O, i, b, res, res)%
:a salida en pantalla de este +ragmento ser&a la que sigue*
?( W C retorna9 Decimal9 D NeXadecimal9 D
?( Y C retorna9 Decimal9 ?@ NeXadecimal9 3
?( Z C retorna9 Decimal9 ?= NeXadecimal9 D
N&"ate primero en que en ninguno de los casos se ha hecho una suma normal de los dos n=meros# Veamos
estas tres operaciones de un modo algo ms claro# 0arcaremos en negrilla los valores que provocan que el bit
resultante en esa posici%n valga 1*
1peraci%n* i X b
Variable Valor dec# Valor he)# Valor binario
i 1@ A @@@@ @@@@ @@@@ @@@@ @@@@ @@@@ @@@@
1@1@
b O O @@@@ @@@@ @@@@ @@@@ @@@@ @@@@ @@@@
@111
3>
Hesultado 2 2 @@@@ @@@@ @@@@ @@@@ @@@@ @@@@ @@@@
@@1@

1peraci%n* i W b
Variable Valor dec# Valor he)# Valor binario
i 1@ A @@@@ @@@@ @@@@ @@@@ @@@@ @@@@ @@@@
1@1@
b O O @@@@ @@@@ @@@@ @@@@ @@@@ @@@@ @@@@
@111
Hesultado 1J N @@@@ @@@@ @@@@ @@@@ @@@@ @@@@ @@@@
1111

1peraci%n* i a b
Variable Valor dec# Valor he)# Valor binario
i 1@ A @@@@ @@@@ @@@@ @@@@ @@@@ @@@@ @@@@
1@1@
b O O @@@@ @@@@ @@@@ @@@@ @@@@ @@@@ @@@@
@111
Hesultado 13 2 @@@@ @@@@ @@@@ @@@@ @@@@ @@@@ @@@@
11@1
Vamos operaci%n por operaci%n# 5n la primera de ellas, i X b, el resultado es @@1@ porque el operador X
hace una comparaci%n bit a bit, devolviendo uno cuando ambos bits comparados tambi(n valen uno 4se ve
claramente que tanto para i como para b, el segundo bit por la derecha vale 18, y @ cuando alguno de ellos 4o
los dos8 es @#
5n la segunda operaci%n, i W b, el resultado es 1111 porque el operador W devuelve 1 cuando al menos uno de
los bits comparados es 1, y @ cuando ambos bits comparados son tambi(n @#
5n el tercer caso, i a b, el resultado es 11@1 porque el operador a devuelve 1 cuando uno y s%lo uno de los
bits comparados vale 1, y cero cuando ambos bits valen 1 o cuando ambos bits valen @#
:os operadores XX 4A$2 l%gico8 y WW 41H l%gico8 se ocupan de comparar dos valores de tipo boolean y
retornan como resultado otro valor de tipo boolean# 5l operador XX devuelve true cuando ambos operandos son
true, y +alse cuando uno de ellos o los dos son +alse# 5l operador WW devuelve true cuando al menos uno de los
operandos es true 4pudiendo ser tambi(n true los dos8, y +alse cuando los dos operandos son +alse# ;uelen
combinarse con los operaciones relacionales para establecer condiciones ms comple"as# 9or e"emplo, la
siguiente e)presi%n devolver&a true si un n=mero es mayor que 1@ y menor que 2@*
(num J ?() WW (num V D()
9ara que veas otro e"emplo, la siguiente e)presi%n devolver&a true si el n=mero es igual a 1@ o igual a 2@*
(num == ?() YY (num == D()
5l operador E * 4question8 eval=a una e)presi%n como true o +alse y devuelve un valor que se le especi+ique
en cada caso 4;i programabas en Visual Basic, equivale ms o menos a la +unci%n ii+8# Vamos a verlo con un
e"emplo*
3J
strin$ mensa;e = (num == ?() Q O:l n5mero es ?(O9 O:l n5mero no es ?(O%
N&"ate bien# 2elante del interrogante se pone la e)presi%n que debe retornar un valor boolean# ;i dicho valor
es true, el operador retornar lo que est( detrs del interrogante, y si es +alse retornar lo que est( detrs de los
dos puntos# $o tiene por qu( retornar siempre un string# 9uede retornar un valor de cualquier tipo# 9or lo tanto,
si en este e"emplo num valiera 1@, la cadena que se asignar&a a mensa"e ser&a [5l n=mero es 1@[, y en caso
contrario se le asignar&a [5l n=mero no es 1@[#
5l operador de asignaci%n 4R8 4s&, s&, ya s( que nos hemos hartado de usarlo, pero vamos a verlo con ms
pro+undidad8 asigna lo que hay a la derecha del mismo en la variable que est a la i!quierda# 9or e"emplo, la
e)presi%n a R b asignar&a a la variable a lo que valga la variable b# :a primera norma que no debes olvidar es
que ambas variables han de ser compatibles# 9or e"emplo, no puedes asignar un n=mero a una cadena, y
viceversa, tampoco puedes asignar una cadena a una variable de alg=n tipo num(rico#
;obre esto hay que tener en cuenta una di+erencia importante entre la asignaci%n de una variable de tipo
valor a otra 4tambi(n de tipo valor, obviamente8 y la asignaci%n de una variable de tipo re+erencia a otra#
Veamos el siguiente +ragmento de c%digo*
int a=@%
int b=a%
b22%
'ras la e"ecuci%n de estas tres l&neas, e+ectivamente, a valdr&a J y b valdr&a L# Ahora bien, Dqu( ocurre si
usamos variables de tipo re+erenciaE Vemoslo 4la propiedad Hadio es de lectura6escritura8*
"ircun4erencia a=ne< "ircun4erencia()%
a.6adio=A%
"ircun4erencia b=a%
b.6adio22%
5st claro que tanto a como b sern ob"etos de la clase circun+erencia# 2espu(s de la e"ecuci%n de estas
l&neas, cunto valdr el radio de la circun+erencia bE 5+ectivamente, J# DB el de la circun+erencia aE FF;orpresaGG
'ambi(n J# DC%mo es esto, si el valor que le hemos asignado al radio de la circun+erencia a es >E Volvamos a
lo que dec&amos sobre los tipos re+erencia* reservaban espacio en el mont%n y devolv&an un puntero de tipo
seguro a la direcci%n de memoria reservada# :o que ha ocurrido aqu&, por lo tanto, es que al hacer la asignaci%n
Circun+erencia bRaK no se ha reservado un nuevo espacio en el mont%n para la circun+erencia b* dado que a, al
tratarse de una variable de tipo re+erencia, devuelve internamente un puntero a la direcci%n reservada para este
ob"eto, es este puntero el que se ha asignado a la variable b, de modo que las variables a y b apuntan ambas al
mismo espacio de memoria# 9or lo tanto, cualquier modi+icaci%n que se haga en el ob"eto usando alguna de
estas variables quedar re+le"ado tambi(n en la otra variable, ya que, en realidad, son la misma cosa o, dicho de
otro modo, representan al mismo ob"eto# ;i quer&amos ob"etos distintos, o sea espacios de memoria distintos en
el mont%n, ten&amos que haberlos instanciado por separado, y despu(s asignar los valores a las propiedades
una por una*
"ircun4erencia a=ne< "ircun4erencia()%
a.6adio=A%
"ircun4erencia b=ne< "ircun4erencia()%
b.6adio=a.6adio%
b.6adio22%
3L
Ahora s&, el radio de la circun+erencia a ser > y el de la circun+erencia b ser J# Cuidado con esto porque
puede conducir a muchos errores# ;i hubi(ramos escrito el c%digo de la siguiente +orma*
"ircun4erencia a=ne< "ircun4erencia()%
a.6adio=A%
"ircun4erencia b=ne< "ircun4erencia()%
b=a%
b.6adio22%
Tubiera ocurrido algo parecido al primer caso# A pesar de haber instanciado el ob"eto b por su lado
4reservando as& un espacio de memoria para b en el mont%n distinto del de a8, al asignar bRa, la re+erencia de b
se destruye, asignndosele de nuevo la de a, de modo que ambas variables volver&an a apuntar al mismo
ob"eto dando el mismo resultado que en el primer caso, es decir, que el radio de ambas circun+erencias 4que en
realidad son la misma8 ser&a J#
5l resto de operadores de asignaci%n son operadores compuestos a partir de otro operador y el operador de
asignaci%n# Veamos a qu( equivalen los operadores \R, 6R, AR, -R, VR, YYR, ZZR, XR, aR, WR
num H= ?(% )) :quivale a num = num H ?(
num )= ?(% )) :quivale a num = num ) ?(
num [= ?(% )) :quivale a num = num [ ?(
num 2= ?(% )) :quivale a num = num 2 ?(
num 7= ?(% )) :quivale a num = num 7 ?(
num VV= ?(% )) :quivale a num = num VV ?(
num JJ= ?(% )) :quivale a num = num JJ ?(
num W= ?(% )) :quivale a num = num W ?(
num Z= ?(% )) :quivale a num = num Z ?(
num Y= ?(% )) :quivale a num = num Y ?(
:a precedencia de operadores determina la prioridad con la que se e"ecutan cuando hay varios de ellos en
una misma e)presi%n# 5+ectivamente, el resultado puede ser distinto en +unci%n del orden en que se e"ecuten#
Vamos con un e"emplo# :a e)presi%n > - 3 \ L V S devolver&a 1>, ya que primero se hace la multiplicaci%n y
despu(s las sumas y las restas# ;i hubi(ramos querido modi+icar dicha precedencia habr&a que haber usado
par(ntesis* 4>-38\LVS devolver&a 3>, ya que primero se e"ecuta lo que hay dentro del par(ntesis, despu(s la
multiplicaci%n y, por =ltimo, la resta# Como ves, la precedencia de operadores no cambia respecto de la que
estudiamos en el colegio en las clases de matemticas# 5n la tabla que tienes al principio de esta entrega, los
operadores estn colocados en orden de precedencia#
:a asociatividad de los operadores indica el orden en que se e"ecutan cuando tienen la misma precedencia#
1bviamente, esto es aplicable solamente a los operadores binarios# 'odos los operadores binarios son
asociativos por la i!quierda salvo los de asignaci%n, que son asociativos por la derecha# 9or e"emplo, en la
e)presi%n >-3-2, primero se hace >-3 y a este resultado se le suma el dos, dado que el operador - es
asociativo por la i!quierda# ;in embargo, en la e)presi%n b R c R d, como el operador de asignaci%n es
asociativo por la derecha, primero se asigna a c el valor de d, y despu(s se asigna a b el valor de c, es decir,
que tras esta e)presi%n las tres variables valdr&an lo mismo#
3O
0 1uinta entrega (Nuestra primera aplicacin en C#$ 23ola mundo4.!
Antes de seguir adelante con el curso, permitidme un pequeo inciso para dirigirme a aquellos de vosotros
que os est(is iniciando en el mundo de la programaci%n a trav(s de este curso# Comprendo vuestra desa!%n*
hasta ahora llevamos cuatro entregas y, prcticamente, no hemos visto nada ms que teor&a# $o entend(is casi
nada de los e"emplos que os hab(is ba"ado y, por si +uera poco, lo ms probable es que ahora mismo no os
acord(is de c%mo se hac&an el O@ o el S@ por ciento de las cosas que hemos visto# 9uede, incluso, que alguno
de vosotros se est( empe!ando a plantear si seguir adelante o de"arlo por imposible# 9ues bien, si os sirve de
consuelo, yo tambi(n su+r& todo esto cuando empec( hace ya unos quince aos y, sin embargo, aqu& me ten(is#
5n realidad no es tan complicado como pueda pareceros ahora# $o importa que no record(is, por e"emplo,
c%mo se llamaban los tipos de datos, o qu( rango de datos pod&an almacenar, o cul era el operador de
complemento binario, por e"emplo# 5sto es lo de menos# :o importante es que sepas qu( cosas e)isten, qu(
posibilidades tienes y d%nde buscarlas# 9uedes creerme* casi nadie se sabe un lengua"e de programaci%n
entero de memoria, de principio a +in 4yo me incluyo, por supuesto8# 5sto es casi imposible# ;in embargo, s&
sabemos cules son las posibilidades, de modo que, cuando no recordamos c%mo se hac&a tal o cual cosa, nos
basta con consultar la documentaci%n para recordarlo#
Adems, a partir de ahora vamos a empe!ar ya a ilustrar la teor&a con pequeos programas, cosa que,
pod(is creerme, resulta mucho ms grati+icante# $o lo hab&amos hecho hasta ahora porque, como vais a
comprobar, nos +altaban los cimientos* espacios de nombres, variables, constantes, tipos, operadores### Ahora
ya los conocemos, o al menos nos suenan, y como el camino se hace andando, pues es lo que vamos a hacer*
andar#
-.1 (uestra primera aplicain en C#
Vamos a empe!ar con una sencilla aplicaci%n para que te vayas +amiliari!ando tanto con el entorno de
desarrollo de Visual ;tudio #$5' como con la clase ;ystem#Console que tanto has visto hasta ahora 4y vas a
seguir vi(ndola hasta que empecemos a desarrollar aplicaciones para <indo.s8# ;e trata, c%mo no, de la
aplicaci%n ms repetida en todos los lengua"es de programaci%n de la Tistoria de la humanidad* la aplicaci%n
[Tola mundo[#
Venga, iremos paso a paso# :o primero que hay que hacer es e"ecutar Visual ;tudio #$5'# Ba s( que es
evidente, pero ten&a que decirlo### Cuando lo hagas aparecer este cuadro de dilogo*
3S
2ale el nombre y ubicaci%n que quieras para tu aplicaci%n# :o que s& te aconse"o es que marques la casilla
de veri+icaci%n [Crear directorio para la soluci%n[, como est en la imagen# As& tendrs bien organi!ados todos
los archivos de la misma# N&"ate en los dos cuadros que hay* el de la i!quierda sirve, como puedes ver, para
indicar el lengua"e que vas a utili!ar, y el de la derecha para indicar el tipo de aplicaci%n que vas a disear# 0s
adelante los iremos viendo, pero a=n no es el momento ms adecuado# 2e momento vamos a crear
aplicaciones de consola 4es decir, programas que se e"ecutan en una ventana de 21;8# DC%moE DIue quieres
empe!ar ya a hacer aplicaciones para <indo.sE As& me gusta, hombre, que seis valientes# ;in embargo, no
vamos a empe!ar con eso a=n# 9or ahora lo que quiero es ensearos el lengua"e C# propiamente dicho, ya que
comen!ar desde cero 4o prcticamente cero8 diseando aplicaciones para <indo.s sin conocer el lengua"e no
har&a sino entorpecer el aprendi!a"e del mismo# Cuando cono!cas el lengua"e podrs comprobar que no te
cuesta ning=n traba"o adaptarte al diseo para <indo.s#
Venga# Ahora quiero que te +i"es un poco en el entorno de desarrollo*
3?
'iene barra de men=s y barras de botones como casi todos los programas de hoy en d&a# 2eba"o, y de
i!quierda a derecha, tienes* 5l cuadro de herramientas, que se desplegar cuando pases el rat%n por encima# ;i
quieres que est( siempre desplegado 4al estilo del cuadro de herramientas de VB L#@, por e"emplo8 tendrs que
hacer clic sobre un pequeo icono con +orma de chincheta que hay al lado del bot%n cerrar de dicho cuadro# A
continuaci%n tienes el espacio de traba"o* en (l te aparecern las ventanas de c%digo de cada archivo, as& como
el e)aminador de ob"etos, la ayuda### Cada ventana mostrar una pequea pestaa en la parte superior 4"usto
deba"o de las barras de botones8, para que puedas pasar de unas a otras con un solo clic 4en esta imagen
tienes las pestaas Class1#cs y AssemblyCn+o#cs8# A la derecha tienes el e)plorador de soluciones# Bueno###
realmente son cuatro ventanas en una* +&"ate en las pestaas que tiene "usto deba"o* el e)plorador de
soluciones, que se ocupa de mostrarte los archivos que componen la soluci%n que ests creando con una
"erarqu&a en rbolK la vista de clases, que muestra las clases que componen la soluci%n, organi!adas tambi(n
en rbolK el contenido de la ayuda, que muestra eso precisamenteK y la b=squeda de ayuda, que, obviamente,
sirve para buscar algo en la ayuda# ,usto deba"o est la ventana de propiedades, que se ocupa de mostrar las
propiedades de alg=n archivo del proyecto, o alguna clase, o alg=n ob"eto, o alg=n m(todo### Aqu& ten(is
tambi(n otra pestaa, la de ayuda dinmica, que va mostrando temas de ayuda que tienen que ver con lo que
ests haciendo 4si escribes class te salen temas sobre class, si escribes string te salen temas sobre eso, etc8#
2eba"o tienes ms ventanas 4la lista de tareas, ventana de comandos, resultados, resultados de b=squeda de
s&mbolos8# 9uede que a ti no te coincidan, ya que todo esto es per+ectamente con+igurable, y como yo me lo he
con+igurado a mi gusto y no recuerdo cules eran las opciones por de+ecto, pues eso, que puede ser que a ti no
te apare!can las mismas ventanas que a mi 4sobre todo aqu& aba"o8# 5n el men= Ver puedes mostrar y ocultar
todas las ventanas que quieras, y tambi(n puedes cambiarlas de sitio simplemente arrastrndolas con el rat%n#
Venga, poned todo esto como ms c%modo os resulte y seguimos###
>@
Bueno, ahora que ya me he asegurado de que nos vamos a entender cuando yo hable de una ventana o de
otra, podemos continuar# Visual ;tudio #$5' guarda los distintos archivos que componen un programa con
distintas e)tensiones, dependiendo de para qu( sirva# :os distintos archivos que contienen el c%digo +uente en
lengua"e C# los guarda con la e)tensi%n [cs[ 4+i"aos en el e)plorador de soluciones, en el cual ten(is Class1#cs y
AssemblyCn+o#cs8# Al archivo de proyecto que est escrito en C# le pone la e)tensi%n [cspro"[# 5l archivo de
proyecto contiene diversa in+ormaci%n sobre el mismo* opciones de compilaci%n, una lista de re+erencias y una
lista de los archivos que lo componen# 0s adelante entraremos en ms detalles# 5l =ltimo archivo que nos
interesa por ahora es el archivo de soluci%n, al cual le pone la e)tensi%n [sln[# 5ste contiene in+ormaci%n sobre
los proyectos que componen la soluci%n#
5l programa que vamos a desarrollar mostrar sencillamente el te)to [Tola 0undo[ en la consola 4a partir de
ahora, la consola es la ventana de 21;8# 2e entrada, Visual ;tudio #$5' nos ha escrito casi todo el c%digo# 5s
el que sigue*
usin$ G+stem%
namespace Nola!undo#
))) Vsummar+J
))) Gummar+ description 4or "lass?.
))) V)summar+J
class "lass?
#
static void !ain(strin$\] ar$s)#
))
)) K^D^9 Add code to start application here
))
&
&
&
;iempre que crees una aplicaci%n de Consola en C#, Visual ;tudio #$5' aadir este c%digo# ;eguro que,
con lo que hemos visto hasta ahora, te suena mucho# :a directiva using ;ystem nos permitir usar miembros de
este espacio de nombres sin poner la palabra ;ystem delante# :uego hay de+inido un espacio de nombres para
la aplicaci%n, que se llama igual que la misma 4Tola0undo8# :uego est el sumario, que sirve para que puedas
poner ah& lo que quieras 4un resumen de lo que hace el programa, una lista de bodas, los 5vangelios###, aunque
normalmente se suele poner un resumen de lo que hace el programa8# 9or =ltimo, una clase llamada Class1
con un m(todo 0ain que es static, que es el m(todo por el que empe!ar la e"ecuci%n del programa# 5sas tres
l&neas que hay dentro del m(todo contienen un comentario 4realmente hubiera bastado una l&nea sola, pero
supongo que as& se ve me"or8# 'e lo tradu!co* [9ara hacer* Aade aqu& el c%digo para que empiece la
aplicaci%n[# 1 sea, que ya sabes d%nde hay que escribir el c%digo de nuestra aplicaci%n [Tola 0undo[* en el
m(todo 0ain# 5scribe esto*
"onsole.,rite-ine(ONola !undoO)%
Ba est# DBaE ;&, s&# Ba est# Vamos a probarla, a ver qu( tal +unciona# Ta! clic en el men=
[2epurarcCniciar[, o bien ha! clic en el bot%n que tiene un tringulo a!ul apuntando hacia la derecha, o bien
pulsa la tecla NJ# ;e ha abierto una ventana de 21; y se ha cerrado rpidamente# $o pasa nada# 'odo est
bien# 9ara evitar que se cierre inmediatamente despu(s de e"ecutarse tienes dos opciones* e"ecutar la
aplicaci%n sin opciones de depuraci%n 4men= 2epurarcCniciar sin depurar[ o bien pulsar Control-NJ8K o aadir
>1
una l&nea de c%digo ms para que espere a que se pulse intro antes de cerrar la ventana# Bo s( que,
seguramente, vas a elegir la primera de ellas 4e"ecutar sin depuraci%n8, pero es me"or la segunda, y luego te
e)plico por qu(# 5scribe la siguiente l&nea a continuaci%n de la que escribimos antes*
strin$ a = "onsole.6ead-ine()%
'ranquilo que luego te e)plico el c%digo# Ahora vuelve a e"ecutar como hicimos la primera ve!# Como ves,
ahora la ventana de 21; se queda abierta hasta que pulses intro# DB por qu( es me"or hacerlo as& que e"ecutar
sin depuraci%nE 9orque si e"ecutamos sin depuraci%n, obviamente, no podremos usar las herramientas de
depuraci%n, como poner puntos de interrupci%n, e"ecutar paso a paso y cosas as&# 5n esta aplicaci%n no tendr&a
mucho sentido, es cierto, pero cuando hagamos programas ms grandes podr(is comprobar que todas estas
herramientas son verdaderamente =tiles#
9or lo tanto, todo el c%digo de nuestro programa terminado es este*
usin$ G+stem%
namespace Nola!undo#
))) Vsummar+J
))) Gummar+ description 4or "lass?.
))) V)summar+J
class "lass?#
static void !ain(strin$\] ar$s)#
))
)) K^D^9 Add code to start application here
))
"onsole.,rite-ine(ONola !undoO)%
strin$ a = "onsole.6ead-ine()%
&
&
&
DIu( es eso de [Console[E Bueno, Console es una clase que pertenece a la biblioteca de clases de #$5'
Nrame.orU y est dentro del espacio de nombres ;ystem# ;irve para e+ectuar diversas operaciones de consola#
$osotros nos +i"aremos principalmente en dos de ellas* 5scribir datos en la consola y leer datos de la misma#d
9ara escribir los datos tenemos los m(todos <rite y <rite:ine# 5ste =ltimo, como puedes apreciar, es el que
hemos usado para nuestra aplicaci%n# :a di+erencia entre ambos es que <rite escribe lo que sea sin aadir el
carcter de +in de l&nea a la cadena, de modo que lo siguiente que se escriba se pondr a continuaci%n de lo
escrito con <rite# <rite:ine s& aade el carcter de +in de l&nea a la cadena, de modo que lo siguiente que se
escriba se colocar en la siguiente l&nea# 5s decir, el siguiente +ragmento de c%digo*
"onsole.,rite(ONolaO)%
"onsole.,rite-ine(OPepeO)%
"onsole.,rite(OP"mo andas, O)%
"onsole.,rite-ine(Ot*oQO)%
1+recer&a este resultado en la consola*
>2
NolaPepe
P"mo andas, t*oQ
Tay 1? sobrecargas del m(todo <rite:ine, y otras 1S del m(todo <rite# DIu( es eso de las sobrecargasE
C%mo, Dhe dicho sobrecargasE, Ds&E 9ues no quer&a decirlo todav&a# 5n +in### veremos muy detallada la
sobrecarga de m(todos cuando lleguemos al tema de los m(todos# 0ientras tanto, que sepas que, ms o
menos, quiero decir que podemos usar estos m(todos de diversas maneras 4si es que me meto en unos l&os###8#
9ara leer datos de la consola tenemos los m(todos Head y Head:ine# $o, no son equivalentes a <rite y
<rite:ine pero en direcci%n contraria# 5l m(todo Head obtiene el primer carcter que a=n no se haya e)tra&do
del bu++er de entrada# 5n caso de que el bu++er est( vac&o o bien contenga un caracter no vlido, retornar V1#
;in embargo, Head:ine obtiene una l&nea completa del bu++er de entrada, es decir, toda la cadena de caracteres
hasta encontrar el carcter de +in de l&nea 4bueno, realmente el +in de l&nea est de+inido por dos caracteres,
pero tampoco vamos a darle ms importancia, ya que esto de la consola viene muy bien para aprender, pero
poco ms8# 9or cierto, estos dos m(todos no estn sobrecargados# DIu( es eso del bu++er de entradaE 9ues
vamos a ver* cuando el programa e"ecuta un m(todo Head y t= escribes Tola, don 9epito, hola don ,os( y
despu(s pulsas la tecla intro, todo lo que has escrito va a dicho bu++er# Head, dec&amos, obtiene el primer
carcter no e)tra&do del bu++er, de modo que la primera ve! devuelve la T, la segunda la o, luego la l, luego la a,
y as& hasta terminar el bu++er# 9or lo tanto, el bu++er de entrada es, para que me entiendas, una !ona de memoria
donde se almacenan datos que se han ingresado pero que a=n no se han le&do#
$o te voy a contar una por una c%mo son todas las sobrecargas de los m(todos <rite y <rite:ine 4tampoco
merece la pena, la verdad8# 'e voy a e)plicar de qu( +ormas los vamos a usar ms com=nmente, y si en alg=n
momento del curso los usamos de otro modo, ya te lo e)plicar(# 5l m(todo <rite lo usaremos de las siguientes
+ormas*
"onsole.,rite(cadena)%
"onsole.,rite(ob;eto)%
"onsole.,rite(ob;eto.miembro)%
"onsole.,rite(Oliteral #(& literal #?& literal #D&...O, dato(, dato?, datoD)%
5n la primera l&nea, [cadena[ puede ser una cadena literal 4por lo tanto, entre comillas8 o bien una variable
de tipo string 4por lo tanto, sin comillas8# 5n la segunda l&nea, [ob"eto[ ser cualquier variable de cualquier tipo, o
bien una e)presi%n# 5n la tercera l&nea, [ob"eto[ ser de nuevo cualquier variable de cualquier tipo, y lo
usaremos as& cuando queramos escribir lo que devuelva un miembro de ese ob"eto 4una propiedad o un
m(todo8# :a =ltima +orma del m(todo <rite la usaremos cuando queramos intercalar datos dentro de un literal#
<rite escribir&a esa cadena intercalando dato@ donde est ]@^, dato1 donde est ]1^, y dato 2 donde est ]2^, y
as& tantos datos como queramos intercalar# 9or cierto, estos datos pueden ser ob"etos de cualquier tipo, o bien
e)presiones o bien lo que devuelva alg=n miembro de alg=n ob"eto# 9ara los programadores de C, es algo
parecido a print+, pero mucho ms +cil de usar#
5l m(todo <rite:ine lo usaremos de las mismas +ormas que el m(todo <rite, adems de esta*
"onsole.,rite-ine()%
Psaremos el m(todo <rite:ine as& cuando queramos poner un salto de l&nea en la consola#
:os m(todos Head y Head:ine no tienen sobrecargas, as& que solamente los podremos usar as&*
variable = "onsole.6ead()%
cadena = "onsole.6ead-ine()%
5n la primera l&nea he puesto [variable[ porque Head devolver un valor de tipo int, equivalente al c%digo del
carcter unicode recibido del bu++er# 9or este motivo, la variable solamente puede ser de tipo int# 5n la segunda
>3
l&nea, Head:ine retorna siempre un valor de tipo string, por lo tanto, [cadena[ debe ser una variable de tipo
string#
9ara terminar ya con esta entrega vamos a terminar de [maquear[ un poquito el programa# As& vas cogiendo
buenas costumbres desde el principio# Como a mi no me gusta nada eso de class1, vamos a cambiarlo# Ta! clic
sobre el archivo Class1#cs en el e)plorador de soluciones# A continuaci%n, en la ventana de propiedades busca
[$ombre de archivo[ y cmbia eso de Class1#cs por un nombre ms signi+icativo 4Tola0undo#cs, por e"emplo8#
9or =ltimo, en la ventana de c%digo, cambia class1 por Tola0undoApp# 5s una buena costumbre colocar el
m(todo 0ain en una clase que se llame como la aplicaci%n ms las letras App# 9uedes borrar si quieres el
sumario 4ya que este programa no creo que necesite muchas apreciaciones8 y los comentarios que aadi%
Visual ;tudio en el m(todo 0ain 4s&, eso de 66 '121* ###8# Ba hemos terminado# $o te olvides de guardarlo antes
de salir de Visual ;tudio #$5', con ArchivocMuardar 'odo, o bien el bot%n con varios disUettes dibu"ados, o bien
la combinaci%n de teclas Control-0ay=sculas-NJ# 2e todos modos, si se te olvida y ha habido modi+icaciones,
Visual ;tudio te preguntar si quieres guardarlas antes de salir# $uestro primer programa en C# ha quedado
as&*
usin$ G+stem%
namespace Nola!undo#
class Nola!undoApp#
static void !ain(strin$\] ar$s)#
"onsole.,rite-ine(ONola !undoO)%
strin$ a = "onsole.6ead-ine()%
&
&
&
5 #exta entrega (+6todos (17 parte!( sobrecarga de m6todos( argumentos
por valor & por re8erencia & m6todos static.!
..1 Mtodos
Ba di"imos en la introducci%n a la 911 que los m(todos son todos aquellos bloques de c%digo que se
ocupan de mane"ar los datos de la clase# Hecapitulemos un momento y echemos un nuevo vista!o al e"emplo
del coche que pusimos en la introducci%n# 5n (l ten&amos tres m(todos* Acelerar, Mirar y Nrenar, que serv&an
para modi+icar la velocidad y la direcci%n de los ob"etos de la clase coche# Como ves, los m(todos sirven para
que los ob"etos puedan e"ecutar una serie de acciones# Veamos c%mo se de+ine un m(todo en C#*
acceso tipo Nombre!etodo(KipoAr$? ar$uento?, KipoAr$D ar$uentoD ...)#
)) Aqu* se codi4ica lo que tiene que hacer el m8todo
&
Veamos* acceso es el modi+icador de acceso del m(todo, que puede ser private, protected, internal o public
4como las variables8# 9osteriormente el tipo de retorno, es decir, el tipo de dato que devolver el m(todo 4que
puede ser cualquier tipo8# :uego el nombre del m(todo 4sin espacios en blanco ni cosas raras8# 2espu(s, entre
par(ntesis y separados unos de otros por comas, la lista de argumentos que aceptar el m(todo* cada uno de
ellos se especi+icar poniendo primero el tipo y despu(s el nombre del mismo# 9or +in, la llave de apertura de
bloque seguida del c%digo del m(todo y, para terminarlo, la llave de cierre del bloque#
>>
Vamos a ilustrar esto con un e"emplo* vamos a construir una clase Bol&gra+oK sus m(todos sern, por
e"emplo, 9intar y Hecargar, que son las operaciones que se suelen e+ectuar con un bol&gra+o# Ambos m(todos
modi+icarn la cantidad de tinta del boli, valor que podr&amos poner en una propiedad llamada 'inta, por
e"emplo# 9ara aquellos que cono!cis la programaci%n procedimental, un m(todo es como un procedimiento o
una +unci%n# 5n determinadas ocasiones necesitaremos pasarle datos a los m(todos para que estos puedan
hacer su traba"o# 9or e"emplo, siguiendo con el bol&gra+o, puede que necesitemos decirle al m(todo 9intar la
cantidad de tinta que vamos a gastar, igual que hac&amos con el m(todo Acelerar de la clase Coche, que
ten&amos que decirle cunto quer&amos acelerar# 9ues bien, estos datos se llaman argumentos# Vamos a verlo*
usin$ G+stem%
class oli$ra4o#
protected int color=(%
protected b+te tinta=?((%
public bool Pintar(b+te $asto) #
i4 ($astoJthis.tinta) return 4alse%
this.tinta 7= $asto%
"onsole.,rite-ine(OGe $astaron #(& unidades de tinta.O, $asto)%
return true%
&
public void 6ecar$ar() #
this.tinta=?((%
"onsole.,rite-ine(Ool*$ra4o recar$adoO)%
&
public int "olor #
$et #
return this.color%
&
set #
this.color = value%
&
&
public b+te Kinta #
$et #
return this.tinta%
&
&
&
>J
2e momento +&"ate bien en lo que conoces y en lo que estamos e)plicando, que son los m(todos# :o dems
lo iremos conociendo a su debido tiempo# 5n este e"emplo tienes los m(todos 9intar y Hecargar 4presta
especial atenci%n a la sinta)is8# 5l primero disminuye la cantidad de tinta, y el segundo establece esta cantidad
nuevamente a 1@@, es decir, rellena el bol&gra+o de tinta#
:os m(todos tambi(n pueden devolver un valor despu(s de su e"ecuci%n si +uera necesario# 5n este
e"emplo, el m(todo 9intar devuelve 'rue si la operaci%n se ha podido e+ectuar y Nalse si no se ha podido 4+&"ate
en que el tipo de retorno es bool8# 2e este modo, el cliente simplemente deber&a +i"arse en el valor devuelto por
el m(todo para saber si todo ha +uncionado correctamente, sin tener que comparar los datos de antes con los
de despu(s 4es decir, sin comprobar si el valor de la propiedad tinta, en este caso, se ha visto modi+icado8# 5ste
m(todo 0ain que vamos a poner a continuaci%n demostrar el +uncionamiento de la clase Bol&gra+o*
class oli$ra4oApp#
static void !ain() #
)) /nstanciacin del ob;eto
oli$ra4o boli = ne< oli$ra4o()%
"onsole.,rite-ine(O:l boli tiene #(& unidades de tintaO, boli.Kinta)%
"onsole.,rite-ine(Oboli.Pintar(@() devuelve #(&O, boli.Pintar(@())%
"onsole.,rite-ine(OAl boli le quedan #(& unidades de tintaO, boli.Kinta)%
"onsole.,rite-ine(Oboli.Pintar(>() devuelve #(&O, boli.Pintar(>())%
"onsole.,rite-ine(OAl boli le quedan #(& unidades de tintaO, boli.Kinta)%
boli.6ecar$ar()%
"onsole.,rite-ine(OAl boli le quedan #(& unidades de tintaO, boli.Kinta)%
strin$ a = "onsole.6ead-ine()%
&
&
Bien, la salida en consola de este programa ser&a la siguiente*
:l boli tiene ?(( unidades de tinta
Ge $astaron @( unidades de tinta.
boli.Pintar(@() devuelve Krue
Al boli le quedan @( unidades de tinta
boli.Pintar(>() devuelve 3alse
Al boli le quedan @( unidades de tinta
ol*$ra4o recar$ado
Al boli le quedan ?(( unidades de tinta
5)aminemos el c%digo y el resultado un momento# 5n primer lugar, como ves, instanciamos el ob"eto boli
con el operador ne. y escribimos la cantidad de tinta del mismo en la consola# 5+ectivamente, 'inta vale 1@@
porque la variable protected que almacena este valor 4la variable tinta8 est iniciali!ada a 1@@ en la declaraci%n#
A continuaci%n, en el m(todo 0ain, se pretende escribir lo que devuelva el m(todo 9intar# ;in embargo, como
ves, antes de eso aparece en la consola otra l&nea, la que escribe precisamente este m(todo 49intar8# D9or qu(
sale primero esto y despu(s lo que est escrito en el m(todo 0ainE 9ues hombre, para que el m(todo devuelva
>L
algo se tiene que haber e"ecutado primero# :%gico, DnoE Bien, como ves, la primera llamada al m(todo 9intar
devuelve 'rue porque hab&a tinta su+iciente para hacerlo# 2espu(s se escribe la tinta que queda y se vuelve a
llamar al m(todo 9intar, pero esta ve! le pasamos como argumento un n=mero mayor que la tinta que quedaba#
9or este motivo, ahora el m(todo pintar devuelve Nalse y no escribe nada en la consola# 9osteriormente se
e"ecuta el m(todo Hecargar, que no devuelve nada y escribe [Bol&gra+o recargado[ en la consola, y, por =ltimo,
se vuelve a escribir la cantidad de tinta, que vuelve a ser 1@@# 2e todo esto podemos e)traer dos ideas
principales con las que quiero que te quedes de momento* una es que los m(todos pueden devolver un valor de
cualquier tipo, y la otra es que si un m(todo no devuelve nada hay que declararlo de tipo void#
Veamos todo esto con otro e"emplo# Vamos a escribir una clase 4muy simpli+icada, eso s&8 que se ocupe de
mane"ar gastos e ingresos, sin intereses ni nada*
class "uentas#
protected double saldo=(%
public double Galdo #
$et #
return this.saldo%
&
&
public bool NuevoGasto(double cantidad) #
i4 (cantidadV=() return 4alse%
this.saldo 7= cantidad%
return true%
&

public bool Nuevo/n$reso(double cantidad) #
i4 (cantidad V=() return 4alse%
this.saldo 2= cantidad%
return true%
&
&
5n esta clase hay una variable protected 4o sea, que es visible dentro de la clase y dentro de clases
derivadas, pero no desde el cliente8, una propiedad y dos m(todos# Como te di"e antes, presta especial atenci%n
a lo que conoces y, sobre todo, a los m(todos, que es con lo que estamos# :os m(todos $uevoCngreso y
$uevoMasto se ocupan de modi+icar el valor de la variable saldo seg=n cunto se ingrese o se gaste# Ahora
bien, si la cantidad que se pretende ingresar es menor o igual que cero, el m(todo no modi+icar el valor de la
variable saldo y devolver +alse# Iuiero que te +i"es de nuevo en c%mo se declara un m(todo* en primer lugar el
modi+icador de acceso 4que puede ser public, protected, private o internal8, despu(s el tipo de dato que
retornar, que podr ser cualquier tipo de dato 4 y en caso de que el m(todo no devuelva ning=n dato, hay que
poner void8, despu(s el nombre del m(todo y, por =ltimo, la lista de argumentos entre par(ntesis# Ba s( que me
estoy repitiendo, pero es que esto es muy importante#
>O
..1.1 /obrecarga de mtodos
:a sobrecarga de m(todos consiste en poner varios m(todos con el mismo nombre en la misma clase, pero
siempre que su lista de argumentos sea distinta# 1"o, repito, siempre que su lista de argumentos sea distinta, es
decir, no puede haber dos m(todos que se llamen igual con la misma lista de argumentos, aunque devuelvan
datos de distinto tipo# 5l compilador sabr&a a cul de todas las sobrecargas nos re+erimos por los argumentos
que se le pasen en la llamada, pero no ser&a capa! de determinar cul de ellas debe e"ecutar si tienen la misma
lista de argumentos# 9or e"emplo, no podr&amos sobrecargar el m(todo $uevoCngreso de este modo*
public int Nuevo/n$reso(double cantidad) )):rror. No se puede sobrecar$ar as*
#...&
A pesar de devolver un valor int en lugar de un bool, su lista de argumentos es id(ntica, por lo que el
compilador avisar&a de un error# ;in embargo, s& podr&amos sobrecargalo de estos modos*
public bool Nuevo/n$reso(sin$le cant)
#...&
public int Nuevo/n$reso(double cantidad, double ar$umentoD)
#...&
public int Nuevo/n$reso(sin$le cantidad, double ar$umentoD)
#...&
Cada sobrecarga tiene marcado en negrilla el elemento que la hace di+erente de las dems# B as& hasta
hartarnos de aadir sobrecargas# Tay un detalle que tambi(n es importante y que no quiero pasar por alto* lo
que di+erencia las listas de argumentos de las di+erentes sobrecargas no es el nombre de las variables, sino el
tipo de cada una de ellas# 9or e"emplo, la siguiente sobrecarga tampoco ser&a vlida*
public bool Nuevo/n$reso(double num) )):rror. No se puede sobrecar$ar as*
#...&
A pesar de que el argumento tiene un nombre distinto 4num en lugar de cantidad8, es del mismo tipo que el
del m(todo del e"emplo, por lo que el compilador tampoco sabr&a cul de las dos sobrecargas e"ecutar#
Bueno, supongo que ahora vendr la pregunta* DCul de todas las sobrecargas vlidas e"ecutar si e+ect=o
la siguiente llamadaE
!is"uentas.Nuevo/n$reso(D((.@=)%
5+ectivamente, aqu& podr&a haber dudas, ya que el n=mero 2@@#J3 puede ser tanto double, como single#
9ara n=meros decimales, el compilador e"ecutar la sobrecarga con el argumento de tipo double# 5n el caso de
n=meros enteros, el compilador e"ecutar la sobrecarga cuyo argumento me"or se adapte con el menor
consumo de recursos 4int, uint, long y unlong, por este orden8# B ahora vendr la otra pregunta* Dy si yo quiero
que, a pesar de todo, se e"ecute la sobrecarga con el argumento de tipo singleE Bien, en ese caso tendr&amos
que aadir un su+i"o al n=mero para indicarle al compilador cul es el tipo de dato que debe aplicar para el
argumento*
!is"uentas.Nuevo/n$reso(D((.@=3)%
:os su+i"os para literales de los distintos tipos de datos num(ricos son los siguientes*
- (ma+5scula o min5scula)9 lon$ ulon$, por este orden%
L (ma+5scula o min5scula)9 int uint, por este orden%
>S
L- -L (independientemente de que est8 en ma+5suculas o min5sculas)9 ulon$%
3 (ma+5scula o min5scula)9 sin$le%
D (ma+5scula o min5scula)9 double%
! (ma+5scula o min5scula)9 decimal%
..1.2 0rgumentos pasados por ,alor y por re&erencia
9uede que necesitemos que los m(todos $uevoCngreso y $uevoMasto devuelvan el saldo nuevo, adems
de true o +alse# D9odemos hacerloE Veamos* siendo estrictos en la respuesta, no se puede, ya que un m(todo
no puede retornar ms de un valor# ;in embargo, s& podemos hacer que un m(todo devuelva datos en uno o
varios de sus argumentos# DC%moE 9ues pasando esos argumentos por re+erencia# 0e e)plicar( me"or* un
m(todo puede aceptar argumentos de dos +ormas distintas 4en C# son tres, aunque dos de ellas tienen mucho
que ver8* argumentos pasados por valor y argumentos pasados por re+erencia#
Cuando un m(todo recibe un argumento por valor, lo que ocurre es que se crea una copia local de la
variable que se ha pasado en una nueva direcci%n de memoria# As&, si el m(todo modi+ica ese valor, la
modi+icaci%n se hace en la nueva direcci%n de memoria, quedando la variable original sin cambio alguno# 9or
e"emplo, si hubi(ramos escrito el m(todo $uevoCngreso de este modo*
public bool Nuevo/n$reso(double cantidad)#
i4 (cantidad V=()
return 4alse%
this.saldo 2= cantidad%
cantidad=this.saldo%
return true%
&
;i el saldo era 1@@, y e+ectuamos la siguiente llamada, Dcul ser&a la salida en la consolaE*
double dinero==A@.>C%
!is"uentas.Nuevo/n$reso(dinero)%
"onsole.,rite(dinero)%
D5res programador de Visual BasicE 9ues te has equivocado# :a salida ser&a 3>J#LO, es decir, la variable
dinero no ha sido modi+icada, ya que se ha pasado al m(todo por valor 4en C#, si no se indica otra cosa, los
argumentos de los m(todos se pasan por valor8# Veamos qu( es lo que ha ocurrido*
>?
:a variable dinero apunta a una determinada !ona de memoria# Al pasarse esta variable por valor, el
compilador hace una copia de este dato en otra !ona de memoria a la que apunta la variable cantidad# As&,
cuando se modi+ica el valor de esta, se modi+ica en esta nueva !ona de memoria, quedando intacta la !ona de
memoria asignada a la variable dinero#
;in embargo, si escribimos el m(todo del siguiente modo para que reciba los valores por re+erencia*
public bool Nuevo/n$reso(re4 double cantidad)#
i4 (cantidad V=()
return 4alse%
this.saldo 2= cantidad%
cantidad=this.saldo%
return true%
&
B modi+icamos tambi(n el c%digo que hac&a la llamada*
double dinero==A@.>C%
!is"uentas.Nuevo/n$reso(re4 dinero)%
"onsole.,rite(dinero)%
:a salida en la consola ser&a >>J#LO# Veamos d%nde est la di+erencia*
N&"ate bien en que, ahora, la variable cantidad apunta a la misma !ona de memoria a la que apunta la
variable dinero# 9or este motivo, cualquier modi+icaci%n que se haga sobre la variable cantidad a+ectar tambi(n
a la variable dinero, ya que dichas modi+icaciones se harn en la !ona de memoria reservada para ambas#
;in embargo, las variables que se pasen a un m(todo usando re+ deben de haber sido iniciali!adas
previamente, es decir, el programa no se habr&a compilado si no se hubiera iniciali!ado la variable dinero# ;i
queremos pasar por re+erencia argumentos cuyo valor inicial no nos interesa deber&amos usar out en lugar de
re+# 9or e"emplo, imagina que queremos devolver en otro argumento el valor del saldo redondeado# D9ara qu(E
$o s(, hombre, s%lo es un e"emplo### Tabr&a que hacerlo as&*
public bool Nuevo/n$reso(re4 double cantidad, out int redondeado)#
redondeado=(int) !ath.6ound(this.saldo)%
i4 (cantidad V=()
return 4alse%
J@
this.saldo 2= cantidad%
cantidad=this.saldo%
redondeado=(int) !ath.6ound(this.saldo)%
return true%
&
B modi+icamos tambi(n el c%digo que hac&a la llamada*
double dinero==A@.>C%
int redoneo%
!is"uentas.Nuevo/n$reso(re4 dinero, out redondeo)%
"onsole.,rite(redondeo)%
Ahora la salida en la consola ser&a 3>L# N&"ate que la variable redondeo no ha sido iniciali!ada antes de
e+ectuar la llamada al m(todo 4no ha recibido ning=n valor8# 9or otro lado, este argumento debe recibir alg=n
valor antes de que el m(todo retorne, por lo que se asigna antes del i+ y luego se asigna otra ve! despu(s de
hacer el ingreso# ;in embargo, la variable dinero s& ha sido iniciali!ada antes de invocar el m(todo, puesto que
el m(todo necesitaba saber cunto hab&a que ingresar, pero no necesita saber nada del valor redondeado, ya
que este se calcular a partir del saldo#
..1.3 Mtodos static
5n e+ecto, por +in vas a saber qu( signi+icaba eso de que el m(todo 0ain ten&a que ser static# Bien, los
m(todos static, son aquellos que se pueden e"ecutar sin necesidad de instanciar la clase donde est escrito# :a
de+inici%n de 'om Archer en el cap&tulo L de su libro [A +ondo C#[ me parece e)celenteK dice as&* [Pn m(todo
esttico es un m(todo que e)iste en una clase como un todo ms que en una instancia espec&+ica de la clase[#
0ucho me"or, DverdadE 9or lo tanto, el hecho de que el m(todo 0ain tenga que ser static no es un capricho, ya
que, de lo contrario, el C:H no ser&a capa! de encontrarlo pues antes de que se e"ecute la aplicaci%n,
l%gicamente, no puede haber instancias de ninguna de las clases que la componen#
5stos m(todos suelen usarse para hacer una serie de operaciones globales que tienen mucho ms que ver
con la clase como tal que con una instancia espec&+ica de la misma* por e"emplo, si tenemos una clase Coche y
queremos listar todas las marcas de coches de que disponemos, lo ms propio es un m(todo static# DIu(
necesidad tenemos de instanciar un ob"eto de esa clase, si solamente queremos ver las marcas disponiblesE
1tro e"emplo podr&a ser un m(todo static en la clase Bol&gra+o que devolviera una cadena con el nombre del
color que le corresponde a un determinado n=mero, ya que no necesitar&a instanciar un ob"eto de la clase para
saber si al n=mero uno le corresponde el color negro, o al J el ro"o, por e"emplo# 9or lo tanto, los m(todos static
no aparecen como miembros de las instancias de una clase, sino como parte integrante de la propia clase#
Vamos a poner un pequeo programa completo que ilustre el uso de los m(todos static*
usin$ G+stem%
namespace 'isa:lectron#
class 'isa:lectron #
public static ushort -imite() #
return =((%
&
)) Aqu* ir*an mEs miembros de la clase
J1
&
class 'isa:lectronApp #
static void !ain() #
"onsole.,rite-ine(O:l l*mite de la 'isa electrn es9 #(&O,
'isa:lectron.-imite())%
strin$ a="onsole.6ead-ine()%
&
&
&
9ara hacer que un m(todo sea static hay que poner esta palabra despu(s del modi+icador de acceso 4si lo
hay8 y antes del tipo de retorno del m(todo# 5ste m(todo devolver&a el l&mite que tienen todas las tar"etas Visa
5lectr%n para e)traer dinero en un s%lo d&a, 4que no s( cul es, pero l&mite tienen8# Ahora presta especial
atenci%n a c%mo se invoca este m(todo dentro del m(todo 0ain 4est en negrilla8# 5n e+ecto, no se ha
instanciado ning=n ob"eto de la clase Visa5lectron, sino que se ha puesto directamente el nombre de la clase#
9or otro lado, soy consciente de que este no es el me"or diseo para esta clase en concreto, pero mi inter(s
principal ahora es que veas muy claro c%mo se de+ine un m(todo static y c%mo se invoca# 0s adelante,
cuando empecemos con la herencia, trataremos la rede+inici%n de m(todos y el polimor+ismo# 9or hoy, creo que
tenemos su+iciente#
9 #6ptima entrega (Constructores( destructores & el recolector de
basura.!
Te de con+esar que esta entrega, por el momento, es la me ms me ha gustado escribir# 5n la parte que
corresponde a la recolecci%n de basura y los destructores me ha sido de gran utilidad el +ormidable art&culo de
,e++rey Hichter titulado* [Marbage Collection* Automatic memory management in #$5' Nrame.orU[ 4o sea,
Hecolecci%n de basura* gesti%n automtica de memoria en #$5' Nrame.orU, [pae que nos entendamos[8#
1bviamente, no te voy a reproducir el art&culo, adems est en ingl(s### ;& he incluido las conclusiones que me
parecen ms "ugosas de las que he podido e)traer de su lectura# $o obstante, si quieres tener una idea clara
4muy clara, dir&a yo8 de c%mo +unciona [por dentro[ el recolector de basura, te recomiendo, c%mo no, que te leas
el original# ;i haces clic a qu& te llevo hasta (l 4te aseguro que no tiene desperdicio8#
1.1 Constructores
5l concepto es muy sencillo de comprender* el constructor de una clase es un m(todo que se encarga de
e"ecutar las primeras acciones de un ob"eto cuando este se crea al instanciar la clase# 5stas acciones pueden
ser, por e"emplo, iniciali!ar variables, abrir archivos, asignar valores por de+ecto a las propiedades### ;obre los
constructores hay un par de reglas que no debes olvidar*
5l constructor ha de llamarse e)actamente igual que la clase#
5l constructor nunca puede retornar un valor#
9or lo tanto, lo primero que se e"ecuta al crear un ob"eto es el constructor de la clase a la que dicho ob"eto
pertenece 4claro, siempre que haya un constructor, pues el compilador no e)ige que e)ista8# Vamos a verlo*
usin$ G+stem%
namespace "onstructores#
J2
class ^b;eto #
public ^b;eto() #
"onsole.,rite-ine(O/nstanciado el ob;etoO)%
&
&
class "onstructoresApp #
static void !ain() #
^b;eto o = ne< ^b;eto()%
strin$ a="onsole.6ead-ine()%
&
&
&
5n este pequeo e"emplo, la clase 1b"eto tiene un constructor# 9resta especial atenci%n a que se llama
e)actamente igual que la clase 41b"eto8 y se declara igual que un m(todo, con la salvedad de que no se pone
ning=n tipo de retorno puesto que, como he dicho antes, un constructor no puede retornar ning=n dato# Al
e"ecutar este programa, la salida en la consola ser&a esta*
/nstanciado el ob;eto
;eguramente te habrs dado cuenta de lo que ha ocurrido* en e+ecto, en el m(todo 0ain no hemos dicho
que escriba nada en la consola# ;in embargo, al instanciar el ob"eto se ha e"ecutado el constructor, y ha sido
este el que ha escrito esa l&nea en la consola#
Cgual que los m(todos, los constructores tambi(n se pueden sobrecargar# :as normas para hacerlo son las
mismas* la lista de argumentos ha de ser distinta en cada una de las sobrecargas# ;e suele hacer cuando se
quiere dar la posibilidad de instanciar ob"etos de +ormas di+erentes# 9ara que lo veas, vamos a sobrecargar el
constructor de la clase 1b"eto# Ahora, el c%digo de esta clase es el siguiente*
class ^b;eto #
public ^b;eto() #
"onsole.,rite-ine(O/nstanciado el ob;eto sin datosO)%
&
public ^b;eto(strin$ !ensa;e) #
"onsole.,rite-ine(!ensa;e)%
&
public ^b;eto(int dato?, int datoD)#
"onsole.,rite-ine(O-os datos pasados al constructor son9 #(& + #?&O, dato?, datoD)%
&
&
Ahora podr&amos instanciar ob"etos de esta clase de tres +ormas distintas* una sin pasarle datosK otra
pasndole una cadena y otra pasndole dos datos de tipo int# N&"ate en este +ragmento de c%digo*
J3
^b;eto o? = ne< ^b;eto()%
^b;eto oD = ne< ^b;eto(OPasando una cadenaO)%
^b;eto o= = ne< ^b;eto(=A, @C)%
:a salida en la consola ser&a esta*
/nstanciado el ob;eto
Pasando una cadena
-os datos pasados al constructor son9 =A + @C
9or otro lado, tenemos los constructores estticos 4static8# :a misi%n de estos constructores es iniciali!ar los
valores de los campos static o bien hacer otras tareas que sean necesarias para el +uncionamiento de la clase
en el momento en que se haga el primer uso de ella, ya sea para instanciar un ob"eto, para e"ecutar un m(todo
static o para ver el valor de un campo static# Ba s( que a=n no hemos visto estos =ltimos 4los campos8, pero
como los vamos a tratar en la pr%)ima entrega creo que podemos seguir adelante con la e)plicaci%n 4no es +cil
establecer un orden l%gico para un curso de C#, porque todo est pro+undamente relacionado8# :os
constructores static, como te dec&a, no se pueden e"ecutar ms de una ve! durante la e"ecuci%n de un
programa, y adems la e"ecuci%n del mismo no puede ser e)pl&cita, pues lo har el compilador la primera ve!
que detecte que se va a usar la clase# Vamos a poner un e"emplo claro y evidente de constructor static*
supongamos que la clase ;ystem#Console tiene uno 4digo supongamos porque no he encontrado nada que me
lo con+irme, pero viendo c%mo +unciona, dedu!co que su comportamiento se debe a esto8* :a primera ve! que el
C:H detecta que se va a utili!ar esta clase o alguno de sus miembros se e"ecuta su constructor static, y lo que
hace este constructor es iniciali!ar las secuencias de lectura y escritura en la ventana de 21; 4o sea, en la
consola8, para que los miembros de esta clase puedan hacer uso de ella# 5videntemente, este constructor no se
e"ecutar ms durante la vida le programa, porque de lo contrario se iniciali!ar&an varias secuencias de escritura
y lectura, lo cual ser&a contraproducente# Vamos a poner un e"emplo que te lo acabe de aclarar*
usin$ G+stem%
namespace "onstructoresGtatic #
class !ensa;e #
public static strin$ KeXto%
static !ensa;e() #
KeXto=ONola, cmo andamosO%
&
&
class "onstructoresGtaticApp #
static void !ain() #
"onsole.,rite-ine(!ensa;e.KeXto)%
strin$ a="onsole.6ead-ine()%
&
&
&
5n este e"emplo tenemos una clase 0ensa"e con dos miembros* un campo static de tipo string llamado 'e)to
que a=n no est iniciali!ado 4digamos que es como una variable a la que se puede acceder sin necesidad de
J>
instanciar la clase8 y un constructor static para esta clase# 5n el m(todo 0ain no instanciamos ning=n ob"eto de
esta clase, sino que simplemente escribimos en la consola el valor del campo 'e)to 4que es static, y recuerda
que a=n no ha sido iniciali!ado8# Como hay un constructor static en la clase, el compilador e"ecutar primero
este constructor, asignando el valor adecuado al campo 'e)to# 9or este motivo, la salida en la consola es la
siguiente*
Nola, cmo andamos
Cuando hablemos de la herencia os mostrar( c%mo se comportan los constructores, tanto de la clase base
como de sus clases derivadas# 2e momento podemos dar por terminadas las e)plicaciones, aunque a partir de
ahora procurar( poner constructores por todas partes, para que no se te olviden#
1.2 #l recolector de basura y los destructores
B aqu& va a empe!ar la pol(mica entre los programadores de C--* los aut(nticos gur=s de este lengua"e
de+endern que quieren seguir gestionando la memoria a su anto"o, y los que no est(n tan avan!ados dirn que
C# les acaba de quitar un gran peso de encima#
5n e+ecto, tal y como os imaginis, C# 4ms propiamente, el c%digo gestionado8 incluye un recolector de
basura 4MC, Marbage Collector8, es decir, que ya no es necesario liberar la memoria dinmica cuando no se
necesita ms una re+erencia a un ob"eto# 5l MC +unciona de un modo pere!oso, esto es, no libera las
re+erencias en el momento en el que se de"an de utili!ar, sino que lo hace siempre que se de uno de estos tres
casos*
Cuando no hay espacio su+iciente en el mont%n para meter un ob"eto que se pretende instanciar#
Cuando detecta que la aplicaci%n va a +inali!ar su e"ecuci%n#
Cuando se invoca el m(todo Collect de la clase ;ystem#MC#
;&, s&, ya s( que los que est(is empe!ando a programar ahora no os hab(is enterado de nada# Veamos,
hasta ahora sab(is que cuando se instancia un ob"eto se reserva un bloque de memoria en el mont%n y se
devuelve una re+erencia 4o puntero8 al comien!o del mismo# 9ues bien, cuando este ob"eto de"a de ser utili!ado
4por e"emplo, estableci(ndolo a null8 lo que se hace es destruir la re+erencia al mismo, pero el ob"eto permanece
en el espacio de memoria que estaba ocupando, y ese espacio de memoria no se puede volver a utili!ar hasta
que no sea liberado# 5n C-- era tarea del programador liberar estos espacios de memoria, y para ello se
utili!aban los destructores# ;in embargo, en C# esto es tarea del MC#
$o obstante, el hecho de que tengamos un MC no quiere decir que los destructores de"en de e)istir# Bueno,
en realidad s& ba"o el punto de vista semntico, aunque no en cuanto al punto de vista sintctico 4si ests
empe!ando y no entiendes muy bien esto no te preocupes demasiado, porque va especialmente dirigido a los
programadores de C-- para que no caigan en el error de pensar que los destructores de C# y los de C-- son la
misma cosa, aunque s& te recomiendo que lo leas para que, al menos, te vayas haciendo una idea8# Ba me
imagino que alguno se estar haciendo un l&o importante con esto* Dc%mo es que hay sinta)is para el
destructor, pero desaparece su semnticaE 9i(nsalo un poco* semnticamente un destructor implica la
liberaci%n de la memoria, pero ocurre que en el c%digo gestionado esto es tarea del MC y, por lo tanto, la
semntica del destructor es necesariamente incompatible con el MC# ;in embargo, s& podemos incluir un
m(todo que se ocupe de reali!ar las otras tareas de +inali!aci%n, como eliminar archivos temporales que
estuviera utili!ando el ob"eto, por poner un e"emplo# 9ues bien, lo que ocurre realmente cuando escribimos un
destructor es que el compilador sobreescribe la +unci%n virtual Ninali!e de la clase ;ystem#1b"ect, colocando en
ella el c%digo que hemos incluido en el destructor, y lo que invoca realmente el MC cuando va a liberar la
memoria de un ob"eto es este m(todo Ninali!e, aunque la sinta)is del destructor se mantiene simplemente para
evitar ms complicaciones en el aprendi!a"e de C# a los programadores de C--# $o olvides esto* los
destructores de C# 4me"or dicho, en el c%digo gestionado8 son, en realidad, +inali!adores# ;in embargo yo voy a
seguir llamndolos destructores para no liarte cuando leas otros libros, puesto que la mayor&a de los autores
utili!an esta terminolog&a# 5ntonces, Dquiere esto decir que podemos elegir entre sobreescribir el m(todo
JJ
Ninali!e de la clase ;ystem#1b"ect y crear un destructorE 9ues no# ;i intentas sobreescribir dicho m(todo en C#
el compilador te indicar que debes escribir un destructor#
:os destructores de C# no pueden ser invocados e)pl&citamente como si +ueran un m(todo ms# 'ampoco
sirve la trampa de invocar el m(todo Ninali!e, ya que su modi+icador de acceso es protected# DB desde las
clases derivadasE 'ampoco, pues el compilador no lo permite, ya que esta llamada se hace impl&citamente en el
destructor# Ba hablaremos de esto cuando lleguemos a la herencia, no nos precipitemos### 9or lo tanto, los
destructores sern invocados por el MC cuando este haga la recolecci%n de basura# Como consecuencia, los
destructores no admiten modi+icadores de acceso ni argumentos 4claro, tampoco pueden ser sobrecargados8#
;e han de nombrar igual que la clase, precedidos por el signo 7# :o veremos me"or con un e"emplo*
class ^b;eto #
U^b;eto() #
"onsole.,rite-ine(O^b;eto liberadoO)%
&
&
Como dec&a, el destructor se llama igual que la clase precedido con el signo 7 4A:' presionado ms las
teclas 1 2 L sucesivamente8# Veamos c%mo se comporta esto con un programa completo*
usin$ G+stem%
namespace Destructores #
class ^b;eto #
U^b;eto() #
"onsole.,rite-ine(O6e4erencia liberadaO)%
&
&
class DestructoresApp #
static void !ain() #
^b;eto o=ne< ^b;eto()%
&
&
&
5n este caso, para probar este programa, lo vamos a e"ecutar sin depuraci%n, o sea, Ctrl-NJ 4de lo contrario
no nos dar&a tiempo a ver el resultado8# 5l programa no hace casi nada* sencillamente instancia la clase 1b"eto
y +inali!a inmediatamente# Al terminar, es cuando el MC entra en acci%n y e"ecuta el destructor de la clase
1b"eto, por lo que la salida en la consola ser&a la siguiente*
6e4erencia liberada
DB por qu( no hemos e"ecutado el m(todo Head:ine dentro de 0ain, como hemos hecho siempreE ;ab&a
que me preguntar&as eso# Vamos a volver a poner el m(todo 0ain con esa l&nea que me dices y luego os lo
comento*
static void !ain() #
JL
^b;eto o=ne< ^b;eto()%
strin$ a="onsole.6ead-ine()%
&
Bien, tenemos dos motivos por los que no lo hemos hecho* el primero es que no servir&a de nada, puesto
que el destructor no se e"ecutar hasta que el MC haga la recolecci%n de basura, y esta no se har hasta que
+inalice la aplicaci%n, y la aplicaci%n +inali!a despu(s de haber e"ecutado todo el c%digo del m(todo 0ain# 5l
segundo motivo es que esto provocar&a un error# DDDCf01EEE F;i est bien escritoG 5n e+ecto, pero se
produce el siguiente error* [$o se puede tener acceso a una secuencia cerrada[# 5l porqu( se produce tiene
una e)plicaci%n bastante sencilla* :a primera ve! que se usa la clase Console se iniciali!an las secuencias de
lectura y escritura en la consola 4seguramente en un constructor static8, y estas secuencias se cierran "usto
antes de +inali!ar la aplicaci%n# 5n el primer e"emplo +uncionar&a correctamente, puesto que esta secuencia se
inicia "ustamente en el destructor, ya que antes de este no hay ninguna llamada a la clase Console# ;in
embargo en el segundo se produce un error, porque las secuencias se inician dentro del m(todo 0ain 4al
e"ecutar Console#Head:ine8, y se cierran cuando va a +inali!ar el programa# 5l problema viene aqu&* los hilos de
e"ecuci%n del MC son de ba"a prioridad, de modo que, para cuando el MC quiere e"ecutar el destructor, las
secuencias de escritura y lectura de la consola ya han sido cerradas, y como los constructores static no se
pueden e"ecutar ms de una ve!, la clase Console no puede abrirlas por segunda ve!#
;igamos con el e"emplo que +uncionaba correctamente# 5n e+ecto, puede parecer que el MC ha sido
sumamente rpido, pues ha liberado el ob"eto en el momento en el que este ya no era necesario# ;in embargo,
veamos el siguiente e"emplo*
namespace Destructores #
class ^b;eto #
U^b;eto() #
"onsole.,rite-ine(O6e4erencia liberadaO)%
&
&
class DestructoresApp #
static void !ain() #
^b;eto o=ne< ^b;eto()%
"onsole.,rite-ine(O:l ob;eto acaba de ser instanciado. Pulsa /NK6^O)%
strin$ a = "onsole.6ead-ine()%
o=null%
"onsole.,rite-ine(O-a re4erencia acaba de ser destruida. Pulsa /NK6^O)%
a = "onsole.6ead-ine()%
G"."ollect()%
"onsole.,rite-ine(OGe acaba de e;ecutar G"."ollect(). Pula /NK6^O)%
a = "onsole.6ead-ine()%
&
&
JO
&
:a salida en la consola ser&a esta*
:l ob;eto acaba de ser instanciado. Pulsa /NK6^
-a re4erencia acaba de ser destruida. Pulsa /NK6^

Ge acaba de e;ecutar G"."ollect(). Pulsa /NK6^
6e4erencia liberada
N&"ate bien en que el destructor de la clase 1b"eto no se ha e"ecutado cuando se destruy% la re+erencia
4oRnull8, sino cuando se ha +or!ado la recolecci%n con MC#Collect48#
;( que algunos programadores de C-- estarn pensando que, al +in y al cabo, establecer la re+erencia a null
y e"ecutar despu(s MC#Collect48 viene a ser lo mismo que el delete de C--# Aunque puede parecer que esto es
correcto a la vista del e"emplo anterior te puedo asegurar que no es as&# Vamos con este otro e"emplo*
usin$ G+stem%
namespace DestructoresD #
class ^b;eto #
public int dato%
public ^b;eto(int valor) #
this.dato=valor%
"onsole.,rite-ine(O"onstruido ^b;eto con el valor #(&O, valor)%
&
U^b;eto() #
"onsole.,rite-ine(ODestructor de ^b;eto con el valor #(&O, this.dato)%
&
&
class DestructoresDApp #
static void !ain() #
^b;eto a=ne< ^b;eto(@)%
^b;eto b=a%
strin$ c%
"onsole.,rite-ine(O'alor de a.dato9 #(&O, a.dato)%
"onsole.,rite-ine(O'alor de b.dato9 #(&O, b.dato)%
"onsole.,rite-ine(OPulsa /NK6^ para e;ecutar a.dato22O)%
c="onsole.6ead-ine()%
a.dato22%
JS
"onsole.,rite-ine(O:;ecutado a.dato22O)%
"onsole.,rite-ine(O'alor de a.dato9 #(&O, a.dato)%
"onsole.,rite-ine(O'alor de b.dato9 #(&O, b.dato)%
"onsole.,rite-ine(OPulsa /NK6^ para e;ecutar a=null% G"."ollect()O)%
c="onsole.6ead-ine()%
a=null%
G"."ollect()%
"onsole.,rite-ine(Oa=null% G"."ollect() han sido e;ecutadosO)%
"onsole.,rite-ine(OPulsa /NK6^ para e;ecutar b=null% G"."ollect()O)%
c="onsole.6ead-ine()%
b=null%
G"."ollect()%
"onsole.,rite-ine(Ob=null% G"."ollect() han sido e;ecutadosO)%
c="onsole.6ead-ine()%
&
&
&
Veamos ahora c%mo ser&a en C-- no gestionado usando delete y luego comparamos las salidas de ambos
programas*
)) ___AK:N"/`NSSS :ste cdi$o estE escrito en "22
Finclude Viostream.hJ
class ^b;eto #
public9
^b;eto(int valor) #
dato=valor%
cout VV O"onstruido ^b;eto con el valor O
VV (O[dO, valor) VV OanO%
&
U^b;eto() #
cout VV ODestructor de ^b;eto con el valor O
VV (O[dO, this7Jdato) VV OanO%
&
int dato%
&%
void main() #
J?
^b;etoH a=ne< ^b;eto(@)%
^b;etoH b = a%
char c%
cout VV O'alor de a7Jdato9 O VV (O[dO, a7Jdato) VV OanO%
cout VV O'alor de b7Jdato9 O VV (O[dO, b7Jdato) VV OanO%
cout VV OPulsa /NK6^ para e;ecutar a7Jdato22anO%
cin.$et(c)%
a7Jdato22%
cout VV O:;ecutado a7Jdato22anO%
cout VV O'alor de a7Jdato9 O VV (O[dO, a7Jdato) VV OanO%
cout VV O'alor de b7Jdato9 O VV (O[dO, b7Jdato) VV OanO%
cout VV OPulsa /NK6^ para e;ecutar delete aanO%
cin.$et(c)%
delete a%
cout VV Odelete a ha sido e;ecutadoanO%
cout VV OPulsa /NK6^ para e;ecutar delete b (esto provocarE un error)anO%
cin.$et(c)%
delete b%
&
5stas son las salidas de ambos programas*#
;A:C2A 25: 9H1MHA0A 5$ C# ;A:C2A 25: 9H1MHA0A 5$ C--
Construido 1b"eto con el valor J
Valor de a#dato* J
Valor de b#dato* J
9ulsa C$'H1 para e"ecutar a#dato--

5"ecutado a#dato--
Valor de a#dato* L
Valor de b#dato* L
9ulsa C$'H1 para e"ecutar aRnullKMC#Collect

aRnullK MC#Collect48 han sido e"ecutados
9ulsa C$'H1 para e"ecutar bRnullKMC#Collect

Construido 1b"eto con el valor J
Valor de aVZdato* J
Valor de bVZdato* J
9ulsa C$'H1 para e"ecutar aVZdato--

5"ecutado aVZdato--
Valor de aVZdato* L
Valor de bVZdato* L
9ulsa C$'H1 para e"ecutar delete a

2estructor de 1b"eto con el valor L
delete a ha sido e"ecutado
9ulsa C$'H1 para e"ecutar delete b 4e###
L@
bRnullK MC#Collect48 han sido e"ecutados
2estructor de 1b"eto con el valor L

AIPg ;5 9H12PC5 P$ 5HH1H
9resta atenci%n a que en estos programas tenemos una doble re+erencia hacia el mismo ob"eto, es decir,
tanto [a[ como [b[ apuntan a la misma !ona de memoria# ;abemos esto porque el constructor se ha e"ecutado
=nicamente una ve! cuando se hi!o [aRne. 1b"eto4J8[, pero cuando se asign% [bRa[ lo que hicimos +ue crear la
doble re+erencia# :a parte en la que se incrementa el campo [dato[ es para demostrar que dicha alteraci%n
a+ecta a ambas re+erencias# :as di+erencias vienen a partir de aqu&* Cuando se e"ecuta aRnullK MC#Collect48 en
C# se ha destruido la re+erencia de [a[, pero no se ha e"ecutado el destructor porque a=n hay una re+erencia
vlida hacia el ob"eto* la re+erencia de [b[# 2espu(s, cuando se destruye la re+erencia de [b[ y se vuelve a
e"ecutar MC#Collect48 observamos que s& se e"ecuta el destructor, ya que el MC no ha encontrado ninguna
re+erencia vlida y puede liberar el ob"eto# ;in embargo, en el programa escrito en C-- ha ocurrido algo muy
distinto* el destructor se ha e"ecutado en el momento de hacer el [delete a[, ya que delete libera la memoria en
la que se alo"aba el ob"eto independientemente de las re+erencias que haya hacia (l# 9or este motivo se
produce un error cuando se intenta e"ecutar [delete b[, puesto que el ob"eto +ue liberado con anterioridad#
9or otro lado, el MC garanti!a que se e"ecutar el destructor de todos los ob"etos alo"ados en el mont%n
4recuerda, tipos re+erencia8 cuando no haya re+erencias hacia ellos, aunque esta +inali!aci%n de ob"etos no sea
determinista, es decir, no libera la memoria en el instante en que de"a de ser utili!ada# 9or contra, en C-- se
puede programar una +inali!aci%n determinista, pero esta tarea es sumamente comple"a en la mayor&a de las
ocasiones y, adems, suele ser una importante +uente de errores y un gran obstculo para un adecuado
mantenimiento del c%digo# Veamos un e"emplo, muy simple, eso s&, de esto =ltimo# Psaremos la misma clase
1b"eto que en el e"emplo anterior, pero este m(todo 0ain*
static void !ain() #
^b;eto a%
strin$ c%
"onsole.,rite-ine(OPulsa /NK6^ para instanciar el primer ob;etoO)%
c="onsole.6ead-ine()%
a=ne< ^b;eto(?)%
"onsole.,rite-ine(OPulsa /NK6^ para instanciar el se$undo ob;etoO)%
c="onsole.6ead-ine()%
a=ne< ^b;eto(D)%
"onsole.,rite-ine(OPulsa /NK6^ para instanciar el tercer ob;etoO)%
c="onsole.6ead-ine()%
a=ne< ^b;eto(=)%
"onsole.,rite-ine(OPulsa /NK6^ para e;ecutar a=nullO)%
c="onsole.6ead-ine()%
a=null%
"onsole.,rite-ine(OPulsa /NK6^ para e;ecutar "G."ollect()O)%
c="onsole.6ead-ine()%
G"."ollect()%
c="onsole.6ead-ine()%
L1
&
5sta ser&a la +unci%n main en C-- no gestionado*
)) ___AK:N"/`NSSS :ste cdi$o estE escrito en "22
void main() #
^b;etoH a%
char c%
cout VV OPulsa /NK6^ para construir el primer ob;etoanO%
cin.$et(c)%
a=ne< ^b;eto(?)%
cout VV OPulsa /NK6^ para construir el se$undo ob;etoanO%
cin.$et(c)%
a=ne< ^b;eto(D)%
cout VV OPulsa /NK6^ para construir el tercer ob;etoanO%
cin.$et(c)%
a=ne< ^b;eto(=)%
cout VV OPulsa /NK6^ para e;ecutar delete aanO%
cin.$et(c)%
delete a%
&
B estos son los resultados de ambos programas*
;A:C2A 25: 9H1MHA0A 5$ C# ;A:C2A 25: 9H1MHA0A 5$ C--
9ulsa C$'H1 para construir el primer ob"eto

Construido 1b"eto con el valor 1
9ulsa C$'H1 para construir el segundo ob"eto

Construido 1b"eto con el valor 2
9ulsa C$'H1 para construir el tercer ob"eto

Construido 1b"eto con el valor 3
9ulsa C$'H1 para e"ecutar aRnull

9ulsa C$'H1 para e"ecutar MC#Collect48

9ulsa C$'H1 para construir el primer ob"eto

Construido 1b"eto con el valor 1
9ulsa C$'H1 para construir el segundo ob"eto

Construido 1b"eto con el valor 2
9ulsa C$'H1 para construir el tercer ob"eto

Construido 1b"eto con el valor 3
9ulsa C$'H1 para e"ecutar delete a

2estructor de 1b"eto con el valor 3
L2
2estructor de 1b"eto con el valor 3
2estructor de 1b"eto con el valor 1
2estructor de 1b"eto con el valor 2
5n estos dos programas estamos creando nuevos ob"etos constantemente con la misma variable# Al hacerlo
se destruye la re+erencia anterior para crear la nueva# 9uedes ver que, tanto en C# como en C--, no se ha
e"ecutado ning=n destructor cuando se destru&a una re+erencia antigua para crear la nueva# Cuando en el
programa escrito en C# hemos destruido la =ltima re+erencia 4aRnull8 y e"ecutado MC#Collect48 se han e"ecutado
los destructores de los tres ob"etos que hab&amos creado, es decir, el CM ha liberado los espacios de memoria
que estaban ocupando# ;in embargo, al e"ecutar [delete a[ en el programa escrito en C--, solamente se ha
liberado el =ltimo ob"eto# DIu( ha sido de los otrosE $ada, y nunca me"or dicho# $o se ha hecho nada con ellos
y, por lo tanto, siguen ocupando la memoria que ten&an asignada y esta memoria no se puede volver a asignar
hasta que no se libere# Como consecuencia, los destructores no se han e"ecutado y, por consiguiente, los otros
recursos que pudieran estar utili!ando siguen en uso 4archivos temporales, bases de datos, cone)iones de
red###8# DB cundo se liberanE 9ues, por e"emplo, cuando apaguemos el ordenador 4claro8# 9ara que esto no
hubiera sucedido 4en el programa de C--, se entiende8 habr&a que haber liberado cada una de las instancias de
la clase 1b"eto antes crear otra, es decir, habr&a que haber e"ecutado [delete a[ antes de crear la segunda y la
tercera re+erencia# 5n este e"emplo la soluci%n era, como has visto, bastante +cil, pero ahora quiero que te
imagines esto en un conte)to algo ms comple"o 4y real8, con re+erencias circulares 4es decir, uno ob"eto apunta
a otro y este, a su ve!, al primero8, re+erencias compartidas por m=ltiples procesos y cosas as&# :a cosa se
puede complicar much&simo#
A pesar de todo, el hecho de que el MC no o+re!ca una +inali!aci%n determinista tambi(n podr&a ser un
contratiempo 4de hecho lo ser&a en muchos casos8* Dqu( ocurre si, por e"emplo, un ob"eto abre una base de
datos en modo e)clusivoE 5+ectivamente, necesitar&amos que la base de datos +uera cerrada lo antes posible
para que otros procesos pudieran acceder a ella, independientemente de si el ob"eto se libera o no de la
memoria# DC%mo lo hacemosE 9ues bien, para estos casos 0icroso+t recomienda escribir un m(todo llamado
Close y6o 2ispose en la clase para liberar estos recursos adems de hacerlo en el destructor, e incluir en la
documentaci%n de la misma un aviso para que se invoque este m(todo cuando el ob"eto no se vaya a usar ms#
5n general, se recomienda escribir un m(todo 2ispose si el ob"eto necesitara +inali!aci%n determinista y no se
pudiera volver a utili!ar hasta una nueva instanciaci%n, y6o un m(todo Close si tambi(n necesitara +inali!aci%n
determinista pero pudiera ser reutili!ado de nuevo sin volverlo a instanciar 4usando por e"emplo un m(todo
1pen8# 1"o, ninguno de los m(todos Close o 2ispose sustituyen al destructor, sino que se escriben simplemente
para liberar otros recursos antes 4o mucho antes8 de que el ob"eto sea liberado de la memoria# 9ero, si
escribimos un m(todo Close o 2ispose para que se haga la +inali!aci%n, Dpara qu( necesitamos el destructorE
9ues lo necesitamos por si el programador que est usando nuestra clase olvida invocar el m(todo Close o el
m(todo 2ispose# As& nos aseguramos de que, tarde o temprano, los recursos que utili!aba el ob"eto se
liberarn# Ahora bien, hay que tener cuidado con esto* si la aplicaci%n cliente ha invocado el m(todo Close o el
2ispose debemos evitar que se e"ecute el destructor, pues ya no hace +alta# 9ara esto tenemos el m(todo
;upressNinali!e de la clase ;ystem#MC# Veamos un e"emplo de esto*
usin$ G+stem%
namespace !etodos"losebDispose #
class 3inalizarDeterminista #
public void Dispose() #
"onsole.,rite-ine(O-iberando recursosO)%
)) Aqu* ir*a el cdi$o para liberar los recursos
G".Guppress3inalize(this)%
L3
&
U3inalizarDeterminista() #
this.Dispose()%
&
&
class !etodos"losebDisposeApp #
static void !ain() #
strin$ c%
3inalizarDeterminista a=ne< 3inalizarDeterminista()%
"onsole.,rite-ine(OPulsa /NK6^ para e;ecutar a.Dispose()O)%
c="onsole.6ead-ine()%
a.Dispose()%
"onsole.,rite-ine(OPulsa /NK6^ para e;ecutar a=null% G"."ollect()O)%
c="onsole.6ead-ine()%
a=null%
G"."ollect()%
"onsole.,rite-ine(O:;ecutado a=null% G"."ollect()O)%
"onsole.,rite-ine(OPulsa /NK6^ para volver a instanciar aO)%
c="onsole.6ead-ine()%
a=ne< 3inalizarDeterminista()%
"onsole.,rite-ine(OPulsa /NK6^ para e;ecutar a=null% G"."ollect()O)%
c="onsole.6ead-ine()%
a=null%
G"."ollect()%
c="onsole.6ead-ine()%
&
&
&
5+ectivamente, en este e"emplo hemos escrito el c%digo de +inali!aci%n de la clase dentro del m(todo
2ispose, y en el destructor simplemente hemos puesto una llamada a este m(todo# N&"ate en que, al +inal del
m(todo 2ispose, hemos invocado el m(todo ;upressNinali!e de la clase MC para que el recolector de basura
no e"ecute el destructor, ya que se ha e"ecutado el m(todo 2ispose# 5n caso de que el cliente no e"ecutara este
m(todo, el MC e"ecutar&a el destructor al hacer la recolecci%n, con lo cual nos aseguramos de que todos los
recursos quedarn libres independientemente de si el programador que usa nuestra clase olvid% o no hacerlo
invocando 2ispose#
FP++++G DBa hemos terminado con estoE 9ues s&###, hemos terminado### de empe!ar# Como os he dicho, #$5'
Nrame.orU o+rece la clase ;ystem#MC para proporcionarnos un cierto control sobre el recolector de basura#
L>
Como son varios los m(todos de esta clase y considero que este tema es muy interesante, me e)tender( un
poquito ms, si no os importa#
Pn +en%meno curioso 4y a la ve! peligroso8 que sucede con la recolecci%n de basura es la resurrecci%n de
ob"etos# ;&, s&, he dicho resurrecci%n# $o es que tenga mucha utilidad, pero quiero contaros qu( es, pues puede
que os libre de alg=n que otro quebradero de cabe!a en el +uturo# ;ucede cuando un ob"eto que va a ser
eliminado vuelve a crear una re+erencia a s& mismo durante la e"ecuci%n de su destructor# Veamos un e"emplo*
usin$ G+stem%
namespace 6esurreccin #
class ^b;eto #
public int dato%
public ^b;eto(int valor) #
this.dato=valor%
"onsole.,rite-ine(O"onstruido ^b;eto con el valor #(&O,
valor)%
&
U^b;eto() #
"onsole.,rite-ine(ODestructor de ^b;eto con el valor #(&O,
this.dato)%
6esurreccionApp.resucitado=this%
&
&
class 6esurreccionApp #
static public ^b;eto resucitado%
static void !ain() #
strin$ c%
"onsole.,rite-ine(OPulsa /NK6^ para crear el ob;etoO)%
c="onsole.6ead-ine()%
resucitado=ne< ^b;eto(?)%
"onsole.,rite-ine(O'alor de resucitado.dato9 #(&O, resucitado.dato)%
"onsole.,rite-ine(OPulsa /NK6^ para e;ecutar resucitado=null% G"."ollect()O)%
c="onsole.6ead-ine()%
resucitado=null%
G"."ollect()%
G".,ait3orPendin$3inalizers()%
"onsole.,rite-ine(O'alor de resucitado.dato9 #(&O, resucitado.dato)%
"onsole.,rite-ine(OPulsa /NK6^ para e;ecutar resucitado=null% G"."ollect()O)%
LJ
c="onsole.6ead-ine()%
resucitado=null%
G"."ollect()%
"onsole.,rite-ine(O:;ecutado resucitado=null% G"."ollect()O)%
c="onsole.6ead-ine()%
&
&
&
Ahora vamos a ver la sorprendente salida en la consola y despu(s la e)aminamos*
Pulsa /NK6^ para crear el ob;eto

"onstruido ^b;eto con el valor ?
'alor de resucitado.dato9 ?
Pulsa /NK6^ para e;ecutar resucitado=null% G"."ollect()

Destructor de ^b;eto con el valor ?
'alor de resucitado.dato9 ?
Pulsa /NK6^ para e;ecutar resucitado=null% G"."ollect()

:;ecutado resucitado=null% G"."ollect()
Vamos poco a poco, que si no podemos perdernos con bastante +acilidad# Al principio, todo bien, como se
esperaba* al instanciar el ob"eto se e"ecuta el constructor del mismo# Ahora es cuando viene lo bueno* se anula
la re+erencia y, por lo tanto, el MC determina que puede liberarlo y e"ecuta su destructor# ;in embargo, cuando
volvemos a escribir el valor del campo [dato[ Feste vuelve a aparecerG 5n e+ecto, el MC no lo liber% a pesar de
haber e"ecutado su destructor, y lo ms curioso es que el motivo por el que no lo ha liberado no es que se haya
creado una nueva re+erencia al ob"eto en el destructor, sino otro que e)plicaremos despu(s# 9ero ah& no queda
la cosa* cuando destruimos la re+erencia y +or!amos la recolecci%n por segunda ve! el destructor no se ha
e"ecutado# B las dudas se acrecentan, claro* Dse ha liberado o no se ha liberadoE y, si se ha liberado, Dpor qu(
no se ha e"ecutado el destructorE 9ues bien, s& se ha liberado, pero no se ha e"ecutado el destructor# 5n
resumen* lo que ha ocurrido es que la primera ve! que destruimos la re+erencia y e"ecutamos MC#Collect se
e"ecut% el destructor pero no se liber%, y la segunda ve! se liber% pero no se e"ecut% el destructor# :a
e)plicaci%n de todo este embrollo es la siguiente* Cuando se instancia un ob"eto, el MC comprueba si este tiene
un destructor# 5n caso a+irmativo, guarda un puntero hacia el ob"eto en una lista de +inali!adores# Al e"ecutar la
recolecci%n, el MC determina qu( ob"etos se pueden liberar, y posteriormente comprueba en la lista de
+inali!adores cules de ellos ten&an destructor# ;i hay alguno que lo tiene, el puntero se elimina de esta lista y se
pasa a una segunda lista, en la que se colocan, por lo tanto, los destructores que se deben invocar# 5l MC, por
=ltimo, libera todos los ob"etos a los que el programa ya no hace re+erencia e)cepto aquellos que estn en esta
segunda lista, ya que si lo hiciera no se podr&an invocar los destructores, y aqu& acaba la recolecci%n# Como
consecuencia, un ob"eto que tiene destructor no se libera en la primera recolecci%n en la que se detecte que ya
no hay re+erencias hacia (l, sino en la siguiente, y este es el motivo por el que, en nuestro e"emplo, el ob"eto no
se liber% en la primera recolecci%n# 'ras esto, un nuevo hilo de ba"a prioridad del MC se ocupa de invocar los
destructores de los ob"etos que estn en esta segunda lista, y elimina los punteros de ella seg=n lo va haciendo#
LL
Claro, la siguiente ve! que hemos anulado la re+erencia y +or!ado la recolecci%n en nuestro e"emplo, el MC
determin% que dicho ob"eto se pod&a liberar y lo liber%, pero no e"ecut% su destructor porque la direcci%n del
ob"eto ya no estaba ni el la lista de +inali!adores ni en la segunda lista# DB si, a pesar de todo, quer&amos que se
volviera a e"ecutar el destructor, no pod&amos hacerloE Bien, para eso tenemos el m(todo
HeHegisterNorNinali!e de la clase MC, que lo que hace es volver a colocar un puntero al ob"eto en la lista de
+inali!adores#
Como te dec&a, son pocas las utilidades que se le pueden encontrar a la resurrecci%n# 2e hecho, yo no he
encontrado ninguna 4desde aqu& os invito a que me mand(is un 5Vmail si a vosotros se os ocurre algo8# 9or este
motivo la he cali+icado de [+en%meno curioso y peligroso[ en lugar de [potente caracter&stica[, pues creo que es
ms un [e+ecto colateral[ del propio +uncionamiento del MC que algo diseado as& a prop%sito# DIue por qu(
digo que es peligrosoE 9orque, dependiendo de c%mo hayamos diseado la clase, el e+ecto puede ser de lo
ms inesperado# Cmagina, por e"emplo, un ob"eto 4llam(mosle 9adre8 de una clase que, a su ve!, crea sus
propios ob"etos de otras clases 4llam(mosles Ti"os8# Al hacer la recolecci%n, el MC determina que el ob"eto
9adre se puede liberar, y con (l todos aquellos a los que este hace re+erencia, es decir, los Ti"os# Como
consecuencia, puede que el MC libere varios de estos Ti"os re+erenciados en el ob"eto 9adre# ;in embargo, si
hemos resucitado al 9adre se puede armar un buen l&o 4de hecho se armar seguro8 cuando este intente
acceder a los ob"etos Ti"os que s& han sido liberados#
9or otro lado, quiero que te +i"es de nuevo en el e"emplo* vers que hay una invocaci%n al m(todo
MC#<aitNor9endingNinali!ers# 5ste m(todo interrumpe la e"ecuci%n del programa hasta que el MC termine de
e"ecutar todos los destructores que hubiera que e"ecutar# B en este caso tenemos que interrumpir la e"ecuci%n
porque la siguiente l&nea a MC#Collect48 intenta recuperar el valor del campo [dato[# Claro, como la recolecci%n
acaba antes de que se hayan e"ecutado los destructores y el hilo de e"ecuci%n de estos es de ba"a prioridad,
cuando se quiere recuperar este valor resulta que el destructor todav&a no se ha e"ecutado, de modo que la
re+erencia del ob"eto todav&a es null#
'enemos tambi(n una serie de caracter&sticas relacionadas con el MC que son verdaderamente
interesantes# ;abemos hasta ahora que uno de los momentos en los que el MC har la recolecci%n de basura
es cuando detecte que se est quedando sin memoria 4es decir, cuando intentemos instanciar un ob"eto y el MC
se de cuenta de que no le cabe en el mont%n8# 9ues bien, vamos a pensar en qu( ocurrir&a si tenemos un ob"eto
que ocupa bastante memoria y que, adems, se suele tardar bastante tiempo en crear# Con lo que sabemos
hasta ahora, el MC lo liberar siempre que no haya re+erencias hacia (l, pero el problema ser&a saber cundo
destruimos la re+erencia hacia (l* no ser&a bueno hacerlo en el momento en el que lo de"emos de necesitar,
porque si nos vuelve a hacer +alta tendr&amos que volver a crearlo, y hemos dicho que este proceso es
demasiado lentoK lo peor es que tampoco es bueno destruir la re+erencia hacia (l al +inal de la e"ecuci%n del
programa porque hemos dicho que ocupa demasiada memoria, y esto puede hacer que, en alg=n momento
determinado, nos quedemos sin espacio para crear otros ob"etos# Claro, hablando siempre en el supuesto de
que el ob"eto no es necesario en un momento determinado, lo ideal ser&a que el ob"eto permaneciera en
memoria siempre que esto no a+ectara al resto de la aplicaci%n, es decir, siempre que hubiera memoria
su+iciente para crear ms ob"etos, y que se quitara de la misma en el caso de que +uera necesario liberar
memoria para la creaci%n de otros ob"etos# 5n otras circunstancias, la decisi%n ser&a bastante complicada# :a
buena noticia es que #$5' Nrame.orU nos proporciona precisamente esta soluci%n, gracias a las re+erencias
+rgiles# 5sto se ve muy bien con un e"emplo*
usin$ G+stem%
namespace 6e4erencia3ra$il #
class ^b;etoGordo #
public int Dato%
public ^b;etoGordo() #
this.Dato=A%
LO
"onsole.,rite-ine(O"reando ob;eto $ordo + costosoO)%
4or (ulon$ i=(%iVD(((((((((%i22) #&
"onsole.,rite-ine(O:l ob;eto $ordo + costoso 4ue creadoO)%
"onsole.,rite-ine()%
&
&
class 6e4erencia3ra$ilApp #
static void !ain() #
"onsole.,rite-ine(OPulsa /NK6^ para crear el ob;eto $ordoO)%
strin$ c="onsole.6ead-ine()%
^b;etoGordo a=ne< ^b;etoGordo()%
"onsole.,rite-ine(O:l valor de a.Dato es #(&O, a.Dato)%
"onsole.,rite-ine()%
,ea06e4erence <rA=ne< ,ea06e4erence(a)%
a=null%
"onsole.,rite-ine(O:;ecutado <rA=ne< ,ea06e4erence(a)%a=null%O)%
"onsole.,rite-ine(O:l resultado de <rA./sAlive es9 #(&O, <rA./sAlive)%
"onsole.,rite-ine(OPulsa /NK6^ para recuperar el ob;eto $ordoO)%
c="onsole.6ead-ine()%
a=(^b;etoGordo) <rA.Kar$et%
"onsole.,rite-ine(O:;ecutado a=(^b;etoGordo) <rA.Kar$etO)%
"onsole.,rite-ine(O:l valor de a.Dato es #(&O, a.Dato)%
"onsole.,rite-ine(OPulsa /NK6^ para e;ecutar a=null%G"."ollectO)%
c="onsole.6ead-ine()%
a=null%
G"."ollect()%
"onsole.,rite-ine(O:l resultado de <rA./sAlive es9 #(&O, <rA./sAlive)%
"onsole.,rite-ine(O"omo ha sido recolectado no se puede recuperarO)%
"onsole.,rite-ine(ONabr*a que instanciarlo de nuevoO)%
c="onsole.6ead-ine()%
&
&
&
:a salida en la consola ser&a esta*
LS
Pulsa /NK6^ para crear el ob;eto $ordo

"reando ^b;eto $ordo + costoso
:l ob;eto $ordo + costos 4ue creado

:l valor de a.Dato es A

:;ecutado <rA=ne< ,ea06e4erence(a)% a=null%
:l resultado de <rA./sAlive es9 Krue
Pulsa /NK6^ para recuperar el ob;eto $ordo

:;ecutado a=(^b;etoGordo) <rA.Kar$et
:l valor de a.Dato es A
Pulsa /NK6^ para e;ecutar a=null% G"."ollect

:l resultado de <rA./sAlive es9 3alse
"omo ha sido recolectado no se puede recuperar
Nabr*a que instanciarlo de nuevo
5n este pequeo e"emplo hemos diseado una clase que tarda un poco en terminar de e"ecutar el
constructor# 2espu(s de crear el ob"eto, vemos cul es el valor del campo 2ato para que os deis cuenta de que
el ob"eto se ha creado con ()ito# Bien, despu(s creamos el ob"eto .rA de la clase ;ystem#<eaUHe+erence, que
es, en e+ecto, la re+erencia +rgil# 5n el constructor de esta clase 4<eaUHe+erence8 hay que pasarle el ob"eto
hacia el que apuntar dicha re+erencia# :o ms importante viene ahora* como ves, hemos destruido la
re+erencia de a 4en aRnull8# ;in embargo, cuando recuperamos el valor de la propiedad CsAlive de .rA, vemos
que esta retorna 'rue, es decir, que el ob"eto sigue en la memoria 4por lo tanto, no ha sido recolectado por el
MC8# Como est vivo recuperamos la re+erencia 4en la l&nea aR41b"etoMordo8 .rA#'arget8 y volvemos a escribir
el valor de a#2ato, para que veas que, en e+ecto, el ob"eto se ha recuperado# 9ara terminar, al +inal volvemos a
destruir la re+erencia 4aRnull8 y, adems, +or!amos la recolecci%n de basura# 9or este motivo, cuando despu(s
pedimos el valor de la propiedad CsAlive vemos que retorna Nalse 4el ob"eto ha sido recolectado por el MC8, as&
que ya no se podr&a recuperar y habr&a que instanciarlo de nuevo para volverlo a usar# :a ensean!a principal
con la que quiero que te quedes es con la siguiente* si esto +uera un programa grande y hubi(ramos tenido un
ob"eto verdaderamente gordo, lo ideal es crear una re+erencia +rgil 4con la clase <eaUHe+erence8 hacia (l
cuando, de momento, no se necesite ms# As&, si el MC necesita memoria podr recolectarlo, pero no lo har en
caso de que no se necesite# 2e este modo, si ms adelante el programa vuelve a necesitar el ob"eto nos
bastar con comprobar lo que devuelve la propiedad CsAlive de la re+erencia +rgil* si devuelve Nalse habr que
instanciarlo de nuevo, pero si devuelve 'rue bastar con recuperar la re+erencia +rgil, de modo que no habr
que perder tiempo en volver a crear el ob"eto#
DB qu( ocurre si el ob"eto tiene un destructorE Bueno, aqu& la cosa se complica un poco* la clase
;ystem#<eaUHe+erence tiene dos sobrecargas para su constructor* una de ellas la has visto ya en el e"emplo
anterior* hay que pasarle el ob"eto hacia el que queremos esta re+erencia# :a otra sobrecarga requiere, adems,
un valor boolean para el segundo argumento 4que se llama 'racUHesurrection8# 5ste argumento sirve para que
podamos indicar si queremos que se pueda recuperar o no el ob"eto despu(s de que se haya e"ecutado su
L?
destructor* si pasamos true, se podr recuperar, y si pasamos +alse no# DIue c%mo se va a poder recuperar si
se ha recolectadoE FCuidado aqu&G Hecuerda que, si una clase tiene destructor, los ob"etos de la misma no se
liberan la primera ve! que el MC determina que no hay re+erencias hacia ellos, sino que solamente se e"ecuta
su destructor, y se liberarn en la siguiente recolecci%n# 9or este motivo, si pasamos true en el argumento
'racUHesurrection del constructor de la clase <eaUHe+erence puede ocurrir alguna de estas tres cosas cuando
queramos recuperar el ob"eto* si el MC no ha hecho ninguna recolecci%n lo recuperaremos sin msK si el MC
hi!o una recolecci%n, el destructor del ob"eto se habr e"ecutado, pero a=n as& podremos recuperarlo, pues a=n
no se ha liberado, con lo cual es una resurrecci%n pura y duraK si el MC hi!o dos recolecciones el ob"eto no ser
recuperable#
DC%mo lo llevasE DBienE D;eguimosE Venga, an&mate hombre, que ya queda poco###
$os queda hablar de las generaciones# $o, no voy a empe!ar un debate sobre padres e hi"os, no# A ver si
nos centramos un poquito, DehE Veeeeenga### ;ac=dete la cabe!a un poco### te har sentir me"or### DBaE 9ues
vamos# 5l MC, para me"orar su rendimiento, agrupa los ob"etos en di+erentes generaciones# D9or qu(E 9ues
porque, generalmente, los =ltimos ob"etos que se construyen suelen ser los primeros en de"ar de ser utili!ados#
9iensa, por e"emplo, en una aplicaci%n para <indo.s, Cnternet 5)plorer, por e"emplo# 5l primer ob"eto que se
crea es la ventana de la aplicaci%n 4bueno, puede que no sea as& siendo estrictos, pero si me admit&s la licencia
entender(is lo que quiero decir mucho me"or8# Bien, cuando abres un cuadro de dilogo con alguna opci%n de
men=, se crea otro ob"eto, o sea, ese cuadro de dilogo precisamente# Tasta aqu&, entonces, tenemos dos
ob"etos# DCul de ellos cerrars primeroE 5+ectivamente, el cuadro de dilogo# 5s decir, el =ltimo que se cre%#
D:o vesE $o estoy diciendo que esto sea as& siempre 4si +uera as&, el mont%n ser&a una pila, y no el mont%n8,
sino que es muy +recuente# :a gran venta"a de que el MC use generaciones es que, cuando necesita memoria,
no revisa todo el mont%n para liberar todo lo liberable, sino que libera primero todo lo que pueda de la =ltima
generaci%n, pues lo ms probable es que sea aqu& donde encuentre ms ob"etos in=tiles# Volvamos al e"emplo
del Cnternet 5)plorer# Antes de la primera recolecci%n se han abierto y cerrado, por e"emplo tres cuadros de
dilogo# 'odos estos ob"etos estn, por lo tanto, en la primera generaci%n, y cuando se e"ecute el MC se pueden
liberar e)cepto, claro est, la ventana de la aplicaci%n, que sigue activa# 'odos los ob"etos que se creen a partir
de la primera recolecci%n pasarn a +ormar parte de la segunda generaci%n# As&, cuando el CM vuelva a
e"ecutarse comprobar solamente si puede liberar los ob"etos de la segunda generaci%n, y, si ha liberado
memoria su+iciente, no mirar los de la primera 4que, recuerda, solamente quedaba la ventana de la aplicaci%n8#
5+ectivamente, ha ganado tiempo, ya que era poco probable que tuviera que recolectar alg=n ob"eto de la
generaci%n anterior# Cmagina que mientras se hac&a esta segunda recolecci%n hab&a un cuadro de dilogo
abierto# Claro, habr liberado todo lo de la segunda generaci%n 4que no hiciera +alta, por supuesto8 menos este
cuadro de dilogo, pues a=n est en uso# A partir de aqu&, los ob"etos pertenecern a la tercera generaci%n, y el
MC tratar de liberar memoria solamente entre estos# ;i consigue liberar lo que necesita, no mirar en las dos
generaciones anteriores, aunque haya ob"etos que se pudieran liberar 4como el cuadro de dilogo que ten&amos
abierto cuando se e"ecut% la segunda recolecci%n8# 9or supuesto, en caso de que liberando toda la memoria
posible de la tercera generaci%n no consiguiera el espacio que necesita, tratar&a de liberar tambi(n espacio de
la segunda generaci%n y, si a=n as& no tiene su+iciente, liberar&a tambi(n lo que pudiera de la primera
generaci%n 4en caso de que no encuentre memoria su+iciente para crear un ob"eto despu(s de recolectar todo lo
recolectable, lan!ar&a una e)cepci%n### o error8# A partir de aqu&, no hay ms generaciones, es decir, el MC
agrupa un m)imo de tres generaciones#
9ara el mane"o de las generaciones tenemos el m(todo MetMeneration, con dos sobrecargas* una devuelve
la generaci%n de una re+erencia +rgil 4ob"eto <eaUHe+erence8 que se le pasa como argumento, y la otra
devuelve la generaci%n de un ob"eto de cualquier clase que se le pasa como argumento# 5l m(todo Collect 4que
tanto hemos usado8 tambi(n tiene dos sobrecargas* una de ellas es la que hemos venido usando hasta ahora,
es decir, sin argumentos, que hace una recolecci%n total del mont%n 4es decir, sin tener en cuenta las
generaciones8, y otra en la que hace solamente la recolecci%n de una generaci%n que se le pase como
argumento# Tay que tener cuidado con esto* la generaci%n ms reciente siempre es la generaci%n cero, la
anterior es la generaci%n uno y la anterior la generaci%n dos# Con esta entrega hay tambi(n un e"emplo que
trata sobre las generaciones, pero no te lo reprodu!co aqu& porque es demasiado largo, y esta entrega ya es, de
por s&, bastante [hermosota[# 5n el sumario de este e"emplo te digo qu( es lo ms importante#
O@
Tay un =ltimo detalle que no quiero de"ar escapar* debido a c%mo +unciona el recolector de basuras, lo ms
recomendable es limitar la utili!aci%n de destructores solamente a aquellos casos en los que sea estrictamente
necesario ya que, como te di"e antes, la liberaci%n de la memoria de un ob"eto que tiene destructor no se
e+ect=a en la primera recolecci%n en la que detecte que ya no hay re+erencias hacia (l, sino en la siguiente#
Aunque te pare!ca mentira, esto del MC tiene todav&a ms [miga[, pero como lo que queda tiene mucho que
ver con la e"ecuci%n multiVhilo lo voy a de"ar para ms adelante, que tampoco quiero provocar dolores de
cabe!a a nadie###
: .ctava entrega (Campos & propiedades.!
2.1 Campos
Algunos autores hablan indistintamente de campos y propiedades como si +ueran la misma cosa, y tiene su
l%gica, no creis que no, porque para el cliente de una clase van a ser cosas muy parecidas# ;in embargo, yo
me voy a mo"ar y voy a establecer distinci%n entre campos y propiedades, no por complicar la vida a nadie, sino
para que sepis a qu( me re+iero cuando hablo de un campo o cuando hablo de una propiedad# Ambas cosas
4campos y propiedades8 representan a los datos de una clase, aunque cada uno de ellos lo hace de un modo
di+erente# Hecuerda que en la tercera entrega hablamos de los indicadores, y desde entonces hemos ido
usando alguno# 9ues bien, los campos de una clase se construyen a base de indicadores# Vamos a empe!ar
"ugando un poco con todo esto 4le daremos ms de una vuelta8*
usin$ G+stem%
namespace "ircun4erencia #
class "ircun4erencia #
public "ircun4erencia(double radio) #
this.6adio=radio%
&
public double 6adio%
public double Perimetro%
public double Area%
public const double P/==.?A?@RD>%
&
class "ircun4erenciaApp #
static void !ain() #
"ircun4erencia c%
strin$ rad%
double radio=(%
do #
tr+ #
"onsole.,rite(ODame un radio para la circun4erencia9 O)%
rad="onsole.6ead-ine()%
radio=Double.Parse(rad)%
O1
& catch #
continue%
&
& <hile (radioV=()%
c=ne< "ircun4erencia(radio)%
c.Perimetro=D H "ircun4erencia.P/ H c.6adio%
c.Area="ircun4erencia.P/ H !ath.Po<(c.6adio,D)%
"onsole.,rite-ine(O:l radio es9 #(&O, c.6adio)%
"onsole.,rite-ine(O:l per*metro es9 #(&O, c.Perimetro)%
"onsole.,rite-ine(O:l Erea es 9 #(&O, c.Area)%
strin$ a="onsole.6ead-ine()%
&
&
&
Bueno, una ve! ms te pido que no te preocupes por lo que no entiendas, porque hay cosas que veremos
ms adelante, como los bloques do, try y catch# 5stn puestos para evitar errores en tiempo de e"ecuci%n 4para
que veis que me preocupo de que no tengis di+icultades8# Bien, lo ms importante de todo es la clase
circun+erencia# DIu( es lo que te he puesto en negrillaE 5+ectivamente, tres variables y una constante# 9ues
bien, esos son los campos de la clase Circun+erencia# D;er&as capa! de ver si hay declarado alg=n campo en la
clase Circun+erenciaApp, que es donde hemos puesto el m(todo 0ainE A ver### a ver### 9or ah& hay uno que dice
que hay tres* uno de la clase Circun+erencia, otro de la clase string y otro de la clase double# DAlguien est de
acuerdo###E 9ues yo no# 5n e+ecto, hay tres variables dentro del m(todo 0ain, pero no son campos de la clase
Circun+erenciaApp, porque estn dentro de un m(todo# 9or lo tanto, todos los campos son indicadores, pero no
todos los indicadores son campos, ya que si una variable representa o no un campo depende del lugar donde
se declare#
Hecuerda los modi+icadores de acceso de los que hablamos por primera ve! en la tercera entrega de este
curso 4private, protected, internal y public8# 9ues bien, estos modi+icadores son aplicables a las variables y
constantes solamente cuando estas representan los campos de una clase, y para ello deben estar declaradas
como miembros de la misma dentro de su bloque# ;in embargo, una variable que est( declarada en otro bloque
distinto 4dentro de un m(todo, por e"emplo8 no podr ser un campo de la clase, pues ser siempre privada para
el c%digo que est( dentro de ese bloque, de modo que no se podr acceder a ella desde +uera del mismo# 9or
este motivo, las tres variables que estn declaradas dentro del m(todo 0ain en nuestro e"emplo no son
campos, sino variables privadas accesibles solamente desde el c%digo de dicho m(todo# 2el mismo modo, si
hubi(ramos declarado una variable dentro del bloque [do[, esta hubiera sido accesible solamente dentro del
bloque [do[, e inaccesible desde el resto del m(todo 0ain#
Ahora quiero que te +i"es especialmente en el c%digo del m(todo 0ain# 5stamos accediendo a los campos
del ob"eto con la sinta)is [nombreob"eto#campo[, igual que se hac&a para acceder a los m(todos, aunque sin
poner par(ntesis al +inal# ;in embargo, hay una di+erencia importante entre el modo de acceder a los tres
campos variables 4Area, 9erimetro y Hadio8 y el campo constante 49C8* 5n e+ecto, a los campos variables hemos
accedido como si +ueran m(todos normales, pero al campo constante hemos accedido como acced&amos a los
m(todos static, es decir, poniendo el nombre de la clase en lugar del nombre del ob"eto# D9or qu(E 9orque,
dado que un campo constante mantendr el mismo valor para todas las instancias de la clase, el compilador
ahorra memoria colocndolo como si +uera static, evitando as& tener que reservar un espacio de memoria
distinto para este dato 4que, recuerda, siempre es el mismo8 en cada una de las instancias de la clase#
O2
Ba s( que alguno estar pensando* [pues vaya una clase Circun+erencia has hecho, que tienes que hacerte
todos los clculos a mano en el m(todo 0ain# 9ara eso nos hab&amos declarado las variables en dicho m(todo
y nos ahorrbamos la clase[# 9ues tienes ra!%n# :o suyo ser&a que +uera la clase Circun+erencia la que hiciera
todos los clculos a partir del radio, en lugar de tener que hacerlos en el m(todo 0ain o en cualquier otro
m(todo o programa que utilice esta clase# Vamos con ello, a ver qu( se puede hacer*
class "ircun4erencia #
public "ircun4erencia(double radio) #
this.6adio=radio%
this.Perimetro=D H P/ H this.6adio%
this.Area=P/ H !ath.Po<(this.6adio,D)%
&
public double 6adio%
public double Perimetro%
public double Area%
public const double P/==.?A?@RD>%
&
Bueno, ahora, como ves, hemos calculado los valores de todos los campos en el constructor# As& el cliente
no tendr que hacer clculos por su cuenta para saber todos los datos de los ob"etos de esta clase, sino que
cuando se instancie uno, las propiedades tendrn los valores adecuados# DIu( ocurrir&a si en el cliente
escribi(ramos este c%digoE*
"ircun4erencia c=ne< "ircun4erencia(A)%
c.Perimetro=?%
c.Area=D%
Al instanciar el ob"eto, se e"ecutar su constructor dando los valores adecuados a los campos Area y
9erimetro# ;in embargo, despu(s el cliente puede modi+icar los valores de estos campos, asignndole valores a
su anto"o y haciendo, por lo tanto, que dichos valores no sean coherentes 4claro, si el radio vale >, el per&metro
no puede ser 1, ni el rea puede ser 28# DC%mo podemos arreglar esta +alta de seguridadE 9ues usando algo
que no e)ist&a hasta ahora en ning=n otro lengua"e* los campos de s%lo lectura 4o"o, que digo campos, no
propiedades8# Vemoslo*
class "ircun4erencia #
public "ircun4erencia(double radio) #
this.6adio=radio%
this.Perimetro=D H P/ H this.6adio%
this.Area=P/ H !ath.Po<(this.6adio,D)%
&
public double 6adio%
public readonl+ double Perimetro%
public readonl+ double Area%
O3
public const double P/==.?A?@RD>%
&
Bien, ahora tenemos protegidos los campos 9erimetro y Area, pues son de s%lo lectura, de modo que ahora
el cliente no podr modi+icar los valores de dichos campos# 9ara hacerlo +&"ate que hemos puesto la palabra
readonly delante del tipo del campo# ;in embargo, seguimos teniendo un problema* Dqu( pasa si, despu(s de
instanciar la clase, el cliente modi+ica el valor del radioE 9ues que estamos en las mismas### 5l radio volver&a a
no ser coherente con el resto de los datos del ob"eto# DIu( se os ocurre para arreglarloE Claro, podr&amos
poner el campo Hadio tambi(n de s%lo lectura, pero en este caso tendr&amos que instanciar un nuevo ob"eto
cada ve! que necesitemos un radio distinto, lo cual puede resultar un poco engorroso# Iui! podr&amos hacer
un pequeo rodeo* ponemos el radio tambi(n como campo de s%lo lectura y escribimos un m(todo para que el
cliente pueda modi+icar el radio, y escribimos en (l el c%digo para modi+icar los tres campos, de modo que
vuelvan a ser coherentes# ;in embargo, esto no se puede hacer# D9or qu(E 9orque los campos readonly
solamente pueden ser asignados una ve! en el constructor, y a partir de aqu& su valor es constante y no se
puede variar en esa instancia# B entonces, Dpor qu( no usamos constantes en ve! de campos de s%lo lecturaE
9ero hombre###, c%mo me preguntas eso### 9ara poder usar constantes hay que saber previamente el valor que
van a tener 4como la constante 9C, que siempre vale lo mismo8, pero, en este caso, no podemos usar
constantes para radio, rea y per&metro porque no sabremos sus valores hasta que no se e"ecute el programa#
Hesumiendo* los campos de s%lo lectura almacenan valores constantes que no se conocern hasta que el
programa est( en e"ecuci%n# Tabr que hacer otra cosa para que esto +uncione me"or, pero la haremos
despu(s### Antes tengo que contaros ms cosas sobre los campos#
9or otro lado, los campos, igual que los m(todos y los constructores, tambi(n pueden ser static# ;u
comportamiento ser&a parecido* un campo static es aquel que tiene mucho ms que ver con la clase que con
una instancia particular de ella# 9or e"emplo, si quisi(ramos aadir una descripci%n a la clase circun+erencia,
podr&amos usar un campo static, porque todas las instancias de esta clase se a"ustarn necesariamente a dicha
descripci%n# ;i ponemos el modi+icador static a un campo de s%lo lectura, este campo ha de ser iniciali!ado en
un constructor static# Ahora bien, recuerda que las constantes no aceptan el modi+icador de acceso static* si su
modi+icador de acceso es public o internal ya se comportar como su +uera un campo static# 9ongamos un
e"emplo de esto*
class "ircun4erencia #
static "ircun4erencia() #
Descripcion=OPol*$ono re$ular de in4initos ladosO%
&
public "ircun4erencia(double radio) #
this.6adio=radio%
this.Perimetro=D H P/ H this.6adio%
this.Area=P/ H !ath.Po<(this.6adio,D)%
&
public double 6adio%
public readonl+ double Perimetro%
public readonl+ double Area%
public const double P/==.?A?@RD>%
public static readonl+ strin$ Descripcion%
&
O>
Ahora la clase Circun+erencia cuenta con un constructor static que se ocupa de iniciali!ar el valor del campo
2escripci%n, que tambi(n es static#
Bien, retomemos la problemtica en la que estbamos sumidos con la clase Circun+erencia# Veamos* el
ob"etivo es que esta clase contenga siempre datos coherentes, dado que el rea y el per&metro siempre estn
en +unci%n del radio, y que el radio se pueda modi+icar sin necesidad de volver a instanciar la clase# 9or lo tanto,
tenemos claro que no podemos usar campos ni campos de s%lo lectura, ya que los primeros no nos permiten
controlar los datos que contienen, y los segundos no nos permiten modi+icar su valor despu(s de ser
iniciali!ados en el constructor#
Con lo que hemos aprendido hasta ahora ya tenemos herramientas su+icientes como para solventar el
problema, aunque, como veremos despu(s, no sea el modo ms id%neo de hacerlo# Veamos* podr&amos
cambiar los modi+icadores de acceso de los campos, haci(ndolos private o protected en lugar de public, y
despu(s escribir m(todos para retornar sus valores# Vamos a ver c%mo se podr&a hacer esto*
usin$ G+stem%
namespace "ircun4erencia!etodos #
class "ircun4erencia #
public "ircun4erencia(double rad) #
this.radio=rad%
&
protected double radio%
const double P/==.?A?@RD>%
public double 6adio() #
return this.radio%
&
public void 6adio(double rad) #
this.radio=rad%
&
public double Perimetro() #
return D H P/ H this.radio%
&
public double Area() #
return P/ H !ath.Po<(this.radio,D)%
&
&
class "ircun4erenciaApp #
static void !ain() #
"ircun4erencia c=ne< "ircun4erencia(A)%
"onsole.,rite-ine(O:l radio de la circun4erencia es #(&O,c.6adio())%
"onsole.,rite-ine(O:l per*metro de la circun4erencia es #(&O, c.Perimetro())%
OJ
"onsole.,rite-ine(O:l Erea de la circun4erencia es #(&O, c.Area())%
"onsole.,rite-ine(OPulsa /NK6^ para incrementar el radio en ?O)%
strin$ a = "onsole.6ead-ine()%
c.6adio(c.6adio()2?)%
"onsole.,rite-ine(O:l radio de la circun4erencia es #(&O,c.6adio())%
"onsole.,rite-ine(O:l per*metro de la circun4erencia es #(&O,
c.Perimetro())%
"onsole.,rite-ine(O:l Erea de la circun4erencia es #(&O, c.Area())%
a="onsole.6ead-ine()%
&
&
&
Como ves, ahora la clase Circun+erencia garanti!a que sus datos contendrn siempre valores coherentes,
adems de permitir que se pueda modi+icar el radio, pues el m(todo Hadio est sobrecargado* una de las
sobrecargas simplemente devuelve lo que vale la variable protected radio y la otra no devuelve nada, sino que
da al radio un nuevo valor# 9or otro lado, ya que hemos escrito m(todos para devolver per&metro y rea nos
ahorramos las variables para estos datos, pues podemos calcularlos directamente en dichos m(todos# ;in
embargo, la +orma de usar esta clase es muy +or!ada y muy poco intuitiva, es decir, poco natural# 5n e+ecto, no
resulta natural tener que poner los par(ntesis cuando lo que se quiere no es e"ecutar una operaci%n, sino
simplemente obtener un valor# 5l colmo ya es cuando queremos incrementar el radio en una unidad, en la l&nea
c#Hadio4c#Hadio48-18K esto es completamente antinatural, pues lo ms l%gico hubiera sido poder hacerlo con
esta otra l&nea* c#Hadio--# 9ero, tranquilos, C# tambi(n nos soluciona estas pequeas de+iciencias, gracias a
las propiedades#
2.2 Propiedades
Como di"e al principio, las propiedades tambi(n representan los datos de los ob"etos de una clase, pero lo
hacen de un modo completamente distinto a los campos# Antes vimos que los campos no nos permit&an tener el
control de su valor salvo que +ueran de s%lo lectura, y si eran de s%lo lectura solamente se pod&an asignar una
ve! en el constructor# 5sto puede ser verdaderamente =til en muchas ocasiones 4y por eso os lo he e)plicado8,
pero no en este caso en concreto# 9ues bien, las propiedades solventan todos estos problemas* por un lado nos
permiten tener un control absoluto de los valores que reciben o devuelven, y adems no tenemos limitaciones
para modi+icar y cambiar sus valores tantas veces como sea preciso#
:as propiedades +uncionan internamente como si +ueran m(todos, esto es, e"ecutan el c%digo que se
encuentra dentro de su bloque, pero se muestran al cliente como si +ueran campos, es decir, datos# ;oy
consciente de que, dicho as&, suena bastante raro, pero vers que es muy +cil# :a sinta)is de una propiedad es
la siguiente*
acceso \static] tipo NombrePropiedad #
$et #
)) "di$o para calcular el valor de retorno (si procede)
return 'alor6etorno%
&
OL
set #
)) "di$o para validar +)o asi$nar el valor de la propiedad
&
&
Veamos* primero el modi+icador de acceso, que puede ser cualquiera de los que se usan tambi(n para los
campos# ;i no se indica, ser private# 2espu(s la palabra static si queremos de+inirla como propiedad esttica,
es decir, que ser&a accesible sin instanciar ob"etos de la clase, pero no accesible desde las instancias de la
misma 4como los campos static8# 9osteriormente se indica el tipo del dato que almacenar la propiedad
4cualquier tipo valor o cualquier tipo re+erencia8, seguido del nombre de la propiedad# 2entro del bloque de la
propiedad ves que hay otros dos bloques* el bloque get es el bloque de retorno, es decir, el que nos permitir
ver lo que vale la propiedad desde la aplicaci%n clienteK y el bloque set es el bloque de asignaci%n de la
propiedad, es decir, el que nos permitir asignarle valores desde la aplicaci%n cliente# 5l orden en que se
pongan los bloques get y set es indi+erente, pero, obviamente, ambos han de estar dentro del bloque de la
propiedad# 9or otro lado, si se omite el bloque de asignaci%n 4set8 habremos construido una propiedad de s%lo
lectura# Veremos esto mucho me"or con un e"emplo# Vamos a modi+icar la clase Circun+erencia para ver c%mo
podr&a ser usando propiedades*
usin$ G+stem%
namespace "ircun4erencia #
class "ircun4erencia #
public "ircun4erencia(double radio) #
this.radio=radio%
&
private double radio%
const double P/==.?A?@RD>%
public double 6adio #
$et #
return this.radio%
&
set #
this.radio=value%
&
&
public double Perimetro #
$et #
return D H P/ H this.radio%
&
&
public double Area #
OO
$et #
return P/ H !ath.Po<(this.radio, D)%
&
&
&
&
Bueno, lo cierto es que, desde la primera clase Circun+erencia que escribimos a esta hay un abismo### Ahora
no hemos escrito m(todos para modi+icar el radio ni para obtener los valores de las otros datos, sino que hemos
escrito propiedades# Mracias a esto conseguimos que el cliente pueda acceder a los datos de un modo mucho
ms natural# 9ongamos un m(todo 0ain para que aprecies las di+erencias, y luego lo e)plicamos con calma*
static void !ain() #
"ircun4erencia c=ne< "ircun4erencia(A)%
"onsole.,rite-ine(O:l radio de la circun4erencia es #(&O,c.6adio)%
"onsole.,rite-ine(O:l per*metro de la circun4erencia es #(&O, c.Perimetro)%
"onsole.,rite-ine(O:l Erea de la circun4erencia es #(&O, c.Area)%
"onsole.,rite-ine(OPulsa /NK6^ para incrementar el 6adio en ?O)%
strin$ a = "onsole.6ead-ine()%
c.6adio22%
"onsole.,rite-ine(O:l radio de la circun4erencia es #(&O,c.6adio)%
"onsole.,rite-ine(O:l per*metro de la circun4erencia es #(&O, c.Perimetro)%
"onsole.,rite-ine(O:l Erea de la circun4erencia es #(&O, c.Area)%
a="onsole.6ead-ine()%
&
Ahora puedes apreciar claramente las di+erencias* accedemos a las propiedades tal y como hac&amos
cuando hab&amos de+inido los datos de la clase a base de campos# ;in embargo tenemos control absoluto
sobre los datos de la clase gracias a las propiedades# 5n e+ecto, podemos modi+icar el valor del Hadio con toda
naturalidad 4en la l&nea c#Hadio--8 y esta modi+icaci%n a+ecta tambi(n a las propiedades 9erimetro y Area#
Vamos a ver poco a poco c%mo ha +uncionado este programa* cuando instanciamos el ob"eto se e"ecuta su
constructor, asignndose el valor que se pasa como argumento al campo radio 4que es protected y, por lo tanto,
no accesible desde el cliente8# Cuando recuperamos el valor de la propiedad Hadio para escribirlo en la consola
se e"ecuta el bloque [get[ de dicha propiedad, y este bloque devuelve, precisamente el valor del campo radio,
que era la variable donde se almacenaba este dato# Cuando se recuperan los valores de las otras dos
propiedades tambi(n para escribirlos en la consola sucede lo mismo, es decir, se e"ecutan los bloques get de
cada una de ellas que, como veis, retornan el resultado de calcular dichos datos# 9or =ltimo, cuando
incrementamos el valor del radio 4c#Hadio--8 lo que se e"ecuta es el bloque set de la propiedad, es decir, que se
asigna el nuevo valor 4representado por [value[8 a la variable protected radio# DB por qu( las propiedades Area
y 9erimetro no tienen bloque setE Hecuerda que el bloque set es el bloque de asignaci%nK por lo tanto, si se
omite, tendremos una propiedad de s%lo lectura# DB cul es la di+erencia con los campos de s%lo lecturaE 9ues
la di+erencia es evidente* un campo de s%lo lectura ha de estar representado necesariamente por una variable,
y, adems, solamente se le puede asignar el valor una ve! en el constructorK por contra, el que una propiedad
sea de s%lo lectura no implica que su valor sea constante, sino =nica y e)clusivamente que no puede ser
modi+icado por el cliente# ;i hubi(ramos puesto campos de s%lo lectura no los podr&a modi+icar ni el cliente, ni la
OS
propia clase ni el mism&simo Bill Mates en persona# DB de d%nde ha salido el valueE Bien, value es una variable
que declara y asigna impl&citamente el compilador en un bloque set para que nosotros sepamos cul es el valor
que el cliente quiere asignar a la propiedad, es decir, si se escribe c#HadioRS, value valdr&a S# As& podremos
comprobar si el valor que se intenta asignar a la propiedad es adecuado# 9or e"emplo, si el valor que se intenta
asignar al radio +uera negativo habr&a que recha!arlo, puesto que no tendr&a sentido, pero como a=n no hemos
llegado a esa parte, lo de"amos para la pr%)ima entrega#
$o me gustar&a acabar esta entrega sin evitar que alguien pueda tomar conclusiones equivocadas# Veamos,
os he dicho que las propiedades +uncionan internamente como si +ueran m(todos, pero que no es necesario
poner los par(ntesis cuando son invocadas, pues se accede a ellas como si +ueran campos# ;in embargo esto
no quiere decir que siempre sea me"or escribir propiedades en lugar de m(todos 4claro, si no requieren ms de
un argumento8# :as propiedades se han inventado para hacer un uso ms natural de los ob"etos, y no para otra
cosa# D5ntonces, cundo es bueno escribir un m(todo y cundo es bueno escribir una propiedadE 9ues bien,
hay que escribir un m(todo cuando este implique una acci%n, y una propiedad cuando esta implique un dato#
Vamos a retomar una ve! ms el [remanido[ e"emplo de la clase Coche# 9od&amos haber escrito el m(todo
Nrenar como una propiedad, con lo que, en la aplicaci%n cliente tendr&amos que invocarlo as&* coche#NrenarR1@#
;in embargo, aunque +uncionar&a e)actamente igual, esto no tendr&a mucho sentido, pues +renar es una acci%n
y no un dato, y el modo ms natural de e"ecutar una acci%n es con un m(todo, o sea, coche#Nrenar41@8#
2.3 #jercicio 1
Bien, creo que, con todo lo que hemos aprendido hasta ahora, llega el momento de proponeros un [pequeo
e"ercicio[# Aparecer resuelto con la pr%)ima entrega 4no antes, que de lo contrario no tendr&a gracia8, pero te
recomiendo que intentes hacerlo por tu cuenta y mires la soluci%n cuando ya te +uncione o si te quedas
atascado sin remedio ya que, de lo contrario, no aprenders nunca a escribir c%digo# 5so s&* m&rate los apuntes
y la teor&a tanto como lo necesites, porque estos siempre los vas a tener a tu disposici%n# 'ambi(n es
importante que no te rindas a las primeras de cambio* cuando te apare!can errores de compilaci%n intenta
resolverlos t= mismo, porque cuando est(s desarrrollando una aplicaci%n propia tendrs que hacerlo as&, de
modo que lo me"or ser que empieces cuanto antes# 9or =ltimo, te aconse"o que antes de mirar el e"ercicio
resuelto si ves que no te sale eches un vista!o a las pistas que te voy poniendo, a ver si as& lo vas sacando#
Bueno, venga, vale de rollos y vamos a lo que vamos*
5"ercicio 1* Aunque soy consciente de que este e"ercicio te parecer un mundo si no hab&as programado
antes, te aseguro que es muy +cil# 5s un poco amplio para que puedas practicar casi todo lo que hemos visto
hasta ahora# Vete haci(ndolo paso por paso con tranquilidad, y usa el tipo uint para todos los datos num(ricos*
5scribe una aplicaci%n con estos dos espacios de nombres* Meometria y 9ruebaMeometria# 2entro del espacio
de nombres Meometria tienes que escribir dos clases* 9unto y Cuadrado# :a clase 9unto ha de tener dos
campos de s%lo lectura* Q e B 4que sern las coordenadas del punto8# :a clase Cuadrado ha de tener las
siguientes propiedades del tipo 9unto 4de solo lectura8* Vertice1, Vertice2, Vertice3 y Vertice> 4que
corresponden a los cuatro v(rtices del cuadrado8# :a base de todos los cuadrados de esta clase ser siempre
hori!ontal# 'ambi(n ha de tener las propiedades :ado, Area y 9erimetro, siendo la primera de lectura6escritura y
las otras dos de s%lo lectura# 9or otro lado, debe tener dos constructores* uno para construir el cuadrado por
medio de los v(rtices 1 y 3 y otro para construir el cuadrado a trav(s del Vertice1 y la longitud del lado# 5n el
espacio de nombres 9ruebaMeometria es donde escribirs una clase con un m(todo 0ain para probar si
+uncionan las clases escritas anteriormente# 5n este espacio de nombres quiero que utilices la directiva using
para poder utili!ar todos los miembros del espacio de nombres Meometr&a directamente# 5n este espacio de
nombres escribe tambi(n un m(todo que muestre todos los datos de un cuadrado en la consola# Tala, al ta"o###
2.3.1 Pistas para el ejercicio 1 3#ntrega 24
:a clase 9unto necesita los campos de s%lo lectura Q e B, que sern las coordenadas del punto# 5ntonces,
necesitars un constructor para asignarles los valores necesarios#
DIu( le hace +alta al constructor de la clase 9untoE :os dos datos que se han de guardar en las variables
privadas ) e y, de modo que el constructor necesitar dos argumentos* uno para ) y otro para y#
O?
;i necesitas cuatro propiedades de s%lo lectura de tipo 9unto para los v(rtices, necesitars tambi(n alguna
variable privada del mismo tipo para almacenar estos datos#
;i la base del cuadrado es hori!ontal, Dnecesitas realmente una variable para cada v(rticeE Bo creo que te
sobra con una para un v(rtice y otra para el lado#
;i tienes el lado, Dnecesitas realmente variables para las propiedades de s%lo lectura Area y 9erimetroE
DC%mo escribimos un constructor para construir el cuadrado a partir de los v(rtices 1 y 3E ;encillamente,
con un constructor que acepte dos argumentos del tipo 9unto, y a partir de los dos v(rtices puedes calcular el
lado#
DC%mo escribimos un constructor para constuir el cuadrado a partir del v(rtice 1 y el ladoE Con otro
constructor que acepte un argumento de tipo 9unto y otro de tipo uint#
9ara escribir un m(todo que muestre los datos del cuadrado basta con escribir uno que acepte un
argumento de la clase Cuadrado#
2.3.2 5esolucin del ejercicio
Bien, aqu& est# 5ste es todo el c%digo del e"ercicio, y est convenientemente comentado# 9or supuesto, se
podr&a haber escrito de muchas +ormas distintas, por lo que es ms que probable que el que has hecho t= no
sea e)actamente igual# ;i el tuyo tambi(n +unciona, est per+ecto# ;i no, intenta terminarlo siguiendo con tu
idea, y usa el e"ercicio que te doy ya resuelto como gu&a en lugar de copiarlo tal cual#
usin$ G+stem%
namespace Geometria #
))) Vsummar+J
))) "lase Punto, con dos campos de slo lectura para almacenar
))) las coordenadas del punto.
))) V)summar+J
class Punto #
)) :ste es el constructor de la clase
public Punto(uint X, uint +) #
this.T=X%
this.b=+%
&
)) :stos son los campos de slo lectura de la clase
public readonl+ uint T%
public readonl+ uint b%
&
))) Vsummar+J
))) "lase "uadrado. "ada cosa estE comentada.
))) V)summar+J
class "uadrado #
S@
)H "onstructor9 constru+e un cuadrado a partir del v8rtice? +
H la lon$itud del lado H)
public "uadrado(Punto vert?, uint lado) #
this.vertice?=vert?%
this.lado=lado%
&
)H "onstructor9 constru+e un cuadrado a partir de los v8rtices
H ? + tres. Nabr*a que comprobar si las componentes del v8rtice
H = son ma+ores o menores que las del v8rtice?. Gin embar$o, como
H a5n no hemo lle$ado a eso vamos a presuponer que las componentes
H del v8rtice = son siempre ma+ores que las del uno. De todas
H 4ormas, por si acaso, para calcular el lado tomaremos el valor
H absoluto de la di4erencia. H)
public "uadrado(Punto vert?, Punto vert=) #
this.vertice?=vert?%
this.lado=(uint) !ath.Abs(vert=.T7vert?.T)%
&
)) 'ariable local para almacenar el v8rtice?
protected Punto vertice?%
)H 'ariable local para el lado. Ge podr*a construir con un campo,
H pero el enunciado dec*a que ten*a que ser una propiedad. H)
protected uint lado%
)H Propiedad 'ertice?9 devuelve el Punto almacenado en la variable
H protected vertice? H)
public Punto 'ertice? #
$et #
return this.vertice?%
&
&
)H Propiedad 'erticeD9 como no ha+ variable local para almacenar
H el punto, se crea + se retorna uno nuevo9 la componente T es
H i$ual a la T del v8rtice? mEs la lon$itud del lado. H)
public Punto 'erticeD #
$et #
S1
Punto p=ne< Punto(this.vertice?.T 2 this.lado,this.vertice?.b)%
return p%
&
&
)H Propiedad 'ertice=9 como no ha+ variable local para almacenar
H el punto, se crea + se retorna uno nuevo9 la componente T es
H i$ual a la T del v8rtice? mEs la lon$itud del lado, + ocurre
H lo mismo con la componente b H)
public Punto 'ertice= #
$et #
Punto p=ne< Punto(this.vertice?.T 2 this.lado,this.vertice?.b2this.lado)%
return p%
&
&
)H Propiedad 'erticeA9 como no ha+ variable local para almacenar
H el punto, se crea + se retorna uno nuevo9 la componente T es
H i$ual a la T del v8rtice?, + la componente b es i$ual a la
H b del v8rtice? mEs la lon$itud del lado H)
public Punto 'erticeA #
$et #
Punto p=ne< Punto(this.vertice?.T,this.vertice?.b2this.lado)%
return p%
&
&
)H Propiedad de lectura)escritura -ado9 en el bloque $et se retorna
H el valor de la variable local lado, + en el bloque set se
H asi$na el nuevo valor a la variable local lado. H)
public uint -ado #
$et #
return this.lado%
&
set #
this.lado=value%
&
S2
&
)H Propiedad de slo lectura Per*metro9 se retorna el per*metro,
H que es cuatro veces el lado H)
public uint Perimetro #
$et #
return this.ladoHA%
&
&
)H Propiedad de slo lectura Area9 se retorna el Erea, que es el
H cuadrado del lado H)
public uint Area #
$et #
return (uint) !ath.Po<(this.lado,D)%
&
&
&
&
namespace PruebaGeometria #
usin$ Geometria%
class GeometriaApp #
)H !8todo para mostrar las propiedades del cuadrado que se le
H pase como ar$umento. "omo debe poderse llamar al m8todo sin
H instanciar la clase GeometriaApp, tiene que ser static H)
static void Prop"uadrado("uadrado cuad) #
"onsole.,rite-ine(O"oordenadas de los v8rtices del cuadrado9O)%
"onsole.,rite-ine(O'8rtice ?9 (#(&,#?&)O, cuad.'ertice?.T, cuad.'ertice?.b)%
"onsole.,rite-ine(O'8rtice D9 (#(&,#?&)O, cuad.'erticeD.T, cuad.'erticeD.b)%
"onsole.,rite-ine(O'8rtice =9 (#(&,#?&)O, cuad.'ertice=.T, cuad.'ertice=.b)%
"onsole.,rite-ine(O'8rtice A9 (#(&,#?&)O, cuad.'erticeA.T, cuad.'erticeA.b)%
"onsole.,rite-ine(O-on$itud del lado9 #(&O, cuad.-ado)%
"onsole.,rite-ine(OPer*metro9 #(&O, cuad.Perimetro)%
"onsole.,rite-ine(Ocrea9 #(&O, cuad.Area)%
"onsole.,rite-ine(OPulsa /NK6^ para continuarO)%
strin$ a="onsole.6ead-ine()%
S3
&
)H !8todo !ain9 punto de entrada a la aplicacin. :n este m8todo
H se crearE un cuadrado a partir de un v8rtice + el lado + otro
H a partir de los v8rtices ? + =, + se harEn las llamadas
H pertinentes al m8todo Prop"uadrado para mostrar sus propiedades H)
static void !ain() #
)) 'ariables para construir los puntos
uint X, +%
)) 'ariables para construir los cuadrados
"uadrado cuadrado%
Punto p?, pD%
uint lado%
)H Pidiendo datos para construir el primer cuadrado. No se
H inclu+e cdi$o para evitar errores si se introducen
H cadenas en lu$ar de n5meros porque a5n no se ha eXplicado.
H "uando se pruebe el pro$rama ha+ que tener la precaucin
H de poner siempre n5meros. H)
"onsole.,rite-ine(O"uadrado a partir del lado + un v8rticeO)%
"onsole.,rite(O:scribe componente T del v8rtice9 O)%
X=L/nt=D.Parse("onsole.6ead-ine())%
"onsole.,rite(O:scribe componente b del v8rtice9 O)%
+=L/nt=D.Parse("onsole.6ead-ine())%
"onsole.,rite(O:scribe la lon$itud del lado9 O)%
lado=L/nt=D.Parse("onsole.6ead-ine())%
p?=ne< Punto(X,+)% )) "onstru+endo el v8rtice?
cuadrado=ne< "uadrado(p?,lado)% )) "onstru+endo el cuadrado
"onsole.,rite-ine(O"onstruido el cuadrado. Pulsa /NK6^ para ver sus propiedadesO)%
strin$ a="onsole.6ead-ine()%
Prop"uadrado(cuadrado)% )) -lamada al m8todo Prop"uadrado
)H Pidiendo los datos para construir un cuadrado a partir
H de los v8rtices ? + =. H)
"onsole.,rite-ine(O"uadrado a partir de dos v8rticesO)%
"onsole.,rite(O:scribe componente T del v8rtice?9 O)%
X=L/nt=D.Parse("onsole.6ead-ine())%
S>
"onsole.,rite(O:scribe componente b del v8rtice?9 O)%
+=L/nt=D.Parse("onsole.6ead-ine())%
p?=ne< Punto(X,+)% )) "onstru+endo el v8rtice?
"onsole.,rite(O:scribe componente T del v8rtice=9 O)%
X=L/nt=D.Parse("onsole.6ead-ine())%
"onsole.,rite(O:scribe componente b del v8rtice=9 O)%
+=L/nt=D.Parse("onsole.6ead-ine())%
pD=ne< Punto(X,+)% )) "onstru+endo el v8rtice=
cuadrado=ne< "uadrado(p?,pD)% )) "onstru+endo el cuadrado
"onsole.,rite-ine(O"onstruido el cuadrado. Pulsa /NK6^ para ver sus propiedadesO)%
a="onsole.6ead-ine()%
Prop"uadrado(cuadrado)% )) -lamada al m8todo Prop"uadrado
&
&
&
Bien, esto es todo#
; Novena entrega (Control de 8luo condicional$ i8...else i8...else< s=itc>.!
6.1 Control de &lujo" estructuras condicionales
Temos visto hasta ahora que los programas van e"ecutando las l&neas de c%digo con orden# ;in embargo,
hay muchas situaciones en las que es preciso alterar ese orden, o bien puede ocurrir que sea necesario que se
e+ect=en una serie de operaciones que pueden ser distintas en otras circunstancias# 9or e"emplo, si el programa
pide una clave de acceso, deber continuar con la e"ecuci%n normal en caso de que la clave introducida por el
usuario sea correcta, y deber salir del mismo en caso contrario# 9ues bien* para todas estas cuestiones que,
por otra parte, son muy +recuentes, tenemos las estructuras de control de +lu"o#
5n C# contamos con varias de estas estructuras, as& que las iremos e)plicando con calma una a una,
empe!ando en esta entrega con las estructuras condicionales# 2e nuevo he de avisar a los programadores de
C6C--* el comportamiento de algunas de estas estructuras cambia ligeramente en C#, as& que leed esta
entrega atentamente, pues de lo contrario pod(is encontraros con varios problemas a la hora de usarlas#
6.2 +nstruccin i&...else i&...else
5mpe!ar( diciendo que, para los que no sepan ingl(s, i+ signi+ica si condicional, es decir, si te portas bien, te
compro un helado y te de"o ver la tele# 9ues bien, en programaci%n, es ms o menos lo mismo# 9ongamos un
poco de pseudoVc%digo para que los principiantes se vayan haciendo a la idea*
Gi (te portas bien)
#
te compro un helado%
te de;o ver la tele%
SJ
&
5st bastante claro, DverdadE 5n programaci%n se eval=a a verdadero o +also la condici%n, que es lo que
est dentro de los par(ntesis# ;i esta condici%n se eval=a a true 4verdadero8 se e"ecutan las l&neas del bloque, y
si se eval=a a +alse 4+also8 no se e"ecutan# Vamos a verlo, ahora s&, en C#*
i4 (num==?()
#
"onsole.,rite-ine(O:l n5mero es i$ual a ?(O)%
&
5n este pequeo e"emplo, se eval=a como verdadero o +also lo que est dentro de los par(ntesis, es decir,
numRR1@# 9or lo tanto, el operador RR retornar true siempre que num valga 1@, y +alse si vale otra cosa# 9or
cierto, ya que estamos, no con+undas el operador de comparaci%n RR con el de asignaci%n R# 2igo esto porque
en otros lengua"es 4Visual Basic, por e"emplo8 se usa el mismo operador 4R8 para ambas cosas, y es el
compilador el que determina si es de comparaci%n o de asignaci%n seg=n el conte)to# $o ocurre as& en C#* RR
es de comparaci%n siempre, y R es de asignaci%n siempre# 9or lo tanto, qu( hubiera sucedido si hubi(ramos
escrito el e"emplo as&*
i4 (num=?() ))/ncorrecto9 se estE usando = en lu$ar de ==
#
"onsole.,rite-ine(O:l n5mero es i$ual a ?(O)%
&
:os programadores de C % C-- dirn que la e)presi%n siempre se evaluar&a a true, adems de que se
asignar&a el valor 1@ a la variable num# 9ero este curso es de C#, as& que los programadores de C % C-- se han
vuelto a equivocar* en C# se producir&a un error, porque la e)presi%n no se eval=a a true o +alse, sino que tiene
que retornar true o +alse necesariamente# 5s decir, el compilador de C# no eval=a n=meros como valores
boolean# 5sto hace que sea imposible equivocarse de operador en e)presiones de este tipo#
Bien, continuemos# Como puedes apreciar, la instrucci%n i+ e"ecuta el c%digo de su bloque siempre que la
e)presi%n que se eval=a retorne true# ;in embargo, no es necesario abrir el bloque en el caso de que solamente
haya que e"ecutar una sentencia# As&, podr&amos haber escrito el e"emplo de esta otra +orma*
i4 (num==?()
"onsole.,rite-ine(O:l n5mero es i$ual a ?(O)%
1 bien*
i4 (num==?() "onsole.,rite-ine(O:l n5mero es i$ual a ?(O)%
5n cualquiera de los dos casos, hubiera +uncionado igual porque, recordemos, el compilador entiende que
todo es la misma instrucci%n mientras no encuentre un punto y coma o una llave de apertura de bloque#
'ambi(n puede ocurrir que tengamos que e"ecutar una serie de acciones si se da una condici%n y otras
acciones en caso de que esa condici%n no se d(# 9ues bien, para eso tenemos la instrucci%n else# Volviendo a
la interpretaci%n lingh&stica para +avorecer todo esto a los principiantes, ser&a como un [de lo contrario[, es decir,
si te portas bien, te compro un helado y te de"o ver la teleK de lo contrario, te castigo en tu cuarto y te quedas sin
cenar# DIuieres un poquito de pseudoVc%digo para ver estoE Venga, aqu& lo tienes*
Gi (te portas bien)
#
SL
te compro un helado%
te de;o ver la tele%
&
de lo contrario
#
te casti$o en tu cuarto%
te quedas sin cenar%
&
Alguno debe estar parti(ndose de risa 4FFFVABA P$ 9;5P21VCf2CM1GGG8# 9ero la cuesti%n es que se
entiende per+ectamente, as& que este pseudoVc%digo es### bueno### de"(moslo en [estupendo[ 4u+++, casi se me
escapa###8# Bien, veamos algo de esto, ahora s&, en C#*
i4 (num==?()
#
"onsole.,rite-ine(O:l n5mero es i$ual a ?(O)%
&
else
#
"onsole.,rite-ine(O:l n5mero no es i$ual a ?(O)%
&
5sto es muy +cil, Dno te pareceE ;i se cumple la condici%n, se e"ecuta el c%digo del bloque i+, y si no se
cumple se e"ecuta el c%digo del bloque else# 2el mismo modo, si el bloque consta de una =nica l&nea, podemos
ahorrarnos las llaves, as&*
i4 (num==?()
"onsole.,rite-ine(O:l n5mero es i$ual a ?(O)%
else
"onsole.,rite-ine(O:l n5mero no es i$ual a ?(O)%
1 bien*
i4 (num==?() "onsole.,rite-ine(O:l n5mero es i$ual a ?(O)%
else "onsole.,rite-ine(O:l n5mero no es i$ual a ?(O)%
Como veis, sucede lo mismo que cuando nos ahorramos las llaves anteriormente# Ahora bien, recordad que
si no se ponen las llaves, tanto i+ como else a+ectan =nicamente a la primera l&nea que se encuentre tras la
condici%n# Vamos a ver estos dos e"emplos, para que os quede esto bien claro*
i4 (num==?()
"onsole.,rite-ine(O:l n5mero es i$ual a ?(O)%
"onsole.,rite-ine(ONe dichoO)%
else )) /ncorrecto9 el compilador no sabe a qu8 i4 se re4iere
SO
"onsole.,rite-ine(O:l n5mero no es i$ual a ?(O)%
VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
i4 (num==?()
"onsole.,rite-ine(O:l n5mero es i$ual a ?(O)%
else
"onsole.,rite-ine(O:l n5mero no es i$ual a ?(O)%
"onsole.,rite-ine(ONe dichoO)% )) :sta l*nea se e;ecuta siempre
5n el primer caso se producir&a un error en tiempo de compilaci%n, porque el compilador no sabr&a enla!ar el
else con el i+ ya que, al no haber llaves de bloque, da por terminada la in+luencia de este despu(s de la primera
l&nea que est tras (l# 5n el segundo caso no se producir&a un error, pero la =ltima l&nea 4la que escribe [Te
dicho[ en la consola8 se e"ecutar&a siempre, independientemente de si se cumple o no la condici%n, pues no hay
llaves dentro del bloque else, por lo cual este a+ecta solamente a la l&nea que le sigue# 9ara que else a+ectara a
estas dos l&neas habr&a que haber escrito las llaves de bloque*
i4 (num==?()
"onsole.,rite-ine(O:l n5mero es i$ual a ?(O)%
else
#
"onsole.,rite-ine(O:l n5mero no es i$ual a ?(O)%
"onsole.,rite-ine(ONe dichoO)% )) :sta l*nea se e;ecuta siempre
&
Ahora s&, en caso de cumplirse la condici%n se e"ecutar&a la l&nea que hay detrs del i+, y en caso contrario se
e"ecutar&an las dos l&neas escritas dentro del bloque else#
'ambi(n podr&a suceder que hubiera que enla!ar varios i+ con varios else# Volvamos con otro e"emplo para
ver si nos entendemos* si compras el libro te regalo el separador, de lo contrario, si compras la pluma te regalo
el cargador, de lo contrario, si compras el cuaderno te regalo un llavero, y, de lo contario, no te regalo nada#
Veamos de nuevo el pseudoVc%digo de esto*
Gi (compras el libro)
#
te re$alo el separador%
&
de lo contrario si (compras la pluma)
#
te re$alo el car$ador%
&
de lo contrario si (compras el cuaderno)
#
te re$alo un llavero%
SS
&
de lo contrario
#
no te re$alo nada%
&
1 sea, queda claro lo que sucede* si se cumple alguna de las condiciones se producen una serie de
consecuencias, y si no se cumple ninguna de las tres condiciones la consecuencia es que no hay regalo alguno#
5n este caso, sin embargo, hay que tomarse el pseudoVc%digo al pie de la letra para que la relaci%n con la
programaci%n sea e)acta* Dqu( ocurre si se dan dos o las tres condicionesE 9ues en la vida real,
probablemente, te llevar&as varios regalos, pero si lo tomamos al pie de la letra, solamente te podr&as llevar el
primer regalo en el que se cumpliese la condici%n, pues las dems estn precedidas por la e)presi%n [de lo
contrario[, que es, en s& misma, otra condici%n, es decir, si se cumple una las dems ya no se contemplan
aunque tambi(n se cumplan# 5sto es e)actamente lo que ocurre en programaci%n* el compilador no sigue
anali!ando las dems condiciones en el momento en el que encuentre una que retorna true# Veamos algo de
esto en C#*
i4 (num==?()
#
"onsole.,rite-ine(O:l n5mero es i$ual a ?(O)%
&
else i4 (numJ@)
#
"onsole.,rite-ine(O:l n5mero es ma+or que @O)%
&
else i4 (numJ?@)
#
"onsole.,rite-ine(O:l n5mero es ma+or que ?@O)%
&
else
#
"onsole.,rite-ine(O:l n5mero no es ?( ni ma+or que @O)%
&
Bien, e)aminemos las di+erentes posibilidades# ;i num vale 1@, se e"ecutar el bloque del primer i+, por lo
que la salida en la consola ser [5l n=mero es igual a 1@[# ;in embargo, a pesar de que 1@ es tambi(n mayor
que cinco, no se e"ecutar el bloque del primer else i+, pues ni siquiera se llega a comprobar dado que ya se ha
cumplido una condici%n en la estructura# ;i num vale un n=mero mayor que J, menor que 1J y distinto de 1@, o
sea, ?, por e"emplo, se e"ecuta el bloque del primer else i+ saliendo [el n=mero es mayor que J[ en la consola#
Ahora bien* Dqu( sucede si el n=mero es mayor que 1JE 9ues sucede e)actamente lo mismo, ya que, si es
mayor que 1J tambi(n es mayor que J, de modo que se e"ecuta el bloque del primer else i+ y despu(s se de"an
de comprobar el resto de las condiciones# 9or lo tanto, en este e"emplo, el bloque del segundo else i+ no se
S?
e"ecutar&a en ning=n caso# 9or =ltimo, el bloque del else se e"ecutar siempre que num valga J o menos de J,
pues es el =nico caso en el que no se cumple ninguna de las condiciones anteriores#
9or otra parte, las condiciones que se eval=en en un i+ o en un else i+ no tienen por qu( ser tan sencillas#
Hecuerda que en C# estas e)presiones 4las condiciones8 han de retornar true o +alse necesariamente, por lo
que podemos usar y combinar todo aquello que pueda retornar true o +alse, como variables de tipo bool,
m(todos o propiedades que retornen un tipo bool, condiciones simples o compuestas mediante los operadores
l%gicos XX 4A$2 l%gico8 WW 41H l%gico8 y G 4$1' l%gico8, o incluso me!clar unas con otras# por e"emplo*
i4 ((3icheros.:Xiste(Archivo) YY "rear) WW :spacioDiscoJ?((( )
#
"onsole.,rite-ine(O-os datos se $uardarEn en el archivoO)%
&
else
#
"onsole.,rite-ine(O:l archivo no eXiste + no se puede crear o bien no ha+ espacioO)%
&
5n este e"emplo, [5)iste[ es un m(todo de la clase Nicheros que retorna true o +alse, [Crear[ es una variable
bool y [5spacio2isco[ es una variable de tipo uint# Como ves, en una sola condici%n estn combinados varios
elementos que pueden retornar valores boolean# ;e encierra entre par(ntesis la e)presi%n
Nicheros#5)iste4Archivo8 WW Crear porque necesitamos que se eval=e todo esto primero para despu(s comparar
con la otra e)presi%n, ya que el operador XX se e"ecuta antes que el WW# As& esta e)presi%n retornar true en
caso de que el m(todo 5)iste devuelva true, la variable crear valga true o sucedan ambas cosas#
9osteriormente se establece el resultado de la otra e)presi%n, es decir 5spacio2iscoZ1@@@, y despu(s se
comparan los dos resultados con el operador XX, obteniendo el resultado +inal, que ser true si ambos
operandos valen true, y +alse si alguno de ellos o los dos valen +alse# As&, si el archivo e)iste o bien si se quiere
crear en caso de que no e)ista se guardarn los datos si, adems, hay espacio su+iciente, y si, por el contrario,
el archivo no e)iste y no se quiere crear o bien si no hay espacio, los datos no se guardarn#
Antes de terminar con la instrucci%n i+, quiero puntuali!ar una cosa# Cuando queramos hacer una simple
asignaci%n a una variable dependiendo de un determinado valor, podemos hacerlo con i+ o bien podemos usar
el operador Iuestion 4E*8 4revisa c%mo +unciona este operador en la entrega >8, que, a mi entender, es ms
c%modo# 9or e"emplo, tenemos un m(todo en el que necesitamos saber, de entre dos n=meros, cul es el
mayor y cul el menor# 9odemos hacerlo con i+, as&*
i4 (num?JnumD)
#
ma+or=num?%
menor=numD%
&
else
#
ma+or=numD%
menor=num?
&
?@
Correcto, pero tambi(n podemos hacer la lectura del c%digo algo ms c%moda si usamos el operador
Iuestion, as&*
ma+or=(num?JnumD) Q num?9 numD%
menor=(num?JnumD) Q numD9 num?%
Bien, creo que esto ya est su+icientemente e)plicado# 5ntiendo que para los que se est(n iniciando, esto
puede resultar todav&a un poco con+uso, pero tranquilos, hay un modo in+alible para hacerse con todo esto*
prctica# Cuanto ms lo us(is ms seguros os hallar(is con esto, y como en cualquier programa el uso de i+ es
el pan nuestro de cada d&a, os har(is con ello muy pronto#
6.3 +nstruccin s7itc%
Pna instrucci%n s.itch +unciona de un modo muy similar a una construcci%n con i+###else i+### else# ;in
embargo, hay un di+erencia que es +undamental* mientras en las construcciones i+###else i+### else las condiciones
pueden ser distintas en cada uno de los i+ ### else i+, en un s.itch se eval=a siempre la misma e)presi%n,
comprobando todos los posibles resultados que esta pueda retornar# Pn s.itch equivaldr&a a comprobar las
di+erentes situaciones que se pueden dar con respecto a una misma cosa# 9or e"emplo, si te compras un coche
y tienes varias opciones de +inanciaci%n* 5n caso de usar la primera opci%n te descuento un 1@ por ciento, en
caso de usar la segunda opci%n te descuento un cinco por ciento, en caso de usar la tercera opci%n te
descuento un dos por ciento, y en cualquier otro caso no te descuento nada# Como ves, se comprueba siempre
el valor de un solo elemento, que en este caso ser&a la opci%n# 9ongamos un poco de pseudoVc%digo otra ve!*
comprobemos (opcion)
#
en caso de ?9
te descuento un ?([%
Nada mEs%
en caso de D9
te descuento un @[%
Nada mEs%
en caso de =9
te descuento un D[%
Nada mEs%
en otro caso9
no te descuento nada%
Nada mEs%
&
;&, ya s( que eso ni es pseudoVc%digo ni es [na[, pero lo que pretendo es que se entienda con +acilidad, y
creo que as& se entiende mucho me"or que siendo estrictos, as& que no seis tan criticones, hombre###
Bueno, como veis, se comprueba siempre lo que vale [opcion[K si vale 1 sucede el primer caso, si vale 2 el
segundo, si vale 3 el tercero, y si vale cualquier otra cosa sucede el =ltimo caso# Vamos a verlo en C#*
s<itch (opcion)
?1
#
case ?9
descuento=?(%
brea0%
case D9
descuento=@%
brea0%
case =9
descuento=D%
brea0%
de4ault9
descuento=(%
brea0%
&
Tay algunas cosas importantes en las que quiero que te +i"es especialmente* solamente se establece un
bloque para la instrucci%n [s.itch[, pero ninguno de los [case[ abre ning=n bloque 4tampoco lo hace [de+ault[8#
Pna ve! que se terminan las instrucciones para cada caso hay que poner [breaU[ para que el compilador salga
del s.itch 4esto lo digo especialmente para los programadores de Visual Basic, ya que en VB no hab&a que
poner nada al +inal de cada Case en un ;elect Case8# DB qu( ocurre si no se cierra el [case[ con un [breaU[E
Bien, pueden ocurrir dos cosas 4los programadores de C6C--, por +avor, que no se precipiten###8# Veamos* si
queremos que el programa haga las mismas cosas en distintos casos, habr que poner todos estos casos y no
cerrarlos con breaU# 9or e"emplo, si el descuento es J tanto para la segunda como para la tercera opci%n, habr&a
que hacerlo as&*
s<itch (opcion)
#
case ?9
descuento=?(%
brea0%
case D9
case =9
descuento=@%
brea0%
de4ault9
descuento=(%
brea0%
&
?2
As&, en caso de que opcion valiera 2 % tres, el descuento ser&a del JA, pues, si opcion vale 2, el +lu"o del
programa entrar&a por case 2 y continuar&a por case 3 e"ecutando el c%digo de este =ltimo al no haber cerrado el
case 2 con breaU, y si opci%n vale 3 entrar&a por case 3 e"ecutando, por lo tanto, el mismo c%digo# ;in embargo,
y aqu& quiero que se +i"en especialmente los programadores de C6C--, no hubiera sido vlido establecer
acciones para el case 2 sin cerrarlo con breaU# Vamos a ponerlo primero, y luego lo e)plico ms detenidamente*
s<itch (opcion)
#
case ?9
descuento=?(%
brea0%
case D9
re$alo=O"ar$ador de "DO
case =9
descuento=@%
brea0%
de4ault9
descuento=(%
brea0%
&
Voy a e)aminar esto como si estuviera escrito en C6C--* si opcion vale 2, el +lu"o entrar&a por case 2,
estableciendo el regalo y seguir&a por case 3 estableciendo tambi(n el descuento, dado que case 2 no ha sido
cerrado con un breaU# ;i opci%n vale 3 entrar&a solamente por case 3, estableciendo =nicamente el descuento y
no el regalo# Ciertamente, esto era muy c%modo si quer&as de+inir acciones espec&+icas para un valor
determinado y aadirles otras que +ueran comunes para varios valores# ;in embargo, esto no se puede hacer
en C#, dado que el compilador avisar&a de un error, diciendo que hay que cerrar case 2# D9or qu(E 9ues bien, a
pesar de la comodidad de esta construcci%n en C6C-- para determinadas circunstancias, lo cierto es que lo
ms com=n es que se omita el breaU por error que por intenci%n, lo cual provoca muchos +allos que ser&an muy
di+&ciles de detectar# 9or este motivo, los diseadores del lengua"e C# decidieron que el riesgo no merec&a la
pena, ya que esta +uncionalidad se puede conseguir +cilmente con un simple i+, as&*
s<itch (opcion)
#
case ?9
descuento=?(%
brea0%
case D9
case =9
i4 (opcion==D) re$alo=O"ar$ador de "DO%
descuento=@%
brea0%
?3
de4ault9
descuento=(%
brea0%
&
5s cierto que esto es un poco ms inc%modo, pero tambi(n es cierto que es mucho ms seguro, ya que se
evitan los problemas que sobreven&an en C6C-- cuando te olvidabas de poner un breaU#
9ara terminar, la e)presi%n que se ha de comprobar en un s.itch ha de ser, necesarimente, compatible con
los tipos sbyte, byte, short, ushort, int, uint, long, ulong, char o string 4s&, s&, para los programadores de ,ava,
string tambi(n8# B recordad* al decir compatible quiero decir que sea de uno de esos tipos o que se pueda
convertir a uno de ellos#
9ara esta entrega tienes un e"emplo del s.itch 4sigue este v&nculo para ba"rtelo8, pero no he diseado
ninguno sobre i+, ya que lo vamos a usar constantemente a partir de ahora# Adems, no quiero daros todo tan
hecho, porque corremos el riesgo de que aprendis mucha teor&a pero luego no seis capaces de llevarla a la
prctica# Ciertamente, hab&a pensado en modi+icar la clase Cuadrado 4s&, la del e"ercicio que propuse en la
entrega anterior8# Hecuerda que uno de los constructores de esta clase, concretamente el que constru&a un
cuadrado a partir de los v(rtices uno y tres, era muy inseguro, puesto que si dbamos componentes ) menores
para el v(rtice 3 que para el v(rtice 1, luego no nos coincid&a la longitud del lado, adems de que los v(rtices se
colocaban al rev(s# ;in embargo he decidido complicaros un poco la vida, y poner esto como un e"ercicio en
ve! de un e"emplo# ;&, s&, lo que lees, un e"ercicio# As& matamos dos p"aros de un tiro* te devanas los sesos
para hacerlo y, si no te sale, lo puedes ver hecho cuando llegue la pr%)ima entrega# Aqu& va el enunciado*
6.4 #jercicio 2
Veamos, uno de los constructores de la clase Cuadrado que hicimos en el e"ercicio 1 era muy inseguro#
Vamos a verlo gr+icamente, para que todos nos hagamos una idea e)acta de lo que estoy diciendo# :os
v(rtices del cuadrado han de corresponderse e)actamente igual a como estn en esta imagen*
?>
Ahora bien, ten&amos un problema si, por e"emplo, pasbamos L, L a las coordenadas para el v(rtice1 y 1,1
a las coordenadas para el v(rtice3, y es que no se calculaba bien la longitud del lado, adems de que los
v(rtices se nos cambiaban de sitio 4se puede comprobar en la e"ecuci%n del e"emplo8#
9ues bien, lo que hay que arreglar es esto, precisamente# 5l constructor debe aceptar dos argumentos del
tipo 9unto sin presuponer que alguno de ellos corresponde a alg=n v(rtice determinado, y despu(s hacer las
comprobaciones necesarias para calcular el v(rtice1 y la longitud del lado correctos# 1"o, que es un poco ms
complicado de lo que puede parecer a simple vista# Tala, al l&o### 9or cierto, no hay pistas, lo siento###
1? D6cima entrega (Control de 8luo iterativo$ bucles (8or( =>ile( do!(
instrucciones de salto & recursividad.!
18.1 Control de &lujo" estructuras iterati,as
9asamos ahora a un nuevo con"unto de instrucciones de mucha utilidad# 5n realidad, casi todos los
lengua"es cuentan con instrucciones parecidas 4si no iguales8 o que +uncionan de un modo muy similar a las que
vamos a ver aqu&# :as estructuras iterativas de control de +lu"o se ocupan de repetir una serie de l&neas de
c%digo tantas veces como el programador indique o bien hasta que se de una cierta condici%n# A estas
estructuras tambi(n se les llama bucles#
Aquellos de vosotros que cono!cis otros lengua"es ver(is que todos estos bucles se parecen mucho a los
que ya conoc(is# :os que os est(is iniciando ahora en la programaci%n puede que tard(is un poco en hallar la
utilidad de todo esto* Dpara qu( vamos a hacer que el programa repita varias veces el mismo c%digoE Bueno,
de momento os dir( que en todo programa, al igual que los bloques i+ y los bloques s.itch, los bucles son
tambi(n el pan nuestro de cada d&a, as& que no tardar(is en acostumbraros a ellos#
?J
18.2 'ucles &or
:os bucles +or van asignando valores a una variable desde un valor inicial hasta un valor +inal, y cuando la
variable contiene un valor que est +uera del intervalo el bucle termina# Veamos la sinta)is para hacernos me"or
a la idea*
4or (var=inicial%condicin%si$uientevalor)
#
/nstrucciones
&
;( que esto es algo di+&cil de leer, incluso para aquellos que hayan programado en otros lengua"es, puesto
que los bucles +or de C no se parecen mucho, en cuanto a su sinta)is, al resto de los bucles +or de los otros
lengua"es, as& que tratar( de e)plicarlo con detenimiento# Como veis, tras la sentencia +or se indican las
especi+icaciones del bucle entre par(ntesis# 2ichas especi+icaciones estn divididas en tres partes separadas
por punto y coma* la parte de asignaci%n del valor inicial en primer lugarK la parte que veri+ica la continuidad del
bucle 4mediante una condici%n8 en segundo lugarK y la parte en que se calcula el siguiente valor en tercer lugar#
9ongamos un e"emplo* vamos a calcular el +actorial de un n=mero dado, que se encuentra almacenado en la
variable num# ;e podr&a hacer de dos +ormas*
4or (b+te i=num% iJ? % i77)
#
4actH=i%
&

^ bien9

4or (b+te i=?% iV=num % i22)
#
4actH=i%
&
Claro, para que esto +uncione, la variable +act ha de valer 1 antes de que el programa comience a e"ecutar el
bucle# Bien, veamos ahora c%mo se van e"ecutando estas instrucciones paso a paso*

1@ paso$

8or (b&te iAnum< iB1 < iCC!
D
8actEAi<
F
"@ paso$

8or (b&te iAnum< iB1 < iCC!
D
8actEAi<
F
'@ paso$

8or (b&te iAnum< iB1 < iCC!
D
8actEAi<
F
>i paso* Ji paso* Li paso*
?L

+or 4byte iRnumK iZ1 K iVV8
]
+act\RiK
^

+or 4byte iRnumK iZ1 K iVV8
]
+act\RiK
^

+or 4byte iRnumK iZ1 K iVV8
]
+act\RiK
^
5n primer lugar se asigna a la variable i el valor de num 4vamos a suponer que num vale 38, es decir,
despu(s del primer paso, el valor de i es 3# 9osteriormente se comprueba si dicha variable es mayor que 1, es
decir, si 3Z1# Como la condici%n del segundo paso se cumple se e"ecuta el c%digo del bucle en el tercer paso,
+act\Ri, con lo que +act 4que val&a 18 ahora vale 3 41\38# 5n el cuarto paso se asigna el siguiente valor a i 4iVV8,
con lo que, ahora, i valdr 2# 5n el quinto se vuelve a comprobar si i es mayor que 1, y como esto se cumple, el
se)to paso vuelve a e"ecutar el c%digo del bucle 4de nuevo, +act\Ri8, con lo que ahora +act vale L 43\28# 5l
s(ptimo paso es id(ntico al cuarto, es decir, se asigna el siguiente valor a la variable i 4de nuevo, iVV8, con lo que
ahora i valdr&a 1# 5l octavo paso es id(ntico al quinto, comprobando por lo tanto si i es mayor que 1# ;in
embargo esta ve!, la condici%n no se cumple 41 no es mayor que 1, sino igual8, por lo que la e"ecuci%n saldr&a
del bucle y e"ecutar&a la siguiente l&nea del programa que est( +uera de (l# 2ate cuenta de que el bucle se
seguir e"ecutando siempre que la condici%n 4 iZ1 8 se cumpla, y de"ar de e"ecutarse cuando la condici%n no se
cumpla# 9or lo tanto, no habr&a sido vlido poner iRR2 en lugar de iZ1, ya que esta condici%n se cumplir&a
=nicamente cuando num valiera 2, pero no en cualquier otro caso# D;er&as capa! de ver c%mo +uncionar&a el
otro bucleE Venga, int(ntalo#
18.3 'ucles &or anidados
5+ectivamente, se pueden colocar bucles +or dentro de otros bucles +or, con lo que obtendr&amos lo que se
llaman los bucles +or anidados# ;on tambi(n muy =tiles* por e"emplo, piensa que tienes almacenadas unas
cuantas +acturas en una base de datos, y quieres leerlas todas para presentarlas en pantalla# 5l problema est
en que cada +actura tiene una o varias l&neas de detalle# DC%mo podr&amos hacer para cargar cada +actura con
todas sus l&neas de detalleE 9ues usando bucles anidados# Colocar&amos un bucle +or para cargar las +acturas,
y otro bucle +or dentro de (l para que se cargaran las l&neas de detalle de cada +actura# As&, el segundo bucle se
e"ecutar completo en cada iteraci%n del primer bucle# Veamos un e"emplo que nos aclare todo esto un poco
ms*
usin$ G+stem%
namespace uclesAnidados
#
class uclesAnidadosApp
#
static void !ain()
#
4or (int i=?% iV==% i22)
#
"onsole.,rite-ine(O3actura n5mero #(&O, i)%
"onsole.,rite-ine(ODetalles de la 4acturaO)%

?O
4or (int ;=?% ;V==% ;22)
#
"onsole.,rite-ine(O -*nea de detalle #(&O, ;)%
&

"onsole.,rite-ine()%
&

strin$ a="onsole.6ead-ine()%
&
&
&
Como ves, el bucle ["[ est dentro del bucle [i[, de modo que se e"ecutar completo tantas veces como se
itere el bucle i# 9or este motivo, la salida en consola ser&a la siguiente*
3actura n5mero ?
Detalles de la 4actura
-*nea de detalle ?
-*nea de detalle D
-*nea de detalle =
3actura n5mero D
Detalles de la 4actura
-*nea de detalle ?
-*nea de detalle D
-*nea de detalle =
3actura n5mero =
Detalles de la 4actura
-*nea de detalle ?
-*nea de detalle D
-*nea de detalle =
D;igues sin verlo claroE Bueno, veamos c%mo se van e"ecutando estos bucles*

1@ paso$

8or (int iA1< iGA'< iHH!
D
"@ paso$

8or (int iA1< iGA'< iHH!
D
'@ paso$

8or (int iA1< iGA'< iHH!
D
?S
8or (int A1< GA'< HH!
D
...
F
F
8or (int A1< GA'< HH!
D
...
F
F
8or (int A1< GA'< HH!
D
...
F
F
>i paso*

+or 4int iR1K iYR3K i--8
]
+or 4int "R1K "YR3K "--8
]
###
^
^
Ji paso*

+or 4int iR1K iYR3K i--8
]
+or 4int "R1K "YR3K "--8
]
###
^
^
Li paso*

+or 4int iR1K iYR3K i--8
]
+or 4int "R1K "YR3K "--8
]
###
^
^
Oi paso*

+or 4int iR1K iYR3K i--8
]
+or 4int "R1K "YR3K "--8
]
###
^
^
Si paso*

+or 4int iR1K iYR3K i--8
]
+or 4int "R1K "YR3K "--8
]
###
^
^
?i paso*

+or 4int iR1K iYR3K i--8
]
+or 4int "R1K "YR3K "--8
]
###
^
^
1@i paso*

+or 4int iR1K iYR3K i--8
]
+or 4int "R1K "YR3K "--8
]
###
^
^
11i paso*

+or 4int iR1K iYR3K i--8
]
+or 4int "R1K "YR3K "--8
]
###
^
^
12i paso*

+or 4int iR1K iYR3K i--8
]
+or 4int "R1K "YR3K "--8
]
###
^
^
13i paso*

1>i paso*

1Ji paso*

??
+or 4int iR1K iYR3K i--8
]
+or 4int "R1K "YR3K "--8
]
###
^
^
+or 4int iR1K iYR3K i--8
]
+or 4int "R1K "YR3K "--8
]
###
^
^
+or 4int iR1K iYR3K i--8
]
+or 4int "R1K "YR3K "--8
]
###
^
^
1Li paso*

+or 4int iR1K iYR3K i--8
]
+or 4int "R1K "YR3K "--8
]
###
^
^
1Oi paso*

+or 4int iR1K iYR3K i--8
]
+or 4int "R1K "YR3K "--8
]
###
^
^
1Si paso*

+or 4int iR1K iYR3K i--8
]
+or 4int "R1K "YR3K "--8
]
###
^
^
5l decimonoveno paso ser&a igual que el se)to, el vig(simo igual que el s(ptimo, y as& hasta terminar el
bucle i# Bueno, donde estn los puntos suspensivos estar&a el c%digo que +orma parte del bucle "# Como ves, el
segundo bucle 4el bucle "8 se e"ecuta completo para cada valor que toma la variable i del primero de los bucles#
Vete haciendo el clculo mental de cunto van valiendo las variables para que lo veas claro# 9or supuesto, se
pueden anidar tantos bucles como sea necesario#
1tra +orma de anidar los bucles es utili!ando solamente una =nica sentencia +or, aunque no es un modo muy
recomendable de hacerlo puesto que resulta mucho ms di+&cil de leer# 5l siguiente c%digo*
4or (int i=?, int ;=?% iV==, ;V==% i22, ;22)
#
...
&
;er&a el equivalente a esto otro*
4or (int i=?% iV==% i22)
#
4or (int ;=?% ;V==% ;22)
#
...
&
&
1@@
18.4 'ucles 7%ile
Bien, para los que no sepan ingl(s, [.hile[ signi+ica [mientras[, de modo que ya os pod(is hacer la idea* un
bucle .hile se repetir mientras una condici%n determinada se cumpla, o sea, devuelva true# Veamos su
sinta)is*
<hile (eXpresin bool)
#
/nstrucciones
&
5+ectivamente, las [Cnstrucciones[ que se hallen dentro del bucle .hile se e"ecutarn continuamente
mientras la e)presi%n de tipo boolean retorne true# 9or e"emplo, podemos escribir un bucle .hile para pedir una
contrasea de usuario# Algo as&*
usin$ G+stem%
namespace ucles,hile
#
class ucles,hileApp
#
static void !ain()
#
strin$ "lave=O"ompadre, cmprame un cocoO%
strin$ 6es=OO%

<hile (6esS="lave)
#
"onsole.,rite(ODame la clave9 O)%
6es="onsole.6ead-ine()%
&

"onsole.,rite-ine(O-a clave es correctaO)%

strin$ a="onsole.6ead-ine()%
&
&
&
5n este pequeo e"emplo el programa pedir una y otra ve! la clave al usuario, y cuando este teclee la clave
correcta ser cuando +inalice la e"ecuci%n del mismo# As&, la salida en la consola de este programa ser&a algo
como esto 4en ro"o est lo que se ha tecleado durante su e"ecuci%n8*
1@1
Dame la clave9 No quiero
Dame la clave9 Iue no
Dame la clave9 eres contumaz, PehQ
Dame la clave9 'aaaaale
Dame la clave9 "ompadre, cmprame un coco
-a clave es correcta
DAlguna preguntaE DIue qu( habr&a pasado si la condici%n no se hubiera cumplido antes de e"ecutar el
bucle, es decir, si Hes ya contuviera lo mismo que Clave antes de llegar al .hileE Bien, pues, en ese caso, el
bucle no se hubiera e"ecutado ninguna ve!, es decir, al comprobar que la e)presi%n de tipo boolean retorna
+alse, la e"ecuci%n del programa pasa a la primera l&nea que se encuentra a continuaci%n del bucle# Vamos a
verlo para que te quede ms claro# 0odi+icaremos ligeramente el e"emplo anterior, as&*
usin$ G+stem%
namespace ucles,hile
#
class ucles,hileApp
#
static void !ain()
#
strin$ "lave=O"ompadre, cmprame un cocoO%
strin$ 6es="lave%
<hile (6esS="lave)
#
"onsole.,rite(ODame la clave9 O)%
6es="onsole.6ead-ine()%
&
"onsole.,rite-ine(O-a clave es correctaO)%
strin$ a="onsole.6ead-ine()%
&
&
&
5n e+ecto, en este caso, la salida en consola ser&a la siguiente*
-a clave es correcta
Ba que la e"ecuci%n no pasa por el bucle# Bueno, ya veis que es muy sencillo# 9or cierto, luego os propondr(
algunos e"ercicios para que practiqu(is un poco todo esto de los bucles 4a ver si pensabais que os ibais a
escaquear8#
1@2
18.- 'ucles do
Ciertamente, estos bucles tienen mucho que ver con los bucles .hile# :a di+erencia es que estos se e"ecutan
siempre al menos una ve!, mientras que los bucles .hile, como acabamos de ver antes, pueden no e"ecutarse
ninguna ve!# Veamos la sinta)is de los bucles [do[*
do
#
/nstrucciones
& <hile (eXpresin bool)%
Como ves, tambi(n hay un .hile y una e)presi%n boolean, pero en este caso se encuentra al +inal# 2e este
modo, la e"ecuci%n pasar siempre por las instrucciones del bucle una ve! antes de evaluar dicha e)presi%n#
Vamos a rehacer el e"emplo anterior cambiando el bucle .hile por un bucle do*
usin$ G+stem%
namespace uclesDo
#
class uclesDoApp
#
static void !ain()
#
strin$ "lave=O"ompadre, cmprame un cocoO%
strin$ 6es=OO%
do
#
"onsole.,rite(ODame la clave9 O)%
6es="onsole.6ead-ine()%
& <hile (6esS="lave)%
"onsole.,rite-ine(O-a clave es correctaO)%
strin$ a="onsole.6ead-ine()%
&
&
&
5l resultado ser&a el mismo que antes# :a di+erencia est en que aqu& dar&a e)actamente lo mismo lo que
valiera la variable Hes antes de llegar al bucle, puesto que este se va a e"ecutar antes de comprobar dicho
valor, y al e"ecutarse, el valor de Hes se sustituye por lo que se introdu!ca en la consola# 9or lo tanto, repito, los
bucles do se e"ecutan siempre al menos una ve!#
9or otro lado tenemos otro tipo de bucle, los bucles +oreach, pero no hablaremos de ellos hasta que
hayamos visto arrays e indi!adores# 'ened un poco de paciencia, que todo se andar#
1@3
18.. +nstrucciones de salto
$o es que vaya a salirnos un tirinene en la pantalla dando brincos como un poseso, no# :as instrucciones de
salto permiten modi+icar tambi(n el +lu"o del programa, +or!ando la siguiente iteraci%n de un bucle antes de
tiempo, o la salida del mismo o bien mandando la e"ecuci%n directamente a un punto determinado del programa
4esto =ltimo est altamente perseguido y penado por la ley, o sea, los "e+es de proyecto8# ;on pocas y muy
sencillas, as& que pod(is estar tranquilos, que no os voy a soltar otra biblia con esto###
18.1 a instruccin brea9
Algo hemos visto ya sobre la instrucci%n breaU# DC%mo que noE Anda, repsate la entrega anterior,
hombre### 0ira que se te ha olvidado pronto### 5n +in### a lo que vamos# :a instrucci%n breaU +uer!a la salida de
un bucle antes de tiempo o bien de una estructura de control de +lu"o condicional en la que se encuentre 4un
s.itch8# Ahora nos +i"aremos en los bucles, que es donde andamos# 9ondremos un e"emplo sencillo* 5l siguiente
programa escribir m=ltiplos de J hasta llegar a 1@@*
usin$ G+stem%
namespace /nstruccionrea0
#
class /nstruccionrea0App
#
static void !ain()
#
int num=(%
<hile (true)
#
"onsole.,rite-ine(num)%
num2=@%
i4 (numJ?(() brea0%
&
strin$ a="onsole.6ead-ine()%
&
&
&
DIu( es eso de .hile 4true8E 9ues un bucle in+inito# D$o dec&amos que dentro de los par(ntesis hab&a que
colocar una e)presi%n booleanE 9ues entonces### true es una e)presi%n boolean# 2e este modo, el bucle es
in+inito 4claro, true siempre es true8# ;in embargo, cuando la variable num tiene un valor mayor que 1@@ la
e"ecuci%n del bucle terminar, pues se e"ecuta una instrucci%n breaU#
18.2 a instruccin continue
:a instrucci%n continue +uer!a la siguiente iteraci%n del bucle donde se encuentre 4que puede ser un bucle
+or, .hile, do o +oreach8# Como esto se ve muy bien con un e"emplo, vamos con ello* 5l siguiente programa
mostrar todos los n=meros del uno al veinte a e)cepci%n de los m=ltiplos de tres*
1@>
usin$ G+stem%
namespace /nstruccion"ontinue
#
class /nstruccion"ontinueApp
#
static void !ain()
#
4or (int i=?% iV=D(% i22)
#
i4 (i [ = == () continue%
"onsole.,rite-ine(i)%
&
strin$ a="onsole.6ead-ine()%
&
&
&
5n este e"emplo, el bucle +or va asignando valores a la variable i entre 1 y 2@# ;in embargo, cuando el valor
de i es tres o m=ltiplo de tres 4es decir, cuando el resto de la divisi%n entre i y 3 es cero8 se e"ecuta una
instrucci%n continue, de modo que se +uer!a una nueva iteraci%n del bucle sin que se haya escrito el valor de i
en la consola# 9or este motivo, aparecer&an todos los n=meros del uno al veinte a e)cepci%n de los m=ltiplos de
tres#
18.6 :er mardito goto:
;&, C# mantiene vivo al [maldito goto[# ;i te digo la verdad, el goto, aparte de ser el principal baluarte de la
[programaci%n desVestructurada[, es un maestro de la supervivencia### de lo contrario no se e)plicar&a que
siguiera vivo# 5n +in### 'ratar( de e)plicaros c%mo +unciona sin de"arme llevar por mis sentimientos### 2e
momento te dir( que goto hace que la e"ecuci%n del programa salte hacia el punto que se le indique# ;imple y
llanamente# :uego te pongo e"emplos, pero antes quiero contarte alguna cosilla sobre esta pol(mica instrucci%n#
;eg=n tengo entendido, la discusi%n sobre mantener o no el goto dentro del lengua"e C# +ue bastante
importante# 9uede que alguno se est( preguntando por qu(# Veamos* la primera pol(mica sobre el goto surgi%
cuando se empe!aba a hablar de la programaci%n estructurada, all por +inales de los L@ 4hay que ver, yo a=n
no hab&a nacido8# ;i alguno ha le&do alg=n programa escrito por un [a+icionado[ al goto sabr per+ectamente a
qu( me re+iero* esos programas son como la ca"a de pandora, puesto que no sabes nunca qu( puede pasar
cuando hagas un cambio aparentemente insigni+icante, ya que no tienes modo se saber a qu( otras partes del
programa a+ectar ese cambio#
5n realidad, el problema no es la instrucci%n goto en s& misma, sino el uso inadecuado que algunos
programadores le dan 4pocos, gracias a 2ios8# Ciertamente, hay ocasiones en las que una instrucci%n goto hace
la lectura de un programa mucho ms +cil y natural# DHecordis de la entrega anterior el e"emplo en el que
hab&a un s.itch en el que nos interesaba que se e"ecutaran el caso 2 y el caso 3E :o hab&amos resuelto con un
i+, de este modo*
s<itch (opcion)
1@J
#
case ?9
descuento=?(%
brea0%
case D9
case =9
i4 (opcion==D) re$alo=O"ar$ador de "DO%
descuento=@%
brea0%
de4ault9
descuento=(%
brea0%
&
5n este e"emplo, si opci%n val&a 2 se asignaba una cadena a la variable regalo y, adems se asignaba J a la
variable descuento# 9ues bien, en este caso un goto habr&a resultado mucho ms natural, intuitivo y +cil de leer#
Vemoslo*
s<itch (opcion)
#
case ?9
descuento=?(%
brea0%
case D9
re$alo=O"ar$ador de "DO%
$oto case =%
case =9
descuento=@%
brea0%
de4ault9
descuento=(%
brea0%
&
Como veis, hemos resuelto el problema anterior de un modo mucho ms natural que antes, sin tener que
usar una sentencia i+# Veamos ahora un e"emplo de c%mo $1 se debe usar un goto*
i4 (opcion==?) $oto Lno%
i4 (opcion==D) $oto Dos%
1@L
i4 (opcion===) $oto Kres%
$oto ^tro%
Lno9
#
descuento=?(%
$oto 3in%
&
Dos9
#
re$alo=O"ar$ador de "DO%
&
Kres9
#
descuento=@%
$oto 3in%
&
^tro9
descuento=(%
3in9
"onsole.,rite-ine(O:l descuento es #(& + el re$alo #?&O,
descuento, re$alo)%
5ste +ragmento de c%digo hace lo mismo que el anterior, pero, indudablemente, est much&simo ms
enredado, es mucho ms di+&cil de leer, y hemos mandado a paseo a todos los principios de la programaci%n
estructurada# Como ves, un mal uso del goto puede hacer que un programa sencillo en principio se convierta en
un aut(ntico desbara"uste# 5n resumen, no hagis esto nunca#
;i quer(is mi opini%n, yo soy partidario de usar el goto s%lo en casos muy concretos en los que
verdaderamente haga la lectura del c%digo ms +cil 4como en el e"emplo del s.itch8, aunque, si te digo la
verdad, no me hubiera molestado nada en absoluto si el goto hubiera sido suprimido por +in# 2e todos modos, si
no tienes muy claro cundo es bueno usarlo y cundo no, lo me"or es no usarlo nunca, sobre todo si vives de
esto y quieres seguir haci(ndolo# $adie se lleva las manos a la cabe!a si se da un pequeo rodeo para evitar el
goto, pero mucha gente se pone e)tremadamente nerviosa nada ms ver uno, aunque est( bien puesto#
18.18 5ecursi,idad
Bueno, en realidad esto no tiene mucho que ver con las estructuras de control de +lu"o, pero he decidido
ponerlo aqu& porque en algunos casos un m(todo recursivo puede reempla!ar a un bucle# Adems no sabr&a
c%mo hacer para colocarlo en otra entrega### y no quer&a de"arlo sin e)plicar, aunque sea un poco por encima y
a pesar de que esta entrega se alargue un poco ms de lo normal#
Bien, vamos al ta"o* los m(todos recursivos son m(todos que se llaman a s& mismos# ;( que puede dar la
impresi%n de que, siendo as&, la e"ecuci%n no terminar&a nunca, pero sin embargo esto no es cierto# :os
1@O
m(todos recursivos han de +inali!ar la tra!a en alg=n punto# Vemoslo con un e"emplo# DHecordis c%mo
hab&amos calculado el +actorial mediante un bucleE 9ues ahora vamos a hacerlo con un m(todo recursivo#
N&"ate bien*
static double 3act(b+te num)
#
i4 (num==() return ?%

return numH3act((b+te) (num7?))% )) Aqu* 3act se llama a s* mismo
&
;&, lo s(, recono!co que es algo con+uso, sobre todo para aquellos que est(is empe!ando# 9ero tranquilos,
que tratar( de e)plicaros esto con detenimiento# 9rimero e)plicar( los motivos por los que uso un tipo double
como valor de retorno y un tipo byte para el argumento# Veamos, uso el tipo double porque es el que admite
valores ms grandes, s&, ms que el tipo 2ecimal, ya que se almacena en memoria de un modo di+erente# 9or
otro lado, uso el tipo byte para el argumento sencillamente porque no tendr&a sentido usar un tipo que acepte
n=meros mayores, ya que pasando de 1O@ el valor del +actorial no cabe ni si quiera en el tipo double# Pna ve!
aclarado esto, veamos c%mo +unciona# 9rimero os dibu"o la tra!a, tal y como +unciona si se quiere calcular el
+actorial de 3 4o sea, num vale 38*
9ara asegurarme de que comprendes esto bien, observa el c%digo y el gr+ico seg=n vas siguiendo la
e)plicaci%n# Cuando en el programa hacemos una llamada al m(todo 4Nact438 en el gr+ico8 este,
evidentemente, comien!a su e"ecuci%n# 9rimero comprueba si el argumento que se le ha pasado es igual a cero
4revisa el c%digo8# Como en este caso el argumento vale 3, el m(todo retornar lo que valga el producto de 3
por el +actorial de 3V1, o sea, 3 por el +actorial de 2# Claro, para poder retornar esto debe calcular previamente
cunto vale el +actorial de 2, por lo se produce la segunda llamada al m(todo Nact# 5n esta segunda llamada,
sucede algo parecido* el argumento vale 2, y como no es igual a cero el m(todo procede a retornar 2 por el
+actorial de 1 42 V 18, pero, obviamente, vuelve a suceder igual# 9ara poder retornar esto ha de calcular
previamente cunto vale el +actorial de 1, por lo que se produce la tercera llamada al m(todo Nact, volviendo a
1@S
darse de nuevo la misma situaci%n* como 1 no es igual a cero, procede a retornar el producto de 1 por el
+actorial de cero, y de nuevo tiene que calcular cunto vale el +actorial de cero, por lo que se produce una nueva
llamada al m(todo Nact# ;in embargo esta ve! s& se cumple la condici%n, es decir, cero es igual a cero, por lo
que esta ve! el m(todo Nact retorna 1 al m(todo que lo llam%, que era el que ten&a que calcular previamente
cunto val&a el +actorial de @ y multiplicarlo por 1# As&, la +unci%n que ten&a que calcular 1\Nact4@8 ya sabe que la
=ltima parte, es decir, Nact4@8, vale 1, por lo que hace el producto y retorna el resultado al m(todo que lo llam%,
que era el que ten&a que calcular cunto val&a 2 \ Nact418# Como este ya tiene el resultado de Nact418 4que es,
recuerda 1\18, e"ecuta el producto, retornando 2 al m(todo que lo llam%, que era el que ten&a que calcular
cunto val&a 3\Nact428# Como ahora este m(todo ya sabe que Nact428 vale 2, e"ecuta el producto y retorna el
resultado, que es L, +inali!ando la tra!a# ;i te das cuenta, un m(todo recursivo va llamndose a s& mismo hasta
que se cumple la condici%n que hace que termine de llamarse, y empie!a a retornar valores en el orden inverso
a como se +ueron haciendo las llamadas#
Bueno, creo que ya est todo dicho por hoy, as& que llega el momento de los e"ercicios# ;u"(tate +uerte a la
silla, porque esta ve! te voy a poner en unos cuantos aprietos#
18.11 #ercicio 3
Antes de nada, no te asustes que es muy +cil# ;i no sabes qu( es alguna cosa, en las pistas te doy las
de+iniciones de todo# 5n este e"ercicio te voy a pedir que escribas seis m(todos, los cuales te detallo a
continuaci%n*
5l m(todo rNact* debe ser recursivo y retornar el +actorial de un n=mero# Ahora bien, no me vale que copies
el que est escrito en esta entrega# A ver si eres capa! de hacerlo con una sola l&nea de c%digo en lugar de dos#
5l m(todo itNact* debe retornar tambi(n el +actorial de un n=mero, pero esta ve! tiene que ser iterativo 4o
sea, no recursivo8#
5l m(todo r0C2* debe ser recursivo y retornar el m)imo com=n divisor de dos n=meros# 5n las pistas te
escribo el algoritmo para poder hacerlo#
5l m(todo it0C2* tambi(n debe retornar el m)imo com=n divisor de dos n=meros, pero esta ve! debe ser
iterativo 4o sea, no recursivo8#
5l m(todo 0C0* debe ser iterativo y retornar el m&nimo com=n m=ltiplo de dos n=meros#
5l m(todo 5s9er+ecto* debe ser iterativo y retornar true si un n=mero dado es per+ecto y +alse si el n=mero
no es per+ecto#
1bviamente, todos ellos han de ser static, para que se puedan llamar sin necesidad de instanciar ning=n
ob"eto# 5scribe tambi(n un m(todo 0ain que pruebe si todos ellos +uncionan# 9or cierto, trata de hacerlos de
modo que sean lo ms e+icientes posible, esto es, que hagan el menor n=mero de operaciones posible# Tala, al
ta"o###
18.11.1 Pistas para el ejercicio 3 3#ntrega 184
5n primer lugar las de+iniciones, para aquellos a los que haya pillado desprevenidos*
5l +actorial de un n=mero es el resultado de multiplicar sucesivamente este n=mero por todos aquellos que
sean in+eriores, hasta la unidad# 9or e"emplo, el +actorial de > 4que se escrbie >G8 es >\3\2\1#
5l m)imo com=n divisor de dos n=meros es el n=mero ms grande entre el que se pueden dividir ambos
obteniendo resto cero# 9or e"emplo, el m)imo com=n divisor de 12 y 1S es L, puesto que 1S6L es 3 con resto
cero y 126L es 2 con resto cero, y ning=n n=mero mayor que seis es divisible entre los dos# :%gicamente, el
m)imo com=n divisor puede coincidir con el menor de los n=meros# 9or e"emplo, el m)imo com=n divisor
entre L y 12 es L, puesto que L6L es 1 con resto cero y 126L es 2 con resto cero#
1@?
5l m&nimo com=n m=ltiplo entre dos n=meros es el ms pequeo que se puede dividir entre los dos
obteniendo resto cero# 9or e"emplo, el m&nimo com=n m=ltiplo entre ? y L es 1S, porque 1S6? es 2 con resto cero
y 1S6L es 3 con resto cero, y no hay ning=n n=mero menor que 1S que tambi(n sea divisible entre los dos#
:%gicamente, el m&nimo com=n m=ltiplo puede ser igual al mayor de ellos# 9or e"emplo, el m&nimo com=n
m=ltiplo entre L y 12 es 12#
Pn n=mero es per+ecto cuando la suma de todos sus divisores a e)cepci%n de (l mismo es igual a (l# 9or
e"emplo, el L es per+ecto, porque la suma de todos sus divisores e)cepto (l mismo es tambi(n L 41-2-38#
Bueno, y ahora con las pistas propiamente dichas, aunque realmente no voy a dar demasiadas 4que luego
[to[ se sabe8# ;olamente te indicar( c%mo calcular el m)imo com=n divisor con un m(todo recursivo#
5l m(todo r0C2 tiene que ser recursivo# Veamos, una de las +ormas de calcular el m)imo com=n divisor es
la siguiente* si los dos n=meros son iguales, el 0C2 es cualquiera de ellos# 2e lo contrario al mayor se le resta
el menor y se comprueba si la di+erencia es igual al menor# ;i no, lo mismo# 9or e"emplo, entre L3 y 1S, como no
son iguales se restan* L3V1S, que es >J y se vuelve a hacer la comprobaci%n entre los dos n=meros 4>J y el
menor de los anteriores, o sea, 1S8# Como no son iguales, se vuelven a restar* >JV1S, que es 2O y se vuelve a
comprobar 42O y el menor de los anteriores, o sea, 1S nuevamente8# Como siguen sin ser iguales, se vuelve a
hacer la resta* 2OV1S, que es ?, y se vuelve a comprobar 4? y el menor de los anteriores, o sea, 1S8# ;iguen sin
ser iguales, pero recuerda que ahora el mayor es 1S y el menor es ?# ;e vuelven a restar* 1SV? que es ?# Ahora
la di+erencia es igual que el menor de los dos n=meros, por lo que ya tenemos el 0C2* ?#
18.11.2 5esolucin del ejercicio
Bueno, aqu& lo ten(is# Ciertamente, es un poquito largo, pero nada comparado con un programa de verdad,
Dno te pareceE 5l c%digo est comentado para que no os cueste mucho desci+rarlo#
usin$ G+stem%
namespace :;ercicio=
#
class !etodos
#
public static double r3act(b+te num)
#
)) asta con usar el operador IL:GK/^N (Q9) en lu$ar de i4
return (num==() Q ? 9 num H r3act((b+te) (num7?))%
&
public static double it3act(b+te num)
#
)H Aqu* necesitaremos un bucle que va+a haciendo las sucesivas
H multiplicaciones hasta encontrar el 4actorial H)
double 3act=?%

4or (b+te i=D%iV=num%i22)
3act H= i%
11@
return 3act%
&
public static ulon$ r!"D(ulon$ num?, ulon$ numD)
#
)H Primero se calcula cuEl de ellos es ma+or + cuEl es menor.
H :n caso de que sean i$uales, ma+or + menor van a valer lo
H mismo. H)
ulon$ ma+or=(num?JnumD) Q num? 9 numD%
ulon$ menor=(num?JnumD) Q numD 9 num?%
)H :Xaminemos este return9 si son i$uales retornarE ma+or.
H :n realidad hubiera sido lo mismo que retornara menor,
H puesto que, recuerda, son i$uales. :n caso contrario
H se vuelve a calcular el !"D, esta vez entre la di4erencia
H de ellos + el menor. :sto se repetirE hasta que sean
H i$uales H)
return (ma+or==menor) Q ma+or 9 r!"D(ma+or7menor, menor)%
&
public static ulon$ it!"D(ulon$ num?, ulon$ numD)
#
)H Aqu* tambi8n necesitamos saber cuEl es el ma+or + cuEl el
H menor, recordando siempre que si son i$uales se retornarE
H cualquiera de ellos H)
ulon$ ma+or=(num?JnumD) Q num? 9 numD%
ulon$ menor=(num?JnumD) Q numD 9 num?%
i4 (ma+or==menor) return ma+or%
)H Ge asi$na la mitad del ma+or al posible !"D porque,
H l$icamente, nin$5n n5mero ma+or que la mitad del ma+or de
H los dos serE divisible entre este, o sea, si el ma+or es,
H por e;emplo, B, nin$5n n5mero ma+or que A (o sea, la mitad
H de B) serE divisible entre B. As* nos ahorramos al menos
H la mitad de las operaciones H)
ulon$ !"D=ma+or)D%
)H :ste bucle va reduciendo !"D en una unidad mientras no
H sea divisible entre los dos. "uando lo sea, el bucle no se
111
H itera + se devuelve !"D H)
<hile (ma+or [ !"DS=( YY menor [ !"DS=()
#
!"D7=?%
&
return !"D%
&
public static ulon$ !"!(ulon$ num?, ulon$ numD)
#
)H :n este caso no ser*a necesario ver cuEl es el ma+or
H + cuEl es el menor, pero calcularlo nos a+uda a evitar
H muchas operaciones, pues de lo contrario tendr*amos que
H ir comprobando n5meros desde ? hasta que se encontrara
H el !"!. H)
ulon$ ma+or=(num?JnumD) Q num? 9 numD%
ulon$ menor=(num?JnumD) Q numD 9 num?%
)H Dado que el menor de los m5ltiplos comunes de dos n5meros
H tiene que ser, como m*nimo, i$ual que el ma+or, asi$namos a
H la varable mcm lo que vale el ma+or de los dos. H)
ulon$ mcm=ma+or%
)H :ste bucle puede ser al$o con4uso, pero en realidad es el
H ideal. :4ectivamente, evito hacer demasiadas operaciones
H calculando si los sucesivos m5ltiplos del ma+or lo son
H tambi8n del menor, en cu+o caso se ha encontrado. H)
<hile (mcm [ menor S=()
mcm2=ma+or%
return mcm%
&
public static bool :sPer4ecto(ulon$ num)
#
)H Nos hace 4alta esta variable para ir almacenando los
H divisores del n5mero en cuestin H)
ulon$ sumadiv=(%
)H :ste bucle recorre todos los n5meros desde la unidad hasta
112
H la mitad del n5mero en cuestin. PPorqu8 hasta la mitadQ
H Pues porque nin$5n n5mero ma+or de la mitad puede ser
H uno de sus divisores, l$icamente. -ue$o, si el n5mero
H por el que vamos iterando es divisor de num se suma a
H sumadiv, + si no a sumadiv se le suma (, o sea, nada H)
4or (ulon$ i=?%iV=num)D% i22)
sumadiv 2= (num [ i==() Q i 9 (%
)H Aqu* podr*amos haber usado un IL:GK/^N o un i4, pero esto
H es su4iciente, puesto que ha+ que retornar precisamente el
H resultado de la comparacin, es decir, si son i$uales ha+
H que retornar true, + si no lo son ha+ que retornar 4alse H)
return sumadiv==num%
&
&
class !etodosApp
#
static void !ain()
#
ulon$ num?=(%
ulon$ numD=(%
do
#
tr+
#
"onsole.,rite(ODame un n5mero para calcular el 4actorial9 O)%
num?=L/nt>A.Parse("onsole.6ead-ine())%
&
catch
#
continue%
&
& <hile (num?JD@@)%
"onsole.,rite-ine(O:l m8todo r3act dice que el 4actorial de #(& es #?&O,
num?,!etodos.r3act((b+te) num?))%
113
"onsole.,rite-ine(O:l m8todo r3act dice que el 4actorial de #(& es #?&O,
num?,!etodos.it3act((b+te) num?))%
"onsole.,rite-ine()%
do
#
tr+
#
"onsole.,rite(OAhora dame uno de los n5meros mara !"D + !"!9 O)%
num?=L/nt>A.Parse("onsole.6ead-ine())%
"onsole.,rite(OAhora dame el otro n5mero mara !"D + !"!9 O)%
numD=L/nt>A.Parse("onsole.6ead-ine())%
&
catch
#
continue%
&
& <hile (num?==( YY numD==()%
"onsole.,rite-ine(O:l m8todo r!"D dice que el !"D entre #(& + #?& es #D&O,
num?, numD, !etodos.r!"D(num?, numD))%
"onsole.,rite-ine(O:l m8todo it!"D dice que el !"D entre #(& + #?& es #D&O,
num?, numD, !etodos.it!"D(num?, numD))%
"onsole.,rite-ine()%
"onsole.,rite-ine(O:l m8todo !"! dice que el !"! entre #(& + #?& es #D&O,
num?, numD, !etodos.!"!(num?, numD))%
"onsole.,rite-ine()%
do
#
tr+
#
"onsole.,rite(OPor 4in, dame un n5mero para ver si es per4ecto9 O)%
num?=L/nt>A.Parse("onsole.6ead-ine())%
&
catch
#
11>
continue%
&
& <hile (num?JD@@)%
"onsole.,rite-ine(O:l m8todo :sPer4ecto dice que el n5mero #(& #?&O,
num?, !etodos.:sPer4ecto(num?) Q Oes per4ectoO9 Ono es per4ectoO)%

strin$ a="onsole.6ead-ine()%
&
&
&
11 *nd6cima entrega (-rra&s.!
Tasta ahora hemos aprendido un mont%n de cosas con respecto a las variables, pero siempre ten&amos que
saber con antelaci%n el n=mero de variables que el programa iba a necesitar# ;in embargo, habr situaciones
en las que no sea posible determinar este n=mero hasta que el programa no se est( e"ecutando# 9ongamos por
e"emplo que estamos diseando un programa de +acturaci%n# 5videntemente, cada +actura tendr una serie de
l&neas de detalle, pero ser imposible conocer el n=mero de l&neas de detalle de cada +actura en tiempo de
diseo, esto es, antes de que el programa comience su e"ecuci%n# 9ues bien, para solucionar estas di+icultades
contamos con los arrays y los indi!adores#
11.1 0rrays
Antes de comen!ar a e)plicaros con mayor claridad qu( es un array quiero advertir nuevamente a los
programadores de C6C--* 5n C#, aunque parecidos, los arrays son di+erentes tanto semntica como
sintcticamente, de modo que te recomiendo que no pases por alto esta entrega#
Bien, una ve! hechas todas las aclaraciones previas, creo que podemos comen!ar# Pn array es un indicador
que puede almacenar varios valores simultneamente# Cada uno de estos valores se identi+ica mediante un
n=mero al cual se llama &ndice# As&, para acceder al primer elemento del array habr&a que usar el &ndice cero,
para el segundo el &ndice uno, para el tercero el &ndice dos, y as& sucesivamente# Iue nadie se preocupe si de
momento todo esto es un poco con+uso, ya que lo voy a ir desmenu!ando poco a poco# Vamos a ver c%mo se
declara un array*
tipo\] variable%
Bien, como veis es muy parecido a como se declara una variable normal, s%lo que hay que poner corchetes
detrs del tipo# :os programadores de C6C-- habrn observado inmediatamente la di+erencia sintctica# 5n
e+ecto, en la declaraci%n de un array en C# los corchetes se colocan detrs del tipo y no detrs de la variable#
5sta pequea di+erencia sintctica se debe a una importante di+erencia semntica* aqu& los arrays son ob"etos
derivados de la clase ;ystem#Array# 9or lo tanto, y esto es muy importante, cuando declaramos un array en C#
este a=n no se habr creado, es decir, no se habr reservado a=n memoria para (l# 5n consecuencia, los arrays
de C# son todos dinmicos, y antes de poder usarlos habr que instanciarlos, como si +uera cualquier otro
ob"eto# Veamos un breve e"emplo de lo que quiero decir*
strin$\] nombres% )) Declaracin del arra+
nombres = ne< strin$\=]% )) /nstanciacin del arra+
11J
5n e+ecto, tal como pod(is apreciar, el array nombres ser utili!able =nicamente a partir de su instanciaci%n#
5n este e"emplo, el n=mero 3 que est dentro de los corchetes indica el n=mero total de elementos de que
constar el array# $o os equivoqu(is, puesto que todos los arrays de C# estn basados en cero, esto es, el
primer elemento del array es cero# 9or lo tanto, en este caso, el =ltimo elemento ser&a 2 y no 3, ya que son tres
los elementos que lo componen 4@, 1 y 28# Veamos un e"emplo algo ms completo y despu(s lo comentamos*
usin$ G+stem%
namespace Arra+s
#
class Arra+sApp
#
static void !ain()
#
strin$\] nombres% )) Declaracin del arra+
ushort num=(%
do
#
tr+
#
"onsole.,rite(OP"uEntos nombres vas a introducirQ O)%
num=L/nt?>.Parse("onsole.6ead-ine())%
&
catch
#
continue%
&
& <hile (num==()%
nombres=ne< strin$\num]% )) /nstanciacin del arra+
4or (int i=(% iVnum% i22)
#
"onsole.,rite(O:scribe el nombre para elemento #(&9 O, i)%
nombres\i]="onsole.6ead-ine()%
&
"onsole.,rite-ine(O/ntroducidos los #(& nombresO, num)%
"onsole.,rite-ine(OPulsa /NK6^ para listarlosO)%
strin$ a="onsole.6ead-ine()%
4or (int i=(% iVnum% i22)
11L
#
"onsole.,rite-ine(O:lemento #(&9 #?&O, i, nombres\i])%
&
a="onsole.6ead-ine()%
&
&
&
Veamos ahora la salida en la consola *
P"uEntos nombres vas a introducirQ =
:scribe el nombre para el elemento (9 Muanito
:scribe el nombre para el elemento ?9 Maimito
:scribe el nombre para el elemento D9 Moselito
/ntroducidos los = nombres
Pulsa /NK6^ para listarlos
:lemento (9 Muanito
:lemento ?9 Maimito
:lemento D9 Moselito
5n este pequeo programa hemos declarado un array y lo hemos instanciado despu(s de haber preguntado
al usuario cuntos elementos iba a tener# Como veis, hemos utili!ado un bucle +or para recoger todos los
valores que hay que meter en el array# Iuiero que prest(is especial atenci%n a c%mo hemos introducido los
valores en el array* en la l&nea [nombres_i` R Console#Head:ine48[ lo que hacemos es que al elemento [i[ del
array le asignamos lo que devuelva el m(todo Head:ine# Como [i[ tomar valores entre @ y el n=mero total de
elementos menos uno rellenaremos el array completo 4+i"aos en la condici%n del bucle, que es iYnum, es decir,
que si i es igual a num el bucle ya no se itera8# 2espu(s tenemos otro bucle +or para recorrer todo el array y
escribir sus valores en la consola# 5n de+initiva, para acceder a un elemento del array se usa la sinta)is
[array_&ndice`[#
Pn array tambi(n puede iniciali!arse en la propia declaraci%n, bien instancindolo 4como cualquier otro
ob"eto8 o bien asignndole los valores directamente# Vamos a reescribir el e"emplo anterior instanciando el array
en la declaraci%n del mismo*
usin$ G+stem%
namespace Arra+sD
#
class Arra+sDApp
#
static void !ain()
#
ushort num==%
do
11O
#
tr+
#
"onsole.,rite(OP"uEntos nombres vas a introducirQ O)%
num=L/nt?>.Parse("onsole.6ead-ine())%
&
catch
#
continue%
&
& <hile (num==()%
strin$\] nombres=ne< strin$\num]% )) Declaracin e instanciacin del arra+
4or (int i=(% iVnum% i22)
#
"onsole.,rite(O:scribe el nombre para elemento #(&9 O, i)%
nombres\i]="onsole.6ead-ine()%
&
"onsole.,rite-ine(O/ntroducidos los #(& nombresO, num)%
"onsole.,rite-ine(OPulsa /NK6^ para listarlosO)%
strin$ a="onsole.6ead-ine()%
4or (int i=(% iVnum% i22)
#
"onsole.,rite-ine(O:lemento #(&9 #?&O, i, nombres\i])%
&
a="onsole.6ead-ine()%
&
&
&
Bien, ahora, como puedes observar, el array ha sido instanciado en la misma l&nea en la que +ue declarado#
5l +uncionamiento de este e"emplo, por lo tanto, ser&a el mismo que el del e"emplo anterior# Veamos ahora otro
e"emplo de iniciali!aci%n del array asignndole los valores en la declaraci%n*
usin$ G+stem%
namespace Arra+s=
#
class Arra+s=App
11S
#
static void !ain()
#
)) Declaracin e inicializacin del arra+
strin$\] nombres=#OMuanitoO, OMaimitoO, OMoselitoO&%
4or (int i=(% iVnombres.-en$th% i22)
#
"onsole.,rite-ine(O:lemento #(&9 #?&O, i, nombres\i])%
&
strin$ a="onsole.6ead-ine()%
&
&
&
5n este caso, el array nombres ha sido iniciali!ado en la propia declaraci%n del mismo, asignndole los tres
valores que va a contener# Como ves, dichos valores estn entre llaves y separados por comas# :as comillas
son necesarias en este caso, ya que el array es de tipo string# DIue d%nde est la instanciaci%n del arrayE
Bueno, cuando hacemos esto, la instanciaci%n la hace por deba"o el compilador, es decir, de +orma impl&cita#
9resta atenci%n tambi(n a la condici%n del bucle* ahora hemos usado la propiedad :ength del array nombres en
lugar de una variable# 5n e+ecto, esta propiedad nos devuelve el n=mero de elementos de un array# 9or lo tanto,
la salida en consola de este programa ser&a esta*
:lemento (9 Muanito
:lemento ?9 Maimito
:lemento D9 Moselito
9or otro lado, el hecho de que un array haya sido iniciali!ado no quiere decir que sea inamovible# ;i un array
que ya contiene datos se vuelve a instanciar, el array volver a estar vac&o, y obtendr las dimensiones de la
nueva instanciaci%n#
Bien, todos estos arrays que hemos e)plicado hasta el momento son arrays unidimensionales, es decir, que
tienen una sola dimensi%n 4un solo &ndice8# ;in embargo esto no soluciona a=n todas las necesidades del
programador# 9ongamos, por e"emplo, que queremos almacenar las combinaciones de las ocho columnas de
una quiniela de +=tbol en un array#DC%mo lo hacemosE 9ues bien, el me"or modo es utili!ar un array
multidimensional#
11.2 0rrays multidimensionales
:os arrays multidimensionales son aquellos que constan de dos o ms dimensiones, es decir, que cada
elemento del array viene de+inido por dos o ms &ndices# Vamos a echar un vista!o a la declaraci%n de un array
multidimensional 4en este caso, ser tridiensional, es decir, con tres dimensiones8*
tipo\,,] variable%
Como ves, hay dos comas dentro de los corchetes, lo cual indica que el array es tridimensional, puesto que
los tres &ndices del mismo se separan uno de otro por comas# Veamos un pequeo e"emplo que lo clari+ique un
poco ms*
11?
strin$\,] alumnos = ne< strin$\D,A]%
5ste array es bidimensional y servir&a para almacenar una lista de alumnos por aula, esto es, tenemos dos
aulas 4el primer &ndice del array es 28 y cuatro alumnos en cada una 4el segundo &ndice es >8# Veamos un poco
de c%digo y una tabla para que os hagis una idea de c%mo se almacena esto*
alumnos\(,(]=O-oloO%
alumnos\(,?]=O!arioO%
alumnos\(,D]=OMuanO%
alumnos\(,=]=OPepeO%
alumnos\?,(]=O-olaO%
alumnos\?,?]=O!ar*aO%
alumnos\?,D]=OMuanaO%
alumnos\?,=]=OPepaO%
5sto ser&a como almacenar los datos en esta tabla*
-*,- ? -*,- 1
$10BH5 @ :olo :ola
$10BH5 1 0ario 0ar&a
$10BH5 2 ,uan ,uana
$10BH5 3 9epe 9epa
DIue quieres saber por qu( he separado a los chicos de las chicasE Bueno, no es que sea un retr%grado, es
para que se vea me"or todo esto# 0ira que sois detallistas### Bueno, creo que va quedando bastante claro# DB
c%mo recorremos un array multidimensionalE 9ues con bucles anidados# Vamos ya con un e"emplo ms
completito de todo esto# 5ste pequeo programa pregunta al usuario por el n=mero de columnas que quiere
generar de una quiniela de +=tbol, y despu(s las rellena al a!ar y las muestra en pantalla*
usin$ G+stem%
namespace Iuinielas
#
class IuinielasApp
#
static void !ain()
#
const char local=d?d%
const char empate=dTd%
const char visitante=dDd%
const b+te num3ilas=?A%
b+te num"olumnas=(%
char\,] quiniela%
12@
b+te azar%
6andom rnd=ne< 6andom(unchec0ed((int) DateKime.No<.Kic0s))%
do
#
tr+
#
"onsole.,rite-ine(O!*nimo una columna + mEXimo ochoO)%
"onsole.,rite(OP"uEntas columnas quieres $enerarQ O)%
num"olumnas=+te.Parse("onsole.6ead-ine())%
&
catch
#
continue%
&
& <hile (num"olumnasV? YY num"olumnasJB)%
quiniela=ne< char\num"olumnas, num3ilas]%
4or (b+te i=(% iVnum"olumnas% i22)
#
4or (b+te ;=(% ;Vnum3ilas% ;22)
#
azar=(b+te) (rnd.NeXtDouble()H=D)%
s<itch (azar)
#
case (9
quiniela\i,;]=local%
brea0%
case ?9
quiniela\i,;]=empate%
brea0%
case D9
quiniela\i,;]=visitante%
brea0%
&
&
121
&
"onsole.,rite-ine(OIuiniela $enerada. Pulsa /NK6^ para verlaO)%
strin$ a="onsole.6ead-ine()%
4or (b+te i=(% iVnum"olumnas% i22)
#
"onsole.,rite(O"olumna #(&9 O, i2?)%
4or (b+te ;=(% ;Vnum3ilas% ;22)
#
"onsole.,rite(O#(& O, quiniela\i,;])%
&
"onsole.,rite-ine()%
"onsole.,rite-ine()%
&
a="onsole.6ead-ine()%
&
&
&
Como veis, esto se va poniendo cada ve! ms interesante# 2e este programa, aparte de la clase Handom,
hemos visto todo e)cepto los bloques try y catch, de modo que si hay algo que no entiendes te recomiendo que
revises las entregas anteriores# :a clase Handom es para generar n=meros aleatorios 4al a!ar8# 5n la
instanciaci%n de dicha clase hemos puesto algo que puede resultarte algo con+uso# 5s esta l&nea*
6andom rnd=ne< 6andom(unchec0ed((int) DateKime.No<.Kic0s))%
Bien, el constructor de esta clase tiene dos sobrecargas* una de ellas es sin argumentos, y la otra acepta un
argumento de tipo int, que es la que hemos usado# D9or qu(E 9orque de lo contrario siempre generar&a los
mismos n=meros en cada e"ecuci%n del programa, lo cual no ser&a muy =til en este caso# Como necesitamos
que se generen n=meros distintos tenemos que pasarle n=meros di+erentes en el argumento int del constructor
de la clase Handom, y el modo ms e+ica! de conseguirlo es hacer que ese n=mero dependa del tiempo que
lleve encendido el ordenador# 9or otro lado, el n=mero lo generamos al e"ecutar el m(todo $e)t2ouble, el cual
nos retorna un n=mero mayor o igual a @ y menor que 1# 5sta es la l&nea*
azar=(b+te) (rnd.NeXtDouble()H=D)%
D9or qu( lo hemos multiplicado por 32E 9ues bien, como queremos n=meros enteros entre @ y 2 4o sea, @, 1
o 28 bastar con multiplicar este n=mero 4recuerda que est entre cero y uno8 por 3# DB la 2E Ahora voy,
hombre# D1s acordis de los su+i"os en los literales, para indicar si se deb&a considerar si el n=mero era de un
tipo o de otroE 9ues aqu& est la e)plicaci%n# 2ado que el m(todo $e)t2ouble retorna un valor double, tenemos
que multiplicarlo por otro valor double# 9or eso le ponemos el su+i"o [2[ al n=mero tres# 2espu(s todo ese
resultado se convierte a byte y se asigna a la variable a!ar, que es la que se comprueba en el s.itch para
asignar el carcter necesario seg=n su valor a cada elemento del array#
9or lo dems creo que a estas alturas no deber&a tener que e)plicaros gran cosa* tenemos un par de bucles
anidados para asignar los valores al array y despu(s otros dos bucles anidados para recorrer dicho array y
mostrar su contenido en la consola#
122
1tra cuesti%n importante en la que quiero que te +i"es es en que ya estoy empe!ando a de"ar de usar
[literales y n=meros mgicos[, usando constantes en su lugar# 5+ectivamente, podr&a haberme ahorrado las
cuatro constantes* local, empate, visitante y numNilas, poniendo sus valores directamente en el c%digo, algo as&*
...
4or (b+te i=(% iVnum"olumnas% i22)
#
4or (b+te ;=(% ;V?A% ;22)
#
azar=(b+te) (rnd.NeXtDouble()H=D)%
s<itch (azar)
#
case (9
quiniela\i,;]=d?d%
brea0%
case ?9
quiniela\i,;]=dTd%
brea0%
case D9
quiniela\i,;]=dDd%
brea0%
&
&
&
...
5n e+ecto, +uncionar&a e)actamente igual, pero Dqu( ocurrir&a si otra persona que no sabe qu( es una
quiniela, o por qu( tiene que ser el n=mero 1>, o qu( signi+ican el 1, la Q o el 2E 9ues que el c%digo ser&a
menos claro# :as constantes, sin embargo, hacen la lectura del c%digo ms +cil# 9or otro lado, si alg=n d&a
cambiaran los signos, por e"emplo, si hubiese que poner una [a[ en lugar del [1[, una [b[ en lugar de la [)[ y una
[c[ en lugar del [2[ y no hubi(semos usado constantes habr&a que buscar todos estos literales por todo el
c%digo y sustituirlos uno por uno, mientras que usando constantes 4que estn declaradas al principio8 basta con
modi+icar sus valores, haciendo as& el cambio e+ectivo ya para todo el programa# As& que ya lo sab(is* a partir de
ahora vamos a evitar en lo posible los [literales y los n=meros mgicos[#
9ara terminar con esto, el n=mero de dimensiones de un array se llama rango# 9ara conocer el rango de un
array mediante c%digo basta con invocar la propiedad HanU del mismo 4heredada de la clase ;ystem#Array8#
Veamos un e"emplo de esto*
usin$ G+stem%
namespace 6an$os
#
123
class 6an$osApp
#
static void !ain()
#
int\] arra+?=ne< int\D]%
int\,] arra+D=ne< int\D,D]%
int\,,] arra+==ne< int\D,D,D]%
int\,,,] arra+A=ne< int\D,D,D,D]%
"onsole.,rite-ine(O6an$o de arra+?9 #(&O, arra+?.6an0)%
"onsole.,rite-ine(O6an$o de arra+D9 #(&O, arra+D.6an0)%
"onsole.,rite-ine(O6an$o de arra+=9 #(&O, arra+=.6an0)%
"onsole.,rite-ine(O6an$o de arra+A9 #(&O, arra+A.6an0)%
strin$ a="onsole.6ead-ine()%
&
&
&
:a salida en la consola de todo esto ser&a la siguiente*
6an$o de arra+?9 ?
6an$o de arra+D9 D
6an$o de arra+=9 =
6an$o de arra+A9 A
11.3 0rrays de arrays
5n e+ecto, para liar un poco ms la made"a, tenemos tambi(n los arrays de arrays# 5stos son arrays que
pueden contener otros arrays# DB para qu( diablos queremos meter un array dentro de otroE D$o nos basta con
los arrays multidimensionalesE 9ues realmente podr&a bastarnos, en e+ecto, pero habr&a ocasiones en las que
tendr&amos que hacer bastantes cabriolas con el c%digo por no usar los arrays de arrays# 9ensad en un
programa en el que el usuario tiene que mane"ar simultneamente m=ltiples ob"etos de distintas clases
derivadas de una clase base, por e"emplo, tringulos y cuadrados derivados de la clase +igura# ;i solamente
pudi(ramos usar arrays unidimensionales o multidimensionales tendr&amos que declarar un array distinto para
cada tipo de ob"eto 4uno para tringulos y otro para cuadrados8# :a di+icultad viene ahora* DIu( ocurre si hay
que redibu"ar todos los ob"etos, ya sean cuadrados o tringulosE 5videntemente, habr&a que escribir un bucle
para cada uno de los arrays para poder invocar los m(todos Hedibu"ar de cada uno de los elementos# ;in
embargo, si metemos todos los arrays dentro de un array de arrays nos bastar&a con escribir un par de bucles
anidados para recorrer todos los ob"etos y de"ar el resto en manos del polimor+ismo# Ciertamente, a=n no hemos
estudiado a +ondo ninguno de los mecanismos de la herencia# $o obstante, con lo que sabemos hasta ahora,
podemos poner un e"emplo sobre los arrays de arrays, aunque probablemente no se aprecie realmente la
venta"a# Veamos el e"emplo, y luego lo comentamos# 5so s&, presta especial atenci%n a la sinta)is, tanto en la
declaraci%n como en las instanciaciones*
usin$ G+stem%
12>
namespace Arra+sdeArra+s
#
class Arra+sDeArra+sApp
#
static void !ain()
#
ob;ect\]\] numeros% )) Declaracin del arra+ de arra+s
numeros=ne< ob;ect\D]\]% )) /nstanciacin del arra+ de arra+s
numeros\(]=ne< ob;ect\=]% )) /nstanciacin del primer arra+
numeros\?]=ne< ob;ect\A]% )) /nstanciacin del se$undo arra+
numeros\(]\(]==.=D@D%
numeros\(]\?]=>.D@D%
numeros\(]\D]==D%
numeros\?]\(]==u%
numeros\?]\?]=Cu%
numeros\?]\D]=Au%
numeros\?]\=]=BCu%
4or (int i=(%iVnumeros.-en$th%i22)
#
4or (int ;=(%;Vnumeros\i].-en$th%;22)
#
"onsole.,rite-ine(numeros\i]\;].KoGtrin$())%
&
&
strin$ a="onsole.6ead-ine()%
&
&
&
5n este e"emplo vamos a usar un array en el que incluiremos dos arrays* uno para n=meros de tipo double y
otro para n=meros de tipo ulong# Como estos dos tipos estn derivados de la clase ;ystem#1b"ect, lo que
hacemos es declarar el array de este tipo en la primera l&nea del m(todo 0ain, y despu(s lo instanciamos
dici(ndole que contendr dos arrays 4en la segunda l&nea8# 2espu(s instanciamos tambi(n como tipo ob"ect los
dos arrays que contendr el primero, y le asignamos valores* al array numeros_@` le asignamos valores de tipo
double, y al array numeros_1` le asignamos valores de tipo ulong# 2espu(s usamos un par de bucles anidados
para recorrer todos los elementos del array de arrays con el ob"eto de invocar el m(todo 'o;tring48 de todos
ellos 4heredado de la clase ;ystem#1b"ect8# Como ves, el bucle [i[ recorre el array de arrays 4+&"ate en la
condici%n, iYnumeros#:ength8, y el bucle ["[ recorre cada uno de los elementos del array numeros_i`, seg=n sea
el valor de i en cada iteraci%n# Con los e"emplos de esta entrega se incluye tambi(n el de los cuadrados y los
12J
tringulos que te mencion( antes 4en la carpeta Niguras8, pero no lo reprodu!co aqu& porque a=n no hemos
visto la herencia# ;in embargo, cuando lo e"ecutes, vers me"or la utilidad de los arrays de arrays#
Bien, creo que ya es su+iciente para esta entrega# $o te pondr( e"ercicios sobre los arrays todav&a, pues
pre+iero esperar a que hayamos vistos los indi!adores y los bucles +oreach# 9or cierto### espero que la pr%)ima
entrega no se haga esperar tanto como esta# A ver si hay algo de suerte###
1" Duod6cima entrega (IndiIadores( sobrecarga de operadores &
conversiones de8inidas.!
:arga ha sido mi ausencia de estas pginas desde la publicaci%n de la =ltima entrega# 1s pido perd%n por
tanto retraso# 2esde este momento hago prop%sito de enmienda para las entregas sucesivas, a ver si, al
menos, me es posible tener lista una entrega al mes#
Cada ve! estamos ms cerca de las opciones ms avan!adas de este lengua"e# 5n esta entrega
comen!aremos hablando de un concepto ms o menos nuevo* los indi!adores# Ciertamente, el palabro es un
tanto e)trao, pero os aseguro que esta ve! no he sido yo el que le ha puesto ese nombre###
9ara terminar esta entrega entraremos de lleno en la sobrecarga de operadores y las conversiones
de+inidas, lo que de"ar libre el camino para que en las pr%)imas entregas podamos empe!ar ya a hablar en
pro+undidad de los mecanismos de la herencia, inter+aces y dems +lorituras#
12.1 +ndi;adores
2ec&a que un indi!ador es un concepto ms o menos nuevo porque no es nuevo en realidad 4al menos, a m&
no me lo parece8# 0s bien se trata de una simpli+icaci%n en lo que se re+iere a la l%gica de +uncionamiento de
un ob"eto que es en realidad un array o una colecci%n#
Antes de ver su sinta)is considero que es necesario comprender bien el concepto y el ob"etivo de un
indi!ador# 9ara ello vamos a poner un e"emplo* podemos considerar que un libro no es ms que un ob"eto que
contiene una serie de cap&tulos# ;i nos olvidamos por un momento de los indi!adores deber&amos construir el
ob"eto :ibro con una colecci%n Cap&tulos dentro de (l en la que pudi(ramos aadir o modi+icar cap&tulos, de
modo que deber&amos o+recer, por e"emplo, un m(todo Add para lo primero y un m(todo 0odi+y para lo
segundo# 2e este modo, habr&amos de llamar a alguno de estos m(todos desde el c%digo cliente para e+ectuar
dichas operaciones, es decir, algo como esto*
static void !ain()
#
-ibro mi-ibro=ne< -ibro()%
mi-ibro."apitulos.Add(O-a psicolo$*a de la musaraeaO)%
mi-ibro."apitulos.Add(OPPuede una hormi$a montar en bicicletaQO)%
mi-ibro."apitulos.!odi4+(OLn pedrusco en manos de !ir es un !irO,?)%
...
&
:os indi!adores, sin embargo, o+recen la posibilidad de tratar al ob"eto :ibro como si +uera un array o una
colecci%n en s& mismo, haciendo la codi+icaci%n ms intuitiva a la hora de usarlo# ;i hubi(ramos escrito la clase
:ibro como un indi!ador, el c%digo equivalente al anterior podr&a ser algo como esto*
static void !ain()
#
12L
-ibro mi-ibro=ne< -ibro()%
mi-ibro\(]=O-a psicolo$*a de la musaraeaO%
mi-ibro\?]=OPPuede una hormi$a montar en bicicletaQO%
mi-ibro\?]=OLn pedrusco en manos de !ir es un !irO%

...
&
;in duda, este c%digo resulta mucho ms natural* ya que el ob"eto :ibro no es ms que un con"unto de
cap&tulos, lo suyo es tratarlo como si +uera un array, independientemente de que dicho ob"eto pueda o+recer
tambi(n otra serie de propiedades, m(todos y dems#
Bien, ahora que ya sabemos para qu( sirve un indi!ador podemos ver su sinta)is# Ba ver(is que no es nada
del otro mundo*
class -ibro
#
public ob;ect this\int indeX]
#
$et
#
...
&
set
#
...
&
&
...
&
Como pod(is apreciar, el indi!ador se construye de un modo muy similar a las propiedades, con la salvedad
de que el nombre de esta [propiedad[ es el propio ob"eto, es decir, this, el tipo, l%gicamente, es ob"ect y,
adems, requiere un argumento entre corchetes, que ser&a el &ndice del elemento al que queremos acceder#
Veamos ahora el e"emplo del libro completo*
usin$ G+stem%
usin$ G+stem."ollections%
namespace /ndizador-ibros
#
class -ibro
12O
#
private Arra+-ist capitulos=ne< Arra+-ist()%
public ob;ect this\int indice]
#
$et
#
i4 (indice J= capitulos."ount YY indice V ()
return null%
else
return capitulos\indice]%
&
set
#
i4 (indice J= ( WW indice V capitulos."ount)
capitulos\indice]=value%
else i4 (indice == capitulos."ount)
capitulos.Add(value)%
else
thro< ne< :Xception(ONo se puede asi$nar a este elementoO)%
&
&
public int Num"apitulos
#
$et
#
return capitulos."ount%
&
&
&
class /ndizador-ibrosApp
#
static void !ain()
#
-ibro mi-ibro=ne< -ibro()%
12S
mi-ibro\(]=O-a psicolo$*a de la musaraeaO%
mi-ibro\?]=OPPuede una hormi$a montar en bicicletaQO%
mi-ibro\?]=OLn pedrusco en manos de !ir es un !irO%
4or (int i=(%iVmi-ibro.Num"apitulos%i22)
"onsole.,rite-ine(O"apitulo #(&9 #?&O,i2?,mi-ibro\i])%
strin$ a="onsole.6ead-ine()%
&
&
&
Tay un detalle realmente importante que no os hab&a contado hasta ahora# :a propiedad $umCapitulos
devuelve el n=mero de cap&tulos que hay incluidos en el ob"eto :ibro hasta este momento# 9uede que alguno se
est( preguntando por qu( diantres he puesto esta propiedad, ya que un array tiene ya la propiedad :ength que
devuelve e)actamente lo mismo, y una colecci%n tiene ya la propiedad Count, que hace tambi(n lo mismo# :a
ra!%n es muy simple* :ibro es una clase, no una colecci%n ni un array# 5l hecho de que hayamos implementado
un indi!ador har que se pueda acceder a los elementos de los ob"etos de esta clase como si +uera un array,
pero no que el compilador genere una propiedad Count o :ength por su cuenta y riesgo# 9or este motivo, si
queremos una propiedad que o+re!ca el n=mero de elementos contenidos en el ob"eto tendremos que
implementarla nosotros# 5n este caso, yo la he llamado $umCapitulos#
9or otro lado, veis que he declarado el campo privado [capitulos[ del tipo ;ystem#Collections#Array:ist#
DIuer(is saber por qu(E 9ues porque necesito meter los cap&tulos en alg=n array o en alguna colecci%n 4en
este caso, se trata de una colecci%n8# 5s decir, una ve! ms, el hecho de implementar un indi!ador no convierte
a una clase en una colecci%n o en un array, sino simplemente hace que o+re!ca una inter+a! similar a estos,
nada ms# 9or lo tanto, si tengo que almacenar elementos en un array o en una colecci%n, l%gicamente,
necesito un array o una colecci%n donde almacenarlos# 5n de+initiva, el indi!ador hace que podamos [encubrir[
dicha colecci%n en aras de obtener una l%gica ms natural y mane"able#
9ara terminar, la l&nea que dice [thro. ne. 5)ception###[ manda un mensa"e de error al cliente# $o os
preocup(is demasiado porque veremos las e)cepciones o errores a su debido tiempo#
12.2 /obrecarga de operadores
5sto es algo que te sonar muy e)trao, sobre todo al principio, si no eras programador de C--, as& que
procura tomrtelo con calma y entenderlo bien porque aqu& es bastante +cil armarse un buen "aleo mental#
Veamos, sobrecargar un operador consiste en modi+icar su comportamiento cuando este se utili!a con una
determinada clase# DIu( operadorE 9ues casi cualquiera* -, V, \, 6, YY, ZZ, etc# 4luego te pongo una lista de los
operadores que puedes sobrecargar en C#8# Vamos a empe!ar poniendo e"emplos de la utilidad de esto para
ver si consigo que lo vayis comprendiendo#
'odos sabemos que es per+ectamente +actible sumar dos n=meros de tipo int, o dos de tipo short, e incluso
se pueden sumar dos n=meros de tipos distintos# 9ero, por e"emplo, Dqu( ocurrir&a si tengo una variable en la
que almaceno cantidades en metros y otra en cent&metros y las sumoE 9ues ocurrir que el resultado ser&a
incorrecto, puesto que solo puedo sumar metros con metros y cent&metros con cent&metros# 5sto es*
double m=?(%
double c=?(%
double Guma!etros=m2c%
12?
double Guma"entimetros=m2c%
"onsole.,rite-ine(Guma!etros)%
"onsole.,rite-ine(Guma"entimetros)%
5videntemente, el ordenador no sabe nada de metros ni cent&metros, de modo que el resultado ser&a 2@ en
ambos casos# Claro, esto es incorrecto, porque 1@ metros ms 1@ cent&metros sern 1@#1 metros, o bien 1@1@
cent&metros, pero en ning=n caso ser el 2@ que hemos obtenido# D2e qu( modo podr&amos solventar estoE
9ues una posibilidad ser&a crear una clase metros con un m(todo que se llamara ;umarCentimetros, de modo
que hiciera la suma correcta# ;in embargo, el uso de esta clase ser&a un poco +or!ado, ya que si queremos
sumar, lo suyo es que usemos el operador - en lugar de tener que invocar un m(todo espec&+ico#
9ues bien, aqu& es donde entra la sobrecarga de operadores# ;i puedo sobrecargar el operador - en la clase
metros para que haga una suma correcta independientemente de las unidades que se le sumen, habremos
resuelto el problema +acilitando enormemente la codi+icaci%n en el cliente# 5stoy pensando en algo como esto*
!etros m=ne< !etros(?()%
"entimetros c=ne< "entimetros(?()%
!etros Guma!etros=m2c%
"entimetros Guma"entimetros=c2m%
"onsole.,rite-ine(Guma!etros."antidad)%
"onsole.,rite-ine(Guma"entimetros."antidad)%
B que el resultado sea correcto, es decir, 1@#1 metros y 1@1@ cent&metros, sin necesidad de convertir
previamente los cent&metros a metros y viceversa# D:o vamos pillandoE D;&E Bien es cierto que esto resulta a=n
un tanto e)trao al tener que usar los constructores y la propiedad Cantidad, pero ms adelante veremos que
esto tambi(n tiene soluci%n# 9or ahora vamos a ver la sinta)is de un operador - sobrecargado*
\acceso] static Nombre"lase operator2(Kipo a\, Kipo b])
#
...
&
Veamos, en primer lugar el modi+icador de acceso en caso de que proceda, con la precauci%n de que el
modi+icador de acceso de un operador sobrecargado no puede ser de un mbito mayor que el de la propia
clase, es decir, si la clase es internal, el operador sobrecargado no puede ser public, puesto que su nivel de
acceso ser&a mayor que el de la clase que lo contiene# 2espu(s la palabra static# :%gicamente, tiene que ser
static puesto que el operador est sobrecargado para todos los ob"etos de la clase, y no para una sola instancia
en particular# 2espu(s el nombre de la clase, lo cual implica que hay que retornar un ob"eto de esta clase#
2espu(s la palabra [operator[ seguida del operador que deseamos sobrecargar 4o"o, deben ir "untos sin
separarlos con ning=n espacio en blanco8 y, a continuaci%n, la lista de argumentos entre par(ntesis# ;i el
operador a sobrecargar es unitario, necesitaremos un =nico argumento, y si es binario necesitaremos dos#
Veamos una primera apro)imaci%n a la clase 0etros para hacernos una idea ms clara y luego seguimos dando
ms detalles*
public class !etros
#
private double cantidad=(%
public !etros() #&
13@
public !etros(double cant)
#
this.cantidad=cant%
&
public double "antidad
#
$et
#
return this.cantidad%
&
set
#
this.cantidad=value%
&
&
public static !etros operator2(!etros m, "entimetros c)
#
!etros ret'alue=ne< !etros()%
ret'alue."antidad=m."antidad2c."antidad)?((%
return ret'alue%
&
&
Como ves, la sobrecarga del operador est aba"o del todo# DC%mo +ucionar&a estoE 9ues vamos a ver si
consigo e)plicrtelo bien para que lo entiendas* 2igamos que esto es un m(todo que se e"ecutar cuando el
compilador se tope con una suma de dos ob"etos, el primero de la clase 0etros y el segundo de la clase
Centimetros# 9or e"emplo, si [m[ es un ob"eto de la clase 0etros, y [c[ un ob"eto de la clase Centimetros, la
l&nea*
!etros Guma!etros=m2c%
Tar&a que se e"ecutara el m(todo anterior# 5s decir, se crea un nuevo ob"eto de la clase 0etros 4el ob"eto
retValue8# 5n su propiedad Cantidad se suman lo que valga la propiedad Cantidad del argumento m y la
cent(sima parte de la propiedad Cantidad del argumento c, retornando al +inal el ob"eto con la suma hecha,
ob"eto que se asignar&a, por lo tanto, a ;uma0etros# 5s decir, al haber sobrecargado el operador - en la clase
0etros, cuando el compilador se encuentra con la suma en cuesti%n lo que hace es lo que hayamos
implementado en el m(todo, en lugar de la suma normal# 2icho de otro modo, hemos modi+icado el
comportamiento del operador -#
Ahora bien, Dqu( ocurrir&a si nos encontramos con una resta en lugar de una sumaE
!etros Guma!etros=m7c%
131
9ues que el compilador dar&a un error, ya que el operador V no est sobrecargado y, por lo tanto, es incapa!
de e+ectuar la operaci%n# 9or lo tanto, para que se pudiera e+ectuar dicha resta habr&a que sobrecargar tambi(n
el operador V, de este modo*
public static !etros operator7(!etros m, "entimetros c)
#
!etros ret'alue=ne< !etros()%
ret'alue."antidad=m."antidad7c."antidad)?((%

return ret'alue%
&
Ba ves que es muy parecido, s%lo que esta ve! restamos en lugar de sumar# :o mismo habr&a que hacer con
los operadores \ y 6, para que tambi(n se hicieran correctamente las multiplicaciones y divisiones#
9or otro lado, Dc%mo reaccionar&a el compilador si se encuentra con esta otra l&neaE
"entimetros Guma"entimetros=c2m%
9ues volver&a a dar error, puesto que la sobrecarga en la clase 0etros a+ecta a las sumas cuando el primer
operando es de la clase 0etros y el segundo es de la clase Cent&metros# Ba s( que estars pensando en aadir
otra sobrecarga del operador - en la clase 0etros en la que pongas los cent&metros en el primer argumento y
los metros en el segundo# ;in embargo, eso seguir&a dando error, puesto que ;umaCentimetros no es un ob"eto
de la clase 0etros, sino de la clase Centimetros# 9or lo tanto, lo ms adecuado ser&a sobrecargar el operador
de ese modo pero en la clase Centimetros y no en la clase 0etros#
9uede que ahora est(s pensando tambi(n en sobrecargar el operador R, para que puedas asignar
directamente un ob"eto de la clase cent&metros a otro de la clase metros# ;in embargo el operador de
asignaci%n 4es decir, R8 no se puede sobrecargar# 0ala suerte### 5n lugar de esto podemos usar las
conversiones de+inidas, como vemos a continuaci%n#
5stos son los operadores que puedes sobrecargar con C#*
Pnitarios* -# V, G, 7, --, VV, true, +alse
Binarios* -, V, \, 6, A, X, W, a,YY, ZZ, RR, GR, Z, Y, ZR, YR
5+ectivamente, tal como supones, las comas las he puesto para separar los operadores, porque la coma no
se puede sobrecargar#
12.3 Con,ersiones de&inidas
;on tambi(n un elemento de lo ms =til# Tasta ahora hemos visto que con la sobrecarga de operadores
podemos hacer algo tan inveros&mil como por e"emplo sumar ob"etos de clases distintas e incompatibles# 9ues
bien, las conversiones de+inidas vienen a abundar un poco ms sobre estos conceptos, permitiendo hacer
compatibles tipos que antes no lo eran#
2ec&amos antes que el +ragmento de c%digo que pongo a continuaci%n segu&a siendo un tanto e)trao por
culpa de tener que usar constructores y propiedades*
!etros m=ne< !etros(?()%
"entimetros c=ne< "entimetros(?()%
!etros Guma!etros=m2c%
132
"entimetros Guma"entimetros=c2m%
"onsole.,rite-ine(Guma!etros."antidad)%
"onsole.,rite-ine(Guma"entimetros."antidad)%
:as conversiones de+inidas nos van a permitir mane"ar todo esto de un modo mucho ms natural, como
ver(is en este c%digo equivalente*
!etros m=(!etros) ?(%
"entimetros c=("entimetros) ?(%
!etros Guma!etros=m2c%
"entimetros Guma"entimetros=c2m%
"onsole.,rite-ine((double) Guma!etros)%
"onsole.,rite-ine((double) Guma"entimetros)%
Ahora todo parece mucho ms claro, Dno es ciertoE Como veis, ya no tenemos que complicarnos en usar
constructores y tampoco tenemos que usar la propiedad Cantidad para escribir su valor con <rite:ine# 5sto es
as& porque, tanto para la clase 0etros como para la clase Centimetros, hemos creado conversiones de+inidas
que nos han hecho compatibles esas clases con el tipo double# Mracias a esto podemos convertir a metros un
n=mero 4como en la primera l&nea8 y tambi(n podemos convertir a double un ob"eto de la clase 0etros 4dentro
de <rite:ine8# Veamos el c%digo de estas conversiones de+inidas en la clase 0etros*
public static eXplicit operator !etros(double cant)
#
!etros ret'alue=ne< !etros(cant)%
return ret'alue%
&
public static eXplicit operator double(!etros m)
#
return m."antidad%
&
Ni"aos bien* 5n la primera conversi%n de+inida, la conversi%n que estoy de+iniendo es 0etros, de modo que
pueda convertir en ob"etos de esta clase cualquier n=mero de tipo double con la sinta)is 40etros8 numero# 5n la
segunda lo que hago es precisamente lo contrario, esto es, de+ino la conversi%n double para convertir en datos
de este tipo ob"etos de la clase 0etro, mediante la sinta)is 4double8 1b"eto0etros# 9or este motivo, en la
primera conversi%n de+inida el argumento es de tipo double, y en la segundo es de tipo 0etros# 9or otra parte, la
palabra e)plicit hace que sea obligatorio usar el operador de conversi%n para convertir entre estos dos tipos# 5s
decir, no se puede asignar un valor de tipo double a un ob"eto de la clase 0etros sin usar el operador de
conversi%n 40etros8# Ahora bien, tambi(n podr&amos de+inir estas conversiones como impl&citas, de modo que el
operador de conversi%n no +uera necesario# 9ara ello bastar&a con poner la palabra implicit en lugar de e)plicit,
esto es, as&*
public static implicit operator !etros(double cant)
#
!etros ret'alue=ne< !etros(cant)%
133
return ret'alue%
&
public static implicit operator double(!etros m)
#
return m."antidad%
&
Con lo que el resultado todav&a es mucho ms mane"able# Ni"aos en c%mo podr&amos de"ar +inalmente el
c%digo cliente de las clases 0etros y Centimetros*
!etros m=?(%
"entimetros c=?(%
!etros Guma!etros=m2c%
"entimetros Guma"entimetros=c2m%
"onsole.,rite-ine(Guma!etros)%
"onsole.,rite-ine(Guma"entimetros)%
Ahora veis que ni siquiera hemos necesitado hacer las conversiones e)pl&citas, ya que las conversiones
de+inidas en ambas clases las hemos hecho impl&citas con la palabra clave implicit en lugar de e)plicit# DIue
por qu( se puede asignar 1@, que es de tipo int al no tener el su+i"o 2, si la conversi%n de+inida est creada
solamente para el tipo doubleE 9ues, sencillamente, porque el tipo double es compatible con el tipo int, de modo
que basta con hacer la conversi%n de+inida con el tipo double para que sirva con todos los tipos num(ricos#
Antes de terminar a=n nos queda una cosilla ms# Dqu( pasar&a si intentamos asignar un ob"eto de la clase
cent&metros a otro de la clase metros y viceversaE
!etros c:n!etros=c%
"entimetros m:n"entimetros=m%
9ues, nuevamente, el compilador generar&a otro error# Antes de poder hacer eso deber&amos crear tambi(n
las conversiones de+inidas pertinentes en 0etros y Cent&metros para hacerlos compatibles y, tambi(n, para que
se asignen los valores adecuadamente# Vamos a verlo*
5n la clase 0etros habr&a que escribir*
public static implicit operator !etros("entimetros c)
#
!etros ret'alue=ne< !etros()%
ret'alue."antidad=c."antidad)?((%
return ret'alue%
&
b en la clase "ent*metros, lo mismo pero al rev8s9
public static implicit operator "entimetros(!etros m)
#
"entimetros ret'alue=ne< "entimetros()%
13>
ret'alue."antidad=m."antidadH?((%
return ret'alue%
&
Como veis, no basta con escribir las conversiones para hacerlos compatibles, sino que tambi(n hay que
escribirlas correctamente para que se asignen valores adecuados, es decir, si a [m[ 4que es de tipo 0etros8 le
asignamos 1@ cent&metros, [m[ tiene que valer, necesariamente, @#1 metros#
9or =ltimo, ser&a bueno que no os olvidarais de otro pequeo detalle, y es reescribir el m(todo 'o;tring
4recordad que este m(todo se hereda siempre de ;ystem#1b"ect8# ;oy consciente de que a=n no hemos visto la
herencia, pero bueno, dicho m(todo tendr&a que ir as& tanto en la clase 0etros como en la clase Cent&metros*
public override strin$ KoGtrin$()
#
return this.cantidad.KoGtrin$()%
&
$o quiero dar por terminada esta entrega sin aconse"aros en cuanto a esto de la sobrecarga de operadores
y las conversiones de+inidas, y es que hay algunos principios que siempre es bueno respetar*
Psa la sobrecarga de operadores y las conversiones de+inidas =nicamente cuando el resultado +inal sea ms
natural e intuitivo, no por el mero hecho de que sabes hacerlo# 9or e"emplo, en una clase Alumno podr&as
sobrecargar el operador - por e"emplo para cambiar su nombre# Ciertamente se puede hacer, pero
evidentemente el resultado ser muy con+uso, porque no tiene sentido l%gico alguno sumar una cadena a un
ob"eto de clase Alumno para modi+icar una de sus propiedades# 2icho de otro modo, es como el vino* mientras
su uso normal es bueno, el abuso es muy malo#
9or otra parte tienes que pensar en un detalle que es muy importante* si por medio de la sobrecarga de
operadores y las conversiones de+inidas haces que dos tipos sean compatibles, tienes que intentar que sean
compatibles a todos los niveles posibles# 9or e"emplo, con las clases 0etros y Centimetros, habr&a que
sobrecargar tambi(n los operadores 6, \, A, Z, ZR, Y, YR, RR y GR para hacer posibles las divisiones,
multiplicaciones, el clculo de restos y las comparaciones 4es decir, la comparaci%n 1 metro RR 1@@ cent&metros
deber&a retornar true8#
'e voy a proponer un par de e"ercicios para esta entrega# 5s probable que con ello te ponga en alg=n apuro,
pero recuerda que el m(todo pruebaVerror es el me"or modo de aprender#
12.4 #jercicio 4
Pna anotaci%n para aquellos que segu&s el curso desde +uera de 5uropa* 5n las pistas de este e"ercicio
e)plico algunos conceptos con los que qui! no est(is +amiliari!ados en vuestro pa&s#
$ecesitamos una clase para almacenar los datos de una +actura# 2ichos datos son* $ombre del cliente,
tel(+ono, direcci%n, poblaci%n, provincia, c%digo postal, $CN o CCN y porcenta"e de CVA# 9or otra parte tienes que
tener presente que en una misma +actura puede haber una o varias l&neas de detalle con los siguientes datos*
Cantidad, descripci%n, precio unitario e importe# Psa un indi!ador para acceder a cada una de estas l&neas de
detalle# 5sta clase debe o+recer, adems, propiedades que devuelvan la base imponible, la cuota de CVA y el
total a pagar# 5scribid tambi(n un m(todo 0ain cliente de esta clase que demuestre que +unciona
correctamente#
;upongo que ya habrs deducido que para que la clase Nactura cumpla los requisitos que te pido tendrs
que construir tambi(n una clase 2etalle# 9ues bien, te propongo tambi(n que sobrecargues el operador - para
que puedas sumar ob"etos de la clase 2etalle a ob"etos de la clase Nactura# 1"o, en este caso solamente
queremos hacer posible la suma Nactura-2etalle, nada ms#
13J
12.4.1 Pistas para el ejercicio 4 3#ntrega 124
Tay algunos datos para la clase Nactura que pueden resultarte e)traos si ests siguiendo el curso desde
alg=n pa&s +uera de 5spaa o 5uropa, de modo que vamos a e)plicarlos*
5l campo 4o propiedad, t= sabrs###8 para $CN o CCN ha de ser el mismo# 5l $CN es un n=mero de
identi+icaci%n +iscal de una persona +&sica, y el CCN es el equivalente para una empresa# 2ebe ser de tipo string
porque tanto en uno como en otro hay siempre una letra#
5l CVA es un impuesto 4el Cmpuesto sobre el Valor Aadido8 que se aplica a todos los bienes de consumo# 5n
5spaa actualmente el porcenta"e a aplicar de CVA para la mayor&a de los productos el del 1LA# 5l CVA de una
+actura, por lo tanto, ser&a el 1LA del total de dicha +actura# A pesar de que algunas actividades estn e)entas
de CVA, vamos a suponer para nuestro e"ercicio que no hay e)cepciones, y que todos los productos estn
gravados con un 1LA#
:a base imponible es la suma del precio de todos los productos que constan en la +actura, sin haberle
aplicado a=n el CVA#
:a cuota de CVA ser el 1LA de la base imponible#
5l total de la +actura, por lo tanto, es la suma de la base imponible ms la cuota#
12.4.2 5esolucin del ejercicio
Bien, aqu& lo tienes# DC%mo que es largoE Bueno, puede que s&, si lo comparas con lo que hemos venido
haciendo hasta ahora, pero no es nada comparado con un programa de verdad###
usin$ G+stem%
usin$ G+stem."ollections%
namespace 3acturas
#
public class 3actura
#
private strin$ nombre=OO%
private strin$ ape?=OO%
private strin$ apeD=OO%
private strin$ direccion=OO%
private strin$ poblacion=OO%
private strin$ provincia=OO%
private strin$ codpostal=OO%
private strin$ ni4=OO%
private b+te iva=(%
private Detalles detalles=ne< Detalles()%
public strin$ Nombre
#
$et
13L
#
return this.nombre%
&
set
#
this.nombre=value%
&
&
public strin$ Ape?
#
$et
#
return this.ape?%
&
set
#
this.ape?=value%
&
&
public strin$ ApeD
#
$et
#
return this.apeD%
&
set
#
this.apeD=value%
&
&
public strin$ Direccion
#
$et
#
13O
return this.direccion%
&
set
#
this.direccion=value%
&
&
public strin$ Poblacion
#
$et
#
return this.poblacion%
&
set
#
this.poblacion=value%
&
&
public strin$ Provincia
#
$et
#
return this.provincia%
&
set
#
this.provincia=value%
&
&
public strin$ "odPostal
#
$et
#
return this.codpostal%
13S
&
set
#
this.codpostal=value%
&
&
public strin$ N/3
#
$et
#
return this.ni4%
&
set
#
this.ni4=value%
&
&

public b+te /'A
#
$et
#
return this.iva%
&
set
#
this.iva=value%
&
&
public Detalles Detalles
#
$et
#
return this.detalles%
13?
&
&

public static 3actura operator2(3actura 4, Detalle d)
#
3actura ret=4."lone()%

ret.detalles\ret.detalles."ount]=d%

return ret%
&

public decimal ase/mponible
#
$et
#
decimal ret=(%

4or (int i=(%iVthis.detalles."ount%i22)
ret2=this.Detalles\i]./mporte%

return ret%
&
&
public decimal "uota
#
$et
#
return this.ase/mponibleHthis.iva)?((%
&
&
public decimal Kotal
#
$et
1>@
#
return this.ase/mponible2this."uota%
&
&
public 3actura "lone()
#
3actura ret=ne< 3actura()%
ret.nombre=this.nombre%
ret.ape?=this.ape?%
ret.apeD=this.apeD%
ret.direccion=this.direccion%
ret.poblacion=this.poblacion%
ret.provincia=this.provincia%
ret.codpostal=this.codpostal%
ret.ni4=this.ni4%
ret.iva=this.iva%
4or (int i=(% iVthis.detalles."ount%i22)
ret.detalles\i]=this.detalles\i]%
return ret%
&
public override strin$ KoGtrin$()
#
strin$ ln=OanO%
return this.nombre2O O2this.ape?2O O2this.apeD2ln2
this.detalles.KoGtrin$()2ln2OAG: /!P^N/-:9 O2ln2
this.ase/mponible.KoGtrin$()2ln2O"L^KA9 O2
this."uota.KoGtrin$()2ln2OK^KA- D: -A 3A"KL6A9 O
2this.Kotal.KoGtrin$()2ln%
&
&
public class Detalles
#
Arra+-ist det=ne< Arra+-ist()%
public int "ount
1>1
#
$et
#
return det."ount%
&
&
public Detalle this\int idX]
#
$et
#
i4 (this.det."ount==( YY idXJ=this.det."ount YY idXV()
thro< ne< :Xception(ONo se encuentra en la listaO)%
else
return (Detalle) this.det\idX]%
&
set
#
i4 (idXJthis.det."ount YY idXV()
thro< ne< :Xception(ONo se puede asi$nar a este elementoO)%
else
#
i4 (value==null)
this.det.6emove(this.det\idX])%
else
this.det.Add(value)%
&
&
&
public override strin$ KoGtrin$()
#
strin$ ret=OO%
strin$ ln=OanO%

4or (int i=(%iVthis."ount%i22)
1>2
ret2=this\i].KoGtrin$()2ln%

return ret%
&
&
public class Detalle
#
private int cantidad%
private strin$ descripcion%
private decimal precio%
public Detalle(int cantidad, strin$ descripcion, decimal precio)
#
this.cantidad=cantidad%
this.descripcion=descripcion%
this.precio=precio%
&
public int "antidad
#
$et
#
return this.cantidad%
&
set
#
this.cantidad=value%
&
&
public strin$ Descripcion
#
$et
#
return this.descripcion%
&
set
1>3
#
this.descripcion=value%
&
&
public decimal Precio
#
$et
#
return this.precio%
&
set
#
this.precio=value%
&
&
public decimal /mporte
#
$et
#
return (decimal) this.cantidad H this.precio%
&
&
public override strin$ KoGtrin$()
#
return this.cantidad.KoGtrin$()2O O2
this.descripcion2O O2this.precio.KoGtrin$()2O O2
this./mporte.KoGtrin$()%
&
&
public class 3acturasApp
#
static void !ain(strin$\] ar$s)
#
b+te opcion=(%
1>>
Detalle d%
int idX%
3actura 4=ne< 3actura()%
"onsole.,rite(ODame el nombre9 O)%
4.Nombre="onsole.6ead-ine()%
"onsole.,rite(ODame el primer apellido9 O)%
4.Ape?="onsole.6ead-ine()%
"onsole.,rite(ODame el se$undo apellido9 O)%
4.ApeD="onsole.6ead-ine()%
"onsole.,rite(ODame la direccin9 O)%
4.Direccion="onsole.6ead-ine()%
"onsole.,rite(ODame la poblacin9 O)%
4.Poblacion="onsole.6ead-ine()%
"onsole.,rite(ODame la provincia9 O)%
4.Provincia="onsole.6ead-ine()%
"onsole.,rite(ODame el cdi$o postal9 O)%
4."odPostal="onsole.6ead-ine()%
"onsole.,rite(ODame el N/3)"/39 O)%
4.N/3="onsole.6ead-ine()%
do
#
tr+
#
"onsole.,rite(ODame el /'A9 O)%
4./'A=+te.Parse("onsole.6ead-ine())%
&
catch
#
continue%
&
& <hile (4./'AV=()%
<hile (opcionS=A)
#
opcion=!enu(4)%
1>J
s<itch (opcion)
#
case ?9
"onsole.,rite-ine()%
d=NuevoDetalle()%
42=d%
"onsole.,rite-ine()%
brea0%
case D9
"onsole.,rite-ine()%
i4 (4.Detalles."ount==() continue%
"onsole.,rite(ODame el *ndice a eliminar9 O)%
idX=/nt=D.Parse("onsole.6ead-ine())%
4.Detalles\idX]=null%
"onsole.,rite-ine()%
brea0%
case =9
"onsole.,rite-ine()%
"onsole.,rite-ine(4.KoGtrin$())%
"onsole.,rite-ine()%
brea0%
&
&
&
static b+te !enu(3actura 4)
#
b+te opcion=(%
"onsole.,rite-ine(O-a 4actura consta de #(& l*neas de detalleO,4.Detalles."ount)%
"onsole.,rite-ine(O?. Aeadir una l*nea de detalleO)%
"onsole.,rite-ine(OD. orrar una l*nea de detalleO)%
"onsole.,rite-ine(O=. 'er 4actura actualO)%
"onsole.,rite-ine(OA. 3inalizarO)%
"onsole.,rite(O:li$e una opcin9 O)%
do
1>L
#
tr+
#
opcion=+te.Parse("onsole.6ead-ine())%
&
catch
#
continue%
&
& <hile (opcionV=( YY opcion JA)%
return opcion%
&
static Detalle NuevoDetalle()
#
"onsole.,rite(ODame la descripcin9 O)%
strin$ descripcion="onsole.6ead-ine()%
"onsole.,rite(ODame la cantidad9 O)%
int cantidad=/nt=D.Parse("onsole.6ead-ine())%
"onsole.,rite(ODame el precio9 O)%
decimal precio=Decimal.Parse("onsole.6ead-ine())%
Detalle d=ne< Detalle(cantidad,descripcion,precio)%
return d%
&
&
&
12.- #jercicio -
Cntenta construir dos clases* la clase 5uro y la clase 9eseta 4la peseta era la antigua moneda o+icial de
5spaa antes de ser reempla!ada por el 5uro8# 'ienes que hacer que los ob"etos de estas clases se puedan
sumar, restar, comparar, incrementar y disminuir con total normalidad como si +ueran tipos num(ricos, teniendo
presente que 1 5uro - 1LL#3SL pesetasR2 euros# Adems, tienen que ser compatibles entre s& y tambi(n con el
tipo double# Hecuerda que 1 5uro R 1LL#3SL pesetas# 9ara este e"ercicio no hay pistas#
1' %rig6sima entrega (Estructuras< +Js sobre las clases< 3erencia e
Inter8aces.!
9or +in nos vamos acercando a las cuestiones ms interesantes de los lengua"es #$5', que son,
indudablemente, las enormes venta"as y la gran +le)ibilidad que nos o+rece el hecho de que dichos lengua"es
sean orientados a ob"etos# Bo s( que esto al principio parece una a+irmaci%n un tanto abstracta, pero no me
1>O
cabe la menor duda de que aprenders a apreciarlo en su verdadera dimensi%n a medida que vayas dominando
todas las t(cnicas que empe!ar( a e)poner a partir de ahora#
Ciertamente, todav&a nos queda por ver algunas cosas no menos interesantes que no estn tan
estrechamente relacionadas con la herencia, pero creo que, despu(s de doce entregas, podemos ya
adentrarnos en la programaci%n orientada a ob"etos con C# propiamente dicha sin temor a que os resulte tan
di+&cil o +rustrante que os haga abandonar# Ba habr tiempo para re+le)i%n, atributos, delegados, mane"adores de
eventos, e"ecuci%n multiVhilo, punteros y dems parabienes de la programaci%n en C##
2ado que estas t(cnicas nos van a obligar a rede+inir o ampliar 4ligeramente, eso s&8 algunos de los
conceptos que hab&amos asentado hasta ahora, ya os aviso de que esta parte va a ocuparnos varias entregas,
aunque ahora no puedo precisaros cuntas e)actamente#
13.1 #structuras
D0s retrasosE Tombre, pues### s&### lamentablemente, s&# Antes de empe!ar a heredar y disear inter+aces y
clases abstractas y cosas de esas, tengo que hablar de las estructuras, porque son tambi(n una parte muy
importante del lengua"e C# 4y VB#$5', y C-- gestionado###8#
Tasta ahora nos hemos hartado de escribir clases y clases, con sus campos, sus propiedades, sus m(todos,
sus constructores y destructores, y hemos visto tambi(n c%mo se pod&an instanciar ob"etos de estas clases# 5n
lo que no nos hemos +i"ado a=n en pro+undidad, sin embargo, es en qu( es lo que esto implica dentro de la
+iloso+&a de #$5'#
5n la entrega 3 di+erencibamos entre tipos valor y tipos re+erencia, diciendo que los primeros representaban
un valor y se almacenaban en la pila, y los segundos representaban una re+erencia, esto es, devolv&an
internamente un puntero al comien!o del ob"eto que, recordemos, se alo"aba en el mont%n# 9ues bien, las
clases representan tipos re+erencia, es decir, cuando instanciamos un ob"eto de una determinada clase, lo que
hacemos es crear un ob"eto en el mont%n, de modo que la variable devuelve internamente un puntero al
comien!o de dicho ob"eto# 5ntonces, Dc%mo podemos crear un tipo valorE 9ues con las estructuras# DB no
podemos almacenar un valor en una claseE 9ues s&, poder### s& que podemos# 2e hecho, eso era lo que os
ped&a en el e"ericio J 4en la entrega anterior8, ni ms ni menos#
;in embargo, usar clases para almacenar algo que realmente son valores nos va a restar e+iciencia y
tambi(n +uncionarn de un modo distinto 4y ms engorroso8# Hestarn e+iciencia puesto que, necesariamente,
tendr que ser creado como un ob"eto en el mont%n, cuyo acceso es ms lento que la pila debido a que a (sta
se accede directamente, mientras que para acceder al mont%n hay que resolver previamente la re+erencia#
Adems, dicho ob"eto tambi(n tendr que ser tenido en cuenta por el recolector de basura# 9or otra parte,
operaciones muy sencillas en principio, como podr&a ser una simple asignaci%n de una variable a otra, se
complicarn, dado que dicha asignaci%n no har&a otra cosa que crear una doble re+erencia, y no una copia del
ob"eto, lo cual, seguramente, nos obligar&a a implementar la inter+ace CCloneable, y hacer asignaciones de lo
ms antinaturales# Ah, y no os preocup(is por eso de implementar inter+aces, que lo veremos dentro de muy
poco# 9or e"emplo, supongamos que hemos diseado una clase 0oneda# 9ues bien, sin entrar en los detalles
de su implementaci%n, veamos c%mo habr&a que hacer una asignaci%n para crear una copia de un ob"eto de
esta clase*
!oneda m
m=ne< !oneda(?()%
!oneda mD=m."lone()%
Como pod(is ver, resulta bastante raro, no por el hecho de invocar el m(todo Clone 4pobrecito, que no tiene
culpa de nada###8, sino por tener que invocarlo para crear una simple copia de su valor en otra variable, puesto
que la clase 0oneda puede ser considerada, per+ectamente, como un valor# Ahora observa el siguiente c%digo y
dime cul te gusta ms*
1>S
!oneda m
m=?(%
!oneda mD=m%
$o s( si coincidirs conmigo, pero a m& me gusta bastante ms este =ltimo# DC%mo lo hemos conseguidoE
9ues, obviamente, utili!ando una estructura en lugar de una clase# Vamos a echarle un vista!o a la estructura
0oneda*
public struct !oneda
#
private double valor%
public static implicit operator !oneda(double d)
#
!oneda m%
m.valor=!ath.6ound(d,D)%
return m%
&
public static implicit operator double(!oneda m)
#
return m.valor%
&
public static !oneda operator22(!oneda m)
#
m.valor22%
return m%
&
public static !oneda operator77(!oneda m)
#
m.valor77%
return m%
&
public override strin$ KoGtrin$()
#
return this.valor.KoGtrin$()%
&
&
1>?
Como pod(is ver se parece enormemente a una clase, salvo que aqu& hemos utili!ado la palabra struct en
lugar de class# :a mayor&a de los conceptos que hemos visto hasta el momento con las clases nos sirven
tambi(n para las estructuras# 9ero, o"o, digo la mayor&a, puesto que hay cosas habituales en las clases que no
se pueden hacer con las estructuras*
;e puede escribir constructores, pero estos han de tener argumentos# 2icho de otro modo, no se puede
escribir un constructor sin argumentos para una estructura#
:%gicamente, al tratarse de un tipo valor que se almacena en la pila, las estructuras no admiten destructores#
$o soportan herencia, lo cual implica que no pueden ser utili!adas como clases base ni como clases
derivadas# $o obstante, s& pueden implementar inter+aces, y lo hacen igual que las clases 4tranquilos, que
veremos esto muy pronto8#
:os campos de una estructura no pueden ser iniciali!ados en la declaraci%n# 1 sea, por e"emplo, no vale
decir int aR1@K porque tendr&amos un error de compilaci%n#
:as estructuras, si no se especi+ican los modi+icadores re+ o out, se pasan por valor, mientras que las clases
se pasan siempre por re+erencia#
$o es necesario instanciar una estructura para poder usar sus miembros# N&"ate en el e"emplo, y vers que,
en el segundo +ragmento de c%digo no hemos instanciado el ob"eto m#
13.2 as clases en pro&undidad
5n la introducci%n dimos ya una de+inici%n de lo que era una clase, y tambi(n me preocup( por estableceros
claramente la di+erencia entre clase y ob"eto# Toy vamos a pro+undi!ar un poquito ms en este concepto, pues
es algo que nos ayudar much&simo en el +uturo a la hora de disear acertadamente una "erarqu&a de clases
con alguna relaci%n de herencia#
Vamos a re+rescar, primeramente, el concepto de clase* dec&amos que una clase era la plantilla a partir de la
cual pod&amos crear ob"etos# 'odos los ob"etos de la misma clase comparten la inter+ace 4es decir, m(todos,
campos y propiedades8, pero los datos que contiene cada ob"eto en sus campos y propiedades pueden di+erir#
Algunos autores dan otras de+iniciones, ms o menos apro)imadas# 9or e"emplo, se puede decir tambi(n que
una clase es algo a lo que se le puede poner un nombre 4un coche, un avi%n, un boli, una mesa, una
barbacoa### con su choricito###, y su pancetita###, y sus chuletillas### y### eeeeeh, bueno### etc#8#
9or su misi%n espec&+ica, podemos dividir las clases 4casi ser&a me"or decir los tipos, porque as& hablamos
tanto de clases como de estructuras8 en tres grandes grupos*
'ipos que o+recen un valor* son aquellos en los que lo =nico que nos importa es el valor que contienen#
9ongamos, por e"emplo, que queremos pagar un art&culo que ha costado 2@ j# 5n realidad, nos dar igual usar
un billete de 2@ u otro, tambi(n de 2@# :o importante es pagar los 2@ j, pero no el billete de 2@ que utilicemos
para ello# 9or lo tanto, los ob"etos cuyo =nico inter(s es su valor son per+ectamente intercambiables, y suelen
estar implementados como estructuras 4Cnt32, Cnt1L, 2ouble###8#
'ipos que o+recen un servicio* se puede incluir aqu& los tipos cuyo =nico inter(s es el servicio que o+recen#
9or e"emplo, cuando vamos a comprar el pan, lo ms importante es que nos lo vendan, pero qui(n nos lo venda
nos trae sin cuidado 4salvo que sea alg=n guarreras con las manos sucias, pero bueno, ese es otro cantar###8#
9or lo tanto, nos interesa el servicio, pero no qui(n haga ese servicio# Pn e"emplo claro de tipo que o+rece un
servicio es la clase Console, pues no se usan nunca instancias de ella 4de hecho, no se puede instanciar8, sino
que, simplemente, usamos sus m(todos static para aprovechar los servicios que nos o+rece#
'ipos de identidad* en este caso, lo ms relevante no es la in+ormaci%n que contienen ni el servicio que
prestan, sino cul es el ob"eto que nos o+rece sus datos y servicios# 9or e"emplo, en una +actura lo ms
importante es la +actura en s&, es decir, no es lo mismo la +actura del gas del mes pasado que la de este mes, o
la del tel(+ono de hace dos aos# 9or este motivo decimos que son tipos de identidad, porque lo ms relevante
es el ob"eto en s&#
1J@
Ciertamente puede haber tipos que, seg=n los miembros que se e)aminen, podr&an incluirse
simultneamente en dos grupos, o, incluso, en los tres# 9or e"emplo, un tipo 'ar"eta2eCr(dito ser&a, claramente,
un tipo de identidad# ;in embargo, tambi(n podr&a o+recer alg=n servicio que no dependiera de la identidad del
ob"eto, como calcular la tasa anual que cobra una determinada entidad por ella, dado que esta in+ormaci%n ser
com=n para todas las tar"etas emitidas por dicha entidad#
13.3 <erencia
'ambi(n e)plicamos el concepto de herencia en la introducci%n, aunque hoy pro+undi!aremos un poquito
ms# 2ec&amos, pues, que gracias a la herencia pod&amos de+inir clases nuevas basadas en clases antiguas,
aadi(ndoles ms datos o +uncionalidad# $o quiere decir esto que podamos utili!ar la herencia de cualquier
manera, sin orden ni concierto 4bueno, lo que es poder### podemos, pero no debemos8# Meneralmente, la
relaci%n de herencia debe basarse en una relaci%n "errquica de con"untos y subcon"untos ms pequeos
incluidos en aquellos# As&, podemos decir que un dispositivo de reproducci%n de v&deo sirve para reproducir
v&deo# ;in embargo, un reproductor de VT; no es igual que un reproductor de 2V2, a pesar de que ambos son
subcon"untos de un con"unto mayor, es decir, los dispositivos de reproducci%n de video# 5n otras palabras, tanto
los reproductores VT; como los reproductores de 2V2 son tambi(n, todos ellos, dispositivos de reproducci%n
de v&deo# 9or lo tanto, podemos establecer una clase base que determine e implemente cules sern los
miembros y el comportamiento o una parte del comportamiento com=n de todos los dispositivos de
reproducci%n de v&deo 4reproducir, parar, pausa, e)pulsar###8, y esta clase servir de base a las clases de
reproductor VT; y reproductor de 2V2, que derivarn sus miembros de la clase base# ;in embargo, alguna de
las clases derivadas puede aadir algo espec&+ico de ella que no tuviera sentido en otro subcon"unto distinto#
9or e"emplo, la clase del reproductor de VT; necesitar incluir tambi(n un m(todo para rebobinar la cinta, cosa
que no tendr&a sentido con un reproductor de 2V2#
Vamos a implementar estas tres clases para que as& os hagis una idea ms clara de lo que estamos
e)plicando# Comen!aremos con la clase 2ispositivoVideo*
public enum :stado6eproductor
#
Parado,
:nPausa,
6eproduciendo,
Gin!edio
&
class Dispositivo'ideo
#
protected bool reproduciendo=4alse%
protected bool pausado=4alse%
protected bool medio=4alse%
public virtual void 6eproducir()
#
i4 (Smedio)
"onsole.,rite-ine(O/nserte un medioO)%
else i4 (reproduciendo)
1J1
"onsole.,rite-ine(Oba estaba reproduciendoO)%
else
#
"onsole.,rite-ine(O6eproduciendo v*deoO)%
reproduciendo=true%
&
&
public virtual void Detener()
#
i4 (Smedio)
"onsole.,rite-ine(O/nserte un medioO)%
else i4 (reproduciendo)
#
"onsole.,rite-ine(O6eproduccin detenidaO)%
reproduciendo=4alse%
pausado=4alse%
&
else
"onsole.,rite-ine(Oba estaba parado, leeeO)%
&
public virtual void Pausa()
#
i4 (Smedio)
"onsole.,rite-ine(O/nserte un medioO)%
else i4 (reproduciendo WW Spausado)
#
"onsole.,rite-ine(O6eproduccin en pausaO)%
pausado=true%
&
else i4(reproduciendo WW pausado)
#
"onsole.,rite-ine(O6eproduccin reanudadaO)%
pausado=4alse%
&
1J2
else
"onsole.,rite-ine(ONo se puede pausar. :stE paradoO)%
&
public virtual void /ntroducir!edio()
#
i4 (medio)
"onsole.,rite-ine(OAntes debe eXpulsar el medio actualO)%
else
#
"onsole.,rite-ine(O!edio introducidoO)%
medio=true%
&
&
public virtual void :Xpulsar()
#
i4 (Smedio)
"onsole.,rite-ine(O/nserte un medioO)%
else
#
medio=4alse%
reproduciendo=4alse%
pausado=4alse%
"onsole.,rite-ine(O:Xpulsando medioO)%
&
&
public :stado6eproductor :stado
#
$et
#
i4 (Smedio)
return :stado6eproductor.Gin!edio%
else i4 (pausado)
return :stado6eproductor.:nPausa%
else i4 (reproduciendo)
1J3
return :stado6eproductor.6eproduciendo%
else
return :stado6eproductor.Parado%
&
&
&
9odr&amos decir que todos los dispositivos de v&deo incorporan este comportamiento que hemos
implementado Dc%mo, que todav&a no hemos visto eso de enumE 9ues vaya despiste### ;i es que### Bueno, os
lo e)plico brevemente que es muy +cil 4ms adelante lo veremos con ms detalle8# :a instrucci%n enum sirve
para agrupar constantes# 5n este caso, hemos agrupado cuatro constantes 49arado, 5n9ausa, Heproduciendo
y ;in0edio8 en un grupo llamado 5stadoHeproductor, de +orma que podemos utili!ar un c%digo mucho ms +cil
de leer que usando n=meros directamente# :a sinta)is es la que veis, ni ms ni menos#
Bueno, a lo que &bamos# 2ec&amos que esta clase incorporaba todo aquello que, como m&nimo, necesitaba
un dispositivo de reproducci%n de v&deo# 9or lo tanto, podr&amos usar esta clase como base para construir otras
clases de reproductores de v&deo ms espec&+icas, como un reproductor de VT; o un reproductor de 2V2#
Temos de +i"arnos en algo importante* hay tres campos con el modi+icador de acceso protected, el cual casi no
hab&amos usado hasta este momento# Aunque ya e)plicamos los modi+icadores de acceso en la entrega 3, os lo
recuerdo* protected hace que el miembro en cuesti%n sea visible en las clases derivadas, pero no en el cliente#
9or lo tanto, estos tres campos los tendremos disponibles en cualquier clase que derivemos de la clase
2ispositivoVideo# 9or otra parte, hemos aadido la palabra virtual en la declaraci%n de cada m(todo# D9ara
qu(E Bien, si hacemos esto, permitimos que las clases derivadas puedan modi+icar la implementaci%n de estos
m(todos# ;i no lo hacen, obviamente, se e"ecutar el c%digo de la clase base# :o veremos me"or si escribimos
ahora las clases derivadas# Comen!aremos con la clase 2ispositivoVT;*
class Dispositivo'NG9Dispositivo'ideo
#
public void 6ebobinar()
#
i4 (Smedio)
"onsole.,rite-ine(O/ntroduzca una cintaO)%
else
"onsole.,rite-ine(O"inta rebobinadaO)%
&
public override void /ntroducir!edio()
#
i4 (medio)
"onsole.,rite-ine(OAntes debe eXpulsar la cinta actualO)%
else
#
"onsole.,rite-ine(O"inta introducidaO)%
1J>
medio=true%
&
&
&
Como veis, esta clase es mucho ms corta, porque =nicamente tiene que aadir o modi+icar aquello de la
clase base que no le sirve# 5l modo de indicar que queremos heredar los miembros de una clase es aadiendo
dos puntos al +inal del nombre de la clase y poner a continuaci%n el nombre de la clase base# 5n nuestra clase
2ispositivoVT; hemos aadido un m(todo llamado Hebobinar que no estaba en la clase base y hemos
sobreescrito el m(todo Cntroducir0edio que s& se encontraba en la clase base# DC%mo que es igualE Anda, +&"ate
en las cadenas que escribe en la consola, y luego me dices si es igual o no lo es### 9ara poder sobreescribir un
m(todo virtual de la clase base hay que utili!ar la palabra override, como veis aqu&# 5l resto de miembros de la
clase base nos sirve, de modo que no tenemos que tocarlos#
Vamos ahora con la clase 2ispositivo2V2*
class DispositivoD'D9Dispositivo'ideo
#
public void /rA(int escena)
#
i4 (Smedio)
"onsole.,rite-ine(O/nserte un medioO)%
else
#
reproduciendo=true%
pausado=true%
&
&
&
5n este caso, no hemos tocado ninguno de los m(todos de la clase base, y hemos aadido el m(todo CrA,
para saltar a una escena determinada# Como pod(is ver, la herencia nos ayuda mucho porque no tenemos que
repetir el mismo c%digo una y otra ve!# Ahora escribiremos un program&n que use estas clases, a ver qu( tal*
class 'ideosApp
#
static void !ain()
#
"onsole.,rite-ine(O'amos a crear un dispositivo $en8ricoO)%
Dispositivo'ideo video=ne< Dispositivo'ideo()%
"onsole.,rite-ine()%
Acciones(video)%
1JJ
"onsole.,rite-ine()%
"onsole.,rite-ine(OAhora crearemos un reproductor 'NGO)%
video=ne< Dispositivo'NG()%
Acciones(video)%
"onsole.,rite-ine()%
"onsole.,rite-ine(OPara terminar crearemos un reproductor D'DO)%
video=ne< DispositivoD'D()%
Acciones(video)%
"onsole.6ead-ine()%
&
static void Acciones(Dispositivo'ideo video)
#
"onsole.,rite-ine()%
"onsole.,rite-ine(OPulsa intro para invocar /ntroducir!edioO)%
"onsole.6ead-ine()%
video./ntroducir!edio()%
:stado(video)%
"onsole.,rite-ine()%
"onsole.,rite-ine(OPulsa intro para invocar 6eproducirO)%
"onsole.6ead-ine()%
video.6eproducir()%
:stado(video)%
"onsole.,rite-ine()%
"onsole.,rite-ine(OPulsa intro para invocar PausaO)%
"onsole.6ead-ine()%
video.Pausa()%
:stado(video)%
"onsole.,rite-ine()%
"onsole.,rite-ine(OPulsa intro para invocar 6eproducirO)%
"onsole.6ead-ine()%
video.6eproducir()%
:stado(video)%
"onsole.,rite-ine()%
"onsole.,rite-ine(OPulsa intro para invocar PausaO)%
1JL
"onsole.6ead-ine()%
video.Pausa()%
:stado(video)%
"onsole.,rite-ine()%
"onsole.,rite-ine(OPulsa intro para invocar DetenerO)%
"onsole.6ead-ine()%
video.Detener()%
:stado(video)%
"onsole.,rite-ine()%
"onsole.,rite-ine(OPulsa intro para invocar PausaO)%
"onsole.6ead-ine()%
video.Pausa()%
:stado(video)%
"onsole.,rite-ine()%
"onsole.,rite-ine(OPulsa intro para invocar :XpulsarO)%
"onsole.6ead-ine()%
video.:Xpulsar()%
:stado(video)%
i4 (video is Dispositivo'NG)
#
Dispositivo'NG video'NG=(Dispositivo'NG) video%
"onsole.,rite-ine()%
"onsole.,rite-ine(OPulsa intro para invocar 6ebobinarO)%
"onsole.6ead-ine()%
video'NG.6ebobinar()%
:stado(video)%
&
i4 (video is DispositivoD'D)
#
DispositivoD'D videoD'D=(DispositivoD'D) video%
"onsole.,rite-ine()%
"onsole.,rite-ine(OPulsa intro para invocar /rA(@)O)%
"onsole.6ead-ine()%
videoD'D./rA(@)%
1JO
:stado(video)%
&
"onsole.,rite-ine()%
&
static void :stado(Dispositivo'ideo video)
#
"onsole.,rite-ine(O:stado actual del reproductor9 #(&O,
video.:stado)%
&
&
Tay algunas cosillas interesantes que no quiero de"ar de comentar# 5n el m(todo main he usado el ob"eto
video, declarado de la clase 2ispositivoVideo para instanciar los dispositivos de los tres tipos distintos# B no
contento con esto, encima se los paso a los m(todos Acciones y 5stado con total impunidad, cuando estos
solamente aceptan como argumento un ob"eto de la clase 2ispositivoVideo# DC%mo diablos no coge el
compilador y me manda de nuevo al cole, a ver si aprendo algoE :a ra!%n de que todo esto +uncione
per+ectamente es la que os di al principio* tanto los dispositivos VT; como los dispositivos 2V2 son tambi(n
dispositivos de v&deo DnoE 9ues al haber derivado estas dos clases de la clase 2ispositivoVideo, el compilador
entiende esto mismo, de modo que los admite sin ning=n tipo de problemas# 9or otro lado, tengo dos i+
marcados en negrilla que qui! resulten bastante enigmticos# D$o dec&as que el compilador tragaba porque, al
+in y al cabo, todos eran dispositivos de v&deoE 9ues s&, en e+ecto, pero date cuenta de un hecho importante* los
m(todos Hebobinar e CrA no estn implementados en la clase 2ispositivoVideo, sino que son particulares de las
otras dos clases# 9or este motivo necesitaremos la conversi%n al tipo espec&+ico que implementa el m(todo para
poder invocarlo#
:os ms avispados 4si hab(is e"ecutado el e"emplo, obviamente8 os habr(is dado cuenta tambi(n de que ha
ocurrido algo raro al invocar el m(todo Cntroducir0edio la segunda ve!, es decir, cuando el ob"eto era un
reproductor de VT;# 5n e+ecto, se e"ecut% el m(todo sobreescrito en la clase 2ispositivoVT;, en lugar de
e"ecutarse el m(todo virtual de+inido en la clase base# DC%mo pudo el compilador saber que era un dispositvo
de VT;, si el ob"eto que se estaba utili!ando era, seg=n est declarado el argumento, un dispositivo gen(ricoE
9ues esta es la magia del polimor+ismo# 9odemos reempla!ar m(todos de la clase base con toda tranquilidad,
porque siempre se e"ecutarn correctamente# ;i os acordis, el polimor+ismo era la capacidad que ten&an los
ob"etos de comportarse de un modo distinto unos de otros aun compartiendo los mismos miembros# 9ues aqu&
lo ten(is#
13.4 +nter&aces
Ahora bien, no tendr&a sentido establecer una relaci%n de herencia entre con"untos completamente distintos,
por ms que muchos de sus miembros +ueran a ser comunes# 9or e"emplo, no estar&a bien heredar una clase
tar"eta de cr(dito y otra clase cuenta corriente de una clase banco# 9or ms que en todos ellos puedan hacerse
ingresos o reintegros, est claro que ni las tar"etas de cr(dito ni las cuentas corrientes son bancos# 5n resumen,
no debemos basarnos =nicamente en la +uncionalidad para establecer una relaci%n de herencia entre clases#
5s en este =ltimo caso donde "uegan un papel importante las inter+aces# 9odemos de+inir las inter+aces como
la de+inici%n de un con"unto de miembros que sern comunes entre clases que sern 4o no8 completamente
distintas# :a inter+ace conoce c%mo ser la +uncionalidad de cualquier clase que la implemente pero,
l%gicamente, no podr conocer los detalles de esa implementaci%n en cada una de esas clases, de modo que
una inter+ace no puede implementar nada de c%digo, sino que, =nicamente, puede describir un con"unto de
miembros# Anteriormente hablamos de la inter+ace CCloneable para poder hacer copias de ob"etos de tipo
1JS
re+erencia mediante el m(todo Clone# 5st claro que sern muchas las clases que puedan hacer copias de s&
mismas, pero tambi(n est claro que, aparte de este detalle, dichas clases no tienen por qu( tener nada ms en
com=n# Bo s( que te estars preguntando que, dado que una inter+ace no implementa nada de c%digo, puesto
que este tiene que ser escrito en cada una de las clases que implemente dicha inter+ace, D9ara qu( diablos
queremos la inter+aceE D$o ser&a ms +cil escribir el m(todo Clone en cada clase y olvidarnos de la inter+aceE
9ues esto se responde muy bien con un e"emplo* las clases Nactura y Array:ist podrn hacer copias de s&
mismas, pero est muy claro que una +actura no tiene absolutamente nada ms que ver con un Array:ist# ;in
embargo, si quisi(ramos escribir un m(todo que se ocupara de hacer la copia de uno de estos ob"etos,
cualquiera que sea, tendremos el problema de que habr&a que escribir una sobrecarga para cada uno de los
tipos que queremos poder copiar# 5s decir, si dicho m(todo se llama Copiar, en este caso habr&a que escribir
dos sobrecargas de (l* Copiar 4Nactura +8 y Copiar 4Array:ist a8# Ciertamente, tambi(n podr&amos escribir un
s%lo m(todo usando como argumento un tipo ob"ect, es decir, Copiar 4ob"ect o8, pero dentro de (l tendr&amos
que determinar cul es, e)actamente, el tipo del ob"eto 4 i+ 4o is Nactura8###else i+ 4o is Array:ist8 8 y hacer la
conversi%n en cada caso 4Nactura + R 4Nactura8 oK o bien Array:ist a R 4Array:ist8 o 8, para poder invocar el
m(todo Clone 4 return +#Clone48K o bien return a#Clone48 8, dado que el tipo ob"ect no contiene ninguna de+inici%n
para dicho m(todo# ;in embargo, si implementamos la inter+ace CCloneable en ambas clases nos ahorraremos
el traba"o, puesto que podremos escribir un =nico m(todo con un argumento que especi+ique cualquier ob"eto
que implemente dicha inter+ace, es decir, Copiar4CCloneable ic8, puesto que el ob"eto ic s& tiene un m(todo Clone
que se puede invocar directamente, independientemente que sea una Nactura o un Array:ist 4 return
ic#Clone48 8# 5n resumen, las inter+aces sirven para poder agrupar +uncionalidades#
Antes de poner alg=n e"emplo con las inter+aces, debo recordaros un par de cuestiones que ya os mencion(
en la introducci%n* los lengua"es #$5' soportan =nicamente herencia simple, es decir, una clase se puede
derivar de otra clase, pero no de variasK sin embargo, s& podemos implementar en una misma clase tantas
inter+aces como nos apete!ca#
Veamos un e"emplo de uso de las inter+aces# Vamos a pensar en que tenemos varias clases distintas que
deben o+recer la capacidad de presentar sus datos en la consola# Ciertamente, no tendr&a mucho sentido utili!ar
una clase base para todas ellas, porque ya hemos dicho que cada una ser distinta de la otra# 9or lo tanto,
disearemos una inter+ace a la que llamaremos, por e"emplo, C9resentable 4que no es igual que imVpresentable,
DehE o"o a la diV+erencia###8# 2icha inter+ace especi+icar simplemente la e)istencia de un m(todo, al cual
llamaremos 9resentar*
inter4ace /Presentable
#
void Presentar()%
&
Como os dec&a, +i"aos bien en que la Cnter+ace no implementa el c%digo del m(todo, sino que simplemente se
limita a indicar que debe e)istir un m(todo 9resentar en todas las clases que la implementen# Ah, otra cosa* os
habr(is +i"ado en que las inter+aces siempre empie!an por la letra C# $o es que lo e)i"a el compilador, sino que es
una convenci%n de codi+icaci%n, es decir, todo el mundo comien!a nombrando sus inter+aces con la letra C, de
modo que te aconse"o que t= tambi(n lo hagas# A continuaci%n vamos a escribir dos clases completamente
distintas que implementarn esta inter+ace*
class Krian$ulo9/Presentable
#
public double ase%
protected double Altura%
public Krian$ulo(double ase, double altura)
1J?
#
this.ase=ase%
this.Altura=altura%
&
public double Area
#
$et # return aseHAltura)D% &
&
public void Presentar()
#
"onsole.,rite-ine(Oase del triEn$ulo9 #(&O, ase)%
"onsole.,rite-ine(OAltura del triEn$ulo9 #(&O, Altura)%
"onsole.,rite-ine(Ocrea del triEn$ulo9 #(&O, Area)%
&
&
class Proveedor9/Presentable
#
public strin$ Nombre%
public strin$ Apellidos%
public strin$ Direccion%
public Proveedor(strin$ nombre, strin$ apellidos, strin$ direccion)
#
Nombre=nombre%
Apellidos=apellidos%
Direccion=direccion%
&
public void Presentar()
#
"onsole.,rite-ine(ONombre9 #(&O, Nombre)%
"onsole.,rite-ine(OApellidos9 #(&O, Apellidos)%
"onsole.,rite-ine(ODireccin9 #(&O, Direccion)%
&
&
9uedes apreciar claramente que un proveedor no tiene absolutamente nada que ver con un tringulo
4bueno### algunos proveedores pueden ser bastante obtusos### pero esa es otra cuesti%n###8# ;eguramente ahora
1L@
vers ms claramente el motivo de que la inter+ace no implemente el m(todo* evidentemente, no lo hace porque
no tiene +orma de saber los detalles de implementaci%n de cada clase# Pn program&n que utilice todo esto te
ayudar a apreciar la venta"a de haber usado una inter+ace*
class :;emplo/nter4acesApp
#
static void !ain()
#
Krian$ulo t=ne< Krian$ulo(?(,@)%
Proveedor p=ne< Proveedor(O:ri0O,O:ri0 otra vezO, Osu casaO)%
"onsole.,rite-ine(Oba se han creado los o;betosO)%
"onsole.,rite-ine(OPulsa /NK6^ para invocar 'erDatos(trian$ulo)O)%
"onsole.6ead-ine()%
'erDatos(t)%
"onsole.,rite-ine()%
"onsole.,rite-ine(OPulsa /NK6^ para invocar 'erDatos(proveedor)O)%
"onsole.6ead-ine()%
'erDatos(p)%
&
static void 'erDatos(/Presentable /P)
#
/P.Presentar()%
&
&
Como ves, al m(todo Ver2atos le podemos pasar cualquier ob"eto que implemente la inter+ace C9resentable,
independientemente de cul sea su clase, lo cual resulta verdaderamente c%modo#
Creo que ya es su+iciente para esta entrega# 5n la pr%)ima seguiremos pro+undi!ando en estas cosillas tan
lindas# 2e momento, y para que est(s entretenido, te propondr( un par de e"ercicios*
13.- #jercicio .
9ara este e"ercicio te pedir( que disees una clase Nactura y una clase 9resupuesto# :a +actura tendr
n=mero, +echa, datos del cliente, l&neas de detalles, porcenta"e de iva, base imponible, cuota y total# 5l
presupuesto ser parecido, aunque en este deber incluir tambi(n +echa de caducidad, pero no habr iva, ni
base imponible, ni cuota# Como ambos documentos van a ser muy parecidos, te recomiendo que consideres la
posibilidad de utili!ar la herencia#
13.-.1 Pistas para el ejercicio . 3#ntrega 134
:o me"or ser&a disear una clase con los miembros comunes ya implementados, la cual podr&a servir de
base para las otras dos# 9odr&as llamarla, por e"emplo, 2ocumento#
1L1
13.-.2 5esolucin del ejercicio
;er en la pr%)ima entrega, dentro de un mes, ms o menos 4espero###8
13.. #jercico 1
Ahora te voy a complicar un poco la vida 4qui! un poco mucho8# A ver si eres capa! de disear un tipo
0oneda, es decir, un tipo num(rico de dos decimales, convertible impl&citamente al tipo 2ecimal# 1"o, tiene que
ser un tipo valor, no un tipo re+erencia# 2irs que prcticamente lo tienes hecho en la entrega### qu( ms
quisieras### este tipo debe implementar las inter+aces CComparable, CConvertible e CNormatable, todas ellas
dentro del espacio de nombres ;ystem de la biblioteca de clases de $5' Nrame.orU# DIue te +altan datosE Ba,
supongo que desear&as saber cules son los miembros de cada una de las inter+aces y para qu( sirven# ;in
embargo, esta ve! no te lo dar( tan mascado# 5l secreto de un programador no es sab(rselo todo de memoria,
sino saber buscar lo que necesita# Como ya llevamos trece entregas con esta, creo que lo ms productivo ser
que empec(is a buscar y solucionar los problemas por vuestra cuenta, porque os servir muy bien como
entrenamiento#
13...1 Pistas para el ejercicio 1 3#ntrega 134
Busca las inter+aces CComparable, CConvertible e CNormatable en cualquier parte, desde Cnternet hasta la
documentaci%n de #$5' Nrame.orU# Cncluso puedes utili!ar +oros y grupos de noticias para preguntar, o alg=n
amiguete que est( puesto en esto, lo que sea 4menos a mi, l%gicamente, si no, no tendr&a gracia###8 5n este
e"ercicio pretendo, bsicamente, que aprendas a buscarte la vida cuando necesites hacer algo#
13...2 5esolucin del ejercicio
;er en la pr%)ima entrega, dentro de un mes, ms o menos 4espero###8
1L2

Das könnte Ihnen auch gefallen