您的位置:首页 > 其它

LINQ之延迟执行标准查询操作符(下)

2012-03-21 23:35 399 查看
操作符:Join

描述:基于匹配键对2个序列的元素进行关联,Join保留了TOuter中元素的顺序,并对于这些元素中的每一个元素,保留TInner中匹配元素的顺序.(来自msdn)

原型:2种

原型一:

publicstaticIEnumerable<TResult>Join<TOuter,TInner,TKey,TResult>( thisIEnumerable<TOuter>outer, IEnumerable<TInner>inner, Func<TOuter,TKey>outerKeySelector, Func<TInner,TKey>innerKeySelector, Func<TOuter,TInner,TResult>resultSelector )

Join的参数比较多,需要特别注意。举个例子:

List<Category>categories=newList<Category> { newCategory{Id=1,Name="Food"}, newCategory{Id=2,Name="Toys"}, newCategory{Id=3,Name="Fruit"} }; List<Product>products=newList<Product> { newProduct{ProductId=201,ProductName="Chuckit",CategoryId=2}, newProduct{ProductId=202,ProductName="SafeMade",CategoryId=2}, newProduct{ProductId=101,ProductName="Taste",CategoryId=1}, newProduct{ProductId=102,ProductName="Canidae",CategoryId=1}, newProduct{ProductId=103,ProductName="Flavor",CategoryId=1} }; varproductList=categories.Join(products,c=>c.Id,p=>p.CategoryId, (c,p)=>new{c.Name,p.ProductName}); foreach(varproductinproductList) { Console.WriteLine("{0}----{1}",product.Name,product.ProductName); }

在这个例子中,我们将categories和products通过categories.Id和products.CategoryId进行关联,返回一个新的对象的序列。

原型二略。

操作符:GroupJoin

描述:基于键相等对两个序列的元素进行关联并对结果进行分组.(来自msdn)

原型:

publicstaticIEnumerable<TResult>GroupJoin<TOuter,TInner,TKey,TResult>( thisIEnumerable<TOuter>outer, IEnumerable<TInner>inner, Func<TOuter,TKey>outerKeySelector, Func<TInner,TKey>innerKeySelector, Func<TOuter,IEnumerable<TInner>,TResult>resultSelector )

GroupJoin和Join很类似,GroupJoin会对返回的结果给予TOuter的Key进行分组。

对于返回的结果,TOuter中的一个元素可能对应多个TInner元素。

注意:如果TInner中没有TOuter的给定元素的关联元素,则该元素的匹配序列为空,但仍将出现在结果中.

举个例子:

List<Category>categories=newList<Category> { newCategory{Id=1,Name="Food"}, newCategory{Id=2,Name="Toys"}, newCategory{Id=3,Name="Fruit"} }; List<Product>products=newList<Product> { newProduct{ProductId=201,ProductName="Chuckit",CategoryId=2}, newProduct{ProductId=202,ProductName="SafeMade",CategoryId=2}, newProduct{ProductId=101,ProductName="Taste",CategoryId=1}, newProduct{ProductId=102,ProductName="Canidae",CategoryId=1}, newProduct{ProductId=103,ProductName="Flavor",CategoryId=1} }; varproductList=categories.GroupJoin(products,c=>c.Id,p=>p.CategoryId, (c,ps)=>new{c.Name,Products=ps.Select(p=>p.ProductName)}); foreach(varproductinproductList) { Console.WriteLine(product.Name+":"); foreach(variteminproduct.Products) { Console.WriteLine(item); } }

打印出来的结果是:

Food:
Taste
Canidae
Flavor
Toys:
Chuckit
SafeMade
Fruit:

我们可以发现Fruit这个category没有匹配的产品,但是也被打印出来了。

操作符:GroupBy
描述:GroupBy主要用来对输入序列进行分组,并且返回IGrouping<K,T>

原型:GroupBy提供了众多的原型,我们主要关注4个:

原型一:

publicstaticIEnumerable<IGrouping<TKey,TSource>>GroupBy<TSource,TKey>( thisIEnumerable<TSource>source, Func<TSource,TKey>keySelector )

List<Product>products=newList<Product> { newProduct{ProductId=201,ProductName="Chuckit",CategoryId=2}, newProduct{ProductId=202,ProductName="SafeMade",CategoryId=2}, newProduct{ProductId=101,ProductName="Taste",CategoryId=1}, newProduct{ProductId=102,ProductName="Canidae",CategoryId=1}, newProduct{ProductId=103,ProductName="Flavor",CategoryId=1} }; varproductList=products.GroupBy(p=>p.CategoryId).OrderBy(item=>item.Key); foreach(varkeyGroupProductsinproductList) { Console.WriteLine(keyGroupProducts.Key+":"); foreach(varproductinkeyGroupProducts) { Console.WriteLine(product.ProductName); } }

输出结果:

1:
Taste
Canidae
Flavor
2:
Chuckit
SafeMade

原型一比较简单,主要包含2个参数,一个输入序列,另外一个是selector。

在这个例子中,我们通过对products序列进行按categoryid进行分组,然后按照分组后的key进行排序。

原型二:

publicstaticIEnumerable<IGrouping<TKey,TSource>>GroupBy<TSource,TKey>( thisIEnumerable<TSource>source, Func<TSource,TKey>keySelector, IEqualityComparer<TKey>comparer )

原型二为我们多提供了IEqualityComparer参数用以自定义比较方法

publicinterfaceIEqualityComparer<inT>

这个接口提供了2个方法boolEquals(Tx,Ty)和intGetHashCode(Tobj)2个方法
注意IEqualityComparer是根据Key来比较的,例如,我们可以写个comparer比较器:
publicclassProductEqualityComparer:IEqualityComparer<int> { publicboolEquals(intx,inty) { if(x==0)returnfalse; if(y==0)returnfalse; returnIsSameVendor(x)==IsSameVendor(y); } publicintGetHashCode(intobj) { intstart=1; intend=100; returnIsSameVendor(obj)?start.GetHashCode():end.GetHashCode(); } publicboolIsSameVendor(intid) { returnid<100; } }

ProductEqualityComparercomparer=newProductEqualityComparer(); List<Product>products=newList<Product> { newProduct{ProductId=201,ProductName="Chuckit",CategoryId=2}, newProduct{ProductId=202,ProductName="SafeMade",CategoryId=200}, newProduct{ProductId=101,ProductName="Taste",CategoryId=1}, newProduct{ProductId=102,ProductName="Canidae",CategoryId=1}, newProduct{ProductId=103,ProductName="Flavor",CategoryId=1} }; IEnumerable<IGrouping<int,Product>>productList=products.GroupBy(p=>p.CategoryId,comparer); foreach(varkeyGroupProductsinproductList) { Console.WriteLine(keyGroupProducts.Key+":"+(comparer.IsSameVendor(keyGroupProducts.Key)?"SameVendor":"DiffVendor")); foreach(varproductinkeyGroupProducts) { Console.WriteLine(product.ProductName); } }原型三:publicstaticIEnumerable<IGrouping<TKey,TElement>>GroupBy<TSource,TKey,TElement>(
thisIEnumerable<TSource>source,
Func<TSource,TKey>keySelector,
Func<TSource,TElement>elementSelector
)

与原型一不一样的是,原型一返回了与输入序列同样的类型元素,原型三可以根据自己的需求,返回所需的元素类型,我们来看个例子:

List<Product>products=newList<Product>
{
newProduct{ProductId=201,ProductName="Chuckit",CategoryId=2},
newProduct{ProductId=202,ProductName="SafeMade",CategoryId=2},
newProduct{ProductId=101,ProductName="Taste",CategoryId=1},
newProduct{ProductId=102,ProductName="Canidae",CategoryId=1},
newProduct{ProductId=103,ProductName="Flavor",CategoryId=1}

};

varproductList=products.GroupBy(p=>p.CategoryId,p=>new{p.ProductId,p.ProductName}).OrderBy(p=>p.Key);

foreach(varkeyGroupProductsinproductList)
{
Console.WriteLine("Category:"+keyGroupProducts.Key);
foreach(varproductinkeyGroupProducts)
{
Console.WriteLine("{0}-{1}",product.ProductId,product.ProductName);
}
}

我们返回的不再是Product,而是我们新建的一个匿名类型,包含ProductId和ProductName

原型四:

publicstaticIEnumerable<IGrouping<TKey,TElement>>GroupBy<TSource,TKey,TElement>(
thisIEnumerable<TSource>source,
Func<TSource,TKey>keySelector,
Func<TSource,TElement>elementSelector,
IEqualityComparer<TKey>comparer
)

原型四实际上是原型二和原型三的结合,比较简单,就不举例子了。

操作符:Distinct

描述:Distinct操作符主要用于过滤重复的元素,2个元素相等是通过GetHashCode和Equals来判断的,原型二为我们提供了自己写对等条件的方法

原型:2种

原型一:

publicstaticIEnumerable<TSource>Distinct<TSource>(
thisIEnumerable<TSource>source
)

这个原型很简单,我们借用msdn的例子来说明:

List<int>ages=newList<int>{21,46,46,55,17,21,55,55};

IEnumerable<int>distinctAges=ages.Distinct();

Console.WriteLine("Distinctages:");

foreach(intageindistinctAges)
{
Console.WriteLine(age);
}

输出结果:

Distinctages:
21
46
55
17

重复的46,55被过滤掉了。

原型二:

publicstaticIEnumerable<TSource>Distinct<TSource>(
thisIEnumerable<TSource>source,
IEqualityComparer<TSource>comparer
)

我们先定义一个相等比较器:

publicclassProductEqualityComparer:IEqualityComparer<Product>
{
publicboolEquals(Productx,Producty)
{
if(x==null||y==null)returnfalse;
returnx.ProductId==y.ProductId&&x.CategoryId==y.CategoryId;
}

publicintGetHashCode(Productobj)
{
intproductid=obj.ProductId;
intcategoryid=obj.CategoryId;
returnproductid.GetHashCode()+categoryid.GetHashCode();
}
}

然后,在查询的时候,应用这个比较器:

ProductEqualityComparercomparer=newProductEqualityComparer();
List<Product>products=newList<Product>
{
newProduct{ProductId=201,ProductName="Chuckit",CategoryId=2},
newProduct{ProductId=201,ProductName="CHUCKIT",CategoryId=2},
newProduct{ProductId=101,ProductName="Taste",CategoryId=1},
newProduct{ProductId=102,ProductName="Canidae",CategoryId=1},
newProduct{ProductId=103,ProductName="Flavor",CategoryId=1}

};

varproductList=products.Distinct(comparer);

foreach(varproductinproductList)
{
Console.WriteLine("{0}:{1}:{2}",product.CategoryId,product.ProductId,product.ProductName);
}

输出结果为:

2:201:Chuckit
1:101:Taste
1:102:Canidae
1:103:Flavor

可以发现,201这个product第二个ProductName为全大写的被过滤掉了,因为根据我们的相等比较器,两个201product是一样的。

操作符:Union

描述:连接2个序列,相同的元素只会保留一个

原型:2种

原型一:

publicstaticIEnumerable<TSource>Union<TSource>(
thisIEnumerable<TSource>first,
IEnumerable<TSource>second
)

这也是个很简单的操作符,我们还是来看个简单的例子:

List<int>int1=newList<int>{12,42,3,2};
List<int>int2=newList<int>{24,3};

varunitInts=int1.Union(int2);
foreach(varunitIntinunitInts)
{
Console.WriteLine(unitInt);
}

输出结果为:

12
42
3
2
24

int2的3被过滤掉了,因为int1已经存在3这个元素了

原型二为我们提供了相等比较器:

publicstaticIEnumerable<TSource>Union<TSource>(
thisIEnumerable<TSource>first,
IEnumerable<TSource>second,
IEqualityComparer<TSource>comparer
)

跟上面的Distinct的原型二类似,我们就不再举例了。

操作符:Intersect

描述:返回2个序列的交集

原型:2种

题外话:看到这里,其实我们就很容易猜出这2种原型是什么了,无非一种是采用默认对等比较的交集操作,另一种是可以自己提供相等比较器的方法

原型一:

publicstaticIEnumerable<TSource>Intersect<TSource>(
thisIEnumerable<TSource>first,
IEnumerable<TSource>second
)

原型二:

publicstaticIEnumerable<TSource>Intersect<TSource>(
thisIEnumerable<TSource>first,
IEnumerable<TSource>second,
IEqualityComparer<TSource>comparer
)

我们举个原型一的例子吧:

List<int>int1=newList<int>{12,42,3,2};
List<int>int2=newList<int>{24,3};

varunitInts=int1.Intersect(int2);
foreach(varunitIntinunitInts)
{
Console.WriteLine(unitInt);
}

结果集只会返回3。

操作符:Except

描述:该操作符返回存在于序列1但是不存在序列2的所有元素

原型:2种

原型一:

publicstaticIEnumerable<TSource>Except<TSource>(
thisIEnumerable<TSource>first,
IEnumerable<TSource>second
)

原型二:

publicstaticIEnumerable<TSource>Except<TSource>(
thisIEnumerable<TSource>first,
IEnumerable<TSource>second,
IEqualityComparer<TSource>comparer
)

我们举个原型一的例子:

List<int>int1=newList<int>{12,42,3,2};
List<int>int2=newList<int>{24,3};

varunitInts=int1.Except(int2);
foreach(varunitIntinunitInts)
{
Console.WriteLine(unitInt);
}

返回的结果集为:

12
42
2

元素3在int2中,所以被过滤掉了,int2中的元素24,不在int1中,所以不会被返回回来。

操作符:Cast

描述:将非泛型的结果皆转成泛型结果集,以支持大部分的标准查询操作符。

如果我们有一个序列是在.net2.0框架上做的,但是,我们又希望用LINQ来操作,此时,我们就可以先将该序列Cast操作后转成IEnumerable<T>.

原型:

publicstaticIEnumerable<TResult>Cast<TResult>(
thisIEnumerablesource
)

借用msdn的例子来说明:

System.Collections.ArrayListfruits=newSystem.Collections.ArrayList();
fruits.Add("apple");
fruits.Add("mango");

IEnumerable<string>query=
fruits.Cast<string>().Select(fruit=>fruit);

foreach(stringfruitinquery)
{
Console.WriteLine(fruit);
}

操作符:OfType

描述:将可以转成某种类型的元素作为结果集输出来,跟Cast一样,OfType的输入序列也是非泛型的IEnumerable

原型:

publicstaticIEnumerable<TResult>OfType<TResult>(
thisIEnumerablesource
)

还是借用msdn的例子:

System.Collections.ArrayListfruits=newSystem.Collections.ArrayList(4);
fruits.Add("Mango");
fruits.Add("Orange");
fruits.Add("Apple");
fruits.Add(3.0);
fruits.Add("Banana");

//ApplyOfType()totheArrayList.
IEnumerable<string>query1=fruits.OfType<string>();

Console.WriteLine("Elementsoftype'string'are:");
foreach(stringfruitinquery1)
{
Console.WriteLine(fruit);
}

ArrayList的签名为:

publicclassArrayList:IList,ICollection,
IEnumerable,ICloneable

它是没有实现IEnumerable<T>,经过OfType后,返回的结果集就支持IEnumerable了。

操作符:AsEnumerable

描述:输入一个IEnumerable<T>序列,返回一个IEnumerable<T>序列。

看到这个描述也许你会觉得很好玩:这不是多此一举吗,没有任何操作,只是为了返回跟输入序列一样的序列。

在稍后的LINQTOSQL我们再来详细讨论这个问题。

原型:

publicstaticIEnumerable<TSource>AsEnumerable<TSource>(
thisIEnumerable<TSource>source
)

操作符:DefaultIfEmpty

原型一:

publicstaticIEnumerable<TSource>DefaultIfEmpty<TSource>(
thisIEnumerable<TSource>source
)

原型二:

publicstaticIEnumerable<TSource>DefaultIfEmpty<TSource>(
thisIEnumerable<TSource>source,
TSourcedefaultValue
)

描述:该操作符主要用来描述,如果序列中的元素为空,该返回什么样的元素。原型一采用默认的对象的构造函数来创建一个空的对象,原型二允许我们定义一个默认的元素。

借用msdn的例子来说明原型二:

classPet
{
publicstringName{get;set;}
publicintAge{get;set;}
}

publicstaticvoidDefaultIfEmptyEx2()
{
PetdefaultPet=newPet{Name="DefaultPet",Age=0};

List<Pet>pets1=
newList<Pet>{newPet{Name="Barley",Age=8},
newPet{Name="Boots",Age=4},
newPet{Name="Whiskers",Age=1}};

foreach(Petpetinpets1.DefaultIfEmpty(defaultPet))
{
Console.WriteLine("Name:{0}",pet.Name);
}

List<Pet>pets2=newList<Pet>();

foreach(Petpetinpets2.DefaultIfEmpty(defaultPet))
{
Console.WriteLine("\nName:{0}",pet.Name);
}
}


输出结果为:

Name:Barley
Name:Boots
Name:Whiskers

Name:DefaultPet
第二个序列为空,因此输出了我们事先定义的默认的对象.


操作符:Range

原型:

publicstaticIEnumerable<int>Range(
intstart,
intcount
)

该操作符返回输入序列的一部分元素

操作符:Repeat

原型:

publicstaticIEnumerable<TResult>Repeat<TResult>(
TResultelement,
intcount
)

对元素element循环count次数

操作符:Empty

原型:

publicstaticIEnumerable<TResult>Empty<TResult>()

描述:这是一个静态方法,但不是一个扩展方法。

IEnumerable<string>strings=Enumerable.Empty<string>();
foreach(stringsinstrings)
Console.WriteLine(s);
Console.WriteLine(strings.Count());

输出结果为:

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