.net-为什么C#编译器允许在IEnumerable <T>和TAlmostAnything之间进行显式转换?

如您所料,以下代码为您提供了一个编译器错误:

List<Banana> aBunchOfBananas = new List<Banana>();

Banana justOneBanana = (Banana)aBunchOfBananas;

但是,使用IEnumerable<Banana>时,您只会得到运行时错误。

IEnumerable<Banana> aBunchOfBananas = new List<Banana>();

Banana justOneBanana = (Banana)aBunchOfBananas;

为什么C#编译器允许这样做?

Danny asked 2019-10-08T16:54:07Z
4个解决方案
48 votes

我想这是因为Banana是一个接口,在该接口中某些实现可以显式转换为Banana-不管这样做多么愚蠢。

另一方面,编译器知道Banana不能显式转换为Banana

顺便说一句,很好的例子!

添加一个示例进行说明。 也许我们会有一些“枚举”,它们最多应始终包含一个2560470769319019519520:

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();
Yuck answered 2019-10-08T16:54:57Z
29 votes

当您说aBunchOfBananas时,此转换对编译器说:“请相信我,无论X是什么,在运行时都可以将其转换为static explicit operator Banana(X bananas),这样就可以了,好吗?”

但是当你说

List<Banana> aBunchOfBananas = new List<Banana>();
Banana justOneBanana = (Banana)aBunchOfBananas;

编译器可以查看每个具体类的定义(aBunchOfBananasX),并看到没有定义static explicit operator Banana(X bananas)(请记住,必须在要转换的类型或要转换的类型中定义显式转换,这是 根据规范,第17.9.4节)。 它知道在编译时您所说的不可能永远是真的。 所以它叫你停止说谎。

但是当你说

IEnumerable<Banana> aBunchOfBananas = new List<Banana>();
Banana justOneBanana = (Banana)aBunchOfBananas;

好吧,现在编译器不知道了。 不管aBunchOfBananas恰好在运行时,它的具体类型X都可以定义static explicit operator Banana(X bananas)。因此,编译器信任您,就像您要求的那样。

jason answered 2019-10-08T16:55:49Z
16 votes

可能是因为编译器知道Banana不会扩展List<T>,但是存在一些实现IEnumerable<T>的对象也可能会扩展Banana并使其有效转换的可能性。

eouw0o83hf answered 2019-10-08T16:56:16Z
0 votes

根据语言规范(6.2.4)“明确的参考转换是:  从任何类类型S到任何接口类型T,只要未密封S并且未实现T...显式引用转换是引用类型之间的转换,需要运行时检查以确保它们正确无误。”

因此,编译器在编译期间不会检查接口实现。 它在运行时执行CLR。 它检查元数据,以尝试在课堂上或其父级中找到实现。 我不知道为什么会这样。 可能需要很多时间。 因此,此代码可以正确编译:

public interface IInterface
{}

public class Banana
{
}

class Program
{
    static void Main( string[] args )
    {
        Banana banana = new Banana();

        IInterface b = (IInterface)banana;
    }
}

另一方面,如果我们尝试将香蕉转换为类,则编译器会检查其元数据并抛出错误:

 FileStream fs = (FileStream)banana;
Alex answered 2019-10-08T16:57:02Z
translate from https://stackoverflow.com:/questions/9198024/why-does-the-c-sharp-compiler-allow-an-explicit-cast-between-ienumerablet-and