# 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** 所示。
![](https://box.kancloud.cn/2016-01-07_568e4d635a383.png)
**图 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 项目在“解决方案资源管理器”中的外观。
![](https://box.kancloud.cn/2016-01-07_568e4d637f0c2.png)
**图 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
~~~
![](https://box.kancloud.cn/2016-01-07_568e4d6399b7c.png)
**图 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 中那样筛选错误列表中的每一列。
![](https://box.kancloud.cn/2016-01-07_568e4d63b6b63.png)
**图 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 的预发布版本。文中的所有信息均可能会发生变更。