1. 程式人生 > >C# LINQ查詢表示式用法對應Lambda表示式

C# LINQ查詢表示式用法對應Lambda表示式

C#程式語言非常優美,我個人還是非常贊同的。特別是在學習一段時間C#後發現確實在它的語法和美觀度來說確實要比其它程式語言強一些(也可能是由於VS編譯器的加持)用起來非常舒服,而且對於C#我覺得他最優美之處不僅僅是語法糖方面還有就是體現在LINQ和Lambda表示式。

本篇文簡單介紹一下關於C#當中LINQ表示式和其對應的Lambda表示式的用法,關於這兩部分內容的相關參考資料:

人民郵電出版社《C#程式設計及應用教程》(第3版)

部落格園部落格:《c# Linq查詢》

同時在介紹的時候我會盡量挑出重要部分同時加上我自己的理解和認識,如有偏差還請指教。

 

首先先讓我們看一下什麼是LINQ查詢表示式和Lambda表示式:

執行結果:

以上是對LINQ查詢表示式和Lambda表示式的一個簡單的應用,都是查詢stu列表中年齡大於等於20歲的結果並且把結果按照年齡降序排列。

由此可見Linq表示式和Lambda表示式並沒有什麼可比性,只是用Lambda可以實現LINQ的查詢語句。

那與Lambda表示式相關的整條語句稱作什麼呢?在微軟並沒有給出官方的命名,在《深入理解C#》中稱其為“點標記”。

例子中主要以LINQ表示式為主,LINQ表示式結構更為清晰易於理解,同時也會給出對應的點標記寫法(所有的LINQ查詢表示式都可以轉成對應的點標記。反之,不是所有的點標記都可以轉成查詢表示式。),所以如果想要了解這部分的讀者也請往下看。本文會介紹LINQ查詢表示式用法以及對應的Lambda表示式。

 

LINQ查詢表示式是一組查詢技術的統稱,其主要思想是將各種查詢功能直接整合到C#語言中,不論是物件、XML、還是資料庫都可以用LINQ編寫查詢語句。LINQ與傳統迭代技術實現的查詢優勢在於更簡明、更易讀這種優勢特別是在篩選多個條件時更為顯著;使用很少的程式程式碼便能實現複雜的查詢和篩選;無需修改或者是進行很少的修改就能移植到其它的資料來源。LINQ查詢語句並不複雜並且與SQL語句有些類似,學習起來難度並不大。

接下來直接看LINQ查詢例子:

 1 //以下為例子中涉及查詢的資料
 2 List<Student> stu = new List<Student>()
 3 {
 4   new Student(){Id = 1,Name = "王同學",Age = 24},
 5   new Student(){Id = 2,Name = "王同學",Age = 30},
 6   new Student(){Id = 3,Name = "王同學",Age = 21},
 7   new Student(){Id = 4,Name = "趙同學",Age = 20},
 8   new Student(){Id = 5,Name = "錢同學",Age = 22},
 9   new Student(){Id = 6,Name = "孫同學",Age = 23},
10   new Student(){Id = 7,Name = "周同學",Age = 23},
11   new Student(){Id = 8,Name = "吳同學",Age = 20},
12   new Student(){Id = 9,Name = "鄭同學",Age = 25},
13   new Student(){Id = 10,Name = "蔣同學",Age = 26}
14 };

 

查詢年齡是30歲的王同學的資訊:

 1 //查詢年齡是30歲的王同學的資訊
 2 var res = from t in stu
 3     where t.Name == "王同學" && t.Age == 30
 4     select t;
 5 
 6 //對應的Lambda表示式
 7 //var res1 = stu.Where(t => t.Age == 30 && t.Name == "王同學");
 8 
 9 foreach (var item in res)//顯示查詢結果
10 {
11     Console.Write("學號:"+item.Id+"\n姓名:"+item.Name+"\n年齡:"+item.Age);
12 }

執行結果:

 

LINQ查詢表示式必須以form子句開頭,並且必須以select或group子句結尾。在第一個from子句和最後一個select或group子句之間,查詢表示式可以包含一個或者多個where、orderby、join、let甚至附加from子句。LINQ表示式整體的用法和SQL語句很像,在上面的例子中就可以看出。

上面的例子的含義就是從資料來源stu中查詢一個數據“t”,“t”滿足的條件就是它的Name是王同學同時它的Age是30,然後查詢這個“t”。LINQ查詢語句的返回值型別為IEnumerable<T>,LINQ執行查詢時,一般利用foreach迴圈執行查詢得到一個序列,這這種方式稱為“延遲執行”。

什麼是“延遲執行”?還是上面例子中的查詢:

 1 //查詢年齡是30歲的王同學的資訊
 2 var res = from t in stu
 3     where t.Name == "王同學" && t.Age == 30
 4     select t;
 5 //var res1 = stu.Where(t => t.Age == 30 && t.Name == "王同學");Lambda表示式寫法
 6 
 7 foreach (var item in res)
 8 {
 9     Console.Write("學號:"+item.Id+"\n姓名:"+item.Name+"\n年齡:"+item.Age);
10 }
11 
12 Console.WriteLine("\n--------------------------------------------------------------------------");
13 
14 stu.Add(new Student(){Id = 11,Name = "王同學",Age = 30});
15 
16 foreach (var item in res)
17 {
18     Console.Write("學號:" + item.Id + "\n姓名:" + item.Name + "\n年齡:" + item.Age);
19     Console.WriteLine();
20 }

執行結果:

 

 延遲查詢就是隻需構造一次查詢語句,可以多次使用。在List中新增新元素之後並沒有重新執行查詢操作,然而res中的結果卻根據List中元素的改變相應發生了改變。

 

從學生中選出年齡小於25歲並且按照年齡降序排列

 1 //從學生中選出年齡小於25歲並且按照年齡降序排列
 2 var res = from t in stu
 3     where t.Age < 25
 4     orderby t.Age descending
 5     select t;
 6 //var res1 = stu.Where(t => t.Age < 25).OrderByDescending(t => t.Age).Select(t => t);Lambda寫法
 7 
 8 foreach (var item in res)
 9 {
10     Console.Write("學號:" + item.Id + "\n姓名:" + item.Name + "\n年齡:" + item.Age);
11     Console.WriteLine();
12 }

執行結果:

 

 

從學生中查詢姓王的同學的資訊並且按照年齡降序排列

 1 //從學生中查詢姓王的同學的資訊並且按照年齡降序排列
 2 var res = from t in stu
 3     from n in t.Name
 4     where n == '王'//名字中帶有王字
 5     orderby t.Age descending
 6     select t;
 7 //var res1 = stu.Where(t => t.Name.IndexOf("王") == 0).OrderByDescending(t => t.Age);lambda表示式
 8 
 9 foreach (var item in res)
10 {
11     Console.Write("學號:" + item.Id + "\n姓名:" + item.Name + "\n年齡:" + item.Age);
12     Console.WriteLine();
13 }

執行結果:

 

 

學生資訊按照年齡、Id進行排序

 1 //引數越靠前,優先順序越高
 2 //先按age排序,當分數相同時再按id排序...依次類推
 3 
 4  var res = from t in stu
 5     orderby t.Age, t.Id
 6     select t;
 7 //var res1 = stu.OrderBy(t => t.Age).ThenBy(t => t.Id);Lambda表示式
 8 
 9 foreach (var item in res)
10 {
11     Console.Write("學號:" + item.Id + "\n姓名:" + item.Name + "\n年齡:" + item.Age);
12     Console.WriteLine();
13 }

執行結果:

 

 

按照年齡進行分組,查詢相同年齡數量大於2的內容

 1 //按照年齡進行分組,查詢相同年齡數量大於2的內容
 2 var res = from t in stu
 3     group t by t.Age into s
 4     where s.Count()>=2
 5     select s;
 6 //var res1 = stu.GroupBy(t => t.Age).Where(s => s.Count() >= 2);lambda表示式
 7 
 8 foreach (var item in res)
 9 {
10     foreach (var items in item)
11     {
12         Console.Write("學號:" + items.Id + "\n姓名:" + items.Name + "\n年齡:" + items.Age);
13         Console.WriteLine();
14     }
15     Console.WriteLine();
16 }

執行結果:

 

 

查詢出集合qSt中year等於集合qSc中year的元素並形成新的集合

 1 List<s> pSt = new List<s>();
 2 pSt.Add(new s() { year = 1999, name = "小張" });
 3 pSt.Add(new s() { year = 2000, name = "小王" });
 4 pSt.Add(new s() { year = 2001, name = "小李" });
 5 pSt.Add(new s() { year = 2010, name = "小趙" });
 6 List<school> pSc = new List<school>();
 7 pSc.Add(new school() { year = 1999, name = "aaa" });
 8 pSc.Add(new school() { year = 2001, name = "bbb" });
 9 pSc.Add(new school() { year = 2002, name = "ccc" });
10 pSc.Add(new school() { year = 2010, name = "ddd" });
11 pSc.Add(new school() { year = 2012, name = "fff" });
12 
13 var res = from t1 in pSc
14      from t2 in pSt
15     where t1.year == t2.year
16     select new {year = t1.year, name = t1.name + t2.name};
17 
18 foreach (var item in res)
19 {
20     Console.Write("年:" + item.year + "姓名:" + item.name);
21     Console.WriteLine();
22 }

執行結果:

 

 

並行linq

並行查詢可以分解查詢的工作,使其分佈在多個執行緒上。當pc擁有多個cpu時,可以看到並行查詢帶來的改進效果。並行LINQ適用於大型的集合查詢,並擁有一定的優勢。使用System.Collections.Concurrent.Partitioner.Create可以手動建立分割槽器。可以粗魯的認為並行linq對於大集合的查詢是優勢比較明顯的。取消長時間執行的並行linq查詢可以設定利用System.Threading.CancellationTokenSource設定取消操作。

 1 Console.WriteLine("開始構造大陣列...");
 2 //構造大陣列
 3 const int count = 100000000;
 4 var data = new int[count];
 5 var r = new Random();
 6 for (int i = 0; i < count; i++)
 7 {
 8     data[i] = r.Next(40);
 9 }
10 Console.WriteLine("開始計算...");
11 var st = System.DateTime.Now;
12 var sum = (from x in data where x > 20 select x).Sum();//常規linq-耗時1.8641s
13 var st2 = System.DateTime.Now;
14 var sum2 = (from x2 in data.AsParallel() where x2 > 20 select x2).Sum();//並行查詢-耗時0.6620s
15 
16  //var sum3 = data.AsParallel().Where(x3 => x3 > 20).Sum();//或並行查詢----x3 => x3 > 20(Lambda表示式)
17 var st3 = System.DateTime.Now;
18 
19  /*Partitioner.Create
20 手動建立分割槽器以及終止LINQ查詢的方法可以詳見文初的部落格連結
21  Create具有多個過載,可依據需求進行分割槽*/
22 
23 var sum4 = (from c in System.Collections.Concurrent.Partitioner.Create(data, true).AsParallel() where c > 20 select c).Sum();
24 
25 var dt1 = st2 - st;
26 var dt2 = st3 - st2;
27 Console.WriteLine("常規linq耗時:{0}s", dt1.TotalSeconds.ToString());
28 Console.WriteLine("並行linq耗時:{0}s", dt2.TotalSeconds.ToString());
29 Console.ReadKey();

執行結果:

 

 

寫在最後,如果你對以上LINQ以及對應的Lambda的使用方法都已經瞭解那你已經初步瞭解了LINQ查詢表示式和Lambda表示式,這裡需要說明的一點是關於Lambda的使用方法並不僅僅只限於進行查詢,他是一個主要用於簡化委託的程式碼編寫形式,他用法遠比文中介紹的更加廣泛,本文是為了對比展現LINQ和Lambda。

以上內容歡迎指正交流。

&n