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 初始化語句中
foreach(var item in list){...}- 在 using 域間中
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。