1

次のコードを考えると、次の例外を回避する方法を知りたいです

System.InvalidOperationException was unhandled
Message=Collection was modified; enumeration operation may not execute.
Source=mscorlib
StackTrace:
   at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)
   at System.Collections.Generic.List`1.Enumerator.MoveNextRare()
   at System.Collections.Generic.List`1.Enumerator.MoveNext()
   at PBV.Program.Main(String[] args) in C:\Documents and Settings\tmohojft\Local Settings\Application Data\Temporary Projects\PBV\Program.cs:line 39
   at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
   at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
   at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
   at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ThreadHelper.ThreadStart()
InnerException: 

コード:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace PBV
{
class Program
{
    struct structItem
    {
        public int y { get; set; }
        public int z { get; set; }
    }

    struct testStruct
    {
        public int x { get; set; }
        public List<structItem> items { get; set; }
    }

    static void Main(string[] args)
    {
        testStruct a = new testStruct();
        structItem b = new structItem();

        for (byte i = 0; i <= 10; i++) {
            b.y = i;
            b.z = i * 2;
            a.items = new List<structItem>();
            a.items.Add(b);
        }

        testStruct c = new testStruct();
        c = a;

        int counter = 0;

        //exception thrown on line below
        foreach (var item in a.items) {
            structItem d = item;
            d.z = 3;

            c.items[counter] = d;
            counter++;
        }

        a = c;
    }
}
}

私はもともと、2番目のforeachに次のように単純に入れようとしました:

item.z = 3;

しかし、それは次のエラーを引き起こしました:

Cannot modify members of "item" because it is a "foreach iteration" 

foreach 内の構造体データを変更できるようにするために一時オブジェクトを作成しようとしましたが、上記の例外が発生します。私の最善の推測は、一時構造体が値自体ではなく元の構造体への参照を保存しているためです。これにより、元の構造体が一時構造体のときに更新されます。

だから私の質問は: この構造体を参照ではなく値で渡すにはどうすればよいですか? または、この問題を回避するためのまったく別の方法はありますか?

助けてくれてありがとう。

編集:すべての回答に感謝します。リストが参照型であることは承知していますが、その場合、参照ではなく値で渡すことはできませんか?

4

4 に答える 4

5

サンプルコードが何をしようとしているのか少し混乱していますが、混乱の一部は、 を設定c = aすると、リストのコピーを作成することを期待している可能性があると思います。そうではありません。構造体自体は値型ですが、そこに含まれる items プロパティは値型ではありません。List<> は参照型なので、 を設定すると、その参照が cc = aにコピーされます。itemsしたがって、ループに入ると、a と c の両方に同じリスト オブジェクトへの参照が含まれます。したがって、列挙中にリストを変更すると、常に失敗します。

これを回避する簡単な方法は、リストの静的コピーを反復処理することです。

foreach (var item in a.items.ToArray())
于 2012-05-18T18:10:41.120 に答える
2

a.Itemsでありc.Items、まったく同じ List インスタンスです。代わりに、昔ながらのforループを使用して反復することができます。そうすれば、列挙子を使用していないため、必要に応じてリストを変更できます。

for (int i = 0; i < a.Items.Count; i++)
{
    c.Items[i] = whatever;
}

とにかくカウンターを保持しているので、これは自然な方法のようです。パフォーマンス上の理由から、項目を追加または削除するつもりがない場合は、リスト サイズをローカル変数に格納することをお勧めします。

于 2012-05-18T18:26:32.923 に答える
1

一部のアプリケーションでは、可変値型が最も適切なセマンティクスを提供します。値型がall-field-copy( *)以外のミューテーションの方法をサポートしているかどうかに関係なく、 aの固有の意味は、多くの場合、 ( )List<someValueType>の意味よりもはるかに明確です。残念ながら、のような組み込みコレクションは、all-field-copy以外の格納されたアイテムを変更する手段を公開していないため、実際には変更可能な値型を十分にサポートしていません。List<someMutableClassType>List<T>

多くの場合、使用せずList<T>に、単にaT[]とアイテム数を保持するのが最善であると思います。コレクションにアイテムを追加する場合は、カウントが長さ以上であるかどうかを確認してください。もしそうなら、長さを2倍にします。次に、カウントで示されたスロットに新しいアイテムを保存し、カウントを増やします。

本当にを使用したい場合はList<someStructType>、他の人が提案したアプローチを使用して、を使用するのではなく、インデックスで整数ループを使用して変更する項目を繰り返すことができますforeach。ただし、可変構造体を使用する場合は、myArray[i].X = i+4;代わりに言うことができるので、var temp=myList[i]; temp.X = i+4; myList[i] = temp;配列の拡張を処理するのは少し面倒です。

(*)aの意味は、List<MutableClassType>各リストアイテムが個別のオブジェクトインスタンスへの唯一の参照を保持しているかどうか、または同じデータを保持しているインスタンスを共有できるかどうかによって異なります。。にはそのようなあいまいさはありませんList<StructNotContainingMutableClassReferences>

(**)XYが同じ構造体タイプの格納場所である場合S、ステートメントX = YはすべてのパブリックフィールドとプライベートフィールドをYからXに任意の順序でコピーします。このステートメントは、すべてのフィールドが空白X = new S(params);の構造体タイプの新しいインスタンスを作成し、Sその上でコンストラクターを呼び出してから、すべてのフィールドをその新しいインスタンスからにコピーしますX。したがって、値型インスタンスがデフォルト以外の値を保持できるフィールドがある場合、インスタンスが変更可能な保存場所に保存されていれば、それらのフィールドは変更可能になります。不変の場所に格納されている値型インスタンスのすべてのフィールドは不変になります。値型を「可変」または「不変」にする唯一のことは、その内容の変更のしやすさに影響を与えることです。

于 2012-05-30T22:30:12.673 に答える
1

可変値オブジェクト (構造体) は、不変よりも扱いがはるかに困難です。構造体を不変にして、コードを再設計することを検討してください。

利点と問題点がどこにあるかを正確に把握していない限り、構造体をまったく使用しない方が簡単な場合がよくあります。代わりにコードをクラスに切り替えて、パフォーマンスが目標を満たしているかどうかを測定してください。

注:c = a;は浅いコピーです (実際には の 2 番目のコピーを作成するわけではありませんがtestStruct、を参照しaます)。ディープ コピーが必要なようです。代わりにコンストラクターにして、参照する代わりに配列をコピーすることを検討してください。

于 2012-05-18T18:28:11.150 に答える