LINQ之延迟执行标准查询操作符(上)
2012-03-15 23:31
447 查看
标准查询操作符(StandardQueryOperator)和查询表达式(QueryExpression)是实现LINQ查询2种方式。
通过查看IL代码,我们会发现查询表达式编译后也是转换成标准查询操作符的,并且有些查询时无法用查询表达式来操作的,
因此标准查询操作符显得格外重要,我们将分几次介绍他们。
大多数的标准查询操作符静态类:System.Linq.Enumerable的扩展方法,并且将IEnumerable作为其第一个参数。
标准查询操作符包括两种类型:延迟执行和立即执行。我们将分别介绍他们。先介绍延迟执行吧。
操作符:Where
描述:用于包含/过滤数据
类型:延迟执行
原型:2种
第一种原型:
publicstaticIEnumerable<TSource>Where<TSource>(
thisIEnumerable<TSource>source,
Func<TSource,bool>predicate
)
这种原型有2个输入参数,但是由于这是一个扩展方法,因此事实上我们不用传如序列作为第一个参数,
第二个参数是委托,委托的输入参数的类型跟枚举的数据项的类型是一样的,在这个例子中为TSource,
我们可以使用lambda表达式,举个例子:
int[]nums=newint[]{4,6,7,1,0,23,5,9,12};
varevenNums=nums.Where(item=>item%2==0);
foreach(varevenNuminevenNums)
{
Console.WriteLine(evenNum);
}
对比之前的where语句,是不是变得更简单了呢?
第二种原型:
publicstaticIEnumerable<TSource>Where<TSource>(
thisIEnumerable<TSource>source,
Func<TSource,int,bool>predicate
)
第二种原型跟第一种原型的唯一区别是,委托方法多了一个int类型的输入参数,该参数是一个索引,代表数据源的索引,跟C#中的大部分索引一样,都是基于0开始的索引。
举个例子,还是上面的数据源,找出奇数位置上的数字:
int[]nums=newint[]{4,6,7,1,0,23,5,9,12};
varevenNums=nums.Where((p,i)=>(i&1)==0);
foreach(varevenNuminevenNums)
{
Console.WriteLine(evenNum);
}
在这个例子中,我们没有用到元素p本身。
操作符:Select
描述:也称作投射,用于产生选择后的元素或者从原有序列中产生出新的元素序列,新的序列类型可能跟原有数据源的类型不一致
类型:延迟执行
原型:2种
第一种原型:
publicstaticIEnumerable<TResult>Select<TSource,TResult>(
thisIEnumerable<TSource>source,
Func<TSource,TResult>selector
)
这种原型接受一个输入数据源和选择器方法委托作为输入参数,同时返回一个新的,可能跟输入数据源元素类型不一样的对象,即TSource和TResult可能是不一样的。
e.g.
string[]allBrands=newstring[]{"Exuviance","Avene","BabyQuasar","Ecoya","Alterna","EcruNewYork"};
varbrandsLength=allBrands.Select(b=>b.Length);
foreach(varlengthinbrandsLength)
{
Console.WriteLine(length);
}
在这个例子中,数据源是一个字符串数组,查询结果返回的是数组中每个字符串的长度。
再举个例子:
string[]allBrands=newstring[]{"Exuviance","Avene","BabyQuasar","Ecoya","Alterna","EcruNewYork"};
varnamedBrands=allBrands.Select(b=>new{b,b.Length});
foreach(varnamedBrandinnamedBrands)
{
Console.WriteLine("{0}length:{1}",namedBrand.b,namedBrand.Length);
}
这个例子中,返回的是一个新构建的对象。
第二种原型:
publicstaticIEnumerable<TResult>Select<TSource,TResult>(
thisIEnumerable<TSource>source,
Func<TSource,int,TResult>selector
)
跟Where操作符的第二种原型类似,委托方法的第二个参数依旧为数据源序列的0基索引。
e.g.
string[]allBrands=newstring[]{"Exuviance","Avene","BabyQuasar","Ecoya","Alterna","EcruNewYork"};
varnamedBrands=allBrands.Select((b,i)=>new{Index=i+1,b});
foreach(varnamedBrandinnamedBrands)
{
Console.WriteLine("{0}.{1}",namedBrand.Index,namedBrand.b);
}
借助索引,我们很容易知道各个品牌的位置。
操作符:SelectMany
描述:将数据源中的每个元素投射成一个IEnumerable(OfT)并将结果序列合并成一个序列
类型:延迟执行
原型:官方提供了4种,这里主要介绍2种
第一种原型:
publicstaticIEnumerable<TResult>SelectMany<TSource,TResult>(
thisIEnumerable<TSource>source,
Func<TSource,IEnumerable<TResult>>selector
)
这里我们借用msdn的例子来说明:
classPetOwner
{
publicstringName{get;set;}
publicList<String>Pets{get;set;}
}
PetOwner[]petOwners=
{newPetOwner{Name="Higa,Sidney",
Pets=newList<string>{"Scruffy","Sam"}},
newPetOwner{Name="Ashkenazi,Ronen",
Pets=newList<string>{"Walker","Sugar"}},
newPetOwner{Name="Price,Vernette",
Pets=newList<string>{"Scratches","Diesel"}}};
//QueryusingSelectMany().
IEnumerable<string>query1=petOwners.SelectMany(petOwner=>petOwner.Pets);
//Onlyoneforeachloopisrequiredtoiterate
//throughtheresultssinceitisa
//one-dimensionalcollection.
foreach(stringpetinquery1)
{
Console.WriteLine(pet);
}相比之下,如果我们用Select方法:IEnumerable<List<String>>query2=
petOwners.Select(petOwner=>petOwner.Pets);
//Noticethattwoforeachloopsarerequiredto
//iteratethroughtheresults
//becausethequeryreturnsacollectionofarrays.
foreach(List<String>petListinquery2)
{
foreach(stringpetinpetList)
{
Console.WriteLine(pet);
}
Console.WriteLine();
}首先,返回的结果集是不一样的,我们执行查询的时候,步骤也是不一样的。第二种原型:publicstaticIEnumerable<TResult>SelectMany<TSource,TResult>(
thisIEnumerable<TSource>source,
Func<TSource,int,IEnumerable<TResult>>selector
)
跟之前的第二种原型类似,不再举例。
操作符:Take
描述:从序列中返回开始的临近几个元素
类型:延迟执行
原型:一种
publicstaticIEnumerable<TSource>Take<TSource>(
thisIEnumerable<TSource>source,
intcount
)
e.g.
int[]nums=newint[]{4,6,7,1,0,23,5,9,12};
varevenNums=nums.Take(3);
操作符:TakeWhile
描述:返回一个序列中的元素直到某个条件成立
类型:延迟执行
原型:2种
第一种原型:
publicstaticIEnumerable<TSource>TakeWhile<TSource>(
thisIEnumerable<TSource>source,
Func<TSource,bool>predicate
)举个例,我们要从一个int类型的数组中找出所有的元素,直到我们第一次遇到0int[]nums=newint[]{4,6,7,1,0,23,5,9,12};
varevenNums=nums.TakeWhile(item=>item!=0);
foreach(varevenNuminevenNums)
{
Console.WriteLine(evenNum);
}
输出结果为:
4
6
7
1
显然,当遇到0时,查询结束
第二种原型增加一个索引参数,不再赘述。
操作符:Skip
描述:从一个序列中跳过指定数目的元素,注意,是从头开始跳过的,因此使用此方法与排序有关。
类型:延迟执行
原型:一种
publicstaticIEnumerable<TSource>Skip<TSource>(
thisIEnumerable<TSource>source,
intcount
)
e.g.
int[]nums=newint[]{4,6,7,1,0,23};
varevenNums=nums.Skip(3);
foreach(varevenNuminevenNums)
{
Console.WriteLine(evenNum);
}
输出结果为:
1
0
23
即,跳过了刚开始的三个数字
操作符:SkipWhile
描述:跳过所有的元素直到某个指定的条件不再成立,并返回剩下的元素
类型:延迟执行
原型:2种
原型一:
publicstaticIEnumerable<TSource>SkipWhile<TSource>(
thisIEnumerable<TSource>source,
Func<TSource,bool>predicate
)
e.g.
int[]nums=newint[]{4,6,7,1,0,23,5,9,12};
varevenNums=nums.SkipWhile(item=>item<10);
foreach(varevenNuminevenNums)
{
Console.WriteLine(evenNum);
}
该例子返回的结果为:
23
5
9
12
因为,当遇到23时,item<10已经不再成立,因此Skip停止,并返回剩下的所有的元素
原型二与前类似。
参考:《Pro.LINQ.Language.Integrated.Query.in.Csharp.2010》
通过查看IL代码,我们会发现查询表达式编译后也是转换成标准查询操作符的,并且有些查询时无法用查询表达式来操作的,
因此标准查询操作符显得格外重要,我们将分几次介绍他们。
大多数的标准查询操作符静态类:System.Linq.Enumerable的扩展方法,并且将IEnumerable作为其第一个参数。
标准查询操作符包括两种类型:延迟执行和立即执行。我们将分别介绍他们。先介绍延迟执行吧。
操作符:Where
描述:用于包含/过滤数据
类型:延迟执行
原型:2种
第一种原型:
这种原型有2个输入参数,但是由于这是一个扩展方法,因此事实上我们不用传如序列作为第一个参数,
第二个参数是委托,委托的输入参数的类型跟枚举的数据项的类型是一样的,在这个例子中为TSource,
我们可以使用lambda表达式,举个例子:
对比之前的where语句,是不是变得更简单了呢?
第二种原型:
第二种原型跟第一种原型的唯一区别是,委托方法多了一个int类型的输入参数,该参数是一个索引,代表数据源的索引,跟C#中的大部分索引一样,都是基于0开始的索引。
举个例子,还是上面的数据源,找出奇数位置上的数字:
在这个例子中,我们没有用到元素p本身。
操作符:Select
描述:也称作投射,用于产生选择后的元素或者从原有序列中产生出新的元素序列,新的序列类型可能跟原有数据源的类型不一致
类型:延迟执行
原型:2种
第一种原型:
这种原型接受一个输入数据源和选择器方法委托作为输入参数,同时返回一个新的,可能跟输入数据源元素类型不一样的对象,即TSource和TResult可能是不一样的。
e.g.
在这个例子中,数据源是一个字符串数组,查询结果返回的是数组中每个字符串的长度。
再举个例子:
这个例子中,返回的是一个新构建的对象。
第二种原型:
跟Where操作符的第二种原型类似,委托方法的第二个参数依旧为数据源序列的0基索引。
e.g.
借助索引,我们很容易知道各个品牌的位置。
操作符:SelectMany
描述:将数据源中的每个元素投射成一个IEnumerable(OfT)并将结果序列合并成一个序列
类型:延迟执行
原型:官方提供了4种,这里主要介绍2种
第一种原型:
这里我们借用msdn的例子来说明:
petOwners.Select(petOwner=>petOwner.Pets);
//Noticethattwoforeachloopsarerequiredto
//iteratethroughtheresults
//becausethequeryreturnsacollectionofarrays.
foreach(List<String>petListinquery2)
{
foreach(stringpetinpetList)
{
Console.WriteLine(pet);
}
Console.WriteLine();
}
thisIEnumerable<TSource>source,
Func<TSource,int,IEnumerable<TResult>>selector
)
跟之前的第二种原型类似,不再举例。
操作符:Take
描述:从序列中返回开始的临近几个元素
类型:延迟执行
原型:一种
thisIEnumerable<TSource>source,
intcount
)
e.g.
varevenNums=nums.Take(3);
操作符:TakeWhile
描述:返回一个序列中的元素直到某个条件成立
类型:延迟执行
原型:2种
第一种原型:
thisIEnumerable<TSource>source,
Func<TSource,bool>predicate
)
varevenNums=nums.TakeWhile(item=>item!=0);
foreach(varevenNuminevenNums)
{
Console.WriteLine(evenNum);
}
输出结果为:
4
6
7
1
显然,当遇到0时,查询结束
第二种原型增加一个索引参数,不再赘述。
操作符:Skip
描述:从一个序列中跳过指定数目的元素,注意,是从头开始跳过的,因此使用此方法与排序有关。
类型:延迟执行
原型:一种
thisIEnumerable<TSource>source,
intcount
)
e.g.
varevenNums=nums.Skip(3);
foreach(varevenNuminevenNums)
{
Console.WriteLine(evenNum);
}
输出结果为:
1
0
23
即,跳过了刚开始的三个数字
操作符:SkipWhile
描述:跳过所有的元素直到某个指定的条件不再成立,并返回剩下的元素
类型:延迟执行
原型:2种
原型一:
thisIEnumerable<TSource>source,
Func<TSource,bool>predicate
)
e.g.
varevenNums=nums.SkipWhile(item=>item<10);
foreach(varevenNuminevenNums)
{
Console.WriteLine(evenNum);
}
该例子返回的结果为:
23
5
9
12
因为,当遇到23时,item<10已经不再成立,因此Skip停止,并返回剩下的所有的元素
原型二与前类似。
参考:《Pro.LINQ.Language.Integrated.Query.in.Csharp.2010》
相关文章推荐
- LINQ之延迟执行标准查询操作符(中)
- LINQ之非延迟执行标准查询操作符(上)
- LINQ之延迟执行标准查询操作符(下)
- LINQ之非延迟执行标准查询操作符(下)
- LINQ 的查询执行何时是延迟执行,何时是立即执行,以及查询的复用
- LINQ 标准的查询操作符 排序 orderby、thenby、Take
- LINQ 标准的查询操作符 分区 Take 、Skip 、TakeWhile 、SkipWhile
- LINQ 的查询执行何时是延迟执行,何时是立即执行,以及查询的复用
- LINQ入门教程之各种标准查询操作符(一)
- LinQ标准的查询操作符 过滤where,index1,oftype
- Linq to BBJECT之非延时标准查询操作符
- LINQ标准查询操作符学习笔记
- LINQ 标准的查询操作符 排序 orderby、thenby、Take
- LINQ 标准的查询操作符 合计操作符 Count()、Sum()、Min()、Max()、Average()和Aggregate()
- LINQ标准查询操作符
- LinQ标准的查询操作符 符合的from in子句
- LINQ 的查询执行何时是延迟执行,何时是立即执行,以及查询的复用
- Linq to OBJECT延时标准查询操作符
- LINQ 标准的查询操作符 合计操作符 Count()、Sum()、Min()、Max()、Average()和Aggregate()
- LinQ标准的查询操作符 排序order,thenby,Take