# Visual Basic 14 的 14 個主要改進之處
**[Lucian Wischik](https://msdn.microsoft.com/zh-cn/magazine/ee532098.aspx?sdmr=LucianWischik&sdmi=authors)**
Visual Basic 14 是 Visual Basic 的最新版,其將作為 Visual Studio 2015 的一部分發布。此版本是從頭開始重新編寫的,采用了 130 萬行 VB 代碼,而較低的版本實際上是使用 C++ 進行編寫的。團隊利用此次重新編寫的機會,重新思考了 VB 的每一部分。我要求團隊挑選出了 14 個主要改進之處。他們綜合了各方面(包括代碼編寫體驗、項目系統基礎和語言本身)選出了自己的最愛。
## 編碼體驗更佳
**1\. 重構**?.NET MVP Jim Wooley 說道,“事實上,我們最終將重構功能直接內置到產品中,這是一個巨大的工程。”
過去,您必須購買附加產品,以對 Visual Basic 進行必要的重構,例如提取方法或內聯臨時變量。C# 的重構較少,而且 Microsoft 與 Developer Express 合作以讓其自 Visual Studio 2005 以來的 Visual Basic 用戶可以使用 Refactor! 加載項。現在,重構功能已內置到 Visual Studio 2015 中。要使用它們,請單擊標識符或突出顯示子表達式。然后,按“Ctrl+點”,或右鍵單擊并選擇“快捷操作”。將彈出一個相關操作的電燈泡上下文菜單,如**圖 1**?所示。
?
**圖 1 Visual Basic 14 現在已內置重構**
請注意,重構能夠感知上下文。例如,如果您要將“Dim circleArea = Math.PI * radius * radius”的右側提取到某個方法中,Visual Basic 建議將該方法命名為“GetCircleArea”。如果您要進一步更改名稱,則系統將轉入內聯重命名模式。此內聯重命名的智能方面表現在,如果您選擇的名稱已被使用,它可以檢測并提醒您名稱沖突,并盡可能避免沖突,而且它適用于您的整個方案,甚至可以更改 C# 項目中的名稱。
**2\. 分析器**一位 Windows PowerShell MVP 說道,“該功能非常引入注目。對于如何使用此功能,我有很多想法。我在用戶的代碼中看到每一個細節。”
分析器是一種用于將這些電燈泡、代碼操作和錯誤曲線供自己所用的方法。您可以使用它們在您的團隊中強制執行代碼準則。如果您急于修復一個問題,可以插入一個分析器在整個解決方案中快速查找常見代碼問題。而且,您使用的很多庫可以通過他們自己的內置分析器變成代碼感知。例如,假設您尚未使用 Microsoft Azure Storage 庫或尚未閱讀有關最佳實踐的文章。由于該庫現在包含可在使用 API 時檢測常見問題的分析器,因此您可以立即確定使用它的方法是否正確無誤。這就像當您鍵入內容時,有一個代碼審閱專家站在您的身后。
您可以在新的“引用 | 分析器”節點下(或通過 NuGet)將分析器添加到您的項目。添加的分析器成為項目編譯的一部分,將在您鍵入時實時運行,并實時顯示錯誤曲線。當您在 Visual Studio 或命令行中構建項目時,分析器會運行,甚至還會在生成服務器上運行。分析器可能會“打開”編譯器的內部,以查看項目源代碼的語法樹及其類型和成員。開發人員會驚訝地發現,將其專業域知識編碼到分析器很容易,這歸功于這些語法樹以及類型和成員。我最喜歡的分析器是檢測我的團隊在何處使用應該是“Async Function … As Task”的 Async Sub 方法并發出警告的分析器。這是很多人沒有意識到的異步編程的困難之處,而且這將導致難于捕獲并發錯誤,因此很高興我的團隊現在可以在編譯時捕獲該錯誤。要開始編寫您自己的分析器,請轉到?[roslyn.codeplex.com](http://roslyn.codeplex.com/)。
**3\. 無需將光標從代碼行移開**?Visual Basic 團隊成員 Dustin Campbell 介紹道,“我們正在做正確的事情。”
作為 VB 用戶,您已經長期習慣在鍵入代碼時,執行快速的“上下移動光標”以查看是否會出現錯誤曲線。否則,您會編寫代碼以修復該錯誤曲線,但是必須對該曲線執行“上下移動”操作以使其消失。
現在,您完全無需這么做。只要將光標停留在原處即可,錯誤曲線會自行顯示或消失。
**4\. XML 文檔注釋中的引用**?.NET MVP Sam Harwell 說道,“對于熱衷于文檔的用戶,這意味著朝正確方向邁進了一大步。”
您是否熱衷于 XML 文檔注釋?以下是一個小示例:
~~~
''' <summary>
''' Similar to <see cref="List(Of Integer).Count"/>
''' </summary>
''' <param name="e">Thing to count</param>
''' <remarks></remarks>
Sub Count(e As IEnumerable)
End Sub
~~~
在較低版本的 VB 中,當您在注釋中鍵入 Cref 和參數名稱時,可以獲得完整的幫助列表,但除此以外只能靠您自己。編譯器執行一些最小的驗證,以檢查名稱是否已存在,但是它們是用灰色進行排版的,很難發現、查找或重構。
現在在 Visual Basic 14 中,Cref 和參數名稱參數將用適當的顏色來標記。您可以將鼠標懸停在其上,以查看工具提示。當您執行重命名符號重構(Ctrl+R、Ctrl+R)時,Visual Basic 將重命名對符號的所有引用,包括 Cref 和參數名稱中的引用。您可以右鍵單擊其中一個引用,然后“轉到定義”或“查找所有引用”。如果您要引用含有若干個重載的方法,現在可以清楚地引用您要的單個重載。所有這些改變使您可以更輕松地在 XML 文檔注釋中鍵入引用,并確保它們正確。
## 項目系統基礎
**5\. “解決方案資源管理器”中的“引用”節點**?Visual Basic 團隊成員 Lucian Wischik 說道,“事實上,我們每天都必須對引用做出微調。”
**圖 2**?顯示了典型的 Visual Basic 14 項目在“解決方案資源管理器”中的外觀。

**圖 2 “引用”節點現在顯示在“解決方案資源管理器”中**
“引用”節點的新增功能。此節點過去為隱藏狀態,您必須單擊“顯示所有文件”才能進行查看,但這樣也會顯示許多無關的文件。
當您 10 年前開始使用 Windows Forms 項目時,這個之前的操作可能會有意義,并且通常會包含正確的引用集。但是,現代開發技術的事實是需要頻繁使用“引用”節點,尤其是用于管理 NuGet 引用。該節點雖然很少,但是很方便,可以輕松地在“解決方案資源管理器”中查找“引用”節點。
**6\. 共享的項目**?Windows 開發人員 MVP Morten Nielsen 評價道,“除了使其更易于使用的鏈接文件以外,該工具真的非常棒。”
假設您要在兩個或多個項目之間共享代碼。例如,常見的情況是保留應用的 Windows Presentation Foundation (WPF) 和 Windows Phone 版本。目標始終是相同的:最大程度地重用代碼,例如,您對某個項目進行的錯誤修復會使其他項目受益。
過去,您可以在兩種技術中進行選擇:使用鏈接的文件來共享常見的源代碼,或將共享代碼重新架構到可移植類庫以共享常見的二進制。現在,Visual Basic 14 提供了第三種強大的技術:共享的項目。
為什么使用共享的項目?共享代碼的任務比較深奧且充滿挑戰,因為沒有良好的普遍適用的解決方案。可移植類庫是一種良好的、干凈的解決方案,但是它們會強制您重新架構代碼,因此常見代碼從不調用 WPF 或 Phone 項目;其只調用 WPF 和 Phone 中的系統 API。共享的項目更易于使用,因為它們不要求重新架構。
要創建共享的項目,請右鍵單擊您的解決方案,然后選擇“添加 | 新項目 | VB | 共享的項目”。接下來,按順序右鍵單擊每個項目的“引用”節點,并選擇“添加 | 共享的項目”。共享的項目是源文件、XAML 文件、圖像和引用該項目的每個項目中包含的其他資產的集合。
對于您的每個項目,您還可以使用自定義常量(例如,WPF 和 PHONE)來設置“我的項目 | 編譯 | 高級編譯選項 | 自定義常量”。然后,在共享的節點中,您可以按照如下方式調用特定于項目的 API:
~~~
#If WPF Then
? ' nothing needed
#ElseIf PHONE Then
? ShowBatteryStatus()
#End If
~~~
**7\. 編譯速度提高 50%**?.NET MVP Sam Harwell 說道,“提高 50% 并非開玩笑。”
過去使用 C++ 來編寫 Visual Basic 編譯器。對于 Visual Basic 14,團隊已使用 VB 對其進行徹底重寫,使其速度變得相當快。兩者對比如下:
* 一個大型解決方案生成(130 萬行代碼)的時間從 68 秒減少到 41 秒。
* 一個寒冷解決方案加載(Windows 應用商店應用)的時間從 6.7 秒減少到 4.6 秒。
這節約了大量的時間。當您思想集中,而非在“完成編碼”和“按 F5 到達斷點”之間猶豫時將會非常好。
對于認為 C++ 的速度快于 VB 的人而言,提高 50% 的性能會讓他們很驚訝。實際上,算法、數據結構和并發性是真正產生速度優勢的地方。使用 VB 進行重寫提高性能主要源自以下幾個方面:重新思考數據結構;能夠表達更潔凈的算法和更安全地重構;使用異步和線程池;使用 Visual Studio 分析工具來發現 CPU 和內存分配熱點;以及使用分析器來檢測簡單的 .NET 限制(例如不必要的裝箱)。
**8.“監視”窗口中的 Lambda 和 LINQ 表達式**?Visual Studio Uservoice 用戶 Marco Senn-Haag 夸獎道,“太棒了!”
LINQ 和 lambda 是匯總數據的好方法。最需要該方法的一個地方是處于調試期間的“監視”和“即時”窗口。過去,此處使用 LINQ 或 lambda 會生成一個錯誤:
~~~
Evaluation of lambda expressions is not valid in the debugger.
~~~
現在,如**圖 3**?中所示,可以正常運行!例如,如果您位于含有名稱為“客戶”的集合的斷點,可以通過在“監視”窗口中編寫此代碼來快速查看該集合:
~~~
From c In customers Where c.OrderStatus = "Unfulfilled" Select c.LastName
~~~

**圖 3“監視”窗口中的 Lambda 和 LINQ 表達式**
您是否知道可以在不啟動程序的情況下使用“即時”窗口?例如,如果您剛使用函數 GetName 編寫了一個模塊,您可以打開“即時”窗口(“調試 | Windows | 即時”)并鍵入 "? GetName()",系統將進行評估。
Visual Studio 2015 還將更好地支持諸如異步和迭代器方法中以及更常見的情形(如 LINQ 查詢內部和周圍以及 lambda)中的“編輯”和“繼續”,甚至允許您向現有方法中添加新的查詢或 lambda 表達式。雖然 Visual Studio 2015 Preview 不具備這一功能,但是您可以在最終版中實現所有此類功能。
**9\. 錯誤列表更佳**?Visual Basic 團隊成員 Anthony D. Green 說道,“這前后都在講述著一個非常精彩的故事。”
Visual Basic 14 中的錯誤列表有很多實用的改進,這些改進響應了用戶的長期請求(見**圖 4**)。之前的錯誤列表常顯示完全符合條件的類型名稱;現在其僅顯示部分符合條件的類型名稱,以便您更方便地查看錯誤消息。而且,它還會顯示錯誤代碼,這便于按照錯誤代碼進行分類。更棒的是,錯誤代碼是指向 Internet 搜索的超鏈接,這比指向 MSDN 文檔頁面的鏈接更加實用。此外,您可以像在 Excel 中那樣篩選錯誤列表中的每一列。

**圖 4 Visual Studio 2015(下圖)中的錯誤列表比 Visual Studio 2013(上圖)更具可讀性和靈活性**
有時,當您進行較大的改動時,很容易會使您的解決方案中的很多下游代碼被損壞。過去,VB 僅顯示前 101 個錯誤。這讓人難以了解損壞的范圍有多大,或者大致了解您要進行哪些改動。現在,Visual Basic 14 將顯示所有錯誤,且沒有任何限制。(可以在“工具 | 選項 | 文本編輯器 | 基本 | 高級 | 顯示關閉文件的診斷信息”下進行配置)。
## 語言改進
**10\. 空傳播運算符**?.NET MVP Deborah Kurata 介紹道,“每個人都需要經常檢查空值以提高效率和減少錯誤率。”
假設您有一個 Customer 類,其中包含合理的空“地址”字段,也許是因為您的應用無需在其中鍵入地址。之前需要對地址執行某些操作的所有代碼(例如將地址顯示給用戶)必須考慮進行空值檢查以確保其正確。這種空值檢查很快會讓人感到厭煩。通過 Visual Basic 14,您可以使用新增的 ?. 運算符來輕松地處理類似于上述情況的空值:
~~~
Console.WriteLine("{0} ({1})",
? customer.Name,
? customer.Address?.Country)
~~~
?. 運算符是分配給臨時變量然后檢查空值的這種常用但繁瑣的模式的簡寫形式:
~~~
Dim _temp = customer.Address
Console.WriteLine("{0} ({1})",
? customer.Name,
? If(_temp Is Nothing, Nothing, _temp.Country))
~~~
例如,您可以在序列中使用 ?.,并將其與常規的點運算符 a?.b.c?.d 結合使用。將從左到右進行讀取。?. 使用的所有空值只停止短序列,然后回答“無”,而 . 使用的所有空值像往常一樣拋出 NullReferenceException。
?. 運算符是 . 運算符的 null 條件版本。大多數運算符也有 null 條件版本:索引,array?(i);委托調用,delegate?(args);字典查找;dict?!key;以及 XML 軸屬性、xml?.@attr、xml?.、xml?...。
您也可以在其他便捷的方法中使用 ?.:
~~~
If customer?.Age > 50 Then ...
' If branch taken only if customer is non-null AND is older than 50
Dim name = If(customer?.Name, "blank")
' Pick a default name if customer is null
Dim first = customers?.FirstOrDefault()
' Only invoke this method if customers is non-null
~~~
**11\. 多行字符串文字**?論壇用戶 Scaramouche 說道,“我對多行字符串文字感到很興奮,這是否有點可悲?”
以下是使用 vbCrLf 編寫多行字符串的過程:
~~~
Dim json = "{" & vbCrLf &
"? 'Name': 'Bad Boys'," & vbCrLf &
"? 'ReleaseDate': '1995-4-7T00:00:00'," & vbCrLf &
"? 'Genres': ['Action','Comedy']" & vbCrLf &
"}"
~~~
可以理解的是,常見的請求已允許字符串文字跨多行。現在,您可以在 Visual Basic 14 中執行以下操作:
~~~
Dim json = "{
? 'Name': 'Bad Boys',
? 'ReleaseDate': '1995-4-7T00:00:00',
? 'Genres': ['Action','Comedy']
}"
~~~
一個相關且值得關注的功能(也是常請求的)是您可以在多行語句中插入注釋。之前不允許在 LINQ 表達式內部添加注釋,如下所示:
~~~
Dim q = From x In y ' This is a from clause
??????? Where x < z ' And this is the where
??????? Select x??? ' This select is redundant
~~~
**12\. 字符串插值**?Channel9 用戶 Judah 說道,“字符串插值使代碼更加簡單,而且可以表達出相關目的。”
字符串插值是使用其中的表達式來編寫字符串的更加簡單的方式,如下所示:
~~~
Dim s = $"hello {p.Name} you are {p.Height:0.00}m tall"
~~~
通常簡寫為以下形式:
~~~
Dim s = String.Format("hello {0} you are {1:0.00}m tall", p.Name, p.Height)
~~~
字符串插值通常比對 String.Format 的顯式調用更易于編寫,因為它可以讓您避免處理位置占位符 {0} 和 {1}。而且,還提供完整的顏色和 IntelliSense 供缺口內的表達式使用。字符串插值與編程字符串配合使用的效果非常棒,如以下示例所示:
~~~
Dim fn = $"C:\Documents\{folder}\{file}.{ext}"
Dim url = $"http://{site}/{path}/{file}?search={query}"
~~~
和對 String.Format 來說很正常的操作一樣,其會對使用當前區域性設置的字符串設置格式。如果您要構建一個包含浮點數的編程字符串(例如當您向 Web 服務傳遞經緯度時),您最可能使用 InvariantCulture 進行代替。Visual Studio 2015 將支持此功能,但是目前這一設計尚未確定。
請注意,Visual Studio 2015 Preview 中尚不存在字符串插值,但是最終將出現在 Visual Studio 2015 中。
**13\. NameOf**?Roslyn 論壇成員 wwloyd 說道,“我認為這將在很多情境中派上用場。”
當字符串文字引用源代碼中的名稱時,NameOf 運算符是將字符串文字嵌入到代碼的更好方式。下面是一個示例:
~~~
Sub f(s As String)
? If s Is Nothing Then Throw New ArgumentNullException(NameOf(s))
End Sub
~~~
NameOf 運算符不會在運行時進行評估:它是一個編譯時常量,在本例中是常量字符串 "s"。使用 NameOf(s) 的原因在于可避免出現拼寫錯誤。例如,如果您重命名方法參數,系統會自動重命名 NameOf 參數。只使用字符串文字將不會出現這種情況。以下是 NameOf 適用的另一個情況:
~~~
Private _age As Integer
Property Age As Integer
? Get
??? Return _age
? End Get
? Set
??? _age = Value
??? RaiseEvent PropertyChanged(
????? Me, New PropertyChangedEventArgs(NameOf(Age)))
? End Set
End Property
~~~
請注意,Visual Studio 2015 Preview 中尚不存在 NameOf,但是最終將出現在 Visual Studio 2015 中。
**14\. 開放源代碼**?“我們正努力深入社區。社區中潛藏著很多有識之士。我們重視來自社區的 pull 請求,就像我們處理自己的想法一樣。”C#/Visual 基礎結構師 Anders Hejlsberg 說道。
最后一個改進并非針對 Visual Basic 本身,而是針對 VB 的使用過程。
VB 編譯器的源代碼現在是開放源代碼。語言本身的設計過程同樣也是。每個新功能提案都是開放的,受公眾的嚴格監督。Microsoft Visual Basic 語言設計團隊的成員現在基本上都是語言的管理者。團隊很重視提案,會深入考慮提案,查看是否存在預料之外的限制或特殊情況,并確定它們是否滿足收錄到語言中的條件。語言設計會議記錄也會公開發布。在 Visual Basic 語言設計團隊工作相當令人興奮,成為 VB 的用戶也令人興奮。
## 總結
Visual Basic 14 有很多改進之處。本文只討論了其中的將近一半而已。總的宗旨是以易用的方式更好地使用現有 VB,而無需引入難以理解的新概念。有關詳細信息,請查看?[roslyn.codeplex.com](http://roslyn.codeplex.com/)?和[blogs.msdn.com/vbteam](http://blogs.msdn.com/vbteam)。
本文是指 Visual Basic 14 和 Visual Studio 2015 的預發布版本。文中的所有信息均可能會發生變更。