Dela via


Operatoröverlagring – fördefinierade unära, aritmetiska, jämförelse- och likhetsoperatorer

En användardefinierad typ kan överbelasta en fördefinierad C#-operator. En typ kan alltså tillhandahålla anpassad implementering av en åtgärd om en eller båda operanderna är av den typen. Avsnittet Överbelastningsbara operatorer visar vilka C#-operatorer som kan överbelastas.

Använd nyckelordet operator för att deklarera en operator. En operatörsdeklaration måste uppfylla följande regler:

  • Den innehåller en public modifierare.
  • En unär operator har en indataparameter. En binär operator har två indataparametrar. I varje fall måste minst en parameter ha typen T eller T? var T är den typ som innehåller operatordeklarationen.
  • Den innehåller static modifieraren, förutom de sammansatta tilldelningsoperatorerna, till exempel +=.
  • Operatorerna increment (++) och decrement (--) kan implementeras som antingen statiska metoder eller instansmetoder.

I följande exempel definieras en förenklad struktur som representerar ett rationellt tal. Strukturen överbelastar några av de aritmetiska operatorerna:

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

Du kan utöka föregående exempel genom att definiera en implicit konvertering från int till Fraction. Därefter skulle överbelastade operatorer stödja argument av dessa två typer. Det innebär att det skulle bli möjligt att lägga till ett heltal till ett bråk och få ett bråk som ett resultat.

Du använder också nyckelordet operator för att definiera en konvertering av anpassad typ. Mer information finns i Användardefinierade konverteringsoperatorer.

Överbelastbara operatorer

I följande tabell visas de operatorer som kan överbelastas:

Operatörer Noteringar
+x, -x, !x, ~x, ++, , --, , truefalse Operatorerna true och false måste vara överlagrade tillsammans.
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 Måste vara överlagrade i par enligt följande: == och !=, < och >, <= och >=.
+=, -=, *=, /=, %=, &=, , \|=, ^=, <<=, , , >>=>>>= De sammansatta tilldelningsoperatorerna kan överbelastas i C# 14 och senare.

En överbelastad operator för sammansatt tilldelning måste följa dessa regler:

  • Den måste innehålla public modifieraren.
  • Det kan inte innehålla static modifieraren.
  • Returtypen måste vara void.
  • Deklarationen måste innehålla en parameter som representerar den högra sidan av den sammansatta tilldelningen.

Från och med C# 14 kan operatorerna increment (++) och decrement (--) överbelastas som instansmedlemmar. Instansoperatorer kan förbättra prestanda genom att undvika att skapa en ny instans. En instansoperator måste följa dessa regler:

  • Den måste innehålla public modifieraren.
  • Det kan inte innehålla static modifieraren.
  • Returtypen måste vara void.
  • Det kan inte deklarera några parametrar, även om dessa parametrar har ett standardvärde.

Operatorer som inte kan överbelastas

I följande tabell visas de operatorer som inte kan överbelastas:

Operatörer Alternativ
x && y, x || y Överbelasta både operatorerna true och false operatorerna & eller | . Mer information finns i Användardefinierade villkorsstyrda logiska operatorer.
a[i], a?[i] Definiera en indexerare.
(T)x Definiera anpassade typkonverteringar som utförs av ett cast-uttryck. Mer information finns i Användardefinierade konverteringsoperatorer.
^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, , , typeofwith
Ingen.

Före C# 14 kan de sammansatta operatorerna inte överbelastas. Genom att överbelasta den motsvarande binära operatorn överbelastas samtidigt implicit den motsvarande sammansatta tilldelningsoperatorn.

Operatörens överbelastningsupplösning

Viktigt!

Det här avsnittet gäller för C# 14 och senare. Före C# 14 tillåts inte användardefinierade sammansatta tilldelningsoperatorer och instansens inkrement- och minskningsoperatorer.

Om x klassificeras som en variabel i ett sammansatt tilldelningsuttryck som x «op»= y, föredras instansoperatorer framför alla statiska operatorer för «op». Om en överbelastad «op»= operator inte deklareras för typen av x eller x inte klassificeras som en variabel används de statiska operatorerna.

Om postfixoperatorn ++x inte klassificeras som en variabel eller om uttrycket x++ används ignoreras instansenoperator++. Annars ges företräde till instansen operator ++. Ett exempel:

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

Anledningen till den här regeln är att y bör tilldelas värdet av xinnan den ökas. Kompilatorn kan inte fastställa det för en användardefinierad implementering i en referenstyp.

För prefixoperatorn ++, om x klassificeras som en variabel i ++x, föredras instansoperatorn framför en statisk unary-operator.

Språkspecifikation för C#

Mer information finns i följande avsnitt i C#-språkspecifikationen:

Se även