【问题标题】:Optimize Linq Where, using key优化 Linq Where,使用 key
【发布时间】:2012-12-05 19:01:36
【问题描述】:

基础知识:

IDictionary<TKey, TValue> 扩展 IEnumerable<T>

public interface IDictionary<TKey, TValue>: .., 
    IEnumerable<KeyValuePair<TKey, TValue>>
{
  ... 
} 

Enumerable 类提供了在 LINQ 中实现 where 子句的扩展方法

 public static class Enumerable
 {
   ...

   public static IEnumerable<T> Where(this IEnumerable<T>, 
       bool Func<T> predicate) 
   {
       ...
   } 
 }

使用 LINQ 时,编译器会将查询语法转换为对 Enumerable.Where 方法的调用。

当迭代从此方法返回的IEnumerable&lt;T&gt; 时,谓词对集合中的每个 项进行评估。

对应的项目产生结果。

所以这样的请求:

var l_res = from n in List where n.key == 1 select n;

将遍历List 中的每个项目。

如果List 实现IDictionary&lt;TKey, TValue&gt;,并且where 子句位于字典中用作键的属性上,我如何利用该键来避免遍历每条记录并执行查找?

我已经知道我可以测试IEnumerable&lt;T&gt; 是否也是IDictionary&lt;TKey, TValue&gt; 实现,然后选择最好使用的请求:

if(list is IDictionary<int, T>)
{
   var l_res = ((IDictionary<int, T>) list)[1]; 
}
else
{
   var l_res = from n in List where n.key == 1 select n ;
}

但我想知道我是否遗漏了 LINQ 中存在的处理此类键控集合的内容。

注意:LINQ-to0SQL 提供程序使用IQueryable&lt;T&gt; 和表达式树来做同样的事情,但我的问题是关于 LINQ-to-Objects。

【问题讨论】:

  • 你不使用var l_res = ((IDictionary)list)[1];的原因是什么?
  • 真的是您的应用程序的性能问题还是您在进行微优化?
  • 好吧,完全没有理由。我正在通过 .Net MicroFramework 编写 OData Provider。并希望避免使用 IQueryable 导致表达式树的巨大复杂性。然后我想确保在走得更远之前不会错过任何东西......

标签: c# linq c#-4.0 linq-to-objects


【解决方案1】:

不,你没有错过任何东西。 LINQ 知道的唯一键控集合是 Lookup&lt;TKey, TElement&gt; class,即便如此,它也没有做任何特别的事情,它只是归结为 Lookup&lt;TKey, TElement&gt; 实现 IEnumerable&lt;IGrouping&lt;TKey, TElement&gt;&gt; 的事实;所以实际上,您正在迭代 IGrouping&lt;TKey, TElement&gt; interface 实现。

也就是说,LINQ 在IEnumerable&lt;T&gt; interface 之外没有任何特殊的接口知识;碰巧IDictionary&lt;TKey, TValue&gt; interface 扩展了IEnumerable&lt;KeyValuePair&lt;TKey, TValue&gt;&gt;,这就是为什么您可以首先对其执行操作。

但是,对于具有特定查找机制的任何类型,您都需要嗅探类型(在 LINQ 中经常这样做),然后在可能的情况下调用特定于类型的操作。如果类型嗅探失败,您可以求助于与 IEnumerable&lt;T&gt; 一起使用的实现。

就像你在这里做的那样。

【讨论】:

    【解决方案2】:

    您可以在字典上实现自定义Where,以分析传入的表达式,如果它看起来像键控访问,请使用特殊的字典工具。

    在我看来,这只是一个理论上的选择。不应该这样做。相反,应该调整过滤机制,使其以正确的方式使用字典。

    另外请注意,一旦您将不透明的Func&lt;T, bool&gt; 传递给其他代码,这段代码就无法查看您传入的委托内部。它不会注意到存在密钥访问。这就是为什么 LINQ to Objects 不能做到这一点。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-16
      • 2011-02-26
      • 2012-12-19
      相关资源
      最近更新 更多