Delen via


Overbelasting van operatoren - vooraf gedefinieerde unaire, rekenkundige, gelijkheids- en vergelijkingsoperatoren

Een door de gebruiker gedefinieerd type kan een vooraf gedefinieerde C#-operator overbelasten. Dat wil gezegd, een type kan de aangepaste implementatie van een bewerking bieden voor het geval een of beide operanden van dat type zijn. In de sectie Overbelastingsbare operators ziet u welke C#-operators kunnen worden overbelast.

Gebruik het operator trefwoord om een operator te declareren. Een operatordeclaratie moet voldoen aan de volgende regels:

  • Het bevat een public wijzigingsfunctie.
  • Een unaire operator heeft één invoerparameter. Een binaire operator heeft twee invoerparameters. In elk geval moet ten minste één parameter een type T hebben of T? waar T het type is dat de operatordeclaratie bevat.
  • Het omvat de static modificator, met uitzondering van de samengestelde toewijzingsoperatoren, zoals +=.
  • De operatoren voor incrementele (++) en decrement (--) kunnen worden geïmplementeerd als statische of instantiemethoden.

In het volgende voorbeeld wordt een vereenvoudigde structuur gedefinieerd die een rationeel getal vertegenwoordigt. De structuur overbelast enkele rekenkundige operatoren:

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

U kunt het voorgaande voorbeeld uitbreiden door een impliciete conversie van int naar Fraction. Vervolgens ondersteunen overbelaste operators argumenten van deze twee typen. Dat wil zeggen, het zou mogelijk zijn om een geheel getal toe te voegen aan een breuk en een breuk als resultaat te verkrijgen.

Je gebruikt ook het operator trefwoord om een conversie van aangepaste typen te definiëren. Zie Door de gebruiker gedefinieerde conversieoperators voor meer informatie.

Overbelastingsoperators

In de volgende tabel ziet u de operators die kunnen worden overbelast:

Bedieners Opmerkingen
+x, , -x!x, ~x, , ++, , --truefalse De true en false operatoren moeten samen worden overladen.
x + y, x - y, x * y, x / y, x % y
x & y, x | y, x ^ y
x << y, , x >> yx >>> y
x == y, , x != yx < y, x > y, , , x <= yx >= y Moet als volgt in paren worden overbelast: == en !=, < en >, <= en >=.
+=, , -=*=, /=, %=, , &=, , \|=^=<<=>>=>>>= In C# 14 en hoger kunnen de operators voor samengestelde toewijzingen worden overbelast.

Een overbelaste operator voor samengestelde toewijzing moet de volgende regels volgen:

  • Deze moet de public wijzigingsfunctie bevatten.
  • De static modificator kan niet worden opgenomen.
  • Het retourtype moet zijn void.
  • De declaratie moet één parameter bevatten, die de rechterkant van de samengestelde toewijzing aangeeft.

Vanaf C# 14 kunnen de operatoren voor verhoging (++) en verlagen (--) worden overbelast als instantiemembers. Exemplaaroperators kunnen de prestaties verbeteren door het maken van een nieuw exemplaar te voorkomen. Een exemplaaroperator moet de volgende regels volgen:

  • Deze moet de public wijzigingsfunctie bevatten.
  • De static modificator kan niet worden opgenomen.
  • Het retourtype moet zijn void.
  • Er kunnen geen parameters worden declareren, zelfs niet als deze parameters een standaardwaarde hebben.

Niet-overbelaste operators

In de volgende tabel ziet u de operators die niet kunnen worden overbelast:

Bedieners Alternatieven
x && y, x || y Overbelast zowel de true en false operatoren als de & en | operatoren. Zie Door de gebruiker gedefinieerde logische operators voor meer informatie.
a[i], a?[i] Definieer een indexeerder.
(T)x Definieer aangepaste typeconversies die worden uitgevoerd door een cast-expressie. Zie Door de gebruiker gedefinieerde conversieoperators voor meer informatie.
^x, , x = yx.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, typeofwith
Geen.

Voor C# 14 kunnen de samengestelde operators niet worden overbelast. Door de bijbehorende binaire operator te overbelasten, wordt de bijbehorende operator voor samengestelde toewijzing impliciet overbelast.

Oplossing van overbelasting van operator

Belangrijk

Deze sectie is van toepassing op C# 14 en hoger. Vóór C# 14 zijn gebruikersgedefinieerde samengestelde toewijzingsoperatoren en incrementele en decrementoperatoren voor exemplaren niet toegestaan.

Als x wordt geclassificeerd als een variabele in een samengestelde toewijzingsexpressie zoals x «op»= y, hebben de instantieoperators de voorkeur boven een statische operator voor «op». Als een overbelaste «op»= operator niet is gedeclareerd voor het type of xx niet als variabele wordt geclassificeerd, worden de statische operators gebruikt.

Als de postfix-operator ++x niet is geclassificeerd als een variabele of als de expressie x++ wordt gebruikt, wordt het exemplaar operator++ genegeerd. Anders wordt de voorkeur gegeven aan het exemplaar operator ++. Bijvoorbeeld

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

De reden voor deze regel is dat y moet worden toegewezen aan de waarde van xvoordat deze wordt verhoogd. De compiler kan niet bepalen dat voor een door de gebruiker gedefinieerde implementatie in een referentietype.

Voor de voorvoegseloperator ++, als x in ++x is geclassificeerd als een variabele, heeft de instantieoperator de voorkeur boven een statische unaire operator.

C#-taalspecificatie

Zie de volgende secties van de C#-taalspecificatie voor meer informatie:

Zie ook