日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

[深入学习C#]LINQ查询表达式详解(1)——基本语法、使用扩展方法和Lambda表达式简化LINQ查询

發布時間:2025/3/11 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [深入学习C#]LINQ查询表达式详解(1)——基本语法、使用扩展方法和Lambda表达式简化LINQ查询 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

此文章非原創,轉載自詩人江湖老,原文地址

  在Git上下載源碼
  在工程中我們少不了要定義類或者結構去儲存數據,這些數據將被臨時地儲存在內存中,現在我們想要對其完成一些類似于查找、過濾等等常見的任務的時候,我們該如何去做呢?
  我們可以自己寫代碼去對集合中的每個對象進行遍歷,檢查變量的每個字段看其是否滿足條件。這樣的故事已經發生太多次了,微軟怎么可能容忍在C#里發生如此弱智的事情呢?于是,C#的設計者決定在C#中集成查詢的語法,以最大限度地減少程序員書寫類似代碼的情況。
  這也就是我們說的LINQ(Language Intergated Query)也就是語言集成查詢,我們可以使用同樣的語法訪問不同的數據源。

為什么要使用LINQ?

  幾乎所有的應用程序都需要對數據進行處理,大部分的程序都通過自定義的邏輯來完成這些操作。這樣做的弊端之意就是,代碼邏輯將會跟它處理的數據的結構緊密耦合在一起,如果數據結構發生了改變,也許將會帶來海量的代碼改動。
  為了解決這個問題,C#將這些處理數據的代碼都抽象出來,提供給了廣大開發者。這就是LINQ。
  LINQ的語法類似于關系和分層查詢語言(SQL和XQuery),我們可以在不更改查詢代碼的情況下對數據結構進行更改。LINQ比之SQL要更加靈活,可以處理更廣泛的邏輯數據結構。當然,這些數據結構都需要實現了IEnumerable或者IEnumerable< T >接口,才可以進行LINQ查詢。

LINQ查詢表達式語法詳解

表達式基礎語法

  LINQ查詢表達式以from子句開始,以select或者group子句結束。在這兩個子句之間可以跟零個或者多個from、let、where、join或者orderby子句。
  每個from子句都是一個生成器,該生成器將引入一個包括序列(Sequence)的元素的范圍變量(range variable)。每個let子句都會引入一個范圍變量,以表示通過前一個范圍變量計算的值。每個where子句都是一個篩選器,用于從結果中排除項。每個join子句都將指定的源序列鍵與其他序列的鍵進行比較,以產生匹配對。每個orderby子句都會根據指定的條件對各項進行重新排序。而最后的select或者group子句根據范圍變量來指定結果的表現形式。最后可以使用into子句來連接查詢,將某一查詢結果的視為后續查詢的生成器。

標準查詢操作符

  在了解LINQ查詢表達式之前,怎么能不了解下它的查詢操作符呢?下面列表列出了LINQ定義的標準查詢操作符。

表1:LINQ標準查詢操作符

標準查詢操作符說明
where OfType<TResult>篩選操作符定義了返回元素的條件。在Where查詢操作符中,可以使用謂詞,例如Lambda表達式定義的謂詞,來返回布爾值。OfType<TResult>根據類型篩選元素,只返回TResult的類型元素
Select 和SelectMany投射操作符用于把對象轉換為另一個類型的新對象。Select和SelectMany定義了根據選擇器函數選擇結果值的投射。
OrderBy、ThenBy 、OrderByDescending 、ThenByDescending 、Reverse排序操作符改變所返回的元素的順序。OrderBy按升序排列,OrderByDescending按降序排列。如果第一次排序結果很類似,就可以使用ThenBy和ThenByDescending操作符進行第二次排序。Reverse反轉集合中的元素順序。
GroupBy、ToLookUp組合運算符把數據放在組里面。GroupBy操作符組合有公共鍵的元素。ToLookUp通過創建一個一對多的字典,來組合元素。
Join、GroupJoin鏈接運算符用于合并不直接相關的集合。使用Join操作符,可以根絕鍵選擇器函數連接兩個集合,這類似于SQL中的Join。GroupJoin操作符連接兩個集合,組合其結果。
Any、All、Contains如果元素序列滿足指定的條件,兩次操作符就返回布爾值。Any、ALll和Contains都是限定符操作符。Any確定集合中是否有確定滿足謂詞函數的元素。ALll確定集合中的所有元素是否都滿足謂詞函數。Contains檢查某個元素是否在集合中。這些操作符都返回一個布爾值。
Take、Skip、TakeWhile、SkipWhile分區操作符返回集合的一個子集,Take、Skip、TakeWhile、SkipWhile都是分區操作符。使用它們可以得到部分結果,使用Take必須指定要從集合中提取的元素個數;Skip跳過指定個數的元素,提取其它元素;TakeWhile提取條件為真的元素。
Distinct、Union、Intersect、Except、ZipSet操作符返回一個集合。Distinct從集合中刪除重復的元素,除了Distinct之外,其它的Set操作符都需要兩個集合。Union返回出現在其中一個集合中的唯一元素。Intersect返回兩個集合中都有的元素。Except返回值出現在一個集合中的元素。Zip是.NET 4新增的,它把兩個集合合并為一個。
First、FirstOrDefault、Last、LastOrDefault、ElementAt、ElementAtOrDefault、Single、SingleOrDefault這些元素操作符僅返回一個元素。First返回第一個滿足條件的元素。FirstOrDefault類似于First,單如果沒有找到滿足條件的元素,就返回類型的默認值。Last返回最后一個滿足條件的元素。ElementAt指定了要返回的元素的位置。Single只返回一個滿足條件的元素。如果有多個元素都滿足條件,就拋出一個異常。
Count、Sum、Min、Max、Average、Aggregate聚合操作符計算集合的一個值。利用這些聚合操作符,可以計算所有值的總和、所有元素的個數、值最大和最小的元素,以及平均值等等。
ToArray、ToEnumerable、ToList、ToDictionary、Cast<TRsult>這些轉換操作符將集合轉換為數組:IEnumerable、IList、IDictionary等。
Empty、Range、Repeat這些生成操作符返回一個心機和。使用Empty時集合是空的;Range返回一系列數字;Repeat返回一個始終重復一個值的集合。

設置案例背景

  假設我們有4個類,Customer,Order,Detail,Product,它們的定義如下:

Customer類字段字段類型
CustomerIDint
Countrystring
Namestring
Citystring
OrdersList<Order>
Order類字段字段類型
OrderIDint
CustomerIDint
Totalint
OrderDateDateTime
DetailsList<Detail>
Detail類字段字段類型
DetailIDint
OrderIDint
UnitPricedouble
Quantitydouble
ProductIDint
Product類字段字段類型
ProductIDint
ProductNamestring

  假設現在它們各自有一個List集合,分別為Customers,Orders,Details,Products。我們在這基礎之上來一步步闡述LINQ查詢表達式。
  customers數據:

CustomerIDCityCountryNameOrders
0北京中國小米orders.FindAll(c => c.CustomerID == 0)
1首爾韓國三星orders.FindAll(c => c.CustomerID == 1)
2加州美國蘋果orders.FindAll(c => c.CustomerID == 2)
3臺北中國HTCorders.FindAll(c => c.CustomerID == 3)
4珠海中國魅族orders.FindAll(c => c.CustomerID == 4)
5北京中國華為orders.FindAll(c => c.CustomerID == 5)
6上海中國索尼orders.FindAll(c => c.CustomerID == 6)
7北京中國聯想orders.FindAll(c => c.CustomerID == 7)
8上海中國諾基亞orders.FindAll(c => c.CustomerID == 8)

  
  orders數據:

OrderIDCustomerIDOrderDateDetails
00DateTime.Nowdetails.FindAll(d => d.OrderID == 0)
10DateTime.Nowdetails.FindAll(d => d.OrderID == 1)
21DateTime.Nowdetails.FindAll(d => d.OrderID == 2)
31DateTime.Nowdetails.FindAll(d => d.OrderID == 3)
42DateTime.Nowdetails.FindAll(d => d.OrderID == 4)
52DateTime.Nowdetails.FindAll(d => d.OrderID == 5)
63DateTime.Nowdetails.FindAll(d => d.OrderID == 6)
73DateTime.Nowdetails.FindAll(d => d.OrderID == 7)
84DateTime.Nowdetails.FindAll(d => d.OrderID == 8)
95DateTime.Nowdetails.FindAll(d => d.OrderID == 9)
106DateTime.Nowdetails.FindAll(d => d.OrderID == 10)
116DateTime.Nowdetails.FindAll(d => d.OrderID == 11)
127DateTime.Nowdetails.FindAll(d => d.OrderID == 12)
137DateTime.Nowdetails.FindAll(d => d.OrderID == 13)
148DateTime.Nowdetails.FindAll(d => d.OrderID == 14)
158DateTime.Nowdetails.FindAll(d => d.OrderID == 15)
168DateTime.Nowdetails.FindAll(d => d.OrderID == 16)

  
  details數據:

DetailIDOrderIDProductIDQuantityUnitPrice
000100010
11121348
22112369
3307547
442235412
550698513
662421310
773197710
8822876
995977812
1010485411
1111275610
1212310009
131317868
141433467
151525766
1616078210

  
  products數據:

ProductIDProductName
0samsung
1nokia
2apple
3xiaomi
4huawei
5lenovo

Demo查詢表達式

條件篩選

  使用where子句,可以按照一個或者多個條件篩選集合,where子句的表達式的結果類型應該是布爾類型。
  篩選在北京且名稱以‘小’開頭的顧客。

var query = from c in customerswhere c.City == "北京" && c.Name.StartsWith("小")select c; foreach (Customer item in query) {Console.WriteLine(item.Name + "\t\t" + item.City); } Console.ReadKey();

  該LINQ查詢會返回在北京而且名字以“小”開頭的Customer集合。
  輸出結果:
  
  

復合from子句篩選

  當需要根絕對象的一個成員進行篩選,而該成員本身是一個集合或者列表,就可以使用復合的from子句。
  篩選訂單數量大于800的信息。

var query = from c in customersfrom o in c.Ordersfrom d in o.Detailswhere d.Quantity > 800select new { Name = c.Name, Total = d.UnitPrice * d.Quantity };foreach (var item in query) {Console.WriteLine(item.Name + "\t\t" + item.Total); } Console.ReadKey();

  輸出結果:
  
  

排序

  要對序列排序,需要使用orderby子句。
  按照城市、顧客ID進行排序。 

var query = from c in customersfrom o in c.Ordersorderby c.City, o.CustomerIDselect new { Name = c.Name, City = c.City, OrderID = o.OrderID };foreach (var item in query) {Console.WriteLine(item.Name + "\t\t" + item.City + "\t\t" + item.OrderID); } Console.ReadKey();

  輸出結果:
  
    

分組

  要根木一個關鍵值對查詢結果分組,可以使用group子句。
  統計各個產品的訂單數量。  

var query = from d in detailsgroup d by d.ProductID into gorderby g.Count(), g.Keyselect new { Name = g.Key, Count = g.Count() }; Console.WriteLine("ProductID"+ "\t" + "Count"); foreach (var item in query) {Console.WriteLine(item.Name + "\t\t" + item.Count); } Console.ReadKey();

  輸出結果:
  
  

對嵌套的對象分組

  如果分組的對象包含嵌套的序列,則各個改變select子句創建的匿名類型。
  統計各個產品的訂單數量,并輸出各個訂單的訂貨數量。

var query = from d in detailsgroup d by d.ProductID into gorderby g.Count(), g.Keyselect new{Name = g.Key,Count = g.Count(),Quantity = from d in gorderby d.Quantityselect d.Quantity};Console.WriteLine("ProductID"+"\t"+"Count"+"\t"+"Quantity"); foreach (var item in query) {Console.Write(item.Name + "\t\t" + item.Count+"\t");foreach (var quantity in item.Quantity){Console.Write("{0};", quantity);}Console.WriteLine(); } Console.ReadKey();

  輸出結果:
  
  

連接

  使用join子句可以根據特定的條件合并兩個數據源,但之前要獲得兩個要連接的列表。
  統計各個顧客和其訂單的信息。

var query = from r infrom c in customersfrom o in c.Ordersfrom d in o.Detailsselect new { Name = c.Name, City = c.City, Money = d.Quantity * d.UnitPrice, ProductID = d.ProductID }join t infrom p in productsselect pon r.ProductID equals t.ProductIDselect new { Name = r.Name, City = r.City, Money = r.Money, ProductName = t.ProductName }; Console.WriteLine("Name" + "\t" + "City" + "\t" + "Money" + "\t" + "ProductName"); foreach (var item in query) {Console.WriteLine(item.Name + "\t" + item.City + "\t" + item.Money + "\t" + item.ProductName); } Console.ReadKey();

  輸出結果:
  
    

聚合操作符

  聚合操作符(包括Count()、Sum()、Min()、Max()、Average()和Aggregate())它們不返回一個序列,而是返回一個值。
  Count()方法返回集合中的項數;Sum()方法匯總序列中所有數字,返回這些數字的和;Min()方法返回集合中的最小值;Max()方法返回集合中的最大值;Average()方法計算集合中的平均值;Aggregate()方法,可以傳遞一個Lambda表達式,該表達式對所有的值進行聚合。
  這些方法的使用方式類似,都是直接對序列或者集合進行操作。下面用Sum()做一個示例:
  統計各個顧客總共訂單的訂貨數量。

var query = from r infrom c in customersselect new{Name = c.Name,OrderCount = ( from o in c.Ordersfrom d in o.Detailsselect d.Quantity).Sum()}orderby r.OrderCountselect r;

  輸出結果:
  

使用擴展方法和Lambda表達式簡化LINQ查詢

什么是擴展方法

  當方法的第一個形參包含this修飾符的時候,該方法稱為擴展方法。擴展方法只能在非泛型、非嵌套的靜態類中聲明,擴展方法的第一個形參不能帶有除this之外的其他任何修飾符,而且形參類型不能是指針類型。
  下面的程序是一個擴展方法的示例:

public static class Extensions {public static void HelloWorld(this string str){Console.WriteLine("{0} 調用了:HellWorld",str);} }

  現在就可以調用該方法了:

string str="Jay"; str.HelloWorld();

  我們可以看到,控制臺中輸出了“Jay 調用了:HelloWorld”。
  之所以這樣,是因為HelloWorld第一個參數類型為string類型,因此該方法就是string類型的擴展方法,所有的string類型變量都可以調用,而變量的內容就是傳遞給HelloWorld的參數。
  上面的程序和下面的代碼結果一樣:

string str = "Jay"; Extensions.HelloWorld(str);

    LINQ擴展方法

      LINQ為IEnumerable<T>接口提供了各種擴展方法,以便用戶在實現了該接口的任意集合上使用LINQ查詢。表1中列出的LINQ查詢操作符,都有相應的擴展方法實現。
      使用擴展方法可以和使用LINQ查詢表達式獲得十分類似甚至是相同的結果,當擴展方法和Lambda表達式結合的時候,會大大簡化LINQ查詢。
      

    簡化LINQ查詢

      前面一節的條件篩選LINQ表達式可以簡化為:

    var query = customers.Where(c => c.City == "北京" && c.Name.StartsWith("小")).Select(c => c);
    • 1

      
      前面一節的條件復合from子句篩選LINQ表達式可以簡化為:

    var query = customers.SelectMany(c => c.Orders, (c, o) => new { _customer = c, _order = o, _detail = o.Details }).SelectMany(a => a._detail, (a, b) => new { _var = a, Total = b.Quantity * b.UnitPrice, Quantity = b.Quantity }).Where(_nameless => _nameless.Quantity > 800).Select(_annonymous => new { Name = _annonymous._var._customer.Name, Total = _annonymous.Total });
    • 1

      
      前面一節的排序LINQ表達式可以簡化為:

    var query = customers.SelectMany(c => c.Orders, (c, o) => new { _customer = c, _order = o }).OrderBy(_var => _var._customer.City).ThenBy(_var => _var._order.CustomerID).Select(_annonymous => new { Name = _annonymous._customer.Name, City = _annonymous._customer.City, OrderID = _annonymous._order.OrderID });
    • 1

      
      前面一節的分組LINQ表達式可以簡化為:

    var query = details.GroupBy(d => d.ProductID).OrderBy(g => g.Count()).ThenBy(g => g.Key).Select(g => new { Name = g.Key, Count = g.Count() });
    • 1

      
      前面一節的對嵌套的對象分組LINQ表達式可以簡化為:

    var query = details.GroupBy(d => d.ProductID).OrderBy(g => g.Count()).ThenBy(g => g.Key).Select(_var => new { Name = _var.Key, Count = _var.Count(), Quantity = _var.OrderBy(d => d.Quantity).Select(d => d.Quantity) });
    • 1

      前面一節的連接LINQ表達式可以簡化為:

    var query = customers.SelectMany(c => c.Orders, (c, o) => new { Name = c.Name, City = c.City, Details = o.Details }).SelectMany(_var => _var.Details, (_var, _detail) => new { Name = _var.Name, City = _var.City, Money = _detail.Quantity * _detail.UnitPrice, ProductID = _detail.ProductID }).Join(products, a => a.ProductID, b => b.ProductID, (a, b) => new { Name = a.Name, City = a.City, Money = a.Money, ProductName = b.ProductName });
    • 1

      
      前面一節的聚合操作LINQ表達式可以簡化為:

    var query = customers.Select(c => new { Name = c.Name, OrderCount = c.Orders.SelectMany(o => o.Details, (o, d) => new { _Quantity = d.Quantity }).Select(_var => _var._Quantity).Sum() }).OrderBy(_var => _var.OrderCount);
    • 1

      以上利用擴展方法和Lambda表達式的簡化后的LINQ查詢代碼的查詢結果,與LINQ查詢表達式結果完全一樣,證明這樣是完全可行的。
      唯一的問題就是代碼看起來比較費解了。

    LINQ查詢表達式簡化轉換原則

      看到這里我們可能要奇怪,為什么我們能用這樣的方式來簡化LINQ查詢表達式呢?
      關于這個問題,我們將在下一篇文章進行詳細講解。
      歡迎大家點擊閱讀。
      本人還是菜鳥,寫的不對的地方還請各位不吝賜教!

    總結

    以上是生活随笔為你收集整理的[深入学习C#]LINQ查询表达式详解(1)——基本语法、使用扩展方法和Lambda表达式简化LINQ查询的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。