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”后斷掉。
---------------------------------------------------------------------------------------------------------------------------------------------------
為什么foreach循環不能改變Dictionary中Key對應的Value?
 首先,我們知道要使用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<TKey,TValue>.
4         //
5         // Returns:
6         //     A System.Collections.Generic.Dictionary<TKey,TValue>.Enumerator structure
7         //     for the System.Collections.Generic.Dictionary<TKey,TValue>.
8         public Dictionary<TKey, TValue>.Enumerator GetEnumerator();

我們再看看Dictionary<TKey, TValue>.Enumerator的代碼

 1         // Summary:
 2         //     Enumerates the elements of a System.Collections.Generic.Dictionary<TKey,TValue>.
 3         [Serializable]
 4         public struct Enumerator : IEnumerator<KeyValuePair<TKey, TValue>>, IDisposable, IDictionaryEnumerator, IEnumerator
 5         {
 6 
 7             // Summary:
 8             //     Gets the element at the current position of the enumerator.
 9             //
10             // Returns:
11             //     The element in the System.Collections.Generic.Dictionary<TKey,TValue> at
12             //     the current position of the enumerator.
13             public KeyValuePair<TKey, TValue> Current { get; }
14 
15             // Summary:
16             //     Releases all resources used by the System.Collections.Generic.Dictionary<TKey,TValue>.Enumerator.
17             public void Dispose();
18             //
19             // Summary:
20             //     Advances the enumerator to the next element of the System.Collections.Generic.Dictionary<TKey,TValue>.
21             //
22             // Returns:
23             //     true if the enumerator was successfully advanced to the next element; false
24             //     if the enumerator has passed the end of the collection.
25             //
26             // Exceptions:
27             //   System.InvalidOperationException:
28             //     The collection was modified after the enumerator was created.
29             public bool MoveNext();
30         }

讓我們看看,我們能否修改KeyValuePair的值

 1     public struct KeyValuePair<TKey, TValue>
 2     {
 3         //
 4         // Summary:
 5         //     Initializes a new instance of the System.Collections.Generic.KeyValuePair<TKey,TValue>
 6         //     structure with the specified key and value.
 7         //
 8         // Parameters:
 9         //   key:
10         //     The object defined in each key/value pair.
11         //
12         //   value:
13         //     The definition associated with key.
14         public KeyValuePair(TKey key, TValue value);
15 
16         // Summary:
17         //     Gets the key in the key/value pair.
18         //
19         // Returns:
20         //     A TKey that is the key of the System.Collections.Generic.KeyValuePair<TKey,TValue>.
21         public TKey Key { get; }
22         //
23         // Summary:
24         //     Gets the value in the key/value pair.
25         //
26         // Returns:
27         //     A TValue that is the value of the System.Collections.Generic.KeyValuePair<TKey,TValue>.
28         public TValue Value { get; }
29 
30         // Summary:
31         //     Returns a string representation of the System.Collections.Generic.KeyValuePair<TKey,TValue>,
32         //     using the string representations of the key and value.
33         //
34         // Returns:
35         //     A string representation of the System.Collections.Generic.KeyValuePair<TKey,TValue>,
36         //     which includes the string representations of the key and value.
37         public override string ToString();
38     }

大家看到了,TValue Value { 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就可以改變了。大家可以試試。

 

 

//        // Summary:        //     Returns an enumerator that iterates through the System.Collections.Generic.Dictionary<TKey,TValue>.        //        // Returns:        //     A System.Collections.Generic.Dictionary<TKey,TValue>.Enumerator structure        //     for the System.Collections.Generic.Dictionary<TKey,TValue>.        public Dictionary<TKey, TValue>.Enumerator GetEnumerator();


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2024 CODEPRJ.COM