7

最近我在使用通用字典时遇到了以下异常

发生了 InvalidOperationException。集合已修改

我意识到这个错误主要是因为我使用的静态字典的线程安全问题。

一点背景:我目前有一个应用程序,它有 3 种与此问题相关的不同方法。

  1. foreach方法 A 使用并返回一个值遍历字典。
  2. 方法 B 将数据添加到字典中。
  3. 方法 C 更改字典中键的值。

有时在遍历字典时,还会添加数据,这就是导致此问题的原因。foreach我在迭代字典内容的代码部分中不断收到此异常。为了解决这个问题,我用 替换了通用字典,ConcurrentDictionary这里是我所做的详细信息。

目标:我的主要目标是完全消除异常

对于方法 B(向字典添加新键),我替换.AddTryAdd

对于方法 C(更新字典的值),我没有做任何更改。代码的粗略草图如下:

  static public int ChangeContent(int para)
  {
      foreach (KeyValuePair<string, CustObject> pair in static_container)
      {
             if (pair.Value.propA != para ) //Pending cancel 
             {
                pair.Value.data_id = prim_id;    //I am updating the content
                return 0;

             }
      }
     return -2;
  }

对于方法 A - 我只是在字典上进行迭代,这是运行代码停止的地方(在调试模式下),Visual Studio 告诉我这是发生错误的地方。我使用的代码类似于以下

    static public CustObject RetrieveOrderDetails(int para)
    {
            foreach (KeyValuePair<string, CustObject> pair in static_container)
            {                   
                if (pair.Value.cust_id.Equals(symbol))
                {
                    if (pair.Value.OrderStatus != para) 
                    {
                       return pair.Value; //Found
                    }
                }
            }
            return null; //Not found
    }

这些更改是否会解决我遇到的异常。

编辑:

它在此页面上声明该方法GetEnumerator允许您在写入的同时遍历元素(尽管它可能已过时)。这和使用 foreach 不一样吗?

4

2 回答 2

3

对于元素的修改,一种选择是使用 for 循环手动迭代字典,例如:

Dictionary<string, string> test = new Dictionary<string, string>();
int dictionaryLength = test.Count();

for (int i = 0; i < dictionaryLength; i++)
{
    test[test.ElementAt(i).Key] = "Some new content";
}

但请注意,如果您还添加到 Dictionary 中,则必须适当地增加 dictionaryLength(或者如果您移动元素则减少它)。

根据您正在做什么,如果顺序很重要,您可能希望使用 SortedDictionary 代替。

您可以通过在每次迭代中调用 test.Count() 显式更新 dictionaryLength 来扩展它,并且还可以使用包含您已经修改的键列表的附加列表等等,如果有丢失任何键的危险,它真的取决于你在做什么以及你的需求是什么。

您可以使用 test.Keys.ToList() 进一步获取密钥列表,该选项的工作方式如下:

Dictionary<string, string> test = new Dictionary<string, string>();
List<string> keys = test.Keys.ToList();
foreach (string key in keys)
{
    test[key] = "Some new content";
}

IEnumerable<string> newKeys = test.Keys.ToList().Except(keys);

if(newKeys.Count() > 0)
    // Do it again or whatever.

请注意,我还展示了一个示例,说明如何确定在获取初始键列表和完成迭代之间是否添加了任何新键,以便您可以循环并处理新键。

希望这些选项中的一个适合(或者您甚至可能想要混合和匹配键上的循环,例如在您进行时更新它而不是长度) - 正如我所说,这与您正在尝试的确切内容一样多尽可能多地做任何事情。

于 2013-02-20T11:08:31.563 回答
1

foreach()尝试将容器复制到新实例之前

var unboundContainer = static_container.ToList();
foreach (KeyValuePair<string, CustObject> pair in unboundContainer)

另外我认为Value从线程安全的角度来看更新属性是不正确的,请重构您的代码以TryUpdate()代替使用。

于 2013-02-20T09:35:13.463 回答