技术开发 频道

LINQ项目-查询语法


【IT168技术文档】

  C# 的现有 foreach 语句通过 .NET Framework 的 IEnumerable/IEnumerator 方法为迭代提供声明性语法。foreach 语句完全是可选的,但经过证实,它是一个非常方便和常用的语言机制。

  以此为先例,查询语法 通过声明性语法为以下最常用的查询操作符简化了查询表达式:Where、Select、SelectMany、GroupBy、OrderBy、ThenBy、OrderByDescending 和 ThenByDescending。

  下面我们首先看一下本文开头的简单查询:
IEnumerable expr = names .Where(s => s.Length == 5) .OrderBy(s => s) .Select(s => s.ToUpper());
  使用查询语法,我们可以按如下方式重新编写这个语句:
IEnumerable expr = from s in names where s.Length == 5 orderby s select s.ToUpper();
  与 C# 的 foreach 语句一样,查询语法表达式更加简洁、易读,但完全是可选的。每个可以用查询语法编写的表达式都有一个相应的使用点标记的语法(虽然较为冗长)。

  下面,我们首先看一下查询表达式的基本结构。C# 中的每个语法查询表达式都以 from 子句开始,以 select 或 group 子句结束。最初的 from 子句后可以跟随零个或多个 from 或 where 子句。每个 from 子句都是一个在序列中引入迭代变量的生成器,每个 where 子句都是一个从结果中排除项目的筛选器。最终的 select 或 group 子句之前可能会添加指定结果顺序的 orderby 子句。单个查询表达式的简化语法如下所示:
from itemName in srcExpr ((from itemName in srcExpr) | (where predExpr))* (orderby (keyExpr (ascending|descending)?)+)? ((select selExpr) | (group selExpr by keyExpr))
  例如,请考虑以下两个查询表达式:
var query1 = from p in people where p.Age > 20 orderby p.Age descending, p.Name select new { p.Name, Senior = p.Age > 30, p.CanCode }; var query2 = from p in people where p.Age > 20 orderby p.Age descending, p.Name group new { p.Name, Senior = p.Age > 30, p.CanCode } by p.CanCode;
  编译器处理这些查询表达式的方式,就好像它们是使用以下显式点标记编写的:
var query1 = people.Where(p => p.Age > 20) .OrderByDescending(p => p.Age) .ThenBy(p => p.Name) .Select(p => new { p.Name, Senior = p.Age > 30, p.CanCode }); var query2 = people.Where(p => p.Age > 20) .OrderByDescending(p => p.Age) .ThenBy(p => p.Name) .GroupBy(p => p.CanCode, p => new { p.Name, Senior = p.Age > 30, p.CanCode });

  查询表达式基于方法名称执行机械转换。所选择的查询操作符实现取决于所查询的变量类型以及范围内的扩展方法。

  到目前为止,所展示的查询表达式只使用了一个生成器。如果使用多个生成器,则每个后续的生成器将在其前一个生成器的上下文中进行计算。例如,请考虑以下对查询的小修改:
var query = from s1 in names where s1.Length == 5 from s2 in names where s1 == s2 select s1 + " " + s2;
  如果运行以下输入数组:
string[] names = { "Burke", "Connor", "Frank", "Everett", "Albert", "George", "Harris", "David" };
  我们将得到以下结果:
Burke Burke Frank Frank David David
  上述查询表达式扩展为以下点标记表达式:
var query = names.Where(s1 => s1.Length == 5) .SelectMany(s1 => names.Where(s2 => s1 == s2) .Select(s2 => s1 + " " + s2) );
  请注意,使用 SelectMany 会导致内部查询表达式对外部结果失效。

  本部分前面所述的查询表达式的简化语法忽略了一个非常有用的功能。将一个查询的结果视为后续查询的生成器通常很有用。为此,查询表达式将使用 into 关键字在 select 或 group 子句后拼接一个新的查询表达式。下面是简化的语法,它展示 into 关键字如何适应其余的语法:
from itemName in srcExpr ((from itemName in srcExpr) | (where predExpr))* (orderby (keyExpr (ascending|descending)?)+)? ((select selExpr) | (group selExpr by keyExpr)) ( into itemName ((from itemName in srcExpr) | (where predExpr))* (orderby (keyExpr (ascending|descending)?)+)? ((select selExpr) | (group selExpr by keyExpr)) )*
  对于后续处理 group by 子句的结果,into 关键字特别有用。例如,请考虑以下程序:
var query = from item in names orderby item group item by item.Length into lengthGroups orderby lengthGroups.Key descending select lengthGroups; foreach (var group in query) { Console.WriteLine("Strings of length {0}", group.Key); foreach (var val in group.Group) Console.WriteLine(" {0}", val); }
  该程序将输出以下内容:
Strings of length 7 Everett Strings of length 6 Albert Connor George Harris Strings of length 5 Burke David Frank
  本部分描述 C# 如何实现查询表达式。其他语言可能选择通过显式语法支持其他查询操作符。

  需要注意的是,查询语法绝对不是硬连接到标准查询操作符的。它是纯粹的语法功能,通过以适当的名称和签名实现基础方法,来应用于任何符合 LINQ 样式 的类型。上述标准查询操作符是使用扩展方法增加 IEnumerable 接口来实现这一点的。开发人员可以对任何所需的类型使用查询语法,只要确保它符合 LINQ 样式(通过直接实现必需的方法,或者将它们添加为扩展方法)即可。

  这种可扩展性在 LINQ 项目本身中采用,方法是:提供两个支持 LINQ 的 API,分别名为 DLinq(为基于 SQL 的数据访问实现 LINQ 样式)和 Xlinq(允许 LINQ 通过 XML 数据查询)。这两者将在以下部分中描述。
0
相关文章