代码之家  ›  专栏  ›  技术社区  ›  Chris Simpson

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

  •  1
  • Chris Simpson  · 技术社区  · 14 年前

    在复杂的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"; }
    }
    

    倾倒

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

    倾倒 错误:

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

    from item in items
    

    有趣的是,移动 哪里 也解决了这个问题,尽管你可能会同意这有点难看:

    from typeA item in items.Where(i => i is typeA)
    

    在计算强制转换之前不过滤掉无效项?

    3 回复  |  直到 14 年前
        1
  •  10
  •   Eric Lippert    14 年前

    原因1:

    类型的强制转换发生 之前 在左边

    假设我们按你的方式做。你有一个 List<object> 你说呢

    from string s in myObjects where s.Length > 100
    

    你会得到一个错误,说这个对象没有Length属性——当然是因为按照你的方式转换为字符串 之后 过滤器,因此过滤器不能依赖于由强制转换确定的不变量。大概是因为你 要使用目标类型的属性吗 . 但你不能两全其美;要么左操作先运行,要么右操作先运行。他们不能 二者都

    原因3:

    已经有一种方法可以做你想做的事:

    ... from foos.OfType<Bar>() ...
    

    这相当于先过滤,然后只提供正确类型的过滤值序列。

        2
  •  1
  •   Amy B    14 年前

    为什么在评估演员阵容之前,没有过滤掉无效的项目?

    where from 必须跑。

    (from typeA item in items 
    

    你不小心把你的 表情。拆下 TypeA (可选)从 你会准备好的。这与foreach语句中的隐式强制转换相同:

    foreach(TypeA item in items) //will throw for same reason
    {
      if (item is TypeA)
    

    我可以通过删除“typeA”来解决这个问题,但我不想在原始语句中这样做,因为我必须将类型转换到所有地方:

    您可以使用以下任一解决方案:

    (items.OfType<TypeA>()).Dump();
    
    (from item in items
    where item is TypeA
    let itemA = item as TypeA
    select itemA).Dump();
    
        3
  •  0
  •   Jay    14 年前

    .Cast<typeA>()
    

    例子:

    class Program
    {
        static void Main(string[] args)
        {
            var list = new List<BaseType> {new TypeA(), new TypeB()};
            IEnumerable<TypeA> results = list.Where(x => x is TypeA).Cast<TypeA>();
            Console.WriteLine("Succeeded. Press any key to quit.");
            Console.ReadKey();
        }
    
        public class BaseType{}
        public class TypeA : BaseType {}
        public class TypeB : BaseType {}
    }