[深入学习C#]LINQ查询表达式详解(2)——查询表达式的转换

转载自诗人江湖老,原文地址

  C#在执行LINQ查询表达式的时候,并不会指定其执行语义,而是将查询表达式转换为遵循查询表达式模式的方法的调用。具体而言,查询表达式将转换为以下名称的调用:Where、Select、SelectMany、Join、GroupJoin、OrderBy、OrderByDescending、ThenBy、ThenByDescending、GroupBy、Cast等等。
  如同在前文中提到的用扩展方法和Lambda表达式简化LINQ查询表达式一般,这也是对查询表达式的一种转换。简化后的方法其实就是LINQ查询的实际执行。
  本文中用来示例的代码,参数设定都沿用上一篇文章的设定。

转换规则简述

  从查询表达式到方法调用的转换是一种句法映射,在执行任何类型绑定或重载决策之前发生。该转换可以保证在句法上正确,但不能保证生成语法正确的 C# 代码。转换查询表达式后,以常规方法调用的方式处理生成的方法调用,而这进而可能暴露错误,例如在方法不存在、参数类型错误或方法为泛型方法且类型推断失败这些情况下。
  不允许对查询表达式中的范围变量进行赋值。但允许 C# 实现在某些时候以不实施此限制,因为对于此处介绍的句法转换方案,有些时候可能根本无法实施此限制。
  某些转换使用由 * 指示的透明标识符注入范围变量。

转换规则讲解

带继续符的select和groupby子句的查询表达式的转换

  继续符是指 into 操作符,带有继续符的查询表达式类似如下:
   from ···into x ···
   转换为
   from x in (from ···) ···
   示例:

from c in customersgroup c by c.Country into g
select new { Country=c.Country , Customer = g.Count()}

   转换为

from g infrom c in customersgroup c by c.Country
select new { Country = g.Key, CustCount = g.Count() }

   最终转换为 

customers.
GroupBy(c => c.Country).
Select(g => new { Country = g.Key, CustCount = g.Count() })

含有显式范围变量类型的查询表达式的转换

   显式指定范围变量类型的 from 子句
   from T x in e
   转换为
   from x in (e).Cast<T>()

   显式指定范围变量类型的 join 子句
   join T x in e on k1 equals k2
   转换为
   join x in (e).Cast<T>()

   示例: 

from Customer in customers
where c.City == "London"
select c

   转换为

from c in customers.Cast<Customer>()
where c.City == "London"
select c

   最终转换为

customers.Cast<Customer>().Where(c=>c.City=="London")>

   显式范围变量类型对于查询实现非泛型 IEnumerable 接口的集合很有用,但对于实现泛型IEnumerable 接口的集合没什么用处。如果 customers 属于 ArrayList 类型,则在面的示例中即会如此。
  

退化查询表达式的转换

   退化查询表达式,是指选择源元素本身的查询,如:

from c in customers
select c

  确保查询表达式的结果永不为源对象本身非常重要,因为这样会向查询的客户端暴露源的类型和标识符。因此,在查询表达式为退化查询的时候,可通过在源上显式调用 Select 来保护直接以源代码形式写入的简并查询。然后,由 Select 实施者及其他查询操作员确保这些方法永远不会返回源对象本身。

  退化查询表达式如下:
  from x in e select x
  转换为
  (e).Select(x=>x)
  示例:

from c in customers
select c

  转换为

customers.Select(c => c)

from、 let、 where、 join 和 orderby 子句的转换

转换规则

  带有另一个 from 子句且后接一个 select 子句的查询表达式
  from x1 in e1
  from x2 in e2
  select v
  转换为
  (e1).SelectMany( x1 => e2 , ( x1 , x2 ) => v )

  带有另一个 from 子句且后接一个 select 子句的查询表达式
  from x1 in e1
  from x2 in e2
  ···
  转换为
  from * in (e1).SelectMany( x1 => e2 , ( x1 , x2 ) => new { x1 , x2 })

  带有 let 子句的查询表达式
  from x in e
  let y=f
  转换为
  from * in (e).Select( x => new { x , y = f })

  带有 where 子句的查询表达式
  from x in e
  where f
  ···
  转换为
  from x in (e).Where( x => f )

  带有 join 子句(不含 into)且后接 select 子句的查询表达式
  from x1 in e1
  join x2 in e2 on k1 equals k2
  select v
  转换为
  ( e1 ) . Join( e2 , x1 => k1 , x2 => k2 , ( x1 , x2 ) => v )

  带有 join 子句(不含 into)且后接除 select 子句之外的其他内容的查询表达式
  from x1 in e1
  join x2 in e2 on k1 equals k2
  
  转换为
  from * in ( e1 ) . Join(
  e2 , x1 => k1 , x2 => k2 , ( x1 , x2 ) => new { x1 , x2 })
  

  带有 join 子句(含 into)且后接 select 子句的查询表达式
  from x1 in e1
  join x2 in e2 on k1 equals k2 into g
  select v
  转换为
  ( e1 ) . GroupJoin( e2 , x1 => k1 , x2 => k2 , ( x1 , g ) => v )
  
  带有 join 子句(含 into)且后接除 select 子句之外的其他内容的查询表达式
  from x1 in e1
  join x2 in e2 on k1 equals k2 into g
  
  转换为
  from * in ( e1 ) . GroupJoin(
  e2 , x1 => k1 , x2 => k2 , ( x1 , g ) => new { x1 , g })
  

  带有 orderby 子句的查询表达式
  from x in e
  orderby k1 , k2 , … , kn
  
  转换为
  from x in ( e ) .
  OrderBy ( x => k1 ) .
  ThenBy ( x => k2 ).
  … .
  ThenBy ( x => kn )
  
  如果排序子句指定 descending 方向指示器,则将改为生成对 OrderByDescending 或
ThenByDescending 的调用。

转换规则实例演示

  我们假定,在下面的每个查询表达式中没有 let、 where、 join 或 orderby 子句,并且最多只有一个初始 from 子句。
  
  示例1:

  from c in customersfrom o in c.Ordersselect new { c.Name, o.OrderID, o.Total }

  转换为

customers.
SelectMany(c => c.Orders,(c,o) => new { c.Name, o.OrderID, o.Total })
  • 1
  • 2

  
  示例2:  

from c in customers
from o in c.Orders
orderby o.Total descending
select new { c.Name, o.OrderID, o.Total }

  转换为

from * in customers.SelectMany(c => c.Orders, (c,o) => new { c, o })
orderby o.Total descending
select new { c.Name, o.OrderID, o.Total }

  最终转换为

customers.SelectMany(c => c.Orders, (c,o) => new { c, o }).OrderByDescending(x => x.o.Total).Select(x => new { x.c.Name, x.o.OrderID, x.o.Total })

  其中 x 是编译器生成的以其他方式不可见且不可访问的标识符。

  
  示例3:

from o in orders
let t = o.Details.Sum(d => d.UnitPrice * d.Quantity)
where t >= 1000
select new { o.OrderID, Total = t }

  转换为

from * in orders.
Select(o => new { o, t = o.Details.Sum(d => d.UnitPrice * d.Quantity) })
where t >= 1000
select new { o.OrderID, Total = t }

  最终转换为

orders.Select(o => new { o, t = o.Details.Sum(d => d.UnitPrice * d.Quantity) }).Where(x => x.t >= 1000).Select(x => new { x.o.OrderID, Total = x.t })

  其中 x 是编译器生成的以其他方式不可见且不可访问的标识符。

  示例4:

from c in customers
join o in orders on c.CustomerID equals o.CustomerID
select new { c.Name, o.OrderDate, o.Total }

  转换为

customers.Join(orders, c => c.CustomerID, o => o.CustomerID,(c, o) => new { c.Name, o.OrderDate, o.Total })

  
  示例5:

from c in customers
join o in orders on c.CustomerID equals o.CustomerID into co
let n = co.Count()
where n >= 10
select new { c.Name, OrderCount = n }

  转换为

from * in customers.GroupJoin(orders, c => c.CustomerID, o => o.CustomerID,
(c, co) => new { c, co })
let n = co.Count()
where n >= 10
select new { c.Name, OrderCount = n }

  最终转换为

customers.GroupJoin(orders, c => c.CustomerID, o => o.CustomerID,(c, co) => new { c, co }).Select(x => new { x, n = x.co.Count() }).Where(y => y.n >= 10).Select(y => new { y.x.c.Name, OrderCount = y.n)

  其中 x 和 y 是编译器生成的以其他方式不可见且不可访问的标识符。
  
  示例6:

from o in orders
orderby o.Customer.Name, o.Total descending
select o

  转换为

orders.OrderBy(o => o.Customer.Name).ThenByDescending(o => o.Total)

select 子句的转换

  from x in e select v
  转换为
  ( e ) . Select ( x => v )
  当 v 为标识符 x 时,转换仅为
  ( e )
  

Groupby 子句的转换

  from x in e group v by k
  转换为
  ( e ) . GroupBy ( x => k , x => v )
  当 v 为标识符 x 时,转换为
  ( e ) . GroupBy ( x => k )

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/548650.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

第十八天实验-交换综合

配置思路 eth-trunk &#xff1e; 创建vlan &#xff1e; 划分vlan &#xff1e; trunk干道 &#xff1e; STP &#xff1e; SVI &#xff1e; VRRP &#xff1e; DHCP 一&#xff0c;配置eth-trunk SW&#xff11;&#xff1a; [sw1]int Eth-Trunk 0 ---创建…

[深入学习C#]匿名函数、委托和Lambda表达式

转载自诗人江湖老,原文地址 匿名函数&#xff08;Anonymous Function&#xff09;是表示“内联”方法定义的表达式。匿名函数本身及其内部没有值或者类型&#xff0c;但是可以转换为兼容的委托或者表达式树类型(了解详情)。匿名函数转换的计算取决于转换的目标类型&#xff1a;…

HCIA第九节课作业

子网划分&#xff1a; 如何去合理的分配地址 总共需要多少个网段&#xff1f; 14个网段 环回接口8个6个 192.168.1.0/14 借4位 192.168.1.0/28 192.168.1.16/28 192.168.1.32/28 192.168.1.48/28 可用地址数量是多少?14个地址 子网划分第多少位的时候,只有两个可…

如何下载防盗链的歌曲?

初音&#xff0c;大家都很熟悉了。它是YAHAMA&#xff08;雅马哈&#xff0c;就是生产电子琴的那个公司&#xff09;推出的音乐编辑软件vocaloid的一款产品。 1月27号&#xff08;春节放假回来的前一天&#xff09;就要推出vocaloid 3啦&#xff01;&#xff01; 很喜欢IA的《鸟…

SQL Server大量数据秒级插入/新增/删除

转载自诗人江湖老,原文地址 /// <summary>/// 快速保存数据&#xff0c;自动识别insert和update/// </summary>/// <param name"_sourceTable">需要保存的源数据表</param>/// <param name"_sqlCon">数据库连接</param&g…

在当前主机添加两块网卡,将两块网卡绑定实现网卡冗余操作。

编辑虚拟网络接口配置文件,指定网卡IP 1. change the /etc/sysconfig/network-scripts/ifcfg-bond0 configuration. # cat ifcfg-bond0 DEVICEbond0 BOOTPROTOstatic IPADDR172.24.80.168 NETMASK255.255.255.0 BROADCAST172.24.80.250 ONBOOTyes TYPEEthernet # 不…

如何发布.Net MVC 网站项目(攻略集锦)

转载自诗人江湖老&#xff0c;原文地址 参考文献既然是攻略&#xff0c;就必然有参考文献&#xff0c;放在文章开头&#xff0c;以示敬意&#xff01; 1.MVC项目发布步骤 2.未能从程序集加载“System.ServiceModel.Activation.HttpModule” 3. Win7 IIS 另一个程序正在使用此…

ssh建立原理 及配置两台主机的远程连接实现免密登陆

一、SSH是什么&#xff1f;具体的实现有哪些&#xff1f; SSH是一种协议标准 SSH是用在安全远程登录以及其它安全网络服务 二、SSH原理&#xff1a; SSH为Secure Shell的缩写&#xff0c;默认端口22&#xff0c;由IETF的网络小组&#xff08;Network Working Group&…

ssh-scan处理手记

登陆一台Linux RedHat As4的服务器&#xff0c;发现有很多网络连接&#xff0c;为本机去连接其他服务器的22端口。再一看进程&#xff0c;好多ssh-scan的进程。估计是密码设得太简单&#xff0c;被人家黑了。处理思路&#xff1a;找到ssh-scan进程的相应程序文件&#xff0c;删…

常用 SQL Server 规范集锦

原文地址 常见的字段类型选择   &#xff11;.字符类型建议采用varchar/nvarchar数据类型   &#xff12;.金额货币建议采用money数据类型   &#xff13;.科学计数建议采用numeric数据类型   &#xff14;.自增长标识建议采用bigint数据类型 (数据量一大&#xff…

Linux服务器搭建----Web服务器(apache)

WWW的介绍 万维网&#xff08;亦作“Web”、“WWW”、“W3”&#xff0c;英文全称为“World Wide Web”&#xff09;&#xff0c;是一个由许多互相链接的超文本组成的系统&#xff0c;通过互联网访问。在这个系统中&#xff0c;每个有用的事物&#xff0c;称为一样“资源”&…

Pa interface issue——PA_EXCEED_ROUND_LIMIT

最近处理一个PA_TRANSACTION_INTERFACE_ALL的Error Error_code:PA_EXCEED_ROUND_LIMIT 原因是PA_EXCEED_ROUND_LIMIT is null or too small for cost*invoice_rate - cost*actual_rate. 就是说limit的value为空或者不满足金额*当前汇率与金额*录入时汇率的差额。 问题的难点在于…

C#实现 Linq 序列的Distinct—— IEnumerable.Distinct()——IEqualityComparer

转载自诗人江湖老&#xff0c;原文地址 在C#中使用List或者Collection的时候&#xff0c;我们经常需要使用到Distinct操作&#xff0c;但是微软默认提供的Distinct重载方法并不能满足我们的需求。这时候&#xff0c;我们就需要自己动手做一番工作了。 Distinct方法的重载 Linq…

F5 配置手册 -F5 BIG-IP 10.1-1-激活

F5 配置手册 F5 BIG-IP 10.1-1-激活 统一沟通(中国)有限公司 2012-01-29 前言&#xff1a; F5---太贵! 不过你如果有命运看到这个文章,F5也就不值钱了。 因为:F5提供测试版的虚拟机,我们买不起,总应该玩得起吧! 但愿F5提供更多的机会,给别人机会,也是给自己机会。 参照: 你也可…

[深入学习C#]利用反射给对象赋值

转载自诗人江湖老&#xff0c;原文地址  C#中利用反射能够获取对象的属性信息&#xff0c;也可以利用反射给对象赋值。 我们如果想利用凡是给一个对象属性赋值可以通过PropertyInfo.SetValue()方式进行赋值&#xff0c;但要注意值的类型要与属性保持一致。   假设我们有如下…

快速配置 Samba 将 Linux 目录映射为 Windows 驱动器

一、局域网内的 Linux 服务器上操作步骤&#xff1a;  1、安装samba&#xff08;CentOS Linux&#xff09;&#xff1a; yum install samba system-config-samba samba-client samba-common2、创建www账号 /usr/sbin/groupadd www/usr/sbin/useradd -g www wwwcat /etc/passw…

用委托来实现IEqualityComparer接口

转载自诗人江湖老&#xff0c;原文地址 class FuncEqualityComparer<T> : IEqualityComparer<T> {readonly Func<T, T, bool> _comparer;readonly Func<T, int> _hash;public FuncEqualityComparer( Func<T, T, bool> comparer ): this( compar…

Beginning iCloud in iOS 5 Tutorial Part 2(转载)

原文地址&#xff1a;http://www.raywenderlich.com/6031/beginning-icloud-in-ios-5-tutorial-part-2 Setting Up the User Interface The Xcode project template we chose already set up an empty view controller for us. We will extend it by adding the current docume…

JQuery Datatables 服务端分页简单应用学习

背景介绍 最近在一个简单小项目中碰到需要一个前端数据表格控件&#xff0c;在看了网上的资料后最终选择了JQuery Datatables。Datatables功能及其强大&#xff0c;基本满足我的所有需求&#xff0c;在加上其插件Editor具有inline模式&#xff0c;很多需要直接修改数据的功能不…

开源文化与开源社区

开源文化的形成  1998年2月8日&#xff0c;Bruce Perens发表的著名文章“TheOpen Source Definition”让当时众多的开源爱好者&#xff08;大多是***和程序员&#xff09;对开源理念有了一个清晰的认识。在那之后&#xff0c;更多的人开始关注开源所带来的实用意义和背后的文…