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

我最初试图简单地将以下内容放在第二个 foreach 中:

item.z = 3;

但这导致了以下错误:

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

我试图创建一个临时对象以便能够修改 foreach 中的结构数据,但我收到了上面的异常。我最好的猜测是因为我的临时结构正在保存对原始结构的引用而不是值本身 - 这导致我的原始结构在我的临时结构被更新时被更新。

所以我的问题是: 我如何通过值而不是引用传递这个结构?还是有完全不同的方法来解决这个问题?

在此先感谢您的帮助。

编辑:感谢所有的答案家伙。我知道该列表是一个引用类型,但这是否使得不可能通过值而不是引用传递呢?

4

4 回答 4

5

我对您的示例代码尝试执行的操作感到有些困惑,但我认为您的部分困惑可能是当您设置时c = a,您希望它会复制列表。它不是。虽然结构本身是值类型,但它包含的 items 属性不是。List<> 是一个引用类型,所以当你设置 时c = a,它会将该引用复制items到 c。因此,当您进入循环时, 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

可变值对象(结构)比不可变更难处理。考虑使结构不可变并围绕它重新设计代码。

除非您确切知道好处和痛点在哪里,否则根本不使用 struct 通常更容易。尝试将您的代码改为类并衡量性能是否符合您的目标。

注意:c = a;是浅拷贝(根本不是真正制作你testStruct的第二个副本,而是指a),看起来你想要深拷贝 - 考虑改为构造函数并复制数组而不是引用它。

于 2012-05-18T18:28:11.150 回答
1

在某些应用程序中,可变值类型提供了最合适的语义。无论值类型是否支持除 all-field-copy( *)之外的任何变异方法, a 的内在含义List<someValueType>通常比List<someMutableClassType>( ) 更清晰。不幸的是,像这样的内置集合确实不能很好地支持可变值类型,因为它们除了所有字段复制之外没有公开任何改变存储项的方法。List<T>

我建议在许多情况下,您最好不要使用List<T>,而是简单地保留一个T[]和一个项目计数。如果要向集合中添加一个项目,请检查计数是否大于或等于长度;如果是这样,则将长度加倍。然后将新项目存储在计数指示的槽中并增加计数。

如果你真的想使用 a List<someStructType>,你可以使用其他人建议的方法,通过在索引上使用整数循环来迭代要修改的项目,而不是使用 a 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所有字段为空的 struct 类型的新实例,在其上调用构造函数,然后将该新实例中的所有字段复制到X. 因此,如果值类型实例的任何字段可以保存非默认值,那么如果实例存储在可变存储位置,这些字段将是可变的。存储在不可变位置的值类型实例的所有字段都是不可变的。使值类型“可变”或“不可变”的唯一作用是影响更改其内容的难易程度。

于 2012-05-30T22:30:12.673 回答