今天在使用 Linq 的    ToDictionary() 时发生了异常,提示:  
System.ArgumentException: 已添加了具有相同键
因为以前一直没有遇到有相同键的情况,所以从来没关注过这个问题。然后写段试验来码来处理这个问题
csclass Program { public static void Main(string[] args) { var data = new[]{ Tuple.Create("001", "James"), Tuple.Create("002", "Linda"), Tuple.Create("003", "Frank"), Tuple.Create("004", "Jack"), Tuple.Create("002", "Rose"), Tuple.Create("001", "Lynn"), Tuple.Create("008", "Luke") }; var dict = data.ToDictionary(t => t.Item1, t => t.Item2); // 这里就抛异常了 // System.ArgumentException: 已添加了具有相同键的 foreach (var pair in dict) { // 不要见怪,用了 C# 6.0 的语法 Console.WriteLine($"{pair.Key} = {pair.Value}"); } } }
 原来    ToDictionary() 不会处理重复键,也没有提供多的参数来处理重复键。想了一下,这种问题大概应该用    ToLookup() 来解决,所以改写了代码  
cs// var dict = data.ToDictionary(t => t.Item1, t => t.Item2); // System.ArgumentException: 已添加了具有相同键的 var dict = data.ToLookup(t => t.Item1, t => t.Item2) .ToDictionary(t => t.Key, t => t.First());
     ToLookup() 返回的是一个    ILookup<> 接口,看定义是一个    IGrouping<> 接口的枚举。    IGrouping<> 本身也是一个    IEnumerable<> ,具有    Key 属性。因为    IGrouping<> 将具有同一个键的所有元素都包含在内了,所以在生成 Dictionary 的时候只取其中一个元素,根据上下文需要,可以取    First() ,或者    Last() 。  
 这是      ILookup<> 和      IGrouping<> 的定义    
cspublic interface ILookup<TKey, TElement> : IEnumerable<IGrouping<TKey, TElement>>, IEnumerable public interface IGrouping<out TKey, out TElement> : IEnumerable<TElement>, IEnumerable
 因为    ToLookup() 返回的结果包含太多枚举对象,所以不知道性能如何。如果不放心,可以自己写个    ToDictionaryEx() 来解决,当然也不敢保证性能就比    ToLookup() 好,要实验了才知道。  
csstatic class ToDictionaryExtentions { public static IDictionary<TKey, TValue> ToDictionaryEx<TElement, TKey, TValue>( this IEnumerable<TElement> source, Func<TElement, TKey> keyGetter, Func<TElement, TValue> valueGetter) { IDictionary<TKey, TValue> dict = new Dictionary<TKey, TValue>(); foreach (var e in source) { var key = keyGetter(e); if (dict.ContainsKey(key)) { continue; } dict.Add(key, valueGetter(e)); } return dict; } }
现在实验代码又该改改了
cs// var dict = data.ToDictionary(t => t.Item1, t => t.Item2); // System.ArgumentException: 已添加了具有相同键的 //var dict = data.ToLookup(t => t.Item1, t => t.Item2) // .ToDictionary(t => t.Key, t => t.First()); var dict = data.ToDictionaryEx(t => t.Item1, t => t.Item2);
没有测试性能的问题,等暴露了性能问题再来测试吧