VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > Python基础教程 >
  • C#教程之C# 遍历Dictionary并修改其中的Value

C#的Dictionary类型的值,知道key后,value可以修改吗?答案是肯定能修改的。我在遍历的过程中可以修改Value吗?答案是也是肯定能修改的,但是不能用For each循环。否则会报以下的Exception.

System.InvalidOperationException: 'Collection was modified; enumeration operation may not execute.'

之所以会报Exception是For each本身的问题,和Dictionary没关系。For each循环不能改变集合中各项的值,如果需要迭代并改变集合项中的值,请用For循环。

大家来看下例子:

复制代码
 1             // defined the Dictionary variable
 2             Dictionary<int, string> td = new Dictionary<int, string>();
 3             td.Add(1, "str1");
 4             td.Add(2, "str2");
 5             td.Add(3, "str3");
 6             td.Add(4, "str4");
 7             // test for
 8             TestForDictionary(td);
 9             // test for each
10             TestForEachDictionary(td);
复制代码
TestForDictionary Code
复制代码
1         static void TestForDictionary(Dictionary<int, string> paramTd)
2         {
3             
4             for (int i = 1;i<= paramTd.Keys.Count;i++)
5             {
6                 paramTd[i] = "string" + i;
7                 Console.WriteLine(paramTd[i]);
8             }
9         }
复制代码
TestForDictionary的执行结果
string1
string2
string3
string4

TestForEachDictionary Code
复制代码
 1         static void TestForEachDictionary(Dictionary<int, string> paramTd)
 2         {
 3             int forEachCnt = 1;
 4             foreach (KeyValuePair<int,string> item in paramTd)//System.InvalidOperationException: 'Collection was modified; enumeration operation may not execute.'
 5             {
 6                 paramTd[item.Key] = "forEach" + forEachCnt;
 7                 Console.WriteLine(paramTd[item.Key]);
 8                 forEachCnt += 1;
 9             }
10         }
复制代码
TestForEachDictionary里的For each会在循环第二次的时候报错,也就是说它会在窗口中打印出“forEach1”后断掉。
---------------------------------------------------------------------------------------------------------------------------------------------------
为什么For each循环不能改变集合中各项的值
 首先,我们知道要使用Foreach in语句需要满足下列条件:
 1.迭代集合实现了System.Collections.IEnumerable或者System.Collections.Generic.IEnumerable<T>接口;
 2.有public Enumerator GetEnumerator();方法
 3.GetEnumerator的返回类型是Enumerator,那就必须有Current属性和MoveNext方法
 其实1,2,3是一个东西,关联性很强。
 我迭代的集合是Dictionary,我把Dictionary的代码片段贴出来:
复制代码
1     [ComVisible(false)]
2     [DebuggerDisplay("Count = {Count}")]
3     [DebuggerTypeProxy(typeof(Generic.Mscorlib_DictionaryDebugView<,>))]
4     [DefaultMember("Item")]
5     public class Dictionary<TKey, TValue> : IDictionary<TKey, TValue>, ICollection<KeyValuePair<TKey, TValue>>, IEnumerable<KeyValuePair<TKey, TValue>>, IEnumerable, IDictionary, ICollection, IReadOnlyDictionary<TKey, TValue>, IReadOnlyCollection<KeyValuePair<TKey, TValue>>, ISerializable, IDeserializationCallback
6     {
7     ...
8     }
复制代码

Dictionary也有无参公共方法GetEnumerator

复制代码
1         //
2         // Summary:
3         //     Returns an enumerator that iterates through the System.Collections.Generic.Dictionary`2.
4         //
5         // Returns:
6         //     A System.Collections.Generic.Dictionary`2.Enumerator structure for the System.Collections.Generic.Dictionary`2.
7         public Enumerator GetEnumerator();
复制代码

其实最主要的是Enumerator接口,我们看看它的代码:

复制代码
 1 namespace System.Collections
 2 {
 3     //
 4     // Summary:
 5     //     Supports a simple iteration over a non-generic collection.
 6     [ComVisible(true)]
 7     [Guid("496B0ABF-CDEE-11d3-88E8-00902754C43A")]
 8     public interface IEnumerator
 9     {
10         //
11         // Summary:
12         //     Gets the element in the collection at the current position of the enumerator.
13         //
14         // Returns:
15         //     The element in the collection at the current position of the enumerator.
16         object Current { get; }
17  
18         //
19         // Summary:
20         //     Advances the enumerator to the next element of the collection.
21         //
22         // Returns:
23         //     true if the enumerator was successfully advanced to the next element; false if
24         //     the enumerator has passed the end of the collection.
25         //
26         // Exceptions:
27         //   T:System.InvalidOperationException:
28         //     The collection was modified after the enumerator was created.
29         bool MoveNext();
30         //
31         // Summary:
32         //     Sets the enumerator to its initial position, which is before the first element
33         //     in the collection.
34         //
35         // Exceptions:
36         //   T:System.InvalidOperationException:
37         //     The collection was modified after the enumerator was created.
38         void Reset();
39     }
40 }
复制代码

大家看到了,object Current { get; }是我们需要修改的项,只有get方法,所以它是只读的。


上面是从代码方面解释foreach迭代时不可改变集合项,还有一个原因是是你改变值类型的值时,它对应的栈地址也就相应的改变了,这个大家都知道,我就不多说了。那么问题来了,如果我不改变变量的地址,是不是我就可以用Foreach改变集合项呢?答案是肯定的。可以看下边MSDN的代码:

复制代码
 1 static void ModifyDictionaryValueForEach()
 2  {
 3      Span<int> storage = stackalloc int[10];
 4      int num = 0;
 5      foreach (ref int item in storage)
 6      {
 7          item = num++;
 8      }
 9  
10      foreach (ref readonly var item in storage)
11      {
12          Console.Write($"{item} ");
13      }
14      // Output:
15      // 0 1 2 3 4 5 6 7 8 9
16  }
复制代码

上边的代码必须是在C#版本是7.3下运行.Span<T>是.net Core2.1里的类型。然后使用ref就可以改变了。大家可以试试。


相关教程