是否可以为 linq-to-objects 编译查询
Posted
技术标签:
【中文标题】是否可以为 linq-to-objects 编译查询【英文标题】:Is it possible to compile a query for linq-to-objects 【发布时间】:2011-02-08 15:26:05 【问题描述】:我有一个递归循环中的对象查询的 linq,当对象接近 1000 个并且网站上有超过 100 个用户时,我害怕 - 我的网站会中断。那么是否可以编译一个 linq to objects 查询。
linq 查询除了查找节点的直接子节点之外什么也不做。
【问题讨论】:
能否将递归转换为常规迭代? 【参考方案1】:要了解为什么编译 的概念对于 LINQ to Object 查询没有真正意义,了解 LINQ 是如何实现的很有用。首先,应该清楚的是,以流畅语法编写的 LINQ 查询在编译时由 C# 编译器转换为等效的方法调用语法,无论您使用的是哪种 LINQ 变体:
from person in people
where person.Age < 18
select person.Name
// will be converted to:
people.Where(person => person.Age < 18).Select(person => person.Name)
从现在开始,LINQ 查询基本上是一组带有一些参数的方法调用,通常将一个IEnumerable<T>
对象转换为另一个IEnumerable<T>
对象。延迟执行与编译不同,只需在遍历输出IEnumerable<T>
之前不从原始IEnumerable<T>
获取任何对象即可实现。基本上,延迟执行的方法在不触及原始集合的情况下象征性地对其参数进行操作,从而构建一个可以随意查询内容的生成器。
考虑到这一点,看看上面表达式中的 lambda 表达式 person => person.Age < 18
。它接受一个Person
对象并返回一个bool
。 Lambda 表达式是无类型的;根据推断其类型的上下文,它们可以被视为表达式树或匿名方法。在这种情况下,类型是从Where
扩展方法的参数类型推断出来的。这就是 LINQ to SQL 和 LINQ to Object 的区别所在。在 LINQ to Objects 中,Where
方法只采用 Func<Person, bool>
而不是 Expression<Func<Person, bool>>
。这实质上意味着在 LINQ to Objects 中,C# 编译器将 lambda 表达式编译为匿名方法,并在编译时生成 IL,并将该方法的委托传递给 Where
。
在其他 LINQ 风格中,如 LINQ to SQL,lambda 不编译为 IL。相反,编译器从 lambda 表达式构建一个表达式树对象,并将表达式树传递给 LINQ 方法。 LINQ 方法使用这些表达式树来构建用于查询内容的模型。运行查询时,为使用表达式树表示查询而构建的对象模型将转换为另一种事物(取决于使用的 LINQ 变体),例如 LINQ to SQL 中的 SQL 语句,以便在数据库上执行。这个转换过程在运行时完成,这就是我们所说的LINQ 查询编译。
总结一下,问题是编译成什么? LINQ to Object 不需要在运行时编译的原因是它首先不是表达式树格式;它已经是 IL。
与普通循环相比,您几乎无需担心 LINQ to Objects 的性能。
【讨论】:
很棒的帖子。次要问题:“Lambda 表达式是无类型的”不是我想听到人们到处说的话。 “Lambda表达式的类型由编译器决定,不是程序员声明的”会更好吗? @David B:我认为就 C# 类型系统而言,这是正确的说法。 lambda 表达式本身永远不会有没有上下文的类型(例如,即使您明确声明了i
:var x = (int i) => i;
的类型,语句 var x = i => i;
也是无效的)。从这个意义上说,它类似于方法组。 §6.5 中的 C# 3.0 规范明确指出:“匿名方法表达式或 lambda 表达式被归类为匿名函数(§7.14)。表达式没有类型,但可以隐式转换到兼容的委托类型或表达式树类型。” [强调我的]
规范中使用的语言对我来说很有趣。 “转换”应该暗示某种类型开始转换,不是吗?将 Lambda 表达式默认视为具有 ExpressionTree 类型,编译器在某些情况下隐式转换为委托不是更合适吗? (编辑:我猜不是基于您的 var 示例)
@David B:不,这不是它的工作方式。像 lambdas 和方法组这样的东西不属于 C# 类型系统。 Lambda 被从“lambda 表达式”伪类型(在类型系统中不可观察)转换为从上下文推断的类型。这种转换不像铸造或类似的东西。它直接影响编译器中事物的解释。这与类型之间的隐式转换不同。
这是否意味着对IQueryable<T>
的LINQ to Objects 查询(例如,使用IEnumerable<T>.AsQueryable()
)会导致持续的表达式编译?那么,它是否取决于这个 LINQ to 或 Where
重载使用了什么?【参考方案2】:
与所有优化一样,当您到达那里时,请担心它。在非常高的可能性下,如果您同时连接 100 多个用户,您的瓶颈将在某个地方完全不同。
【讨论】:
以上是关于是否可以为 linq-to-objects 编译查询的主要内容,如果未能解决你的问题,请参考以下文章
对于不同版本的 Microsoft Visual C++,编译的 .lib 文件是不是可以互换?