Programacion c sharp_04
Upcoming SlideShare
Loading in...5
×
 

Like this? Share it with your network

Share

Programacion c sharp_04

on

  • 527 views

Programación básica c sharp

Programación básica c sharp

Statistics

Views

Total Views
527
Views on SlideShare
527
Embed Views
0

Actions

Likes
0
Downloads
30
Comments
0

0 Embeds 0

No embeds

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

Programacion c sharp_04 Document Transcript

  • 1. Capítulo 4 ELEMENTOS DE UNA CLASE C#La clase como elemento fundamental de la programación orientada a objetos requieretoda la atención posible por parte del programador. El correcto manejo de suselementos y el buen aprovechamiento de los mismos es lo que permitirá sacar elmáximo provecho de esta metodología de programación, y sobretodo hacer más fácil yefectiva la tarea de programar una aplicación de software. La teoría general de laprogramación orientada a objetos define unos elementos básicos que conforman unaclase, pero cada uno de los lenguajes de programación ha realizado sus propios aportesa estos elementos, especialmente ampliando su funcionalidad o representándolosmediante elementos propios del lenguaje, con el objetivo de volverlos más potentes yfáciles de manejar.El lenguaje C# ha dotado a las clases de una serie de elementos que en aparienciaamplían el conjunto de elementos definidos en la teoría general, pero más que eso, enrealidad lo que se busca es poner a disposición del programador toda una gama derecursos que le permitan construir componentes de software que cumplan todos losrequerimientos de la programación orientada a objetos y permitan expresar loselementos generales en la forma más efectiva y eficiente posible.Estos son los elementos básicos que constituyen una clase en C#: Constructores Destructores Constantes Campos Métodos Propiedades Indizadores Operadores Eventos Delegados EstructurasConstructoresUn constructor es un método de una clase que se ejecuta automáticamente cada vezque se crea una instancia de la misma. Aunque no se especifique, como ha sucedido entodas las clases que hasta ahora hemos implementado, el compilador de C# siempreestablece internamente un método constructor que no hace absolutamente nada.Además, siempre que vamos a crear un objeto definido por una clase, hacemos unllamado a su constructor en el momento que creamos una nueva instancia con eloperador new. Por ejemplo, retomando nuestra clase de los números complejos,definida en el anterior capítulo, en las siguientes dos líneas de código, la primera líneadefine un objeto de tipo Complejo y la segunda se encarga de llamar al constructor deesa clase.
  • 2. 86 CAPITULO 4 PROGRAMACION CON C# 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 automática para dar una determinada configuración a un objeto, o incluso para hacerlo funcional. Un método constructor lleva el mismo nombre de la clase que lo contiene y se debe declarar con nivel de accesibilidad pública (public), aunque también es admitido el nivel interno (internal). El nivel de accesibilidad pública 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 método 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 parámetros que manejan. En el siguiente fragmento de código, la clase Complejo implementa tres constructores, public class Complejo { public Complejo() { } public Complejo(string NumeroComplejo) { // Código para procesar el parámetro } public Complejo(double Real, double Imaginario) { // Código para procesar los parámetros } } En el momento de la ejecución, de acuerdo a los parámetros que reciba el constructor, el sistema decidirá a cual de estos constructores llamar. Si la declaración 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 parámetro un valor tipo cadena de texto. Destructor Un destructor es un método que se ejecuta automáticamente justo antes de que un objeto sea destruido. A diferencias del constructor, un método destructor no puede sobrecargarse ni tampoco heredarse. Además, no puede invocarse explícitamente. El lenguaje C# cuenta con una herramienta llamada recolector de basura que se encarga de destruir aquellos objetos que ya no se estén utilizando y aún siganwww.pedrov.phpnet.us
  • 3. CAPITULO 4 ELEMENTOS DE UN CLASE C# 87ocupando espacio en memoria. Como ya se había mencionada anteriormente, eloperador new, aplicado a la creación de un objeto, se encarga de asignar la memorianecesaria que este necesita. En el momento que el objeto deja de ser referenciado, elrecolector de basura se encarga de liberar la memoria que ocupaba. Un objeto se dejade referenciar cuando la ejecución sale del ámbito que lo definió. En los ejemplos quehemos analizado hasta el momento, la mayoría de objetos deja de ser referenciadocuando se finaliza la ejecución del método Main.La sintaxis para implementar el destructor es la siguiente: ~NombreClase() { // Acciones antes de destruir un objeto }En la práctica un método destructor es utilizado para realizar tareas que se necesitanantes de destruir el objeto, como puede ser: guardar valores de datos, limpiar lamemoria manualmente o fijar alguna configuración especial. Ejemplo 4.1 Un constructor y un destructorEn el siguiente ejemplo vamos a programar un constructor y un destructor paradeterminar el instante en que se ejecutan cada uno de ellos. Supongamos que tenemosuna clase Estudiante que permite procesar algunos datos de estudiantes, y presenta dossobrecargas para su constructor. La primera sobrecarga no hace prácticamente nada yla segunda exige el código del estudiante. /* Archivo: Ejemplo41.cs */ using System; using System.Windows.Forms; public class Estudiante { string código; // Constructor por defecto public Estudiante() { } // Constructor con código del estudiante public Estudiante(string CodigoEstudiante) { código = CodigoEstudiante; MessageBox.Show("Código: " + código, "Construyendo..."); } // Propiedad public string Codigo { set { codigo = value; } get { return codigo; } } // Destructor ~Estudiante() { MessageBox.Show("Ejecutando el destructor...", "Destruyendo..."); } } pedrov.cs@hotmail.com
  • 4. 88 CAPITULO 4 PROGRAMACION CON C#public class Programa{ static void Main() { Estudiante alumno = new Estudiante(); //Estudiante escolar = new Estudiante("01"); }} Compile el archivo con la instrucción, > csc ejemplo41.cs Al ejecutar el programa se crea un objeto de tipo Estudiante pero, dado que la ejecución llega al final del método Main, inmediatamente se inicia el proceso de destrucción 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 ejecución e iniciar el proceso de destrucción, también 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 línea del programa y active la segunda. Vuelva a compilar el programa y analice la ejecución. Observará que aparece la caja de mensajes del constructor y esta se mantiene hasta hacer clic en el botón aceptar. La caja de mensajes se mantiene por que el programa está en plena ejecución. Pero no ocurre lo mismo cuando pasa al proceso de destrucción. Métodos Hasta este punto ya hemos trabajado con muchos métodos. Sabemos que un método es lo que otros lenguajes de programación, sobre todo estructurados, se denominan procedimientos o funciones. Además, se sabe que el principal método que dirige la ejecución de un programa C# es el método Main, que es el punto por donde se inicia la ejecución y la carga en memoria por parte del sistema operativo. Los métodos 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 método existe en la medida que exista para un programador que hace uso de una determinada clase, es decir un método publico, en la práctica se puede hablar también de métodos privados, queriendo significar que son acciones internas que se realizan con los elementos de un objeto. En general, un método se define con una instrucción que tiene la siguiente sintaxis: public tipo NombreMétodo(Argumentos) { // Implementación del método return valor } Todo método que debe devolver un valor, el cual debe ser del mismo tipo que el método. Los métodos que ejecutan acciones que no requieren la devolución de un valor, se deben definir como void. C# para permitirle al programador compartir métodos genéricos que se ejecutan sin necesidad de hacer referencia a ningún objeto en particular, permite la definición de métodos estáticos. Esto métodos, que se definen con el modificador static, sewww.pedrov.phpnet.us
  • 5. CAPITULO 4 ELEMENTOS DE UN CLASE C# 89comportan como las funciones o procedimientos de acceso publico que se utilizan en laprogramación estructurada, o en C++. Esta es la forma que implementa C# parapermitirle al programador contar con funciones que son accesibles en el mismo niveldonde actúa la clase en la cual se han incorporado.PropiedadesLas propiedades tienen un aspecto similar a un método, pero no admiten argumentos.Se utilizan para establecer o asignar valores a los atributos de un objeto. Aunque,también pueden utilizarse para procesar valores internos del objeto y retornar un valorsin 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 sección get se encarga de retornar un valor, ya sea de un atributo o como resultadode un proceso de datos. La sección set, en cambio, si debe actuar directamente sobreun atributo, ya que de otra forma no tendría sentido, y su objetivo es asignar un valor.En ambas secciones puede incluirse todo un conjunto de instrucciones, antes deretornar o asignar un valor.En el caso que no se requieren procesar parámetros, pueden presentarse por parte delprogramador dudas sobre lo que debe utilizarse: un método o una propiedad. Noexisten reglas definidas sobre cual de los dos utilizar, y bajo que condiciones. Tododepende del diseño o claridad que se desee darle al código de programación. Porejemplo, si se tiene una clase Persona, y se desea procesar a través de ella la edad deuna persona, con base en su fecha de nacimiento, que se encuentra incluida en algúnatributo, bien podría implementarse un método o una propiedad, y todo sería correcto.Propiedades de solo lecturaUna propiedad de solo lectura es aquella que únicamente permite leer un datocontenido en algún atributo de un objeto, o como resultado de un proceso interno. Estetipo de propiedades no debe permitir ingresar un dato al objeto. Es decir no debeincluir la sección set.La implementación de una propiedad de solo lectura se logra incluyendo en el cuerpode la propiedad únicamente una sección get, con lo cual se limita su funcionalidadsolo 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 } } pedrov.cs@hotmail.com
  • 6. 90 CAPITULO 4 PROGRAMACION CON C# 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 implementación solo debe incluirse la sección 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 clasificación de las propiedades es lo que permite establecer un buen nivel de encapsulamiento en las clases que se diseñen. Sobrecarga de métodos Sobrecargar un método significa implementar varios métodos de una clase con el mismo nombre pero con diferentes parámetros, ya sea en cantidad o en tipo. C#, también acepta diferencias en los valores devueltos por un método sobrecargado, pero siempre y cuando esta no sea la única diferencia. Un método podría tener una versión que devuelva un entero (int) y contar con tres parámetros, todos de tipo cadena (string). Si otra versión del método devuelve un double, y tiene tres parámetros, 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 métodos, 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 compilación, dado que C# tratará de identificar cada método a través de la cantidad de argumentos y sus tipos. Como no encuentra diferencias que permitan una clara identificación, no se puede terminar el proceso de compilación.www.pedrov.phpnet.us
  • 7. CAPITULO 4 ELEMENTOS DE UN CLASE C# 91 Ejemplo 4.2 Autómata que procesa direcciones webEn este ejemplo vamos a construir una clase con dos métodos para identificar si unacadena de texto corresponde a una dirección web bien escrita, a un correo electrónico ono corresponde a ninguno de los dos formatos. Los programas que se encargan derealizar este tipo de análisis suelen denominarse autómatas.Una de las tareas más complejas a las que se puede enfrentar un programador es la deprocesar cadenas de texto para determinar si están escritas de acuerdo a un formato osintaxis preestablecidos. En la mayoría de los casos esta tarea requiere la revisión, enforma repetitiva, carácter por carácter, en busca de los patrones que han sidoestudiados y establecidos con anticipación. Una solución, dada por las Ciencias de laComputación, para facilitar el estudio y análisis de las condiciones que se debenestablecer en el procesamiento de textos, es la teoría de los autómatas y los lenguajesregulares, y más específicamente las llamadas expresiones regulares, un campo depermanente investigación que ha permitido alcanzar los logros que hoy en día tienen ala Computación y a la Informática en el sitial en que se encuentran.Las expresiones regulares, como tal, son un lenguaje que permite simbolizar conjuntosde cadenas de texto formadas por concatenación de otras cadenas. La definición exactade expresión regular se ha establecido a través de un intrincado conjunto de axiomas detipo matemático, que por ahora no entraremos a detallar. Tan solo vamos a dar unanoción breve del concepto de expresión regular en la misma forma como lo hace laayuda incluida en el kit de desarrollo de .NET, donde se la asemeja con los patronesque solemos utilizar para realizar búsquedas de archivos en el disco duro de nuestrocomputador. Por ejemplo, si queremos que el sistema nos muestre todos los archivosfuentes de C# podemos hacerlo a través del patrón “*.cs”. Esta es una forma de decirleal sistema operativo que muestre solo los archivos cuyo nombre termine en loscaracteres “.cs”. Podríamos decir que para el sistema operativo la cadena *.cs es unaexpresión regular.A partir de aquí, y en lo que resta de esta práctica, para aquellos lectores que no estánfamiliarizados con las expresiones regulares, se les sugiere olvidar todo lo que sabensobre 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 expresionesregulares básicas que se manejan en la teoría general de la computación. Supongamosque tenemos un carácter a, entonces se representa: Expresión regular Conjunto representado ε Representa a la cadena vacía a Representa a la cadena formada por a Todas las cadenas formadas por la concatenación de a, tales como a, aa, a+ aaa, aaa, … etc. Todas las cadenas formadas por la concatenación de a, incluyendo a la a* cadena vacía. Es decir, a* = (a+) ∪ {ε }Entonces, la expresión regular 01*, representa a todas las cadenas que empiezan por elcarácter 0 (cero) seguido de ninguno o cualquier cantidad de unos. Aquí, estánrepresentadas cadenas como 0, 01, 011, 0111, etc. La expresión regular (ab)+c,representa todas las cadenas que repiten la cadena ab, una o más veces, y terminan en pedrov.cs@hotmail.com
  • 8. 92 CAPITULO 4 PROGRAMACION CON C# el carácter 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 disposición 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 teoría y las clases que .NET pone disposición del programador para implementar un método que permita analizar cadenas de texto y determinar si una cadena de texto corresponde a una dirección web, a un correo electrónico o a ninguno de ellos. .NET dispone de una mayor cantidad de elementos, o caracteres especiales, que aquellos manejados en la teoría general computación para la construcción de las expresiones regulares. Vamos a describir brevemente algunos de ellos, para utilizarlos en nuestro ejemplo: Caracteres especiales Descripción [ ] Permiten determinar una lista de caracteres, de los cuales se escogerá uno. Por ejemplo, [0123] pone a disposición cualquiera de estos dígitos 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 repetición 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 carácter que sea un carácter alfanumérico o el carácter de subrayado. También 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 expresión 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 dirección web debe empezar por la repetición del carácter w, tres veces. Esto podemos expresarlo como, [w]{3} A continuación viene un punto. Este símbolo corresponde a un carácter 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 alfanuméricos, donde no cabe la posibilidad de que sea una cadena vacía. Vamos a suponer que solo se aceptan caracteres en minúsculas, por lo cual su representación puede hacerse como, [a-z0-9]+www.pedrov.phpnet.us
  • 9. CAPITULO 4 ELEMENTOS DE UN CLASE C# 93El tipo de dominio puede corresponder a una de las siguientes posibilidades: com, net,info u org. En este caso existe una disyunción de la cual se debe escoger solo unaopción y se expresa como, (com|net|info|org)Finalmente es necesario que la cadena a analizar coincida exactamente desde su iniciohasta su final, por lo cual es necesario incluir los limites A y Z al principio y al finalde la expresión regular, respectivamente.En definitiva la expresión regular que nos permitirá validar una dirección web es lasiguiente: expresion = @"A[w]{3}(.)[a-z0-9]+(.)(com|net|info|org)Z";El símbolo @ al inicio de la asignación le informa al compilador de C# que noidentifique en la cadena de texto las secuencias estándar de escape.De la misma forma podemos crear una expresión regular que identifique a todas lasdirecciones de correo electrónico que cumplan con el formato, nombreusuario@nombredominio.tipodominioLa siguiente es la expresión regular que identifica a este tipo de cadenas: expresion = @"A(w+.?w*@w+.)(com)Z";En esta expresión 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 Regexy se encuentra en el espacio de nombres System.Text.RegularExpressions. Con estaclase se pueden definir objetos que reciben una expresión regular y se encargan deprocesar una cadena de texto para identificar todas las subcadenas que hacen parte deella. La clase Regex implementa el método IsMatch que recibe como argumento lacadena que se va a analizar y retorna un valor booleano, true o false, de acuerdo a si seencontró cadenas que se identifiquen con la expresión regular o no. La siguiente líneadefine un objeto tipo Regex que procesa una expresión regular como las definidas eneste análisis: Regex automata = new Regex(expresion);La clase Regex exige que la expresión regular sea asignada en el momento de lacreación del objeto.Con base en el anterior análisis vamos a programar un ensamblado, tipo libreríadinámica, que se encargará de recibir una cadena de texto y validarla de acuerdo a loantes requerido. Este ensamblado estará constituido básicamente por una clase,llamada AutomataWEB, conformada por dos métodos estáticos, EsWeb y EsCorreo,que servirán para validar las direcciones web y las direcciones de correo electrónico,respectivamente./* Archivo: AutomataWEB.cs */using System;using System.Text.RegularExpressions;public class AutomataWEB{ pedrov.cs@hotmail.com
  • 10. 94 CAPITULO 4 PROGRAMACION CON C# public AutomataWEB() { } // Método 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); } // Método para identificar direcciones de correo electrónico 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 instrucción, > csc /t:library AutomataWEB.cs Los métodos estáticos no pueden hacer parte de ningún 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 dirección 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 verificación con ayuda del autómata 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 minúsculas if (AutomataWEB.EsWeb(cadena)) Console.Write("La cadena es una dirección web."); else if (AutomataWEB.EsCorreo(cadena)) Console.Write("La cadena es una dirección de email."); else Console.Write("La cadena no es una dirección válida."); }}www.pedrov.phpnet.us
  • 11. CAPITULO 4 ELEMENTOS DE UN CLASE C# 95Compile el programa con la instrucción, > csc /r:AutomataWEB.dll ejemplo42.csEjecute el programa resultante y pruebe su funcionamiento ingresando cadenas quecorrespondan a direcciones web, correos electrónicos y otras que no cumplan conninguno de los dos formatos.El estudio de las expresiones regulares requiere un análisis más profundo y detallado,por lo cual este ejemplo se constituye solo en una breve introducción a laspotencialidades que ofrece este tema en las aplicaciones de software modernas. Unadescripción más detallada se realizará en un capítulo posterior dedicadoexclusivamente a este tema. Como ejercicio, el lector debería modificar los métodosprogramados aquí para validar cualquier dirección web, incluyendo todos los tipos dedominio y las abreviaturas de los países a los que pertenecen, y de esta forma irganando terreno en el manejo de las expresiones regulares con .NET. Ejemplo 4.3 Complejos de la forma a + biEn este ejemplo vamos a continuar en la construcción de la clase Complejo que se hatomado como modelo de las descripciones de este capítulo. Sabemos que, todocomplejo tiene la forma a + bi, y como tal tiene su validez dentro de las matemáticas.Siempre que se necesite trabajar con un complejo, las cosas serían más fáciles si tantola asignación como el valor retornado por una variable de este tipo se hacen entérminos de la forma matemática de un número complejo. Sería ideal poder realizaruna asignación 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 valornumérico válido, por ahora no es posible en el nivel de programación lograr estapretensión. Sin embargo, si podemos hacer una aproximación de este tipo deasignación 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 delos números complejos sea totalmente transparente.En este punto, nos enfrentamos a una situación bastante particular. Debemosprogramar una validación que se encargue de reconocer un número complejo en unacadena de texto, interpretándola adecuadamente, para determinar las partes real eimaginaria del mismo. Para ello, es necesario, antes que nada, garantizar que la cadenade texto realmente incida con el formato de un número complejo válido.Existen diversas formas para expresar un número complejo. Se sabe que un mismonúmero complejo se puede escribir en formas equivalentes, tales como 2 + 3i, 2 + i3, 3i+ 2, i3 + 2, y cualquiera de ellas es válida. Además existen complejos cuya forma,debido a las propiedades matemáticas, 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 también que0 + 4i es igual a 4i. Incluso, cualquier número real puede considerarse como uncomplejo de parte imaginaria igual a cero.La clase Complejo debe poseer la capacidad suficiente de recibir un número complejoen cualquiera de sus formatos válidos y procesarlo adecuadamente. En general, y parafacilitar la comprensión de las descripciones algoritmicas vamos a dividir en cuatrogrupos los formatos en que puede estar escrito cualquier número complejo: pedrov.cs@hotmail.com
  • 12. 96 CAPITULO 4 PROGRAMACION CON C# 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 sintácticas no se han incluido los signos, positivo o negativo, que pueden adoptar la parte real y la parte imaginaria de un complejo. Más adelante, en cada caso particular entraremos a considerar este aspecto. Tampoco, el agrupamiento realizado obedece a ninguna regla específica relacionada con estos números, sino únicamente se busca agruparlos de acuerdo a algunas características de forma que pueden convenientemente facilitar la generalización de cada uno de ellos. El primer grupo es el formato más usual entre los matemáticos, la parte real va en primera instancia y la parte imaginara después. El tercer formato suele encontrárselo con mucha frecuencia en libros de ingeniería. 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 relación 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 través de expresiones regulares que representen a los grupos de la tabla anterior, para identificar si corresponden o no a números 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 versión antigua del componente. Lo primero que se debe hacer es incluir dos nuevas líneas de código 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 numéricos 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 número representado, al convertirla a tipo double se puede presentar una inconsistencia de interpretación si en la configuración 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 máquina actual para mostrar números con parte entera y decimal. Para evitar inconsistencias se adecuaran los números al formato que maneje la máquina donde se esté trabajando. En la siguiente línea de código, la propiedad CurrencyDecimalSeparator devuelve una cadena tipo string con el separador decimal que utiliza el sistema operativo en la máquina actual: sd = NumberFormatInfo.CurrentInfo.CurrencyDecimalSeparator; El punto central de este ejemplo, es construir un método que le permita a las variables instanciadas de la clase Complejo recibir una cadena de texto y validarla para determinar si corresponde a un número complejo. Para ello vamos a determinar los patrones que pueden determinarlos.www.pedrov.phpnet.us
  • 13. CAPITULO 4 ELEMENTOS DE UN CLASE C# 97Sabemos que, un número es una cadena de dígitos decimales en la cual puede o noaparecer un separador decimal. Si el separador decimal fuera un punto, que en ellenguaje de expresiones regulares de .NET se representa como (.), un número tendríael formato, numero = @"(d+(.)?d*)";El cuantificador ? especifica que el elemento que le antecede puede aparecer una, oninguna vez, en la cadena analizada. Como en este caso vamos a utilizar como válidoel separador decimal definido por el sistema, sd, entonces introducimos unaconcatenación con este, así: numero = @"(d+(" + sd + @")?d*)";Todo número complejo, exceptuando aquellos que no posean parte imaginaria nula,incluyen un literal que representa a la raíz cuadrada de -1. Este generalmente sesimboliza con la letra minúscula i. Definimos este símbolo en la siguiente forma: i = @"(i)";El signo que puede anteceder a un número puede ser positivo, (+), o negativo, (-).Sabemos que para expresar opción en la escogencia de uno u otro símbolo se utilizanlos corchetes. Por lo tanto el signo de un número, en términos de expresión regular de.NET, quedaría expresado por, signo = @"([+-])";Con los anteriores elementos podemos expresar la parte real e imaginaria de uncomplejo 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 + biEn el caso más general, un complejo puede estar formado por una parte real y una parteimaginaria, a + bi, que queda incluida en una expresión regular como la siguiente: expresion1 = real + imaginario;Pero, para incluir en la expresión regular los otros dos casos (complejos con parteimaginaria 0 o 1) es necesario tratar en forma independiente esta parte. En el primercaso, 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, sepuede obtener dejando como opcional el número que la acompaña. imaginario = "(" + signo + "(" + numero + ")?" + i + ")?"; pedrov.cs@hotmail.com
  • 14. 98 CAPITULO 4 PROGRAMACION CON C# Con estas modificaciones, e indicándole 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 expresión 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 número complejo. La siguiente línea define el objeto complejo1 con la expresión regular antes analizada: Regex complejo1 = new Regex(expresion1); La comprobación de la cadena entrante, puede realizarse mediante el método booleano IsMatch del objeto complejo1. Así, if (complejo1.IsMatch(cadena)) return true; De la misma forma como se realizó la expresión regular para el primer grupo de complejos, puede definirse las expresiones regulares para los otros casos de posibles formatos de números complejos. En definitiva, con estos elementos construimos el método booleano EsComplejo, en la siguiente forma: // Método para válidar un número 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 básicos de un complejo string numero = @"(d+(" + sd + @")?d*)"; string i = @"(i)"; string signo = @"([+-])"; // Validación 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; // Validación 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; // Validación 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; // Validación para a + ib real = signo + "?" + numero; imaginario = signo + i + numero; string expresion4 = @"A" + real + imaginario + @"Z";www.pedrov.phpnet.us
  • 15. CAPITULO 4 ELEMENTOS DE UN CLASE C# 99 Regex complejo4 = new Regex(expresion4); return complejo4.IsMatch(cadena); }Se ha incluido una llamada a un método denominado QuitarEspacios, que se encargade eliminar todos los espacios en blanco que puedan existir en la cadena de texto.Aunque la inclusión de los espacios pudo haberse considerado en las expresionesregulares de cada uno de los casos, esto haría un tanto más compleja su estructuración,por lo que se ha optado por el camino más fácil, ¡quitarlos! Este método recibe unacadena de texto, y a través de una expresión regular apropiada, busca uno o másespacios y los reemplaza con una cadena vacía. 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 númerocomplejo, es necesario separar sus partes real e imaginaria para asignarlas a susrespectivos atributos. El método PartesComplejo se basa de un razonamiento muysimple: se busca la parte imaginaria del complejo, se lee su valor y luego se elimina,dejando de esta forma únicamente la parte real. // Método 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; pedrov.cs@hotmail.com
  • 16. 100 CAPITULO 4 PROGRAMACION CON C# 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 expresión regular que se utiliza en este método existen dos particularidades. La primera es que no se busca un sola coincidencia en toda la cadena (aunque, bien podría haberse hecho), por que se supone que la cadena analizada ya está comprobado que corresponde a un número 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 expresión regular. La razón, en este punto ya se sabe que la parte imaginaria está bien escrita y por lo tanto todo lo que se encuentre será válido. imaginaria = signo + "?" + numero + "?" + i + numero + "?"; Cuando se analiza una cadena a través de una expresión regular, el motor de análisis busca todas las subcadenas que hagan parte de esa familia y las va guardando en un objeto de tipo MatchCollection. Para recuperar la colección de cadenas objetivo encontradas existe el método Matches que hace parte de los objetos de tipo Regex. En la siguiente línea 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 método, y los anteriores, estamos listos para ampliar y mejorar las capacidades de nuestra clase Complejo. Se pondrá a disposición del usuario de la clase, tres constructores sobrecargados. El primero no pide ningún dato de entrada. El segundo método da la posibilidad de ingresar los valores real e imaginario del complejo y el tercer método 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 números. El siguiente método privado se encarga de preparar la salida de un complejo en forma de cadena de texto, con el formato a + bi.www.pedrov.phpnet.us
  • 17. CAPITULO 4 ELEMENTOS DE UN CLASE C# 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 elvalor de un complejo o recibir su valor desde el exterior, validando su correctaescritura. Esta es su implementación: 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 sinnecesidad de recurrir a ninguna propiedad en especial, en la misma forma como loshacen los valores numéricos 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 delmétodo WriteLine, en el formato adecuado. Esto mejora el nivel de abstracción de laclase Complejo y le asegura a sus objetos un comportamiento más cercano a losvalores numéricos, facilitando su manejo por parte de cualquier programador. Paralograr esto es necesario sobrescribir el método ToString que hace parte de toda clasedefinida en .NET.La clase Complejo al igual que todas las clases de .NET, en realidad son heredadas deuna clase genérica que forma parte de la raíz del framework, llamada Object. Aunqueesta herencia no se necesita determinar en forma explicita, el compilador de C# lointerpreta así con todas las clases definidas como superclases. Un método que sehereda de Object para todas las clases es ToString el que se ejecuta por defectocuando se intenta imprimir un objeto cualquiera. En la mayoría de los casos cuando seimprime un objeto, sin especificar ninguna propiedad, este método devuelve el nombrecompleto del objeto. En nuestro caso vamos a sobrescribir el método para obligarloescribir el valor del número complejo. Así: // Sobrecarga del método ToString pedrov.cs@hotmail.com
  • 18. 102 CAPITULO 4 PROGRAMACION CON C# 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(); } }www.pedrov.phpnet.us
  • 19. CAPITULO 4 ELEMENTOS DE UN CLASE C# 103public string Valor{ get { return FormatoSalida(); } set { if (EsComplejo(value)) PartesComplejo(value); else { real = 0; imaginario = 0; } }}// Sobrecarga del método ToStringpublic override string ToString(){ return FormatoSalida();}// Métodos privadosprivate 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;}// Método para válidar un número complejoprivate 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 básicos de un complejo string numero = @"(d+(" + sd + @")?d*)"; string i = @"(i)"; pedrov.cs@hotmail.com
  • 20. 104 CAPITULO 4 PROGRAMACION CON C# string signo = @"([+-])"; // Validación 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; // Validación 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; // Validación 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; // Validación 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); } // Método 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"; elsewww.pedrov.phpnet.us
  • 21. CAPITULO 4 ELEMENTOS DE UN CLASE C# 105 parteImag = parteImag.Replace("i", ""); // Eliminar la parte imaginaria parteReal = imaginario1.Replace(cadena, ""); } else { parteReal = cadena; parteImag = "0"; } // Verificar la cadenas de texto vacías 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 librería dinámica, con la instrucción, > csc /t:library Complejo.csEl siguiente programa hace uso de la clase Complejo y muestra el funcionamiento delos cambios realizados:/* Archivo: Ejemplo43.cs */using System;public class NumerosComplejos{ static void Main() { Complejo z = new Complejo(); Console.Write("Ingrese un número complejo: "); z.Valor = Console.ReadLine(); Console.Write("z = {0}n", z); Console.Write("a = {0}; b = {1}n", z.Real, z.Imaginario); Console.Write("Módulo: {0}n", z.Modulo); pedrov.cs@hotmail.com
  • 22. 106 CAPITULO 4 PROGRAMACION CON C# 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("Módulo: {0}n", w.Modulo); Console.Write("Argumento: {0}", w.Argumento); }} Compile este archivo con la instrucción de línea de comandos, > csc /r:Complejo.dll ejemplo43.cs Ejecute el programa resultante y analice el comportamiento de cada línea 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 versión desarrollada en los anteriores ejemplos. Esta es una característica 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 editándose y aumentando sus elementos y mantener la compatibilidad hacia versiones anteriores. Sobrecarga de operadores El concepto de sobrecarga también 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 más conocido es el operador sobrecargado es +, quién tiene una versión para valores numéricos y otra para valores tipo cadena de texto. Cuando el operador se aplica a dos valores que representan cantidades numéricas, realiza una suma matemática, pero cuando se aplica a dos cadenas de texto, produce como resultado una cadena que es la concatenación de las dos primeras. Las siguientes líneas de código muestran un ejemplo típico: int a = 5 + 7; string c = "Hola" + "Mundo"; En la variable entera a se almacena el valor numérico 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 programación la claridad y naturalidad suficientes para hacer de las operaciones con objetos un trabajo fácil 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 podría hacer una sobrecarga para el operador +, de tal manera que al aplicarse a un cierto tipo de datos numéricos produjera una multiplicación, algo que haría perder innecesariamente la lógica del lenguaje de programación. Todo operador, al momento de sobrecargarse, debe ejecutarse acorde a la función 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 programación es actuar como intermediario en el proceso de comunicación entre la máquina y el ser humano, y por lo tanto debe buscar ser lo más claro posible.www.pedrov.phpnet.us
  • 23. CAPITULO 4 ELEMENTOS DE UN CLASE C# 107Para sobrecargar un operador se utiliza un método estático que debe hacer parte deltipo o clase que lo va a utilizar. La siguiente es la sintaxis general que se utiliza parasobrecargar un operador unario:public static TipoDevuelto operator operador (Tipo operando){ // Implementación}En forma similar se sobrecarga un operador binario:public static TipoDevuelto operator operador (Tipo1 operando1, Tipo2 operando2){ // Implementación}Aunque C# no permite la sobrecarga de todo los operadores que maneja, si lo hacepara la mayoría de operadores relacionados con las matemáticas y los bits. En lasiguiente lista se muestran todos los operadores que admiten sobrecarga: Operadores unarios +, -, !, ~, ++, --, true, false Operadores binarios +, -, *, /, %, &, |, ^, <<, >>, ==, !=, >, <, >=, <= Ejemplo 4.4 Operaciones con complejosEn este ejemplo vamos a sobrecargar los operadores matemáticos para la claseComplejo. Hasta ahora no hemos definido la forma de realizar operaciones concomplejos. Si un programador deseara obtener una suma de complejos debería recurrira la definición matemática y aplicar el proceso con las partes de los complejos que sevayan a operar.Comencemos con la suma de números complejos. Las matemáticas la definen de lasiguiente forma: si se tienen dos complejos z1 y z2 , la suma de ellos es un númerocomplejo cuya parte real es la suma de las partes reales de los dos complejos, y deigual forma la parte imaginaria es igual a la suma de las partes imaginarias. Ennotación matemática: Si z1 = a1 + b1i y z2 = a2 + b2 i , entonces z1 + z2 = (a1 + a2 ) + (b1 + b2 )iUna posible solución al problema de programar la suma de complejos, podría ser ladefinición de un método estático que se encargue de recibir como parámetros doscomplejos, realizar la suma utilizando la definición matemática y devolver el resultadoen términos de una variable compleja. Este método, implementado por la claseComplejo, bien podría 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; } pedrov.cs@hotmail.com
  • 24. 108 CAPITULO 4 PROGRAMACION CON C# La solución es buena, y de hecho funciona muy bien. En las siguientes líneas de código se muestra como debería utilizarse el método Suma: Complejo z1 = new Complejo("5 + 3i"); Complejo z2 = new Complejo("8 - 2i"); Complejo z = new Complejo(); z = Complejo.Suma(z1, z2); Esta codificación de operaciones matemáticas, basada en llamadas a métodos, puede resultar molesta en situaciones donde se van a realizar operaciones de uso muy común. Un programador que haga uso de los complejos, talvez preferiría codificar una suma en forma más natural, o por lo menos como está acostumbrado a hacerlo con los demás números que maneja el lenguaje de programación, así suma = z1 + z2; Para lograr esto es necesario sobrecargar el operador +, indicándole cual debe ser el proceso a seguir cuando se aplique a números complejos. El siguiente método 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 método de sobrecarga hace exactamente lo que debería hacer el programador usuario de la clase. La ventaja es que solo se programa aquí, y en adelante bastará con aplicar una operación de suma común y corriente, como si de otro número cualquiera se tratará. Antes de sobrecargar las demás operaciones vamos sobrecargar el operador inverso aditivo, o signo negativo, (-), el cual invierte el signo de las partes que conforman un complejo. Matemáticamente 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 través de la suma, así: z1 − z2 = z1 + ( − z2 ) Con lo que la sobrecarga del operador resta sigue la misma noción. public static Complejo operator -(Complejo z1,Complejo z2) {www.pedrov.phpnet.us
  • 25. CAPITULO 4 ELEMENTOS DE UN CLASE C# 109 Complejo resta = z1 + (- z2); return resta; }En las dos últimas sobrecargas aparentemente se ha modificado el mismo operador. Enrealidad no es así. El compilador de C# distingue claramente a cada operador por elnúmero de operandos sobre los cuales actúa. En el primer caso, al existir un solooperando entiende que se trata del operador inverso aditivo, mientras que el segundocaso queda claro que se trata del operador resta.La multiplicación de complejos se obtiene realizando una multiplicación polinomial delos dos operandos. En general esta operación se simplifica en el siguiente resultado: Si z1 = a1 + b1i y z2 = a2 + b2 i , entonces z1 ⋅ z2 = (a1a2 − b1b2 ) + (a1b2 + a2 b1 )iCon base en esta definición, la sobrecarga del operador multiplicación, *, 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 multiplicación no solo puede darse entre números complejos, también puedemultiplicarse un numero real por un complejo. Para lograr esto es necesario aplicar dossobrecargas más al operador *. Puede multiplicarse un complejo por la izquierda o porla derecha. Ambas situaciones deben quedar bien claras para el compilador de C#. Ladefinición matemática de esta multiplicación establece:Si c ∈ ℝ y z = a + bi , entonces c ⋅ z = ca + cbiEn 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 multiplicación por la derecha se puede implementar conbase en la anterior, así: public static Complejo operator *(Complejo z, double c) { return c * z; }Existe una operación propia de los complejos, que no esta definida para ningún otrotipo numérico. Es el conjugado de un complejo. Esta operación, lo único que hace esinvertir la parte imaginaria del número complejo al cual se aplica. Si z = a + bi , el conjugado de z se define como z = a − bi pedrov.cs@hotmail.com
  • 26. 110 CAPITULO 4 PROGRAMACION CON C# En C# no existe un operador cuya funcionalidad tenga alguna relación con el conjugado de un complejo. En vista de esto vamos a sobrecargar el operador ! (negación lógica). 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 división de números complejos se puede definir, con base en el conjugado y el módulo del divisor, en la siguiente forma: z1 1 = z1 ⋅ z2 z2 z2 2 Por lo tanto, el método que sobrecarga el operador división, /, 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;www.pedrov.phpnet.us
  • 27. CAPITULO 4 ELEMENTOS DE UN CLASE C# 111 }}// Propiedadespublic 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 método ToStringpublic 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(); pedrov.cs@hotmail.com
  • 28. 112 CAPITULO 4 PROGRAMACION CON C# 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; } // Métodos privados private double Tamano() { double c; c = Math.Sqrt(real * real + imaginario * imaginario); return c; }www.pedrov.phpnet.us
  • 29. CAPITULO 4 ELEMENTOS DE UN CLASE C# 113// Calcular el ángulo del complejoprivate 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;}// Método para válidar un número complejoprivate 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 básicos de un complejo string numero = @"(d+(" + sd + @")?d*)"; string i = @"(i)"; string signo = @"([+-])"; // Validación 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; // Validación 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; // Validación 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; // Validación para a + ib real = signo + "?" + numero; imaginario = signo + i + numero; string expresion4 = @"A" + real + imaginario + @"Z"; Regex complejo4 = new Regex(expresion4); pedrov.cs@hotmail.com
  • 30. 114 CAPITULO 4 PROGRAMACION CON C# return complejo4.IsMatch(cadena); } // Método 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 vacías 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; }www.pedrov.phpnet.us
  • 31. CAPITULO 4 ELEMENTOS DE UN CLASE C# 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 instrucción, > csc /t:library Complejo.csCon los cambios realizados ya contamos con una clase Complejo capaz de definirobjetos cuyo comportamiento se asemeja bastante a los números que maneja C#. Lasobrecarga de los operadores aritméticos nos permitirá codificar las operaciones de estetipo en la misma forma como se hace con cualquier otro tipo numérico. Aunque talvezno es el mejor, esta clase es un buen ejemplo de abstracción y encapsulamiento, lo cualpermite contar con tipos complejos con un buen nivel de autonomía para resolver lamayoría de problemas propios de su naturaleza.El siguiente programa hace uso de la clase Complejo y realiza algunas operacionescon números 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 compílelo con la instrucción, > csc /r:Complejo.dll ejemplo44.cs pedrov.cs@hotmail.com
  • 32. 116 CAPITULO 4 PROGRAMACION CON C# El lector podrá comprobar que la clase Complejo define objetos que en forma autónoma se encargan de realizar la mayoría de térreas 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 abstracción 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 número complejo, la clase no posee un mecanismo para informar directamente sobre esa situación anómala y en vez de eso asume un valor nulo sin que el usuario se entere de tal situación. Se podría implementar un mecanismo de mensajes para informar al usuario que existe un error en la asignación de un valor, pero esto podría afectar la generalidad del componente y limitarlo a un único entorno de ejecución. El objetivo es crear un componente de software útil en cualquier entorno, consola, sistema gráfico de Windows o web. En las siguientes secciones se describirán elementos de la programación con C# que permiten dar mayor robustez a los componentes de software, y con ellos podremos resolver en forma técnica las deficiencias de nuestra clase Complejo. Eventos Un evento es una acción que produce un componente y a la que otro componente puede responder o puede controlar mediante código. Los eventos más conocidos son aquellos que se producen por acción del usuario, por ejemplo, al hacer clic con el botón principal del ratón sobre un botón de una ventana se produce un evento que a su vez ejecuta un código de programación. Sin embargo, esta última asociación didáctica para intentar explicar el concepto de evento, más que ayudar, puede distorsionar la noción que sobre el mismo impone la programación orientada a objetos. En la práctica 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 método) 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 interés. Una noción muy general de cómo 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 implementación 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 métodos que se ejecuten cuando cada uno de estos objetos, internamente, hace el llamado al procedimiento especial que hemos denominado MiEvento. class ClaseB {www.pedrov.phpnet.us
  • 33. CAPITULO 4 ELEMENTOS DE UN CLASE C# 117 ClaseA a1 = new ClaseA(); ClaseA a2 = new ClaseB(); A MiEvento de a1 asociar MetodoA1; A MiEvento de a2 asociar MetodoA2; MetodoA1() { Implementación de MiEvento para a1; } MetodoA2() { Implementación de MiEvento para a2; } }La ventaja que tiene el manejo de eventos es que el programador puede implementarrespuestas diferentes para el evento de cada objeto, adecuándolas a sus intereses y a laforma como desee personalizar el manejo de los componentes en cada caso específico.Incluso puede no hacer ninguna implementación.Vistas las cosas como nos las muestra este esquema, podemos decir que un evento esuna señal que envían los objetos a1 y a2, descendientes de la clase ClaseA, hacia laclase ClaseB y que en este son respondidas mediante los métodos MetodoA1 yMetodoA2, respectivamente.En el manejo de un evento es importante tener en cuenta tres elementos básicos queintervienen: el componente que genera el evento, un manejador de eventos y unmétodo que responde a la señal. Manejador de Componente Método eventos Figura 3.14: Un componente genera un evento que es controlado por un manejador de eventos, quien decide cual es el método que se debe ejecutar.La mayoría de clases de .NET establecen uno o varios eventos en los objetos quedefinen, lo cual le permite al programador personalizar su comportamiento de acuerdoa la aplicación donde se vayan a utilizar, y de esta manera imprimir mayor versatilidada la reutilización de componentes. La programación de eventos facilita enormemente laadecuación y control de los componentes de software y al mismo tiempo acorta lostiempos de desarrollo utilizados por los programadores.Aunque el uso de eventos no es un tema nuevo en la programación, la forma deimplementarlos si ha estado un tanto escondida para los programadores. Un buenejemplo de ello son los entornos de desarrollo integrado, herramientas estas que en lamayoría de los casos automatizan el proceso de creación de los eventos y ponen adisposición del programador el espacio definitivo donde se requiere su intervención.Sin embargo, conocer como se implementa un evento puede ayudarle a sacar mayorprovecho de estos y la programación de sus propios objetos con eventos adecuados.Además, en muchos casos los programadores tenemos la tendencia a relacionar loseventos únicamente con objetos gráficos, lo cual dificulta su concepción y utilizaciónen componentes que no pertenezcan a este campo. Lo importante aquí, es dejar claroque con C# a todo componente de software que desarrollemos le podemos asignareventos. pedrov.cs@hotmail.com
  • 34. 118 CAPITULO 4 PROGRAMACION CON C# El proceso de creación y programación de un evento requiere la realización de una serie de pasos que pueden hacer de este una tarea confusa. Para la descripción lo hemos dividido en dos etapas: implementación y control (o respuesta). Implementación de un evento Teniendo en cuenta el esquema utilizado en los párrafos anteriores, la implementación es el trabajo que se debe hacer por fuera de la clase ClaseB. En la implementación 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 método. 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 explicación, los anteriores elementos se definen por fuera de la clase ClaseA y, obviamente, también de la clase ClaseB. Lo que viene en seguida es lo que se debería 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 declaración 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 provocaría un error en tiempo de ejecución. 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 quién 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 enésima suma de un número. Un objeto definido a partir de la clase, recibirá un número entero positivo y realizará una suma secuencial desde 1 hasta dicho valor.www.pedrov.phpnet.us
  • 35. CAPITULO 4 ELEMENTOS DE UN CLASE C# 119Además, cada vez que se realice una suma parcial, el objeto emitirá un mensaje deaviso a la clase que lo contiene.Para hacer más fácil la descripción no utilizaremos un evento con parámetros, lo cualnos evita tener que definir una clase de argumentos para el mismo. Vamos al segundopaso del proceso. Se define el manejador de eventos, al cual llamaremosEventoSumador 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 claseque 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 deltipo EventoSumador.Nos detenemos en este punto y pasamos a implementar la funcionalidad de la claseSumador. Esta clase se encargará de realizar la suma de los números mediante un cicloque irá realizando la suma 1 + 2 + 3 + …, hasta el valor del número pasado acualquiera de sus objetos. public class Sumador { // Declaración del evento public event EventoSumador SumaParcial; // Atributos int numero; // Constructor public Sumador(int valorNumero) { numero = valorNumero; } // Método 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() { pedrov.cs@hotmail.com
  • 36. 120 CAPITULO 4 PROGRAMACION CON C# 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 análisis, nuestro archivo fuente, al que llamaremos Sumador.cs, queda así:/* Archivo: Sumador.cs */using System;public delegate void EventoSumador();public class Sumador{ // Declaración del evento public event EventoSumador SumaParcial; // Atributos int numero; // Constructor public Sumador(int valorNumero) { numero = valorNumero; } // Método 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 librería dinámica, con la instrucción, > csc /t:library Sumador.cs Con esto tenemos un componente que define objetos que son capaces de provocar un evento. La siguiente parte consistiría 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 hacerwww.pedrov.phpnet.us
  • 37. CAPITULO 4 ELEMENTOS DE UN CLASE C# 121uso de un objeto y sus eventos y es igual para objetos creados por el programador opara 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 método que debe tener la misma firma de método (el mismo tipo e iguales parámetros) que el delegado declarado para el evento. tipo MetodoEvento(object ObjetoEmisor, ClaseArgumentosEvento e) { // Implementación 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 método, este es llamado cada vez que la clase provoca el evento.Ejemplo 4.6 Un evento controladoEn seguida vamos utilizar el componente que desarrollamos en el ejemplo anterior.Con este componente vamos definir un objeto e implementar el método para su eventoSumaParcial.El método que controlará el evento lo definiremos así: static void RSumaParcial() { Console.WriteLine("Evento de r..."); }Este método, ú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 elmétodo asignándolo al evento SumaParcial de r. r.SumaParcial += new EventoSumador(RSumaParcial);Esta asignación debe hacerse dentro de un método de la clase, después de haber creadoel objeto que provoca el evento. La mayoría de aplicaciones desarrolladas con ayudade un entorno integrado de desarrollo realizan esta asignación en un método de cargainicial que es llamado directamente por el método Main, o en su defecto lo incluyen enel cuerpo de este método. Sin embargo, esto no significa que obligatoriamente debarealizarse de esa manera, ya que el programador puede realizarlo en cualquier otrométodo aunque sus efectos pueden tener algunas variaciones. Es importante tener encuenta que el evento no llama a su método controlador hasta tanto no se haya ejecutadoel método 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() pedrov.cs@hotmail.com
  • 38. 122 CAPITULO 4 PROGRAMACION CON C# { 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 instrucción, > csc /r:Sumador.dll ejemplo46.cs Al ejecutar el programa, se carga en el objeto el valor 10, lo cual implica que se deberán realizar diez ciclos y de hecho se llama igual número 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 método controlador del evento se ha definido privado y estático. Ninguna de las dos cosas es un requisito impuesto por las reglas de control de eventos. El método definido es uno más de los muchos que el programador puede implementar, y como tal puede tener cualquier nivel de accesibilidad. La opción de estático es un requisito impuesto por el método Main. Cuando se hace el registro del método controlador, r.SumaParcial += new EventoSumador(RSumaParcial); al incluir unicamente el nombre del método, implícitamente se produce una referencia al objeto que contiene a dicho método. Como esta línea se encuentra en el cuerpo de un método estático, no es posible hacer referencia a un objeto de la misma clase y en consecuencia la única solución es definir un método estático, 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 comprensión de su lógica de implementación y control por parte del lector poco experimentado en el tema.Ejemplo 4.7 Un evento con argumentos Basándonos 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 dewww.pedrov.phpnet.us
  • 39. CAPITULO 4 ELEMENTOS DE UN CLASE C# 123entrada y el otro corresponderá a una nueva versión del evento SumaParcial, el cualpermitirá 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 unaclase, y también la forma de controlar ese evento en un objeto que lo genera. Pero elevento que se ha programado solo se limita a enviar una señal al cliente y no le retornaningún parámetro. El objetivo era mostrar al lector la forma de generar sus propioseventos en los componentes que cree. Ahora vamos a incluir dos eventos, para nuestrosumador, que cumplan las especificaciones de la mayoría de eventos de .NET.Los eventos generados por los objetos de .NET mantienen un esquema depresentación. Todo evento devuelve dos parámetros: el primero es la identidad delobjeto que lo generó, y el segundo corresponde a una variable que lleva los datosreferentes a los argumentos del evento.Lo primero que vamos a hacer es programar una clase que nos permita fijar los datosde los eventos. La clase se llamará SigmaArgumentosEvento y contendrá datos sobreerrores y valores numéricos relacionados con la sumatoria. La devolución de un errorse realizará a través de los atributos Error y MensajeError. Un tercer dato numérico,Valor, servirá para retornar cualquier valor numérico 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 claseEventArgs. Aunque esta clase no especifica datos, si es importante derivar a partir deella, para mantener una base común con todos los eventos generados en .NET. Se debetener en cuenta que muchas clases del Framework no heredan directamente deEventArgs, sino de otras clases que a su vez heredaron de esta. Igual, siempre existiráuna línea de jerarquía en la cual EventArgs es la base.Utilizando la clase anterior, ahora ya es posible definir un manejador de eventos quepermita controlar eventos con datos. La definición de dicho manejador queda comosigue: public delegate void EventoSumador(Object emisor, SigmaArgumentosEvento e); pedrov.cs@hotmail.com
  • 40. 124 CAPITULO 4 PROGRAMACION CON C# El primer parámetro del manejador de eventos nos permitirá enviar una referencia al objeto que generó el evento. Este parámetro puede ser importante en un momento dado para determinar quién generó el evento, sobretodo por que, en la práctica, puede ser necesario hacer que un mismo método controle a varios eventos. A continuación 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 versión 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 definición 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 través de la propiedad Valor, de dicho evento. Dado que la generación de un mismo evento puede necesitarse hacer desde diferentes miembros de la clase es recomendable incluirla en un método 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 método para generar un determinado evento. Este no es un requisito de programación, pero si ayuda a hacer más claro el código y su mantenimiento. Observe que el evento SumaParcial, a través 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 diseñado 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 número asignado para la sumatoria sea menor que 1. El método generador del evento es el siguiente: private void LlamarEntradaNumero() { SigmaArgumentosEvento e=new SigmaArgumentosEvento(); if (numero < 1) { e.Error = true; e.MensajeError="El número ingresado es incorrecto."; e.Valor = numero; } if (EntradaNumero != null) { EntradaNumero(this, e); } }www.pedrov.phpnet.us
  • 41. CAPITULO 4 ELEMENTOS DE UN CLASE C# 125En definitiva, y con las explicaciones mostradas en los comentarios de codificación, laclase 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(); } } // Método 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; } // Métodos 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 número ingresado es incorrecto."; pedrov.cs@hotmail.com
  • 42. 126 CAPITULO 4 PROGRAMACION CON C# 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; } setwww.pedrov.phpnet.us
  • 43. CAPITULO 4 ELEMENTOS DE UN CLASE C# 127 { numero = value; LlamarEntradaNumero(); } } // Método 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; } // Métodos 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 número ingresado es incorrecto."; e.Valor = numero; } if (EntradaNumero != null) { EntradaNumero(this, e); } }}Compile el archivo en un ensamblado dll con la instrucción, > csc /t:library Sigma.csEl siguiente programa utiliza dos objetos derivados de la clase Sigma, e implementa losmétodos que controlan sus eventos../* Archivo: ejemplo47.cs */using System;using System.Windows.Forms;public class Programa pedrov.cs@hotmail.com
  • 44. 128 CAPITULO 4 PROGRAMACION CON C#{ 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 instrucción, > 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 método, SumaEntradaNumero. Este método muestra, en una caja de mensajes, información relacionada con el objeto que genera el evento, específicamente el valor numérico que se ha ingresado. Aquí es donde se aprovecha el primer parámetro del evento para conocer cual fue el objeto que lo generó. La forma como se nombran los métodos controladores es muy importante cuando se va a trabajar con muchos objetos, para evitar confusiones y facilitar el mantenimiento del código. Aquí se ha seguido las recomendaciones hechas por la documentación delwww.pedrov.phpnet.us
  • 45. CAPITULO 4 ELEMENTOS DE UN CLASE C# 129Framework de .NET. Un método controlador debe nombrarse iniciando con el nombredel objeto generador, seguido de una cadena equivalente al nombre del evento. Ejemplo 4.8 Un evento de la consolaEl objeto de .NET que más hemos utilizado hasta el momento ha sido el objetoConsole. Este objeto, es uno de los pocos del Framework que tan solo cuenta con unevento (al menos hasta la versión 3.0). Ese evento se llama CancelKeyPress y segenera cada que el usuario de una aplicación cancela, en forma forzada, la finalizaciónde una aplicación de consola presionando las teclas CTRL+C.En este ejemplo vamos a programar un método para controlar el eventoCancelKeyPress y aprovecharlo para mostrar un mensaje de cancelación en una cajade texto. El siguiente es el método que controla este evento: static void ConsoleCancelKeyPress(object emisor, EventArgs e) { MessageBox.Show("Operación cancelada..."); }El evento CancelKeyPress ha sido definido mediante el manejador de eventos delobjeto Console que se identifica con el nombre ConsoleCancelEventHandler. Por lotanto el registro del método controlador de este evento se debe realizar de la siguienteforma: Console.CancelKeyPress += new ConsoleCancelEventHandler(ConsoleCancelKeyPress);El siguiente es el programa que muestra el evento CancelKeyPress en acción:/* 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 aplicación aún no ha terminado . . ."); Console.ReadKey(true); } static void ConsoleCancelKeyPress(object emisor, EventArgs e) { MessageBox.Show("Operación cancelada ...", "Cancelado"); }}Compile el programa con la instrucción, > csc ejemplo.csEl programa se ejecuta hasta la línea Console.ReadKey(true); pedrov.cs@hotmail.com
  • 46. 130 CAPITULO 4 PROGRAMACION CON C# 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 combinación 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 práctica vamos a desarrollar un evento generado por la clase Complejo, después de que se ingresa y verifica un valor complejo en forma de cadena de texto. A través 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 validación 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; // Implementación de Complejo... } A su vez, definimos un método que se encargue de generar el evento. Esto nos permite hacer la llamada desde más 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); } }www.pedrov.phpnet.us
  • 47. CAPITULO 4 ELEMENTOS DE UN CLASE C# 131Como ya se dijo este evento será generado desde los métodos que se encargan de leercadenas de texto y verificar si corresponde a un complejo. Estos son, uno de losconstructores 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, agreguela clase ComplejoArgumentosEvento y realice los cambios que aquí se han descrito. Endefinitiva el archivo queda como sigue:/* Archivo: Complejo.cs */using System;using System.Text.RegularExpressions;using System.Globalization;public class ComplejoArgumentosEvento : EventArgs{ // Campos bool error; pedrov.cs@hotmail.com
  • 48. 132 CAPITULO 4 PROGRAMACION CON C# // 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(); } }www.pedrov.phpnet.us
  • 49. CAPITULO 4 ELEMENTOS DE UN CLASE C# 133public 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 método ToStringpublic 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){ pedrov.cs@hotmail.com
  • 50. 134 CAPITULO 4 PROGRAMACION CON C#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; } // Métodos 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)www.pedrov.phpnet.us
  • 51. CAPITULO 4 ELEMENTOS DE UN CLASE C# 135 alfa = - Math.PI / 2; else alfa = 0; return alfa;}// Método para válidar un número complejoprivate 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 básicos de un complejo string numero = @"(d+(" + sd + @")?d*)"; string i = @"(i)"; string signo = @"([+-])"; // Validación 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; // Validación 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; // Validación 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; // Validación 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);}// Método para separar la parte real y la parte imaginariaprivate 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*)"; pedrov.cs@hotmail.com
  • 52. 136 CAPITULO 4 PROGRAMACION CON C# 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 vacías 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) {www.pedrov.phpnet.us
  • 53. CAPITULO 4 ELEMENTOS DE UN CLASE C# 137 ComplejoArgumentosEvento e = new ComplejoArgumentosEvento(); e.Error = existeError; if (NumeroComprobado != null) { NumeroComprobado(this, e); } }}Vuelva a compilar el archivo con la instrucción, > csc /t:library Complejo.csEl siguiente programa utilice el evento NumeroComprobado para mostrar al usuario unmensaje informándole si el número 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.csEn este caso se utilizó la consola para enviar un mensaje al usuario, pero también pudohaberse programado en una caja de mensajes gráfica. Esto le da más versatilidad a laclase Complejo, ya que el programador puede utilizarla en diferentes contextos yadecuar sus mensajes al entorno de desarrollo donde se aplique. pedrov.cs@hotmail.com