C#中Linq的入門教程

一、LINQ的體系結構

語言集成查詢 (LINQ) (C#) | Microsoft 官方文檔

LINQ總共包括五個部分:

 

程序集

命名空間

描述

LINQ to Objects

System.Core.dll

System.Linq

提供對內存中集合操作的支持

LINQ to XML

System.Xml.Linq.dll

System.Xml.Linq

提供對XML數據源的操作的支持

LINQ to SQL

System.Data.Linq.dll

System.Data.Linq

提供對Sql Server數據源操作的支持。(微軟已宣佈不再更新,推薦使用LINQ to Entities)

LINQ to DataSet

System.Data.DataSetExtensions.dll

System.Data

提供對離線數據集DataTable操作的支持。

LINQ to Entities

System.Core.dll 和System.Data.Entity.dll

System.Linq和System.Data.Objects

LINQ to Entities 是 Entity Framework 的一部分並且取代LINQ to SQL 作為在數據庫上使用 LINQ 的標準機制。

目前,除瞭以下的,還可以下載其他第三方提供程序,例如LINQ to JSON、LINQ to MySQL、LINQ to Amazon、LINQ to Flickr和LINQ to SharePoint。無論使用什麼數據源,都可以通過LINQ使用相同的API進行操作。

二、 LINQ的語法

1、Query查詢表達式語法

LINQ查詢表達式以from子句開頭,以select子句或group子句結束。

在兩個子句之間,可以使用where、orderby、join、let等查詢操作符。

關鍵字有: from 、where 、select 、group 、into 、orderby、join、let、in、on、equals、by、ascending、descending等。

  • from…in…:指定要查找的數據源以及范圍變量,多個from子句則表示從多個數據源查找數據。註意:c#編譯器會把“復合from子句”的查詢表達式轉換為SelectMany()擴展方法。
  • join…in…on…equals…:指定多個數據源的關聯方式
  • let:引入用於存儲查詢表達式中子表達式結果的范圍變量。通常能達到層次感會更好,使代碼更易於閱讀。
  • orderby、descending:指定元素的排序字段和排序方式。當有多個排序字段時,由字段順序確定主次關系,可指定升序和降序兩種排序方式
  • where:指定元素的篩選條件。多個where子句則表示瞭並列條件,必須全部都滿足才能入選。每個where子句可以使用謂詞&&、||連接多個條件表達式。
  • group:指定元素的分組字段。
  • select:指定查詢要返回的目標數據,可以指定任何類型,甚至是匿名類型。(目前通常被指定為匿名類型)
  • into:提供一個臨時的標識符。該標識可以引用join、group和select子句的結果。 
    1) 直接出現在join子句之後的into關鍵字會被翻譯為GroupJoin。(into之前的查詢變量可以繼續使用) 
    2) select或group子句之後的into它會重新開始一個查詢,讓我們可以繼續引入where, orderby和select子句,它是對分步構建查詢表達式的一種簡寫方式。(into之前的查詢變量都不可再使用)

編譯器會在程序編譯時轉換LINQ查詢,以調用相應的擴展方法。

下面是一個簡單的示例,查詢一個int數組中小於5的元素,並按照從小到大的順序排列:

int[] arr = new int[] { 1, 4, 2, 6, 7, 9, 5, 1, 2, 4 };
var query = from r in arr
            where r < 5
            orderby r
            select r;
foreach (var item in query)
{
    Console.WriteLine(item);
}
Console.ReadLine();

Linq語句最終被轉換為調用IEnumerable<T>的擴展方法,在System.Linq.Enumerable靜態類中定義瞭N多擴展。所以隻要繼承與IEnumerable的類都支持Linq查詢 。

2、Lambda語法

標準查詢操作符

Enumberable 類定義的標準查詢操作符。

  • 篩選操作符:定義返回元素的條件。 
    Where:使用謂詞,返回符合條件的元素。 
    OfType<TResult>:返回符合類型的元素。 
  • 投射操作符:用於把對象轉換為另一個類型的新對象。 
    Select :定義根據選擇器函數選擇結果值的投射。 
    SelectMany:定義根據選擇器函數選擇結果值的投射。 
  • 排序操作符:改變返回的元素的順序。 
    Orderby: 升序排序。 
    OrderBydescending:  降序排序。 
    ThenBy 和 ThenByDescending: 二次排序。 
    Reverse: 反轉集合元素。 
  • 連接操作符:用於合並不直接相關的集合。 
    Join:   根據鍵選擇器函數連接兩個集合。 
    GroupJoin:   連接兩個集合,並分組。 
  • 組合操作符:把數據放在組中。 
    GroupBy  : 組合公共鍵的元素。 
    ToLookup:創建一個一對多字典,組合元素。 
  • 限定(量詞)操作符:元素滿足指定的條件。 
    Any :部分滿足謂詞函數的元素。 
    All : 所有元素是否都滿足謂詞函數。 
    Contains:   檢查某個元素是否在集合中。 
  • 分區操作符:返回集合的子集。 
    Take: 從集合提取元素個數。 
    Skip :跳過指定的元素個數,提取其他元素。 
    TakeWhile :提取條件為真的元素。 
    SkipWhile:提取條件為真的元素。 
  • Set操作符:返回一個集合。 
    Distinct :(去重)刪除重復的元素。 
    Union: (並集)返回集合中唯一元素。 
    Intersect:(交集)返回兩個集合都有的元素。 
    Except : (差集)隻出現在一個集合中的元素。 
    Zip: 兩個集合合並為一個元素。 
  • 元素操作符:返回一個元素。 
    First:返回第一個滿足條件的元素。 
    FirstOrDefault:類似First,如果未找到,返回類型的默認值。 
    Last:返回最後一個滿足條件的元素。 
    LastOrDefault:類似Last,如果未找到,返回類型的默認值。 
    ElementAt:返回元素的位置。 
    ElementAtOrDefault:指定索引(超出索引,取默認值) 
    Single:返回一個滿足條件的元素。如果有多個元素都滿足條件,就拋出一個異常。 
    SingleOrDefault:類似Single,如果非唯一或者找不到,返回類型的默認值。 
  • 聚合操作符:計算集合值。 
    Sum: 總和。 
    Count:  所有元素個數。 
    LongCount:計數(大型集合) 
    Min:  最小元素。 
    Max : 最大元素。 
    Average: 平均值。 
    Aggregate:  根據輸入的表達式獲取聚合值。 
  • 轉換操作符: 
    ToArray:變成數組 
    AsEnumerable:變成IEnumeralbe<T> 
    AsQueryable:變成IQueryable 
    ToList:變成List<T> 
    ToDictionary:變成字典 
    Cast<TResult> :轉換 
    ToLookup:變一對多字典Lookup<Tkey,TElement> 
  • 生成操作符: 
    Empty :空集合。 
    DefaultIfEmpty:默認值集合 
    Range:返回一系列數字。 
    Repeat: 返回始終重復一直的集合。 
  • 等值操作 
    SequenceEqual:成對比較 
  • 串聯操作 
    Concat:串聯

3、擴展方法存在對應的查詢表達式關鍵字:

  • Where:where
  • Select:select
  • SelectMany:使用多個 from 子句
  • OrderBy:orderby
  • ThenBy:orderby …, …
  • OrderByDescending:orderby … descending
  • ThenByDescending:orderby …, … descending
  • GroupBy:group … by 或 group … by … into …
  • Join:join … in … on … equals …
  • GroupJoin: join … in … on … equals … into … 

三、LINQ的特性

1、延遲執行查詢

LINQ具有“延遲計算”的特性。

Linq的執行不是在Linq的賦值語句執行,而是在通過foreach遍歷訪問結果時執行。

var names = new List<string> { "Nino", "Alberto", "Juan", "Mike", "Phil" };

var namesWithJ = (from n in names
                  where n.StartsWith("J")
                  orderby n
                  select n);

Console.WriteLine("First iteration");
foreach (string name in namesWithJ)
{
    Console.WriteLine(name);
}
Console.WriteLine();

names.Add("John");
names.Add("Jim");
names.Add("Jack");
names.Add("Denny");

Console.WriteLine("Second iteration");
foreach (string name in namesWithJ)
{
    Console.WriteLine(name);
}

返回的結果是:

兩次遍歷的結果不一樣,說明執行並不是在Linq的定義語句執行,而是在foreach執行。

換成如下,兩次執行結果就一樣瞭。

var namesWithJ = (from n in names
                  where n.StartsWith("J")
                  orderby n
                  select n
).ToList();

2、運算符延遲計算符號

按字母順序整理:

1、具有延遲計算的運算符

Cast,Concat,DefaultIfEmpty,Distinct,Except,GroupBy,GroupJoin,Intersect,Join,OfType,OrderBy,OrderByDescending,Repeat,Reverse,Select,SelectMany,Skip,SkipWhile,Take,TakeWhile,ThenBy,ThenByDescending,Union,Where,Zip

2、立即執行的運算符

對一系列源元素執行聚合函數的查詢必須首先循環訪問這些元素。CountMaxAverage 和 First 就屬於此類查詢。

由於查詢本身必須使用 foreach 以便返回結果,因此這些查詢在執行時不使用顯式 foreach 語句,直接立即執行。

Aggregate,All,Any,Average,Contains,Count,ElementAt,ElementAtOrDefault,Empty,First,FirstOrDefault,Last,LastOrDefault,LongCount,Max,Min,Range,SequenceEqual,Single,SingleOrDefault,Sum,ToArray,ToDictionary,ToList,ToLookup

註意:特殊的AsEnumerable運算符,用於處理LINQ to Entities操作遠程數據源,將IQueryable遠程數據立即轉化為本地的IEnumerable集合。若AsEnumerable接收參數是IEnumerable內存集合則什麼都不做。

3、強制立即執行

若要強制立即執行任意查詢並緩存其結果,可以調用 ToList<TSource> 或 ToArray<TSource> 方法。

通過調用 ToList 或 ToArray,可以將所有數據緩存在單個集合對象中。

var numQuery2 =
           (from num in numbers
            where (num % 2) == 0
            select num).ToList();
var numQuery3 =
          (from num in numbers
           where (num % 2) == 0
            select num).ToArray();

四、使用 LINQ 進行數據轉換

語言集成查詢 (LINQ) 不僅可用於檢索數據,而且還是一個功能強大的數據轉換工具。

通過使用 LINQ 查詢,您可以將源序列用作輸入,並采用多種方式修改它以創建新的輸出序列。您可以通過排序和分組來修改該序列,而不必修改元素本身。

但是,LINQ 查詢的最強大的功能是能夠創建新類型。這一功能在 select 子句中實現。

例如,可以執行下列任務:

1、將多個輸入聯接到一個輸出序列

class Student
{
    public string Name { get; set; }
    public int Age { get; set; }
    public string City { get; set; }
    public List<int> Scores { get; set; }
}

class Teacher
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
    public string City { get; set; }
}

private static void Main(string[] args)
{
    //創建第一個數據源
    var students = new List<Student>() {
            new Student () {
            Age = 23,
            City = "廣州",
            Name = "小C",
            Scores = new List<int> () { 85, 88, 83, 97 }
            },
            new Student () {
            Age = 18,
            City = "廣西",
            Name = "小明",
            Scores = new List<int> () { 86, 78, 85, 90 }
            },
            new Student () {
            Age = 33,
            City = "夢裡",
            Name = "小叁",
            Scores = new List<int> () { 86, 68, 73, 97 }
            }
        };

    //創建第二個數據源
    var teachers = new List<Teacher>() {
            new Teacher () {
            Age = 35,
            City = "夢裡",
            Name = "啵哆"
            },
            new Teacher () {
            Age = 28,
            City = "雲南",
            Name = "小紅"
            },
            new Teacher () {
            Age = 38,
            City = "河南",
            Name = "麗麗"
            }
        };

    //創建查詢
    var peopleInDreams = (from student in students
                          where student.City == "夢裡"
                          select student.Name)
                .Concat(from teacher in teachers
                        where teacher.City == "夢裡"
                        select teacher.Name);

    //執行查詢
    foreach (var person in peopleInDreams)
    {
        Console.WriteLine(person);
    }

    Console.Read();
}

結果

小叁 
啵哆

2、選擇各個源元素的子集

1. 若要隻選擇源元素的一個成員,請使用點運算。

var query = from cust in Customers
               select cust.City;

2. 若要創建包含源元素的多個屬性的元素,可以使用具有命名對象或匿名類型的對象初始值設定項。

var query = from cust in Customer
               select new {Name = cust.Name, City = cust.City};

3、將內存中的對象轉換為 XML

//創建數據源
var students = new List<Student>()
            {
                new Student()
                {
                    Age = 18,
                    Name = "小A",
                    Scores = new List<int>() {88,85,74,66 }
                },
                new Student()
                {
                    Age = 35,
                    Name = "小B",
                    Scores = new List<int>() {88,85,74,66 }
                },
                new Student()
                {
                    Age = 28,
                    Name = "小啥",
                    Scores = new List<int>() {88,85,74,66 }
                }
            };

//創建查詢
var studentsToXml = new XElement("Root",
    from student in students
    let x = $"{student.Scores[0]},{student.Scores[1]},{student.Scores[2]},{student.Scores[3]}"
    select new XElement("student",
           new XElement("Name", student.Name),
           new XElement("Age", student.Age),
           new XElement("Scores", x))
);

//執行查詢
Console.WriteLine(studentsToXml);

4、 對源元素執行操作

輸出序列可能不包含源序列的任何元素或元素屬性。

輸出可能是通過將源元素用作輸入參數計算出的值的序列。

//數據源
double[] radii = { 1, 2, 3 };

//創建查詢
var query = from radius in radii
            select $"{radius * radius * 3.14}";

//執行查詢
foreach (var i in query)
{
    Console.WriteLine(i);
}

到此這篇關於C#中Linq用法的文章就介紹到這瞭。希望對大傢的學習有所幫助,也希望大傢多多支持WalkonNet。

推薦閱讀: