web-dev-qa-db-ja.com

foreachループで代入演算子(=)が無効なのはなぜですか?

foreachループで代入演算子(=)が無効なのはなぜですか?私はC#を使用していますが、引数はforeachをサポートする他の言語(PHPなど)でも同じであると思います。たとえば、私がこのようなことをした場合:

string[] sArray = new string[5];

foreach (string item in sArray)
{
   item = "Some assignment.\r\n";
}

「 'foreach反復変数'であるため、 'item'に割り当てることができません」というエラーが表示されます。

26
Jim Fell

コードは次のとおりです。

foreach (string item in sArray)
{
   item = "Some assignment.\r\n";
}

これが 大まかな概算 コンパイラがこれを使って行うことの:

using (var enumerator = sArray.GetEnumerator())
{
    string item;
    while (enumerator.MoveNext())
    {
        item = enumerator.Current;

        // Your code gets put here
    }
}

IEnumerator<T>.Currentプロパティは読み取り専用ですが、ローカルのitem変数を新しい値に割り当てようとしているため、ここでは実際には関係ありません。これを防ぐコンパイル時チェックは、基本的に、期待どおりに機能しないこと(つまり、ローカル変数を変更し、基になるコレクション/シーケンスに影響を与えないこと)からユーザーを保護するために行われます。

列挙中にstring[]などのインデックス付きコレクションの内部を変更する場合、従来の方法は、forの代わりにforeachループを使用することです。

for (int i = 0; i < sArray.Length; ++i)
{
    sArray[i] = "Some assignment.\r\n";
}
62
Dan Tao

foreachループは、物事を割り当てるのではなく、コレクション内のオブジェクトを反復処理するように設計されています。これは単に言語の設計です。

また、MSDNから:

「このエラーは、変数への割り当てが読み取り専用コンテキストで発生した場合に発生します。読み取り専用コンテキストには、foreach反復変数、using変数、および固定変数が含まれます。このエラーを解決するには、ブロック、foreachステートメントを使用する際にステートメント変数への割り当てを避けてください。 、および固定ステートメント。」

Foreachキーワードは、IEnumerableインスタンスを列挙するだけです(GetEnumerator()メソッドを呼び出してIEnumeratorインスタンスを取得します)。 IEnumeratorは読み取り専用であるため、IEnumeratorを使用して値を変更することはできません= foreachコンテキストを使用して変更することはできません。

5
Dominic K

言語仕様がそう言っているからです。

しかし、真剣に、すべてのシーケンスが配列または論理的に変更または書き込み可能なものであるとは限りません。例えば:

foreach (var i in Enumerable.Range(1, 100)) {
   // modification of `i` will not make much sense here.
}

i = something;でローカル変数を変更することは技術的には可能でしたが、誤解を招く可能性があります(実際に内部で何かを変更すると思うかもしれませんが、そうではありません)。

これらの種類のシーケンスをサポートするために、IEnumerable<T>はそのsetプロパティにCurrentアクセサーを必要とせず、読み取り専用にします。したがって、foreachは、Currentプロパティを使用して基になるコレクション(存在する場合)を変更できません。

5
Mehrdad Afshari

foreachループを使用して、ループしている配列を変更することはできないためです。ループiteratesは配列を通過するため、反復する内容を変更しようとすると、予期しない動作が発生する可能性があります。さらに、DarinとDManが指摘しているように、それ自体が読み取り専用であるIEnumerableを繰り返し処理しています。

PHPは、そのforeachループで配列のコピーを作成し、参照を使用しない限り、そのコピーを繰り返し処理します。参照を使用する場合は、配列自体を変更します。

4
BoltClock

IEnumerableは読み取り専用だからです。

2
Darin Dimitrov

一般に、これを実行しようとしている場合は、おそらく最良の構造を使用していないため、設計について長く真剣に考える必要があります。この場合、最良の答えはおそらく

string[] sArray = Enumerable.Repeat("Some assignment.\r\n", 5).ToArray();

C#のこの種のループの代わりに、ほとんどの場合、より高いレベルの構造を使用できます。 (そしてC++ですが、それはまったく別のトピックです)

2
Billy ONeal

Foreachを実行している配列を変更することはできません。代わりに次のコードを使用してください。

string[] sArray = new string[5]; 

for (int i=0;i<sArray.Length;i++)
{
    item[i] = "Some Assignment.\r\n";
}
1
Icemanind

それを変更させることは完全に可能です。しかし、これはどういう意味ですか?基になる列挙が変更されたように読み取られますが、変更されていません(それを許可することも可能ですが、それ自体に欠点があります)。

したがって、実際に起こったこと以外の何かを示すものとして人々が自然に読むコードがあります。コンピュータ言語の目的が主に人々に理解されることであることを考えると(コンパイラ、マシンコード、または適切な名前のBrainf ** kを使用しない限り、コンパイラはそれらに対して設定されたバランスを処理します)、これは欠陥を示します。言語。

0
Jon Hanna

Foreachは、繰り返したりスキップしたりせずに、配列を1回相互作用するように設計されています(ただし、continueキーワードを使用すると、foreachコンストラクト内の一部のアクションをスキップできます)。現在のアイテムを変更する場合は、代わりにforループの使用を検討してください。

0
Andy

「ForEach」を介してループスルーされているリストを変更することはできません。

最良のオプションは、使用したいアイテムを保存するための一時的なリストを作成することです。

0
Jamie Keeling