Partager via


Surcharge des opérateurs : opérateurs unaires, arithmétiques, d’égalité et de comparaison prédéfinis

Un type défini par l’utilisateur peut surcharger un opérateur C# prédéfini. Autrement dit, un type peut fournir l’implémentation personnalisée d’une opération au cas où un ou les deux opérandes sont de ce type. La section Opérateurs surchargés indique quels opérateurs C# peuvent être surchargés.

Utilisez le operator mot clé pour déclarer un opérateur. Une déclaration d’opérateur doit respecter les règles suivantes :

  • Il inclut un public modificateur.
  • Un opérateur unaire a un paramètre d’entrée. Un opérateur binaire a deux paramètres d’entrée. Dans chaque cas, au moins un paramètre doit avoir un type T ou T?T est le type qui contient la déclaration d’opérateur.
  • Il inclut le static modificateur, à l’exception des opérateurs d’affectation composée, tels que +=.
  • Les opérateurs d’incrément (++) et de décrémentation (--) peuvent être implémentés en tant que méthodes statiques ou d’instance.

L’exemple suivant définit une structure simplifiée pour représenter un nombre rationnel. La structure surcharge certains des opérateurs arithmétiques :

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

Vous pouvez étendre l’exemple précédent en définissant une conversion implicite en intFraction. Ensuite, les opérateurs surchargés prennent en charge les arguments de ces deux types. Autrement dit, il serait possible d’ajouter un entier à une fraction et d’obtenir une fraction en conséquence.

Vous utilisez également le operator mot clé pour définir une conversion de type personnalisée. Pour plus d’informations, consultez les opérateurs de conversion définis par l’utilisateur.

Opérateurs surchargés

Le tableau suivant présente les opérateurs qui peuvent être surchargés :

Opérateurs Remarques
+x, -x, , ~x!x, ++, --, , true,false Les opérateurs true et false doivent être surchargés ensemble.
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 > yx < y, , x <= yx >= y Doit être surchargé en paires comme suit : == et !=, < et >, <= et >=.
+=, , -=, , %=^=>>=/=&=\|=<<=*=>>>= Les opérateurs d’affectation composés peuvent être surchargés dans C# 14 et versions ultérieures

Un opérateur d’affectation composé surchargé doit respecter les règles suivantes

  • Il doit inclure le public modificateur.
  • Il ne peut pas inclure le static modificateur.
  • Le type de retour doit être void.
  • La déclaration doit inclure un paramètre, qui représente le côté droit de l’affectation composée.

À partir de C# 14, les opérateurs d'incrément (++) et de décrément (--) peuvent être surchargés en tant que membres d'instance. Les opérateurs d’instance peuvent améliorer les performances en évitant la création d’une nouvelle instance. Un opérateur d’instance doit respecter ces règles :

  • Il doit inclure le public modificateur.
  • Il ne peut pas inclure le static modificateur.
  • Le type de retour doit être void.
  • Il ne peut déclarer aucun paramètre, même si ces paramètres ont une valeur par défaut.

Opérateurs non surchargeables

Le tableau suivant montre les opérateurs qui ne peuvent pas être surchargés :

Opérateurs Alternatives
x && y, x || y Surchargez à la fois les opérateurs true et false ainsi que les opérateurs & ou |. Pour plus d’informations, consultez Opérateurs logiques conditionnels définis par l’utilisateur.
a[i], a?[i] Définissez un indexeur.
(T)x Définissez les conversions de types personnalisés effectuées par une expression de transtypage. Pour plus d’informations, consultez les opérateurs de conversion définis par l’utilisateur.
^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, , stackallocswitch, , typeofwith
Aucun.

Avant C# 14, les opérateurs composés ne peuvent pas être surchargés. La surcharge de l’opérateur binaire correspondant surcharge implicitement l’opérateur d’affectation composée correspondante.

Résolution de la surcharge des opérateurs

Importante

Cette section s’applique à C# 14 et versions ultérieures. Avant C# 14, les opérateurs d’affectation composée définis par l’utilisateur et les opérateurs d’incrémentation d’instance et de décrémentation ne sont pas autorisés.

Si x est classifiée comme variable dans une expression d’affectation à opérateurs multiples, par exemple x «op»= y, les opérateurs d’instance sont préférés à n’importe quel opérateur statique pour «op». Si un opérateur surchargé «op»= n’est pas déclaré pour le type de x variable ou x n’est pas classé comme variable, les opérateurs statiques sont utilisés.

Pour l’opérateur ++postfix, s’il x n’est pas classé comme variable ou si l’expression x++ est utilisée, l’instance operator++ est ignorée. Sinon, la préférence est donnée à l’instance operator ++. Par exemple,

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

La raison de cette règle est que y doit être affectée à la valeur de xavant qu'elle soit incrémentée. Le compilateur ne peut pas déterminer cela pour une implémentation définie par l'utilisateur dans un type de référence.

Pour l’opérateur préfixe ++, si x est classé comme étant une variable dans ++x, l’opérateur d’instance est préféré plutôt qu’à un opérateur unaire statique.

Spécification du langage C#

Pour plus d’informations, consultez les sections suivantes de la spécification du langage C# :

Voir aussi