Indizadores
Un indizador es un concepto más o menos
nuevo. Se trata de una simplificación de un objeto que es en realidad un array
o una colección.
A modo de ejemplo se muestra un caso
concreto. Un libro no es más que un objeto que contiene una serie de capítulos,
para construir el objeto Libro con una colección Capítulos dentro de él, en la
que sea posible añadir o modificar capítulos, de modo que necesita un método
Add y un método Modify y luego llamar a estos métodos desde el código cliente:
static void Main()
{
Libro miLibro=new Libro();
miLibro.Capitulos.Add("Las tres crisis");
miLibro.Capitulos.Add("Cosmos");
miLibro.Capitulos.Modify("El mundo y sus demonios",1);
...
}
Un indizador, permite tratar al objeto
como un array o una colección, haciendo la codificación más intuitiva. El
código equivalente al anterior sería así:
static void Main()
{
Libro miLibro=new Libro();
miLibro[0]=" Los dragones del edén ";
miLibro[1]="Cosmos";
miLibro[1]=" El mundo y sus demonios";
...
}
Este código es más natural: pues el objeto
Libro no es más que un conjunto de capítulos, y conviene tratarlo como un
array, independientemente de que el objeto permita ofrecer también otra serie
de propiedades, métodos y demás. Esta es su sintaxis
class Libro
{
public object this[int index]
{
get
{
...
}
set
{
...
}
}
...
}
El indizador se construye de un modo muy
similar a las propiedades, pero el
nombre de esta "propiedad" es el propio objeto, es decir, this, el
tipo, lógicamente, es object y, además, requiere un argumento entre corchetes,
que sería el índice del elemento al que queremos acceder. El ejemplo del libro
completo:
class Libro
{
private ArrayList
capitulos=new ArrayList();
public object this[int indice]
{
get
{
if (indice >=
capitulos.Count || indice < 0)
return null;
else
return capitulos[indice];
}
set
{
if (indice >= 0
&& indice < capitulos.Count)
capitulos[indice]=value;
else if (indice ==
capitulos.Count)
capitulos.Add(value);
else
throw new Exception("No
se puede asignar a este elemento");
}
}
public int NumCapitulos
{
get
{
Return capítulos.Count;
}
}
class IndizadorLibros
{
static void Main()
{
Libro
miLibro=new Libro();
miLibro[0]="EGB Un tereno
y grácil bucle";
miLibro[1]="Yo soy un
extraño bucle";
miLibro[1]="La nueva mente
del emperador";
for (int i=0;i<miLibro.NumCapitulos;i++)
Console.WriteLine("Capitulo
{0}: {1}",i+1,miLibro[i]);
string a=Console.ReadLine();
}
}
}
La propiedad NumCapitulos devuelve el
número de capítulos que hay incluidos en el objeto Libro hasta este momento. Un
array tiene ya la propiedad Length que devuelve exactamente lo mismo, y una
colección tiene ya la propiedad Count, que hace también lo mismo. La razón de
crear esta propiedad es porque Libro es una clase, no una colección ni un
array. Al implementar un indizador, se puede acceder a los elementos de los
objetos de esta clase como si fuera un array, pero el compilador no genera una
propiedad Count o Length. Por este motivo, si deseamos una propiedad que
ofrezca el número de elementos contenidos en el objeto será necesario implementarla
nosotros mismos. En este caso se llama NumCapitulos.
También se ha declarado el campo privado
"capitulos" del tipo System.Collections.ArrayList. Debido a que es
necesario meter los capítulos en algún array o en alguna colección (en este
caso, se trata de una colección).
El hecho de implementar un indizador no
convierte a una clase en una colección o en un array, pero ofrece una interfaz
similar a estos. En definitiva, el indizador hace que podamos
"encubrir" dicha colección en aras de obtener una lógica más natural
y manejable.
Sobrecarga de operadores
Sobrecargar un operador consiste en
modificar su comportamiento cuando este se utiliza con una determinada clase.
Es perfectamente factible sumar dos
números de tipo int, o dos de tipo short, e incluso se pueden sumar dos números
de tipos distintos. Pero, si en una variable hay almacenadas cantidades en
metros y en otra variables están almacenadas en centímetros ¿Cómo las sumo? El resultado de sumarlas directamente sería
incorrecto.
double m=10;
double c=10;
double SumaMetros=m+c;
double SumaCentimetros=m+c;
Console.WriteLine(SumaMetros);
Console.WriteLine(SumaCentimetros);
El ordenador no sabe nada de metros ni
centímetros, por lo que el resultado sería 20 en ambos casos. Y por supuesto, es
incorrecto, porque 10 metros más 10 centímetros serán 10.1 metros, o bien 1010
centímetros, pero en ningún caso será el 20. Una posibilidad de resolver este problema sería crear una
clase metros con un método que se llamara SumarCentimetros, para que hiciera la
suma correcta. Pero el uso de esta clase sería un poco forzado, pues para sumar
utilizaremos el operador + en lugar de
tener que invocar un método específico.
Aquí es donde resulta útil la sobrecarga
de operadores. Si es posible sobrecargar
el operador + en la clase metros para que haga una suma correcta independientemente
de las unidades que se le sumen, se habrá resuelto el problema. Ejemplo.
Metros m=new Metros(10);
Centimetros c=new Centimetros(10);
Metros SumaMetros=m+c;
Centimetros SumaCentimetros=c+m;
Console.WriteLine(SumaMetros.Cantidad);
Console.WriteLine(SumaCentimetros.Cantidad);
Y el resultado será correcto: 10.1 metros
y 1010 centímetros, sin necesidad de convertir previamente los centímetros a
metros y viceversa. La sintaxis de un operador + sobrecargado es la siguiente:
[acceso] static NombreClase operator+(Tipo a[, Tipo b])
{
...
}
El modificador de acceso en caso de que
proceda, con la precaución de que el modificador de acceso de un operador
sobrecargado no puede ser de un ámbito mayor que el de la propia clase, es
decir, si la clase es internal, el operador sobrecargado no puede ser public,
puesto que su nivel de acceso sería mayor que el de la clase que lo contiene.
Después la palabra static. Lógicamente, tiene que ser static puesto que el
operador está sobrecargado para todos los objetos de la clase, y no para una
sola instancia en particular. Después el nombre de la clase, lo cual implica
que hay que retornar un objeto de esta clase. Después la palabra
"operator" seguida del operador que deseamos sobrecargar (deben ir
juntos sin separarlos con ningún espacio en blanco) y, a continuación, la lista
de argumentos entre paréntesis. Si el operador a sobrecargar es unitario,
necesitamos un único argumento, pero si es binario necesitaremos dos. Ejemplo:
public class Metros
{
private double cantidad=0;
public Metros() {}
public Metros(double cant)
{
this.cantidad=cant;
}
public double Cantidad
{
get
{
return this.cantidad;
}
set
{
this.cantidad=value;
}
}
public static Metros operator+(Metros m, Centimetros
c)
{
Metros retValue=new Metros();
retValue.Cantidad=m.Cantidad+c.Cantidad/100;
return retValue;
}
}
La sobrecarga del operador está definid abajo
y en negrita. Esto es un método que se ejecutará cuando el compilador se tope
con una suma de dos objetos, el primero de la clase Metros y el segundo de la
clase Centímetros. Por ejemplo, si "m" es un objeto de la clase
Metros, y "c" un objeto de la clase Centimetros, la línea:
Metros SumaMetros=m+c;
Haría que se ejecute el método anterior.
Es decir, se crea un nuevo objeto de la clase Metros (el objeto retValue). En
su propiedad Cantidad se suman lo que valgan las propiedades Cantidad del
argumento m y la centésima parte de la propiedad Cantidad del argumento c,
retornando al final el objeto con la suma hecha, objeto que se asignaría, por
lo tanto, a SumaMetros. Es decir, al haber sobrecargado el operador + en la
clase Metros, cuando el compilador se encuentra con la suma, hace lo que
hayamos implementado en el método, en lugar de la suma normal. Dicho de otro
modo, se modificado el comportamiento del operador +.
Si nos encontramos con una resta en lugar
de una suma
Metros SumaMetros=m-c;
El compilador dará un error, ya que el operador - no está sobrecargado y,
por lo tanto, es incapaz de efectuar la operación. Por lo tanto, para que se
pudiera efectuar la resta, hay que sobrecargar
también el operador -:
public static Metros operator-(Metros m, Centimetros
c)
{
Metros retValue=new Metros();
retValue.Cantidad=m.Cantidad-c.Cantidad/100;
return retValue;
}
Lo mismo se puede hacer con los operadores
* y /, para que también se hicieran correctamente las multiplicaciones y
divisiones.
Si el compilador si se encuentra con
esta línea:
Centimetros SumaCentimetros=c+m;
Volvería a dar error, pues la sobrecarga
en la clase Metros afecta a las sumas cuando el primer operando es de la clase
Metros y el segundo es de la clase Centímetros. Si añadimos otra sobrecarga del
operador + en la clase Metros en la que los centímetros vayan en el primer
argumento y los metros en el segundo. Seguiría dando error, pues
SumaCentimetros no es un objeto de la clase Metros, sino de la clase
Centimetros. Lo más adecuado sería sobrecargar el operador de ese modo en la
clase Centimetros y no en la clase Metros.
El operador “=” no se puede sobrecargar,
por lo que para comparar directamente centímetros con metros hay que usar las
conversiones definidas.
Estos son los operadores que se permite
sobrecargar en C#:
Unitarios: +. -, !, ~, ++, --,
true, false
Binarios: +, -, *, /, %, &, |,
^,<<, >>, ==, !=, >, <, >=, <=
No hay comentarios:
Publicar un comentario