盘问表达式和LINQ to object(下)
接下来我们要研究的大部分城市涉及到透明标识符
let子句和透明标识符let子句不过是引入了一个新的范畴变量。他的值是基于其他范畴变量的。let 标识符=表达式;
首先展示一个不适用let操纵符来使用的按用户名称长度来排序:
... var queryWithoutLet = from user in SampleData.AllUsers orderby user.Name.Length select user; foreach (User user in queryWithoutLet) { Console.WriteLine($"{user.Name}‘s length is {user.Name.Length}"); } ...
可以看得出为了按名称排序*使用了两次Name.Lengthl来进行盘问。这是相当耗费性能的。所以,需要有一种手段来制止这种冗余的计算方法,这就引出了let操纵:它对一个表达式进行求值, 并引入一个新的范畴变量。
... var query = from user in SampleData.AllUsers let length = user.Name.Length orderby length select new { length = length, Name = user.Name }; foreach (var item in query) { Console.WriteLine($"{item.Name}‘s length is {item.length}"); } ...
上述代码孕育产生的功效都不异,只不过只计算了一次Length操纵。代码清单引入了一个新的范畴变量:length,它包罗了用户名的长度(针对原始序列中确当前用户)。我们接着把新的范畴变量用于排序和最后的投影。 你发明问题了吗? 我们需要使用两个范畴变量, 但 Lambda表达式只会给Select通报一个参数(具体原因在后面)! 这就该透明标识符进场了。
我们在最后的投影中使用了两个范畴变量, 不过Select要领只对单个序列起感化。 如何把范畴变量合并在一起呢? 答案是,创建一个匿名类型来包罗两个变量,不过需要进行一个巧妙的转换, 以便看起来就像在select和orderby子句中实际应用了两个参数。
下图展示了这个过程:
上述代码清单的执行过程,此中let子句引入了length范畴变量。
下面是转译后的代码:
... var translatedQuery = SampleData.Users .Select(user => new {user, length = user.Name.Length}) .OrderBy(z => z.length) .Select(z => new {Name = z.user.Name, Length = z.length}); ...
盘问的每个部分都进行了适当的调解:对付原始的盘问表达式直接引用user或length的处所,如果引用产生在let子句之后, 就用z.user或 z.length来取代。这里z这个名称是随机选择的——一切都被编译器隐藏起来。
需要进行说明的是,匿名类型只是一种实现的方法,因为在C#规范上面没有严格规定透明标识符的转移过程,C#规范只是描述了透明标识符应该以怎样的形式去表示。C#的现有编译器是通过匿名类型来实现的,以后不知道会怎样。
联结LINQ中的联结与Sql上面的联结的观点相似,只不过LINQ上面的联结操纵的序列。LINQ有三种种种型的联结,但并不是都是用join关键字,首先来看与sql中的内联结相似的join联结。
关于联结,我筹备先说一个最重要的结论:联结的左边会进行流式传输,而右边会进行缓冲传输,所以,在联结两个序列时,应该尽可能的将较小的阿谁序列放到联结的右侧。这个结论很重要,所以我筹备在章节中多次提及。
MSDN文档在描述计算内联结的jon要领时,将相关的序列称作inner和outer(可以检察IEnumerable<T>.Join()要领。)。这个只是用来区分两个序列的叫法而已,不是真的在指内联结和外联结。对付IEnumerable<T>.Join()来说,outer是指Join的左边,inner是指Join的右边。
首先看一下join的语法:
left-key-selector的类型必需要与right-key-selector的类型匹配(能够进行合理的转换也是有效的),意义上面来说也要相等,我们不能吧一小我私家的出生日期和一个都市的人口做关联。
联结的标记是”equals“而不是“=”或者“==”。
我们也完全有可能用匿名类型来作为键, 因为匿名类型实现了适当的相等性和散列。 如果想创建一个多列的键, 就可以使用匿名类型。
实例: