C#使用LINQ展平值为集合的泛型字典时类型报错排查
问题说明
参考其他问题下的回答,我尝试编写一个可支持任意类型字典的展平方法。
我最初编写了如下函数,但在SelectMany子句处收到了
无法推断方法的类型参数
的报错。
public List<T> FlattenDictionary<T>(Dictionary<object, T> dictionary) { var list = dictionary.Values // To get just the List<string>s .SelectMany(x => x) // Flatten .ToList(); // Listify return list; }
根据@Ctznkane525的回答,我将方法修改为如下形式:
public List<T> FlattenListDictionary<I, T>(Dictionary<I, T> dictionary) where T : IEnumerable<T> { var list = dictionary.Values // To get just the List<string>s .SelectMany(x => x) // Flatten .ToList(); // Listify return (List <T>) list; }
修改后代码可以无错编译,但实际调用时仍然会报错,测试用例如下:
var dictionary = new Dictionary<int, List<string>>(); var list_1 = new List<string>(); list_1.Add("hello"); list_1.Add("good morning"); list_1.Add("hi"); var list_2 = new List<string>(); list_2.Add("cat"); list_2.Add("dog"); list_2.Add("mouse"); dictionary[10] = list_1; dictionary[320] = list_2; var list = (List<string>) FlattenListDictionary(dictionary);
无论是否添加类型强制转换,都会收到如下错误:
类型“System.Collections.Generic.List
”不能用作泛型类型或方法“FlattenListDictionary<I,T>(Dictionary<I,T>)”中的类型参数“T”。不存在从System.Collections.Generic.List 到System.Collections.Generic.IEnumerable<System.Collections.Generic.List >的隐式引用转换。
我也尝试将约束
where T : IEnumerable<T>
修改为
where T : List<T>
但仍然无法解决问题。
问题原因
你的代码核心问题是泛型约束逻辑完全写反了:
- 你定义的
T是字典的值类型(也就是测试用例里的List<string>),但你写的where T : IEnumerable<T>要求List<string>必须实现IEnumerable<List<string>>,可实际上List<string>实现的是IEnumerable<string>,根本不满足你写的约束,这就是报错的直接原因。 - 第一版代码报错是因为你没有给泛型加任何可枚举约束,编译器无法确认
T类型可以被SelectMany遍历,自然没法推断SelectMany的类型参数。 - 第二版里最后加的
(List<T>)强转也是多余的,ToList()方法本身就会返回对应元素类型的列表,不需要额外强转。
正确实现
你需要把「字典值的可枚举类型」和「可枚举里的元素类型」拆成两个独立的泛型参数,写对约束即可:
/// <summary> /// 展平值为可枚举集合的字典,返回所有集合元素组成的列表 /// </summary> /// <typeparam name="TKey">字典键类型</typeparam> /// <typeparam name="TValue">字典值类型(必须是可枚举类型)</typeparam> /// <typeparam name="TElement">字典值集合内的元素类型</typeparam> public List<TElement> FlattenDictionary<TKey, TValue, TElement>(Dictionary<TKey, TValue> dictionary) where TValue : IEnumerable<TElement> { return dictionary.Values.SelectMany(x => x).ToList(); }
调用时不需要写类型参数,也不需要强转,C#编译器可以自动推断所有泛型类型:
var dictionary = new Dictionary<int, List<string>>(); var list_1 = new List<string> { "hello", "good morning", "hi" }; var list_2 = new List<string> { "cat", "dog", "mouse" }; dictionary[10] = list_1; dictionary[320] = list_2; // 直接调用即可,返回值就是List<string>类型 var list = FlattenDictionary(dictionary);
如果你只需要处理值为List<T>的场景,不需要兼容所有IEnumerable<T>实现,可以简化成两个泛型参数的版本,写法更简洁:
public List<T> FlattenListDictionary<TKey, T>(Dictionary<TKey, List<T>> dictionary) { return dictionary.Values.SelectMany(x => x).ToList(); }
内容的提问来源于stack exchange,提问作者LairdPleng
相关产品推荐
相关产品推荐

