Sdílet prostřednictvím


Přetížení operátorů – předdefinované unární, aritmetické, rovnosti a porovnávací operátory

Uživatelem definovaný typ může přetížit předdefinovaný operátor jazyka C#. To znamená, že typ může poskytnout vlastní implementaci operace v případě, že jeden nebo oba operandy jsou tohoto typu. Oddíl Přetížitelné operátory ukazuje, které operátory jazyka C# je možné přetížit.

Deklarujte operátor pomocí klíčového slova operator. Deklarace operátoru musí splňovat následující pravidla:

  • public Obsahuje modifikátor.
  • Unární operátor má jeden vstupní parametr. Binární operátor má dva vstupní parametry. V každém případě musí mít alespoň jeden parametr typ T nebo T? kde T je typ, který obsahuje deklaraci operátoru.
  • static Obsahuje modifikátor s výjimkou operátorů složeného přiřazení, například +=.
  • Operátory inkrementace () a dekrementace (++--) je možné implementovat jako statické metody nebo metody instance.

Následující příklad definuje zjednodušenou strukturu, která představuje logické číslo. Struktura přetíží některé aritmetické operátory:

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
    }
}

Předchozí příklad můžete rozšířit definováním implicitního převodu z int na Fraction. Přetížené operátory by pak podporovaly argumenty těchto dvou typů. To znamená, že by bylo možné přidat celé číslo k zlomku a získat zlomek v důsledku toho.

Pomocí klíčového slova operator definujete převod vlastního typu. Další informace naleznete v tématu Uživatelem definované operátory převodu.

Přetížitelné operátory

Následující tabulka ukazuje operátory, které je možné přetížit:

Operátoři Poznámky
+x, -x, !x, , ~x++, --, , truefalse Operátory true a false musí být přetíženy dohromady.
x + y, x - y, x * y, x / y, , x % y
x & y, x | y, , x ^ y
x << y, , x >> yx >>> y
x == y, x != y, x < y, x > y, , x <= yx >= y Musí být přetíženy ve dvojicích následujícím způsobem: == a !=< , a ><= , a >=.
+=, -=, , *=, /=%=&=\|=^=<<=, >>=>>>= Operátory složeného přiřazení je možné přetížit v jazyce C# 14 a novějším.

Přetížený operátor složeného přiřazení musí dodržovat tato pravidla:

  • Musí obsahovat public modifikátor.
  • Modifikátor nemůže obsahovat static .
  • Návratový typ musí být void.
  • Deklarace musí obsahovat jeden parametr, který představuje pravou stranu složeného přiřazení.

Počínaje jazykem C# 14 je možné přetížit operátory inkrementace (++) a dekrementace (--) jako členy instance. Operátory instancí můžou zvýšit výkon tím, že se zabrání vytvoření nové instance. Operátor instance musí dodržovat tato pravidla:

  • Musí obsahovat public modifikátor.
  • Modifikátor nemůže obsahovat static .
  • Návratový typ musí být void.
  • Nemůže deklarovat žádné parametry, i když mají tyto parametry výchozí hodnotu.

Nepřetížitelné operátory

Následující tabulka ukazuje operátory, které nelze přetížit:

Operátoři Alternativy
x && y, x || y Přetěžujte jak operátory true a false, tak operátory & nebo |. Další informace naleznete v tématu Uživatelem definované podmíněné logické operátory.
a[i], a?[i] Definujte indexer.
(T)x Definujte vlastní převody typů prováděné výrazem typu. Další informace naleznete v tématu Uživatelem definované operátory převodu.
^x, x = y, x.y, , x?.yc ? t : f, x ?? y, , ??= y
x..y, x->y, =>, f(x), as, await, checked, unchecked, default, delegate, is, nameof, new
sizeof, stackalloc, switch, , typeofwith
Žádné.

Před C# 14 nelze složené operátory přetížit. Přetížení odpovídajícího binárního operátoru implicitně přetěžuje odpovídající operátor složeného přiřazení.

Rozlišení přetížení operátoru

Důležité

Tato část platí pro C# 14 a novější. Před verzí C# 14 nejsou povolené operátory pro uživatelem definované složené přiřazení a operátory pro inkrementaci a dekrementaci instancí.

Pokud je x klasifikován jako proměnná ve výrazu složeného přiřazení, jako je například x «op»= y, mají instance operátorů přednost před libovolným statickým operátorem pro «op». Pokud není přetížený «op»= operátor deklarován pro typ x proměnné nebo x není klasifikován jako proměnná, použijí se statické operátory.

Pro postfixový operátor ++, pokud x není klasifikováno jako proměnná nebo je použit výraz x++, instance operator++ je ignorována. V opačném případě je upřednostněná instance operator ++. Příklad:

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

Důvodem tohoto pravidla je, že y by se měla přiřadit k hodnotě xpřed jejím zvýšením. Kompilátor nedokáže určit podmínky pro uživatelsky definovanou implementaci v referenčním typu.

U operátoru předpony ++, pokud je x klasifikován jako proměnná v ++x, je preferován operátor instance před statickým unárním operátorem.

Specifikace jazyka C#

Další informace najdete v následujících částech specifikace jazyka C#:

Viz také