Sovraccarico dell’operatore C # per `+ =`?

Sto cercando di fare sovraccarichi dell’operatore per += , ma non posso. Posso fare solo un sovraccarico dell’operatore per + .

Come mai?

modificare

Il motivo per cui non funziona è che ho una class Vector (con un campo X e Y). Considera il seguente esempio.

 vector1 += vector2; 

Se il sovraccarico del mio operatore è impostato su:

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

Quindi il risultato non verrà aggiunto a vector1, ma invece, vector1 diventerà un nuovissimo Vector per riferimento.

Operatori sovraccarichi , da MSDN:

Gli operatori di assegnazione non possono essere sovraccaricati, ma += , ad esempio, viene valutato usando + , che può essere sovraccaricato.

Inoltre, nessuno degli operatori di assegnazione può essere sovraccaricato. Penso che questo sia dovuto al fatto che ci sarà un effetto per la raccolta di dati inutili e la gestione della memoria, che è un potenziale buco di sicurezza nel mondo fortemente tipizzato di CLR.

Tuttavia, vediamo cosa è esattamente un operatore. Secondo il famoso libro di Jeffrey Richter , ogni linguaggio di programmazione ha una propria lista di operatori, che sono compilati in un metodo speciale, e CLR stesso non sa nulla degli operatori. Quindi vediamo cosa rimane esattamente dietro agli operatori + e += .

Vedi questo semplice codice:

 Decimal d = 10M; d = d + 10M; Console.WriteLine(d); 

Lasciare visualizzare il codice IL per queste istruzioni:

  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 

Ora vediamo questo codice:

 Decimal d1 = 10M; d1 += 10M; Console.WriteLine(d1); 

E IL-code per questo:

  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 

Sono uguali! Quindi l’operatore += è solo zucchero sintattico per il tuo programma in C # , e puoi semplicemente sovraccaricare + operatore.

Per esempio:

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

Questo codice verrà compilato ed eseguito correttamente come:

  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 

Aggiornare:

Secondo il tuo aggiornamento, come dice @EricLippert, dovresti davvero avere i vettori come object immutabile. Il risultato dell’aggiunta dei due vettori è un nuovo vettore, non il primo con dimensioni diverse.

Se, per qualche motivo, hai bisogno di cambiare il primo vettore, puoi usare questo sovraccarico (ma per quanto mi riguarda, questo è un comportamento molto strano):

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

Ciò è dovuto alla stessa ragione per cui l’operatore di assegnazione non può essere sovraccaricato. Non è ansible scrivere codice che esegua correttamente il compito.

 class Foo { // Won't compile. public static Foo operator= (Foo c1, int x) { // duh... what do I do here? I can't change the reference of c1. } } 

Gli operatori di assegnazione non possono essere sovraccaricati, ma + =, ad esempio, viene valutato usando +, che può essere sovraccaricato.

Da MSDN .

Non puoi sovraccaricare += perché non è davvero un operatore unico, è solo zucchero sintattico . x += y è solo un modo abbreviato di scrivere x = x + y . Poiché += è definito in termini di operatori + e = , consentendo di sovrascriverlo separatamente potrebbe creare problemi, nei casi in cui x += y e x = x + y non si sono comportati nello stesso modo.

A un livello inferiore, è molto probabile che il compilatore C # compili entrambe le espressioni nello stesso bytecode, il che significa che è molto probabile che il runtime non possa trattarle in modo diverso durante l’esecuzione del programma.

Posso capire che potresti volerlo trattare come un’operazione separata: in una dichiarazione come x += 10 sai che puoi mutare l’object x in posizione e forse risparmiare un po ‘di tempo / memoria, piuttosto che creare un nuovo object x + 10 prima di assegnarlo al vecchio riferimento.

Ma considera questo codice:

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

Dovrebbe a == b alla fine? Per la maggior parte dei tipi, no, a è 10 più di b . Ma se potessi sovraccaricare l’operatore += per mutare in posizione, allora sì. Ora considera che a e b potrebbero essere passati in giro per parti distanti del programma. La tua ansible ottimizzazione potrebbe creare bug confusi se il tuo object inizia a cambiare dove il codice non se lo aspetta.

In altre parole, se le prestazioni sono così importanti, non è troppo difficile sostituire x += 10 con una chiamata al metodo come x.increaseBy(10) , ed è molto più chiara per tutte le persone coinvolte.

Penso che troverai questo link informativo: Operatori sovraccarichi

Gli operatori di assegnazione non possono essere sovraccaricati, ma + =, ad esempio, viene valutato usando +, che può essere sovraccaricato.

Questo perché questo operatore non può essere sovraccaricato:

Gli operatori di assegnazione non possono essere sovraccaricati, ma + =, ad esempio, viene valutato usando +, che può essere sovraccaricato.

MSDN

Solo overload + operatore, a causa di

x += y uguale a x = x + y

Il sovraccarico dell’operatore per + è usato in += operatore, A += B uguale a A = operator+(A, B) .

Se sovraccarichi + operatore in questo modo:

 class Foo { public static Foo operator + (Foo c1, int x) { // implementation } } 

tu puoi fare

  Foo foo = new Foo(); foo += 10; 

o

  foo = foo + 10; 

Questo verrà compilato ed eseguito ugualmente.

C’è sempre la stessa risposta a questo problema: Perché hai bisogno del += , se lo ottieni gratuitamente se sovraccarichi il + . Ma cosa succede se ho una class come questa.

 using System; using System.IO; public class Class1 { public class MappableObject { FileStream stream; public int Blocks; public int BlockSize; public MappableObject(string FileName, int Blocks_in, int BlockSize_in) { Blocks = Blocks_in; BlockSize = BlockSize_in; // Just create the file here and set the size stream = new FileStream(FileName); // Here we need more params of course to create a file. stream.SetLength(sizeof(float) * Blocks * BlockSize); } public float[] GetBlock(int BlockNo) { long BlockPos = BlockNo * BlockSize; stream.Position = BlockPos; using (BinaryReader reader = new BinaryReader(stream)) { float[] resData = new float[BlockSize]; for (int i = 0; i < BlockSize; i++) { // This line is stupid enough for accessing files a lot and the data is large // Maybe someone has an idea to make this faster? I tried a lot and this is the simplest solution // for illustration. resData[i] = reader.ReadSingle(); } } retuen resData; } public void SetBlock(int BlockNo, float[] data) { long BlockPos = BlockNo * BlockSize; stream.Position = BlockPos; using (BinaryWriter reader = new BinaryWriter(stream)) { for (int i = 0; i < BlockSize; i++) { // Also this line is stupid enough for accessing files a lot and the data is large reader.Write(data[i]; } } retuen resData; } // For adding two MappableObjects public static MappableObject operator +(MappableObject A, Mappableobject B) { // Of course we have to make sure that all dimensions are correct. MappableObject result = new MappableObject(Path.GetTempFileName(), A.Blocks, A.BlockSize); for (int i = 0; i < Blocks; i++) { float[] dataA = A.GetBlock(i); float[] dataB = B.GetBlock(i); float[] C = new float[dataA.Length]; for (int j = 0; j < BlockSize; j++) { C[j] = A[j] + B[j]; } result.SetBlock(i, C); } } // For adding a single float to the whole data. public static MappableObject operator +(MappableObject A, float B) { // Of course we have to make sure that all dimensions are correct. MappableObject result = new MappableObject(Path.GetTempFileName(), A.Blocks, A.BlockSize); for (int i = 0; i < Blocks; i++) { float[] dataA = A.GetBlock(i); float[] C = new float[dataA.Length]; for (int j = 0; j < BlockSize; j++) { C[j] = A[j] + B; } result.SetBlock(i, C); } } // Of course this doesn't work, but maybe you can see the effect here. // when the += is automimplemented from the definition above I have to create another large // object which causes a loss of memory and also takes more time because of the operation -> altgough its // simple in the example, but in reality it's much more complex. public static MappableObject operator +=(MappableObject A, float B) { // Of course we have to make sure that all dimensions are correct. MappableObject result = new MappableObject(Path.GetTempFileName(), A.Blocks, A.BlockSize); for (int i = 0; i < Blocks; i++) { float[] dataA = A.GetBlock(i); for (int j = 0; j < BlockSize; j++) { A[j]+= + B; } result.SetBlock(i, A); } } } } 

Dici ancora che è positivo che il += sia "auto-implementato". Se si tenta di eseguire un calcolo ad alte prestazioni in C # è necessario disporre di tali funzionalità per ridurre il tempo di elaborazione e il consumo di memoria, se qualcuno ha una buona soluzione è molto apprezzato, ma non dirmi che devo farlo con metodi statici , questa è solo una soluzione alternativa e non vedo alcun motivo per cui C # implementa += se non è definito, e se è definito verrà usato. Alcuni dicono che non avere una differenza tra + e += impedisce errori, ma non è questo il mio problema?

Ho avuto la stessa identica domanda e non posso assolutamente rispondere meglio di questa persona