Li ho visti entrambi in uso in numerosi pezzi di codice C #, e mi piacerebbe sapere quando usare i++o ++i( iessendo una variabile numero come int, float, double, ecc). Qualcuno che lo sa?
Li ho visti entrambi in uso in numerosi pezzi di codice C #, e mi piacerebbe sapere quando usare i++o ++i( iessendo una variabile numero come int, float, double, ecc). Qualcuno che lo sa?
Risposte:
Stranamente sembra che le altre due risposte non lo spieghino, e vale sicuramente la pena dire:
i++significa "dimmi il valore di i, quindi incrementa"
++isignifica "incremento i, quindi dimmi il valore"
Sono operatori pre-incremento, post-incremento. In entrambi i casi la variabile viene incrementata , ma se si dovesse assumere il valore di entrambe le espressioni esattamente negli stessi casi, il risultato sarà diverso.
La risposta tipica a questa domanda, purtroppo già pubblicata qui, è che uno fa l'incremento "prima" delle operazioni rimanenti e l'altro fa l'incremento "dopo" le operazioni rimanenti. Anche se ciò rende intuitivamente l'idea, quella affermazione è completamente sbagliata . La sequenza di eventi nel tempo è estremamente ben definita in C #, e non è enfaticamente no vero che le versioni prefisso (++ var) e postfix (var ++) di ++ facciano le cose in un ordine diverso rispetto ad altre operazioni.
Non sorprende che vedrai molte risposte sbagliate a questa domanda. Molti libri "insegnati a C #" sbagliano anche. Inoltre, il modo in cui C # lo fa è diverso da come lo fa C. Molte persone ragionano come se C # e C fossero la stessa lingua; non sono. La progettazione degli operatori di incremento e decremento in C # secondo me evita i difetti di progettazione di questi operatori in C.
Esistono due domande a cui è necessario rispondere per determinare quali sono esattamente le operazioni di prefisso e postfisso ++ in C #. La prima domanda è: qual è il risultato? e la seconda domanda è quando ha luogo l'effetto collaterale dell'incremento?
Non è ovvio quale sia la risposta a nessuna delle due domande, ma in realtà è abbastanza semplice una volta che la vedi. Lascia che ti spieghi esattamente cosa fanno x ++ e ++ x per una variabile x.
Per il modulo prefisso (++ x):
Per il modulo postfix (x ++):
Alcune cose da notare:
Innanzitutto, l'ordine degli eventi nel tempo è esattamente lo stesso in entrambi i casi . Ancora, è assolutamente non il caso che l' ordine degli eventi in tempo varia tra prefisso e suffisso. È del tutto falso affermare che la valutazione avviene prima di altre valutazioni o dopo altre valutazioni. Le valutazioni avvengono esattamente nello stesso ordine in entrambi i casi, come si può vedere dai passaggi da 1 a 4 identici. L' unica differenza è l' ultimo passaggio : se il risultato è il valore del valore temporaneo o del nuovo valore incrementato.
Puoi dimostrarlo facilmente con una semplice app per console C #:
public class Application
{
public static int currentValue = 0;
public static void Main()
{
Console.WriteLine("Test 1: ++x");
(++currentValue).TestMethod();
Console.WriteLine("\nTest 2: x++");
(currentValue++).TestMethod();
Console.WriteLine("\nTest 3: ++x");
(++currentValue).TestMethod();
Console.ReadKey();
}
}
public static class ExtensionMethods
{
public static void TestMethod(this int passedInValue)
{
Console.WriteLine("Current:{0} Passed-in:{1}",
Application.currentValue,
passedInValue);
}
}
Ecco i risultati ...
Test 1: ++x
Current:1 Passed-in:1
Test 2: x++
Current:2 Passed-in:1
Test 3: ++x
Current:3 Passed-in:3
Nel primo test, puoi vedere che entrambi currentValuee ciò che è stato passato alTestMethod() all'estensione mostrano lo stesso valore, come previsto.
Tuttavia, nel secondo caso, le persone proveranno a dirti che l'incremento di currentValueavviene dopo la chiamata a TestMethod(), ma come puoi vedere dai risultati, accade prima della chiamata come indicato dal risultato "Corrente: 2".
In questo caso, innanzitutto il valore di currentValueviene memorizzato in un temporaneo. Successivamente, viene memorizzata una versione incrementata di quel valore, currentValuema senza toccare il temporaneo che conserva ancora il valore originale. Finalmente quel temporaneo è passato a TestMethod(). Se l'incremento si è verificato dopo la chiamata a TestMethod(), verrà scritto due volte lo stesso valore non incrementato, ma non lo è.
È importante notare che il valore restituito da entrambe le operazioni
currentValue++e++currentValuesi basa sul valore temporaneo e non sul valore effettivo memorizzato nella variabile al momento in cui entrambe le operazioni terminano.Richiama nell'ordine delle operazioni sopra, i primi due passaggi copiano il valore corrente della variabile nel temporaneo. Questo è ciò che viene utilizzato per calcolare il valore di ritorno; nel caso della versione del prefisso, è quel valore temporaneo incrementato mentre nel caso della versione del suffisso, è quel valore direttamente / non incrementato. La variabile stessa non viene letta nuovamente dopo la memorizzazione iniziale nel temporaneo.
In parole povere, la versione postfix restituisce il valore letto dalla variabile (ovvero il valore del temporaneo) mentre la versione prefisso restituisce il valore che è stato riscritto nella variabile (ovvero il valore incrementato del temporaneo). Né restituisce il valore della variabile.
Questo è importante da capire perché la variabile stessa potrebbe essere volatile e essere cambiata su un altro thread, il che significa che il valore di ritorno di tali operazioni potrebbe differire dal valore corrente memorizzato nella variabile.
È sorprendentemente comune che le persone si confondano molto con la precedenza, l'associatività e l'ordine in cui vengono eseguiti gli effetti collaterali, sospetto soprattutto perché è così confuso in C. C # è stato attentamente progettato per essere meno confuso in tutti questi aspetti. Per qualche ulteriore analisi di questi problemi, incluso me che dimostri ulteriormente la falsità dell'idea che le operazioni di prefisso e postfisso "spostano roba nel tempo" vedi:
https://ericlippert.com/2009/08/10/precedence-vs-order-redux/
che ha portato a questa domanda SO:
int [] arr = {0}; int value = arr [arr [0] ++]; Valore = 1?
Potresti anche essere interessato ai miei precedenti articoli sull'argomento:
https://ericlippert.com/2008/05/23/precedence-vs-associativity-vs-order/
e
https://ericlippert.com/2007/08/14/c-and-the-pit-of-despair/
e un caso interessante in cui C rende difficile ragionare sulla correttezza:
https://docs.microsoft.com/archive/blogs/ericlippert/bad-recursion-revisited
Inoltre, incontriamo problemi simili simili quando consideriamo altre operazioni che hanno effetti collaterali, come semplici incatenamenti:
https://docs.microsoft.com/archive/blogs/ericlippert/chaining-simple-assignments-is-not-so-simple
Ed ecco un post interessante sul perché gli operatori di incremento generano valori in C # anziché in variabili :
i++o ++iviene utilizzato nel codice, le cose che accadono in background sono proprio questo; sullo sfondo . Scrivo C # per salire ai livelli di astrazione al di sopra di quello che sta succedendo a questo livello, quindi se questo conta davvero per il tuo codice C #, potresti già essere nella lingua sbagliata.
i++;è) è in for (int i = 0; i < x; i++)... E sono molto felice di questo! (e non uso mai l'operatore prefisso). Se devo scrivere qualcosa che richiederà un programmatore senior 2 minuti per decifrare ... Beh ... È meglio scrivere un'altra riga di codice o introdurre una variabile temporanea :-) Penso che il tuo "articolo" (ho vinto "chiamalo" risposta ") conferma la mia scelta :-)
Se hai:
int i = 10;
int x = ++i;
allora xsarà 11.
Ma se hai:
int i = 10;
int x = i++;
allora xsarà 10.
Nota come sottolinea Eric, l'incremento avviene contemporaneamente in entrambi i casi, ma è il valore che viene dato in quanto il risultato differisce (grazie Eric!).
In generale, mi piace usare a ++imeno che non ci sia una buona ragione per non farlo. Ad esempio, quando scrivo un ciclo, mi piace usare:
for (int i = 0; i < 10; ++i) {
}
Oppure, se ho solo bisogno di incrementare una variabile, mi piace usare:
++x;
Normalmente, in un modo o nell'altro non ha molto significato e si riduce allo stile di codifica, ma se si utilizzano gli operatori all'interno di altri compiti (come nei miei esempi originali), è importante essere consapevoli dei potenziali effetti collaterali.
iper il nome della variabile piuttosto che varin quanto è una parola chiave C #.
int i = 0;
Console.WriteLine(i++); // Prints 0. Then value of "i" becomes 1.
Console.WriteLine(--i); // Value of "i" becomes 0. Then prints 0.
Questo risponde alla tua domanda?
Il modo in cui funziona l'operatore è che viene incrementato contemporaneamente, ma se è prima di una variabile, l'espressione valuterà con la variabile incrementata / decrementata:
int x = 0; //x is 0
int y = ++x; //x is 1 and y is 1
Se è dopo la variabile, l'istruzione corrente verrà eseguita con la variabile originale, come se non fosse stata ancora incrementata / decrementata:
int x = 0; //x is 0
int y = x++; //'y = x' is evaluated with x=0, but x is still incremented. So, x is 1, but y is 0
Concordo con dcp nell'uso del pre-incremento / decremento (++ x) se non necessario. Davvero l'unica volta in cui uso il post-incremento / decremento è in loop o loop di quel tipo. Questi loop sono gli stessi:
while (x < 5) //evaluates conditional statement
{
//some code
++x; //increments x
}
o
while (x++ < 5) //evaluates conditional statement with x value before increment, and x is incremented
{
//some code
}
Puoi anche farlo indicizzando matrici e simili:
int i = 0;
int[] MyArray = new int[2];
MyArray[i++] = 1234; //sets array at index 0 to '1234' and i is incremented
MyArray[i] = 5678; //sets array at index 1 to '5678'
int temp = MyArray[--i]; //temp is 1234 (becasue of pre-decrement);
Ecc, ecc ...
Solo per la cronaca, in C ++, se puoi usare uno dei due (cioè) non ti interessa l'ordine delle operazioni (vuoi solo incrementare o decrementare e usarlo in seguito) l'operatore prefisso è più efficiente poiché non lo fa devi creare una copia temporanea dell'oggetto. Sfortunatamente, la maggior parte delle persone usa posfix (var ++) invece del prefisso (++ var), proprio perché è quello che abbiamo imparato inizialmente. (Mi è stato chiesto di questo in un'intervista). Non sono sicuro che ciò sia vero in C #, ma suppongo che lo sarebbe.