130 votes

Surcharger un opérateur C# pour `+=` ?

Je essaie de faire des surcharges d'opérateurs pour +=, mais je ne peux pas. Je ne peux faire une surcharge d'opérateur que pour +.

Pourquoi?

Éditer

La raison pour laquelle cela ne fonctionne pas est que j'ai une classe Vecteur (avec un champ X et Y). Considérez l'exemple suivant.

vector1 += vector2;

Si ma surcharge d'opérateur est définie comme suit:

public static Vector operator +(Vector left, Vector right)
{
    return new Vector(right.x + left.x, right.y + left.y);
}

Alors le résultat ne sera pas ajouté à vector1, mais à la place, vector1 deviendra également un tout nouveau Vecteur par référence.

2 votes

Il semble qu'une longue discussion ait déjà eu lieu à ce sujet: maurits.wordpress.com/2006/11/27/…

39 votes

Pouvez-vous expliquer pourquoi vous essayez de faire cela? Vous obtenez gratuitement un opérateur "+=" surchargé lorsque vous surchargez "+". Y a-t-il une situation dans laquelle vous voulez que "+=" soit surchargé mais ne voulez pas que "+" soit surchargé?

3 votes

En venant de C ++, cela semble juste faux, mais en C #, cela a en fait tout son sens.

160voto

VMAtm Points 8401

Opérateurs surchargés, depuis MSDN:

Les opérateurs d'assignation ne peuvent pas être surchargés, mais +=, par exemple, est évalué en utilisant +, qui peut être surchargé.

De plus, aucun des opérateurs d'assignation ne peut être surchargé. Je pense que c'est parce qu'il y aura un effet sur la collecte des déchets et la gestion de la mémoire, ce qui constitue une faille de sécurité potentielle dans le monde fort typé du CLR.

Néanmoins, voyons ce qu'est exactement un opérateur. Selon le célèbre livre de Jeffrey Richter, chaque langage de programmation a sa propre liste d'opérateurs, qui sont compilés dans des appels de méthodes spéciales, et le CLR lui-même ne sait rien des opérateurs. Alors voyons ce qui se cache exactement derrière les opérateurs + et +=.

Regardez ce code simple :

Décimal d = 10M;
d = d + 10M;
Console.WriteLine(d);

Voyons le code IL pour ces instructions :

  IL_0000:  nop
  IL_0001:  ldc.i4.s   10
  IL_0003:  newobj     instance void [mscorlib]System.Decimal::.ctor(int32)
  IL_0008:  stloc.0
  IL_0009:  ldloc.0
  IL_000a:  ldc.i4.s   10
  IL_000c:  newobj     instance void [mscorlib]System.Decimal::.ctor(int32)
  IL_0011:  call       valuetype [mscorlib]System.Decimal [mscorlib]System.Decimal::op_Addition(valuetype [mscorlib]System.Decimal,
                                                                                                valuetype [mscorlib]System.Decimal)
  IL_0016:  stloc.0

Passons maintenant à ce code :

Décimal d1 = 10M;
d1 += 10M;
Console.WriteLine(d1);

Et le code IL pour cela :

  IL_0000:  nop
  IL_0001:  ldc.i4.s   10
  IL_0003:  newobj     instance void [mscorlib]System.Decimal::.ctor(int32)
  IL_0008:  stloc.0
  IL_0009:  ldloc.0
  IL_000a:  ldc.i4.s   10
  IL_000c:  newobj     instance void [mscorlib]System.Decimal::.ctor(int32)
  IL_0011:  call       valuetype [mscorlib]System.Decimal [mscorlib]System.Decimal::op_Addition(valuetype [mscorlib]System.Decimal,
                                                                                                valuetype [mscorlib]System.Decimal)
  IL_0016:  stloc.0

ils sont égaux! Donc l'opérateur += est simplement du sucre syntaxique pour votre programme en C#, et vous pouvez simplement surcharger l'opérateur +.

Par exemple :

class Foo
{
    private int c1;

    public Foo(int c11)
    {
        c1 = c11;
    }

    public static Foo operator +(Foo c1, Foo x)
    {
        return new Foo(c1.c1 + x.c1);
    }
}

static void Main(string[] args)
{
    Foo d1 =  new Foo (10);
    Foo d2 = new Foo(11);
    d2 += d1;
}

Ce code sera compilé et exécuté avec succès comme :

  IL_0000:  nop
  IL_0001:  ldc.i4.s   10
  IL_0003:  newobj     instance void ConsoleApplication2.Program/Foo::.ctor(int32)
  IL_0008:  stloc.0
  IL_0009:  ldc.i4.s   11
  IL_000b:  newobj     instance void ConsoleApplication2.Program/Foo::.ctor(int32)
  IL_0010:  stloc.1
  IL_0011:  ldloc.1
  IL_0012:  ldloc.0
  IL_0013:  call       class ConsoleApplication2.Program/Foo ConsoleApplication2.Program/Foo::op_Addition(class ConsoleApplication2.Program/Foo,
                                                                                                          class ConsoleApplication2.Program/Foo)
  IL_0018:  stloc.1

Mise à jour :

Conformément à votre mise à jour - comme le dit @EricLippert, vous devriez vraiment avoir les vecteurs en tant qu'objet immuable. Le résultat de l'addition des deux vecteurs est un nouveau vecteur, pas le premier avec des tailles différentes.

Si, pour une raison quelconque, vous avez besoin de modifier le premier vecteur, vous pouvez utiliser cette surcharge (mais selon moi, c'est un comportement très étrange) :

public static Vector operator +(Vector left, Vector right)
{
    left.x += right.x;
    left.y += right.y;
    return left;
}

2 votes

Déclarer que c'est le cas n'est pas répondre à pourquoi.

1 votes

@Jouke van der Maas Et comment veux-tu que je réponde pourquoi c'est impossible? C'est impossible par conception, que puis-je dire d'autre?

2 votes

Pourquoi l'ont-ils conçu de cette façon; c'est vraiment la question. Veuillez consulter certaines des autres réponses.

18voto

pickypg Points 8948

Je pense que vous trouverez ce lien instructif : Opérateurs surchargeables

Les opérateurs d'affectation ne peuvent pas être surchargés, mais +=, par exemple, est évalué en utilisant +, qui peut être surchargé.

2 votes

@pickypg - ce commentaire n'est pas lié à ce fil de discussion: veuillez restaurer votre réponse, elle répond à ma question, je pense que je n'ai pas d'autre choix que d'utiliser votre méthode, je pensais qu'il y avait une meilleure façon de le résoudre.

18voto

agent-j Points 14703

Cela est dû à la même raison pour laquelle l'opérateur d'assignation ne peut pas être surchargé. Vous ne pouvez pas écrire de code qui effectuerait correctement l'assignation.

class Foo
{
   // Ne compilera pas.
   public static Foo operator= (Foo c1, int x)
   {
       // duh... que dois-je faire ici ? Je ne peux pas changer la référence de c1.
   }
}

Les opérateurs d'assignation ne peuvent pas être surchargés, mais +=, par exemple, est évalué en utilisant +, qui peut être surchargé.

De MSDN.

18voto

benzado Points 36367

Vous ne pouvez pas surcharger += car ce n'est pas vraiment un opérateur unique, c'est simplement du sucre syntaxique. x += y est simplement un moyen abrégé d'écrire x = x + y. Parce que += est défini en termes des opérateurs + et =, permettre de le remplacer séparément pourrait créer des problèmes, dans les cas où x += y et x = x + y ne se comporteraient pas exactement de la même manière.

À un niveau plus bas, il est très probable que le compilateur C# compile les deux expressions en le même bytecode, ce qui signifie très probablement que l'exécution en temps réel ne peut pas les traiter différemment lors de l'exécution du programme.

Je comprends que vous puissiez vouloir le traiter comme une opération séparée : dans une instruction comme x += 10, vous savez que vous pouvez muter l'objet x en place et peut-être gagner du temps/mémoire, plutôt que de créer un nouvel objet x + 10 avant de l'assigner sur l'ancienne référence.

Mais considérez ce code :

a = ...
b = a;
a += 10;

a == b devrait-il être vrai à la fin ? Pour la plupart des types, non, a est 10 de plus que b. Mais si vous pouviez surcharger l'opérateur += pour muter directement, alors oui. Maintenant, considérez que a et b pourraient être passés à des parties éloignées du programme. Votre possible optimisation pourrait créer des bugs confus si votre objet commence à changer là où le code ne s'y attend pas.

En d'autres termes, si la performance est si importante, il n'est pas trop difficile de remplacer x += 10 par un appel de méthode tel que x.augmenterDe(10), et c'est beaucoup plus clair pour tout le monde impliqué.

2 votes

Personnellement, je changerais it's just syntactic sugar en it's just syntactic sugar in C#; sinon, cela semble trop général, mais dans certains langages de programmation, ce n'est pas juste du sucre syntaxique mais cela pourrait réellement apporter des avantages de performance.

0 votes

Pour les types simples (int, float, etc.), += pourrait probablement être optimisé par un compilateur intelligent, car l'arithmétique est simple. Mais une fois que vous traitez avec des objets, tout est permis. Tous les langages rencontrent à peu près les mêmes problèmes. C'est pourquoi la surcharge d'opérateurs est mauvaise.

0 votes

@SebastianMach La question est spécifiquement étiquetée avec des balises c# et dotnet. De toute évidence, en c++ par exemple, '+' et '+=' (et même '=') peuvent être surchargés séparément.

9voto

Andrew Orsich Points 24503

C'est parce que cet opérateur ne peut pas être surchargé :

Les opérateurs d'assignation ne peuvent pas être surchargés, mais +=, par exemple, est évalué en utilisant +, qui peut être surchargé.

MSDN

Surcharger simplement l'opérateur +, à cause de

x += y équivaut à x = x + y

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X