Linq 学习笔记(二)
下面就來介紹一些查詢的示例:
1。Linq查詢
var racers = from r in Formula1.GetChampions()
where r.Wins > 15 && (r.Country == "Brazil" || r.Country == "Austria") select r;
foreach (var r in racers)
{
Responose.Write("{0:A}", r);
}
使用擴展方法的查詢
并不是所有的查詢都可以用LINQ查詢完成。也不是所有的擴展方法都映射到LINQ查詢子句上。高級查詢需要使用擴展方法。為了更好地理解帶擴展方法的復雜查詢,最好看看簡單的查詢是如何映射的。使用擴展方法Where()和Select(),會生成與前面LINQ 查詢非常類似的結果:
var racers = Formula1.GetChampions().Where(r => r.Wins > 15 && (r.Country == "Brazil" || r.Country == "Austria")).Select(r = > r);
2。用索引來過濾
不能使用LINQ 查詢的一個例子是Where()方法的重載。在Where()方法的重載中,可以傳送第二個參數——索引。索引是過濾器返回的每個結果的計數器。可以在表達式中使用這個索引,執行基于索引的計算。下面的代碼由Where()擴展方法調用,它使用索引返回姓氏以A開頭、索引為偶數的賽手:
var racers = Formula1.GetChampions().Where((r, index) => r.LastName.StartsWith("A") &&
index % 2 != 0);
foreach (var r in racers)
{
Responose.Write("{0:A}", r);
}
3。類型過濾
為了進行基于類型的過濾,可以使用OfType()擴展方法。這里數組數據包含string和int對象。使用OfType()擴展方法,把string類傳送給泛型參數,就從集合中返回字符串。
object[] data = { "one", 2, 3, "four", "five",6 };
var query = data.OfType<string>();
foreach (var s in query)
{
Console.WriteLine(s);
}
4。復合的from子句
var ferrariDrivers = from r in Formula1.GetChampions() from c in r.Cars where c == "Ferrari"
orderby r.LastName select r.FirstName + " " + r.LastName;?
C#編譯器把復合的from 子句和LINQ 查詢轉換為SelectMany()擴展方法。SelectMany()可用于迭代序列的序列。示例中SelectMany()方法的重載版本如下所示:
public static IEnumerable<TResult> SelectMany<TSource,TCollection,TResult>(this IEnumerable<TSource> source,Func<TSource,IEnumerable<TCollection>> collectionSelector,Func<TSource,TCollection,TResult>resultSelector);
第一個參數是隱式參數,從GetChampions()方法中接收Racer對象序列。第二個參數是collectionSelector委托,它定義了內部序列。在λ表達式r=>r.Cars中,應返回賽車集合。第三個參數是一個委托,現在為每個賽車調用該委托,接收Racer和Car對象。λ表達式創建了一個匿名類型,它帶Racer和Car屬性。這個SelectMany()方法的結果是攤平了賽手和賽車的層次結構,為每輛賽車返回匿名類型的一個新對象集合。
這個新集合傳送給Where()方法,過濾出駕駛Ferrari 的賽手。最后,調用OrderBy()和Select()方法:
var ferrariDrivers = Formula1.GetChampions().SelectMany(r => r.Cars,
(r, c) => new { Racer = r, Car = c }).Where(r => r.Car == "Ferrari").OrderBy(r =>r.Racer.LastName).Select(r => r.Racer.FirstName + " " +r.Racer.LastName);
把SelectMany()泛型方法解析為這里使用的類型,所解析的類型如下所示。在這個例子中,數據源是
Racer類型,所過濾的集合是一個string數組,當然所返回的匿名類型的名稱是未知的,這里顯示為TResult:
public static IEnumerable<TResult> SelectMany<Racer, string, TResult>(
this IEnumerable<Racer> source,Func<Racer,IEnumerable<string>> collectionSelector,
Func<Racer,string,TResult> resultSelector);
查詢僅從LINQ 查詢轉換為擴展方法,所以結果與前面的相同。
5。排序
var racers = from r in Formula1.GetChampions() where r.Country == "Brazil" orderby r.Wins descending select r;
orderby子句解析為OrderBy()方法,orderby descending子句解析為OrderBy Descending()方法:
var racers = Formula1.GetChampions().Where(r => r.Country == "Brazil").OrderByDescending(r => r.Wins).Select(r => r);
OrderBy()和OrderByDescending()方法返回IOrderEnumerable<TSource>。這個接口派生于接口IEnumerable<TSource>,但包含一個額外的方法CreateOrderedEnumerable-<TSource>()。這個方法用于進一步給序列排序。如果根據關鍵字選擇器來排序,兩項的順序相同,就可以使用ThenBy()和ThenByDescending()方法繼續排序。這兩個方法需要IOrderEnumerable<TSource>才能工作,但也返回這個接口。所以,可以添加任意多個ThenBy()和ThenByDescending()方法,對集合排序。
使用LINQ 查詢時,只需把所有用于排序的不同關鍵字(用逗號分隔開)添加到orderby 子句中例如
var racers = (from r in Formula1.GetChampions() orderby r.Country, r.LastName, r.FirstName select r).Take(10);
6。分組
var countries = from r in Formula1.GetChampions() group r by r.Country into g orderby g.Count() descending, g.Key where g.Count() >= 2 select new { Country = g.Key, Count = g.Count() };
foreach (var item in countries)
{
Response.Write("{0, -10} {1}",item.Country, item.Count);
}
擴展方法的分組表示:
var countries = Formula1.GetChampions().GroupBy(r => r.Country).OrderByDescending(g => g.Count()).
ThenBy(g => g.Key).Where(g => g.Count() >= 2).Select(g => new { Country = g.Key,Count = g.Count()});
7。對嵌套的對象分組
如果分組的對象應包含嵌套的對象,就可以改變select 子句創建的匿名類型。
var countries = from r in Formula1.GetChampions() group r by r.Country into g orderby g.Count() descending, g.Key where g.Count() >= 2 select new
{
Country = g.Key, Count = g.Count(),
Racers = from r1 in g orderby r1.LastName select r1.FirstName + " " + r1.LastName
};
foreach (var item in countries)
{
Response.Write("{0, -10} {1}", item.Country, item.Count);
foreach (var name in item.Racers)
{
Response.Write("{0}; ", name);
}
Response.Write("<br/>");
}
8。連接
使用join 子句可以根據特定的條件合并兩個數據源,但之前要獲得兩個要連接的列表。
var racers = from r in Formula1.GetChampions() from y in r.Years where y > 2003 select new
{
Year = y,
Name = r.FirstName + " " + r.LastName
};
var teams = from t in Formula1.GetContructorChampions() from y in t.Years where y > 2003
select new { Year = y, Name = t.Name };
有了這兩個查詢,再通過子句join t in teams on r.Year equals t.Year就可以得到結果集了。
var racersAndTeams = from r in racers join t in teams on r.Year equals t.Year select new
{
Year = r.Year,
Racer = r.Name,
Team = t.Name
};
Response.Write("Year Champion " + "Constructor Title");
foreach (var item in racersAndTeams)
{
Response.Write("{0}: {1,-20} {2}",item.Year, item.Racer, item.Team);
}
9。設置操作
擴展方法Distinct()、Union()、Intersect()和Except()都是設置操作。
var ferrariDrivers = from r in Formula1.GetChampions() from c in r.Cars where c == "Ferrari"
orderby r.LastName select r;
現在建立另一個相同的查詢,但where 子句的參數不同,以獲得所有駕駛McLaren 的冠軍。最好不要再次編寫相同的查詢。而可以創建一個方法,給它傳送參數car:
private static IEnumerable<Racer> GetRacersByCar(string car)
{
return from r in Formula1.GetChampions() from c in r.Cars where c == car orderby r.LastName select r;
}
但是,因為該方法不需要在其他地方使用,所以應定義一個委托類型的變量來保存LINQ 查詢。變量racerByCar 必須是一個委托類型,它需要一個字符串參數,返回IEnumerable <Racer>,類似于前面實現的方法。為此,定義了幾個泛型委托Func<>,所以不需要聲明自己的委托。把一個λ表達式賦予變量racerByCar。λ表達式的左邊定義了一個car 變量,其類型是Func 委托的第一個泛型參數(字符串)。右邊定義了LINQ 查詢,它使用該參數和where 子句:
Func<string, IEnumerable<Racer>> racersByCar = Car => from r in Formula1.GetChampions() from c in r.Cars where c == car orderby r.LastName select r;
現在可以使用Intersect()擴展方法,獲得駕駛Ferrari 和McLaren 的所有冠軍:
Response.Write("World champion with " + "Ferrari and McLaren");
foreach (var racer in racersByCar("Ferrari").
Intersect(racersByCar("McLaren")))
{
Response.Write(racer);
}
10。分區
擴展方法Take()和Skip()等的分區操作可用于分頁,例如顯示5×5 個賽手。在下面的LINQ 查詢中,擴展方法Take()和Skip()添加到查詢的最后。Skip()方法先忽略根據頁面的大小和實際的頁數計算出的項數,再使用方法Take()根據頁面的大小提取一定數量的項:
int pageSize = 5;
int numberPages = (int)Math.Ceiling(Formula1.GetChampions().Count()/(double)pageSize);
for (int page = 0; page < numberPages; page++)
{
Response.Write("Page {0}", page);
var racers = (from r in Formula1.GetChampions() orderby r.LastName select r.FirstName + " " + r.LastName).Skip(page * pageSize).Take(pageSize);
foreach (var name in racers)
{
Response.Write(name);
}
Response.Write();
}
11。合計操作符
合計操作符如Count()、Sum()、Min()、Max()、Average()和Aggregate(),不返回一個序列,而返
回一個值。
var query = from r in Formula1.GetChampions() where r.Years.Count() > 3 orderby r.Years.Count() descending select new
{
Name = r.FirstName + " " +r.LastName,
TimesChampion = r.Years.Count()
};
foreach (var r in query)
{
Response.Write("{0} {1}", r.Name, r.TimesChampion);
}
Sum()方法匯總序列中的所有數字,返回這些數字的和。下面的Sum()用于計算一個國家贏得比賽的
總次數。首先根據國家對賽手分組,再在新創建的匿名類型中,給Wins 屬性賦予某個國家贏得比賽的總
次數。
var countries = (from c in from r in Formula1.GetChampions() group r by r.Country into c
select new
{
Country = c.Key,
Wins = (from r1 in c select r1.Wins).Sum()
}
orderby c.Wins descending, c.Country select c).Take(5);
foreach (var country in countries)
{
Response.Write("{0} {1}",country.Country, country.Wins);
}
方法Min()、Max()、Average()和Aggregate()的使用方式與Count()和Sum()相同。Min()返回集合
中的最小值,Max()返回集合中的最大值,Average()計算集合中的平均值。對于Aggregate()方法,可
以傳送一個λ表達式,對所有的值進行匯總。
12。轉換
查詢可以推遲到訪問數據項時再執行。在迭代中使用查詢,查詢會執行。而使用轉換操作符會立即執行查詢,把結果放在數組、列表或字典中。在下面的例子中,調用ToList()擴展方法,立即執行查詢,把結果放在List<T>中:
List < Racer > racers = (from r in Formula1.GetChampions()
where r.Starts > 150 orderby r.Starts descending select r).ToList();
foreach (var racer in racers)
{
Response.Write("{0} {0:S}", racer);
}
把返回的對象放在列表中并沒有這么簡單。例如,對于集合中從賽車到賽手的快速訪問,可以使用新類Lookup<TKey, TElement>。
提示:
Dictionary<TKey, TValue>只支持一個鍵對應一個值。在System.Linq 命名空間的類Lookup<TKey,
TElement>中,一個鍵可以對應多個值。這些類詳見第10 章。
使用復合的from 查詢,可以攤平賽手和賽車序列,創建帶有Car 和Racer 屬性的匿名類型。在返回的Lookup 對象中,鍵的類型應是表示汽車的string,值的類型應是Racer。為了進行這個選擇,可以給ToLookup()方法的一個重載版本傳送一個鍵和一個元素選擇器。鍵選擇器表示Car 屬性,元素選擇器表示Racer 屬性。
ILookup<string, Racer> racers = (from r in Formula1.GetChampions() from c in r.Cars select new
{ Car = c, Racer = r }).ToLookup(cr = > cr.Car, cr = > cr.Racer);
if (racers.Contains("Williams"))
{
foreach (var williamsRacer in racers["Williams"])
{
Response.Write(williamsRacer);
}
}
如果需要在未類型化的集合上使用LINQ查詢,例如ArrayList,就可以使用Cast()方法。在下面的例子中,基于Object 類型的ArrayList 集合用Racer 對象填充。為了定義強類型化的查詢,可以使用Cast()方法。
System.Collections.ArrayList list = new System.Collections.ArrayList(Formula1.GetChampions() as
System.Collections.ICollection);
var query = from r in list.Cast <Racer>() where r.Country == "USA" orderby r.Wins descending select r;
foreach (var racer in query)
{
Response.Write("{0:A}", racer);
}
13。生成操作符
生成操作符Range()、Empty()和Repear()不是擴展方法,而是返回序列的正常靜態方法。在LINQto Objects 中,這些方法可用于Enumerable 類。有時需要填充一個范圍的數字,此時就應使用Range()方法。這個方法把第一個參數作為起始值,把第二個參數作為要填充的項數。
var values = Enumerable.Range(1, 20);
foreach (var item in values)
{
Response.Write("{0} ", item);
}
Range()方法不返回填充了所定義值的集合,這個方法與其他方法一樣,也推遲執行查詢,返回一個RangeEnumerator,其中只有一個yield return 語句,來遞增值。可以把該結果與其他擴展方法合并起來,獲得另一個結果,例如使用Select()擴展方法:
var values = Enumerable.Range(1, 20).Select(n = > n * 3);
Empty()方法返回一個不返回值的迭代器,它可以用于參數需要一個集合,且可以給參數傳送空集
合的情形。Repeat()方法返回一個迭代器,該迭代器把同一個值重復特定的次數。
?
?
ASP.NET開發技術交流群: 67511751(人員招募中...)
posted on 2011-08-31 16:03 Juvy 閱讀(...) 評論(...) 編輯 收藏轉載于:https://www.cnblogs.com/Juvy/archive/2011/08/31/2160847.html
總結
以上是生活随笔為你收集整理的Linq 学习笔记(二)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 信息安全官谁:逼近的挑战,你准备好了吗?
- 下一篇: BufferedInputStream学