zoukankan      html  css  js  c++  java
  • 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();

  • 相关阅读:
    【ESXI6.0】 ESXI6.0安装时无法安装网卡驱动的解决方法及将网卡驱动加载进ISO
    [转]Vs解决方案的目录结构设置和管理
    在控制台编译运行java程序详细指导
    详解Linux安装GCC方法
    MySQL server has gone away 问题的解决方法
    eclipse或Myeclipse中web项目没有run on server时怎么办?
    DTM/DEM/DSM/DOM/DLG
    linux常用命令
    为什么很多地方看到初始值是1970年8月1日
    mongoDB
  • 原文地址:https://www.cnblogs.com/forbetter223/p/10026113.html
Copyright © 2011-2022 走看看