【发布时间】:2009-08-05 05:04:00
【问题描述】:
在针对对象集合的 LINQ 查询的幕后究竟发生了什么?它只是语法糖还是发生了其他事情使其更有效的查询?
【问题讨论】:
标签: linq
在针对对象集合的 LINQ 查询的幕后究竟发生了什么?它只是语法糖还是发生了其他事情使其更有效的查询?
【问题讨论】:
标签: linq
您是指查询表达式,还是查询在后台执行的操作?
查询表达式首先扩展为“普通”C#。例如:
var query = from x in source
where x.Name == "Fred"
select x.Age;
翻译成:
var query = source.Where(x => x.Name == "Fred")
.Select(x => x.Age);
确切的含义取决于source的类型当然...在LINQ to Objects中,它通常实现IEnumerable<T>和Enumerable扩展方法发挥作用。 ..但它可能是一组不同的扩展方法。 (例如,LINQ to SQL 将使用 Queryable 扩展方法。)
现在,假设我们正在使用LINQ to Objects...扩展方法扩展后,上面的代码变为:
var query = Enumerable.Select(Enumerable.Where(source, x => x.Name == "Fred"),
x => x.Age);
接下来Select 和Where 的实现变得很重要。忽略错误检查,它们是 像这样的:
public static IEnumerable<T> Where<T>(this IEnumerable<T> source,
Func<T, bool> predicate)
{
foreach (T element in source)
{
if (predicate(element))
{
yield return element;
}
}
}
public static IEnumerable<TResult> Select<TSource, TResult>
(this IEnumerable<TSource> source,
Func<TSource, TResult> selector)
{
foreach (TSource element in source)
{
yield return selector(element);
}
}
接下来是将迭代器块扩展为状态机,我不会在这里讨论,但我有一个 article about。
最后,将 lambda 表达式转换为额外的方法 + 适当的委托实例创建(或表达式树,取决于被调用方法的签名)。
所以基本上 LINQ 使用了 很多 C# 的巧妙功能:
但是,单个操作非常简单——它们不执行索引等操作。连接和分组是使用哈希表完成的,但是像“where”这样的简单查询只是线性的。不要忘记 LINQ to Objects 通常只是将数据视为仅向前可读的序列 - 它不能执行二进制搜索之类的操作。
通常我希望手写查询比 LINQ to Objects 稍微快一点,因为抽象层较少,但它们的可读性会降低,而且性能差异通常不会很大。
与以往一样,对于性能问题:如有疑问,请衡量!
【讨论】:
如果您需要更好的性能,请考虑尝试 i4o - 对象索引。它为大型集合(想想 100,000+ 行)构建内存对象,然后 LINQ 使用这些对象来加速查询。您需要大量数据才能完成这项工作,但改进令人印象深刻。
【讨论】:
这只是语法糖 - 没有魔法。
您可以用“速记”、C# 或其他任何方式写出等效的代码,并且执行相同的代码。
(当然,编译器会很好地生成高效的代码,所以它生成的代码可能比你自己编写的代码效率高一点,只是因为你可能不知道最高效的编写方式该代码。)
【讨论】: