2012-02-08 51 views
59

下面的代碼給你一個編譯器錯誤,如你所期望:爲什麼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#編譯器允許這樣做?

+0

的事情列表不是一個單一的東西。 – 2012-02-08 17:07:38

+2

雖然不允許使用值類型。 – ken2k 2012-02-08 17:08:07

+1

這也適用於IList 。這就是在下面的答案中,讓整個界面與具體類在我的點擊。 – deepee1 2012-02-08 22:34:29

回答

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(); 
+4

你不能這樣做 - 它不會找到運營商。一個例子是'班SingleBanana:香蕉,IEnumerable '。這就是爲什麼這隻適用於接口 - 因爲如果這兩種類型都是類的,那麼在編譯時就可以確定這是不可能的。 – Random832 2012-02-08 20:04:27

+0

@ Random832良好的捕獲,但實際上你的類簽名也不會工作(你不能有一個明確的轉換到基類)。我已經按照預期版本更新了我的答案。 – Yuck 2012-02-08 20:17:09

+0

呃,我的觀點是你不能使用運算符超載_at all_爲此。它工作的原因是因爲派生類對象是_inherently_可轉換爲基類的。 – Random832 2012-02-09 00:45:14

16

這可能是因爲編譯器知道Banana沒有延伸List<T>,但有可能某些實現了IEnumerable<T>的對象也可能延伸Banana並進行有效的轉換。

+1

很遺憾看到唯一正確的答案,在第一個帖子中發佈,沒有那麼多upvotes。也許短代碼片段會有所幫助。 – 2013-08-21 00:43:35

29

當你說Y y = (Y)x;這個演員對編譯器說:「相信我,不管x是什麼,在運行時它可以被轉換爲Y,所以,就這麼做,好嗎?

但是,當你說

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

編譯器可以查看每個這些具體類(BananaList<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)。所以編譯器相信你,就像你問的那樣。

+0

這是downvoted? – jason 2012-02-08 21:12:14

+0

也許是因爲你沒有提到你在類和整數之間做了區分?不知道的是,你的第一個(X和Y)和第二個例子完全一樣。 – 2012-02-14 19:24:31

+1

是的,這是downvoted ...這是錯的。在運行時無法找到轉換運算符(除非涉及'dynamic'關鍵字)。有另一個downvote。 – 2013-08-21 00:40:21

0

根據語言規範(6.2.4) 「顯式引用轉換是: 從任意類型S至任意接口類型T,只要S沒有被密封和提供S不實現Ť .. 顯式引用轉換是那些需要運行時檢查以確保它們正確的引用類型之間的轉換......「

因此編譯器在編譯期間不檢查接口實現。它在運行時執行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; 
相關問題