LINQ 查詢作業中的類型關聯性 (C#)

若要有效地撰寫查詢,您應該了解完整查詢作業中的變數類型如何彼此相關。 如果您了解這些關聯性,則可更輕鬆地理解文件中的 LINQ 範例和程式碼範例。 此外,您將了解使用 var 讓變數成為隱含類型時發生的情況。

LINQ 查詢作業在資料來源、查詢本身和查詢執行中都是強型別。 查詢中的變數類型必須與資料來源中的項目類型以及 foreach 陳述式中的反覆運算變數類型相容。 如果類型錯誤可以在使用者遇到它們之前進行更正,則這個強型別可確保在編譯時期攔截到它們。

為了示範這些類型關聯性,後面的大部分範例都會使用所有變數的明確類型。 最後一個範例示範即使使用隱含類型時,還是如何使用 var 來套用相同原則。

未轉換來源資料的查詢

下圖顯示未對資料執行任何轉換的 LINQ to Objects 查詢作業。 來源包含一系列的字串,而且查詢輸出也是一系列的字串。

Diagram that shows the relation of data types in a LINQ query.

  1. 資料來源的類型引數決定範圍變數的類型。
  2. 所選取物件的類型會決定查詢變數的類型。 name 是字串。 因此,查詢變數是 IEnumerable<string>
  3. foreach 陳述式中,會逐一查看查詢變數。 因為查詢變數是一序列的字串,所以反覆運算變數也是字串。

轉換來源資料的查詢

下圖顯示對資料執行簡單轉換的 LINQ to SQL 查詢作業。 查詢會接受一系列的 Customer 物件作為輸出,並只選取結果中的 Name 屬性。 因為 Name 是字串,所以查詢會產生一系列的字串作為輸出。

Diagram showing a query that transforms the data type.

  1. 資料來源的類型引數決定範圍變數的類型。
  2. select 陳述式會傳回 Name 屬性,而非完整 Customer 物件。 因為 Name 是字串,所以 custNameQuery 的類型引數是 string,而非 Customer
  3. 因為 custNameQuery 是一序列的字串,所以 foreach 迴圈的反覆運算變數也必須是 string

下圖顯示稍微複雜的轉換。 select 陳述式會傳回匿名型別,只擷取原始 Customer 物件的兩個成員。

Diagram showing a more complex query that transforms the data type.

  1. 資料來源的類型引數一律是查詢中範圍變數的類型。
  2. 因為 select 陳述式會產生匿名型別,所以必須使用 var 讓查詢變數成為隱含類型。
  3. 因為查詢變數的類型是隱含的,所以 foreach 迴圈中的反覆運算變數也是隱含的。

讓編譯器推斷類型資訊

雖然您應該了解查詢作業中的類型關聯性,但可以選擇讓編譯器為您執行所有工作。 var 關鍵字可以用於查詢作業中的任何區域變數。 下圖與先前討論的範例 2 類似。 不過,編譯器會提供查詢作業中每個變數的強型別。

Diagram that shows the type flow with implicit typing.

LINQ 和泛型型別 (C#)

LINQ 查詢以泛型型別為基礎。 您不需要深入了解泛型,就可以開始撰寫查詢。 不過,您可能需要了解兩個基本概念:

  1. 建立泛型集合類別 (例如 List<T>) 的執行個體時,請將 "T" 取代為清單中會包含之物件的類型。 例如,字串的清單是以 List<string> 表示,而 Customer 物件的清單則是以 List<Customer> 表示。 泛型清單是強型別,而且優點多於以 Object 形式儲存項目的集合。 如果您嘗試將 Customer 新增至 List<string>,則會在編譯時收到錯誤。 由於不需要執行執行階段類型轉換,因此您可以輕鬆使用泛型集合。
  2. IEnumerable<T> 介面藉由使用 foreach 陳述式來列舉泛型集合類別。 泛型集合類別支援 IEnumerable<T>,就像 ArrayList 這類非泛型集合類別支援 IEnumerable

如需泛型的詳細資訊,請參閱泛型

LINQ 查詢中的 IEnumerable<T> 變數

LINQ 查詢變數的類型為 IEnumerable<T> 或衍生類型 (如 IQueryable<T>)。 當您看到類型為 IEnumerable<Customer> 的查詢變數時,只表示查詢在執行時會產生由零個以上的 Customer 物件所組成的序列。

IEnumerable<Customer> customerQuery =
    from cust in customers
    where cust.City == "London"
    select cust;

foreach (Customer customer in customerQuery)
{
    Console.WriteLine($"{customer.LastName}, {customer.FirstName}");
}

讓編譯器處理泛型型別宣告

如果您想要,也可以使用 var 關鍵字避免使用泛型語法。 var 關鍵字會指示編譯器查看 from 子句中指定的資料來源,來推斷查詢變數的類型。 下列範例會產生與上一個範例相同的已編譯程式碼:

var customerQuery2 =
    from cust in customers
    where cust.City == "London"
    select cust;

foreach(var customer in customerQuery2)
{
    Console.WriteLine($"{customer.LastName}, {customer.FirstName}");
}

當變數的類型很明顯,或不需要明確指定巢狀泛型型別 (例如群組查詢所產生的巢狀泛型型別) 時,var 關鍵字會很有用。 一般而言,如果使用 var,建議您考慮到它會讓其他人更難看懂您的程式碼。 如需詳細資訊,請參閱隱含型別區域變數