私は両方のC#コードの数々の作品で使用されているそれらを見てきた、と私は使用したときに知りたいのですi++
か++i
(i
のような数の変数であることint
、float
、double
、など)。これを知っている人はいますか?
私は両方のC#コードの数々の作品で使用されているそれらを見てきた、と私は使用したときに知りたいのですi++
か++i
(i
のような数の変数であることint
、float
、double
、など)。これを知っている人はいますか?
回答:
奇妙なことに、他の2つの回答では詳しく説明されていないようです。
i++
「の値を教えてi
から、増分する」を意味します
++i
「インクリメントしi
、値を教えて」を意味します
これらは、プリインクリメント、ポストインクリメントの演算子です。 どちらの場合も変数はインクリメントされますが、両方の式の値をまったく同じ場合に取ると、結果は異なります。
この質問に対する典型的な回答は、残念ながらすでにここに投稿されていますが、一方は残りの操作の「前」の増分を行い、もう一方は残りの操作の「後」の増分を行います。これは直感的に理解を深めますが、その発言は完全に間違っています。時間のイベントのシーケンスは、 C#で、非常に明確に定義されており、それが強調されていない ++の接頭辞(++ VAR)と後置(VAR ++)のバージョンが他の操作に対して異なる順序で物事を行うことをケース。
この質問に対して多くの間違った回答が表示されるのは当然のことです。非常に多くの「C#を自分で教える」本も間違っています。また、C#が行う方法は、Cが行う方法とは異なります。多くの人々は、C#とCが同じ言語であるかのように考えています。ではない。私の意見では、C#のインクリメント演算子とデクリメント演算子の設計は、Cでのこれらの演算子の設計上の欠陥を回避します。
C#での接頭辞++と接尾辞++の操作を正確に判断するには、2つの質問に答える必要があります。最初の質問は結果は何ですか?2番目の質問は、増分の副作用がいつ発生するかです。
どちらの質問に対する答えが何であるかは明らかではありませんが、実際に見ると、それは実際には非常に簡単です。変数xに対してx ++と++ xが何をするかを正確に説明します。
プレフィックス形式(++ x)の場合:
Postfixフォーム(x ++)の場合:
注意すべき点:
まず、時間内のイベントの順序はどちらの場合もまったく同じです。ここでも、時間内のイベントの順序が前置と後置の間で変化することは絶対にありません。評価が他の評価の前または後に行われると言うのは完全に誤りです。手順1から4が同一であることからわかるように、評価は両方のケースでまったく同じ順序で行われます。唯一の違いは、最後のステップ -結果は、一時的、または新しい、インクリメント値の値であるかどうか。
簡単な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);
}
}
これが結果です...
Test 1: ++x
Current:1 Passed-in:1
Test 2: x++
Current:2 Passed-in:1
Test 3: ++x
Current:3 Passed-in:3
最初のテストでは、期待どおり、両方currentValue
とTestMethod()
拡張に渡されたものが同じ値を示していることがわかります。
ただし、2番目のケースでは、の呼び出し後にインクリメントがcurrentValue
発生することを伝えようとしますが、結果からわかるように、「Current:2」の結果で示されるように、呼び出しの前にインクリメントが発生します。TestMethod()
この場合、最初にの値がcurrentValue
一時的に格納されます。次に、その値のインクリメントされたバージョンがcurrentValue
、元の値をまだ格納している一時に触れずに、再び格納されます。最後に、その一時変数がに渡されTestMethod()
ます。呼び出し後にインクリメントが発生した場合、インクリメントTestMethod()
されていない同じ値が2回書き込まれますが、そうではありません。
これは、値は両方から返されることに注意することが重要だ
currentValue++
と++currentValue
操作が時間のいずれかの操作を終了時に変数に格納されている実際の値が一時的ではないに基づいています。上記の操作の順序で思い出してください。最初の2つのステップでは、変数の現在の値を一時変数にコピーします。これが戻り値の計算に使用されます。接頭辞バージョンの場合は、その一時的な値が増分されますが、接尾辞バージョンの場合は、その値は直接/増分されません。変数自体は、一時変数に最初に格納された後は再度読み込まれません。
簡単に言うと、postfixバージョンは変数から読み取られた値(つまり一時変数の値)を返し、prefixバージョンは変数に書き戻された値(つまり一時変数の増分された値)を返します。どちらも変数の値を返しません。
変数自体が揮発性であり、別のスレッドで変更されている可能性があるため、これらの操作の戻り値が変数に格納されている現在の値と異なる可能性があるため、これは理解することが重要です。
驚くべきことに、優先順位、結合性、副作用の実行順序について人々が非常に混乱することはよくありますが、Cでの混乱が主な原因であると考えられます。C#は、これらすべての点で混乱が少ないように注意深く設計されています。これらの問題のいくつかの追加の分析については、前置および後置操作が「時間内にものを移動する」という考えの偽りをさらに示す私を含めて、以下を参照してください。
https://ericlippert.com/2009/08/10/precedence-vs-order-redux/
このSOの質問につながった:
int [] arr = {0}; int値= arr [arr [0] ++]; 値= 1?
この件に関する私の以前の記事にも興味があるかもしれません。
https://ericlippert.com/2008/05/23/precedence-vs-associativity-vs-order/
そして
https://ericlippert.com/2007/08/14/c-and-the-pit-of-despair/
そして、Cが正しさについて推論するのを難しくする興味深いケース:
https://docs.microsoft.com/archive/blogs/ericlippert/bad-recursion-revisited
また、チェーンされた単純な割り当てなど、副作用のある他の操作を検討すると、同様の微妙な問題が発生します。
https://docs.microsoft.com/archive/blogs/ericlippert/chaining-simple-assignments-is-not-so-simple
そして、ここで、インクリメント演算子が変数ではなくC#の値になる理由についての興味深い投稿があります。
i++
または++i
コードで使用されている場合、バックグラウンドで行われていることはそれだけです。バックグラウンドで。私はC#を記述して、このレベルで何が起こっているかよりも抽象化レベルに上るようにしています。そのため、これが本当にC#コードにとって重要な場合は、すでに間違った言語になっている可能性があります。
i++;
)だけであるfor (int i = 0; i < x; i++)
...そして、私は非常に嬉しい!(そして、私はこれまでに前置演算子を使用していません)。上級のプログラマーが解読するのに2分かかるようなものを書く必要がある場合...うーん...もう1行のコードを書くか、一時的な変数を導入する方がいいです:-)私はあなたの「記事」(私が勝った)だと思います「答え」と呼ばないでください)私の選択を証明します:-)
あなたが持っている場合:
int i = 10;
int x = ++i;
その後にx
なります11
。
しかし、あなたが持っている場合:
int i = 10;
int x = i++;
その後にx
なります10
。
エリックが指摘しているように、増分は両方の場合に同時に発生しますが、結果として異なる値が与えられます(エリックに感謝します!)。
一般的には、使用++i
する正当な理由がない限り、使用するのが好きです。たとえば、ループを作成するときは、次のように使用します。
for (int i = 0; i < 10; ++i) {
}
または、変数をインクリメントするだけの場合は、次のように使用します。
++x;
通常、どちらか一方の方法ではあまり意味がなく、コーディングスタイルになりますが、(元の例のように)他の割り当て内で演算子を使用している場合は、潜在的な副作用に注意することが重要です。
i
なく変数名に使用しましたvar
。
演算子が機能する方法は、それが同時にインクリメントされることですが、それが変数の前にある場合、式はインクリメント/デクリメントされた変数で評価されます。
int x = 0; //x is 0
int y = ++x; //x is 1 and y is 1
変数の後にある場合、現在のステートメントは、まだインクリメント/デクリメントされていないかのように、元の変数で実行されます。
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
必要な場合を除いて、プリインクリメント/デクリメント(++ x)を使用する場合、dcpに同意します。実際、私がポストインクリメント/デクリメントを使用するのは、whileループまたはそのようなループのみです。これらのループは同じです:
while (x < 5) //evaluates conditional statement
{
//some code
++x; //increments x
}
または
while (x++ < 5) //evaluates conditional statement with x value before increment, and x is incremented
{
//some code
}
配列などのインデックスを作成するときにもこれを行うことができます。
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);
などなど...
参考までに、C ++では、いずれかを使用できる場合(つまり)操作の順序を気にしない場合(インクリメントまたはデクリメントして後で使用したいだけ)、前置演算子は効率的ではないため、より効率的です。オブジェクトの一時的なコピーを作成する必要があります。残念ながら、ほとんどの人は接頭辞(++ var)ではなくposfix(var ++)を使用しています。(私はインタビューでこれについて尋ねられました)。これがC#で当てはまるかどうかはわかりませんが、当てはまると思います。