Partilhar via


Sobrecarga do operador - operadores unários, aritméticos, de igualdade e de comparação predefinidos

Um tipo definido pelo usuário pode sobrecarregar um operador C# predefinido. Ou seja, um tipo pode fornecer a implementação personalizada de uma operação no caso de um ou ambos os operandos serem desse tipo. A seção Operadores sobrecarregáveis mostra quais operadores C# podem ser sobrecarregados.

Use a operator palavra-chave para declarar um operador. A declaração do operador deve satisfazer as seguintes regras:

  • Inclui um public modificador.
  • Um operador unário tem um parâmetro de entrada. Um operador binário tem dois parâmetros de entrada. Em cada caso, pelo menos um parâmetro deve ter tipo T ou T? onde T é o tipo que contém a declaração do operador.
  • Inclui o static modificador, exceto para os operadores de atribuição compostos, como +=.
  • Os operadores increment (++) e decrement (--) podem ser implementados como métodos estáticos ou de instância.

O exemplo a seguir define uma estrutura simplificada para representar um número racional. A estrutura sobrecarrega alguns dos 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++, operand.denominator);

    public static Fraction operator --(Fraction operand) =>
        new Fraction(operand.numerator--, 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
    }
}

Você pode estender o exemplo anterior definindo uma conversão implícita de int para Fraction. Então, operadores sobrecarregados apoiariam argumentos desses dois tipos. Ou seja, tornar-se-ia possível adicionar um inteiro a uma fração e obter uma fração como resultado.

Você também usa a operator palavra-chave para definir uma conversão de tipo personalizada. Para obter mais informações, consulte Operadores de conversão definidos pelo usuário.

Operadores sobrecarregados

A tabela a seguir mostra os operadores que podem ser sobrecarregados:

Operadores Observações
+x, -x, !x, ~x++, --, true, false Os operadores true e false devem ser sobrecarregados em conjunto.
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 Deve ser sobrecarregado em pares da seguinte forma: == e !=, < e >, <= e >=.
+=, -=, , *=/=, %=, &=, \|=, ^=<<=>>=>>>= Os operadores de atribuição compostos podem ser sobrecarregados em C# 14 e posterior.

Um operador sobrecarregado de atribuição composta deve seguir estas regras:

  • Deve incluir o modificador public.
  • Não pode incluir o static modificador.
  • O tipo de retorno deve ser void.
  • A declaração deve incluir um parâmetro, que representa o lado direito da atribuição composta.

A partir do C# 14, os operadores de incremento (++) e decréscimo (--) podem ser sobrecarregados como membros da instância. Os operadores de instância podem melhorar o desempenho evitando a criação de uma nova instância. Um operador de instância deve seguir estas regras:

  • Deve incluir o modificador public.
  • Não pode incluir o static modificador.
  • O tipo de retorno deve ser void.
  • Ele não pode declarar nenhum parâmetro, mesmo que esses parâmetros tenham um valor padrão.

Operadores não sobrecarregáveis

A tabela a seguir mostra os operadores que não podem ser sobrecarregados:

Operadores Alternativas
x && y, x || y Sobrecarregar os operadores true e false, assim como os operadores & ou |. Para obter mais informações, consulte Operadores lógicos condicionais definidos pelo usuário.
a[i], a?[i] Defina um indexador.
(T)x Defina conversões de tipo personalizadas realizadas por uma expressão de transmissão. Para obter mais informações, consulte Operadores de conversão definidos pelo usuário.
^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
Nenhum.

Antes do C# 14, os operadores compostos não podiam ser sobrecarregados. Sobrecarregar o operador binário correspondente sobrecarrega implicitamente o operador de atribuição composto correspondente.

Resolução de sobrecarga de operadores

Importante

Esta seção se aplica ao C# 14 e posterior. Antes do C# 14, operadores de atribuição composta definidos pelo usuário e operadores de incremento e decréscimo de instância não eram permitidos.

Se x for classificado como uma variável em uma expressão de atribuição composta, como x «op»= y, os operadores de instância são preferidos sobre qualquer operador estático para «op». Se um operador sobrecarregado «op»= não for declarado para o tipo de x ou x não for classificado como uma variável, os operadores estáticos serão usados.

Para o operador postfix ++, se x não for classificado como uma variável ou se a expressão x++ for utilizada, a instância operator++ será ignorada. Caso contrário, é dada preferência à instância operator ++. Por exemplo

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

A razão para esta regra é que y deve ser atribuído ao valor de xantes de ser incrementado. O compilador não pode determinar isso para uma implementação definida pelo usuário em um tipo de referência.

Para o operador ++de prefixo , se x for classificado como uma variável em ++x, o operador de instância é preferido sobre um operador unário estático.

Especificação da linguagem C#

Para obter mais informações, consulte as seguintes seções da especificação da linguagem C#:

Ver também