以下代码为您提供编译器错误,正如您所期望的那样:
List<Banana> aBunchOfBananas = new List<Banana>();
Banana justOneBanana = (Banana)aBunchOfBananas;
但是,使用IEnumerable<Banana>
时,只会出现运行时错误。
IEnumerable<Banana> aBunchOfBananas = new List<Banana>();
Banana justOneBanana = (Banana)aBunchOfBananas;
为什么C#编译器允许这样做?
答案 0 :(得分:48)
我认为这是因为IEnumerable<T>
是一个接口,其中某些实现可以对Banana
进行显式转换 - 无论多么愚蠢。
另一方面,编译器知道List<T>
无法显式转换为Banana
。
顺便提一下,很好的例子选择!
添加一个示例以澄清。也许我们有一些“可枚举的”应始终包含最多一个Banana
:
public class SingleItemList<T>:Banana, IEnumerable<T> where T:Banana {
public static explicit operator T(SingleItemList<T> enumerable) {
return enumerable.SingleOrDefault();
}
// Others omitted...
}
然后你可以实际执行此操作:
IEnumerable<Banana> aBunchOfBananas = new SingleItemList<Banana>();
Banana justOneBanana = (Banana)aBunchOfBananas;
与编写以下内容相同,编译器非常满意:
Banana justOneBanana = aBunchOfBananas.SingleOrDefault();
答案 1 :(得分:29)
当你说Y y = (Y)x;
这个演员对编译器说“信任我,无论x
是什么时,在运行时它可以被转换为Y
,所以,就这样做,好吧?”
但是当你说
时List<Banana> aBunchOfBananas = new List<Banana>();
Banana justOneBanana = (Banana)aBunchOfBananas;
编译器可以查看每个具体类(Banana
和List<Banana>
)的定义,并看到没有定义static explicit operator Banana(List<Banana> bananas)
(请记住,必须定义显式强制转换)在被铸造的类型或被铸造的类型中,这来自规范,第17.9.4节)。它在编译时知道你所说的不可能是真的。因此,你大吼大叫停止说谎。
但是当你说
时IEnumerable<Banana> aBunchOfBananas = new List<Banana>();
Banana justOneBanana = (Banana)aBunchOfBananas;
好吧,现在编译器不知道。很有可能的情况是,无论aBunchOfBananas
恰好在运行时,其具体类型X
都可以定义static explicit operator Banana(X bananas)
。所以编译器会像你要求的那样信任你。
答案 2 :(得分:16)
可能是因为编译器知道 Banana
没有扩展List<T>
,但实现IEnumerable<T>
的某个对象可能也会延伸Banana
并使其成为有效的演员。
答案 3 :(得分:0)
根据语言规范(6.2.4) “明确的参考转换是: 从任何类型S到任何接口类型T,如果S未密封且提供S未实现T. ... 显式引用转换是引用类型之间的转换,需要运行时检查才能确保它们正确...“
因此编译器在编译期间不检查接口实现。它在运行时执行CLR。它检查元数据,试图在类中或在其父类中查找实现。我不知道为什么它会像这样。可能需要很多时间。所以这段代码编译正确:
public interface IInterface
{}
public class Banana
{
}
class Program
{
static void Main( string[] args )
{
Banana banana = new Banana();
IInterface b = (IInterface)banana;
}
}
另一方面,如果我们尝试将banana转换为类,编译器会检查其元数据并抛出错误:
FileStream fs = (FileStream)banana;