Compartir a través de


Sobrecarga de operadores: operadores unarios predefinidos, aritméticos, de igualdad y de comparación

Puede sobrecargar un operador predefinido de C# en un tipo definido por el usuario. Al sobrecargar un operador, se proporciona una implementación personalizada para la operación cuando uno o ambos operandos son de ese tipo. Consulte la sección Operadores sobrecargables para obtener una lista de operadores de C# que puede sobrecargar.

La documentación de referencia del lenguaje C# cubre la versión más reciente publicada del lenguaje C#. También contiene documentación inicial sobre las características de las versiones preliminares públicas de la próxima versión del lenguaje.

La documentación identifica cualquier característica introducida por primera vez en las últimas tres versiones del idioma o en las versiones preliminares públicas actuales.

Sugerencia

Para buscar cuándo se introdujo por primera vez una característica en C#, consulte el artículo sobre el historial de versiones del lenguaje C#.

Use la operator palabra clave para declarar un operador. Una declaración de operador debe cumplir las siguientes reglas:

  • Incluye un modificador public.
  • Un operador unario tiene un parámetro de entrada. Un operador binario tiene dos parámetros de entrada. En cada caso, al menos un parámetro debe tener el tipo T o T? donde T es el tipo que contiene la declaración del operador.
  • Incluye el static modificador, excepto para los operadores de asignación compuestos, como +=.
  • Los operadores de incremento (++) y decremento (--) se pueden implementar como métodos estáticos o de instancia. Los operadores de método de instancia son una nueva característica introducida en C# 14.

En el ejemplo siguiente se define una estructura simplificada para representar un número racional. La estructura sobrecarga algunos de los operadores aritméticos:

public struct Fraction
{
    private int numerator;
    private int denominator;

    public Fraction(int numerator, int denominator)
    {
        if (denominator == 0)
        {
            throw new ArgumentException("Denominator cannot be zero.", nameof(denominator));
        }
        this.numerator = numerator;
        this.denominator = denominator;
    }

    public static Fraction operator +(Fraction operand) => operand;
    public static Fraction operator -(Fraction operand) => new Fraction(-operand.numerator, operand.denominator);

    public static Fraction operator +(Fraction left, Fraction right)
        => new Fraction(left.numerator * right.denominator + right.numerator * left.denominator, left.denominator * right.denominator);

    public static Fraction operator -(Fraction left, Fraction right)
        => left + (-right);

    public static Fraction operator *(Fraction left, Fraction right)
        => new Fraction(left.numerator * right.numerator, left.denominator * right.denominator);

    public static Fraction operator /(Fraction left, Fraction right)
    {
        if (right.numerator == 0)
        {
            throw new DivideByZeroException();
        }
        return new Fraction(left.numerator * right.denominator, left.denominator * right.numerator);
    }

    // Define increment and decrement to add 1/den, rather than 1/1.
    public static Fraction operator ++(Fraction operand)
        => new Fraction(operand.numerator + 1, operand.denominator);

    public static Fraction operator --(Fraction operand) =>
        new Fraction(operand.numerator - 1, operand.denominator);

    public override string ToString() => $"{numerator} / {denominator}";

    // New operators allowed in C# 14:
    public void operator +=(Fraction operand) =>
        (numerator, denominator ) =
        (
            numerator * operand.denominator + operand.numerator * denominator,
            denominator * operand.denominator
        );

    public void operator -=(Fraction operand) =>
        (numerator, denominator) =
        (
            numerator * operand.denominator - operand.numerator * denominator,
            denominator * operand.denominator
        );

    public void operator *=(Fraction operand) =>
        (numerator, denominator) =
        (
            numerator * operand.numerator,
            denominator * operand.denominator
        );

    public void operator /=(Fraction operand)
    {
        if (operand.numerator == 0)
        {
            throw new DivideByZeroException();
        }
        (numerator, denominator) =
        (
            numerator * operand.denominator,
            denominator * operand.numerator
        );
    }

    public void operator ++() => numerator++;

    public void operator --() => numerator--;
}

public static class OperatorOverloading
{
    public static void Main()
    {
        var a = new Fraction(5, 4);
        var b = new Fraction(1, 2);
        Console.WriteLine(-a);   // output: -5 / 4
        Console.WriteLine(a + b);  // output: 14 / 8
        Console.WriteLine(a - b);  // output: 6 / 8
        Console.WriteLine(a * b);  // output: 5 / 8
        Console.WriteLine(a / b);  // output: 10 / 4
    }
}

Puede ampliar el ejemplo anterior definiendo una conversión implícita de int a Fraction. A continuación, los operadores sobrecargados admitirían argumentos de esos dos tipos. Es decir, sería posible agregar un entero a una fracción y obtener una fracción como resultado.

También se usa la operator palabra clave para definir una conversión de tipos personalizada. Para obtener más información, vea Operadores de conversión definidos por el usuario.

Operadores sobrecargables

En la tabla siguiente se muestran los operadores que se pueden sobrecargar:

Operadores Notas
+x, -x, !x, ~x, ++, --, , truefalse Los operadores true y false deben sobrecargarse juntos.
x + y, x - y, x * y, x / y, , x % y,
x & y, x | y, , x ^ y,
x << y, , x >> y, x >>> y
x == y, x != y, x < y, x > y, , x <= y, x >= y Debe sobrecargarse en pares de la siguiente manera: == y !=, < y >, <= y >=.
+=, -=, *=, /=, %=, &=, \|=, ^=, <<=, , >>=>>>= Los operadores de asignación compuesta se pueden sobrecargar en C# 14 y versiones posteriores.

Un operador sobrecargado de asignación compuesta debe seguir estas reglas:

  • Debe incluir el public modificador .
  • No puede incluir el static modificador .
  • El tipo de valor devuelto debe ser void.
  • La declaración debe incluir un parámetro, que representa el lado derecho de la asignación compuesta.

A partir de C# 14, puede sobrecargar los operadores de incremento (++) y decremento (--) como miembros de instancia. Los operadores de instancia pueden mejorar el rendimiento evitando la creación de una nueva instancia. Un operador de instancia debe seguir estas reglas:

  • Debe incluir el public modificador .
  • No puede incluir el static modificador .
  • El tipo de valor devuelto debe ser void.
  • No puede declarar ningún parámetro, incluso si esos parámetros tienen un valor predeterminado.

Operadores no sobrecargables

En la tabla siguiente se muestran los operadores que no se pueden sobrecargar:

Operadores Alternativas
x && y, x || y Sobrecargue tanto los operadores true como los false, y los operadores & o |. Para obtener más información, consulte Operadores lógicos condicionales definidos por el usuario.
a[i], a?[i] Defina un indexador.
(T)x Defina las conversiones personalizadas de tipo realizadas mediante una expresión de conversión. Para obtener más información, vea Operadores de conversión definidos por el usuario.
^x, x = y, x.y, x?.y, c ? t : f, x ?? y, , ??= y
x..y, x->y, =>, f(x), as, await, checked, unchecked, default, delegate, is, nameof, new
sizeof, stackalloc, switch, , typeof, with
Ninguno.

Antes de C# 14, no era posible sobrecargar los operadores compuestos. Sobrecargar el operador binario correspondiente sobrecarga implícitamente el operador de asignación compuesta correspondiente.

Resolución de sobrecarga de operadores

Importante

Esta sección se aplica a C# 14 y versiones posteriores. Antes de C# 14, no se permiten operadores de asignación compuesta definidos por el usuario ni operadores de incremento y decremento de instancia.

Si x se clasifica como una variable en una expresión de asignación compuesta como x «op»= y, los operadores de instancia tienen prioridad sobre cualquier operador estático para «op». Si el tipo de x no declara un operador sobrecargado «op»= o x no se clasifica como una variable, se usan los operadores estáticos.

Para el operador ++postfix , si x no se clasifica como una variable o se usa la expresión x++ , se omite la instancia operator++ . De lo contrario, se da preferencia a la instancia operator ++. Por ejemplo

x++; // Instance operator++ preferred.
y = x++; // instance operator++ isn't considered.

El motivo de esta regla es que y se debe asignar al valor de xantes de que se incremente. El compilador no puede determinar que esto es válido para una implementación definida por el usuario en un tipo de referencia.

Para el operador ++de prefijo , si x se clasifica como una variable en ++x, se prefiere el operador de instancia sobre un operador unario estático.

Especificación del lenguaje C#

Para más información, vea las secciones siguientes de la Especificación del lenguaje C#:

Consulte también