sábado, 19 de octubre de 2013

43 Estructuras dinámicas: Listas tipo Pila - Problema de aplicación


Hasta ahora hemos visto como desarrollar los algoritmos para administrar una lista tipo Pila, hemos visto que hay bastante complejidad en el manejo de punteros pero todo esto acarrea ventajas en la solución de problemas que requieren una estructura de tipo Pila.


Planteo del problema:


Este práctico tiene por objetivo mostrar la importancia de las pilas en las Ciencias de la Computación y más precisamente en la programación de software de bajo nivel.

Todo compilador o intérprete de un lenguaje tiene un módulo dedicado a analizar si una expresión está correctamente codificada, es decir que los paréntesis estén abiertos y cerrados en un orden lógico y bien balanceados.

Se debe desarrollar una clase que tenga las siguientes responsabilidades (clase Formula):


- Ingresar una fórmula que contenga paréntesis, corchetes y llaves.
- Validar que los ( ) [] y {} estén correctamente balanceados. 

Para la solución de este problema la clase formula tendrá un atributo de la clase Pila.

Veamos como nos puede ayudar el empleo de una pila para solucionar este problema.

Primero cargaremos la fórmula en un TextBox.

Ejemplo de fórmula: (2+[3-12]*{8/3})

El algoritmo de validación es el siguiente:

Analizamos caracter a caracter la presencia de los paréntesis, corchetes y llaves.

Si vienen símbolos de apertura los almacenamos en la pila.

Si vienen símbolos de cerrado extraemos de la pila y verificamos si está el mismo símbolo pero de apertura:

en caso negativo podemos inferir que la fórmula no está correctamente balanceada.

Si al finalizar el análisis del último caracter de la fórmula la pila está vacía podemos concluir que está correctamente balanceada.

Ejemplos de fórmulas no balanceadas:

}(2+[3-12]*{8/3})
Incorrecta: llega una } de cerrado y la pila está vacía.
{[2+4}]
Incorrecta: llega una llave } y en el tope de la pila hay un corchete [.
{[2+4]
Incorrecta: al finalizar el análisis del último caracter en la pila queda pendiente una llave {.

Pila


Programa:

archivo: Pila.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Formula
{
    class Pila
    {
        class Nodo
        {
            public char simbolo;
            public Nodo sig;
        }

        private Nodo raiz;

        public Pila()
        {
            raiz = null;
        }

        public void Insertar(char x)
        {
            Nodo nuevo;
            nuevo = new Nodo();
            nuevo.simbolo = x;
            if (raiz == null)
            {
                nuevo.sig = null;
                raiz = nuevo;
            }
            else
            {
                nuevo.sig = raiz;
                raiz = nuevo;
            }
        }

        public char Extraer()
        {
            if (raiz != null)
            {
                char informacion = raiz.simbolo;
                raiz = raiz.sig;
                return informacion;
            }
            else
            {
                return char.MaxValue;
            }
        }

        public bool Vacia()
        {
            if (raiz == null)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    }
}


archivo: Form1.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Formula
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Pila pila1;
            pila1 = new Pila();
            string cadena = textBox1.Text;
            for (int f = 0; f < cadena.Length; f++)
            {
                if (cadena.ElementAt(f) == '(' || cadena.ElementAt(f) == '[' || cadena.ElementAt(f) == '{')
                {
                    pila1.Insertar(cadena.ElementAt(f));
                }
                else
                {
                    if (cadena.ElementAt(f) == ')')
                    {
                        if (pila1.Extraer() != '(')
                        {
                            Text = "Incorrecta";
                            return;
                        }
                    }
                    else
                    {
                        if (cadena.ElementAt(f) == ']')
                        {
                            if (pila1.Extraer() != '[')
                            {
                                Text = "Incorrecta";
                                return;
                            }
                        }
                        else
                        {
                            if (cadena.ElementAt(f) == '}')
                            {
                                if (pila1.Extraer() != '{')
                                {
                                    Text = "Incorrecta";
                                    return;
                                }
                            }
                        }
                    }
                }
            }
            if (pila1.Vacia())
            {
                Text = "Correcta";
            }
            else
            {
                Text = "Incorrecta";
            }
        }
    }
}

Primero declaramos y definimos la clase Pila. Almacenamos en cada nodo un caracter y llamamos al campo de información símbolo.

No es necesario implementar los métodos Imprimir, Cantidad, etc. Porque no se requieren para este problema.

La clase Formula tiene como atributos: un TextBox y un Button

En el método Click verifica si la fórmula están correctos los parentesis, corchetes y llaves.

En este analizamos la fórmula para verificar si está correctamente balanceada.

En este método es donde está gran parte del algoritmo de este problema. Mostramos en el titulo del Form si la formula esta correctamente balanceada.


Definimos una pila y extraemos el contenido del TextBox:

            Pila pila1;
            pila1 = new Pila();
            string cadena = textBox1.Text;

El for se repite tantas veces como caracteres tenga el TextBox.

Se deben procesar sólo los símbolos ( [ { y ) ] }.

Si el símbolo es un ( [ { de apertura procedemos a cargarlo en la pila:

                if (cadena.ElementAt(f) == '(' || cadena.ElementAt(f) == '[' || cadena.ElementAt(f) == '{')
                {
                    pila1.Insertar(cadena.ElementAt(f));
                }

En caso de ser un ) cerrado debemos extraer un carácter de la pila y verificar si no coincide con el paréntesis de apertura ( la fórmula está incorrecta:

                    if (cadena.ElementAt(f) == ')')
                    {
                        if (pila1.Extraer() != '(')
                        {
                            Text = "Incorrecta";
                            return;
                        }
                    }

El mismo proceso es para  los símbolos ]  }.

Al finalizar el análisis de toda la cadena si la pila está vacía podemos afirmar que la fórmula está correctamente balanceada, en caso contrario quiere decir que faltan símbolos de cerrado y es incorrecta:

            if (pila1.Vacia())
            {
                Text = "Correcta";
            }
            else
            {
                Text = "Incorrecta";
            }


Es importante entender que la clase Formula utiliza un objeto de la clase Pila para resolver el algoritmo de verificar el balanceo de la fórmula, pero no accede directamente a los nodos de la lista.


42 Estructuras dinámicas: Listas tipo Pila

Una lista se comporta como una pila si las inserciones y extracciones las hacemos por un mismo lado de la lista. También se las llama listas LIFO (Last In First Out - último en entrar primero en salir)

Importante: Una pila al ser una lista puede almacenar en el campo de información cualquier tipo de valor (int, char, float, vector de caracteres, un objeto, etc)

Para estudiar el mecanismo de utilización de una pila supondremos que en el campo de información almacena un entero (para una fácil interpretación y codificación)

Inicialmente la PILA está vacía y decimos que el puntero raiz apunta a null (Si apunta a null decimos que no tiene una dirección de memoria):

pila vacía


Insertamos un valor entero en la pila: insertar(10)

pila


Luego de realizar la inserción la lista tipo pila queda de esta manera: un nodo con el valor 10 y raiz apunta a dicho nodo. El puntero del nodo apunta a null ya que no hay otro nodo después de este.

Insertamos luego el valor 4: insertar(4)

pila


Ahora el primer nodo de la pila es el que almacena el valor cuatro. raiz apunta a dicho nodo. Recordemos que raiz es el puntero externo a la lista que almacena la dirección del primer nodo. El nodo que acabamos de insertar en el campo puntero guarda la dirección del nodo que almacena el valor 10.

Ahora qué sucede si extraemos un nodo de la pila. ¿Cuál se extrae? Como sabemos en una pila se extrae el último en entrar.

Al extraer de la pila tenemos: extraer()

pila


La pila ha quedado con un nodo.

Hay que tener cuidado que si se extrae un nuevo nodo la pila quedará vacía y no se podrá extraer otros valores (avisar que la pila está vacía)

Problema 1:

Confeccionar una clase que administre una lista tipo pila (se debe poder insertar, extraer e imprimir los datos de la pila)

Programa:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ListasTipoPila1
{
    class Pila
    {
        class Nodo
        {
            public int info;
            public Nodo sig;
        }

        private Nodo raiz;

        public Pila()
        {
            raiz = null;
        }

        public void Insertar(int x)
        {
            Nodo nuevo;
            nuevo = new Nodo();
            nuevo.info = x;
            if (raiz == null)
            {
                nuevo.sig = null;
                raiz = nuevo;
            }
            else
            {
                nuevo.sig = raiz;
                raiz = nuevo;
            }
        }

        public int Extraer()
        {
            if (raiz != null)
            {
                int informacion = raiz.info;
                raiz = raiz.sig;
                return informacion;
            }
            else
            {
                return int.MaxValue;
            }
        }

        public void Imprimir() 
        {
            Nodo reco=raiz;
            Console.WriteLine("Listado de todos los elementos de la pila.");
            while (reco!=null) 
            {
                Console.Write(reco.info+"-");
                reco=reco.sig;
            }
            Console.WriteLine();
        }

        static void Main(string[] args)
        {
            Pila pila1=new Pila();
            pila1.Insertar(10);
            pila1.Insertar(40);
            pila1.Insertar(3);
            pila1.Imprimir();
            Console.WriteLine("Extraemos de la pila:"+pila1.Extraer());
            pila1.Imprimir();
            Console.ReadKey();
        }
    }
}

Analicemos las distintas partes de este programa:

        class Nodo
        {
            public int info;
            public Nodo sig;
        }

        private Nodo raiz;

Para declarar un nodo debemos utilizar una clase. En este caso la 
información del nodo (info) es un entero y siempre el nodo tendrá una 
referencia de tipo Nodo, que le llamamos sig.

El puntero sig apunta al siguiente nodo o a null en caso que no exista otro nodo. Este puntero es interno a la lista. Para poder acceder a los atributos los definimos de tipo public.

También definimos un puntero de tipo Nodo llamado raiz. Este puntero tiene la dirección del primer nodo de la lista. En caso de estar vacía la lista, raiz apunta a null (es decir no tiene dirección)

El puntero raiz es fundamental porque al tener la dirección del primer nodo de la lista nos permite acceder a los demás nodos.

        public Pila()
        {
            raiz = null;
        }

En el constructor de la clase hacemos que raiz guarde el valor null. Tengamos en cuenta que si raiz tiene almacenado null la lista está vacía, en caso contrario tiene la dirección del primer nodo de la lista.

        public void Insertar(int x)
        {
            Nodo nuevo;
            nuevo = new Nodo();
            nuevo.info = x;
            if (raiz == null)
            {
                nuevo.sig = null;
                raiz = nuevo;
            }
            else
            {
                nuevo.sig = raiz;
                raiz = nuevo;
            }
        }

Uno de los métodos más importantes que debemos entender en una pila es el de Insertar un elemento en la pila.

Al método llega la información a insertar, en este caso en particular es un valor entero.
La creación de un nodo requiere dos pasos:

- Definición de un puntero o referencia a un tipo de dato Nodo:

            Nodo nuevo;


- Creación del nodo (creación de un objeto):


            nuevo = new Nodo();

Cuando se ejecuta el operador new se reserva espacio para el nodo. Realmente se crea el nodo cuando se ejecuta el new.

pila


Paso seguido debemos guardar la información del nodo:

            nuevo.info = x;

En el campo info almacenamos lo que llega en el parámetro x. Por ejemplo si llega un 5 el nodo queda:

pila


Por último queda enlazar el nodo que acabamos de crear al principio de la lista.

Si la lista está vacía debemos guardar en el campo sig del nodo el valor null para indicar que no hay otro nodo después de este, y hacer que raiz apunte al nodo creado (sabemos si una lista esta vacía si raiz almacena un null)

            if (raiz == null)
            {
                nuevo.sig = null;
                raiz = nuevo;
            }

pila


Gráficamente podemos observar que cuando indicamos raiz=nuevo, el puntero raiz guarda la dirección del nodo apuntado por nuevo.

Tener en cuenta que cuando finaliza la ejecución del método el puntero nuevo desaparece, pero no el nodo creado con el operador new.

En caso que la lista no esté vacía, el puntero sig del nodo que acabamos de crear debe apuntar al que es hasta este momento el primer nodo, es decir al nodo que apunta raiz actualmente.

            else
            {
                nuevo.sig = raiz;
                raiz = nuevo;
            }

Como primera actividad cargamos en el puntero sig del nodo apuntado por nuevo la dirección de raiz, y posteriormente raiz apunta al nodo que acabamos de crear, que será ahora el primero de la lista.

Antes de los enlaces tenemos:

pila


Luego de ejecutar la línea:

                nuevo.sig = raiz;

Ahora tenemos:

pila


Por último asignamos a raiz la dirección que almacena el puntero nuevo.

                raiz = nuevo;

La lista queda:

pila


El método Extraer:

        public int Extraer()
        {
            if (raiz != null)
            {
                int informacion = raiz.info;
                raiz = raiz.sig;
                return informacion;
            }
            else
            {
                return int.MaxValue;
            }
        }

El objetivo del método extraer  es retornar la información del primer nodo y además borrarlo de la lista.

Si la lista no está vacía guardamos en una variable local la información del primer nodo:

                int informacion = raiz.info;

Avanzamos raiz al segundo nodo de la lista, ya que borraremos el primero:

                raiz = raiz.sig;

el nodo que previamente estaba apuntado por raiz es eliminado automáticamente, al no tener ninguna referencia.

Retornamos la información:

                return informacion;

En caso de estar  vacía la pila retornamos el número entero máximo y 
lo tomamos como código de error (es decir nunca debemos guardar el 
entero mayor en la pila)

                return int.MaxValue;

Es muy importante entender gráficamente el manejo de las listas. La 
interpretación gráfica nos permitirá plantear inicialmente las 
soluciones para el manejo de listas.

pila


Por último expliquemos el método para recorrer una lista en forma completa e imprimir la información de cada nodo:

        public void Imprimir() 
        {
            Nodo reco=raiz;
            Console.WriteLine("Listado de todos los elementos de la pila.");
            while (reco!=null) 
            {
                Console.Write(reco.info+"-");
                reco=reco.sig;
            }
            Console.WriteLine();
        }

Definimos un puntero auxiliar reco y hacemos que apunte al primer nodo de la lista:

            Nodo reco=raiz;

Disponemos una estructura repetitiva que se repetirá mientras reco 
sea distinto a null. Dentro de la estructura repetitiva hacemos que reco
 avance al siguiente nodo:

            while (reco!=null) 
            {
                Console.Write(reco.info+"-");
                reco=reco.sig;
            }

Es muy importante entender la línea:

                reco=reco.sig;

Estamos diciendo que reco almacena la dirección que tiene el puntero sig del nodo apuntado actualmente por reco.

Gráficamente:

pila


Al analizarse la condición:

            while (reco!=null) 

se valúa en verdadero ya que reco apunta a un nodo y se vuelve a ejecutar la línea:

                reco=reco.sig;

Ahora reco apunta al siguiente nodo:

pila


La condición del while nuevamente se valúa en verdadera y avanza el puntero reco al siguiente nodo:
                reco=reco.sig;

pila


Ahora sí reco apunta a null y ha llegado el final de la lista (Recordar que el último nodo de la lista tiene almacenado en el puntero sig el valor null, con el objetivo de saber que es el último nodo)

Para poder probar esta clase recordemos que debemos definir un objeto de la misma y llamar a sus métodos:

        static void Main(string[] args)
        {
            Pila pila1=new Pila();
            pila1.Insertar(10);
            pila1.Insertar(40);
            pila1.Insertar(3);
            pila1.Imprimir();
            Console.WriteLine("Extraemos de la pila:"+pila1.Extraer());
            pila1.Imprimir();
            Console.ReadKey();
        }

Insertamos 3 enteros, luego imprimimos la pila, extraemos uno de la pila y finalmente imprimimos nuevamente la pila.

Problema 2:

Agregar a la clase Pila un método que retorne la cantidad de nodos y otro que indique si esta vacía.

Programa:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ListasTipoPila1
{
    class Pila
    {
        class Nodo
        {
            public int info;
            public Nodo sig;
        }

        private Nodo raiz;

        public Pila()
        {
            raiz = null;
        }

        public void Insertar(int x)
        {
            Nodo nuevo;
            nuevo = new Nodo();
            nuevo.info = x;
            if (raiz == null)
            {
                nuevo.sig = null;
                raiz = nuevo;
            }
            else
            {
                nuevo.sig = raiz;
                raiz = nuevo;
            }
        }

        public int Extraer()
        {
            if (raiz != null)
            {
                int informacion = raiz.info;
                raiz = raiz.sig;
                return informacion;
            }
            else
            {
                return int.MaxValue;
            }
        }

        public void Imprimir() 
        {
            Nodo reco=raiz;
            Console.WriteLine("Listado de todos los elementos de la pila.");
            while (reco!=null) 
            {
                Console.Write(reco.info+"-");
                reco=reco.sig;
            }
            Console.WriteLine();
        }

        public bool Vacia()
        {
            if (raiz == null)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        public int Cantidad()
        {
            int cant = 0;
            Nodo reco = raiz;
            while (reco != null)
            {
                cant++;
                reco = reco.sig;
            }
            return cant;
        }

        static void Main(string[] args)
        {
            Pila pila1=new Pila();
            pila1.Insertar(10);
            pila1.Insertar(40);
            pila1.Insertar(3);
            pila1.Imprimir();
            Console.WriteLine("La cantidad de nodos de la lista es:"+pila1.Cantidad());
            while (pila1.Vacia()==false) 
            {
                Console.WriteLine(pila1.Extraer());
            }
            Console.ReadKey();
        }
    }
}

Para verificar si la pila esta vacía verificamos el contenido de la variable raiz, si tiene null luego la lista esta vacía y por lo tanto retornamos un true:

        public bool Vacia()
        {
            if (raiz == null)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

El algoritmo para saber la cantidad de nodos es similar al imprimir, 
pero en lugar de mostrar la información del nodo procedemos a 
incrementar un contador:

        public int Cantidad()
        {
            int cant = 0;
            Nodo reco = raiz;
            while (reco != null)
            {
                cant++;
                reco = reco.sig;
            }
            return cant;
        }

Para probar esta clase en la main creamos un objeto de la clase Pila insertamos tres enteros:

            Pila pila1=new Pila();
            pila1.Insertar(10);
            pila1.Insertar(40);
            pila1.Insertar(3);

Imprimimos la pila (nos muestra los tres datos):

            pila1.Imprimir();

Llamamos al método Cantidad (nos retorna un 3):

Console.WriteLine("La cantidad de nodos de la lista es:"+pila1.Cantidad());

Luego mientras el método Vacia nos retorne un false (lista no vacía) procedemos a llamar al método extraer:


            while (pila1.Vacia()==false) 
            {
                Console.WriteLine(pila1.Extraer());
            }



    Problemas propuestos

    1. Agregar un método a la clase Pila que retorne la información del primer nodo de la Pila sin borrarlo.

41 Estructuras dinámicas: Listas


Una lista es un conjunto de nodos, cada uno de los cuales tiene dos campos: uno de información y un apuntador al siguiente nodo de la lista. Además un apuntador externo señala el primer nodo de la lista.
Representación gráfica de un nodo:

Nodo

La información puede ser cualquier tipo de dato simple, estructura de datos o inclusive uno o más objetos.
La dirección al siguiente nodo es un puntero.

Representación gráfica de una lista:

lista

Como decíamos, una lista es una secuencia de nodos (en este caso cuatro nodos). La información de los nodos en este caso es un entero y siempre contiene un puntero que guarda la dirección del siguiente nodo.
raiz es otro puntero externo a la lista que contiene la dirección del primer nodo.

El estado de una lista varía durante la ejecución del programa:

lista vacia


De esta forma representamos gráficamente una lista vacía.

Si insertamos un nodo en la lista quedaría luego:
lista con un nodo


Si insertamos otro nodo al principio con el valor 9 tenemos:
lista con dos nodos


Lo mismo podemos borrar nodos de cualquier parte de la lista.

Esto nos trae a la mente el primer problema planteado: el desarrollo del procesador de texto. Podríamos utilizar una lista que inicialmente estuviera vacía e introdujéramos un nuevo nodo con cada línea que tipea el operador. Con esta estructura haremos un uso muy eficiente de la memoria.

Tipos de listas.

Según el mecanismo de inserción y extracción de nodos en la lista tenemos los siguientes tipos:
  • Listas tipo pila.
  • Listas tipo cola.
  • Listas genéricas.
Una lista se comporta como una pila si las inserciones y extracciones las hacemos por un mismo lado de la lista. También se las llama listas LIFO (Last In First Out - último en entrar primero en salir)

Una lista se comporta como una cola si las inserciones las hacemos al final y las extracciones las hacemos por el frente de la lista. También se las llama listas FIFO (First In First Out - primero en entrar primero en salir)


Una lista se comporta como genérica cuando las inserciones y extracciones se realizan en cualquier parte de la lista.

Podemos en algún momento insertar un nodo en medio de la lista, en otro momento al final, borrar uno del frente, borrar uno del fondo o uno interior, etc.

40 Estructuras dinámicas

Conocemos algunas estructuras de datos como son los vectores y matrices. No son las únicas. Hay muchas situaciones donde utilizar alguna de estas estructuras nos proporcionará una solución muy ineficiente (cantidad de espacio que ocupa en memoria, velocidad de acceso a la información, etc.)

Ejemplo 1. Imaginemos que debemos realizar un procesador de texto, debemos elegir la estructura de datos para almacenar en memoria las distintas líneas que el operador irá tipeando. Una solución factible es utilizar una matriz de caracteres. Pero como sabemos debemos especificar la cantidad de filas y columnas que ocupará de antemano. Podría ser por ejemplo 2000 filas y 200 columnas. Con esta definición estamos reservando de antemano 800000 bytes de la memoria, no importa si el operador después carga una línea con 20 caracteres, igualmente ya se ha reservado una cantidad de espacio que permanecerá ociosa.

Tiene que existir alguna estructura de datos que pueda hacer más eficiente la solución del problema anterior.
Ejemplo 2. ¿Cómo estarán codificadas las planillas de cálculo? ¿Reservarán espacio para cada casilla de la planilla al principio? Si no la lleno, ¿lo mismo se habrá reservado espacio?


Utilizar una matriz para almacenar todas las casillas de una planilla de cálculo seguro será ineficiente.



Bien, todos estos problemas y muchos más podrán ser resueltos en forma eficiente cuando conozcamos estas nuevas estructuras de datos (Listas, árboles)

sábado, 12 de octubre de 2013

39 Controles comunes - ComboBox

El control ComboBox permite seleccionar un string de una lista.

Para inicializar los string que contendrá el ComboBox debemos acceder a la propiedad Items
Un evento muy útil con este control es cuando el operador selecciona un Item de la lista. Para capturar la selección de un item debemos codificar el evento SelectedIndexChanged.

Problema 1:

Cargar en un ComboBox los nombres de varios colores. Al seleccionar alguno mostrar en la barra de título del Form el string seleccionado.


Programa:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApplicationComboBox1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            Text = comboBox1.Text;
        }
    }
}

Cuando se selecciona un string de la lista se dispara el evento SelectedIndexChanged y procedemos a extraer el texto seleccionado del ComboBox y lo mostramos en el título del Form:

        private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            Text = comboBox1.Text;
        }


Problema 2:

Disponer tres controles de tipo ComboBox con valores entre 0 y 255 (cada uno representa la cantidad de rojo, verde y azul). Luego al presionar un botón pintar el fondo del Form con el color que se genera combinando los valores de los ComboBox.

ComboBox

Programa:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApplicationComboBox2
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            for (int f = 0; f <= 255; f++)
            {
                comboBox1.Items.Add(f.ToString());
                comboBox2.Items.Add(f.ToString());
                comboBox3.Items.Add(f.ToString());
            }
            comboBox1.SelectedIndex = 0;
            comboBox2.SelectedIndex = 0;
            comboBox3.SelectedIndex = 0;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            int rojo = int.Parse(comboBox1.Text);
            int verde = int.Parse(comboBox2.Text);
            int azul = int.Parse(comboBox3.Text);
            BackColor = Color.FromArgb(rojo, verde, azul);
        }
    }
}

La carga manual de cada ComboBox nos haría perder mucho tiempo en tiempo de diseño por lo que lo hacemos mediante un algoritmo. Cuando se carga el Form se ejecuta el evento Load donde mediante un for procedemos a añadir los 256 valores:

        private void Form1_Load(object sender, EventArgs e)
        {
            for (int f = 0; f <= 255; f++)
            {
                comboBox1.Items.Add(f.ToString());
                comboBox2.Items.Add(f.ToString());
                comboBox3.Items.Add(f.ToString());
            }

La propiedad Items del ComboBox tiene un método llamado Add que añade
 un elemento a la lista (como debemos pasar un string como parámetro 
convertimos a la variable entera f a string)

Luego para dejar seleccionado por defecto el primer item añadido inicializamos la propiedad SelectedIndex:

            comboBox1.SelectedIndex = 0;
            comboBox2.SelectedIndex = 0;
            comboBox3.SelectedIndex = 0;

En el evento Click del botón procedemos a extraer el valor seleccionado de cada ComboBox y lo convertimos a entero:

            int rojo = int.Parse(comboBox1.Text);
            int verde = int.Parse(comboBox2.Text);
            int azul = int.Parse(comboBox3.Text);

Para cambiar el color de fondo del Form actualizamos la propiedad BackColor. El color lo generamos llamando al método estático FromArgb de la clase Color:

            BackColor = Color.FromArgb(rojo, verde, azul);


Problemas propuestos


  1. Solicitar el ingreso del nombre de una persona y seleccionar de un control ComboBox un país. Al presionar un botón mostrar en la barra del título del Form el nombre ingresado y el país seleccionado.

37 Controles comunes - CheckBox

El control CheckBox permite implementar un cuadro de selección (básicamente un botón de dos estados: seleccionado o no seleccionado)


Problema 1:

Confeccionar un programa que muestre 3 objetos de la clase CheckBox con etiquetas de tres idiomas.

Cuando se presiona un botón mostrar en la barra de títulos del Form todos los CheckBox seleccionados hasta el momento.

CheckBox


Programa:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApplicationCheckBox1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Text = "";
            if (checkBox1.Checked == true)
            {
                Text = Text + "(Inglés)";
            }
            if (checkBox2.Checked == true)
            {
                Text = Text + "(Francés)";
            }
            if (checkBox3.Checked == true)
            {
                Text = Text + "(Alemán)";
            }
        }
    }
}

La clase CheckBox tiene una propiedad llamada Checked (si tiene el valor true significa que el CheckBox esta seleccionado, en caso contrario no esta seleccionado.

En el evento Click del botón primero borramos el contenido del título del Form:

            Text = "";

Y seguidamente mediante estructuras if verificamos el estado de cada CheckBox, en caso de estar seleccionado concatenamos al título del Form el valor que representa ese CheckBox:

            if (checkBox1.Checked == true)
            {
                Text = Text + "(Inglés)";
            }


Problema 2:

Disponer un control Label que muestre el siguiente mensaje: "Esta de acuerdo con las normas del servicio?", luego un CheckBox y finalmente un objeto de tipo Button desactivo (propiedad Enabled con false). Cuando se tilde el CheckBox debemos activar el botón (para esto debemos responder al evento)

CheckBox


Programa:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApplicationCheckBox2
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void checkBox1_CheckedChanged(object sender, EventArgs e)
        {
            if (checkBox1.Checked == true)
            {
                button1.Enabled = true;
            }
            else
            {
                button1.Enabled = false;
            }
        }
    }
}

Debemos implementar el evento CheckedChange del objeto checkBox1 (preguntamos si el CheckBox se encuentra seleccionado o no, en caso de estar seleccionado activamos el botón asignando a la propiedad Enabled el valor true):

        private void checkBox1_CheckedChanged(object sender, EventArgs e)
        {
            if (checkBox1.Checked == true)
            {
                button1.Enabled = true;
            }
            else
            {
                button1.Enabled = false;
            }
        }


Problema propuesto


  1. Disponer tres objetos de la clase CheckBox con nombres de navegadores web. Cuando se presione un botón mostrar en el título del Form los programas seleccionados.

36 Controles comunes - TextBox

El control más común para la entrada de datos por teclado es el TextBox.


Problema 1:

Confeccionar un programa que permita ingresar dos valores enteros por teclado y al presionar un botón mostrar en una Label la suma de dichos valores.

TextBox

Programa:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApplicationTextBox1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            int valor1 = int.Parse(textBox1.Text);
            int valor2 = int.Parse(textBox2.Text);
            int suma = valor1 + valor2;
            label4.Text = suma.ToString();
        }
    }
}

Para saber el valor almacenado en un TextBox disponemos de la 
propiedad Text. Como la propiedad Text es de tipo string debemos 
convertir dicho valor a tipo entero mediante el método estático Parse de la clase int.

Luego para recuperar como enteros los dos valores almacenados en los TextBox:

            int valor1 = int.Parse(textBox1.Text);
            int valor2 = int.Parse(textBox2.Text);

Sumamos los dos enteros:

            int suma = valor1 + valor2;

Y finalmente cargamos en un objeto de la clase Label el resultado de 
la suma. Como la variable suma es un entero debemos llamar al método 
ToString() para retornar dicho valor como string:

            label4.Text = suma.ToString();


Problema 2:

Solicitar que se ingrese una clave. Si se ingresa la cadena "abc123" mostrar un mensaje de clave correcta en caso contrario mostrar clave incorrecta.

Utilizar un control de tipo TextBox para el ingreso de la clave y una Label para mostrar el resultado al presionar un botón.

Inicializar la propiedad UseSystemPasswordChar con el valor true (esto hace que cuando el operador tipee caracteres dentro del TextBox se visualicen como asteriscos)

TextBox


Programa:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApplicationTextBox2
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            if (textBox1.Text == "abc123")
            {
                label2.Text = "Clave correcta";
            }
            else
            {
                label2.Text = "Clave incorrecta";
            }
        }
    }
}

Para verificar si la clave es correcta comparamos la cadena cargada en el textBox1 con la cadena "abc123".
Hay otra propiedad en la clase TextBox llamada PasswordChar, si la propiedad UseSystemPasswordChar esta configurada con false podemos inicializar la propiedad PasswordChar con el caracter que queremos que se muestre al ingresar datos en el TextBox. Probar de inicializarlo con el caracter '+' y veremos que en vez de aparecer asteriscos aparecen caracteres '+'

Problema 3:

Disponer un control de tipo TextBox e inicializar la propiedad Multiline con el valor true (esto permite ingresar múltiples líneas dentro de un TextBox.

TextBox


Programa:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApplicationTextBox3
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            MessageBox.Show(textBox2.Text);
        }
    }
}

Cuando se presiona un botón se muestra en cuadro de mensajes (MessageBox) el texto ingresado en el textBox2:

            MessageBox.Show(textBox2.Text);

Problema propuesto


  1. Solicitar el ingreso de una clave de hasta 10 caracteres en un control de tipo TextBox (inicializar la propiedad MaxLength con el valor 10)
    Mostrar en un cuadro de mensajes la clave ingresada al presionar un botón. 

viernes, 4 de octubre de 2013

35 Controles comunes - Button

Un control común a disponer dentro de un Form son los botones, esto se hace disponiendo objetos de la clase Button.

Problema 1:

Confeccionar un formulario que muestre tres objetos de la clase Button, disponer como en cada botón 1,2 y 3. Cuando se presiona el botón mostrar en el título del formulario el valor de la etiqueta del botón presionado.

Button

Programa:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApplicationButton1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Text = button1.Text;
        }

        private void button2_Click(object sender, EventArgs e)
        {
            Text = button2.Text;
        }

        private void button3_Click(object sender, EventArgs e)
        {
            Text = button3.Text;
        }
    }
}

Para el evento click de cada botón inicializamos la propiedad Text del formulario con la propiedad Text del botón presionado (como la clase Form1 hereda de la clase Form luego accedemos a la propiedad Text sin anteceder nombre alguno: Text = button1.Text; ):

        private void button1_Click(object sender, EventArgs e)
        {
            Text = button1.Text;
        }

Problema 2:

el problema anterior para que se acumulen en el título del formulario los valores de los botones presionados.

Programa:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApplicationButton2
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Text = Text + button1.Text;
        }

        private void button2_Click(object sender, EventArgs e)
        {
            Text = Text + button2.Text;
        }

        private void button3_Click(object sender, EventArgs e)
        {
            Text = Text + button3.Text;
        }
    }
}

Concatenamos el actual de la propiedad Text del formulario con el valor de la propiedad Text del botón respectivo:

        private void button1_Click(object sender, EventArgs e)
        {
            Text = Text + button1.Text;
        }

Problema 3:

Similar al problema anterior solo permitir mostrar hasta 10 caracteres en el título del formulario.

Programa:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApplicationButton3
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            if (Text.Length < 10)
            {
                Text = Text + button1.Text;
            }
        }

        private void button2_Click(object sender, EventArgs e)
        {
            if (Text.Length < 10)
            {
                Text = Text + button2.Text;
            }
        }

        private void button3_Click(object sender, EventArgs e)
        {
            if (Text.Length < 10)
            {
                Text = Text + button3.Text;
            }
        }
    }
}

Como la propiedad Text es de tipo string luego podemos acceder a la propiedad Length para conocer la cantidad de caracteres almacenados:

        private void button1_Click(object sender, EventArgs e)
        {
            if (Text.Length < 10)
            {
                Text = Text + button1.Text;
            }
        }

Problema propuesto


  1. Elaborar una interfaz que muestre una calculadora (utilizar objetos de la clase Button y un objeto de la clase Label donde se muestra el valor ingresado), tener en que solo se debe implementar la interfaz y la carga de un valor de hasta 12 dígitos.

34 Controles comunes - Label

En el "cuadro de herramientas" podemos ver las componentes visuales agrupadas. En la pestaña de comunes podemos acceder a los controles visuales que normalmente toda aplicación requiere.
El primer control que vamos a analizar es la clase Label. Un control de tipo Label nos mostrar básicamente un texto inicializando la propiedad Text.

Clase Label - Windows Forms


Las propiedades más comunes de este control son:
  • Text: Es el string que muestra el control.
  • BackColor: Define el color de fondo del control.
  • ForeColor: Define el color del Texto .
  • Font: Define la fuente del texto.
  • BorderStyle: Define si la label tiene un borde visible.
  • AutoSize: Permite o no el label en Tamaño.
  • Cursor: Definimos el ícono del cursor a mostrar cuando disponemos el mouse del control.
  • Visible: Determina si el control está visible u oculto cuando ejecutamos el programa.

Problema propuesto


  1. Crear una aplicación que muestre en 6 objetos de la clase Label con algunos nombres de controles visuales contenidos en la pestaña de "controles comunes" del cuadro de herramientas

33 Ventana de eventos (Windows Forms)

La ventana de eventos coincide con la ventana de propiedades. Para activar la disponibles para un objeto debemos presionar:

Ventana de eventos Windows Forms


Podemos observar la lista de que puede reaccionar el objeto seleccionado en ese momento. Por ejemplo si tenemos seleccionado un objeto de la clase Button el evento más común que deberemos implementar es el Click (este evento se dispara cuando en tiempo de ejecución del programa se presiona el botón)
Para disponer el código para dicho evento debemos hacer doble clic sobre dicho evento (esto hace que se active la ventana del editor y genere automáticamente el método asociado a dicho evento):

Ventana de eventos Windows Forms

Problema:

Confeccionar un programa que al presionar un botón se muestre en un objeto de la clase Label el string "Hola Mundo".

Programa:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApplication5
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            label1.Text = "Hola Mundo";
        }
    }
}
tener en cuenta que la clase anterior es parcial (el archivo Form1.Designer.cs contiene la definición de los dos objetos y la inicialización de sus propiedades y evento):
namespace WindowsFormsApplication5
{
    partial class Form1
    {
        /// 
        /// Variable del diseñador requerida.
        /// 
        private System.ComponentModel.IContainer components = null;

        /// 
        /// Limpiar los recursos que se estén utilizando.
        /// 
        /// true si los recursos administrados se deben eliminar;
        /// false en caso contrario, false.
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Código generado por el Diseñador de 

        /// 
        /// Método necesario para admitir el Diseñador. No se puede modificar
        /// el contenido del método con el .
        /// 
        private void InitializeComponent()
        {
            this.label1 = new System.Windows.Forms.Label();
            this.button1 = new System.Windows.Forms.Button();
            this.SuspendLayout();
            // 
            // label1
            // 
            this.label1.AutoSize = true;
            this.label1.Location = new System.Drawing.Point(48, 102);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(35, 13);
            this.label1.TabIndex = 0;
            this.label1.Text = "label1";
            // 
            // button1
            // 
            this.button1.Location = new System.Drawing.Point(51, 148);
            this.button1.Name = "button1";
            this.button1.Size = new System.Drawing.Size(75, 23);
            this.button1.TabIndex = 1;
            this.button1.Text = "Presionar";
            this.button1.UseVisualStyleBackColor = true;
            this.button1.Click += new System.EventHandler(this.button1_Click);
            // 
            // Form1
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(292, 273);
            this.Controls.Add(this.button1);
            this.Controls.Add(this.label1);
            this.Name = "Form1";
            this.Text = "Form1";
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

        private System.Windows.Forms.Label label1;
        private System.Windows.Forms.Button button1;
    }
}

Al ejecutar el programa si presionamos el botón vemos como cambia el contenido de la Label (esto debido a que en el evento click del botón cambiamos el valor de la propiedad Text del objeto de la clase Label):

Ventana de eventos Windows Forms

Problema propuesto

  1. Disponer 7 objetos de la clase Button con los días de la semana. Fijar en los atributos Text de cada botón los días de la semana. Al presionar un botón mostrar en un objeto de la clase Label el día seleccionado. 

32 Ventana de propiedades (Windows Forms)

La " de propiedades" nos permite inicializar de las propiedades del objeto que se encuentra seleccionado en el formulario (Button, MonthCalendar, TextBox etc.)

Por ejemplo si disponemos dos objetos de la clase Button y seleccionamos uno de ellos podremos editar las propiedades del mismo en la "ventana de propiedades":

Ventana de propiedades Windows Forms


A medida que seleccionamos un objeto en la ventana de "Diseño" podemos ver como se actualiza la "ventana de propiedades", por ejemplo la propiedad Text de la clase Button fijar la que muestra el botón.

El formulario también es un objeto, esto quiere decir que se lo seleccionamos luego la "ventana de propiedades" nos muestra las propiedades de la clase Form:

Ventana de propiedades Windows Forms

Problema propuesto

  1. Elaborar una interfaz que muestre una calculadora (utilizar objetos de la clase Button y un objeto de la clase donde se mostrarían los resultados y se cargarían los datos).

31 Cuadro de herramientas (Windows Forms)

El cuadro de herramientas contiene componentes visuales que nos permiten elaborar nuestro formulario.
Podemos ver controles visuales en :

Cuadro de herramientas Windows Forms


O agrupados por su uso(Menús, datos etc.):

Cuadro de herramientas Windows Forms

Problema 1:

: Desarrollar un programa que muestre un objeto de cada una de las siguientes clases: MonthCalendar, y Button

La interfaz visual debe ser parecida a esta:

Cuadro de herramientas Windows Forms


Hasta ahora solo hemos creado una interfaz visual, como podemos ver algunas componentes en de ejecución tienen funcionalidad (el objeto de la clase MonthCalendar si ejecutamos el programa nos seleccionar una fecha, cambiar de mes etc., el control de la clase TextBox nos permite ingresar una cadena de caracteres, pero el objeto de la clase Button cuando se presiona podemos ver que se visualiza que es hundido con el mouse pero no hace nada):

Cuadro de herramientas Windows Forms

30 Interfaces visuales (Windows Forms)

Hasta ahora hemos resuelto todos los algoritmos haciendo las salidas a través de una consola en modo texto. La realidad que es muy común la necesidad de hacer la entrada y salida de datos mediante una interfaz más amigable con el usuario.

En C# existen varias librerías de clase para implementar interfaces visuales. Utilizaremos las ventanas.
Para crear una aplicación que utilice esta librería debemos crear un proyecto. Los pasos son los siguientes:

  1. Desde el menú de opciones del .Net seleccionamos la opción: Archivo -> Nuevo proyecto...
  2. Seleccionamos la plantilla "Aplicación de Windows Forms".Proyecto Windows Forms
  3. Ahora ya tenemos un esqueleto para desarrollar nuestra aplicación. Si vemos la ventana del "Explorador de soluciones tenemos tres archivos generados en : Program.cs, Form1.cs y Form1.Designer.cs:
  4. Proyecto Windows Forms
  5. En la tenemos el Form listo para disponer controles con el mouse.
  6. Ahora podemos seleccionar un control visual de la ventana "Cuadro de herramientas" (seleccionemos el control Button) y seguidamente presionemos el botón izquierdo del mouse dentro del formulario que se encuentra en la parte central del Visual Studio .net:
  7. Proyecto Windows Forms
  8. Ahora podemos analizar la ventana "Propiedades" que nos muestra las propiedades del objeto seleccionado del formulario. Podemos por ejemplo si tenemos seleccionado el botón cambiar la propiedad text (la misma cambia la etiqueta que muestra el botón):
    Proyecto Windows Forms
  9. Cuando ejecutamos la aplicación el resultado podemos ver que es muy distinto a la interfaz en modo texto vista hasta el momento:
    Windows Forms
  10. Por último vamos a ver los contenidos de los archivos generados automáticamente por el Visual Studio .Net.

    Program.ch

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Windows.Forms;
    
    namespace WindowsFormsApplication1
    {
        static 
        {
            /// 
            /// Punto de entrada principal para la aplicación.
            /// 
            [STAThread]
            static void Main()
            {
                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);
                Application.Run(new Form1());
            }
        }
    }
    

    Form1.ch

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    
    namespace WindowsFormsApplication1
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }
        }
    }
    

    Form1.Designer.ch

    namespace WindowsFormsApplication1
    {
        partial class Form1
        {
            /// 
            /// Variable del diseñador requerida.
            /// 
            private System.ComponentModel.IContainer components = null;
    
            /// 
            /// Limpiar los recursos que se estén utilizando.
            /// 
            /// true si los recursos administrados se deben eliminar; false en caso contrario, false.
            protected override void Dispose(bool disposing)
            {
                if (disposing && (components != null))
                {
                    components.Dispose();
                }
                base.Dispose(disposing);
            }
    
            #region Código generado por el Diseñador de Windows Forms
    
            /// 
            /// Método necesario para admitir el Diseñador. No se puede modificar
            /// el contenido del método con el .
            /// 
            private void InitializeComponent()
            {
                this.button1 = new System.Windows.Forms.Button();
                this.SuspendLayout();
                // 
                // button1
                // 
                this.button1.Location = new System.Drawing.Point(190, 223);
                this.button1.Name = "button1";
                this.button1.Size = new System.Drawing.Size(75, 23);
                this.button1.TabIndex = 0;
                this.button1.Text = "Hola Mundo";
                this.button1.UseVisualStyleBackColor = true;
                // 
                // Form1
                // 
                this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
                this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
                this.ClientSize = new System.Drawing.Size(292, 273);
                this.Controls.Add(this.button1);
                this.Name = "Form1";
                this.Text = "Form1";
                this.ResumeLayout(false);
    
            }
    
            #endregion
    
            private System.Windows.Forms.Button button1;
        }
    }
    

domingo, 22 de septiembre de 2013

29 Clase parcial (partial class)

Hasta ahora hemos visto que una clase se la implementa en dentro de un archivo. El lenguaje C# permite la implementación de una clase en dos o más archivos. Para esto hay que agregarle el modificador partial cuando declaramos la clase.


Este concepto es ámpliamente utilizado por el entorno del .Net en la generación de interfaces visuales.

Como veremos en conceptos futuros es necesario presentar " class" para su entendimiento.

Una clase no es más ni menos que crear una clase completa y luego agrupar métodos y propiedades en dos o más archivos.


Problema 1:

Plantear una clase Rectangulo, definir dos propiedades: Lado1 y Lado2. Definir dos métodos RetornarSuperficie y RetornarPerimetro. Dividir la clase en dos archivos utilizando el concepto de "partial class".

Programa:

Archivo1.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ClaseParcial1
{
    partial class Rectangulo
    {
        private int lado1;
        public int Lado1
        {
            set
            {
                lado1 = value;
            }
            get
            {
                return lado1;
            }
        }
        private int lado2;
        public int Lado2
        {
            set
            {
                lado2 = value;
            }
            get
            {
                return lado2;
            }
        }
    }
}


Archivo2.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ClaseParcial1
{
    partial class Rectangulo
    {
        public int RetornarSuperficie()
        {
            int sup = Lado1 * Lado2;
            return sup;
        }

        public int RetornarPerimetro()
        {
            int per = Lado1 * 2 + Lado2 * 2;
            return per;
        }
    }
}


Program.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ClaseParcial1
{
    class Program
    {
        static void Main(string[] args)
        {
            Rectangulo rectangulo1 = new Rectangulo();
            rectangulo1.Lado1 = 5;
            rectangulo1.Lado2 = 10;
            Console.WriteLine("La superficie del rectángulo es:" +
                                rectangulo1.RetornarSuperficie());
            Console.WriteLine("El perímetro del rectángulo es:" + 
                                rectangulo1.RetornarPerimetro());
            Console.ReadKey();
        }
    }
}


Para codificar este proyecto procedemos de la siguiente forma:

  1. Seleccionamos desde el menú de opciones Archivo -> Nuevo proyecto...
  2. En el diálogo definimos el nombre del proyecto: ClaseParcial1
  3. Ahora tenemos que agregar los otros archivos. Presionamos el botón derecho del mouse en la ventana del "Explorador de soluciones" sobre el nombre del proyecto ("ClaseParcial1") y seleccionamos la opción Agregar -> Nuevo elemento.
  4. Seleccionamos en el diálogo la plantilla "Clase" y en la parte inferior del diálogo definimos el nombre del archivo, en nuestro caso lo llamamos "archivo1.cs".
  5. En este archivo planteamos la clase "partial class Rectangulo" que define las dos propiedades y atributos.
  6. En forma similar seguimos los crear el archivo2.cs y codificar la clase "partial class Rectangulo" que define los dos métodos.
  7. Finalmente codificamos la clase principal en el archivo Program.cs
    En la main creamos un objeto de la clase Rectangulo e inicializamos las propiedades y llamamos a sus métodos. 

28 Orden de ejecución de los constructores con herencia

Cuando tenemos constructores en las clases y subclases el orden de ejecución de los mismos es :


  Primero se ejecuta el constructor de la clase Padre.
   se ejecuta el constructor de la subclase.


Problema 1:

Plantear tres clases A, B, C que B herede de A y C herede de B. Definir un constructor a cada clase que muestre un mensaje. Luego definir un objeto de la clase C.

Programa:

using System;
using System.Collections.;
using System.Linq;
using System.Text;

namespace Herencia3
{
     A
    {
        public A()
        {
            Console.WriteLine("Constructor de la clase A");
        }
    }

    public class B : A 
    {
        public B()
        {
            Console.WriteLine("Constructor de la clase B");
        }
    }

    public class C : B
    {
        public C()
        {
            Console.WriteLine("Constructor de la clase C");
        }
    }

    class Prueba
    {
        static void Main(string[] args)
        {
            C obj1 = new C();
            Console.ReadKey();
        }
    }
}

Como se puede la ejecución del programa la salida por pantalla es:

Constructor de la clase A
Constructor de la clase B
Constructor de la clase C

Es decir cuando creamos un objeto de la clase C lo primero que se ejecuta es el constructor de la clase de nivel superior (en este caso la clase A), luego el constructor de la clase B y finalmente se ejecuta el constructor de la clase C.



Problema 2:

Plantear tres clases A, B, C que B herede de A y C herede de B. Definir un constructor a cada clase que reciba como parámetro un entero. Luego definir un objeto de la clase C.

Programa:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Herencia4
{
    public class A
    {
        public A(int a)
        {
            Console.WriteLine(a);
        }
    }

    public class B : A
    {
        public B(int b):base(b/2)
        {
            Console.WriteLine(b);
        }
    }

    public class C : B
    {
        public C(int c):base(c/2)
        {
            Console.WriteLine(c);
        }
    }

    class Prueba
    {
        static void Main(string[] args)
        {
            C obj1 = new C(20);
            Console.ReadKey();
        }
    }
}

Como podemos ver la clase el constructor de la clase C debe llamar en forma explícita al constructor de la clase padre mediante la base con el valor a pasar (en este ejemplo le pasamos el parámetro c dividido por dos):

        public C(int c):base(c/2)

En forma similar el constructor de la clase B debe llamar al constructor de la clase A:

        public B(int b):base(b/2)

Si ejecutamos el programa podemos ver que aparece por pantalla los números:

5
10
20


Donde definimos el objeto obj1 pasamos el 20, el constructor de la clase C llama al constructor de la clase B pasando el valor 10, luego el constructor de la clase B llama al constructor de la clase C pasando el valor 5. Como vimos anteriormente primero se ejecuta el constructor de la clase A mostrando el valor 5, seguidamente el constructor de la clase B mostrando el valor 10 y finalmente se ejecuta el constructor de la clase A mostrando el 20.

27 Herencia

Vimos en el concepto anterior que dos clases pueden estar relacionadas por la colaboración. Ahora veremos otro tipo de relaciones entre clases que es la Herencia.

La herencia significa que se pueden crear nuevas clases partiendo de clases existentes, que tendrá todas los , propiedades y los métodos de su 'superclase' o 'clase padre' y además se le podrán añadir otros atributos, propiedades y métodos propios.

clase padre

Clase de la que desciende o deriva una clase. Las clases hijas (descendientes) heredan (incorporan) automáticamente los atributos, propiedades y métodos de la la clase padre.

Subclase

Clase desciendiente de otra. Hereda automáticamente los atributos, propiedades y métodos de su superclase. Es una especialización de otra clase. Admiten la definición de nuevos atributos y métodos para aumentar la especialización de la clase.

Veamos algunos ejemplos teóricos de herencia:

1) Imaginemos la clase Vehículo. Qué clases podrían derivar de ella?
                            Vehiculo

   Colectivo                Moto                    
                             
                                             FordK        Renault 9

Siempre hacia abajo en la jerarquía hay una especialización (las subclases añaden nuevos atributos, propiedades y métodos.

2) Imaginemos la clase . Qué clases podrían derivar de ella?

                                           Software

             DeAplicacion                                        DeBase

ProcesadorTexto       PlanillaDeCalculo                          SistemaOperativo

Word   WordPerfect    Excel     Lotus123                         Linux    Windows       

El primer tipo de relación que habíamos visto clases, es la de colaboración. Recordemos que es cuando una clase contiene un objeto de otra clase como atributo.

Cuando la relación entre dos clases es del tipo "...tiene un..." o "...es parte de...", no debemos implementar herencia. Estamos frente a una relación de colaboración de clases no de herencia.

Si tenemos una ClaseA y otra ClaseB y notamos que entre ellas existe una relacion de tipo "... tiene un...", no debe implementarse herencia sino declarar en la clase ClaseA un atributo de la clase ClaseB.

Por ejemplo: tenemos una clase Auto, una clase Rueda y una clase Volante. Vemos que la relación entre ellas es: Auto "...tiene 4..." Rueda, Volante "...es parte de..." Auto; pero la clase Auto no debe derivar de Rueda ni Volante de Auto porque la relación no es de tipo-subtipo sino de colaboración. Debemos declarar en la clase Auto 4 atributos de tipo Rueda y 1 de tipo Volante.

Luego si vemos que dos clase responden a la pregunta ClaseA "..es un.." ClaseB es posible que haya una relación de herencia.

Por ejemplo:
Auto "es un" Vehiculo
Circulo "es una" Figura
Mouse "es un" DispositivoEntrada
Suma "es una" Operacion


Problema 1:

Ahora plantearemos el primer problema utilizando herencia. Supongamos que necesitamos implementar dos clases que llamaremos Suma y Resta. Cada clase tiene como atributo valor1, valor2 y resultado. Las propiedades a definir son Valor1, Valor2 y Resultado, el método Operar (que en el caso de la clase "Suma" suma los dos y en el caso de la clase "Resta" hace la diferencia entre Valor1 y Valor2.
Si analizamos ambas clases encontramos que muchas propiedades son idénticos. En estos casos es bueno definir una clase padre que agrupe dichas propiedades, atributos y responsabilidades comunes.
La relación de herencia que podemos disponer para este problema es:

                                        Operacion

                        Suma                              Resta

Solamente el método operar es distinto clases Suma y Resta (esto hace que no lo podamos disponer en la clase Operacion), luego las propiedades Valor1, Valor2 son idénticos a las dos clases, esto hace que podamos disponerlos en la clase Operacion. Lo mismo las propiedades Valor1, Valor2 y Resultado se definirán en la clase padre Operacion.


Crear un proyecto y luego crear cuatro clases llamadas: Operacion, Suma, Resta y Prueba


Programa:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Herencia1
{
  
    public class Operacion {
        protected int valor1;
        protected int valor2;
        protected int resultado;

        public int Valor1 
        {
            set 
            {
                valor1=value;
            }
            get
            {
                return valor1;
            }
        }

        public int Valor2 
        {
            set 
            {
                valor2=value;
            }
            get
            {
                return valor2;
            }
        }

        public int Resultado
        {
            protected set 
            {
               resultado=value;
            }
            get
            {
                return resultado;
            }
        }        
    }


    public class Suma: Operacion
    {
        public void Operar() 
        {
            Resultado=Valor1+Valor2;
        }
    }


    public class Resta: Operacion
    {
        public void Operar() 
        {
            Resultado=Valor1-Valor2;
        }
    }

    class Prueba
    {
        static void Main(string[] args)
        {
            Suma suma1 = new Suma();
            suma1.Valor1 = 10;
            suma1.Valor2 = 7;
            suma1.Operar();
            Console.WriteLine("La suma de " + suma1.Valor1 + " y " + 
              suma1.Valor2 + " es " + suma1.Resultado);

            Resta resta1 = new Resta();
            resta1.Valor1 = 8;
            resta1.Valor2 = 4;
            resta1.Operar();
            Console.WriteLine("La diferencia de " + resta1.Valor1 + 
              " y " + resta1.Valor2 + " es " + resta1.Resultado);

            Console.ReadKey();
        }
    }
}

La clase Operación define tres atributos y sus tres propiedades que las acceden:

        protected int valor1;
        protected int valor2;
        protected int resultado;

        public int Valor1 
        {
            set 
            {
                valor1=value;
            }
            get
            {
                return valor1;
            }
        }

        public int Valor2 
        {
            set 
            {
                valor2=value;
            }
            get
            {
                return valor2;
            }
        }

        public int Resultado
        {
            protected set 
            {
               resultado=value;
            }
            get
            {
                return resultado;
            }
        }        

Ya veremos que definimos los atributos con este nuevo modificador de acceso (protected) para que la subclase tenga acceso a dichos atributos. Si los definimos private las subclases no pueden acceder a dichos atributos.

Ahora veamos como es la sintaxis para indicar que una clase hereda de otra:

    public class Suma: Operacion

Disponemos dos puntos y seguidamente el nombre de la clase padre (con esto estamos indicando que todas las propiedades de la clase Operación son también propiedades de la clase Suma.

Luego la característica que añade la clase Suma es el siguiente método:

        public void Operar() 
        {
            Resultado=Valor1+Valor2;
        }

El método Operar puede acceder a las propiedades heredadas (siempre y cuando los mismos se declaren protected, en caso que sean private si bien lo hereda de la clase padre solo los pueden modificar métodos de dicha clase padre)

Ahora podemos decir que la clase Suma tiene tres propiedades y un método.

Luego en otra clase creamos un objeto de la clase Suma:

    class Prueba
    {
        static void Main(string[] args)
        {
            Suma suma1 = new Suma();
            suma1.Valor1 = 10;
            suma1.Valor2 = 7;
            suma1.Operar();
            Console.WriteLine("La suma de " + suma1.Valor1 + " y " + 
              suma1.Valor2 + " es " + suma1.Resultado);

            Resta resta1 = new Resta();
            resta1.Valor1 = 8;
            resta1.Valor2 = 4;
            resta1.Operar();
            Console.WriteLine("La diferencia de " + resta1.Valor1 + 
              " y " + resta1.Valor2 + " es " + resta1.Resultado);

            Console.ReadKey();
        }
    }

Podemos llamar tanto al método propio de la clase Suma "Operar()" como acceder a las propiedades heredadas de la clase Operacion. Quien utilice la clase Suma solo debe conocer que métodos y propiedades públicas tiene (independientemente que pertenezcan a la clase Suma o a una clase superior)
La lógica es similar para declarar la clase Resta.

La clase Operación agrupa en este caso un conjunto de atributos y propiedades comunes a un conjunto de subclases (Suma, Resta). No tiene sentido definir objetos de la clase Operacion.

El planteo de jerarquías de clases es una tarea compleja que requiere un perfecto entendimiento de todas las clases que intervienen en un problema, cuales son sus atributos, propiedades y responsabilidades.


Problema 2:

Confeccionar una clase Persona que tenga como atributos el nombre y la edad (definir las propiedades para poder acceder a dichos atributos). Definir como responsabilidad un método para imprimir.

Plantear una segunda clase Empleado que herede de la clase Persona. Añadir un atributo sueldo ( y su propiedad) y el método para imprimir su sueldo.

Definir un objeto de la clase Persona y llamar a sus métodos y propiedades. También crear un objeto de la clase Empleado y llamar a sus métodos y propiedades.


Programa:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Herencia2
{
    public class Persona
    {
        protected string nombre;
        protected int edad;

        public string Nombre
        {
            set
            {
                nombre = value;
            }
            get
            {
                return nombre;
            }
        }

        public int Edad
        {
            set
            {
                edad = value;
            }
            get
            {
                return edad;
            }
        }

        public void Imprimir()
        {
            Console.WriteLine("Nombre:" + Nombre);
            Console.WriteLine("Edad:" + Edad);
        }
    }

    public class Empleado : Persona
    {
        protected float sueldo;

        public float Sueldo
        {
            set
            {
                sueldo = value;
            }
            get
            {
                return sueldo;
            }
        }

        new public void Imprimir()
        {
            base.Imprimir();
            Console.WriteLine("Sueldo:" + Sueldo);
        }
    }

    class Prueba
    {
        static void Main(string[] args)
        {
            Persona persona1 = new Persona();
            persona1.Nombre = "Juan";
            persona1.Edad = 25;
            Console.WriteLine("Los datos de la persona son:");
            persona1.Imprimir();

            Empleado empleado1 = new Empleado();
            empleado1.Nombre = "Ana";
            empleado1.Edad=42;
            empleado1.Sueldo = 2524;
            Console.WriteLine("Los dats del empleado son:");
            empleado1.Imprimir();

            Console.ReadKey();
        }
    }
}

La clase Persona define los atributos protegidos (protected) nombre y edad. Luego las propiedades públicas Nombre y Edad (que acceden a los atributos para modificarlos o consultar sus valores.


El método imprimir es público para que se lo pueda llamar desde donde definimos un objeto de esta clase.

    public class Persona
    {
        protected string nombre;
        protected int edad;

        public string Nombre
        {
            set
            {
                nombre = value;
            }
            get
            {
                return nombre;
            }
        }

        public int Edad
        {
            set
            {
                edad = value;
            }
            get
            {
                return edad;
            }
        }

        public void Imprimir()
        {
            Console.WriteLine("Nombre:" + Nombre);
            Console.WriteLine("Edad:" + Edad);
        }
    }

La clase Empleado hereda de la clase Persona y agrega un atributo llamado sueldo y la respectiva propiedad Sueldo para acceder al atributo. Como la clase Empleado define otro método Imprimir debemos anteceder la palabla clave new (con esto indicamos que sobreescribimos el método existente en la clase padre.

Para llamar desde el método imprimir de la clase Empleado al método imprimir de la clase Persona es con la sintaxis base.Imprimir()

    public class Empleado : Persona
    {
        protected float sueldo;

        public float Sueldo
        {
            set
            {
                sueldo = value;
            }
            get
            {
                return sueldo;
            }
        }

        new public void Imprimir()
        {
            base.Imprimir();
            Console.WriteLine("Sueldo:" + Sueldo);
        }
    }

Por último en la clase Prueba creamos un objeto de la clase Persona y un objeto de la clase Empleado:


    class Prueba
    {
        static void Main(string[] args)
        {
            Persona persona1 = new Persona();
            persona1.Nombre = "Juan";
            persona1.Edad = 25;
            Console.WriteLine("Los datos de la persona son:");
            persona1.Imprimir();

            Empleado empleado1 = new Empleado();
            empleado1.Nombre = "Ana";
            empleado1.Edad=42;
            empleado1.Sueldo = 2524;
            Console.WriteLine("Los dats del empleado son:");
            empleado1.Imprimir();

            Console.ReadKey();
        }
    }

26 Concepto de propiedad

La mayoría de los lenguajes de programación a objetos acceden a sus atributos a través de métodos. Esto lo vimos en el concepto anterior cuando accedíamos al atributo monto de un cliente:

        public void Depositar(int m)
        {
            monto = monto + m;
        }

        public int RetornarMonto()
        {
            return monto;
        }

Vimos que luego llamamos a dichos métodos con la sintaxis:

    cliente3.Depositar(200);
    cliente3.Extraer(150);

En C# normalmente este tipo de problemas se lo resuelve implementado una propiedad. Veamos el mismo problemas resolviéndolo utilizando propiedades.

Problema 1:

El problema era : Un banco tiene 3 clientes que pueden hacer depósitos y extracciones. También el banco requiere que al final del día calcule la cantidad de dinero depositada.

Programa:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Propiedades1
{
    class Cliente
    {
        private string nombre;
        private int monto;

        public string Nombre
        {
            set
            {
                nombre = value;
            }
            get
            {
                return nombre;
            }
        }

        public int Monto
        {
            set
            {
                monto = value;
            }
            get
            {
                return monto;
            }
        }

        public void Imprimir()
        {
            Console.WriteLine(Nombre + " tiene depositado la suma de " + Monto);
        }
    }

    class Banco
    {
        private Cliente cliente1, cliente2, cliente3;

        public Banco()
        {
            cliente1 = new Cliente();
            cliente1.Nombre = "Juan";
            cliente1.Monto = 0;
            cliente2 = new Cliente();
            cliente2.Nombre = "Ana";
            cliente2.Monto = 0;
            cliente3 = new Cliente();
            cliente3.Nombre = "Pedro";
            cliente3.Monto = 0;
        }

        public void Operar()
        {
            cliente1.Monto = cliente1.Monto + 100;
            cliente2.Monto = cliente2.Monto + 150;
            cliente3.Monto = cliente3.Monto + 200;
        }

        public void DepositosTotales()
        {
            int t = cliente1.Monto + cliente2.Monto + cliente3.Monto;
            Console.WriteLine("El total de dinero en el banco es:" + t);
            cliente1.Imprimir();
            cliente2.Imprimir();
            cliente3.Imprimir();
        }

        static void Main(string[] args)
        {
            Banco banco1 = new Banco();
            banco1.Operar();
            banco1.DepositosTotales();
            Console.ReadKey();
        }
    }
}


Lo más importante es entender que una propiedad es una forma de acceder al contenido de un atributo, tanto para consultar su como modificarlo.

        private string nombre;
        private int monto;

        public string Nombre
        {
            set
            {
                nombre = value;
            }
            get
            {
                return nombre;
            }
        }

        public int Monto
        {
            set
            {
                monto = value;
            }
            get
            {
                return monto;
            }
        }

La propiedad Nombre mediante el modificador set inicializa el atributo nombre con el valor que llega del objeto:
            cliente1.Nombre = "Juan";

Como vemos donde definimos el objeto cliente1 accedemos a la propiedad mediante el punto y le asignamos un valor (en este caso un string porque la propiedad es de tipo string)
Si queremos consultar el atributo nombre lo podemos hacer mediante la propiedad Nombre. Es común definir el nombre que le damos a la propiedad con el mismo nombre que tiene el atributo pero con el primer caracter en mayúsculas:

        //atributo en minúsculas 
        private int monto;
        //nombre de la propiedad con el mismo nombre pero en mayúsculas. 
        public int Monto
        {
            set
            {
                monto = value;
            }
            get
            {
                return monto;
            }
        }

Podemos observar que la sintaxis para acceder a las propiedades donde definimos objetos es intuitiva y sencillas, por ejemplo para saber cuanto dinero hay en el banco la sintaxis con propiedades es:

            int t = cliente1.Monto + cliente2.Monto + cliente3.Monto;
Y como la vimos anteriormente de un método que retorna el monto tenemos la siguiente sintaxis:
int t = cliente1.RetornarMonto () + 
        cliente2.RetornarMonto () + 
        cliente3.RetornarMonto ();

Lo primero que nos viene a la mente es porque no definir los atributos con el modificador public:

        public int monto;

Para luego poder consultarlos y/o modificarlos con la sintaxis:

            int t = cliente1.monto + cliente2.monto + cliente3.monto;

Ahora veamos que cuando consultamos o inicializamos una propiedad en realidad lo que está sucediendo es la ejecución de un método (set o get) donde podemos disponer código donde validar el valor asignado. Por ejemplo si disponemos la restricción que el Monto siempre debe ser positivo para que se almacene, luego debemos codificar la propiedad con la siguiente sintaxis:

        public int Monto
        {
            set
            {
                if (value >= 0)
                {
                    monto = value;
                }
                else
                {
                    Console.WriteLine("No se puede tener un monto negativo.");
                }
            }
            get
            {
                return monto;
            }
        }

Es decir si el valor que le asignamos a la propiedad Monto es negativo luego no se inicializa el atributo monto con dicho valor.

Si ejecutamos este código luego debe mostrar un mensaje indicando que "No se puede tener monto negativo":

 cliente1.Monto = -100;


Problema 2:

Plantear un programa que permita jugar a los dados. Las reglas de juego son: se tiran tres dados si los tres salen con el mismo valor mostrar un mensaje que "gano", sino "perdió".

Programa:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Propiedades2
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    namespace Colaboracion2
    {
        class Dado
        {
            private int valor;

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

            private static Random aleatorio;

            public Dado()
            {
                aleatorio = new Random();
            }

            public void Tirar()
            {
                Valor = aleatorio.Next(1, 7);
            }

            public void Imprimir()
            {
                Console.WriteLine("El valor del dado es:" + Valor);
            }
        }

        class JuegoDeDados
        {
            private Dado dado1, dado2, dado3;

            public JuegoDeDados()
            {
                dado1 = new Dado();
                dado2 = new Dado();
                dado3 = new Dado();
            }

            public void Jugar()
            {
                dado1.Tirar();
                dado1.Imprimir();
                dado2.Tirar();
                dado2.Imprimir();
                dado3.Tirar();
                dado3.Imprimir();
                if (dado1.Valor == dado2.Valor && dado1.Valor == dado3.Valor)
                {
                    Console.WriteLine("Ganó");
                }
                else
                {
                    Console.WriteLine("Perdió");
                }
                Console.ReadKey();
            }

            static void Main(string[] args)
            {
                JuegoDeDados j = new JuegoDeDados();
                j.Jugar();
            }
        }
    }
}

El atributo valor se lo accede por medio de la propiedad Valor:

            private int valor;

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

Luego cuando queremos consultar el valor del dado desde el jugo de dados por medio de la sintaxis siguiente podemos comparar si los tres dados tienen el mismo número:

                if (dado1.Valor == dado2.Valor && dado1.Valor == dado3.Valor)
                {
                    Console.WriteLine("Ganó");
                }
                else
                {
                    Console.WriteLine("Perdió");
                }

Algo importante es poder restringir la ejecución del set o get desde fuera de la clase, por ejemplo en este caso queremos evitar que desde la clase JuegoDeDados se puede cambiar el valor del dado con la siguiente sintaxis:

    dado1.Valor=7;

La línea anterior provocará un error ya que sección del set de la 
propiedad la hemos definido de tipo private (con esto hacemos que solo 
los métodos de la clase puedan ejecuta el set. La sintaxis para acceder a
 la propiedad Valor desde la clase es:

            public void Tirar()
            {
                Valor = aleatorio.Next(1, 7);
            }

Esto es correcto ya que el método Tirar pertenece a la clase Dado y por lo tanto puede asignarle un valor a la propiedad Valor (cuando se asigna un valor a una propiedad se ejecuta el set)


Problemas propuestos


  1. Plantear una clase Club y otra clase Socio.
    La clase Socio debe tener los siguientes atributos privados: nombre y la antigüedad en el club (en años) Definir dos propiedades para poder acceder al nombre y la antigüedad del socio(no permitir cargar un valor negativo en la antigüedad). La clase Club debe tener como atributos 3 objetos de la clase Socio. Definir una responsabilidad para imprimir el nombre del socio con mayor antigüedad en el club. 

25 Colaboración de clases

Normalmente un problema resuelto con la metodología de programación orientada a objetos no interviene una sola clase, sino muchas clases que interactúan y se comunican.
Plantearemos un problema separando las actividades en dos .

Problema 1:

Un banco tiene 3 clientes que pueden hacer depósitos y extracciones. También el banco requiere que al final del día calcule la cantidad de dinero que hay depositada.
Lo primero que hacemos es identificar las clases:
Podemos identificar la clase y la clase Banco.
Luego debemos definir los y los métodos de cada clase:

Cliente  
    atributos
        nombre
        monto
    métodos
        constructor
        Depositar
        Extraer
        RetornarMonto

Banco
    atributos
        3 Cliente (3 objetos de la clase Cliente)
    métodos
        constructor
        Operar
        DepositosTotales

Creamos un proyecto llamado: Colaboracion1 y dentro del proyecto creamos dos clases llamadas: Cliente y Banco.

Programa:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Colaboracion1
{
    class Cliente
    {
        private string nombre;
        private int monto;

        public Cliente(string nom)
        {
            nombre = nom;
            monto = 0;
        }

        public void Depositar(int m)
        {
            monto = monto + m;
        }

        public void Extraer(int m)
        {
            monto = monto - m;
        }

        public int RetornarMonto()
        {
            return monto;
        }

        public void Imprimir()
        {
            Console.WriteLine(nombre+" tiene  la suma de "+monto);
        }
    }

    class Banco
    {
        private Cliente cliente1, cliente2, cliente3;

        public Banco() 
        {
            cliente1=new Cliente("Juan");
            cliente2=new Cliente("Ana");
            cliente3=new Cliente("Pedro"); 
        }

        public void Operar()
        {
            cliente1.Depositar(100);
            cliente2.Depositar(150);
            cliente3.Depositar(200);
            cliente3.Extraer(150);
        }

        public void DepositosTotales()
        {
            int t = cliente1.RetornarMonto () + 
                    cliente2.RetornarMonto () + 
                    cliente3.RetornarMonto ();
            Console.WriteLine ("El total de dinero en el banco es:" + t);
            cliente1.Imprimir();
            cliente2.Imprimir();
            cliente3.Imprimir();
        }

        static void Main(string[] args)
        {
            Banco banco1 = new Banco();
            banco1.Operar();
            banco1.DepositosTotales();
            Console.ReadKey();
        }
    }
}

Analicemos la implementación del problema.


Los atributos de una clase normalmente son privados para que no se tenga acceso directamente desde otra clase, los atributos son modificados por los métodos de la misma clase:

        private string nombre;
        private int monto;

El constructor recibe como parámetro el nombre del cliente y lo almacena en el atributo respectivo e inicializa el atributo monto en cero:

        public Cliente(string nom)
        {
            nombre = nom;
            monto = 0;
        }
Los métodos Depositar y Extraer actualizan el atributo monto con el dinero que llega como parámetro (para simplificar el problema no hemos validado que cuando se extrae dinero el atributo monto quede con un negativo):

        public void Depositar(int m)
        {
            monto = monto + m;
        }

        public void Extraer(int m)
        {
            monto = monto - m;
        }

El método RetornarMonto tiene por objetivo comunicar al Banco la cantidad de dinero que tiene el cliente (recordemos que como el atributo monto es privado de la clase, debemos tener un método que lo retorne):

        public int RetornarMonto()
        {
            return monto;
        }

Por último el método imprimir muestra nombre y el monto de dinero del cliente:

        public void Imprimir()
        {
            Console.WriteLine(nombre+" tiene depositado la suma de "+monto);
        }

Como podemos observar la clase Cliente no tiene función Main. Entonces donde definimos objetos de la clase Cliente?
La respuesta a esta pregunta es que en la clase Banco definimos tres objetos de la clase Cliente.
Veamos ahora la clase Banco que requiere la colaboración de la clase Cliente.
Primero definimos tres atributos de tipo Cliente:

    class Banco
    {
        private Cliente cliente1, cliente2, cliente3;

En le constructor creamos los tres objetos (cada vez que creamos un objeto de la clase Cliente debemos pasar a su constructor el nombre del cliente, recordemos que su monto de depósito se inicializa con cero):

        public Banco() 
        {
            cliente1=new Cliente("Juan");
            cliente2=new Cliente("Ana");
            cliente3=new Cliente("Pedro"); 
        }

El método operar del banco (llamamos a los métodos Depositar y Extraer de los clientes):

        public void Operar()
        {
            cliente1.Depositar(100);
            cliente2.Depositar(150);
            cliente3.Depositar(200);
            cliente3.Extraer(150);
        }

El método DepositosTotales obtiene el monto depositado de cada uno de los tres clientes, procede a mostrarlos y llama al método imprimir de cada cliente para poder mostrar el nombre y depósito:

        public void DepositosTotales()
        {
            int t = cliente1.RetornarMonto () + 
                    cliente2.RetornarMonto () + 
                    cliente3.RetornarMonto ();
            Console.WriteLine ("El total de dinero en el banco es:" + t);
            cliente1.Imprimir();
            cliente2.Imprimir();
            cliente3.Imprimir();
        }

Por último en la Main definimos un objeto de la clase Banco (la clase Banco es la clase principal en nuestro problema):

        static void Main(string[] args)
        {
            Banco banco1 = new Banco();
            banco1.Operar();
            banco1.DepositosTotales();
            Console.ReadKey();
        }

Problema 2:

Plantear un programa que permita jugar a los dados. Las reglas de juego son: se tiran tres dados si los tres salen con el mismo valor mostrar un mensaje que "gano", sino "perdió".
Lo primero que hacemos es identificar las clases:
Podemos identificar la clase Dado y la clase JuegoDeDados.
Luego los atributos y los métodos de cada clase:

Dado  
    atributos
        valor
    métodos
        constructor
        Tirar
        Imprimir
        RetornarValor

JuegoDeDados
    atributos
        3 Dado (3 objetos de la clase Dado)
    métodos
        constructor
        Jugar

Creamos un proyecto llamado: Colaboracion2 y dentro del proyecto creamos dos clases llamadas: Dado y JuegoDeDados.

Programa:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Colaboracion2
{
    class Dado
    {
        private int valor;
        private static Random aleatorio;

        public Dado()
        {
            aleatorio = new Random();
        }

        public void Tirar()
        {
            valor = aleatorio.Next(1, 7);
        }

        public void Imprimir() 
        {
            Console.WriteLine("El valor del dado es:"+valor);
        }

        public int RetornarValor()
        {
            return valor;
        }
    }

    class JuegoDeDados
    {
        private Dado dado1,dado2,dado3;
    
        public JuegoDeDados() 
        {
            dado1=new Dado();
            dado2=new Dado();
            dado3=new Dado();         
        }
    
        public void Jugar() 
        {
            dado1.Tirar();
            dado1.Imprimir();
            dado2.Tirar();
            dado2.Imprimir();
            dado3.Tirar();
            dado3.Imprimir();
            if (dado1.RetornarValor()==dado2.RetornarValor() && 
                dado1.RetornarValor()==dado3.RetornarValor()) 
            {
                Console.WriteLine("Ganó");
            }
            else
            {
                Console.WriteLine("Perdió");
            }
            Console.ReadKey();
        }
    
        static void Main(string[] args)
        {
            JuegoDeDados j = new JuegoDeDados();
            j.Jugar();
        }
    }
}

La clase Dado define el atributo "valor" donde almacenamos un valor aleatorio que representa el número que sale al tirarlo.

Definimos otro atributo de la clase Random. Esta clase nos facilita la generación de un número aleatorio que nos indicará el valor del dato. Como luego se crearán tres objetos de la clase dado y nosotros solo requerimos un objeto de la clase Random luego definimos el atributo de tipo static, con esto todos los objetos de la clase Dado acceden al mismo objeto de la clase Random:


        private int valor;
        private static Random aleatorio;

En el constructor creamos el objeto de la clase Random:

        public Dado()
        {
            aleatorio = new Random();
        }

El método Tirar almacena el valor aleatorio (para generar un valor aleatorio utilizamos el método Next de la clase Random, el mismo genera un valor entero comprendido entre los dos parámetros que le pasamos (nunca genera la cota superior):

        public void Tirar()
        {
            valor = aleatorio.Next(1, 7);
        }

El método Imprimir de la clase Dado muestra por pantalla el valor del dado:

        public void Imprimir() 
        {
            Console.WriteLine("El valor del dado es:"+valor);
        }

Por último el método que retorna el valor del dado (se utiliza en la otra clase para ver si los tres dados generaron el mismo valor):

        public int RetornarValor()
        {
            return valor;
        }

La clase JuegoDeDatos define tres atributos de la clase Dado (con esto decimos que la clase Dado colabora con la clase JuegoDeDados):

    class JuegoDeDados
    {
        private Dado dado1,dado2,dado3;

En el constructor procedemos a crear los tres objetos de la clase Dado:

        public JuegoDeDados() 
        {
            dado1=new Dado();
            dado2=new Dado();
            dado3=new Dado();         
        }

En el método Jugar llamamos al método Tirar de cada dado, pedimos que se imprima el valor generado y finalmente procedemos a verificar si se ganó o no:

        public void Jugar() 
        {
            dado1.Tirar();
            dado1.Imprimir();
            dado2.Tirar();
            dado2.Imprimir();
            dado3.Tirar();
            dado3.Imprimir();
            if (dado1.RetornarValor()==dado2.RetornarValor() && 
                dado1.RetornarValor()==dado3.RetornarValor()) 
            {
                Console.WriteLine("Ganó");
            }
            else
            {
                Console.WriteLine("Perdió");
            }
            Console.ReadKey();
        }

En la Main creamos solo un objeto de la clase principal (en este caso la clase principal es el JuegoDeDados):

        static void Main(string[] args)
        {
            JuegoDeDados j = new JuegoDeDados();
            j.Jugar();
        }

Problemas propuestos


  1. Plantear una clase Club y otra clase Socio.
    La clase Socio debe tener los siguientes atributos privados: nombre y la antigüedad en el club (en años). En el constructor pedir la carga del nombre y su antigüedad. La clase Club debe tener como atributos 3 objetos de la clase Socio. Definir una responsabilidad para imprimir el nombre del socio con mayor antigüedad en el club.