C#中传递List<实现接口的类>给List<接口>参数报错的原因
这个问题我之前也碰到过,核心是C#泛型的不变性在搞鬼:虽然TestClass实现了ItestInterface,但List<TestClass>和List<ItestInterface>是完全独立的类型,C#不允许直接隐式转换它们。
为什么要做这种限制?举个简单的例子:如果允许把List<TestClass>转成List<ItestInterface>,那在PrintTestClass方法里,理论上可以往这个列表里添加另一个实现了ItestInterface的类(比如AnotherTestClass : ItestInterface),但原来的列表实际只能容纳TestClass类型的对象,这就会导致运行时类型错误。所以C#在编译阶段就直接阻止了这种潜在的危险。
下面提供几种可行的解决办法,按推荐程度排序:
1. 将方法参数改为IEnumerable<ItestInterface>
从C# 4.0开始,IEnumerable<T>是协变的(支持out关键字),这意味着只要TestClass实现了ItestInterface,List<TestClass>就可以直接赋值给IEnumerable<ItestInterface>。这种方案不需要创建新集合,性能最优,也是最推荐的做法。
修改后的代码:
class MainClass { public static void Main(string[] args) { Console.WriteLine("Hello World!"); TestClass A = new TestClass(); A.Name = "A"; TestClass B = new TestClass(); B.Name = "B"; TestClassList aList = new TestClassList(); aList.myList.Add(A); aList.myList.Add(B); PrintTestClass(aList.myList); // 直接调用,无需任何转换 } // 修改参数类型为IEnumerable<ItestInterface> public static void PrintTestClass(IEnumerable<ItestInterface> testList) { foreach (ItestInterface item in testList) Console.WriteLine(item.Name); } } // 接口和类部分保持不变 interface ItestInterface { string Name { get; set; } } class TestClass : ItestInterface { public string Name { get; set; } public string otherVar { get; set; } } class TestClassList { public List<TestClass> myList; public TestClassList() { myList = new List<TestClass>(); } }
2. 使用LINQ的Cast+ToList转换
如果必须保留List<ItestInterface>作为参数类型,可以用LINQ把List<TestClass>转换为List<ItestInterface>:
// 调用时直接转换 PrintTestClass(aList.myList.Cast<ItestInterface>().ToList());
这个方法会遍历原集合,把每个元素转换为接口类型,然后创建一个新的List<ItestInterface>。注意这种方式会产生额外的内存开销,适合集合规模不大的场景。
3. 手动创建新的List<ItestInterface>
和第二种方案效果一致,只是写法更直观,手动创建新列表并复制元素:
var interfaceList = new List<ItestInterface>(aList.myList); PrintTestClass(interfaceList);
内容的提问来源于stack exchange,提问作者tmptplayer

