为何无法将ICollection类型字典传入期望IEnumerable类型的方法?
泛型集合协变疑问:ICollection可转IEnumerable,但嵌套在字典中却不行?
可行示例
下面的代码可正常编译运行,ICollection<string>类型变量能直接传入期望IEnumerable<string>的方法:
internal class Program { static void Main(string[] args) { ICollection<string> myList = new List<string>(); myList.Add("A"); myList.Add("B"); myList.Add("C"); MyMethod(myList); } private static void MyMethod(IEnumerable<string> myList) { foreach(string myItem in myList) { Console.WriteLine(myItem); } } }
不可行示例
但当ICollection<string>作为字典的值类型时,IDictionary<string, ICollection<string>>无法传入期望IDictionary<string, IEnumerable<string>>的方法:
internal class Program { static void Main(string[] args) { IDictionary<string, ICollection<string>> myDictionary = new Dictionary<string, ICollection<string>>(); myDictionary.Add("First", new List<string>()); myDictionary["First"].Add("A"); myDictionary["First"].Add("B"); myDictionary["First"].Add("C"); myDictionary.Add("Second", new List<string>()); myDictionary["Second"].Add("X"); myDictionary["Second"].Add("Y"); myDictionary["Second"].Add("Z"); MyMethod(myDictionary); // 此处编译器报错 } private static void MyMethod(IDictionary<string, IEnumerable<string>> myDictionary) { foreach (KeyValuePair<string, IEnumerable<string>> keyValuePair in myDictionary) { foreach (string item in keyValuePair.Value) { Console.WriteLine(keyValuePair.Key); Console.WriteLine(item); } } } }
编译器报错信息
Argument 1: cannot convert from 'System.Collections.Generic.IDictionary<string, ICollection
>' to 'System.Collections.Generic.IDictionary<string, IEnumerable >'
原因分析
核心问题在于泛型接口的协变/不变性:
IEnumerable<out T>是协变接口(out关键字标记),只要T是U的实现类,IEnumerable<T>就可以隐式转换为IEnumerable<U>。ICollection<string>实现了IEnumerable<string>,所以单个集合可直接转换。IDictionary<TKey, TValue>是不变接口,未用out标记TValue。这是因为IDictionary包含写入操作(如Add方法),如果允许IDictionary<string, ICollection<string>>转为IDictionary<string, IEnumerable<string>>,那么在MyMethod中理论上可以执行myDictionary.Add("Third", someIEnumerableThatIsNotICollection),但原字典的值类型要求是ICollection<string>,会直接破坏类型安全。
解决办法
根据你的需求(仅需遍历操作,无需修改字典),可采用以下几种方式解决:
- 将方法参数改为
IReadOnlyDictionary<string, IEnumerable<string>>:IReadOnlyDictionary<TKey, out TValue>是协变接口,支持隐式转换。 - 修改方法为泛型方法,添加类型约束:
private static void MyMethod<T>(IDictionary<string, T> myDictionary) where T : IEnumerable<string> { // 原遍历逻辑保持不变 } - 在调用时显式转换(不推荐,安全性低于前两种方式):
MyMethod(myDictionary.ToDictionary(kv => kv.Key, kv => (IEnumerable<string>)kv.Value));
内容的提问来源于stack exchange,提问作者PaulH567
相关产品推荐
相关产品推荐

