您的位置:首页 > 其它

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》
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: