【问题标题】:C#; InvalidCastException, what´s wrong?C#; InvalidCastException,怎么了?
【发布时间】:2016-04-18 19:01:51
【问题描述】:

我想将列表的对象转换为它的子类。 为此我使用了Casting populated List<BaseClass> to List<ChildClass>

现在,当我执行代码时,我总是在 System.Core.dll 中收到 InvalidCastException ""对象项无法转换为子项"

[Serializable]
public class SubItem: Item
{
    public SubItem(int id) : base( id)
    {          
    }
}


public getStuff(int id){
 //where the Error is thrown
//Inventory has Items in it, furthermore atm Inventory is definitely not     empty, but could be later on
 var items = Inventory.FindAll(x=> x.id==id).Cast<SubItem>().ToList();
}

项目来自 API,我无法在那里更改任何内容。 项目继承自 IEquatable。

LINQ 确实可以工作,但是当我进行投射时,我得到了错误。我做错了什么?

【问题讨论】:

  • 你有错字public SubItem(ind id) : base( id)
  • 您的库存包含物品或子物品?
  • int id 而不是 ind it。但这应该是一个错字,否则它不会编译(没有class ind..)。正如我猜测的那样,您正在尝试将Item 转换为SubItem,这是不可能的。您可能想检查实例是否为 typeof(SubItem)?
  • 列表中的对象具有匹配的id,但实际上不是子项。也许您需要 OfType,也许这是一个错误。
  • Inventory.FindAll(x=> x.id==id).ToList()

标签: c# linq exception


【解决方案1】:

可能是您列表中的某些项目不是派生类类型,您应该能够在调试器中看到这一点。此外,正如有人指出的那样,您可能必须在进行强制转换之前从数据库中提取数据,linq 提供程序可能无法为您执行此操作。

所以您可以尝试以下任一方法:

  • 过滤掉不属于 SubItem 的项目
var items = Inventory.FindAll(x=> x.id==id).OfType<SubItem>().ToList();
  • 在投射之前从数据库中拉回数据
var items = Inventory.FindAll(x=> x.id==id).ToList().Cast<SubItem>().ToList();
  • 如果 linq 提供程序无法执行 OfType,则将两者结合使用
var items = Inventory.FindAll(x=> x.id==id).ToList().OfType<SubItem>().ToList();

【讨论】:

  • 如果我使用第一个和最后一个,我不会得到错误,但我的列表是空的。如果我使用第二个,我会得到同样的错误。为什么顺便说一句,我必须使用 ToList() 不是已经是 List 了吗?
  • @EsoMoa 那么我会说你没有取回 SubItem 的任何对象。如果您持有 FindAll().ToList() 的结果并检查列表的内容,它将向您显示对象类型是什么。然后你就会知道你也可以投射它们或修复你的 ORM 工具以更好地映射对象。
【解决方案2】:

我将假设您的属性Inventory 是泛型类型Item 的强类型集合/列表/IEnumerable(例如:ICollection&lt;Item&gt;)。

如果一个或多个项目不是泛型参数中指定的类型,则在调用 Cast 时会出现 InvalidCastException。请参阅 Microsoft 页面Enumerable.Cast 中的详细信息。为了解决这个问题,您只需要检索将在过滤器期间对其执行强制转换的那种类型的项目。这可以通过Enumerable.OfType 完成。然后你的代码变成:

var items = Inventory.FindAll(x=> x.id==id).OfType<SubItem>().ToList();

来自文档Enumerable.Cast

如果一个元素不能转换为 TResult 类型,这个方法会抛出一个异常。要仅获取那些可以转换为 TResult 类型的元素,请使用 OfType 方法而不是 Cast(IEnumerable)。


根据这些 cmets 进行编辑

确定所有返回的项目都是Item 类型而不是SubItem。这解释了异常。

...说 List 由 Item 组成,这就是我想将它们向下转换为 SubItem 的原因。

您在问题中发布的继承层次结构是System.Object -> Item -> SubItem。 (我包括 Object,因为我假设我们正在处理引用类型,并且所有内容最终都继承自 System.Object)。这意味着SubItem 可以转换为ItemSystem.ObjectItem 实例可以转换为System.Object。然而,反之亦然,你不能把图表向上抛。 System.Object 的实例不能成为ItemItem 不能成为SubItem

有一个通用的List&lt;Item&gt; 很好,它可以包含SubItemItem 或任何从Item 派生的类型。但同样,您不能将 Item 的实例转换为 SubItem

如果不清楚,那么我建议您阅读有关继承以及多态性的内容,这是一个不同的主题,但如果您想知道为什么要使用继承以及是什么使它成为如此出色的抽象工具,那么它也非常重要.

【讨论】:

  • 我不相信你需要在执行.OfType&lt;T&gt; 之后.Cast&lt;T&gt;,因为它已经返回IEnumerable&lt;T&gt;
  • 如果列表只包含项目对象,我不完全明白为什么我应该从列表中获取项目以外的变量。我在 Startpost 中添加了 Item 从 IEquatable 继承的内容。如果我使用您的解决方案,我确实没有收到错误,但列表始终为空,尽管如果我不想将其转换为 SubItem,我会得到一个包含 11 个元素的列表(这是正确的)。
  • @EsoMoa - 我感觉返回的类型不是您所期望的,或者您的代码在某处缺少继承调用。我会尝试添加以下代码行以查看实际返回的类型:var allTypeNames = string.Join(" | ", Inventory.FindAll(x=&gt; x.id==id).ToList().Select(x =&gt; x.GetType().FullName).Distinct());
  • 返回:dllName.Core.Item
  • @EsoMoa - 这就是问题所在。列表中返回的实例是Item 类型而不是SubItem 类型,这就是Cast 失败以及OfType 将它们过滤掉的原因。如果您使用Linq-to-Sql,则必须提供鉴别器,以便创建正确的类型。如果这是在其他地方构建的列表,那么创建列表的任何内容都需要创建正确类型的实例。
【解决方案3】:

假设 Inventory 包含“项目”列表,并且您想要获得“子项目”列表,请将您的行替换为:

var items = Inventory.Where(x => x.id == id).Select(x => new SubItem(x.id)).ToList();

所以第一部分将为您提供满足您条件的项目,第二部分将为每个项目创建一个新的子项目并为您提供它们的列表。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-08
    • 2020-07-01
    • 2015-06-29
    • 2012-09-05
    • 2021-06-21
    • 1970-01-01
    相关资源
    最近更新 更多