OfType<>()与Where()扩展名中的检查类型之间的差异

时间:2012-01-25 23:52:54

标签: c# linq

除了可读性之外,以下linq查询之间的区别是什么,以及何时以及为什么我将使用其中一个:

IEnumerable<T> items = listOfItems.Where(d => d is T).Cast<T>();

IEnumerable<T> items = listOfItems.OfType<T>();

更新 Dang,抱歉在尝试简化我的问题时介绍了一些错误

4 个答案:

答案 0 :(得分:8)

让我们比较三种方法(注意泛型参数):

    listOfItems.Where(t => t is T)上调用的
  1. IEnumerable<X>仍会返回IEnumerable<X>,只会过滤为仅包含T类型的元素。

  2. listOfItems.OfType<T>()上调用的
  3. IEnumerable<X>将返回包含可以投放到IEnumerable<T>类型的元素的T

  4. listOfItems.Cast<T>()上调用的
  5. IEnumerable<X>将返回IEnumerable<T>包含已转换为T类型的元素,或者如果无法转换任何元素,则会抛出异常。

  6. 并且listOfItems.Where(d => d is T).Cast<T>()基本上做了两次相同的事情 - Where过滤了T的所有元素,但仍然保留了IEnumerable<X>类型然后再次Cast尝试将它们转换为T但这次返回IEumerable<T>

答案 1 :(得分:3)

如果我对你的例子采取一些自由并在LINQPad中实现它,这就是我得到的:

<强>方法

List<T> GetNumbers<T>(List<T> nums){
    return nums.Where(d => d is T).ToList<T>();
}

List<T> GetNumbersOfType<T>(List<T> nums){
    return nums.OfType<T>().ToList<T>();
}

<强> IL

GetNumbers:
IL_0000:  ldarg.1     
IL_0001:  ldnull      
IL_0002:  ldftn       05 00 00 2B 
IL_0008:  newobj      0A 00 00 0A 
IL_000D:  call        06 00 00 2B 
IL_0012:  call        07 00 00 2B 
IL_0017:  ret         

GetNumbersOfType:
IL_0000:  ldarg.1     
IL_0001:  call        08 00 00 2B 
IL_0006:  call        07 00 00 2B 
IL_000B:  ret  

我不是IL专家,但看起来GetNumbers方法(使用Where语法)每次通过循环都会创建一个新对象,因此可能会消耗更多内存而不是GetNumbersOfType方法(使用OfType)。

答案 2 :(得分:2)

listOfItems.Where(d => d is T)返回IEnumerable<U>(其中U是listOfItems中项目的类型),仅包含T类型的项目。

listOfItems.OfType<T>()会返回IEnumerable<T>

答案 3 :(得分:1)

基本上,编译代码的编译运行时配置文件没有区别。 OfType<T>会返回OfTypeIterator,其内部执行is测试,yield return匹配。