简体   繁体   English

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

[英]Why does the C# compiler allow an explicit cast between IEnumerable<T> and TAlmostAnything?

The following code gives you a compiler error, as you'd expect: 以下代码为您提供编译器错误,正如您所期望的那样:

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

Banana justOneBanana = (Banana)aBunchOfBananas;

However, when using IEnumerable<Banana> , you merely get a runtime error. 但是,使用IEnumerable<Banana> ,只会出现运行时错误。

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

Banana justOneBanana = (Banana)aBunchOfBananas;

Why does the C# compiler allow this? 为什么C#编译器允许这样做?

I would suppose it's because IEnumerable<T> is an interface where some implementation could have an explicit cast to Banana - no matter how silly that would be. 我想这是因为IEnumerable<T>是一个接口,其中一些实现可以Banana进行显式转换 - 无论多么愚蠢。

On the other hand, the compiler knows that List<T> can't be explicitly cast to a Banana . 另一方面,编译器知道List<T>不能显式地转换为Banana

Nice choice of examples, by the way! 顺便说一下,很好的例子选择!

Adding an example to clarify. 添加一个例子来澄清。 Maybe we'd have some "enumerable" that should always contain at most a single 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...
}

Then you could actually do this: 然后你可以这样做:

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

As it's the same as writing the following, which the compiler is perfectly happy with: 因为它与编写以下内容相同,编译器非常满意:

Banana justOneBanana = aBunchOfBananas.SingleOrDefault();

When you say Y y = (Y)x; 当你说Y y = (Y)x; this cast says to the compiler "trust me, whatever x is, at runtime it can be casted to a Y , so, just do it, okay?" 这个演员对编译器说:“相信我,无论x是什么,在运行时它都可以转换为Y ,所以,就这样做,好吗?”

But when you say 但是当你说的时候

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

the compiler can look at the definitions for each of these concrete classes ( Banana and List<Banana> ) and see that there is no static explicit operator Banana(List<Banana> bananas) defined (remember, an explicit cast must be defined in either the type being casted or the type being casted to, this is from the spec, section 17.9.4). 编译器可以查看每个具体类( BananaList<Banana> )的定义,并看到没有定义static explicit operator Banana(List<Banana> bananas) (请记住,必须在任一个中定义显式转换被铸造的类型或铸造的类型,这是来自规范,第17.9.4节)。 It knows at compile time that what you're saying can not ever be true. 它在编译时知道你所说的不可能是真的。 So it yells at you to stop lying. 所以你大吼大叫停止说谎。

But when you say 但是当你说的时候

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

well, now the compiler doesn't know. 好吧,现在编译器不知道。 It very well could the case that whatever aBunchOfBananas happens to be at run time, its concrete type X could have defined static explicit operator Banana(X bananas) . 很有可能的情况是,无论aBunchOfBananas恰好在运行时,其具体类型X可以定义static explicit operator Banana(X bananas) So the compiler trusts you, like you asked it to. 所以编译器信任你,就像你问的那样。

这可能是因为编译器知道 Banana没有扩展List<T> ,但是实现IEnumerable<T>某个对象也可能会扩展Banana并使其成为有效的强制转换。

According to language spec (6.2.4) "The explicit reference conversions are: From any class-type S to any interface-type T, provided S is not sealed and provided S does not implement T ... The explicit reference conversions are those conversions between reference-types that require run-time checks to ensure they are correct..." 根据语言规范(6.2.4)“显式引用转换是:从任何类型S到任何接口类型T,如果S未密封且提供S未实现T ...显式引用转换是那些引用类型之间的转换需要运行时检查以确保它们是正确的......“

So compiler doesn't check interface realization during compilation. 因此编译器在编译期间不检查接口实现。 It does CLR in runtime. 它在运行时执行CLR。 It checks metadata trying to find realization in class or among its parents. 它检查元数据,试图在类中或在其父类中查找实现。 I dunno why it behaves like this. 我不知道为什么它会像这样。 Probably it takes a lot of time. 可能需要很多时间。 So this code compiles correctly: 所以这段代码正确编译:

public interface IInterface
{}

public class Banana
{
}

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

        IInterface b = (IInterface)banana;
    }
}

In other hand if we try to cast banana to class, compiler checks its metadata and throw an error: 另一方面,如果我们尝试将banana转换为类,编译器会检查其元数据并抛出错误:

 FileStream fs = (FileStream)banana;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM