而对于只实现了IEnumerable接口而没有实现IEnumerable<T>的对象可以通过
public static IEnumerable < TResult > Cast < TResult > ( this IEnumerable source);
来将IEnumerable接口转为IEnumerable<T>(例如ArrayList)。
Linq中的关键字
在C#3.0中,为Linq引入了一些新的关键字,他们是:
from join where group into let orderby select
from
from子句是一个Linq查询的开始,任何一个Linq语句都是以from开始,from子句指定查询的容器,和在此语句有效的局部变量(用来指定容器中的一项,from子句的效果很类似于foreach)。from子句的语法为
from local in container
local就是在此Linq语句中的局部变量,由于container必须为IEnumerable<T>,他的类型可以由container推导出来(即T)。上一段简单的例子:
var container = new List<string> { "张三", "李四", "王五" }; var query = from name in container select name; foreach (string name in query) { Console.WriteLine(name); }
输出
张三
李四
王五
如果container仅仅实现IEnumerable而没有实现IEnumerable<T>,则需要显式指定局部变量的类型,或者是使用Cast转为IEnumerable<T>
var container = new ArrayList { "张三", "李四", "王五" }; var query = from name in container.Cast<string>() select name;
//或者 var query1 = from string name in container select name;
select
对查询的结果进行投影,在子句中指定要选择的列,
有的时候,我们只需要投影某一列,我们可以这样
private static void TestSelectSingleProperty() { var persons = GetPersons(); var query = from p in persons select p.Name; foreach (var item in query) { Console.WriteLine(item); } }
我们还可以指定要投影的列的集合,这个时候我们要用到匿名类型
var query = from p in persons select new { p.ID, p.Name }; foreach (var item in query) { Console.WriteLine("No:{0},Name:{1}",item.ID,item.Name); }
query中的每一项都时候一个拥有ID属性和Name属性的对象,当然有的时候实体的属性名不是我们想要的,或者是通过对属性计算得来的,那么我们可以显式指定属性名,就像下面这样:
var query = from p in persons select new { UserID = p.ID, FriendName = p.Gender == "男" ? "Mr" : "Ms" + p.Name }; foreach (var item in query) { Console.WriteLine("No:{0},Friendly Name:{1}", item.UserID, item.FriendName); }
where
对容器内的数据进行筛选。
var query = from p in persons where p.DepartmentID == 1 select p.Name;
join
类似SQL里的join,Linq中的join子句用于将两个容器的数据以某种关系进行关联。
var departments = GetDepartments(); var persons = GetPersons(); var query = from d in departments join p in persons on d.ID equals p.DepartmentID select new { d, p };
值得注意的是join子句只能使用equals或者是not equal而不能用其他运算符(==都不行)。而equals运算符左边必须联接的左部,右边为右部,不能调换的,否则编译不能通过。
into
into子句用于将join或者是group子句的结果进一步持续化,包装成为一个
System.Linq.IGrouping<TKey, TElement>
对象,而且IGrouping继承自IEnumerable<TElement>,可以看出,IGrouping接口提供分组的键和,该键下所包含的集合。例子见group
group
对结果按照指定的条件进行分组
var container = new List<string> { "ZhangSan", "LiSi", "Wangwu", "ZhaoLiu", "Deng" }; var query = from name in container group name by name.Length into g select new { g.Key, Values = g };
例子演示了通过姓名的长度对一个姓名列表进行分组,并将分组的结果保持到局部变量g中,可以通过下面的代码将query的结果输出
foreach (var group in query) { Console.WriteLine("{0}:", group.Key); foreach (var item in group.Values) { Console.WriteLine(item); } }
let
let子句用于在查询中添加一个新的局部变量,使其在后面的查询中可见
var query = from p in persons let friendlyName = p.Gender == "男" ? "Mr" : "Ms" + p.Name select new { UserID = p.ID, FriendName = friendlyName }; foreach (var item in query) { Console.WriteLine("No:{0},Friendly Name:{1}", item.UserID, item.FriendName); }
在IEnumerable<T>上的其他扩展
Take Skip
用于选取前XX个或者和跳过前XX个,如选择第11到20个则可以
query.Skip(10).Take(10);
OrderBy OrderByDescending
排序而已
query.OrderBy(c => c.Length);
Distinct Union Intersect Except 这些单词都见过吧,分别就是取不重复,并集,交集,差集(这个貌似看看参数就明白了)
Linq的延迟加载特性
Linq查询的执行结果是IEnumerable<T>类型,而对IEnumerable<T>,在内部,C#通过yield关键字实现迭代器达到延迟加载的目的。从而使Linq查询只是在需要的时候才会被执行。
但是,某一些扩展方法在执行时会试图遍历整个容器,从而使延迟加载无效,如排序,聚合函数(Count,Sum,Average等。)
static IEnumerable<int> InfinityInts() { int count = 0; while (true) yield return count++; } public static void LazyLoad() { var query = from i in InfinityInts() select i; foreach (var i in query.Take(20)) { Console.WriteLine(i); } } public static void CantDoLazyLoad() { var query = from i in InfinityInts() select i; foreach (var i in query.OrderBy(i => i).Take(20)) { Console.WriteLine(i); } }