刚看到这个:
Func<List<object>> foo = () => new List<object>();
List<string> s = (List<string>)foo();
IList<string> s1 = (IList<string>)foo();
编译器指向将类型强制转换为List(很合理),但没有涉及到IList,这让我想知道原因是什么?
List<X>
不能是 List<Y>
。因此会出现编译错误。List<X>
实际上是一些实现了 IList<Y>
的派生类,那么第二个强制类型转换可能会成功。(已加粗)显式引用转换包括:
- 从 object 和 dynamic 类型到任何其他引用类型。
- 从任何类类型 S 到任何类类型 T,条件是 S 是 T 的基类。
- 从任何类类型 S 到任何接口类型 T,条件是 S 没有被密封并且 S 没有实现 T。
- 从任何接口类型 S 到任何类类型 T,条件是 T 没有被密封或 T 实现了 S。
- 从任何接口类型 S 到任何接口类型 T,条件是 S 不是 T 的派生类型。
- [省略...]
provided...
子句排除了实际上是隐式转换的转换。sealed
?这是因为它知道不能被继承者重写吗? - Mike Perrenoud一个已知为 List<object>
的对象可能会同时实现 IList<string>
和 IList<object>
,因此强制转换是可能成功的。但在这种情况下不行,因为我们知道该语句只是简单的new List<object>()
,但编译器并不考虑这一点。你可能已经扩展了 List<T>
并实现了其他方法,例如。
// not recommended, but second cast works
public class MyWeirdList : List<object>, IList<string>
已知一个对象是 List<object>
,它不可能也是一个 List<string>
,因为你只能从一个类型继承。
public class MyWeirdList : List<object>, List<string> // compiler error
List<T>
被密封,那么这两个强制转换都将无效,因为编译器可以确定该类不能实现 IList<string>
。您可以尝试使用以下类来测试此功能:public sealed class SealedList<T> : List<T> { }
第一行在编译时失败,第二行在运行时出现“无法将类型为'System.Collections.Generic.List1[System.Object]'的对象转换为类型'System.Collections.Generic.IList
1[System.String]'。”异常。
如果您查看这个问题(将IList<string>强制转换为IList<object>在运行时失败),答案澄清了为什么这个编译可以工作,并提供了一个满足所提供条件的类的示例。
List<string>
的隐式转换操作,但是IList<string>
的实现者可能能够? - Mike Perrenoud