You are on page 1of 0

Captulo 4

ELEMENTOS DE UNA CLASE C#




La clase como elemento fundamental de la programacin orientada a objetos requiere
toda la atencin posible por parte del programador. El correcto manejo de sus
elementos y el buen aprovechamiento de los mismos es lo que permitir sacar el
mximo provecho de esta metodologa de programacin, y sobretodo hacer ms fcil y
efectiva la tarea de programar una aplicacin de software. La teora general de la
programacin orientada a objetos define unos elementos bsicos que conforman una
clase, pero cada uno de los lenguajes de programacin ha realizado sus propios aportes
a estos elementos, especialmente ampliando su funcionalidad o representndolos
mediante elementos propios del lenguaje, con el objetivo de volverlos ms potentes y
fciles de manejar.
El lenguaje C# ha dotado a las clases de una serie de elementos que en apariencia
amplan el conjunto de elementos definidos en la teora general, pero ms que eso, en
realidad lo que se busca es poner a disposicin del programador toda una gama de
recursos que le permitan construir componentes de software que cumplan todos los
requerimientos de la programacin orientada a objetos y permitan expresar los
elementos generales en la forma ms efectiva y eficiente posible.
Estos son los elementos bsicos que constituyen una clase en C#:

Constructores
Destructores
Constantes
Campos
Mtodos
Propiedades
Indizadores
Operadores
Eventos
Delegados
Estructuras

Constructores
Un constructor es un mtodo de una clase que se ejecuta automticamente cada vez
que se crea una instancia de la misma. Aunque no se especifique, como ha sucedido en
todas las clases que hasta ahora hemos implementado, el compilador de C# siempre
establece internamente un mtodo constructor que no hace absolutamente nada.
Adems, siempre que vamos a crear un objeto definido por una clase, hacemos un
llamado a su constructor en el momento que creamos una nueva instancia con el
operador new. Por ejemplo, retomando nuestra clase de los nmeros complejos,
definida en el anterior captulo, en las siguientes dos lneas de cdigo, la primera lnea
define un objeto de tipo Complejo y la segunda se encarga de llamar al constructor de
esa clase.

CAPITULO CAPITULO CAPITULO CAPITULO 4 44 4 PROGRAMACION CON C#
www.pedrov.phpnet.us
86 86 86 86
Complejo z;
z = new Complejo();

El trabajo del constructor es iniciar el objeto que se ha definido mediante la clase.
Dentro del constructor pueden implementarse aquellas acciones que se necesita
ejecutar inicialmente y en forma automtica para dar una determinada configuracin a
un objeto, o incluso para hacerlo funcional.
Un mtodo constructor lleva el mismo nombre de la clase que lo contiene y se debe
declarar con nivel de accesibilidad pblica (public), aunque tambin es admitido el
nivel interno (internal). El nivel de accesibilidad pblica permite que el constructor
pueda ser ejecutado en cualquier instancia, ya sea dentro del proyecto que implement
la clase o por fuera de l, en cambio si el mtodo es internal, significa que solo podr
ejecutarse dentro del proyecto que contiene a la clase. En general, la sintaxis para
implementar un constructor es la siguiente:

[public | internal] NombreClase()
{
// Implementar el constructor o dejar esto vaci
}

Un constructor es el primer elemento de la clase sobre el cual puede aplicarse el
polimorfismo, aqu identificado como sobrecarga. Una clase puede implementar varios
constructores, los cuales deben diferenciarse ya sea en el tipo o la cantidad de
parmetros que manejan. En el siguiente fragmento de cdigo, la clase Complejo
implementa tres constructores,

public class Complejo
{
public Complejo() { }

public Complejo(string NumeroComplejo)
{
// Cdigo para procesar el parmetro
}

public Complejo(double Real, double Imaginario)
{
// Cdigo para procesar los parmetros
}
}

En el momento de la ejecucin, de acuerdo a los parmetros que reciba el constructor,
el sistema decidir a cual de estos constructores llamar. Si la declaracin de un objeto
Complejo se hiciera mediante,

Complejo z = new Complejo("5 + 3i")

se pondr en marcha el segundo constructor, ya que este es el nico que recibe como
parmetro un valor tipo cadena de texto.

Destructor
Un destructor es un mtodo que se ejecuta automticamente justo antes de que un
objeto sea destruido. A diferencias del constructor, un mtodo destructor no puede
sobrecargarse ni tampoco heredarse. Adems, no puede invocarse explcitamente.
El lenguaje C# cuenta con una herramienta llamada recolector de basura que se
encarga de destruir aquellos objetos que ya no se estn utilizando y an sigan
CAPITULO CAPITULO CAPITULO CAPITULO 4 44 4 ELEMENTOS DE UN CLASE C#

pedrov.cs@hotmail.com
87 87 87 87
ocupando espacio en memoria. Como ya se haba mencionada anteriormente, el
operador new, aplicado a la creacin de un objeto, se encarga de asignar la memoria
necesaria que este necesita. En el momento que el objeto deja de ser referenciado, el
recolector de basura se encarga de liberar la memoria que ocupaba. Un objeto se deja
de referenciar cuando la ejecucin sale del mbito que lo defini. En los ejemplos que
hemos analizado hasta el momento, la mayora de objetos deja de ser referenciado
cuando se finaliza la ejecucin del mtodo Main.
La sintaxis para implementar el destructor es la siguiente:

~NombreClase()
{
// Acciones antes de destruir un objeto
}

En la prctica un mtodo destructor es utilizado para realizar tareas que se necesitan
antes de destruir el objeto, como puede ser: guardar valores de datos, limpiar la
memoria manualmente o fijar alguna configuracin especial.

Ejemplo 4.1 Un constructor y un destructor

En el siguiente ejemplo vamos a programar un constructor y un destructor para
determinar el instante en que se ejecutan cada uno de ellos. Supongamos que tenemos
una clase Estudiante que permite procesar algunos datos de estudiantes, y presenta dos
sobrecargas para su constructor. La primera sobrecarga no hace prcticamente nada y
la segunda exige el cdigo del estudiante.

/* Archivo: Ejemplo41.cs */

using System;
using System.Windows.Forms;

public class Estudiante
{
string cdigo;

// Constructor por defecto
public Estudiante() { }

// Constructor con cdigo del estudiante
public Estudiante(string CodigoEstudiante)
{
cdigo = CodigoEstudiante;
MessageBox.Show("Cdigo: " + cdigo, "Construyendo...");
}

// Propiedad
public string Codigo
{
set { codigo = value; }
get { return codigo; }
}

// Destructor
~Estudiante()
{
MessageBox.Show("Ejecutando el destructor...", "Destruyendo...");
}
}
CAPITULO CAPITULO CAPITULO CAPITULO 4 44 4 PROGRAMACION CON C#
www.pedrov.phpnet.us
88 88 88 88

public class Programa
{
static void Main()
{
Estudiante alumno = new Estudiante();

//Estudiante escolar = new Estudiante("01");
}
}

Compile el archivo con la instruccin,

> csc ejemplo41.cs

Al ejecutar el programa se crea un objeto de tipo Estudiante pero, dado que la
ejecucin llega al final del mtodo Main, inmediatamente se inicia el proceso de
destruccin del objeto. Observe que, sin necesidad de hacer nada, la caja de mensajes
del destructor permanece un instante y luego desaparece. En realidad, al llegar al final
de la ejecucin e iniciar el proceso de destruccin, tambin se activa la recogida de
basura y por lo tanto esta se encarga de destruir cualquier dato que exista en memoria,
incluida la caja de mensajes.
Desactive la primera lnea del programa y active la segunda. Vuelva a compilar el
programa y analice la ejecucin. Observar que aparece la caja de mensajes del
constructor y esta se mantiene hasta hacer clic en el botn aceptar. La caja de mensajes
se mantiene por que el programa est en plena ejecucin. Pero no ocurre lo mismo
cuando pasa al proceso de destruccin.

Mtodos
Hasta este punto ya hemos trabajado con muchos mtodos. Sabemos que un mtodo es
lo que otros lenguajes de programacin, sobre todo estructurados, se denominan
procedimientos o funciones. Adems, se sabe que el principal mtodo que dirige la
ejecucin de un programa C# es el mtodo Main, que es el punto por donde se inicia la
ejecucin y la carga en memoria por parte del sistema operativo.
Los mtodos le permiten al programador realizar acciones sobre los atributos internos
de un objeto, o incluso actuar sobre elementos externos que se relacionan con dicho
objeto. Aunque, un mtodo existe en la medida que exista para un programador que
hace uso de una determinada clase, es decir un mtodo publico, en la prctica se puede
hablar tambin de mtodos privados, queriendo significar que son acciones internas
que se realizan con los elementos de un objeto.
En general, un mtodo se define con una instruccin que tiene la siguiente sintaxis:

public tipo NombreMtodo(Argumentos)
{
// Implementacin del mtodo
return valor
}

Todo mtodo que debe devolver un valor, el cual debe ser del mismo tipo que el
mtodo. Los mtodos que ejecutan acciones que no requieren la devolucin de un
valor, se deben definir como void.
C# para permitirle al programador compartir mtodos genricos que se ejecutan sin
necesidad de hacer referencia a ningn objeto en particular, permite la definicin de
mtodos estticos. Esto mtodos, que se definen con el modificador static, se
CAPITULO CAPITULO CAPITULO CAPITULO 4 44 4 ELEMENTOS DE UN CLASE C#

pedrov.cs@hotmail.com
89 89 89 89
comportan como las funciones o procedimientos de acceso publico que se utilizan en la
programacin estructurada, o en C++. Esta es la forma que implementa C# para
permitirle al programador contar con funciones que son accesibles en el mismo nivel
donde acta la clase en la cual se han incorporado.

Propiedades
Las propiedades tienen un aspecto similar a un mtodo, pero no admiten argumentos.
Se utilizan para establecer o asignar valores a los atributos de un objeto. Aunque,
tambin pueden utilizarse para procesar valores internos del objeto y retornar un valor
sin la necesidad de que exista un atributo directamente relacionado.
En general, una propiedad se implementa con la siguiente sintaxis:

public tipo NombrePropiedad
{
get
{
// Devuelve con return un valor
}
set
{
// Asigna el valor de value a un atributo
}
}

La seccin get se encarga de retornar un valor, ya sea de un atributo o como resultado
de un proceso de datos. La seccin set, en cambio, si debe actuar directamente sobre
un atributo, ya que de otra forma no tendra sentido, y su objetivo es asignar un valor.
En ambas secciones puede incluirse todo un conjunto de instrucciones, antes de
retornar o asignar un valor.
En el caso que no se requieren procesar parmetros, pueden presentarse por parte del
programador dudas sobre lo que debe utilizarse: un mtodo o una propiedad. No
existen reglas definidas sobre cual de los dos utilizar, y bajo que condiciones. Todo
depende del diseo o claridad que se desee darle al cdigo de programacin. Por
ejemplo, si se tiene una clase Persona, y se desea procesar a travs de ella la edad de
una persona, con base en su fecha de nacimiento, que se encuentra incluida en algn
atributo, bien podra implementarse un mtodo o una propiedad, y todo sera correcto.

Propiedades de solo lectura
Una propiedad de solo lectura es aquella que nicamente permite leer un dato
contenido en algn atributo de un objeto, o como resultado de un proceso interno. Este
tipo de propiedades no debe permitir ingresar un dato al objeto. Es decir no debe
incluir la seccin set.
La implementacin de una propiedad de solo lectura se logra incluyendo en el cuerpo
de la propiedad nicamente una seccin get, con lo cual se limita su funcionalidad
solo a retornar un valor hacia el cliente de la clase.
La siguiente es la sintaxis para implementar una propiedad de solo lectura:

public tipo NombrePropiedad
{
get {
// Devuelve con return un valor
}
}
CAPITULO CAPITULO CAPITULO CAPITULO 4 44 4 PROGRAMACION CON C#
www.pedrov.phpnet.us
90 90 90 90

Propiedades de solo escritura
Las propiedades de solo escritura solo permiten asignar valores a un atributo del objeto
que las implementa. Esto significa que en la implementacin solo debe incluirse la
seccin set.
La siguiente es la sintaxis para implementar una propiedad de solo escritura:

public tipo NombrePropiedad
{
set
{
// Asigna el valor de value a un atributo
}
}

El uso adecuado de esta clasificacin de las propiedades es lo que permite establecer
un buen nivel de encapsulamiento en las clases que se diseen.

Sobrecarga de mtodos
Sobrecargar un mtodo significa implementar varios mtodos de una clase con el
mismo nombre pero con diferentes parmetros, ya sea en cantidad o en tipo. C#,
tambin acepta diferencias en los valores devueltos por un mtodo sobrecargado, pero
siempre y cuando esta no sea la nica diferencia. Un mtodo podra tener una versin
que devuelva un entero (int) y contar con tres parmetros, todos de tipo cadena
(string). Si otra versin del mtodo devuelve un double, y tiene tres parmetros, no
pueden ser todos del tipo string, al menos uno de ellos debe ser de otro tipo, de lo
contrario el compilador devolver un error.
El siguiente es un ejemplo de una sobrecarga correcta:

public int Matricular(string codigo, string curso)
{
// Registrar datos de un estudiante
}

public bool Matricular(string codigo)
{
// Registrar datos de un estudiante
}

Supongamos que una clase implementa los mtodos,

public int Matricular(string codigo, string curso)
{
// Registrar datos de un estudiante
}
public bool Matricular(string codigo, string curso)
{
// Registrar datos de un estudiante
}

En este caso se generar un error en el momento de la compilacin, dado que C#
tratar de identificar cada mtodo a travs de la cantidad de argumentos y sus tipos.
Como no encuentra diferencias que permitan una clara identificacin, no se puede
terminar el proceso de compilacin.

CAPITULO CAPITULO CAPITULO CAPITULO 4 44 4 ELEMENTOS DE UN CLASE C#

pedrov.cs@hotmail.com
91 91 91 91

Ejemplo 4.2 Autmata que procesa direcciones web

En este ejemplo vamos a construir una clase con dos mtodos para identificar si una
cadena de texto corresponde a una direccin web bien escrita, a un correo electrnico o
no corresponde a ninguno de los dos formatos. Los programas que se encargan de
realizar este tipo de anlisis suelen denominarse autmatas.
Una de las tareas ms complejas a las que se puede enfrentar un programador es la de
procesar cadenas de texto para determinar si estn escritas de acuerdo a un formato o
sintaxis preestablecidos. En la mayora de los casos esta tarea requiere la revisin, en
forma repetitiva, carcter por carcter, en busca de los patrones que han sido
estudiados y establecidos con anticipacin. Una solucin, dada por las Ciencias de la
Computacin, para facilitar el estudio y anlisis de las condiciones que se deben
establecer en el procesamiento de textos, es la teora de los autmatas y los lenguajes
regulares, y ms especficamente las llamadas expresiones regulares, un campo de
permanente investigacin que ha permitido alcanzar los logros que hoy en da tienen a
la Computacin y a la Informtica en el sitial en que se encuentran.
Las expresiones regulares, como tal, son un lenguaje que permite simbolizar conjuntos
de cadenas de texto formadas por concatenacin de otras cadenas. La definicin exacta
de expresin regular se ha establecido a travs de un intrincado conjunto de axiomas de
tipo matemtico, que por ahora no entraremos a detallar. Tan solo vamos a dar una
nocin breve del concepto de expresin regular en la misma forma como lo hace la
ayuda incluida en el kit de desarrollo de .NET, donde se la asemeja con los patrones
que solemos utilizar para realizar bsquedas de archivos en el disco duro de nuestro
computador. Por ejemplo, si queremos que el sistema nos muestre todos los archivos
fuentes de C# podemos hacerlo a travs del patrn *.cs. Esta es una forma de decirle
al sistema operativo que muestre solo los archivos cuyo nombre termine en los
caracteres .cs. Podramos decir que para el sistema operativo la cadena *.cs es una
expresin regular.
A partir de aqu, y en lo que resta de esta prctica, para aquellos lectores que no estn
familiarizados con las expresiones regulares, se les sugiere olvidar todo lo que saben
sobre el significado de algunos caracteres especiales, tales como * y +, y manejar
nicamente el significado que aqu se describa.
Para comenzar, y a manera de ejemplo explicativo, veamos algunas expresiones
regulares bsicas que se manejan en la teora general de la computacin. Supongamos
que tenemos un carcter a, entonces se representa:

Expresin regular Conjunto representado
Representa a la cadena vaca
a Representa a la cadena formada por a
a+
Todas las cadenas formadas por la concatenacin de a, tales como a, aa,
aaa, aaa, etc.
a*
Todas las cadenas formadas por la concatenacin de a, incluyendo a la
cadena vaca. Es decir, a* = (a+) } {

Entonces, la expresin regular 01*, representa a todas las cadenas que empiezan por el
carcter 0 (cero) seguido de ninguno o cualquier cantidad de unos. Aqu, estn
representadas cadenas como 0, 01, 011, 0111, etc. La expresin regular (ab)+c,
representa todas las cadenas que repiten la cadena ab, una o ms veces, y terminan en
CAPITULO CAPITULO CAPITULO CAPITULO 4 44 4 PROGRAMACION CON C#
www.pedrov.phpnet.us
92 92 92 92
el carcter c, tales como abc, ababc, abababc, etc. En este ltimo ejemplo no se
incluye la cadena abcab, ni tampoco la cadena c.
El framework de .NET pone a disposicin del programador el espacio de nombres
System.Text.RegularExpressions, conformado por un conjunto de clases que se
encargan de trabajar con expresiones regulares. Vamos a utilizar esta teora y las clases
que .NET pone disposicin del programador para implementar un mtodo que permita
analizar cadenas de texto y determinar si una cadena de texto corresponde a una
direccin web, a un correo electrnico o a ninguno de ellos.
.NET dispone de una mayor cantidad de elementos, o caracteres especiales, que
aquellos manejados en la teora general computacin para la construccin de las
expresiones regulares. Vamos a describir brevemente algunos de ellos, para utilizarlos
en nuestro ejemplo:

Caracteres especiales Descripcin
[ ]
Permiten determinar una lista de caracteres, de los cuales se
escoger uno. Por ejemplo, [0123] pone a disposicin cualquiera
de estos dgitos para hacerlo coincidir con la cadena analizada.
( )
Permiten establecer alguna subcadena que se har coincidir con
la cadena analizada. Por ejemplo, (01)* representa a todas las
cadenas que son una repeticin de la subcadena 01, tales como
01, 0101, 010101.
\A
Establece que la coincidencia debe cumplirse desde el principio de
la cadena
\Z
Establece que la coincidencia debe establecerse hasta el final de
la cadena
\w
Representa a cualquier carcter que sea un carcter alfanumrico
o el carcter de subrayado. Tambin se representa como [a-zA-
Z0-9]
{N}
Establece que el elemento que le antecede debe repetirse
exactamente N veces. Por ejemplo, [w]{3} representa a la
cadena www.

Para no complicar mucho las cosas vamos a crear una expresin regular que permita
identificar las direcciones web que tienen el formato,

www.nombredominio.tipodominio

donde nombredominio es un nombre formado por una cadena de caracteres
alfanumericos y tipodominio corresponde a alguno de los posibles tipos de dominio
que pueden existir, tales como com, net, info u org.
Para nuestro caso, toda direccin web debe empezar por la repeticin del carcter w,
tres veces. Esto podemos expresarlo como,

[w]{3}

A continuacin viene un punto. Este smbolo corresponde a un carcter especial de las
expresiones regular de .NET, por lo cual debemos escribirlo en la forma

(\.)

El nombre del dominio, como ya se dijo, es una cadena de caracteres alfanumricos,
donde no cabe la posibilidad de que sea una cadena vaca. Vamos a suponer que solo
se aceptan caracteres en minsculas, por lo cual su representacin puede hacerse como,

[a-z0-9]+

CAPITULO CAPITULO CAPITULO CAPITULO 4 44 4 ELEMENTOS DE UN CLASE C#

pedrov.cs@hotmail.com
93 93 93 93
El tipo de dominio puede corresponder a una de las siguientes posibilidades: com, net,
info u org. En este caso existe una disyuncin de la cual se debe escoger solo una
opcin y se expresa como,

(com|net|info|org)

Finalmente es necesario que la cadena a analizar coincida exactamente desde su inicio
hasta su final, por lo cual es necesario incluir los limites \A y \Z al principio y al final
de la expresin regular, respectivamente.
En definitiva la expresin regular que nos permitir validar una direccin web es la
siguiente:

expresion = @"\A[w]{3}(\.)[a-z0-9]+(\.)(com|net|info|org)\Z";

El smbolo @ al inicio de la asignacin le informa al compilador de C# que no
identifique en la cadena de texto las secuencias estndar de escape.
De la misma forma podemos crear una expresin regular que identifique a todas las
direcciones de correo electrnico que cumplan con el formato,

nombreusuario@nombredominio.tipodominio

La siguiente es la expresin regular que identifica a este tipo de cadenas:

expresion = @"\A(\w+\.?\w*\@\w+\.)(com)\Z";

En esta expresin se ha incluido la secuencia \.? que admite la existencia de un punto,
o ninguno, en el nombre del usuario.
La clase que se encarga del procesamiento de las expresiones regulares se llama Regex
y se encuentra en el espacio de nombres System.Text.RegularExpressions. Con esta
clase se pueden definir objetos que reciben una expresin regular y se encargan de
procesar una cadena de texto para identificar todas las subcadenas que hacen parte de
ella. La clase Regex implementa el mtodo IsMatch que recibe como argumento la
cadena que se va a analizar y retorna un valor booleano, true o false, de acuerdo a si se
encontr cadenas que se identifiquen con la expresin regular o no. La siguiente lnea
define un objeto tipo Regex que procesa una expresin regular como las definidas en
este anlisis:

Regex automata = new Regex(expresion);

La clase Regex exige que la expresin regular sea asignada en el momento de la
creacin del objeto.
Con base en el anterior anlisis vamos a programar un ensamblado, tipo librera
dinmica, que se encargar de recibir una cadena de texto y validarla de acuerdo a lo
antes requerido. Este ensamblado estar constituido bsicamente por una clase,
llamada AutomataWEB, conformada por dos mtodos estticos, EsWeb y EsCorreo,
que servirn para validar las direcciones web y las direcciones de correo electrnico,
respectivamente.
/* Archivo: AutomataWEB.cs */

using System;
using System.Text.RegularExpressions;

public class AutomataWEB
{
CAPITULO CAPITULO CAPITULO CAPITULO 4 44 4 PROGRAMACION CON C#
www.pedrov.phpnet.us
94 94 94 94

public AutomataWEB()
{
}
// Mtodo para identificar direcciones web
public static bool EsWeb(string cadena)
{
string expresion;
expresion = @"\A[w]{3}(\.)[a-z0-9]+(\.)(com|net|info|org)\Z";
Regex automata = new Regex(expresion);
return automata.IsMatch(cadena);
}
// Mtodo para identificar direcciones de correo electrnico
public static bool EsCorreo(string cadena)
{
string expresion;
expresion = @"\A(\w+\.?\w*\@\w+\.)(com)\Z";
Regex automata = new Regex(expresion);
return automata.IsMatch(cadena);
}
}

Compile este archivo con la instruccin,

> csc /t:library AutomataWEB.cs

Los mtodos estticos no pueden hacer parte de ningn objeto definido a partir de la
clase que lo contiene. En consecuencia para referirse a ellos se debe hacer mediante el
nombre de la clase. En este caso, para validar una direccin web, se debe hacer
siguiendo la sintaxis,

AutomataWEB.EsWeb(cadena)

Ahora vamos a crear un programa que se encargar de recibir una cadena de texto y
realizar la correspondiente verificacin con ayuda del autmata que hemos creado.

/* Archivo: Ejemplo42.cs */

using System;

public class ValidacionWEB
{
public static void Main()
{
string cadena;

Console.Write("Escriba una cadena de texto: ");
cadena = Console.ReadLine();
cadena = cadena.ToLower(); // convierte a minsculas

if (AutomataWEB.EsWeb(cadena))
Console.Write("La cadena es una direccin web.");
else if (AutomataWEB.EsCorreo(cadena))
Console.Write("La cadena es una direccin de email.");
else
Console.Write("La cadena no es una direccin vlida.");

}
}

CAPITULO CAPITULO CAPITULO CAPITULO 4 44 4 ELEMENTOS DE UN CLASE C#

pedrov.cs@hotmail.com
95 95 95 95
Compile el programa con la instruccin,

> csc /r:AutomataWEB.dll ejemplo42.cs

Ejecute el programa resultante y pruebe su funcionamiento ingresando cadenas que
correspondan a direcciones web, correos electrnicos y otras que no cumplan con
ninguno de los dos formatos.
El estudio de las expresiones regulares requiere un anlisis ms profundo y detallado,
por lo cual este ejemplo se constituye solo en una breve introduccin a las
potencialidades que ofrece este tema en las aplicaciones de software modernas. Una
descripcin ms detallada se realizar en un captulo posterior dedicado
exclusivamente a este tema. Como ejercicio, el lector debera modificar los mtodos
programados aqu para validar cualquier direccin web, incluyendo todos los tipos de
dominio y las abreviaturas de los pases a los que pertenecen, y de esta forma ir
ganando terreno en el manejo de las expresiones regulares con .NET.

Ejemplo 4.3 Complejos de la forma a + bi

En este ejemplo vamos a continuar en la construccin de la clase Complejo que se ha
tomado como modelo de las descripciones de este captulo. Sabemos que, todo
complejo tiene la forma a + bi, y como tal tiene su validez dentro de las matemticas.
Siempre que se necesite trabajar con un complejo, las cosas seran ms fciles si tanto
la asignacin como el valor retornado por una variable de este tipo se hacen en
trminos de la forma matemtica de un nmero complejo. Sera ideal poder realizar
una asignacin a una variable compleja en la forma,

z = 2 + 3i;

Pero en vista de que el compilador de C# no reconoce la sintaxis a + bi como un valor
numrico vlido, por ahora no es posible en el nivel de programacin lograr esta
pretensin. Sin embargo, si podemos hacer una aproximacin de este tipo de
asignacin mediante asignaciones en forma de cadena de texto, tal como

z.Valor = "2 + 3i";

de tal manera que, para el usuario de un programa que utilice esta clase, el manejo de
los nmeros complejos sea totalmente transparente.
En este punto, nos enfrentamos a una situacin bastante particular. Debemos
programar una validacin que se encargue de reconocer un nmero complejo en una
cadena de texto, interpretndola adecuadamente, para determinar las partes real e
imaginaria del mismo. Para ello, es necesario, antes que nada, garantizar que la cadena
de texto realmente incida con el formato de un nmero complejo vlido.
Existen diversas formas para expresar un nmero complejo. Se sabe que un mismo
nmero complejo se puede escribir en formas equivalentes, tales como 2 + 3i, 2 + i3, 3i
+ 2, i3 + 2, y cualquiera de ellas es vlida. Adems existen complejos cuya forma,
debido a las propiedades matemticas, puede ser equivalente a otra. As, por ejemplo,
tenemos que 5 + -2i es lo mismo que 5 2i, o que 4 + 1i es igual a 4 + i, o tambin que
0 + 4i es igual a 4i. Incluso, cualquier nmero real puede considerarse como un
complejo de parte imaginaria igual a cero.
La clase Complejo debe poseer la capacidad suficiente de recibir un nmero complejo
en cualquiera de sus formatos vlidos y procesarlo adecuadamente. En general, y para
facilitar la comprensin de las descripciones algoritmicas vamos a dividir en cuatro
grupos los formatos en que puede estar escrito cualquier nmero complejo:
CAPITULO CAPITULO CAPITULO CAPITULO 4 44 4 PROGRAMACION CON C#
www.pedrov.phpnet.us
96 96 96 96

Grupo Formato
1 a, a + i, a + bi
2 i, i + a, bi, bi + a
3 ib, a + ib
4 ib + a

En estas descripciones sintcticas no se han incluido los signos, positivo o negativo,
que pueden adoptar la parte real y la parte imaginaria de un complejo. Ms adelante, en
cada caso particular entraremos a considerar este aspecto. Tampoco, el agrupamiento
realizado obedece a ninguna regla especfica relacionada con estos nmeros, sino
nicamente se busca agruparlos de acuerdo a algunas caractersticas de forma que
pueden convenientemente facilitar la generalizacin de cada uno de ellos. El primer
grupo es el formato ms usual entre los matemticos, la parte real va en primera
instancia y la parte imaginara despus. El tercer formato suele encontrrselo con
mucha frecuencia en libros de ingeniera. El segundo y cuarto formatos son un
equivalente de los anteriores.
Como ya se vio en el ejemplo anterior, .NET dispone de un recurso muy poderoso para
identificar cadenas de texto que guardan una relacin de semejanza entre si. Estas son
las clases del espacio de nombres System.Text.RegularExpressions, que permiten
procesar expresiones regulares. Vamos a utilizar este recurso para procesar cadenas de
texto a travs de expresiones regulares que representen a los grupos de la tabla anterior,
para identificar si corresponden o no a nmeros complejos bien construidos.
Abra en un editor de texto el archivo que contiene a la clase Complejo que se viene
construyendo en los ejemplos anteriores. Una ventaja de .NET es que permite
modificar un ensamblado, y siempre y cuando no se modifiquen los identificadores de
sus miembros existentes, mantienen la compatibilidad con las aplicaciones que
utilizaban la versin antigua del componente. Lo primero que se debe hacer es incluir
dos nuevas lneas de cdigo con los espacios de nombres que se necesitan para los
nuevos procesamientos que vamos a incluir en la clase.

using System.Globalization;
using System.Text.RegularExpressions;

En vista de que los procesos que se van a realizar requieren conversiones de tipos
numricos a cadenas de texto, y viceversa, es posible que algunos elementos de estos
se vuelvan incompatibles con los tipos. Por ejemplo, si se tiene la cadena de texto
3.52, donde el punto corresponde al separador decimal del nmero representado, al
convertirla a tipo double se puede presentar una inconsistencia de interpretacin si en
la configuracin del sistema operativo se identifica al separador decimal con una coma,
(,) La primera directiva posibilita acceder a las clases que nos van a permitir
determinar el formato que se est utilizando en la mquina actual para mostrar nmeros
con parte entera y decimal. Para evitar inconsistencias se adecuaran los nmeros al
formato que maneje la mquina donde se est trabajando.
En la siguiente lnea de cdigo, la propiedad CurrencyDecimalSeparator devuelve
una cadena tipo string con el separador decimal que utiliza el sistema operativo en la
mquina actual:

sd = NumberFormatInfo.CurrentInfo.CurrencyDecimalSeparator;

El punto central de este ejemplo, es construir un mtodo que le permita a las variables
instanciadas de la clase Complejo recibir una cadena de texto y validarla para
determinar si corresponde a un nmero complejo. Para ello vamos a determinar los
patrones que pueden determinarlos.
CAPITULO CAPITULO CAPITULO CAPITULO 4 44 4 ELEMENTOS DE UN CLASE C#

pedrov.cs@hotmail.com
97 97 97 97
Sabemos que, un nmero es una cadena de dgitos decimales en la cual puede o no
aparecer un separador decimal. Si el separador decimal fuera un punto, que en el
lenguaje de expresiones regulares de .NET se representa como (\.), un nmero tendra
el formato,

numero = @"(\d+(\.)?\d*)";

El cuantificador ? especifica que el elemento que le antecede puede aparecer una, o
ninguna vez, en la cadena analizada. Como en este caso vamos a utilizar como vlido
el separador decimal definido por el sistema, sd, entonces introducimos una
concatenacin con este, as:

numero = @"(\d+(" + sd + @")?\d*)";

Todo nmero complejo, exceptuando aquellos que no posean parte imaginaria nula,
incluyen un literal que representa a la raz cuadrada de -1. Este generalmente se
simboliza con la letra minscula i. Definimos este smbolo en la siguiente forma:

i = @"(i)";

El signo que puede anteceder a un nmero puede ser positivo, (+), o negativo, (-).
Sabemos que para expresar opcin en la escogencia de uno u otro smbolo se utilizan
los corchetes. Por lo tanto el signo de un nmero, en trminos de expresin regular de
.NET, quedara expresado por,

signo = @"([+-])";

Con los anteriores elementos podemos expresar la parte real e imaginaria de un
complejo en la siguiente forma:

real = signo + numero;
imaginario = signo + numero + i;

Ahora veamos el primer grupo de complejos que es posible encontrar:

Grupo Formato
1 a, a + i, a + bi

En el caso ms general, un complejo puede estar formado por una parte real y una parte
imaginaria, a + bi, que queda incluida en una expresin regular como la siguiente:

expresion1 = real + imaginario;

Pero, para incluir en la expresin regular los otros dos casos (complejos con parte
imaginaria 0 o 1) es necesario tratar en forma independiente esta parte. En el primer
caso, la parte imaginaria no existe, por lo tanto se debe dejar como opcional esta parte,
incluyendo a su signo. As:

imaginario = "(" + signo + numero + i + ")?";

A su vez, para el segundo caso, donde la parte imaginaria solo la forma el literal i, se
puede obtener dejando como opcional el nmero que la acompaa.

imaginario = "(" + signo + "(" + numero + ")?" + i + ")?";

CAPITULO CAPITULO CAPITULO CAPITULO 4 44 4 PROGRAMACION CON C#
www.pedrov.phpnet.us
98 98 98 98
Con estas modificaciones, e indicndole al motor de procesamiento de expresiones
regulares que debe validar la coincidencia en toda la cadena, desde el principio (\A)
hasta el final, (\Z), la expresin regular para el grupo 1 de posibles complejos que
pueden pasarse a la clase queda as:

string expresion1 = @"\A" + real + imaginario + @"\Z";

Para este primer grupo de complejos, es necesario definir un objeto del tipo Regex
quien se encargar de procesar las cadenas entrantes para determinar si constituyen un
nmero complejo. La siguiente lnea define el objeto complejo1 con la expresin
regular antes analizada:

Regex complejo1 = new Regex(expresion1);

La comprobacin de la cadena entrante, puede realizarse mediante el mtodo booleano
IsMatch del objeto complejo1. As,

if (complejo1.IsMatch(cadena)) return true;

De la misma forma como se realiz la expresin regular para el primer grupo de
complejos, puede definirse las expresiones regulares para los otros casos de posibles
formatos de nmeros complejos. En definitiva, con estos elementos construimos el
mtodo booleano EsComplejo, en la siguiente forma:

// Mtodo para vlidar un nmero complejo
private bool EsComplejo(string cadena)
{
cadena = QuitarEspacios(cadena);
if (cadena.Length == 0) return false;
string sd;
sd=NumberFormatInfo.CurrentInfo.CurrencyDecimalSeparator;
cadena = cadena.Replace('.', Char.Parse(sd));

// Elementos bsicos de un complejo
string numero = @"(\d+(" + sd + @")?\d*)";
string i = @"(i)";
string signo = @"([+-])";

// Validacin para a, a + i, a + bi
string real = signo + "?" + numero;
string imaginario = "("+signo+"("+numero+")?"+i+")?";
string expresion1 = @"\A" + real + imaginario + @"\Z";
Regex complejo1 = new Regex(expresion1);
if (complejo1.IsMatch(cadena)) return true;

// Validacin para i, i + a, bi, bi + a
imaginario = signo + "?" + numero + "?" + i;
real = "(" + signo + numero + ")?";
string expresion2 = @"\A" + imaginario + real + @"\Z";
Regex complejo2 = new Regex(expresion2);
if (complejo2.IsMatch(cadena)) return true;

// Validacin para ib, ib + a
imaginario = signo + "?" + i + numero;
real = "(" + signo + numero + ")?";
string expresion3 = @"\A" + imaginario + real + @"\Z";
Regex complejo3 = new Regex(expresion3);
if (complejo3.IsMatch(cadena)) return true;

// Validacin para a + ib
real = signo + "?" + numero;
imaginario = signo + i + numero;
string expresion4 = @"\A" + real + imaginario + @"\Z";
CAPITULO CAPITULO CAPITULO CAPITULO 4 44 4 ELEMENTOS DE UN CLASE C#

pedrov.cs@hotmail.com
99 99 99 99
Regex complejo4 = new Regex(expresion4);
return complejo4.IsMatch(cadena);
}

Se ha incluido una llamada a un mtodo denominado QuitarEspacios, que se encarga
de eliminar todos los espacios en blanco que puedan existir en la cadena de texto.
Aunque la inclusin de los espacios pudo haberse considerado en las expresiones
regulares de cada uno de los casos, esto hara un tanto ms compleja su estructuracin,
por lo que se ha optado por el camino ms fcil, quitarlos! Este mtodo recibe una
cadena de texto, y a travs de una expresin regular apropiada, busca uno o ms
espacios y los reemplaza con una cadena vaca.

private string QuitarEspacios(string cadena)
{
Regex espacio = new Regex(@"\s+");
cadena = espacio.Replace(cadena, "");
return cadena;
}

Una vez que se ha determinado la validez de una cadena de texto como nmero
complejo, es necesario separar sus partes real e imaginaria para asignarlas a sus
respectivos atributos. El mtodo PartesComplejo se basa de un razonamiento muy
simple: se busca la parte imaginaria del complejo, se lee su valor y luego se elimina,
dejando de esta forma nicamente la parte real.

// Mtodo para separar la parte real y la parte imaginaria
private void PartesComplejo(string cadena)
{
string sd;
sd=NumberFormatInfo.CurrentInfo.CurrencyDecimalSeparator;
cadena = cadena.Replace('.', Char.Parse(sd));
string parteReal = "";
string parteImag = "";

string signo = @"([+-])";
string numero = @"(\d+(" + sd + @")?\d*)";
string i = @"(i)";

string imaginaria = signo + "?" + numero + "?" + i +
numero + "?";
Regex imaginario1 = new Regex(imaginaria);
if (imaginario1.IsMatch(cadena))
{
// Cargar en mc las cadenas encontrada
MatchCollection mc = imaginario1.Matches(cadena);
// Recuperar la cadena encontrada
foreach(Match m in mc)
{
parteImag = m.ToString();
}
// Analizar algunos casos especiales
if (parteImag == "+i" || parteImag == "i")
parteImag = "1";
else if (parteImag == "-i")
parteImag = "-1";
else
parteImag = parteImag.Replace("i", "");

// Eliminar la parte imaginaria
parteReal = imaginario1.Replace(cadena, "");
}
else
{
parteReal = cadena;
CAPITULO CAPITULO CAPITULO CAPITULO 4 44 4 PROGRAMACION CON C#
www.pedrov.phpnet.us
100 100 100 100
parteImag = "0";
}
// Convierte las cadenas de texto a double
// y las asigna a sus atributos respectivos
real = Double.Parse(parteReal);
imaginario = Double.Parse(parteImag);
}

En la expresin regular que se utiliza en este mtodo existen dos particularidades. La
primera es que no se busca un sola coincidencia en toda la cadena (aunque, bien podra
haberse hecho), por que se supone que la cadena analizada ya est comprobado que
corresponde a un nmero complejo y por lo tanto solo existir una, o ninguna, parte
imaginaria. La otra particularidad, es que se han establecido todas las formas de parte
imaginaria en una sola expresin regular. La razn, en este punto ya se sabe que la
parte imaginaria est bien escrita y por lo tanto todo lo que se encuentre ser vlido.

imaginaria = signo + "?" + numero + "?" + i + numero + "?";

Cuando se analiza una cadena a travs de una expresin regular, el motor de anlisis
busca todas las subcadenas que hagan parte de esa familia y las va guardando en un
objeto de tipo MatchCollection. Para recuperar la coleccin de cadenas objetivo
encontradas existe el mtodo Matches que hace parte de los objetos de tipo Regex. En
la siguiente lnea se recupera todas las cadenas encontradas y se asignan al objeto mc:

MatchCollection mc = imaginario1.Matches(cadena);

En este caso particular, estamos seguros que si la cadena objetivo existe, es nica, y en
el peor de los casos no existe. Con este mtodo, y los anteriores, estamos listos para
ampliar y mejorar las capacidades de nuestra clase Complejo.
Se pondr a disposicin del usuario de la clase, tres constructores sobrecargados. El
primero no pide ningn dato de entrada. El segundo mtodo da la posibilidad de
ingresar los valores real e imaginario del complejo y el tercer mtodo permite
inicializar la variable con un complejo ingresado en forma de cadena de texto. Estos
son los tres constructores:

// Constructores
public Complejo() { }

public Complejo(double parteReal, double parteImaginaria)
{
real = parteReal;
imaginario = parteImaginaria;
}

public Complejo(string valorComplejo)
{
if (EsComplejo(valorComplejo))
PartesComplejo(valorComplejo);
else
{
real = 0;
imaginario = 0;
}
}
La salida devuelta por un objeto de tipo Complejo debe ser acorde a los valores de su
parte real e imaginaria y al formato manejado para representar este tipo de nmeros. El
siguiente mtodo privado se encarga de preparar la salida de un complejo en forma de
cadena de texto, con el formato a + bi.
CAPITULO CAPITULO CAPITULO CAPITULO 4 44 4 ELEMENTOS DE UN CLASE C#

pedrov.cs@hotmail.com
101 101 101 101

private string FormatoSalida()
{
if (real == 0)
return String.Format("{0}i", imaginario);
else
if (imaginario > 0)
return String.Format("{0} + {1}i",
real, imaginario);
else if (imaginario < 0)
return String.Format("{0} - {1}i",
real, imaginario);
else
return real.ToString();
}

Con base en lo anterior se agregar la propiedad Valor, que se encarga de devolver el
valor de un complejo o recibir su valor desde el exterior, validando su correcta
escritura. Esta es su implementacin:

public string Valor
{
get { return FormatoSalida(); }
set
{
if (EsComplejo(value))
PartesComplejo(value);
else
{
real = 0;
imaginario = 0;
}
}
}

Finalmente, es importante que los objetos tipo Complejo se puedan imprimir sin
necesidad de recurrir a ninguna propiedad en especial, en la misma forma como los
hacen los valores numricos de otros tipos. Es decir, si el programador tiene

Console.WriteLine(z);

debe mostrarse en pantalla el valor del complejo, que hace parte del argumento del
mtodo WriteLine, en el formato adecuado. Esto mejora el nivel de abstraccin de la
clase Complejo y le asegura a sus objetos un comportamiento ms cercano a los
valores numricos, facilitando su manejo por parte de cualquier programador. Para
lograr esto es necesario sobrescribir el mtodo ToString que hace parte de toda clase
definida en .NET.
La clase Complejo al igual que todas las clases de .NET, en realidad son heredadas de
una clase genrica que forma parte de la raz del framework, llamada Object. Aunque
esta herencia no se necesita determinar en forma explicita, el compilador de C# lo
interpreta as con todas las clases definidas como superclases. Un mtodo que se
hereda de Object para todas las clases es ToString el que se ejecuta por defecto
cuando se intenta imprimir un objeto cualquiera. En la mayora de los casos cuando se
imprime un objeto, sin especificar ninguna propiedad, este mtodo devuelve el nombre
completo del objeto. En nuestro caso vamos a sobrescribir el mtodo para obligarlo
escribir el valor del nmero complejo. As:

// Sobrecarga del mtodo ToString
CAPITULO CAPITULO CAPITULO CAPITULO 4 44 4 PROGRAMACION CON C#
www.pedrov.phpnet.us
102 102 102 102
public override string ToString()
{
return FormatoSalida();
}

En definitiva la clase complejo, ya casi lista, queda como sigue:

/* Archivo: Complejo.cs */

using System;
using System.Globalization;
using System.Text.RegularExpressions;

public class Complejo
{
// Atributos
private double real;
private double imaginario;

// Constructores
public Complejo() { }

public Complejo(double parteReal, double parteImaginaria)
{
real = parteReal;
imaginario = parteImaginaria;
}

public Complejo(string valorComplejo)
{
if (EsComplejo(valorComplejo))
PartesComplejo(valorComplejo);
else
{
real = 0;
imaginario = 0;
}
}

// Propiedades
public double Real
{
get { return real; }
set { real = value; }
}

public double Imaginario
{
get { return imaginario; }
set { imaginario = value; }
}

public double Modulo
{
get { return Tamano(); }
}

public double Argumento
{
get { return Angulo(); }
}
CAPITULO CAPITULO CAPITULO CAPITULO 4 44 4 ELEMENTOS DE UN CLASE C#

pedrov.cs@hotmail.com
103 103 103 103

public string Valor
{
get { return FormatoSalida(); }
set
{
if (EsComplejo(value))
PartesComplejo(value);
else
{
real = 0;
imaginario = 0;
}
}
}

// Sobrecarga del mtodo ToString
public override string ToString()
{
return FormatoSalida();
}

// Mtodos privados
private double Tamano()
{
double c;
c = Math.Sqrt(real * real + imaginario * imaginario);
return c;
}

private double Angulo()
{
double alfa;
if (real > 0)
alfa = Math.Atan(imaginario / real);
else if (real < 0)
if (imaginario > 0)
alfa = Math.PI + Math.Atan(imaginario / real);
else
alfa = - Math.PI + Math.Atan(imaginario / real);
else
if (imaginario > 0)
alfa = Math.PI / 2;
else if (imaginario < 0)
alfa = - Math.PI / 2;
else
alfa = 0;

return alfa;
}

// Mtodo para vlidar un nmero complejo
private bool EsComplejo(string cadena)
{
cadena = QuitarEspacios(cadena);
if (cadena.Length == 0) return false;
string sd = NumberFormatInfo.CurrentInfo.CurrencyDecimalSeparator;
cadena = cadena.Replace('.', Char.Parse(sd));
// Elementos bsicos de un complejo
string numero = @"(\d+(" + sd + @")?\d*)";
string i = @"(i)";
CAPITULO CAPITULO CAPITULO CAPITULO 4 44 4 PROGRAMACION CON C#
www.pedrov.phpnet.us
104 104 104 104

string signo = @"([+-])";

// Validacin para a, a + i, a + bi
string real = signo + "?" + numero;
string imaginario = "(" + signo + "(" + numero + ")?" + i + ")?";
string expresion1 = @"\A" + real + imaginario + @"\Z";
Regex complejo1 = new Regex(expresion1);
if (complejo1.IsMatch(cadena)) return true;

// Validacin para i, i + a, bi, bi + a
imaginario = signo + "?" + numero + "?" + i;
real = "(" + signo + numero + ")?";
string expresion2 = @"\A" + imaginario + real + @"\Z";
Regex complejo2 = new Regex(expresion2);
if (complejo2.IsMatch(cadena)) return true;

// Validacin para ib, ib + a
imaginario = signo + "?" + i + numero;
real = "(" + signo + numero + ")?";
string expresion3 = @"\A" + imaginario + real + @"\Z";
Regex complejo3 = new Regex(expresion3);
if (complejo3.IsMatch(cadena)) return true;

// Validacin para a + ib
real = signo + "?" + numero;
imaginario = signo + i + numero;
string expresion4 = @"\A" + real + imaginario + @"\Z";
Regex complejo4 = new Regex(expresion4);
return complejo4.IsMatch(cadena);
}

// Mtodo para separar la parte real y la parte imaginaria
private void PartesComplejo(string cadena)
{
string sd;
sd=NumberFormatInfo.CurrentInfo.CurrencyDecimalSeparator;
cadena = QuitarEspacios(cadena);
cadena = cadena.Replace('.', Char.Parse(sd));
string parteReal = "";
string parteImag = "";
string signo = @"([+-])";
string numero = @"(\d+(" + sd + @")?\d*)";
string i = @"(i)";

string imaginaria = signo + "?" + numero + "?" + i + numero + "?";
Regex imaginario1 = new Regex(imaginaria);
if (imaginario1.IsMatch(cadena))
{
// Cargar en mc las cadenas encontrada
MatchCollection mc = imaginario1.Matches(cadena);
// Recuperar la cadena encontrada
foreach(Match m in mc)
{
parteImag = m.ToString();
}
// Analizar algunos casos especiales
if (parteImag == "+i" || parteImag == "i")
parteImag = "1";
else if (parteImag == "-i")
parteImag = "-1";
else
CAPITULO CAPITULO CAPITULO CAPITULO 4 44 4 ELEMENTOS DE UN CLASE C#

pedrov.cs@hotmail.com
105 105 105 105

parteImag = parteImag.Replace("i", "");

// Eliminar la parte imaginaria
parteReal = imaginario1.Replace(cadena, "");
}
else
{
parteReal = cadena;
parteImag = "0";
}
// Verificar la cadenas de texto vacas
if (parteReal.Length == 0) parteReal = "0";
if (parteImag.Length == 0) parteImag = "0";
// Convierte las cadenas de texto a double
// y las asigna a sus atributos respectivos
real = Double.Parse(parteReal);
imaginario = Double.Parse(parteImag); }

private string QuitarEspacios(string cadena)
{
Regex espacio = new Regex(@"\s+");
cadena = espacio.Replace(cadena, "");
return cadena;
}

private string FormatoSalida()
{
if (real == 0)
return String.Format("{0}i", imaginario);
else
if (imaginario > 0)
return String.Format("{0} + {1}i", real, imaginario);
else if (imaginario < 0)
return String.Format("{0} - {1}i", real, - imaginario);
else
return real.ToString();
}
}

Compile este archivo en un ensamblado tipo librera dinmica, con la instruccin,

> csc /t:library Complejo.cs

El siguiente programa hace uso de la clase Complejo y muestra el funcionamiento de
los cambios realizados:

/* Archivo: Ejemplo43.cs */
using System;

public class NumerosComplejos
{
static void Main()
{
Complejo z = new Complejo();
Console.Write("Ingrese un nmero complejo: ");
z.Valor = Console.ReadLine();

Console.Write("z = {0}\n", z);
Console.Write("a = {0}; b = {1}\n", z.Real, z.Imaginario);
Console.Write("Mdulo: {0}\n", z.Modulo);
CAPITULO CAPITULO CAPITULO CAPITULO 4 44 4 PROGRAMACION CON C#
www.pedrov.phpnet.us
106 106 106 106

Console.Write("Argumento: {0}", z.Argumento);

// Uso de una sobrecarga del constructor de Complejo
Complejo w = new Complejo("-3i + 4");
Console.WriteLine(w);
Console.Write("a = {0}; b = {1}\n", w.Real, w.Imaginario);
Console.Write("Mdulo: {0}\n", w.Modulo);
Console.Write("Argumento: {0}", w.Argumento);
}
}

Compile este archivo con la instruccin de lnea de comandos,

> csc /r:Complejo.dll ejemplo43.cs

Ejecute el programa resultante y analice el comportamiento de cada lnea que lo
compone.
Un detalle importante a tener en cuenta es que el ensamblado Complejo.dll, a pesar de
haber sufrido cambios, sigue siendo compatible con los programas que utilizaban la
versin desarrollada en los anteriores ejemplos. Esta es una caracterstica de los
ensamblados de .NET, mientras no se modifique o elimine el nombre de alguno de los
miembros que lo componen, cada componente puede seguir editndose y aumentando
sus elementos y mantener la compatibilidad hacia versiones anteriores.

Sobrecarga de operadores
El concepto de sobrecarga tambin es aplicable a los operadores de C# y consiste en
hacer que estos se comporten de acuerdo a los objetos que los utilizan. El ejemplo ms
conocido es el operador sobrecargado es +, quin tiene una versin para valores
numricos y otra para valores tipo cadena de texto. Cuando el operador se aplica a dos
valores que representan cantidades numricas, realiza una suma matemtica, pero
cuando se aplica a dos cadenas de texto, produce como resultado una cadena que es la
concatenacin de las dos primeras. Las siguientes lneas de cdigo muestran un
ejemplo tpico:

int a = 5 + 7;
string c = "Hola" + "Mundo";

En la variable entera a se almacena el valor numrico 12, mientras que en la variable
tipo cadena c se almacena el valor "HolaMundo". En cada caso el operador + tiene un
comportamiento acorde a los tipos de datos sobre los que se aplica.
La sobrecarga de operadores le da al lenguaje de programacin la claridad y
naturalidad suficientes para hacer de las operaciones con objetos un trabajo fcil de
entender y aplicar por parte del programador. Sin embargo, no se debe abusar de este
recurso, por que un mal uso del mismo puede volver al lenguaje incomprensible y
confuso al momento de aplicar los operadores a algunos objetos. Por ejemplo,
perfectamente se podra hacer una sobrecarga para el operador +, de tal manera que al
aplicarse a un cierto tipo de datos numricos produjera una multiplicacin, algo que
hara perder innecesariamente la lgica del lenguaje de programacin. Todo operador,
al momento de sobrecargarse, debe ejecutarse acorde a la funcin que realiza en otros
objetos ya establecidos o en el mundo real del programador. No debemos perder de
vista que la esencia de un lenguaje de programacin es actuar como intermediario en el
proceso de comunicacin entre la mquina y el ser humano, y por lo tanto debe buscar
ser lo ms claro posible.
CAPITULO CAPITULO CAPITULO CAPITULO 4 44 4 ELEMENTOS DE UN CLASE C#

pedrov.cs@hotmail.com
107 107 107 107
Para sobrecargar un operador se utiliza un mtodo esttico que debe hacer parte del
tipo o clase que lo va a utilizar. La siguiente es la sintaxis general que se utiliza para
sobrecargar un operador unario:

public static TipoDevuelto operator operador (Tipo operando)
{
// Implementacin
}

En forma similar se sobrecarga un operador binario:

public static TipoDevuelto operator operador (Tipo1 operando1,
Tipo2 operando2)
{
// Implementacin
}

Aunque C# no permite la sobrecarga de todo los operadores que maneja, si lo hace
para la mayora de operadores relacionados con las matemticas y los bits. En la
siguiente lista se muestran todos los operadores que admiten sobrecarga:

Operadores unarios
+, -, !, ~, ++, --, true, false
Operadores binarios
+, -, *, /, %, &, |, ^, <<, >>, ==, !=,
>, <, >=, <=

Ejemplo 4.4 Operaciones con complejos

En este ejemplo vamos a sobrecargar los operadores matemticos para la clase
Complejo. Hasta ahora no hemos definido la forma de realizar operaciones con
complejos. Si un programador deseara obtener una suma de complejos debera recurrir
a la definicin matemtica y aplicar el proceso con las partes de los complejos que se
vayan a operar.
Comencemos con la suma de nmeros complejos. Las matemticas la definen de la
siguiente forma: si se tienen dos complejos
1
z y
2
z , la suma de ellos es un nmero
complejo cuya parte real es la suma de las partes reales de los dos complejos, y de
igual forma la parte imaginaria es igual a la suma de las partes imaginarias. En
notacin matemtica:

Si
1 1 1
z a b i = + y
2 2 2
z a b i = + , entonces
1 2 1 2 1 2
( ) ( ) z z a a b b i + = + + +

Una posible solucin al problema de programar la suma de complejos, podra ser la
definicin de un mtodo esttico que se encargue de recibir como parmetros dos
complejos, realizar la suma utilizando la definicin matemtica y devolver el resultado
en trminos de una variable compleja. Este mtodo, implementado por la clase
Complejo, bien podra ser el siguiente:

public static Complejo Suma(Complejo z1, Complejo z2)
{
double a = z1.Real + z2.Real;
double b = z1.Imaginario + z2.Imaginario;
Complejo z = new Complejo(a, b);
return z;
}

CAPITULO CAPITULO CAPITULO CAPITULO 4 44 4 PROGRAMACION CON C#
www.pedrov.phpnet.us
108 108 108 108
La solucin es buena, y de hecho funciona muy bien. En las siguientes lneas de cdigo
se muestra como debera utilizarse el mtodo Suma:

Complejo z1 = new Complejo("5 + 3i");
Complejo z2 = new Complejo("8 - 2i");
Complejo z = new Complejo();
z = Complejo.Suma(z1, z2);

Esta codificacin de operaciones matemticas, basada en llamadas a mtodos, puede
resultar molesta en situaciones donde se van a realizar operaciones de uso muy comn.
Un programador que haga uso de los complejos, talvez preferira codificar una suma en
forma ms natural, o por lo menos como est acostumbrado a hacerlo con los dems
nmeros que maneja el lenguaje de programacin, as

suma = z1 + z2;

Para lograr esto es necesario sobrecargar el operador +, indicndole cual debe ser el
proceso a seguir cuando se aplique a nmeros complejos.
El siguiente mtodo sobrecarga el operador + para la clase Complejo:

public static Complejo operator +(Complejo z1,Complejo z2)
{
Complejo suma = new Complejo();
suma.real = z1.real + z2.real;
suma.imaginario = z1.imaginario + z2.imaginario;
return suma;
}

Como puede observarse, el mtodo de sobrecarga hace exactamente lo que debera
hacer el programador usuario de la clase. La ventaja es que solo se programa aqu, y en
adelante bastar con aplicar una operacin de suma comn y corriente, como si de otro
nmero cualquiera se tratar.
Antes de sobrecargar las dems operaciones vamos sobrecargar el operador inverso
aditivo, o signo negativo, (-), el cual invierte el signo de las partes que conforman un
complejo. Matemticamente establece:

Si z a bi = + entonces z a bi =

Entonces la sobrecarga del operador inverso aditivo, o signo negativo, queda como
sigue:

public static Complejo operator -(Complejo z)
{
Complejo inverso = new Complejo();
inverso.real = - z.real;
inverso.imaginario = - z.imaginario;
return inverso;
}

A su vez, la resta de complejos puede definirse a travs de la suma, as:

1 2 1 2
( ) z z z z = +

Con lo que la sobrecarga del operador resta sigue la misma nocin.

public static Complejo operator -(Complejo z1,Complejo z2)
{
CAPITULO CAPITULO CAPITULO CAPITULO 4 44 4 ELEMENTOS DE UN CLASE C#

pedrov.cs@hotmail.com
109 109 109 109
Complejo resta = z1 + (- z2);
return resta;
}

En las dos ltimas sobrecargas aparentemente se ha modificado el mismo operador. En
realidad no es as. El compilador de C# distingue claramente a cada operador por el
nmero de operandos sobre los cuales acta. En el primer caso, al existir un solo
operando entiende que se trata del operador inverso aditivo, mientras que el segundo
caso queda claro que se trata del operador resta.
La multiplicacin de complejos se obtiene realizando una multiplicacin polinomial de
los dos operandos. En general esta operacin se simplifica en el siguiente resultado:

Si
1 1 1
z a b i = + y
2 2 2
z a b i = + , entonces
1 2 1 2 1 2 1 2 2 1
( ) ( ) z z a a b b a b a b i = + +

Con base en esta definicin, la sobrecarga del operador multiplicacin, *, queda as:

public static Complejo operator *(Complejo z1,Complejo z2)
{
Complejo producto = new Complejo();
double a1 = z1.real, b1 = z1.imaginario;
double a2 = z2.real, b2 = z2.imaginario;
producto.real = a1 * a2 - b1 * b2;
producto.imaginario = a1 * b2 + a2 * b1;
return producto;
}

Pero la multiplicacin no solo puede darse entre nmeros complejos, tambin puede
multiplicarse un numero real por un complejo. Para lograr esto es necesario aplicar dos
sobrecargas ms al operador *. Puede multiplicarse un complejo por la izquierda o por
la derecha. Ambas situaciones deben quedar bien claras para el compilador de C#. La
definicin matemtica de esta multiplicacin establece:

Si c y z a bi = + , entonces c z ca cbi = +

En consecuencia la sobrecarga de * para este caso queda como sigue:

public static Complejo operator *(double c, Complejo z)
{
Complejo z1 = new Complejo();
z1.Real = c * z.Real;
z1.Imaginario = c * z.Imaginario;
return z1;
}

A su vez, la sobrecarga para la multiplicacin por la derecha se puede implementar con
base en la anterior, as:

public static Complejo operator *(Complejo z, double c)
{
return c * z;
}

Existe una operacin propia de los complejos, que no esta definida para ningn otro
tipo numrico. Es el conjugado de un complejo. Esta operacin, lo nico que hace es
invertir la parte imaginaria del nmero complejo al cual se aplica.

Si z a bi = + , el conjugado de z se define como z a bi =
CAPITULO CAPITULO CAPITULO CAPITULO 4 44 4 PROGRAMACION CON C#
www.pedrov.phpnet.us
110 110 110 110

En C# no existe un operador cuya funcionalidad tenga alguna relacin con el
conjugado de un complejo. En vista de esto vamos a sobrecargar el operador !
(negacin lgica). La sobrecarga queda como sigue:

public static Complejo operator !(Complejo z)
{
Complejo conjugado = new Complejo();
conjugado.Real = z.Real;
conjugado.Imaginario = - z.Imaginario;
return conjugado;
}

La divisin de nmeros complejos se puede definir, con base en el conjugado y el
mdulo del divisor, en la siguiente forma:

1
1 2 2
2
2
1 z
z z
z
z
=

Por lo tanto, el mtodo que sobrecarga el operador divisin, /, se puede implementar
como sigue:

public static Complejo operator /(Complejo z1,Complejo z2)
{
Complejo cociente;
cociente = 1 / Math.Pow(z2.Modulo, 2) * (z1 * !z2);
return cociente;
}

Teniendo en cuenta estos cambios, nuestra clase Complejo queda como sigue:

/* Archivo: Complejo.cs */

using System;
using System.Text.RegularExpressions;
using System.Globalization;

public class Complejo
{
// Atributos
private double real;
private double imaginario;

// Constructores
public Complejo() { }

public Complejo(double parteReal, double parteImaginaria)
{
real = parteReal;
imaginario = parteImaginaria;
}
public Complejo(string valorComplejo)
{
if (EsComplejo(valorComplejo))
PartesComplejo(valorComplejo);
else
{
real = 0;
imaginario = 0;
CAPITULO CAPITULO CAPITULO CAPITULO 4 44 4 ELEMENTOS DE UN CLASE C#

pedrov.cs@hotmail.com
111 111 111 111

}
}

// Propiedades
public double Real
{
get { return real; }
set { real = value; }
}

public double Imaginario
{
get { return imaginario; }
set { imaginario = value; }
}

public double Modulo
{
get { return Tamano(); }
}

public double Argumento
{
get { return Angulo(); }
}

public string Valor
{
get { return FormatoSalida(); }
set
{
if (EsComplejo(value))
PartesComplejo(value);
else
{
real = 0;
imaginario = 0;
}
}
}

// Sobrecarga del mtodo ToString
public override string ToString()
{
return FormatoSalida();
}

// Sobrecarga del operador +
public static Complejo operator +(Complejo z1, Complejo z2)
{
Complejo suma = new Complejo();
suma.Real = z1.Real + z2.Real;
suma.Imaginario = z1.Imaginario + z2.Imaginario;
return suma;
}

// Sobrecarga del operador - (negativo)
public static Complejo operator -(Complejo z)
{
Complejo inverso = new Complejo();
CAPITULO CAPITULO CAPITULO CAPITULO 4 44 4 PROGRAMACION CON C#
www.pedrov.phpnet.us
112 112 112 112

inverso.Real = - z.Real;
inverso.Imaginario = - z.Imaginario;
return inverso;
}

// Sobrecarga del operador -
public static Complejo operator -(Complejo z1, Complejo z2)
{
Complejo resta = z1 + (- z2);
return resta;
}

// Sobrecarga del operador *
public static Complejo operator *(Complejo z1, Complejo z2)
{
Complejo producto = new Complejo();
double a1 = z1.Real, b1 = z1.Imaginario;
double a2 = z2.Real, b2 = z2.Imaginario;
producto.Real = a1 * a2 - b1 * b2;
producto.Imaginario = a1 * b2 + a2 * b1;
return producto;
}

public static Complejo operator *(double c, Complejo z)
{
Complejo z1 = new Complejo();
z1.Real = c * z.Real;
z1.Imaginario = c * z.Imaginario;
return z1;
}

public static Complejo operator *(Complejo z, double c)
{
return c * z;
}

// Sobrecarga del operador ! para el conjugado
public static Complejo operator !(Complejo z)
{
Complejo conjugado = new Complejo();
conjugado.Real = z.Real;
conjugado.Imaginario = - z.Imaginario;
return conjugado;
}

// Sobrecarga del operador /
public static Complejo operator /(Complejo z1, Complejo z2)
{
Complejo cociente;
cociente = 1 / Math.Pow(z2.Modulo, 2) * (z1 * !z2);
return cociente;
}

// Mtodos privados
private double Tamano()
{
double c;
c = Math.Sqrt(real * real + imaginario * imaginario);
return c;
}

CAPITULO CAPITULO CAPITULO CAPITULO 4 44 4 ELEMENTOS DE UN CLASE C#

pedrov.cs@hotmail.com
113 113 113 113

// Calcular el ngulo del complejo
private double Angulo()
{
double alfa;
if (real > 0)
alfa = Math.Atan(imaginario / real);
else if (real < 0)
if (imaginario > 0)
alfa = Math.PI + Math.Atan(imaginario / real);
else
alfa = - Math.PI + Math.Atan(imaginario / real);
else
if (imaginario > 0)
alfa = Math.PI / 2;
else if (imaginario < 0)
alfa = - Math.PI / 2;
else
alfa = 0;

return alfa;
}

// Mtodo para vlidar un nmero complejo
private bool EsComplejo(string cadena)
{
cadena = QuitarEspacios(cadena);
if (cadena.Length == 0) return false;
string sd = NumberFormatInfo.CurrentInfo.CurrencyDecimalSeparator;
cadena = cadena.Replace('.', Char.Parse(sd));
// Elementos bsicos de un complejo
string numero = @"(\d+(" + sd + @")?\d*)";
string i = @"(i)";
string signo = @"([+-])";

// Validacin para a, a + i, a + bi
string real = signo + "?" + numero;
string imaginario = "(" + signo + "(" + numero + ")?" + i + ")?";
string expresion1 = @"\A" + real + imaginario + @"\Z";
Regex complejo1 = new Regex(expresion1);
if (complejo1.IsMatch(cadena)) return true;

// Validacin para i, i + a, bi, bi + a
imaginario = signo + "?" + numero + "?" + i;
real = "(" + signo + numero + ")?";
string expresion2 = @"\A" + imaginario + real + @"\Z";
Regex complejo2 = new Regex(expresion2);
if (complejo2.IsMatch(cadena)) return true;

// Validacin para ib, ib + a
imaginario = signo + "?" + i + numero;
real = "(" + signo + numero + ")?";
string expresion3 = @"\A" + imaginario + real + @"\Z";
Regex complejo3 = new Regex(expresion3);
if (complejo3.IsMatch(cadena)) return true;

// Validacin para a + ib
real = signo + "?" + numero;
imaginario = signo + i + numero;
string expresion4 = @"\A" + real + imaginario + @"\Z";
Regex complejo4 = new Regex(expresion4);
CAPITULO CAPITULO CAPITULO CAPITULO 4 44 4 PROGRAMACION CON C#
www.pedrov.phpnet.us
114 114 114 114

return complejo4.IsMatch(cadena);
}

// Mtodo para separar la parte real y la parte imaginaria
private void PartesComplejo(string cadena)
{
string sd;
sd=NumberFormatInfo.CurrentInfo.CurrencyDecimalSeparator;
cadena = QuitarEspacios(cadena);
cadena = cadena.Replace('.', Char.Parse(sd));
string parteReal = "";
string parteImag = "";

string signo = @"([+-])";
string numero = @"(\d+(" + sd + @")?\d*)";
string i = @"(i)";

string imaginaria = signo + "?" + numero + "?" + i + numero + "?";
Regex imaginario1 = new Regex(imaginaria);
if (imaginario1.IsMatch(cadena))
{
// Cargar en mc las cadenas encontrada
MatchCollection mc = imaginario1.Matches(cadena);
// Recuperar la cadena encontrada
foreach(Match m in mc)
{
parteImag = m.ToString();
}
// Analizar algunos casos especiales
if (parteImag == "+i" || parteImag == "i")
parteImag = "1";
else if (parteImag == "-i")
parteImag = "-1";
else
parteImag = parteImag.Replace("i", "");

// Eliminar la parte imaginaria
parteReal = imaginario1.Replace(cadena, "");
}
else
{
parteReal = cadena;
parteImag = "0";
}

// Verificar la cadenas de texto vacas
if (parteReal.Length == 0) parteReal = "0";
if (parteImag.Length == 0) parteImag = "0";
// Convierte las cadenas de texto a double
// y las asigna a sus atributos respectivos
real = Double.Parse(parteReal);
imaginario = Double.Parse(parteImag);
}

// Elimina los espacios de una cadena de texto
private string QuitarEspacios(string cadena)
{
Regex espacio = new Regex(@"\s+");
cadena = espacio.Replace(cadena, "");
return cadena;
}
CAPITULO CAPITULO CAPITULO CAPITULO 4 44 4 ELEMENTOS DE UN CLASE C#

pedrov.cs@hotmail.com
115 115 115 115


// Da formato a la cadena de texto de salida
private string FormatoSalida()
{
if (real == 0)
return String.Format("{0}i", imaginario);
else
if (imaginario > 0)
return String.Format("{0} + {1}i", real, imaginario);
else if (imaginario < 0)
return String.Format("{0} - {1}i", real, - imaginario);
else
return real.ToString();
}
}

Compile el archivo con la instruccin,

> csc /t:library Complejo.cs

Con los cambios realizados ya contamos con una clase Complejo capaz de definir
objetos cuyo comportamiento se asemeja bastante a los nmeros que maneja C#. La
sobrecarga de los operadores aritmticos nos permitir codificar las operaciones de este
tipo en la misma forma como se hace con cualquier otro tipo numrico. Aunque talvez
no es el mejor, esta clase es un buen ejemplo de abstraccin y encapsulamiento, lo cual
permite contar con tipos complejos con un buen nivel de autonoma para resolver la
mayora de problemas propios de su naturaleza.

El siguiente programa hace uso de la clase Complejo y realiza algunas operaciones
con nmeros complejos:

/* Archivo: Ejemplo44.cs */
using System;
public class OperacionesComplejos
{
static void Main()
{
Complejo w = new Complejo();
Complejo z = new Complejo();
Console.Write("w = ");
w.Valor = Console.ReadLine();
Console.Write("z = ");
z.Valor = Console.ReadLine();
Console.Write("-w = {0}\n", -w);
Console.Write("w + z = {0}\n", w + z);
Console.Write("w - z = {0}\n", w - z);
Console.Write("w * z = {0}\n", w * z);
Console.Write("w / z = {0}\n", w / z);
Console.Write("!w = {0}\n", !w);
Console.Write("5w = {0}\n", 5 * w);
}
}

Guarde el archivo con el nombre ejemplo44.cs y complelo con la instruccin,

> csc /r:Complejo.dll ejemplo44.cs

CAPITULO CAPITULO CAPITULO CAPITULO 4 44 4 PROGRAMACION CON C#
www.pedrov.phpnet.us
116 116 116 116
El lector podr comprobar que la clase Complejo define objetos que en forma
autnoma se encargan de realizar la mayora de trreas que les impone su naturaleza,
incluyendo su operatoria y el formato para la salida de los resultados, sin necesidad de
que el programador tenga que preocuparse de esos detalles. Aunque se ha logrado un
buen nivel de abstraccin y encapsulamiento, no podemos decir que todo est
terminado. Por ejemplo, cuando se asigna a un objeto Complejo una cadena que no
corresponde a la forma de un nmero complejo, la clase no posee un mecanismo para
informar directamente sobre esa situacin anmala y en vez de eso asume un valor
nulo sin que el usuario se entere de tal situacin. Se podra implementar un mecanismo
de mensajes para informar al usuario que existe un error en la asignacin de un valor,
pero esto podra afectar la generalidad del componente y limitarlo a un nico entorno
de ejecucin.
El objetivo es crear un componente de software til en cualquier entorno, consola,
sistema grfico de Windows o web. En las siguientes secciones se describirn
elementos de la programacin con C# que permiten dar mayor robustez a los
componentes de software, y con ellos podremos resolver en forma tcnica las
deficiencias de nuestra clase Complejo.

Eventos
Un evento es una accin que produce un componente y a la que otro componente
puede responder o puede controlar mediante cdigo. Los eventos ms conocidos son
aquellos que se producen por accin del usuario, por ejemplo, al hacer clic con el botn
principal del ratn sobre un botn de una ventana se produce un evento que a su vez
ejecuta un cdigo de programacin. Sin embargo, esta ltima asociacin didctica para
intentar explicar el concepto de evento, ms que ayudar, puede distorsionar la nocin
que sobre el mismo impone la programacin orientada a objetos.
En la prctica un evento es una especie de procedimiento que ejecuta un objeto, pero
que se implementa fuera de su clase. Mejor, podemos ver a un evento como una
llamada a un procedimiento (o mtodo) que hace un objeto, el cual es programado en
la misma clase donde este existe.
Los eventos le sirven a una clase para proporcionar notificaciones cuando sucede algo
de inters. Una nocin muy general de cmo funciona un evento la podemos visualizar
en el siguiente esquema. Supongamos que tenemos una clase ClaseA,

class ClaseA
{
Miembro
{
Llamar a MiEvento;
}
}

en la cual uno de sus miembros ejecuta un procedimiento especial al que hemos
llamado MiEvento. Si este procedimiento se define como evento, su implementacin se
puede hacer para cada objeto derivado de ClaseA y en el espacio donde estos se
definan.
Si con la clase ClaseA se definen los objetos a1 y a2, en una clase ClaseB, esta clase
puede implementar mtodos que se ejecuten cuando cada uno de estos objetos,
internamente, hace el llamado al procedimiento especial que hemos denominado
MiEvento.

class ClaseB
{
CAPITULO CAPITULO CAPITULO CAPITULO 4 44 4 ELEMENTOS DE UN CLASE C#

pedrov.cs@hotmail.com
117 117 117 117
ClaseA a1 = new ClaseA();
ClaseA a2 = new ClaseB();

A MiEvento de a1 asociar MetodoA1;
A MiEvento de a2 asociar MetodoA2;

MetodoA1()
{
Implementacin de MiEvento para a1;
}

MetodoA2()
{
Implementacin de MiEvento para a2;
}
}

La ventaja que tiene el manejo de eventos es que el programador puede implementar
respuestas diferentes para el evento de cada objeto, adecundolas a sus intereses y a la
forma como desee personalizar el manejo de los componentes en cada caso especfico.
Incluso puede no hacer ninguna implementacin.
Vistas las cosas como nos las muestra este esquema, podemos decir que un evento es
una seal que envan los objetos a1 y a2, descendientes de la clase ClaseA, hacia la
clase ClaseB y que en este son respondidas mediante los mtodos MetodoA1 y
MetodoA2, respectivamente.
En el manejo de un evento es importante tener en cuenta tres elementos bsicos que
intervienen: el componente que genera el evento, un manejador de eventos y un
mtodo que responde a la seal.



Figura 3.14: Un componente genera un evento que es controlado por un manejador de
eventos, quien decide cual es el mtodo que se debe ejecutar.

La mayora de clases de .NET establecen uno o varios eventos en los objetos que
definen, lo cual le permite al programador personalizar su comportamiento de acuerdo
a la aplicacin donde se vayan a utilizar, y de esta manera imprimir mayor versatilidad
a la reutilizacin de componentes. La programacin de eventos facilita enormemente la
adecuacin y control de los componentes de software y al mismo tiempo acorta los
tiempos de desarrollo utilizados por los programadores.
Aunque el uso de eventos no es un tema nuevo en la programacin, la forma de
implementarlos si ha estado un tanto escondida para los programadores. Un buen
ejemplo de ello son los entornos de desarrollo integrado, herramientas estas que en la
mayora de los casos automatizan el proceso de creacin de los eventos y ponen a
disposicin del programador el espacio definitivo donde se requiere su intervencin.
Sin embargo, conocer como se implementa un evento puede ayudarle a sacar mayor
provecho de estos y la programacin de sus propios objetos con eventos adecuados.
Adems, en muchos casos los programadores tenemos la tendencia a relacionar los
eventos nicamente con objetos grficos, lo cual dificulta su concepcin y utilizacin
en componentes que no pertenezcan a este campo. Lo importante aqu, es dejar claro
que con C# a todo componente de software que desarrollemos le podemos asignar
eventos.
Componente
Manejador de
eventos
Mtodo
CAPITULO CAPITULO CAPITULO CAPITULO 4 44 4 PROGRAMACION CON C#
www.pedrov.phpnet.us
118 118 118 118
El proceso de creacin y programacin de un evento requiere la realizacin de una
serie de pasos que pueden hacer de este una tarea confusa. Para la descripcin lo
hemos dividido en dos etapas: implementacin y control (o respuesta).

Implementacin de un evento
Teniendo en cuenta el esquema utilizado en los prrafos anteriores, la implementacin
es el trabajo que se debe hacer por fuera de la clase ClaseB. En la implementacin de
un evento se deben tener en cuenta los siguientes pasos:

- Crear una clase que guarde los datos del evento. Esta clase se deriva de la clase
System.EventArgs y es quien se encarga de establecer los argumentos que puede
manejar el evento.

public class ClaseArgumentosEvento: EventArgs
{
// Datos del evento
}

- Definir un delegado para el evento. Un delegado es una clase (o es mejor decir, un
tipo) que se encarga de crear una referencia hacia un mtodo. Esta es la forma que
tiene .NET de crear punteros seguros hacia funciones.

public delegate tipo ManejadorEventos(
object ObjetoEmisor, ClaseArgumentosEvento e);

El manejador de eventos posee dos argumentos que son opcionales. El primero,
lleva el nombre del objeto que provoca el evento y el segundo es una variable que
identifica los argumentos del evento.
Siguiendo el esquema de nuestra explicacin, los anteriores elementos se definen
por fuera de la clase ClaseA y, obviamente, tambin de la clase ClaseB. Lo que
viene en seguida es lo que se debera incluir en una clase como ClaseA.

- Definir una clase que defina los objetos que van a generar el evento. En esta clase
se debe incluir una declaracin del evento en la forma siguiente:

public event ManejadorEventos NombreEvento;

- Establecer una llamada que provoque el evento. La llamada al evento debe estar
controlada. Es muy posible que un objeto no haya respondido al evento, en cuyo
caso al llamarlo se provocara un error en tiempo de ejecucin.

if (NombreEvento != null)
{
NombreEvento(this, e)
}

La palabra clave this se utiliza para hacer referencia a la identidad de un objeto. En
este caso es la nica forma de conocer quin est haciendo uso de esta llamada, por
que eso depende del nombre que se le haya dado al objeto definido en una
determinada instancia.

Ejemplo 4.5 Un evento con clase

En el siguiente ejemplo vamos a desarrollar una sencilla clase que se encarga de
calcular la ensima suma de un nmero. Un objeto definido a partir de la clase, recibir
un nmero entero positivo y realizar una suma secuencial desde 1 hasta dicho valor.
CAPITULO CAPITULO CAPITULO CAPITULO 4 44 4 ELEMENTOS DE UN CLASE C#

pedrov.cs@hotmail.com
119 119 119 119
Adems, cada vez que se realice una suma parcial, el objeto emitir un mensaje de
aviso a la clase que lo contiene.
Para hacer ms fcil la descripcin no utilizaremos un evento con parmetros, lo cual
nos evita tener que definir una clase de argumentos para el mismo. Vamos al segundo
paso del proceso. Se define el manejador de eventos, al cual llamaremos
EventoSumador y que se define en la siguiente forma:

public delegate void EventoSumador;

El siguiente paso es declarar el evento, pero esto solo tiene sentido si existe una clase
que lo vaya a implementar. Esta es nuestra clase objetivo, que va a llamarse Sumador.
Inicialmente la clase tendr la siguiente forma,

public class Sumador
{
public event EventoSumador SumaParcial;

// Otros elementos de la clase Sumador
}

El nombre del evento que se va a implementar es SumaParcial y observe que es del
tipo EventoSumador.
Nos detenemos en este punto y pasamos a implementar la funcionalidad de la clase
Sumador. Esta clase se encargar de realizar la suma de los nmeros mediante un ciclo
que ir realizando la suma 1 + 2 + 3 + , hasta el valor del nmero pasado a
cualquiera de sus objetos.

public class Sumador
{
// Declaracin del evento
public event EventoSumador SumaParcial;

// Atributos
int numero;

// Constructor
public Sumador(int valorNumero)
{
numero = valorNumero;
}

// Mtodo que realiza la suma total
public int Sumar()
{
int n = 0;
for (int i = 1; i <= numero; i++)
{
n = n + i;
}
return n;
}
}

El evento SumaParcial debe generarse justo en el instante en que se realice una suma,
es decir que su llamada debe incluirse en el cuerpo del ciclo for. As:

public int Sumar()
{
CAPITULO CAPITULO CAPITULO CAPITULO 4 44 4 PROGRAMACION CON C#
www.pedrov.phpnet.us
120 120 120 120
int n = 0;
for (int i = 1; i <= numero; i++)
{
n = n + i;
if (SumaParcial != null)
{
SumaParcial();
}
}
return n;
}

En definitiva, teniendo en cuenta este anlisis, nuestro archivo fuente, al que
llamaremos Sumador.cs, queda as:

/* Archivo: Sumador.cs */

using System;

public delegate void EventoSumador();

public class Sumador
{
// Declaracin del evento
public event EventoSumador SumaParcial;

// Atributos
int numero;

// Constructor
public Sumador(int valorNumero)
{
numero = valorNumero;
}

// Mtodo que se encarga de sumar
public int Sumar()
{
int n = 0;
for (int i = 1; i <= numero; i++)
{
n = n + i;
if (SumaParcial != null) SumaParcial();
}
return n;
}
}

Compile el archivo en un ensamblado, tipo librera dinmica, con la instruccin,

> csc /t:library Sumador.cs

Con esto tenemos un componente que define objetos que son capaces de provocar un
evento. La siguiente parte consistira en probar como funciona el evento que acabamos
de crear. Pero antes de hacerlo vamos a describir como se realiza esta fase.

Controlar un evento
Para programar un evento definido en otra clase, se debe definir y registrar un
controlador de eventos. Este proceso es el que se realiza en cada clase que vaya a hacer
CAPITULO CAPITULO CAPITULO CAPITULO 4 44 4 ELEMENTOS DE UN CLASE C#

pedrov.cs@hotmail.com
121 121 121 121
uso de un objeto y sus eventos y es igual para objetos creados por el programador o
para los que se han incluido en el Framework de .NET.
El proceso se realiza en dos pasos:
- Se debe definir un controlador de eventos, que como ya se dijo, es un mtodo que
debe tener la misma firma de mtodo (el mismo tipo e iguales parmetros) que el
delegado declarado para el evento.

tipo MetodoEvento(object ObjetoEmisor, ClaseArgumentosEvento e)
{
// Implementacin de la respuesta al evento
}

- Registrar el controlador de eventos, agregando el controlador al evento de un
objeto en particular.

ObjetoEmisor.NombreEvento += new ManejadorEventos(MetodoEvento);

Una vez agregado el mtodo, este es llamado cada vez que la clase provoca el evento.

Ejemplo 4.6 Un evento controlado

En seguida vamos utilizar el componente que desarrollamos en el ejemplo anterior.
Con este componente vamos definir un objeto e implementar el mtodo para su evento
SumaParcial.
El mtodo que controlar el evento lo definiremos as:

static void RSumaParcial()
{
Console.WriteLine("Evento de r...");
}

Este mtodo, nicamente se encarga de escribir en la consola la frase evento de r.
Para que se ejecute cada vez que la clase genere el evento, es necesario registrar el
mtodo asignndolo al evento SumaParcial de r.

r.SumaParcial += new EventoSumador(RSumaParcial);

Esta asignacin debe hacerse dentro de un mtodo de la clase, despus de haber creado
el objeto que provoca el evento. La mayora de aplicaciones desarrolladas con ayuda
de un entorno integrado de desarrollo realizan esta asignacin en un mtodo de carga
inicial que es llamado directamente por el mtodo Main, o en su defecto lo incluyen en
el cuerpo de este mtodo. Sin embargo, esto no significa que obligatoriamente deba
realizarse de esa manera, ya que el programador puede realizarlo en cualquier otro
mtodo aunque sus efectos pueden tener algunas variaciones. Es importante tener en
cuenta que el evento no llama a su mtodo controlador hasta tanto no se haya ejecutado
el mtodo que lo registra.
Este es el archivo fuente de nuestro programa que hace uso del componente Sumador:

/* Archivo: ejemplo46.cs */

using System;

public class Programa
{
static void Main()
CAPITULO CAPITULO CAPITULO CAPITULO 4 44 4 PROGRAMACION CON C#
www.pedrov.phpnet.us
122 122 122 122

{
Sumador r = new Sumador(10);
r.SumaParcial += new EventoSumador(RSumaParcial);
int total = r.Sumar();
Console.Write("Total = {0}\n", total);
}

static void RSumaParcial()
{
Console.WriteLine("Evento de r...");
}
}

Compile el programa con la instruccin,

> csc /r:Sumador.dll ejemplo46.cs

Al ejecutar el programa, se carga en el objeto el valor 10, lo cual implica que se
debern realizar diez ciclos y de hecho se llama igual nmero de veces al evento. La
salida del programa se parece a lo siguiente,

Evento de r...
Evento de r...
Evento de r...
Evento de r...
Evento de r...
Evento de r...
Evento de r...
Evento de r...
Evento de r...
Evento de r...
Total = 55

Un detalle final. El mtodo controlador del evento se ha definido privado y esttico.
Ninguna de las dos cosas es un requisito impuesto por las reglas de control de eventos.
El mtodo definido es uno ms de los muchos que el programador puede implementar,
y como tal puede tener cualquier nivel de accesibilidad. La opcin de esttico es un
requisito impuesto por el mtodo Main. Cuando se hace el registro del mtodo
controlador,

r.SumaParcial += new EventoSumador(RSumaParcial);

al incluir unicamente el nombre del mtodo, implcitamente se produce una referencia
al objeto que contiene a dicho mtodo. Como esta lnea se encuentra en el cuerpo de un
mtodo esttico, no es posible hacer referencia a un objeto de la misma clase y en
consecuencia la nica solucin es definir un mtodo esttico, el cual para ser llamado
no requiere una referencia a objeto alguno.
En el ejemplo anbterior, se han obviado algunos detalles de los eventos, con el objetivo
de facilitar la comprensin de su lgica de implementacin y control por parte del
lector poco experimentado en el tema.

Ejemplo 4.7 Un evento con argumentos

Basndonos en los ejemplos anteriores vamos a implementar dos eventos que trabajen
con argumentos. Uno de los eventos se producir cuando exista un error en el valor de
CAPITULO CAPITULO CAPITULO CAPITULO 4 44 4 ELEMENTOS DE UN CLASE C#

pedrov.cs@hotmail.com
123 123 123 123
entrada y el otro corresponder a una nueva versin del evento SumaParcial, el cual
permitir conocer los diferentes valores que se van generando en la suma.
En los dos ltimos ejemplos se mostr la forma de implementar un evento en una
clase, y tambin la forma de controlar ese evento en un objeto que lo genera. Pero el
evento que se ha programado solo se limita a enviar una seal al cliente y no le retorna
ningn parmetro. El objetivo era mostrar al lector la forma de generar sus propios
eventos en los componentes que cree. Ahora vamos a incluir dos eventos, para nuestro
sumador, que cumplan las especificaciones de la mayora de eventos de .NET.
Los eventos generados por los objetos de .NET mantienen un esquema de
presentacin. Todo evento devuelve dos parmetros: el primero es la identidad del
objeto que lo gener, y el segundo corresponde a una variable que lleva los datos
referentes a los argumentos del evento.
Lo primero que vamos a hacer es programar una clase que nos permita fijar los datos
de los eventos. La clase se llamar SigmaArgumentosEvento y contendr datos sobre
errores y valores numricos relacionados con la sumatoria. La devolucin de un error
se realizar a travs de los atributos Error y MensajeError. Un tercer dato numrico,
Valor, servir para retornar cualquier valor numrico que se requiera.

public class SigmaArgumentosEvento : EventArgs
{
bool error = false;
string mensajeError = "";
int valor;


public bool Error
{
get { return error; }
set { error = value; }
}

public string MensajeError
{
get { return mensajeError; }
set { mensajeError = value; }
}

public int Valor
{
get { return valor; }
set { valor = value; }
}
}

Toda clase que permita especificar los datos de eventos se debe heredar de la clase
EventArgs. Aunque esta clase no especifica datos, si es importante derivar a partir de
ella, para mantener una base comn con todos los eventos generados en .NET. Se debe
tener en cuenta que muchas clases del Framework no heredan directamente de
EventArgs, sino de otras clases que a su vez heredaron de esta. Igual, siempre existir
una lnea de jerarqua en la cual EventArgs es la base.
Utilizando la clase anterior, ahora ya es posible definir un manejador de eventos que
permita controlar eventos con datos. La definicin de dicho manejador queda como
sigue:

public delegate void EventoSumador(Object emisor,
SigmaArgumentosEvento e);
CAPITULO CAPITULO CAPITULO CAPITULO 4 44 4 PROGRAMACION CON C#
www.pedrov.phpnet.us
124 124 124 124

El primer parmetro del manejador de eventos nos permitir enviar una referencia al
objeto que gener el evento. Este parmetro puede ser importante en un momento dado
para determinar quin gener el evento, sobretodo por que, en la prctica, puede ser
necesario hacer que un mismo mtodo controle a varios eventos.
A continuacin viene la clase que implementar dos eventos basados en el manejador
anterior, que se identificar con el nombre de Sigma. Esta clase es una nueva versin
del sumador que se desarrollo en el anterior ejemplo y tan solo contiene algunas
modificaciones con respecto a los eventos que va a implementar. Estos eventos se
llaman SumaParcial y EntradaNumero y su definicin es la siguiente.

public event EventoSumador SumaParcial;
public event EventoSumador EntradaNumero;

El evento SumaParcial se generar cada que se realice una suma en el cuerpo de un
ciclo for, y devolver el valor de la suma acumulada hasta ese momento. Este valor se
devolver a travs de la propiedad Valor, de dicho evento. Dado que la generacin de
un mismo evento puede necesitarse hacer desde diferentes miembros de la clase es
recomendable incluirla en un mtodo como el siguiente:

private void LlamarSumaParcial(int suma)
{
SigmaArgumentosEvento e=new SigmaArgumentosEvento();
e.Valor = suma;
if (SumaParcial != null)
{
SumaParcial(this, e);
}
}

De esta manera cualquier miembro de la clase puede llamar nicamente al mtodo para
generar un determinado evento. Este no es un requisito de programacin, pero si ayuda
a hacer ms claro el cdigo y su mantenimiento.
Observe que el evento SumaParcial, a travs del operador this, devuelve una referencia
al objeto que lo est generando en un momento dado. En la variable e se devuelven los
valores del evento como tal.
El evento EntradaNumero se ha diseado para producirse cada que va a iniciarse el
proceso de la sumatoria. Este debe informar a su cliente que se present un error de
ingreso de datos cuando el nmero asignado para la sumatoria sea menor que 1. El
mtodo generador del evento es el siguiente:

private void LlamarEntradaNumero()
{
SigmaArgumentosEvento e=new SigmaArgumentosEvento();
if (numero < 1)
{
e.Error = true;
e.MensajeError="El nmero ingresado
es incorrecto.";
e.Valor = numero;
}

if (EntradaNumero != null)
{
EntradaNumero(this, e);
}
}
CAPITULO CAPITULO CAPITULO CAPITULO 4 44 4 ELEMENTOS DE UN CLASE C#

pedrov.cs@hotmail.com
125 125 125 125

En definitiva, y con las explicaciones mostradas en los comentarios de codificacin, la
clase Sigma queda as:

public class Sigma
{
public event EventoSumador SumaParcial;
public event EventoSumador EntradaNumero;

//Atributos
int numero;

// Constructor
public Sigma() { }

// Propiedades
public int Numero
{
get { return numero; }
set
{
numero = value;
LlamarEntradaNumero();
}
}

// Mtodo que realiza la suma total
public int Sumar()
{
int n = 0;

for (int i = 1; i <= numero; i++)
{
n = n + i;
LlamarSumaParcial(n);
}
return n;
}

// Mtodos generadores de eventos

private void LlamarSumaParcial(int suma)
{
SigmaArgumentosEvento
e = new SigmaArgumentosEvento();
e.Valor = suma;
if (SumaParcial != null)
{
SumaParcial(this, e);
}
}

private void LlamarEntradaNumero()
{
SigmaArgumentosEvento
e = new SigmaArgumentosEvento();
if (numero < 1)
{
e.Error = true;
e.MensajeError = "El nmero ingresado
es incorrecto.";
CAPITULO CAPITULO CAPITULO CAPITULO 4 44 4 PROGRAMACION CON C#
www.pedrov.phpnet.us
126 126 126 126
e.Valor = numero;
}

if (EntradaNumero != null)
{
EntradaNumero(this, e);
}
}
}

En el siguiente es el archivo se incluyen la clase Sigma y todos los elementos que
hacen necesita para generar los eventos en estudio.

/* Archivo: Sigma.cs */

using System;

public class SigmaArgumentosEvento : EventArgs
{
string mensajeError = "";
bool error = false;
int valor;

public int Valor
{
get { return valor; }
set { valor = value; }
}

public string MensajeError
{
get { return mensajeError; }
set { mensajeError = value; }
}

public bool Error
{
get { return error; }
set { error = value; }
}
}

public delegate void EventoSumador(Object emisor, SigmaArgumentosEvento e);

public class Sigma
{
public event EventoSumador SumaParcial;
public event EventoSumador EntradaNumero;

//Atributos
int numero;

// Constructor
public Sigma() { }

// Propiedades
public int Numero
{
get { return numero; }
set

CAPITULO CAPITULO CAPITULO CAPITULO 4 44 4 ELEMENTOS DE UN CLASE C#

pedrov.cs@hotmail.com
127 127 127 127

{
numero = value;
LlamarEntradaNumero();
}
}
// Mtodo que se encarga realizar la suma total
public int Sumar()
{
int n = 0;

for (int i = 1; i <= numero; i++)
{
n = n + i;
LlamarSumaParcial(n);
}
return n;
}

// Mtodos generadores de eventos

private void LlamarSumaParcial(int suma)
{
SigmaArgumentosEvento e = new SigmaArgumentosEvento();
e.Valor = suma;
if (SumaParcial != null)
{
SumaParcial(this, e);
}
}

private void LlamarEntradaNumero()
{
SigmaArgumentosEvento e = new SigmaArgumentosEvento();
if (numero < 1)
{
e.Error = true;
e.MensajeError = "El nmero ingresado es incorrecto.";
e.Valor = numero;
}

if (EntradaNumero != null)
{
EntradaNumero(this, e);
}
}
}

Compile el archivo en un ensamblado dll con la instruccin,

> csc /t:library Sigma.cs

El siguiente programa utiliza dos objetos derivados de la clase Sigma, e implementa los
mtodos que controlan sus eventos..

/* Archivo: ejemplo47.cs */

using System;
using System.Windows.Forms;

public class Programa
CAPITULO CAPITULO CAPITULO CAPITULO 4 44 4 PROGRAMACION CON C#
www.pedrov.phpnet.us
128 128 128 128

{
public static void Main(string[] args)
{
Sigma suma1;
Sigma suma2;
suma1 = new Sigma();
// Registro de controladores de eventos
suma1.EntradaNumero += new EventoSumador(SumaEntradaNumero);
suma1.SumaParcial += new EventoSumador(Suma1SumaParcial);
// Realizar suma para -1
suma1.Numero = -1;
Console.Write("Suma1 = {0}\n", suma1.Sumar());

suma2 = new Sigma();
// Registro de controladores de eventos para suma2
suma2.EntradaNumero += new EventoSumador(SumaEntradaNumero);
suma2.SumaParcial += new EventoSumador(Suma2SumaParcial);
// Realizar suma para 10
suma2.Numero = 10;
Console.WriteLine("Suma2 = {0}\n", suma2.Sumar());
}

// Controladores de eventos
static void Suma1SumaParcial(object emisor, SigmaArgumentosEvento e)
{
Console.WriteLine(e.Valor);
}

static void Suma2SumaParcial(object emisor, SigmaArgumentosEvento e)
{
Console.WriteLine(e.Valor);
}

static void SumaEntradaNumero(object emisor, SigmaArgumentosEvento e)
{
Sigma s = (Sigma)emisor;

if (e.Error)
MessageBox.Show(e.MensajeError, "N = " + s.Numero);
else
{
MessageBox.Show("Iniciando suma...", "N = " + s.Numero);
}
}
}

Compile el programa con la instruccin,

> csc /r:Sigma.dll ejemplo47.cs

Un aspecto importante. El evento EntradaNumero de los dos objetos, suma1 y
suma2, ha sido controlado por un mismo mtodo, SumaEntradaNumero. Este mtodo
muestra, en una caja de mensajes, informacin relacionada con el objeto que genera el
evento, especficamente el valor numrico que se ha ingresado. Aqu es donde se
aprovecha el primer parmetro del evento para conocer cual fue el objeto que lo
gener.
La forma como se nombran los mtodos controladores es muy importante cuando se va
a trabajar con muchos objetos, para evitar confusiones y facilitar el mantenimiento del
cdigo. Aqu se ha seguido las recomendaciones hechas por la documentacin del
CAPITULO CAPITULO CAPITULO CAPITULO 4 44 4 ELEMENTOS DE UN CLASE C#

pedrov.cs@hotmail.com
129 129 129 129
Framework de .NET. Un mtodo controlador debe nombrarse iniciando con el nombre
del objeto generador, seguido de una cadena equivalente al nombre del evento.

Ejemplo 4.8 Un evento de la consola

El objeto de .NET que ms hemos utilizado hasta el momento ha sido el objeto
Console. Este objeto, es uno de los pocos del Framework que tan solo cuenta con un
evento (al menos hasta la versin 3.0). Ese evento se llama CancelKeyPress y se
genera cada que el usuario de una aplicacin cancela, en forma forzada, la finalizacin
de una aplicacin de consola presionando las teclas CTRL+C.
En este ejemplo vamos a programar un mtodo para controlar el evento
CancelKeyPress y aprovecharlo para mostrar un mensaje de cancelacin en una caja
de texto. El siguiente es el mtodo que controla este evento:

static void ConsoleCancelKeyPress(object emisor, EventArgs e)
{
MessageBox.Show("Operacin cancelada...");
}

El evento CancelKeyPress ha sido definido mediante el manejador de eventos del
objeto Console que se identifica con el nombre ConsoleCancelEventHandler. Por lo
tanto el registro del mtodo controlador de este evento se debe realizar de la siguiente
forma:

Console.CancelKeyPress += new
ConsoleCancelEventHandler(ConsoleCancelKeyPress);

El siguiente es el programa que muestra el evento CancelKeyPress en accin:

/* Archivo: Ejemplo48.cs */

using System;
using System.Windows.Forms;

public class Programa
{
public static void Main(string[] args)
{
Console.CancelKeyPress +=
new ConsoleCancelEventHandler(ConsoleCancelKeyPress);
Console.WriteLine("Hola consola!");
Console.Write("La aplicacin an no ha terminado . . .");
Console.ReadKey(true);
}

static void ConsoleCancelKeyPress(object emisor, EventArgs e)
{
MessageBox.Show("Operacin cancelada ...", "Cancelado");
}
}

Compile el programa con la instruccin,

> csc ejemplo.cs

El programa se ejecuta hasta la lnea

Console.ReadKey(true);
CAPITULO CAPITULO CAPITULO CAPITULO 4 44 4 PROGRAMACION CON C#
www.pedrov.phpnet.us
130 130 130 130
y se queda esperando a que el usuario presione una tecla. Mientras esto no ocurra el
programa sigue en memoria. Si en este punto el usuario presiona la combinacin de
teclas CTRL+C se est forzando al sistema a terminarlo y se genera el evento
CancelKeyPress. Obviamente, si el usuario no presiona estas teclas, el evento nunca
se genera.

Ejemplo 4.9 Evento de entrada para la clase Complejo

En esta prctica vamos a desarrollar un evento generado por la clase Complejo,
despus de que se ingresa y verifica un valor complejo en forma de cadena de texto. A
travs de este evento el cliente que haga uso de la clase podr programar un
mecanismo para controlar los posibles errores que puedan ocasionarse en el ingreso de
los valores complejos. El evento se llamar NumeroComprobado.
El evento debe generarse siempre que se ingrese un valor en forma de cadena de texto,
sin importar si corresponde o no a un complejo. Aqu necesitamos que el evento
retorne un argumento informando sobre el posible error encontrado en la validacin de
la cadena de texto. Esto se realizar definiendo el argumento del tipo
ComplejoArgumentosEvento, en la siguiente forma

public class ComplejoArgumentosEvento : EventArgs
{
// Campos
bool error;

// Propiedades
public bool Error
{
get { return error; }
set { error = value; }
}
}

Definimos un manejador de eventos para la clase Complejo,

public delegate void ComplejoEvento(
object ObjetoEmisor, ComplejoArgumentosEvento e);

En el cuerpo de la clase complejo se define el evento NumeroComprobado as:

public class Complejo
{
public event ComplejoEvento NumeroComprobado;

// Implementacin de Complejo...
}

A su vez, definimos un mtodo que se encargue de generar el evento. Esto nos permite
hacer la llamada desde ms de un proceso de la clase Complejo, as

private void GenerarNumeroComprobado(bool existeError)
{
ComplejoArgumentosEvento e = new
ComplejoArgumentosEvento();
e.Error = existeError;
if (NumeroComprobado != null) {
NumeroComprobado(this, e);
}
}
CAPITULO CAPITULO CAPITULO CAPITULO 4 44 4 ELEMENTOS DE UN CLASE C#

pedrov.cs@hotmail.com
131 131 131 131

Como ya se dijo este evento ser generado desde los mtodos que se encargan de leer
cadenas de texto y verificar si corresponde a un complejo. Estos son, uno de los
constructores

public Complejo(string valorComplejo)
{
bool existeError = false;

if (EsComplejo(valorComplejo))
PartesComplejo(valorComplejo);
else
{
real = 0;
imaginario = 0;
existeError = true;
}
// Generar el evento
GenerarNumeroComprobado(existeError);
}

y la propiedad Valor,

public string Valor
{
get { return FormatoSalida(); }
set
{
bool existeError = false;

if (EsComplejo(value))
PartesComplejo(value);
else
{
real = 0;
imaginario = 0;
existeError = true;
}
// Generar el evento
GenerarNumeroComprobado(existeError);
}
}

Cargue en un editor de texto el archivo Complejo.cs que se viene trabajando, agregue
la clase ComplejoArgumentosEvento y realice los cambios que aqu se han descrito. En
definitiva el archivo queda como sigue:

/* Archivo: Complejo.cs */

using System;
using System.Text.RegularExpressions;
using System.Globalization;

public class ComplejoArgumentosEvento : EventArgs
{
// Campos
bool error;



CAPITULO CAPITULO CAPITULO CAPITULO 4 44 4 PROGRAMACION CON C#
www.pedrov.phpnet.us
132 132 132 132

// Propiedades
public bool Error
{
get { return error; }
set { error = value; }
}
}

public delegate void ComplejoEvento(object Emisor, ComplejoArgumentosEvento e);

public class Complejo
{
// Atributos
private double real;
private double imaginario;

// Eventos
public event ComplejoEvento NumeroComprobado;

// Constructores
public Complejo() { }

public Complejo(double parteReal, double parteImaginaria)
{
real = parteReal;
imaginario = parteImaginaria;
}

public Complejo(string valorComplejo)
{
bool existeError = false;

if (EsComplejo(valorComplejo))
PartesComplejo(valorComplejo);
else
{
real = 0;
imaginario = 0;
existeError = true;
}
// Generar el evento
GenerarNumeroComprobado(existeError);
}

// Propiedades
public double Real
{
get { return real; }
set { real = value; }
}

public double Imaginario
{
get { return imaginario; }
set { imaginario = value; }
}

public double Modulo
{
get { return Tamano(); } }

CAPITULO CAPITULO CAPITULO CAPITULO 4 44 4 ELEMENTOS DE UN CLASE C#

pedrov.cs@hotmail.com
133 133 133 133


public double Argumento
{
get { return Angulo(); }
}

public string Valor
{
get { return FormatoSalida(); }
set
{
bool existeError = false;

if (EsComplejo(value))
PartesComplejo(value);
else
{
real = 0;
imaginario = 0;
existeError = true;
}
// Generar el evento
GenerarNumeroComprobado(existeError);
}
}

// Sobrecarga del mtodo ToString
public override string ToString()
{
return FormatoSalida();
}

// Sobrecarga del operador +
public static Complejo operator +(Complejo z1, Complejo z2)
{
Complejo suma = new Complejo();
suma.Real = z1.Real + z2.Real;
suma.Imaginario = z1.Imaginario + z2.Imaginario;
return suma;
}

// Sobrecarga del operador - (negativo)
public static Complejo operator -(Complejo z)
{
Complejo inverso = new Complejo();
inverso.Real = - z.Real;
inverso.Imaginario = - z.Imaginario;
return inverso;
}

// Sobrecarga del operador -
public static Complejo operator -(Complejo z1, Complejo z2)
{
Complejo resta = z1 + (- z2);
return resta;
}

// Sobrecarga del operador *
public static Complejo operator *(Complejo z1, Complejo z2)
{
CAPITULO CAPITULO CAPITULO CAPITULO 4 44 4 PROGRAMACION CON C#
www.pedrov.phpnet.us
134 134 134 134

Complejo producto = new Complejo();
double a1 = z1.Real, b1 = z1.Imaginario;
double a2 = z2.Real, b2 = z2.Imaginario;
producto.Real = a1 * a2 - b1 * b2;
producto.Imaginario = a1 * b2 + a2 * b1;
return producto;
}

public static Complejo operator *(double c, Complejo z)
{
Complejo z1 = new Complejo();
z1.Real = c * z.Real;
z1.Imaginario = c * z.Imaginario;
return z1;
}

public static Complejo operator *(Complejo z, double c)
{
return c * z;
}

// Sobrecarga del operador ! para el conjugado
public static Complejo operator !(Complejo z)
{
Complejo conjugado = new Complejo();
conjugado.Real = z.Real;
conjugado.Imaginario = - z.Imaginario;
return conjugado;
}

// Sobrecarga del operador /
public static Complejo operator /(Complejo z1, Complejo z2)
{
Complejo cociente;
cociente = 1 / Math.Pow(z2.Modulo, 2) * (z1 * !z2);
return cociente;
}

// Mtodos privados
private double Tamano()
{
double c;
c = Math.Sqrt(real * real + imaginario * imaginario);
return c;
}

// Calcular el ngulo del complejo
private double Angulo()
{
double alfa;
if (real > 0)
alfa = Math.Atan(imaginario / real);
else if (real < 0)
if (imaginario > 0)
alfa = Math.PI + Math.Atan(imaginario / real);
else
alfa = - Math.PI + Math.Atan(imaginario / real);
else
if (imaginario > 0)
alfa = Math.PI / 2;
else if (imaginario < 0)
CAPITULO CAPITULO CAPITULO CAPITULO 4 44 4 ELEMENTOS DE UN CLASE C#

pedrov.cs@hotmail.com
135 135 135 135

alfa = - Math.PI / 2;
else
alfa = 0;

return alfa;
}

// Mtodo para vlidar un nmero complejo
private bool EsComplejo(string cadena)
{
cadena = QuitarEspacios(cadena);
if (cadena.Length == 0) return false;
string sd = NumberFormatInfo.CurrentInfo.CurrencyDecimalSeparator;
cadena = cadena.Replace('.', Char.Parse(sd));
// Elementos bsicos de un complejo
string numero = @"(\d+(" + sd + @")?\d*)";
string i = @"(i)";
string signo = @"([+-])";

// Validacin para a, a + i, a + bi
string real = signo + "?" + numero;
string imaginario = "(" + signo + "(" + numero + ")?" + i + ")?";
string expresion1 = @"\A" + real + imaginario + @"\Z";
Regex complejo1 = new Regex(expresion1);
if (complejo1.IsMatch(cadena)) return true;

// Validacin para i, i + a, bi, bi + a
imaginario = signo + "?" + numero + "?" + i;
real = "(" + signo + numero + ")?";
string expresion2 = @"\A" + imaginario + real + @"\Z";
Regex complejo2 = new Regex(expresion2);
if (complejo2.IsMatch(cadena)) return true;

// Validacin para ib, ib + a
imaginario = signo + "?" + i + numero;
real = "(" + signo + numero + ")?";
string expresion3 = @"\A" + imaginario + real + @"\Z";
Regex complejo3 = new Regex(expresion3);
if (complejo3.IsMatch(cadena)) return true;

// Validacin para a + ib
real = signo + "?" + numero;
imaginario = signo + i + numero;
string expresion4 = @"\A" + real + imaginario + @"\Z";
Regex complejo4 = new Regex(expresion4);
return complejo4.IsMatch(cadena);
}

// Mtodo para separar la parte real y la parte imaginaria
private void PartesComplejo(string cadena)
{
string sd;
sd=NumberFormatInfo.CurrentInfo.CurrencyDecimalSeparator;
cadena = QuitarEspacios(cadena);
cadena = cadena.Replace('.', Char.Parse(sd));
string parteReal = "";
string parteImag = "";

string signo = @"([+-])";
string numero = @"(\d+(" + sd + @")?\d*)";
CAPITULO CAPITULO CAPITULO CAPITULO 4 44 4 PROGRAMACION CON C#
www.pedrov.phpnet.us
136 136 136 136

string i = @"(i)";

string imaginaria = signo + "?" + numero + "?" + i + numero + "?";
Regex imaginario1 = new Regex(imaginaria);
if (imaginario1.IsMatch(cadena))
{
// Cargar en mc las cadenas encontrada
MatchCollection mc = imaginario1.Matches(cadena);
// Recuperar la cadena encontrada
foreach(Match m in mc)
{
parteImag = m.ToString();
}
// Analizar algunos casos especiales
if (parteImag == "+i" || parteImag == "i")
parteImag = "1";
else if (parteImag == "-i")
parteImag = "-1";
else
parteImag = parteImag.Replace("i", "");

// Eliminar la parte imaginaria
parteReal = imaginario1.Replace(cadena, "");
}
else
{
parteReal = cadena;
parteImag = "0";
}

// Verificar la cadenas de texto vacas
if (parteReal.Length == 0) parteReal = "0";
if (parteImag.Length == 0) parteImag = "0";
// Convierte las cadenas de texto a double
// y las asigna a sus atributos respectivos
real = Double.Parse(parteReal);
imaginario = Double.Parse(parteImag);
}

private string QuitarEspacios(string cadena)
{
Regex espacio = new Regex(@"\s+");
cadena = espacio.Replace(cadena, "");
return cadena;
}

private string FormatoSalida()
{
if (real == 0)
return String.Format("{0}i", imaginario);
else
if (imaginario > 0)
return String.Format("{0} + {1}i", real, imaginario);
else if (imaginario < 0)
return String.Format("{0} - {1}i", real, - imaginario);
else
return real.ToString();
}

private void GenerarNumeroComprobado(bool existeError)
{
CAPITULO CAPITULO CAPITULO CAPITULO 4 44 4 ELEMENTOS DE UN CLASE C#

pedrov.cs@hotmail.com
137 137 137 137

ComplejoArgumentosEvento e = new ComplejoArgumentosEvento();
e.Error = existeError;
if (NumeroComprobado != null)
{
NumeroComprobado(this, e);
}
}
}

Vuelva a compilar el archivo con la instruccin,

> csc /t:library Complejo.cs

El siguiente programa utilice el evento NumeroComprobado para mostrar al usuario un
mensaje informndole si el nmero ingresado fue correcto o incorrecto.

/* Archivo: Ejemplo49.cs */

using System;

public class Programa
{
static void Main()
{
Complejo zeta = new Complejo();
zeta.NumeroComprobado += new ComplejoEvento(ZetaNumeroComprobado);
zeta.Valor = Console.ReadLine();
}

static void ZetaNumeroComprobado(object emisor, ComplejoArgumentosEvento e)
{
if (e.Error)
Console.WriteLine("Incorrecto");
else
Console.WriteLine("Correcto...");
}
}

Compile el programa con el comando,

> csc /r:Complejo.dll ejemplo49.cs

En este caso se utiliz la consola para enviar un mensaje al usuario, pero tambin pudo
haberse programado en una caja de mensajes grfica. Esto le da ms versatilidad a la
clase Complejo, ya que el programador puede utilizarla en diferentes contextos y
adecuar sus mensajes al entorno de desarrollo donde se aplique.

You might also like