开篇:在上一篇中,我们了解了预定义委托与Lambda表达式等所谓的新语法,这一篇我们继续征程,看看标准查询运算符和LINQ。标准查询运算符是定义在System.Linq.Enumerable类中的50多个为IEnumerable<T>准备的扩展方法,而LINQ则是一种类似于SQL风格的查询表达式,它们可以大大方便我们的日常开发工作。因此,需要我们予以关注起来!

/* 新语法索引 */

9.标准查询运算符 Standard Query Operator
10.LINQ查询表达式

一、扩展方法哪家强?标准查询运算符:[ C# 3.0/.NET 3.x 新增特性 ]

  标准查询运算符提供了包括筛选、投影、聚合、排序等功能在内的查询功能,其本质是定义在System.Linq.Enumerable类中的50多个为IEnumerable<T>准备的扩展方法

  从上图可以看出,在Enumerable类中提供了很多的扩展方法,这里我们选择其中几个最常用的方法来作一点介绍,使我们能更好地利用它们。首先,我们需要一点数据来进行演示:

    public class Person
{
public int ID { get; set; } public string Name { get; set; } public int Age { get; set; } public bool Gender { get; set; } public override string ToString()
{
return string.Format("{0}-{1}-{2}-{3}", ID, Name, Age,
Gender == true ? "男" : "女");
}
} public class LitePerson
{
public string Name { get; set; } public override string ToString()
{
return Name;
}
} public class Children
{
public int ChildID { get; set; }
public int ParentID { get; set; }
public string ChildName { get; set; } public override string ToString()
{
return string.Format("{0}-{1}-{2}", ChildID, ChildName, ParentID);
}
} static List<Person> GetPersonList()
{
List<Person> personList = new List<Person>()
{
new Person(){ID=,Name="Edison Chou",Age=,Gender=true},
new Person(){ID=,Name="Edwin Chan",Age=,Gender=true},
new Person(){ID=,Name="Jackie Chan",Age=,Gender=true},
new Person(){ID=,Name="Andy Lau",Age=,Gender=true},
new Person(){ID=,Name="Kelly Chan",Age=,Gender=false}
};
return personList;
} static List<Children> GetChildrenList()
{
List<Children> childrenList = new List<Children>()
{
new Children(){ChildID=,ParentID=,ChildName="Lucas"},
new Children(){ChildID=,ParentID=,ChildName="Louise"},
new Children(){ChildID=,ParentID=,ChildName="Edward"},
new Children(){ChildID=,ParentID=,ChildName="Kevin"},
new Children(){ChildID=,ParentID=,ChildName="Mike"}
};
return childrenList;
} static List<Person> GetMorePersonList()
{
List<Person> personList = new List<Person>()
{
new Person(){ID=,Name="爱迪生",Age=,Gender=true},
new Person(){ID=,Name="瓦特",Age=,Gender=true},
new Person(){ID=,Name="牛顿",Age =,Gender=true},
new Person(){ID=,Name="图灵",Age=,Gender=true},
new Person(){ID=,Name="香农",Age=,Gender=true},
new Person(){ID=,Name="居里夫人",Age=,Gender=false},
new Person(){ID=,Name="居里夫人2",Age=,Gender=false},
new Person(){ID=,Name="居里夫人3",Age=,Gender=false},
new Person(){ID=,Name="居里夫人4",Age=,Gender=false},
new Person(){ID=,Name="居里夫人5",Age=,Gender=false},
new Person(){ID=,Name="居里夫人6",Age=,Gender=false},
new Person(){ID=,Name="居里夫人7",Age=,Gender=false},
new Person(){ID=,Name="居里夫人8",Age=,Gender=false},
new Person(){ID=,Name="居里夫人9",Age=,Gender=false},
new Person(){ID=,Name="居里夫人10",Age=,Gender=false},
new Person(){ID=,Name="居里夫人11",Age=,Gender=false},
new Person(){ID=,Name="居里夫人12",Age=,Gender=false},
new Person(){ID=,Name="居里夫人13",Age=,Gender=false},
new Person(){ID=,Name="居里夫人14",Age=,Gender=false}
}; return personList;
}

1.1 筛选高手Where方法

  Where方法提供了我们对于一个集合的筛选功能,但需要提供一个带bool返回值的“筛选器”(匿名方法、委托、Lambda表达式均可),从而表明集合中某个元素是否应该被返回。这里,我们以上面的数据为例,筛选出集合中所有性别为男,年龄大于20岁的子集合,借助Where方法实现如下:

        static void SQOWhereDemo()
{
List<Person> personList = GetPersonList(); List<Person> maleList = personList.Where(p =>
p.Gender == true && p.Age > ).ToList();
maleList.ForEach(m => Console.WriteLine(m.ToString()));
}

  (1)运行结果如下图所示:

  (2)由本系列文章的第二篇可知,扩展方法的本质是在运行时调用扩展类的静态方法,而我们写的Lambda表达式在编译时又会被转为匿名方法(准确地说应该是预定义泛型委托实例)作为方法参数传入扩展方法中,最后调用执行该扩展方法生成一个新的List集合返回。

1.2 投影大牛Select方法

  Select方法可以查询投射,返回新对象集合。这里,假设我们先筛选出所有男性集合,再根据男性集合中所有项的姓名生成子集合(这是一个不同于原类型的类型),就可以借助Select方法来实现。

        static void SQOSelectDemo()
{
List<Person> personList = GetPersonList(); List<LitePerson> liteList = personList.Where(p =>
p.Gender == true).Select(
p => new LitePerson() { Name = p.Name }).ToList();
liteList.ForEach(p => Console.WriteLine(p.ToString()));
}

  (1)运行结果如下图所示:

  (2)这里也可以采用匿名类,可以省去事先声明LitePerson类的步凑,但需要配合var使用:

var annoyList = personList.Where(p =>
p.Gender == true).Select(
p => new { Name = p.Name }).ToList();

  (3)这里因为实现LitePerson类重写了ToString()方法,所以这里直接调用了ToString()方法。

1.3 排序小生OrderBy方法

  说到排序,我们马上想起了SQL中的order by语句,而标准查询运算符中也为我们提供了OrderBy这个方法,值得一提的就是我们可以进行多条件的排序,因为OrderBy方法返回的仍然是一个IEnumerable<T>的类型,仍然可以继续使用扩展方法。但要注意的是,第二次应该使用ThenBy方法。

        static void SQOOrderByDemo()
{
List<Person> personList = GetPersonList();
// 单条件升序排序
Console.WriteLine("Order by Age ascending:");
List<Person> orderedList = personList.OrderBy(p => p.Age).ToList();
orderedList.ForEach(p => Console.WriteLine(p.ToString()));
// 单条件降序排序
Console.WriteLine("Order by Age descending:");
orderedList = personList.OrderByDescending(p => p.Age).ToList();
orderedList.ForEach(p => Console.WriteLine(p.ToString()));
// 多条件综合排序
Console.WriteLine("Order by Age ascending and ID descending:");
orderedList = personList.OrderBy(p => p.Age)
.ThenByDescending(p => p.ID).ToList();
orderedList.ForEach(p => Console.WriteLine(p.ToString()));
}

  运行结果如下图所示:

1.4 连接道士Join方法

  在数据库中,我们对两个表或多个表进行连接查询时往往会用到join语句,然后指定两个表之间的关联关系(例如: a.bid = b.aid)。在标准查询运算符中,细心的.NET基类库也为我们提供了Join方法。现在,假设我们有两个类:Person和Children,其中每个Children对象都有一个ParentID,对应Person对象的ID,现需要打印出所有Person和Children的信息,可以借助Join方法来实现。

        static void SQOJoinDemo()
{
List<Person> personList = GetPersonList();
List<Children> childrenList = GetChildrenList(); // 连接查询
var joinedList = personList.Join(childrenList,
p => p.ID, c => c.ParentID, (p, c) => new
{
ParentID = p.ID,
ChildID = c.ChildID,
ParentName = p.Name,
ChildName = c.ChildName
}).ToList();
joinedList.ForEach(c => Console.WriteLine(c.ToString()));
}

  运行结果如下图所示:

1.5 分组老师GroupBy方法

  在数据库中,我们要对查询结果进行分组会用到 group by 语句,在标准查询运算符中,我们也有对应的GroupBy方法。这里,假设我们对Person数据集按照性别进行分类,该怎么来写代码呢?

        static void SQOGroupByDemo()
{
List<Person> personList = GetPersonList(); IEnumerable<IGrouping<bool, Person>> groups =
personList.GroupBy(p => p.Gender);
IList<IGrouping<bool, Person>> groupList = groups.ToList(); foreach (IGrouping<bool, Person> group in groupList)
{
Console.WriteLine("Group:{0}", group.Key ? "男" : "女");
foreach (Person p in group)
{
Console.WriteLine(p.ToString());
}
}
}

  (1)这里需要注意的是:通过GroupBy方法后返回的是一个IEnumerable<IGrouping<TKey, TSource>>类型,其中TKey是分组依据的类型,这里是根据Gender来分组的,而Gender又是bool类型,所以TKey这里为bool类型。TSource则是分组之后各个元素的类型,这里是将List<Person>集合进行分组,因此分完组后每个元素都存储的是Person类型,所以TSource这里为Person类型,Do you understand now?

  (2)运行结果如下图所示:

  (3)可能有人会说我咋记得住GroupBy返回的那个类型,太长了,我也不想记。怎么办呢?不怕,我们可以使用var关键字嘛:

            var annoyGroups = personList.GroupBy(p => p.Name).ToList();
foreach (var group in annoyGroups)
{
Console.WriteLine("Group:{0}", group.Key);
foreach (var p in group)
{
Console.WriteLine(p.ToString());
}
}

1.6 分页实战Skip与Take方法

  相信很多人都使用过标准查询运算符进行分页操作,这里我们再次来看看如何借助Skip与Take方法来实现分页操作。还是以PersonList集合为例,假如页面上的表格每页显示5条数据,该怎么来写代码呢?

        static void SQOPagedDemo()
{
// 这里假设每页5行数据
// 第一页
Console.WriteLine("First Page:");
var firstPageData = GetPagedListByIndex(, );
firstPageData.ForEach(d => Console.WriteLine(d.ToString()));
// 第二页
Console.WriteLine("Second Page:");
var secondPageData = GetPagedListByIndex(, );
secondPageData.ForEach(d => Console.WriteLine(d.ToString()));
// 第三页
Console.WriteLine("Third Page:");
var thirdPageData = GetPagedListByIndex(, );
thirdPageData.ForEach(d => Console.WriteLine(d.ToString()));
} static List<Person> GetPagedListByIndex(int pageIndex, int pageSize)
{
List<Person> dataList = GetMorePersonList();
return dataList.Skip((pageIndex - ) * pageSize)
.Take(pageSize).ToList();
}

  运行结果如下图所示:

1.7 浅谈延迟加载与即时加载

  (1)延迟加载(Lazy Loading):只有在我们需要数据的时候才去数据库读取加载它。

  在标准查询运算符中,Where方法就是一个典型的延迟加载案例。在实际的开发中,我们往往会使用一些ORM框架例如EF去操作数据库,Where方法的使用则是每次调用都只是在后续生成SQL语句时增加一个查询条件,EF无法确定本次查询是否已经添加结束,所以没有办法木有办法在每个Where方法执行的时候确定最终的SQL语句,只能返回一个DbQuery对象,当使用到这个DbQuery对象的时候,才会根据所有条件生成最终的SQL语句去查询数据库。

    var searchResult = personList.Where(p =>
p.Gender == false).Where(p => p.Age > )
.Where(p=>p.Name.Contains("奶茶"));

  (2)即时加载(Eager Loading):加载数据时就把该对象相关联的其它表的数据一起加载到内存对象中去。

  在标准查询运算符中,FindAll方法就是一个典型的即时加载案例。与延迟加载相对应,在开发中如果使用FindAll方法,EF会根据方法中的条件自动生成SQL语句,然后立即与数据库进行交互获取查询结果,并加载到内存中去。

        var searchResult = personList.FindAll(p=>p.Gender == false
&& p.Name.Contains("奶茶"));

二、查询方式谁更快?LINQ:[ C# 3.0/.NET 3.x 新增特性 ]

2.1 初识LINQ:类似SQL风格的代码

  LINQ又称语言集成查询,它是C# 3.0的新语法。在更多的人看来,它是一种方便的查询表达式,或者说是和SQL风格接近的代码

    var maleList = from p in personList
where p.Gender == true
select p;

  (1)LINQ表达式以"from"开始,以"select 或 group by子句"结尾;

  (2)LINQ表达式的输出是一个 IEnumerable<T> 或 IQueryable<T> 集合;(注:T 的类型 由 select 或 group by 推断出来)

2.2 LINQ使用:实现除Skip和Take外的标准查询运算符的功能

  (1)基本条件查询:

            List<Person> personList = GetPersonList();
List<Children> childList = GetChildrenList();
// 基本条件查询
Console.WriteLine("Basic Query:");
var maleList = from p in personList
where p.Gender == true
select p;
maleList.ToList().ForEach(m =>
Console.WriteLine(m.ToString()));

  (2)排序条件查询:

            // 排序条件查询
Console.WriteLine("Order Query:");
var orderedList = from p in personList
orderby p.Age descending
orderby p.Name ascending
select p;
orderedList.ToList().ForEach(m =>
Console.WriteLine(m.ToString()));

  (3)连接查询:

            // Join连接查询
Console.WriteLine("Join Query:");
var joinedList = from p in personList
join c in childList
on p.ID equals c.ParentID
select new
{
Person = p,
Child = c
};
foreach (var item in joinedList)
{
Console.WriteLine(item.ToString());
}

  (4)分组查询:

            // 分组条件查询
Console.WriteLine("Group Query:");
var groupList = from p in personList
group p by p.Gender;
foreach (var group in groupList)
{
Console.WriteLine("Group:{0}",
group.Key? "男":"女");
foreach(var item in group)
{
Console.WriteLine(item.ToString());
}
}

  运行结果请参考上一节标准查询运算符中相关的运行结果,或下载附件运行查看,这里不再贴图。

2.3 LINQ本质:生成对应的标准查询运算符

  作为一个细心的.Net码农,我们不由得对LINQ表达式为我们做了哪些工作而好奇?于是,我们又想起了我们的“滑板鞋”—Reflector或ILSpy,去看看编译器为我们做了什么事!

  (1)以上述的基本条件查询代码为例,我们看到原来编译器将LINQ生成了对应的标准查询运算符,即Where扩展方法:

  (2)再来看看排序条件查询的代码,也是生成了对应的标准查询运算符,即OrderBy扩展方法:

  (3)总结:LINQ编译后会生成对应的标准查询运算符(查询->Where,排序->OrderBy,连接->Join,分组->GroupBy),所以LINQ表达式其实就是类似于SQL风格的一种更加友好语法糖而已。其本质还是扩展方法、泛型委托等“旧酒”,被一个“新瓶子”所包装了起来,就变得高大上了。

系列总结

  转眼之间,四篇文章的介绍就到此结束了,其实本系列介绍的都是不算新语法,其实也可以说成是老语法了。说它们新,只不过是相对于.NET老版本而言,而且平时开发中大家有可能没有注意到的一些细节,本系列做了一个简单的介绍。这几天看到很多园子里的童鞋开始关注C# 6.0的新特性了,粗略看了看,语法糖居多,相信经过了这一系列的探秘,对于新的语法糖,我们可以站在一个比较高的高度去看待它们。最后,谢谢各位园友的浏览,以及给我的一些鼓励,再次感谢!

参考文章

  (1)MSDN,《标准查询运算符概述》:http://msdn.microsoft.com/zh-cn/library/bb397896.aspx

  (2)MSDN,《LINQ(语言继承查询)》:http://msdn.microsoft.com/library/bb397926.aspx

  (3)jake强,《为提高EF性能需要注意哪些事情?》:http://www.cnblogs.com/jake1/archive/2013/04/25/3043664.html

附件下载

  NewGrammerDemo-v1.3:http://pan.baidu.com/s/1pJnyKtX

作者:周旭龙

出处:http://www.cnblogs.com/edisonchou/

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。

.NET中那些所谓的新语法之四:标准查询运算符与LINQ的更多相关文章

  1. .NET中那些所谓的新语法

    .NET中那些所谓的新语法之四:标准查询运算符与LINQ 摘要: 开篇:在上一篇中,我们了解了预定义委托与Lambda表达式等所谓的新语法,这一篇我们继续征程,看看标准查询运算符和LINQ.标准查询运 ...

  2. .NET中那些所谓的新语法之一:自动属性、隐式类型、命名参数与自动初始化器

    开篇:在日常的.NET开发学习中,我们往往会接触到一些较新的语法,它们相对以前的老语法相比,做了很多的改进,简化了很多繁杂的代码格式,也大大减少了我们这些菜鸟码农的代码量.但是,在开心欢乐之余,我们也 ...

  3. .NET中那些所谓的新语法之二:匿名类、匿名方法与扩展方法

    开篇:在上一篇中,我们了解了自动属性.隐式类型.自动初始化器等所谓的新语法,这一篇我们继续征程,看看匿名类.匿名方法以及常用的扩展方法.虽然,都是很常见的东西,但是未必我们都明白其中蕴含的奥妙.所以, ...

  4. .NET中那些所谓的新语法之三:系统预定义委托与Lambda表达式

    开篇:在上一篇中,我们了解了匿名类.匿名方法与扩展方法等所谓的新语法,这一篇我们继续征程,看看系统预定义委托(Action/Func/Predicate)和超爱的Lambda表达式.为了方便码农们,. ...

  5. C#3.0新增功能09 LINQ 标准查询运算符 02 查询表达式语法

    连载目录    [已更新最新开发文章,点击查看详细] 某些使用更频繁的标准查询运算符具有专用的 C# 语言关键字语法,使用这些语法可以在查询表达式中调用这些运算符. 查询表达式是比基于方法的等效项更具 ...

  6. LINQ to Entities 查询中的标准查询运算符

    投影和筛选方法 投影指的是转换的结果集到所需的窗体中的元素. 例如,可以从结果集中的每个对象投影所需的属性子集,可以投影一个属性并对其执行数学计算,也可以从结果集投影整个对象. 投影方法有 Selec ...

  7. 1、ASP.NET MVC入门到精通——新语法

    本系列目录:ASP.NET MVC4入门到精通系列目录汇总 在学习ASP.NET MVC之前,有必要先了解一下C#3.0所带来的新的语法特性,这一点尤为重要,因为在MVC项目中我们利用C#3.0的新特 ...

  8. MVC基础知识 – 2.新语法

    1.自动属性 Auto-Implemented Properties 2.隐式类型 var 3.参数默认值 和 命名参数 4.对象初始化器 与 集合初始化器 { } 5.匿名类 & 匿名方法 ...

  9. .net学习之新语法学习(匿名类和匿名方法,扩展方法,系统内置委托,Lambda表达式和linq等)

    1.自动属性 Auto-Implemented Properties 2.隐式类型 var  var变量不能作为全局变量使用,因为不能在编译时确定类型 3.参数默认值 和 命名参数 4.对象初始化器 ...

随机推荐

  1. C++ 系列:虚函数

    Copyright © 1900-2016, NORYES, All Rights Reserved. http://www.cnblogs.com/noryes/ 欢迎转载,请保留此版权声明. -- ...

  2. Solr与MySQL查询性能对比

    本文简单对比下Solr与MySQL的查询性能速度. 测试数据量:10407608     Num Docs: 10407608 这里对MySQL的查询时间都包含了从MySQL Server获取数据的时 ...

  3. js对象克隆方法

    方法1: function clone(obj){ var o; switch(typeof obj){ case 'undefined': break; case 'string' : o = ob ...

  4. 分享一个分布式消息总线,基于.NET Socket Tcp的发布-订阅框架,附代码下载

    一.分布式消息总线 在很多MIS项目之中都有这样的需求,需要一个及时.高效的的通知机制,即比如当使用者A完成了任务X,就需要立即告知使用者B任务X已经完成,在通常的情况下,开发人中都是在使用者B所使用 ...

  5. maven权威指南学习笔记(三)——一个简单的maven项目

    目标: 对构建生命周期 (build  lifecycle),Maven仓库 (repositories),依赖管理 (dependency management)和项目对象模型 (Project O ...

  6. windows 物理内存获取

    由于我一般使用的虚拟内存, 有时我们需要获取到物理内存中的数据(也就是内存条中的真实数据), 按理说是很简单,打开物理内存,读取就可以了.但似乎没这么简单: #include "window ...

  7. 消息中间件MetaQ高性能原因分析-转自阿里中间件

    简介 MetaQ是一款高性能的消息中间件,经过几年的发展,已经非常成熟稳定,历经多年双11的零点峰值压测,表现堪称完美. MetaQ当前最新最稳定的稳本是3.x系统,MetaQ 3.x重新设计和实现, ...

  8. c#保留小数点后位数的方法

    Double dValue = 95.12345; ; string strValue = "95.12345"; string result = ""; re ...

  9. 用Fiddler的自动响应模拟系统集成

    1. 下载最新版本的Fiddler Fiddler 官网 2, 安装并启动Fiddler 3, 勾选自动响应 见上图 4, 添加自动响应规则 见上图 5, 添加自动响应内容文件 添加响应文件到Fidd ...

  10. HDU5800 To My Girlfriend(DP)

    题目 Source http://acm.hdu.edu.cn/showproblem.php?pid=5800 Description Dear Guo I never forget the mom ...