C#InvalidCastException虽然是相同的基类

时间:2009-07-13 03:03:29

标签: c# generics exception

以下代码抛出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()类型为List的结果,并将每个项目添加到我的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吗?

2 个答案:

答案 0 :(得分:5)

是的,无效的强制转换异常是正确的。您可以从派生类自由地转换为基类,但是您不能盲目地从基类转换为派生类,除非该对象确实派生类的实例。这是你不能这样做的原因:

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

右? objectstring的基础,您无法从object自由地投射到string。另一方面,这将起作用,因为obj确实是一个字符串:

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

答案 1 :(得分: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>编写扩展方法来提供该逻辑。只需要考虑一些事情......