C#3.0新增功能07 查詢表達式

查詢是什麼及其做用是什麼

查詢是一組指令,描述要從給定數據源(或源)檢索的數據以及返回的數據應具備的形狀和組織。 查詢與它生成的結果不一樣。html

一般狀況下,源數據按邏輯方式組織爲相同類型的元素的序列。 例如,SQL 數據庫表包含行的序列。 在 XML 文件中,存在 XML 元素的「序列」(儘管這些元素在樹結構按層次結構進行組織)。 內存中集合包含對象的序列。數據庫

從應用程序的角度來看,原始源數據的特定類型和結構並不重要。 應用程序始終將源數據視爲 IEnumerable<T> 或 IQueryable<T> 集合。 例如在 LINQ to XML 中,源數據顯示爲 IEnumerable<XElement>。express

對於此源序列,查詢可能會執行三種操做之一:編程

  • 檢索元素的子集以生成新序列,而不修改各個元素。 查詢而後可能以各類方式對返回的序列進行排序或分組,以下面的示例所示(假定 scores 是 int[]):api

IEnumerable<int> highScoresQuery =
    from score in scores
    where score > 80
    orderby score descending
    select score;
  • 如前面的示例所示檢索元素的序列,可是將它們轉換爲新類型的對象。 例如,查詢能夠只從數據源中的某些客戶記錄檢索姓氏。 或者能夠檢索完整記錄,而後用於構造其餘內存中對象類型甚至是 XML 數據,再生成最終的結果序列。 下面的示例演示從 int 到 string 的投影。 請注意 highScoresQuery 的新類型。
IEnumerable<string> highScoresQuery2 =
    from score in scores
    where score > 80
    orderby score descending
    select $"The score is {score}";
  • 檢索有關源數據的單獨值,如:數組

    • 與特定條件匹配的元素數。ide

    • 具備最大或最小值的元素。ui

    • 與某個條件匹配的第一個元素,或指定元素集中特定值的總和。 例如,下面的查詢從 scores 整數數組返回大於 80 的分數的數量:spa

int highScoreCount =
    (from score in scores
     where score > 80
     select score)
     .Count();

在前面的示例中,請注意在調用 Count 方法以前,在查詢表達式兩邊使用了括號。也能夠經過使用新變量存儲具體結果,來表示此行爲。 這種方法更具可讀性,由於它使存儲查詢的變量與存儲結果的查詢分開。code

IEnumerable<int> highScoresQuery3 =
    from score in scores
    where score > 80
    select score;

int scoreCount = highScoresQuery3.Count();

在上面的示例中,查詢在 Count 調用中執行,由於 Count 必須循環訪問結果才能肯定 highScoresQuery 返回的元素數。 

查詢表達式是什麼
  查詢表達式是以查詢語法表示的查詢。 查詢表達式是一流的語言構造。 它如同任何其餘表達式同樣,能夠在 C# 表達式有效的任何上下文中使用。 查詢表達式由一組用相似於 SQL 或 XQuery 的聲明性語法所編寫的子句組成。 每一個子句進而包含一個或多個 C# 表達式,而這些表達式可能自己是查詢表達式或包含查詢表達式。
(1)查詢表達式必須以 from 子句開頭,且必須以 select 或 group 子句結尾。 
(2)在第一個 from 子句與最後一個 select 或 group 子句之間,能夠包含如下這些可選子句中的一個或多個:whereorderbyjoinlet,甚至是其餘 from 子句。 還可使用 into 關鍵字,使 join 或 group 子句的結果能夠充當相同查詢表達式中的其餘查詢子句的源。

查詢變量

在 LINQ 中,查詢變量是存儲查詢而不是查詢結果的任何變量。 更具體地說,查詢變量始終是可枚舉類型,在 foreach 語句或對其 IEnumerator.MoveNext 方法的直接調用中循環訪問時會生成元素序列。

下面的代碼示例演示一個簡單查詢表達式,它具備一個數據源、一個篩選子句、一個排序子句而且不轉換源元素。 該查詢以 select 子句結尾。

static void Main()
{
    // 數據源
    int[] scores = { 90, 71, 82, 93, 75, 82 };

    // 查詢表達式
    IEnumerable<int> scoreQuery = // 查詢變量
        from score in scores      // 必須
        where score > 80          // 可選
        orderby score descending  // 可選
        select score;             // 必須以 select 或者 group 結尾

    // 執行查詢併產生結果
    foreach (int testScore in scoreQuery)
    {
        Console.WriteLine(testScore);
    }                  
}
// 輸出: 93 90 82 82

在上面的示例中,scoreQuery 是查詢變量,它有時僅僅稱爲查詢。 查詢變量不存儲在 foreach 循環生成中的任何實際結果數據。 而且當 foreach 語句執行時,查詢結果不會經過查詢變量 scoreQuery 返回。 而是經過迭代變量 testScore 返回。 scoreQuery 變量能夠在另外一個 foreach 循環中進行循環訪問。 只要既沒有修改它,也沒有修改數據源,便會生成相同結果。

查詢變量能夠存儲採用查詢語法、方法語法或是二者的組合進行表示的查詢。 在如下示例中,queryMajorCities 和 queryMajorCities2 都是查詢變量:

var cities = new City
{
  new city(){Name = "上海",Population = 24180000},
  new city(){Name = "南京",Population = 8436200},
  new city(){Name = "北京",Population = 21710000},
  new city(){Name = "廣州",Population = 14900000}
};

// 查詢語法
IEnumerable<City> queryMajorCities =
    from city in cities
    where city.Population > 100000
    select city;

// 基於方法的語法
IEnumerable<City> queryMajorCities2 = cities.Where(c => c.Population > 100000);

另外一方面,如下兩個示例演示不是查詢變量的變量(即便各自使用查詢進行初始化)。 它們不是查詢變量,由於它們存儲結果:

int highestScore =
    (from score in scores
     select score)
    .Max();

// 或者拆分表達式
IEnumerable<int> scoreQuery =
    from score in scores
    select score;

int highScore = scoreQuery.Max();
// 下面的表達式返回相同的結果
int highScore = scores.Max();

List<City> largeCitiesList =
    (from country in countries
     from city in country.Cities
     where city.Population > 10000
     select city)
       .ToList();

// 或者拆分表達式
IEnumerable<City> largeCitiesQuery =
    from country in countries
    from city in country.Cities
    where city.Population > 10000
    select city;

List<City> largeCitiesList2 = largeCitiesQuery.ToList();

有關表示查詢的不一樣方式的詳細信息,請參閱 LINQ 中的查詢語法和方法語法

查詢變量的顯式和隱式類型化

本文檔一般提供查詢變量的顯式類型以便顯示查詢變量與 select 子句之間的類型關係。 可是,還可使用 var 關鍵字指示編譯器在編譯時推斷查詢變量(或任何其餘局部變量)的類型。 例如,本主題中前面演示的查詢示例也可使用隱式類型化進行表示:

// 在這裏和全部查詢中使用var都是可選的。querycities是一個IEnumerable<city>就像它是顯式類型同樣
var queryCities =
    from city in cities
    where city.Population > 100000
    select city;

有關詳細信息,請參閱隱式類型化局部變量和 LINQ 查詢操做中的類型關係

開始查詢表達式

查詢表達式必須以 from 子句開頭。 它指定數據源以及範圍變量。 範圍變量表示遍歷源序列時,源序列中的每一個連續元素。 範圍變量基於數據源中元素的類型進行強類型化。 在下面的示例中,由於 countries 是 Country 對象的數組,因此範圍變量也類型化爲 Country。 由於範圍變量是強類型,因此可使用點運算符訪問該類型的任何可用成員。

IEnumerable<Country> countryAreaQuery =
    from country in countries
    where country.Area > 500000 //面積大於500000
    select country;

範圍變量一直處於範圍中,直到查詢使用分號或 continuation 子句退出。

查詢表達式可能會包含多個 from 子句。 在源序列中的每一個元素自己是集合或包含集合時,可以使用其餘 from 子句。 例如,假設具備 Country 對象的集合,其中每一個對象都包含名爲 Cities 的 City 對象集合。 若要查詢每一個 Country 中的 City 對象,請使用兩個 from 子句,以下所示:

IEnumerable<City> cityQuery =
    from country in countries
    from city in country.Cities
    where city.Population > 10000
    select city;

有關詳細信息,請參閱 from 子句

結束查詢表達式

查詢表達式必須以 group 子句或 select 子句結尾。

group 子句

使用 group 子句可生成按指定鍵組織的組的序列。 鍵能夠是任何數據類型。 例如,下面的查詢會建立包含一個或多個 Country 對象而且其鍵是 char 值的組的序列。

var queryCountryGroups =
    from country in countries
    group country by country.Name[0];

有關分組的詳細信息,請參閱 group 子句

select 子句

使用 select 子句可生成全部其餘類型的序列。 簡單 select 子句只生成類型與數據源中包含的對象相同的對象的序列。 在此示例中,數據源包含 Country 對象。 orderby 子句只按新順序對元素進行排序,而 select 子句生成從新排序的 Country 對象的序列。

IEnumerable<Country> sortedQuery =
    from country in countries
    orderby country.Area
    select country;

select 子句能夠用於將源數據轉換爲新類型的序列。 此轉換也稱爲投影。 在下面的示例中,select 子句對只包含原始元素中的字段子集的匿名類型序列進行投影。 請注意,新對象使用對象初始值設定項進行初始化。

// 此處 var 是必須的,由於查詢返回了匿名類型
var queryNameAndPop =
    from country in countries
    select new { Name = country.Name, Pop = country.Population };

有關可使用 select 子句轉換源數據的全部方法的詳細信息,請參閱 select 子句

使用「into」進行延續

能夠在 select 或 group 子句中使用 into 關鍵字建立存儲查詢的臨時標識符。 若是在分組或選擇操做以後必須對查詢執行其餘查詢操做,則能夠這樣作。 在下面的示例中,countries 按 1000 萬範圍,根據人口進行分組。 建立這些組以後,附加子句會篩選出一些組,而後按升序對組進行排序。 若要執行這些附加操做,須要由 countryGroup 表示的延續。

// 該查詢返回的類型是 IEnumerable<IGrouping<int, Country>>
var percentileQuery =
    from country in countries
    let percentile = (int) country.Population / 10_000_000
    group country by percentile into countryGroup
    where countryGroup.Key >= 20
    orderby countryGroup.Key
    select countryGroup;

// 分組是 IGrouping<int, Country>
foreach (var grouping in percentileQuery)
{
    Console.WriteLine(grouping.Key);
    foreach (var country in grouping)
        Console.WriteLine(country.Name + ":" + country.Population);
}

有關詳細信息,請參閱 into

篩選、排序和聯接

在開頭 from 子句與結尾 select 或 group 子句之間,全部其餘子句(wherejoinorderbyfromlet)都是可選的。 任何可選子句均可以在查詢正文中使用零次或屢次。

IEnumerable<City> queryCityPop =
    from city in cities
    where city.Population < 200000 && city.Population > 100000
    select city;

有關詳細信息,請參閱 where 子句

orderby 子句

使用 orderby 子句可按升序或降序對結果進行排序。 還能夠指定次要排序順序。 下面的示例使用 Area 屬性對 country 對象執行主要排序。 而後使用 Population 屬性執行次要排序。

IEnumerable<Country> querySortedCountries =
    from country in countries
    orderby country.Area, country.Population descending
    select country;

ascending 關鍵字是可選的;若是未指定任何順序,則它是默認排序順序。 有關詳細信息,請參閱 orderby 子句

join 子句

使用 join 子句可基於每一個元素中指定的鍵之間的相等比較,將一個數據源中的元素與另外一個數據源中的元素進行關聯和/或合併。 在 LINQ 中,聯接操做是對元素屬於不一樣類型的對象序列執行。 聯接了兩個序列以後,必須使用 select 或 group 語句指定要存儲在輸出序列中的元素。 還可使用匿名類型將每組關聯元素中的屬性合併到輸出序列的新類型中。下面的示例關聯其 Category 屬性與 categories 字符串數組中一個類別匹配的 prod 對象。篩選出其 Category 不與 categories 中的任何字符串匹配的產品。select 語句會投影其屬性取自 cat 和 prod 的新類型。

var categoryQuery =
    from cat in categories
    join prod in products on cat equals prod.Category
    select new { Category = cat, Name = prod.Name };

還能夠經過使用 into 關鍵字將 join 操做的結果存儲到臨時變量中來執行分組聯接。 有關詳細信息,請參閱 join 子句

let 子句

使用 let 子句可將表達式(如方法調用)的結果存儲在新範圍變量中。 在下面的示例中,範圍變量 firstName 存儲 Split 返回的字符串數組的第一個元素。

string[] names = { "Svetlana Omelchenko", "Claire O'Donnell", "Sven Mortensen", "Cesar Garcia" };
IEnumerable<string> queryFirstNames =
    from name in names
    let firstName = name.Split(' ')[0]
    select firstName;

foreach (string s in queryFirstNames)
Console.Write(s
+ " ");
//輸出: Svetlana Claire Sven Cesar

有關詳細信息,請參閱 let 子句

查詢表達式中的子查詢

查詢子句自己可能包含查詢表達式,這有時稱爲子查詢。 每一個子查詢都以本身的 from 子句開頭,該子句不必定指向第一個 from 子句中的相同數據源。 例如,下面的查詢演示在 select 語句用於檢索分組操做結果的查詢表達式。

var queryGroupMax =
    from student in students
    group student by student.GradeLevel into studentGroup
    select new
    {
        Level = studentGroup.Key,
        HighestScore =
            (from student2 in studentGroup
             select student2.Scores.Average())
             .Max()
    };

有關詳細信息,請參閱如何:對分組操做執行子查詢

 

其餘技術請參閱

 

相關文章
相關標籤/搜索