Delen via


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

U kunt een vooraf gedefinieerde C#-operator in een door de gebruiker gedefinieerd type overbelasten. Door een operator te overbelasten, geeft u een aangepaste implementatie voor de bewerking op wanneer een of beide operanden van dat type zijn. Zie de sectie Overbelastingsoperatoren voor een lijst met C#-operators die u kunt overbelasten.

De C#-taalreferentiedocumenten beschrijven de meest recent uitgebrachte versie van de C#-taal. Het bevat ook de eerste documentatie voor functies in openbare previews voor de aanstaande taalrelease.

De documentatie identificeert alle functies die voor het eerst zijn geïntroduceerd in de laatste drie versies van de taal of in de huidige openbare previews.

Aanbeveling

Raadpleeg het artikel over de versiegeschiedenis van de C#-taal om te achterhalen wanneer een functie voor het eerst is geïntroduceerd in C#.

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. Operatoren voor exemplaarmethoden zijn een nieuwe functie die is geïntroduceerd in C# 14.

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

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 kunt u de operatoren voor incrementele (++) en degradatie (--) als instantieleden overbelasten. 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»= yinstantieoperators, hebben voorrang op een statische operator voor «op». Als het type x geen overbelaste «op»= operator declareert of x 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