Linq - InvalidCastException - 为什么"where"不会过滤无效类型

Chr*_*son 1 c# linq

在复杂的linq查询中有问题所以我在LINQPad中简化了它:

void Main()
{
    List<basetype> items = new List<basetype>()
    {
        new typeA() { baseproperty = "1", extendedproperty = 1 },
        new typeB() { baseproperty = "2", extendedproperty = 1.1 },
        new typeA() { baseproperty = "3", extendedproperty = 1 },
    };

    items.Dump();

    (from typeA item in items
     where item is typeA
     select item).Dump();
}

public abstract class basetype
{
    public string baseproperty { get; set; }
    public string type { get; set; }
}

public class typeA : basetype
{
    public int extendedproperty { get; set; }
    public typeA() { type = "A"; }
}

public class typeB : basetype
{
    public double extendedproperty { get; set; }
    public typeB() { type = "B"; }
}
Run Code Online (Sandbox Code Playgroud)

第一个转储工作正常并返回:

extendedproperty baseproperty type 
1                1            A
1.1              2            B
1                3            A

但是第二个转储错误:

InInvalidCastException: Unable to cast object of type 'typeB' to type 'typeA'.

我可以通过删除"typeA"来解决这个问题,但我不想在原始语句中这样做,因为我必须在整个地方强制转换类型:

from item in items
Run Code Online (Sandbox Code Playgroud)

有趣的是,移动地点也修复了这个问题虽然你可能会认为有点难看:

from typeA item in items.Where(i => i is typeA)
Run Code Online (Sandbox Code Playgroud)

我的问题是:为什么是原来的地方投评估之前不会过滤掉不合理的项目?

Eri*_*ert 10

理由#1:

对类型的强制转换发生过滤器之前,因为它出现在左侧.在C#中,左边的东西几乎总是出现在右边的东西之前.

理由#2:

假设我们按照你的方式做到了.你有一个List<object>,你说

from string s in myObjects where s.Length > 100
Run Code Online (Sandbox Code Playgroud)

并且你得到一个错误,说该对象没有Length属性 - 因为当然按照你的方式在过滤器发生转换为字符串,因此过滤器不能依赖于强制转换所确定的不变量.大概是你把演员放在那里因为你想使用目标类型的属性.但你不可能两种方式; 左操作先运行或右操作先运行.他们不能先跑.

原因#3:

已经有办法做你想做的事:

... from foos.OfType<Bar>() ...
Run Code Online (Sandbox Code Playgroud)

这相当于先过滤然后提供一个只有正确类型的过滤值的序列.

  • @Chris:我认为这种混乱是因为在'from`声明中隐含了一个强制转换.LINQ语句的来源不是`items`; 它是`items`,每个项目都被强制转换为`typeA`. (2认同)