Sobrecarga de operadores en C#. Ejemplos

Abr 1, 2012 Codigos C# Tutoriales C# 4 comentarios

C#, como la mayoría de los lenguajes modernos permite la sobrecarga de operadores, o sea la capacidad de redefinir operadores para una clase. No todos los operadores pueden ser sobrecargados, además existen restricciones relativas a cuando pueden ser sobrecargados ciertos operadores, como la definición de == y != que debe hacerse conjuntamente.

En la página de MSDN pueden ver la lista de operadores en C# y su posibilidad de sobrecargarse.

Importancia de redefinir operadores

El hecho de que C# y otros lenguajes soporten la sobrecarga de operadores no es un capricho, a través de esta característica podemos realizar operaciones complejas a través de argumentos de funciones y nos permite escribir código mucho más instuitivo y legible. En realidad definir la sobrecarga de un operador es muy similar a la declaración y definición de una función o método cualquiera. Suponiendo que tenemos una clase Point, veamos el siguiente código:

        Point x = new Point (0,1);
Point y = new Point (1,0);
Point z1 = Point.Suma(x,y);   //A traves de un método Suma estático de la clase Point
Point z2 = x.Suma(y);          //A traves de un método Suma en la clase Point
Point z3 = x + y;                 //Redefiniendo el operador + en la clase Point

Seguramente estás de acuerdo conmigo en que la tercera forma es mucho más intuitiva que las otras, aunque puede que en este caso tan sencillo no se vea todavía la utilidad.

Como redefinir un operador

Para redefinir un operador en C#, se usa la siguiente sintaxis:

[modificadores*][type] operator [operator] (parámetros){()}

Los operadores unarios reciben un solo parámetro y los binarios dos en cada caso uno de los parámetros debe ser del tipo de la clase que los redefine.
*Los operadores redefinidos deben ser public static…

Por ejemplo, para redefinir el operador + en la clase Point y usarlo como en el ejemplo anterior, sería:

public static Point operator +(Point a, Point b){()}

Errores comunes

1. Si se sobrecargan los operadores de comparación, dicha sobrecarga debe realizarse en parejas; es decir, si se sobrecarga ==, también se debe sobrecargar !=. Lo mismo para < y >, y para <= y >=.

2. Si por ejemplo, estás sobrecargando el operador == no puedes utilizar (a == b), (a == null) o (b == null) para comprobar la igualdad de referencia dentro de la sobrecarga. Esto produce una llamada al operador sobrecargado ==, lo que es lo mismo que volver a llamar al operador que estás redefiniendo y como resultado se produce un ciclo infinito. Utiliza ReferenceEquals o convierta el tipo a Object para evitar esto.

Ejemplo (representando un número complejo)

Sabemos que un número complejo tiene una parte real y una imaginaria, y tienen una forma particular de sumarse. Tratemos de simular los números complejos a través de la clase Complex en C#:

// complex.cs
using System;
 
public class Complex
{
   public int real;
   public int imaginary;
 
   //Constructor de la clase
   public Complex(int real, int imaginary)
   {
      this.real = real;
      this.imaginary = imaginary;
   }
 
   // Declaro cual operador será sobrecargado, los tipos que pueden ser sumados
   // (dos objetos de tipo Complex) y el resultado será de tipo Complex
   public static Complex operator +(Complex c1, Complex c2)
   {
      return new Complex(c1.real + c2.real, c1.imaginary + c2.imaginary);
   }
 
   // Redefinimos el operador ==, noten que podemos usar dentro de la función
   // el operador == porque estamos comparando int con int, sino hubiera que
   // hacer un casting
   public static bool operator ==(Complex c1, Complex c2)
   {
      return c1.real == c2.real &amp;&amp; c1.imaginary == c2.imaginary;
   }
 
   //Redefinimos el operador !=, ya que definimos antes el operador ==
   public static bool operator !=(Complex c1, Complex c2)
   {
      return !(c1==c2);
   }
 
   // Sobrecargamos también el método ToString para mostrar el resultado de
   // forma personalizada
   public override string ToString()
   {
      return(String.Format("{0} + {1}i", real, imaginary));
   }
 
   public static void Main()
   {
      Complex c1 = new Complex(2,3);
      Complex c2 = new Complex(3,4);
 
      // Add two Complex objects (num1 and num2) through the
      // overloaded plus operator:
      Complex sum = c1 + c2;
 
     // Mostremos los números y la suma usando el método
	 // ToString sobrecargado
      Console.WriteLine("Primer número complejo:  {0}",num1);
      Console.WriteLine("Segundo número complejo: {0}",num2);
      Console.WriteLine("La suma de los dos números es: {0}",sum);
	  c1 = new Complex(1,1);
	  c2 = new Complex(1,1);
	  Console.WriteLine(c1.Equals(c2));
   }
}

Resultado
First complex number: 2 + 3i
Second complex number: 3 + 4i
The sum of the two numbers: 5 + 7i
false

Notas
En el ejemplo anterior se han sobrecargado los operadores + e ==, en el primer caso esto ha permitido hacer
v3 = v1 + v2, lo cual es muy cómodo ya que de otra manera se hubiese tenido que definir un método Suma y hacer Vector2.Suma(Vector2 sumando2).

En el segundo caso se ha redefinido el operador ==, nótese que también debió ser redefinido el operador != como se explicaba antes. Es decir, si se puede decir si dos objetos son iguales debe también poder decirse si son diferentes, en otros lenguajes como C++ esto no es obligatorio.

En la redefinición se fue un poco más profundo en lo que es la igualdad ya que se determina realmente la igualdad entre dos objetos de tipo Vector2, recordar que por defecto en C# el operador == al igual que el método Equals compara las referencias y dos objetos de tipo Vector2 serian iguales si hacen referencia al mismo objeto. Ahora, la última línea de la salida ha dado false al evaluar c1.Equals(c2), esto se debe al hecho de que una vez redefinido el operador == no quiere decir que se ha redefinido el método Equals en la clase base Object, lo que nos lleva a la pregunta de si sería deseable que al redefinir == sea redefinido también Equals y por por tanto el método GetHashCode.

Sobrecarga explícita o implícita

Veamos un último detalle de la sobrecarga de operadores acerca de los operadores de casteo. Por ejemplo el método siguiente definido dentro de la clase Fahrenheit:

public static explicit operator Celsius(Fahrenheit f)
{
   return new Celsius((f.degrees-32)/1,8);
}

nos permite hacer una conversión de la siguiente forma:

Fahrenheit f;
   ...
Celsius c = (Celsius)f;

Es lo que se conoce como conversión explícita, que nos permite hacer la conversión de Fahrenheit a Celsius, pero veamos como podemos modificar el la sobrecarga para que el código se vea un poco más natural y legible sin hacer casting:

//Modificamos el método anterior:
public static implicit operator Celsius(Fahrenheit f)
{
   return new Celsius((5.0f/9.0f)*(f.degrees-32));
}

Ahora podemos hacer algo como:

Fahrenheit f;
   ...
Celsius c = f;

Cualquier duda en los comentarios.

Compartir:

Relacionados

algunos artículos que te pueden interesar

4 comentarios

Forma parte de nuestra discusión y síguela de cerca

exelente Tomas, justo lo que buscaba

Autor: Eugenio | Fecha: Abr 1, 2012.

Un saludo desde Venezuela, hace unas 3 a 4 semanas que reviso este blog que, dicho sea de paso, me devolvio el interes por la programacion en C# y. Net, ya que algunos de tus ejemplos de código me recordaban mis primeras clases de programacion con el viejo Pascal… en fin, me ha dado por “desempolvar” las viejas cosas que aprendi y afianzarme en las nuevas. Un “gustazo” tu blog. Saludos

Autor: Cristyan Valera | Fecha: Abr 8, 2012.

Gracias Cristian, C# es un lenguaje con mucha potencia y llegó para quedarse. Además, nunca está demás dominar un lenguaje más ;)

Autor: Tomy | Fecha: Abr 12, 2012.

Yo estudié C# y me ha venido bien como base para aprender a programar y tener semi claros los fundamentos de la programación pero dependiendo, y me refiero sobre todo a web, de para donde vayas a dirigir tus pasos te puede valer o no.

Autor: paquíño | Fecha: May 13, 2012.

Escribe tu comentario

Requerido.

Requerido. No público.

Si tienes alguno.