2009-07-13 46 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