ch 11 查询表达式和 LINQ to Objects
intro
- Language Integrated Query (LINQ), C# 3的新特性。
- 标准查询操作符 -> 构成查询表达式 -> 转译为普通c#3 代码 -> 编译(推断、重载、Lambda表达式)
- 序列:一次只能取当前的那个元素。IEnumerable
- 延迟执行和流处理:查询表达式被创建时,不会访问数据,而是在内存中生成了这个查询的表现形式。过滤判断通过委托实例来表示,只有在访问结果的第一个元素的时候,才开始执行。每次只处理一个元素。
- 范围变量 (range variable)
- 声明式而非命令式,函数式编程思想
思考
- 何时使用查询表达式、何时使用点标记?
选择元素: from select
from 范围变量 in 数据源 where 过滤 select 投影
//11-2
from user in SampleData.AllUsers
select user;
转译=>
SampleData.AllUsers.Select(user => user);
显式类型的范围变量:CastOfType
范围变量都可以是隐式类型。Cast,OfType将飞类型化序列转化为强类型。 遇到不匹配类型,Cast报错,OfType跳过。
//11-5
ArrayList list = new ArrayList { "First""Second""Third"};
IEnumerable<string> strings = list.Cast<string>();
list = new ArrayList { 1"not an int"23};
IEnumerable<int> ints = list.OfType<int>();
筛选排序:whereorderby
where
//11-8
User tim = SampleData.Users.TesterTim;
var query = from defect in Sample
where defect.Status != Status.Closed
where defect.AssignedTo = tim
select defect.Summary;
- Q. 多个where,何时合并?
- A. 逻辑上关联的条件合并在一起,逻辑上不关联的保持独立。这里两个where可以合并。
退化的查询表达式:select item
.Select(defect => defect)
查询表达式的结果和源数据永远不会是同一个对象,对返回数据集的改变也不会影响到“主”数据。
orderby descending=>转译=>OrderByDescending()或者ThenByDescending()orderby=>转译=>OrderBy()或者ThenBy(),默认ascendingThenBy()对之前的一个或多个排序规则起辅助作用,是定义为IOrderedEnumerable<T>的扩展方法
透明标识符: let
<CSharp 4 Specification> 7.16.2.4 Fromletwherejoin and orderby clauses
A query expression with a let clause
from x in e let y = fis translated intofrom * in ( e ) . Select ( x => new { x y = f } )
连接:join
内连接:inner join
inner = leftouter = right 内连接从某个对象导航到另一个对象。对右边序列进行缓冲,对左边序列进行流处理。 在SQL中,内连接通常是把某个表的外键和另一个表的主键进行连接。
//11-12
from defect in SampleData.AllDefects
join subscription in SampleData.AllSubscriptions
on defect.Project equals subcription.Project
select new {defect.Summarysubscription.EmailAdress};
send defect to subcripter's emailbox
=>转译=>
leftSequence.Join(rightSequence,
leftKeySelector,
rightKeySelector,
resultSelector)
where
- in left sequence
from defect in SampleData.AllDefects
where defect.Status == Status.Closed
join subscription in SampleData.AllSubscriptions
on defect.Project equals subcription.Project
select new {defect.Summarysubscription.EmailAdress};
- in right sequence
from subscription in SampleData.AllSubscriptions
join defect in (from defect in SampleData.AllDefects
where defect.Status == Status.Closed
select defect)
on defect.Project equals subcription.Project
select new {defect.Summarysubscription.EmailAdress};
分组连接:join into
//11-13
from defect in SampleData.AllDefects
join subscription in SampleData.AllSubscriptions
on defect.Project equals subcription.Project
into groupedSubscriptions
select new { Defect = defectSubscriptions = groupedSubscriptions};
group join != group by: 对于分组连接来说,左边序列和结果序列是一对一,左边元素不匹配任何右边元素时,嵌入序列是空的。
=>转译=>
.GroupJoin()
交叉连接:cross join
不存在序列间的匹配操作,结果包含了所有可能的元素对。笛卡尔积(cartesian join)
//11-15
from user in SampleData.AllUsers
from project in SampleData.AllProjects
select new {User = userProject = project}
- 右边序列依赖于左边元素
// 11-16
from left in Enumerable.Range(14)
from right in Enumerable.Range(11left)
select new {Left = leftRight = right};
=>转译=>
.SelectMany()
分组和延续:group byinto
group by
键和序列的组合封装于IGrouping<TKeyTElement> : IEnemerable<TElement>中。
// 11-17
from defect in SampleData.AllDefects
where defect.AssignedTo != null
group defect by defect.AssignedTo;
=>转译=>
.GroupBy()
group by 的投影
//11-18
from defect in SampleData.AllDefects
where defect.AssignedTo != null
group defect.Summary by defect.AssignedTo;
- 所有查询表达式要以select 或者 group by 子句来结尾。
查询延续(query continuations)
- 把一个查询表达式的结果用作另个一查询表达式的初始序列
- select / group by + into 引入新的范围变量(清除之前的范围变量,只有在延续中声明的范围变量才能在后续使用)。
Q. find scope of defectgrouped and result
// 11-20
from defect in SampleData.AllDefects
where defect.AssignedTo != null
group defect by defect.AssignedTo into grouped
select new { Assignee=grouped.Key
Count=grouped.Count() } into result
orderby result.Count descending
select result;
=>转译=>
SampleData.AllDefects
.Where(defect => defect.AssignedTo != null)
.GroupBy(defect => defect.AssignedTo)
.Select(gouped => new { Assignee = grouped.Key,
Count = grouped.Count()})
.OrderByDescending(result => result.Count);
查询表达式和点标记:dot notation
转译成extension method:查询表达式在编译之前,先被转译为普通的C# (查询操作符、点标记:Enumerable中的扩展方法).
| 查询表达式 | 点标记 |
|---|---|
| - | 没有相应查询表达式: .Reverse().ToDictionary() 等 |
| - | 特定重载 |
| - | 自定义比较器 |
| - | 清晰可读 |
| 多个lambda表达式,多个调用:join 的键选择 | - |
| 排序的多个优先级:orderby f1f2 | .OrderBy(f1).ThenBy(f2) |
// 在匿名类型中直接使用lambda表达式参数
sequence.Select((ItemIndex) => new {ItemIndex});
reference
c sharp in depth LINQ to Objects (C#) MSDN
published in my cs blog: https://sidizhan.github.io/
本文是C# in depth第十一章读书笔记,重点介绍了查询表达式(如from select、where、orderby、join、let、group by等)和LINQ to Objects的使用。探讨了查询表达式的延迟执行、范围变量、筛选排序、连接、分组等概念,并通过示例展示了如何转换为C#代码。







