C#3.0新增功能03 隱式類型本地變數

  • 2019 年 10 月 4 日
  • 筆記

從 Visual C# 3.0 開始,在方法範圍內聲明的變數可以具有隱式「類型」var。 隱式類型本地變數為強類型,就像用戶已經自行聲明該類型,但編譯器決定類型一樣。 i 的以下兩個聲明在功能上是等效的:

var i = 10; // 隱式類型  int i = 10; // 顯式類型

下面的示例演示兩個查詢表達式。 在第一個表達式中,var 的使用是允許的,但不是必需的,因為查詢結果的類型可以明確表述為 IEnumerable<string>。 不過,在第二個表達式中,var 允許結果是一系列匿名類型,且相應類型的名稱只可供編譯器本身訪問。 如果使用 var,便無法為結果新建類。 請注意,在示例 #2 中,foreach 迭代變數 item 必須也為隱式類型。

// 示例 #1: 當 select 子句指定字元串時,var是可選的  string[] words = { "apple", "strawberry", "grape", "peach", "banana" };  var wordQuery = from word in words                  where word[0] == 'g'                  select word;    // 因為序列中的每個元素都是字元串,而不是匿名類型,所以var在這裡也是可選的。  foreach (string s in wordQuery)  {      Console.WriteLine(s);  }    // 示例 #2: var 是必需的,因為select子句指定匿名類型  var custQuery = from cust in customers                  where cust.City == "Phoenix"                  select new { cust.Name, cust.Phone };    // 必須使用var,因為序列中的每個項都是匿名類型  foreach (var item in custQuery)  {      Console.WriteLine("Name={0}, Phone={1}", item.Name, item.Phone);  }

隱式類型本地變數

可聲明局部變數而無需提供顯式類型。 var 關鍵字指示編譯器通過初始化語句右側的表達式推斷變數的類型。 推斷類型可以是內置類型、匿名類型、用戶定義類型或 .NET Framework 類庫中定義的類型。 有關如何使用 var 初始化數組的詳細資訊,請參閱隱式類型化數組。

以下示例演示使用 var 聲明局部變數的各種方式:

// i 被編譯成 int 類型  var i = 5;    // s 被編譯成 string 類型  var s = "Hello";    // a 被編譯成 int[] 數組  var a = new[] { 0, 1, 2 };    // expr 被編譯成 IEnumerable<Customer> 或者 IQueryable<Customer> 類型  var expr =      from c in customers      where c.City == "London"      select c;    // anon 被編譯成匿名類型  var anon = new { Name = "Terry", Age = 34 };    // list 被編譯成 List<int> 集合  var list = new List<int>();

重要的是了解 var 關鍵字並不意味著「變體」,並且並不指示變數是鬆散類型或是後期綁定。 它只表示由編譯器確定並分配最適合的類型。

在以下上下文中,可使用 var 關鍵字:

  • 在局部變數(在方法範圍內聲明的變數)上,如前面的示例所示。
  • 在 for 初始化語句中
for(var x = 1; x < 10; x++)
foreach(var item in list){...}
using (var file = new StreamReader("C:\myfile.txt")) {...}

有關詳細資訊,請參閱如何:在查詢表達式中使用隱式類型本地變數和數組。

var 和匿名類型

在許多情況下,使用 var 是可選的,只是一種語法便利。 但是,在使用匿名類型初始化變數時,如果需要在以後訪問對象的屬性,則必須將變數聲明為 var。 這是 LINQ 查詢表達式中的常見方案。 有關詳細資訊,請參閱匿名類型。

從源程式碼角度來看,匿名類型沒有名稱。 因此,如果使用 var 初始化了查詢變數,則訪問返回對象序列中的屬性的唯一方法是在 foreach 語句中將 var 用作迭代變數的類型。

class ImplicitlyTypedLocals2  {      static void Main()      {          string[] words = { "aPPLE", "BlUeBeRrY", "cHeRry" };            // 如果查詢生成一系列匿名類型,則在foreach語句中使用var訪問屬性。          var upperLowerWords =               from w in words               select new { Upper = w.ToUpper(), Lower = w.ToLower() };            // 執行查詢          foreach (var ul in upperLowerWords)          {              Console.WriteLine("Uppercase: {0}, Lowercase: {1}", ul.Upper, ul.Lower);          }      }  }  /* 輸出:      Uppercase: APPLE, Lowercase: apple      Uppercase: BLUEBERRY, Lowercase: blueberry      Uppercase: CHERRY, Lowercase: cherry   */

特別說明

以下限制適用於隱式類型化變數聲明:

  • 僅當局部變數在相同語句中進行聲明和初始化時,才能使用 var;變數不能初始化為 null,也不能初始化為方法組或匿名函數。
  • var 不能在類範圍內對欄位使用。
  • 使用 var 聲明的變數不能在初始化表達式中使用。 換句話說,此表達式是合法的: int i = (i = 20);,但是此表達式會生成編譯時錯誤:var i = (i = 20);
  • 不能在相同語句中初始化多個隱式類型化變數。
  • 如果一種名為 var 的類型處於範圍內,則 var 關鍵字會解析為該類型名稱,不會被視為隱式類型化局部變數聲明的一部分。

帶 var 關鍵字的隱式類型只能應用於本地方法範圍內的變數。 隱式類型不可用於類欄位,因為 C# 編譯器在處理程式碼時會遇到邏輯悖論:編譯器需要知道欄位的類型,但它在分析賦值表達式前無法確定類型,而表達式在不知道類型的情況下無法進行計算。 考慮下列程式碼:

private var bookTitles;

bookTitles 是類型為 var 的類欄位。 由於該欄位沒有要計算的表達式,編譯器無法推斷出 bookTitles 應該是哪種類型。 此外,向該欄位添加表達式(就像對本地變數執行的操作一樣)也是不夠的:

private var bookTitles = new List<string>();

當編譯器在程式碼編譯期間遇到欄位時,它會在處理與其關聯的任何表達式之前記錄每個欄位的類型。 編譯器在嘗試分析 bookTitles 時遇到相同的悖論:它需要知道欄位的類型,但編譯器通常會通過分析表達式來確定 var 的類型,這在事先不知道類型的情況下無法實現。

你可能會發現,對於在其中難以確定查詢變數的確切構造類型的查詢表達式,var 也可能會十分有用。 這可能會針對分組和排序操作發生。

當變數的特定類型在鍵盤上鍵入時很繁瑣、或是顯而易見、或是不會提高程式碼的可讀性時,var 關鍵字也可能非常有用。 var 採用此方法提供幫助的一個示例是針對嵌套泛型類型(如用於分組操作的類型)。 在下面的查詢中,查詢變數的類型是 IEnumerable<IGrouping<string, Student>>。 只要你和必須維護你的程式碼的其他人了解這一點,使用隱式類型化實現便利性和簡便性時便不會出現問題。

// 與前面的示例相同,只是我們使用整個姓氏作為鍵。  // 查詢變數是IEnumerable<igrouping<string,student>>  var studentQuery3 =      from student in students      group student by student.Last;

但是,使用 var 至少有可能使程式碼對其他開發人員更加難以理解。 為此,C# 文檔通常只在需要時才使用 var。

Tags: