2009-07-13 47 views
1

以下代碼將引發InvalidCastExceptionC#InvalidCastException雖然相同的基類

public static MachineProductCollection MachineProductsForMachine(
    MachineProductCollection MachineProductList, int MachineID) 
{ 
    return (MachineProductCollection) 
     MachineProductList.FindAll(c => c.MachineID == MachineID); 
} 

這讓我驚訝,因爲MachineProductCollection只是MachineProducts的泛型列表是的FindAll()應該返回什麼。以下是完整的MachineProductCollection源代碼。你會注意到僅僅是List的一個包裝。

[Serializable] 
public partial class MachineProductCollection : 
     List<MachineProduct> 
{ 
    public MachineProductCollection() { } 
} 

我使出基本上通過的FindAll()的結果是類型列表,並將每個項目我MachineProductCollection循環以下。顯然,我不喜歡所需的迭代。

public static MachineProductCollection 
    MachineProductForMachine(MachineProductCollection 
    MachineProductList, int MachineID) 
{ 
    MachineProductCollection result = 
     new MachineProductCollection(); 


    foreach (MachineProduct machineProduct in 
     MachineProductList.FindAll(c => c.MachineID == MachineID)) 
    { 
     result.Add(machineProduct); 
    } 

    return result; 
} 

文檔聲明在顯式引用轉換過程中發生故障時會引發InvalidCastException。引用轉換是從一個引用類型轉換爲另一個引用類型雖然他們可能會更改引用的類型,但他們絕不會更改轉換目標的類型或值。將對象從一種類型轉換爲另一種類型是此異常的常見原因。

考慮到List是MachineProductCollection的基礎,這應該是一個InvalidCastException嗎?

回答

5

是的,無效的轉換異常是正確的。您可以從派生類自由投射到基類,但不能盲目地從基類投射到派生類,除非對象真的是派生類的實例。這是你不能這樣做的原因:

object obj = new object(); 
string str = (string) obj; 

對不對? objectstring的基地,你不能自由投擲從objectstring。在另一方面,因爲obj這樣的工作確實是一個字符串:

object obj = "foo"; 
string str = (string) obj; 
+0

很好的答案 - 簡短而甜美。特別是現在使用對象/字符串類比來說,它是完全有意義的。謝謝。 – 2009-07-13 04:03:58

1

你得到InvalidCastException因爲List<MachineProduct>不一定是MachineProductCollection,即使反轉顯然是正確的。

返回實際MachineProductCollection是最簡單的解決方案中使用List<T>的序列構造:

public static MachineProductCollection 
    MachineProductForMachine(MachineProductCollection MachineProductList, int MachineID) 
{ 
    List<MachineProduct> found = 
     MachineProductList.FindAll(c => c.MachineID == MachineID)) 

    return new MachineProductCollection(found); 
} 

但是,由於您使用(通過lambda表達式我猜你有機會獲得LINQ。 NET 3.5或LINQ Bridge),這意味着你可以使用Where擴展方法跳過中間列表:

public static MachineProductCollection 
    MachineProductForMachine(MachineProductCollection MachineProductList, int MachineID) 
{ 
    IEnumerable<MachineProduct> found = 
     MachineProductList.Where(c => c.MachineID == MachineID)) 

    return new MachineProductCollection(found); 
} 

這就是說,你可能會更好只返回一個IEnumerable<MachineProduct>,而不是創建您自己的集合類型。這可能會讓你的生活更輕鬆,除非你打算給你的MachineProductCollection添加特殊的邏輯。在這種情況下,您總是可以根據IEnumerable<MachineProduct>編寫擴展方法來提供該邏輯。只需要考慮一些事情...

+0

對於所有的細節和正確的答案+1。對不起,我無法將兩個答案標記爲正確。 – 2009-07-13 04:02:50