74 - Indizadores


Los indizadores permiten acceder mediante subíndices a un objeto de una clase que lo implementa. El objetivo es facilitar la implementación de un algoritmo.

El indizador evita que implementemos una serie de métodos para administrar el objeto.

La sintaxis de un indizador es:

tipo this[tipo indice]
{
    get
    {
         valor que retorna según indice
    }
    set
    {
        fijar el valor según indice
    }
}

Lo más sencillo es la implementación del siguiente programa:

using System.Linq;
using System.Text;

namespace Indizador1
{
    class Program
    {
        class A
        {
            private int []vec;
            public A()
            {
                vec=new int[3];
            }

            public int this[int indice]
            {
                set
                {
                    vec[indice] = value;
                }
                get
                {
                    return vec[indice];
                }
            }
        }

        static void Main(string[] args)
        {
            A obj1 = new A();
            obj1[0] = 7;
            obj1[1] = 34;
            obj1[2] = 45;
            Console.WriteLine(obj1[0]);
            Console.WriteLine(obj1[1]);
            Console.WriteLine(obj1[2]);
            Console.ReadKey();
        }
    }
}

Definimos y creamos un vector de enteros de 3 elementos:

            private int []vec;
            public A()
            {
                vec=new int[3];
            }

Definimos un indizador con parámetro entero llamado indice(veremos más adelante que el parámetro puede ser de otro tipo o inclusive tener más de uno) Utilizamos la palabra this para indicar que es un indizador.

En la seccion del set se ejecuta cuando le asignamos un valor donde definimos un objeto de dicha clase (obj1[0] = 7;) es cero lo recibe el parámetro indice y el 7 lo almacena la palabra clave value. Por último la sección del get se ejecuta cuando lo accedemos (Console.WriteLine(obj1[0]);

            public int this[int indice]
            {
                set
                {
                    vec[indice] = value;
                }
                get
                {
                    return vec[indice];
                }
            }

Para crear un objeto de la clase A lo hacemos como ya conocemos:

            A obj1 = new A();

Lo nuevo es cuando queremos utilizar el indizador para inicializar datos (ejecuta el set del indizador):

            obj1[0] = 7;
            obj1[1] = 34;
            obj1[2] = 45;

O cuando queremos acceder al indizador para consultas (ejecuta el get):

            Console.WriteLine(obj1[0]);
            Console.WriteLine(obj1[1]);
            Console.WriteLine(obj1[2]);
            Console.ReadKey();

Problema 1:

Confeccionar una clase llamada Cliente definir las propiedades Nombre y Dinero (en el constructor inicializar dichas propiedades)
Desarrollar otra clase llamada Banco donde debemos definir y crear un vector de tres elementos de tipo Cliente.
Implementar un indizador en la clase Banco que permita acceder a cada cliente por un subíndice entero.

Programa:


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

namespace Indizador2
{
    class Cliente
    {
        private string nombre;
        public string Nombre
        {
            set
            {
                nombre = value;
            }
            get
            {
                return nombre;
            }
        }
        private int dinero;
        public int Dinero
        {
            set
            {
                dinero = value;
            }
            get
            {
                return dinero;
            }
        }
        public Cliente(string nom, int din)
        {
            Nombre = nom;
            Dinero = din;
        }
    }

    class Banco
    {
        private Cliente[] clientes;
        public Banco()
        {
            clientes = new Cliente[3];
        }

        public Cliente this[int indice]
        {
            set 
            {
                clientes[indice] = value;
            }
            get
            {
                return clientes[indice];
            }
        }
    }


    class Program
    {
        static void Main(string[] args)
        {
            Banco banco1 = new Banco();
            Cliente cli1 = new Cliente("juan", 1000);
            Cliente cli2 = new Cliente("ana", 2000);
            Cliente cli3 = new Cliente("luis", 1500);
            banco1[0] = cli1;
            banco1[1] = cli2;
            banco1[2] = cli3;
            Console.WriteLine("Datos de los clientes.");
            Console.WriteLine(banco1[0].Nombre);
            Console.WriteLine(banco1[0].Dinero);
            Console.WriteLine();
            Console.WriteLine(banco1[1].Nombre);
            Console.WriteLine(banco1[1].Dinero);
            Console.WriteLine();
            Console.WriteLine(banco1[2].Nombre);
            Console.WriteLine(banco1[2].Dinero);
            Console.WriteLine();
            Console.ReadKey();
        }
    }
}

La clase Banco implementa el indizador:

        public Cliente this[int indice]
        {
            set 
            {
                clientes[indice] = value;
            }
            get
            {
                return clientes[indice];
            }
        }

Para ver las ventajas que presenta implementar un indizador en la clase Banco podemos ver como se inicializan los tres clientes que tiene el banco:


            Banco banco1 = new Banco();
            Cliente cli1 = new Cliente("juan", 1000);
            Cliente cli2 = new Cliente("ana", 2000);
            Cliente cli3 = new Cliente("luis", 1500);
            banco1[0] = cli1;
            banco1[1] = cli2;
            banco1[2] = cli3;

Como podemos ver le asignamos al objeto indicando un subíndice la referencia del objeto de la clase Cliente.

Para poder imprimir el nombre y dinero depositado que tiene cada cliente lo hacemos accediendo al subíndice y seguidamente la propiedad del cliente que necesitamos mostrar:

            Console.WriteLine("Datos de los clientes.");
            Console.WriteLine(banco1[0].Nombre);
            Console.WriteLine(banco1[0].Dinero);
            Console.WriteLine();
            Console.WriteLine(banco1[1].Nombre);
            Console.WriteLine(banco1[1].Dinero);
            Console.WriteLine();
            Console.WriteLine(banco1[2].Nombre);
            Console.WriteLine(banco1[2].Dinero);
            Console.WriteLine();

Un indizador puede tener más de un subíndice, la estructura luego debe ser:

tipo this[tipo indice1,tipo indice2]
{
    get
    {
         valor que retorna según indices
    }
    set
    {
        fijar el valor según indices
    }
}

Problema 2:

Implementar una clase llamada Tablero del juego de la Batalla Naval. Permitir mediante un indizador de dos dimensiones acceder a las casillas del tablero.

Programa:

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

namespace Indizador3
{
    public enum DatoCasilla { agua, barco };
    
    class Tablero
    {
        private DatoCasilla [,] mat;

        public Tablero()
        {
            mat = new DatoCasilla[10, 10];
        }

        public void Graficar()
        {
            for (int f = 0; f < mat.GetLength(0); f++)
            {
                for (int c = 0; c < mat.GetLength(1); c++)
                {
                    if (mat[f, c] == DatoCasilla.agua)
                    {
                        Console.Write("0");
                    }
                    if (mat[f, c] == DatoCasilla.barco)
                    {
                        Console.Write("-");
                    }
                }
                Console.WriteLine();
            }
        }

        public DatoCasilla this[int fila, int columna]
        {
            set
            {
                mat[fila, columna] = value;
            }
            get
            {
                return mat[fila, columna];
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Tablero tablero1 = new Tablero();
            tablero1[0, 0] = DatoCasilla.barco;
            tablero1[0, 1] = DatoCasilla.barco;
            tablero1[0, 2] = DatoCasilla.barco;
            tablero1[0, 9] = DatoCasilla.barco;
            tablero1[1, 9] = DatoCasilla.barco;
            tablero1[2, 9] = DatoCasilla.barco;
            tablero1.Graficar();
            Console.WriteLine();
            if (tablero1[0, 0] == DatoCasilla.barco)
            {
                Console.WriteLine("Hay un barco en esta casilla");
            }
            else
            {
                if (tablero1[0, 0] == DatoCasilla.agua)
                {
                    Console.WriteLine("Agua");
                }
            }
            Console.ReadKey();
        }
    }
}

Declaramos un tipo de dato enumerado con dos valores posibles:

    public enum DatoCasilla { agua, barco };

Declaramos en la clase Tablero una matriz regular con componentes de tipo DatoCasilla:

        private DatoCasilla [,] mat;

En el constructor creamos la matriz de 10 filas por 10 columnas:

        public Tablero()
        {
            mat = new DatoCasilla[10, 10];
        }

Imprimimos el tablero, si en la componente hay "agua" mostramos un cero y si hay "barco" mostramos un guión (cuando se creó la matriz en el constructor se inicializan todas las componentes con el valor 0 que es "agua"):


        public void Graficar()
        {
            for (int f = 0; f < mat.GetLength(0); f++)
            {
                for (int c = 0; c < mat.GetLength(1); c++)
                {
                    if (mat[f, c] == DatoCasilla.agua)
                    {
                        Console.Write("0");
                    }
                    if (mat[f, c] == DatoCasilla.barco)
                    {
                        Console.Write("-");
                    }
                }
                Console.WriteLine();
            }
        }

Ahora veamos lo más importante que es la definición del indizador con dos subíndices:


        public DatoCasilla this[int fila, int columna]
        {
            set
            {
                mat[fila, columna] = value;
            }
            get
            {
                return mat[fila, columna];
            }
        }

En la otra clase creamos un objeto de la clase Tablero:


    class Program
    {
        static void Main(string[] args)
        {
            Tablero tablero1 = new Tablero();

Seguidamente disponemos un barco en la primer fila de la matriz utilizando un indizador (estas tres líneas acceden al set del indizador pasando los dos subíndices y el valor a almacenar):


            tablero1[0, 0] = DatoCasilla.barco;
            tablero1[0, 1] = DatoCasilla.barco;
            tablero1[0, 2] = DatoCasilla.barco;

Un segundo barco lo ubicamos en la última columna:


            tablero1[0, 9] = DatoCasilla.barco;
            tablero1[1, 9] = DatoCasilla.barco;
            tablero1[2, 9] = DatoCasilla.barco;

Para saber que hay en una casilla cualquiera del tablero podemos utilizar el indizador (se ejecuta el get):


            if (tablero1[0, 0] == DatoCasilla.barco)
            {
                Console.WriteLine("Hay un barco en esta casilla");
            }
            else
            {
                if (tablero1[0, 0] == DatoCasilla.agua)
                {
                    Console.WriteLine("Agua");
                }
            }

Retornar