
7.1 对IntelliSense的解释
IntelliSense是在Microsoft应用程序中自动获得帮助和操作的概括性词语。最常见的IntelliSense是Microsoft Word中拼写错误单词下的波浪线,或者在Microsoft Excel电子表格中表示某个单元格的内容与期望的不一致的可视化指示器。
即使是这些基本的指示器也能快速执行相关的操作。在Word中右击带红色波浪线的单词,会显示一个建议替代单词的列表。其他应用程序也有类似特性。
好消息是Visual Studio一直有类似的功能。事实上,最简单的IntelliSense特性要追溯到Visual Basic 6。在Visual Studio的每个版本中,Microsoft都改进了IntelliSense特性,使之更能自动感知上下文,把它放在更多的地方,所以用户应总是能及时获得需要的信息。
在Visual Studio 2015中,有许多不同的功能都划归IntelliSense的名下。从对错误代码的可视化反馈,到设计窗体时的智能标签,再到使用快捷键插入一整块代码,这些功能极大地提升了创建应用程序的效率和控制代码的能力。Visual Studio 2015中的一些特性,如建议模式和Generate From Usage功能,支持应用程序开发的另一种风格——测试驱动开发(Test-Driven Development,TDD)。
7.1.1 通用的IntelliSense
IntelliSense最简单的功能是对代码清单里的代码错误给出即时反馈。图7-1显示了这样一个例子,在这个例子里使用未知的数据类型来实例化一个对象。由于数据类型在这段代码中是未知的, Visual Studio给它添加红色(C#或C++)或蓝色(VB)的波浪线来表示有错误。

图7-1
这种彩色反馈的格式可以在Options对话框的Fonts and Colors组里调整。
把鼠标指针悬停在出错的代码上,将显示一个工具提示,解释出了什么问题。在这个例子中,将鼠标指针悬停在数据类型上时,出现的工具提示是“The type or namespace name‘Customer’could not be found”(类型或名称空间Customer未找到)。
Visual Studio可以在后台不断地预编译用户编写的代码来查找这类错误,查找任何会导致编译错误的代码。如果给项目添加了Customer类,Visual Studio会自动处理,并删除IntelliSense的标记。
和错误相关的智能标签并不是新功能。但是,Visual Studio 2015引入了许多革新来改善其实用工具。图7-1显示了一个灯泡。这是智能标签指示器,在许多不同的情形下都可见(也是有用的)。遇到错误时,会显示灯泡,Visual Studio可以提供一个或多个更正动作过程。单击灯泡右边的箭头,会显示可用的选项,如图7-2所示。

图7-2
可以看出,Visual Studio能想出不同的方式纠正缺失类型的问题。这些是“创建Customer类型的类”的变体,区别是类的位置和作用域。把鼠标移到不同的选项上,右边的方框就会提供一个修正示例。甚至可以更进一步,点击Preview Changes链接,查看变更的更详细描述,包括受影响的文件。图7-3提供了一个示例。

图7-3
所有Microsoft应用程序用于激活智能标记的传统快捷键是Shift+Alt+F10,Visual Studio 2015还为相同的操作提供了更友好的Ctrl+.快捷键。
Visual Studio中的智能标签技术不仅存在于代码窗口中,也不总是涉及灯泡。事实上,当在Design视图(见图7-4)中编辑窗体或用户控件时,Visual Studio 2015也会在可视化组件上提供智能标签。

图7-4
选择一个有智能标签的控件时,在这个控件的右上角会出现一个小三角形按钮。单击这个按钮会显示智能标签任务列表。图7-4显示了标准TextBox控件的任务列表。
打开智能标记的快捷键也可用于可视化控件。
7.1.2 IntelliSense和C++
Visual Studio 2015为C++/CLI提供了完整的IntelliSense支持。Microsoft近年来已经为C++添加了一些IntelliSense支持,然而,始终存在一些限制。大型项目仍然会导致IDE的性能降低。自动完成、Quick Information和Parameter Help等功能已经得到完善。但是,Navigate To等工具则结合使用浏览和IntelliSense来实现它们的功能。
这种情况自从Visual Studio 2012以来得到了改变。下面介绍的所有主题都是C++开发人员感兴趣的内容。底层的基础结构提供了健壮的IntelliSense性能,现在还包含许多IntelliSense功能。因此, C++开发人员重新开始关注IntelliSense。
7.1.3 单词和短语的自动完成
只要一开始编写代码,Visual Studio 2015 IntelliSense的强大功能就会显现出来。在输入代码时, IntelliSense会显示各种下拉列表,以帮助选择正确的成员、函数以及参数类型,甚至在编写完代码之前,就可以减少可能的编译错误。熟悉了IntelliSense行为后,可以显著减少实际的编码量。这对于使用比较繁琐的Visual Basic语言的开发人员而言,节省了许多时间。
1.IntelliSense环境
在Visual Studio 2015中,只要一开始在代码窗口中输入代码,就会显示IntelliSense。图7-5展示了在Visual Basic中创建For循环时显示的IntelliSense。如图7-5(a)所示,只要输入f,就会显示IntelliSense。在输入后面的每个字母时,可用单词的列表会逐渐缩短。可以看出,该列表包含了匹配所输入字母的所有选项(这里是匹配以前缀For开头的所有选项),如语句、类、方法或属性。

图7-5
注意图7-5(b)所示的区别:在单词For的后面输入了一个空格。现在IntelliSense列表缩短为只显示下一个可能的单词(Each)。最后,在IntelliSense列表的上方有一个< new variable >项,这表示可以在这个位置指定新变量。
< new variable >项仅在Visual Basic中显示。
根据输入的字母缩短IntelliSense列表是很有用的,但这个功能是把双刃剑。当要查找一个变量或成员,但不记得它的名称时,就可以输入自己猜测的前几个字母,再使用滚动条定位正确的选项。显然,如果这个选项不以输入的字母开头,这种方法就无效。为了打开选项的完整列表,可以在IntelliSense列表可见时按下Backspace键。或者,如果IntelliSense列表不可见,则使用Ctrl+Space键列出所有选项。
IntelliSense并不只是帮助用户找到以用户输入的字符开头的成员。IntelliSense把输入的字符看做一个单词。于是,在查找匹配时,IntelliSense会考虑成员名中间的单词。为此,IntelliSense会根据Pascal命名规则在成员名中查找单词边界。图7-6显示了一个C#例子,输入Console.in时, IntelliSense会找到In、InputEncoding、OpenStandardInput、SetIn和TreatControlCAsInput,但找不到LargestWindowHeight,尽管它也包含子字符串in。

图7-6
如果确切地知道自己要查找什么,以大写形式输入每个单词的第一个字符,可以减少更多的输入。例如,如果输入System.Console.OSI,IntelliSense就会选择OpenStandardInput。
如果发现IntelliSense信息遮挡了其他代码行,或者希望隐藏该列表,就可以按下Esc键。或者,如果要查看隐藏在IntelliSense列表后面的内容,但不想完全关闭IntelliSense,就可以按住Ctrl键,这会使IntelliSense列表变成半透明,此时就可以读取IntelliSense后面的代码,如图7-7所示。

图7-7
IntelliSense列表不仅用于显示信息。在这个列表中选择一项,Visual Studio就会自动在编辑器窗口中插入完整的文本。从该列表中选择一项有许多方式。可以用鼠标双击需要的项;也可以使用箭头键改变突出显示的项,再按下Enter键或Tab键来插入文本。最后,列表中的一项突出显示时,如果按下提交字符,就会自动选择该项。提交字符是不允许在成员名中出现的字符,包括圆括号、方括号、数学符号和分号。
2.成员列表
IntelliSense已经存在了很长时间,所以大多数开发人员都很熟悉成员列表。输入一个对象的名称,之后输入一个句点( .),表示要引用该对象的某个成员时,Visual Studio就会自动显示这个对象的可用成员列表。如果这是第一次访问这个对象的成员列表,Visual Studio就会以字母顺序显示成员列表,并使列表最前面的选项可见。但如果已经使用过该成员列表,Visual Studio就会突出显示上次访问的成员来减轻重复输入代码的任务。
3.建议模式
默认情况下,Visual Studio 2015显示IntelliSense成员列表时会选中一个成员,在用户输入的过程中,选中项会移到列表中最匹配所输入字符的项。如果按下Enter键、Space键或输入一个提交字符(如左括号),当前选中的成员就会被插入编辑器窗口。这个默认行为称为“完成模式”。
大多数情况下,完成模式提供了用户需要的行为,可以减少很多输入。但在一些活动中,完成模式可能有问题。一个这样的活动是测试驱动开发,这种开发要频繁引用尚未定义的成员。这会使IntelliSense选择用户不希望的成员,插入用户不需要的文本。
为避免这种情况,可以使用IntelliSense的建议模式。IntelliSense在建议模式下时,列表的一个成员会获得焦点,但默认不会选中。在用户输入的过程中,IntelliSense会把焦点指示器移到与用户输入字符最匹配的项,但不会自动选择它,而是把用户输入的字符添加到IntelliSense列表的顶部。如果用户输入一个提交字符或按下Space键或Enter键,就把用户输入的字符串插入编辑器窗口。
图7-8显示了一个可以用建议模式解决的问题。如图7-8(a)所示,用户编写一个测试程序,用来测试CustomerData类上的新方法Load。CustomerData类还没有Load方法,但有LoadAll方法。
如图7-8(b)所示,用户输入Load,后跟左括号字符,IntelliSense就会不正确地假定用户需要LoadAll方法,于是把它插入编辑器。

图7-8
为避免这种情况,可按下Ctrl+Alt+Space快捷键,打开建议模式。现在输入Load时,它会显示在IntelliSense列表的顶部。输入左括号字符后,编辑器窗口就添加了Load,如图7-9所示。

图7-9
仍可以使用箭头键从IntelliSense列表中选择。也可以按下Tab键在成员列表中选择有焦点的项。
IntelliSense将一直处于建议模式下,除非再次按下Ctrl+Alt+Space快捷键,才能回到完成模式下。
4.代码存根的自动完成
除了单词和短语的自动完成功能外,IntelliSense引擎还有一个功能:代码存根(stub)的自动完成特性。在VB中创建一个函数时,编写完该函数的声明后按下Enter键,就可以看到这个特性。Visual Studio会自动重新格式化该行代码,为没有明确定义上下文的参数添加相应的ByVal关键字,并添加一行End Function来完成函数代码。编辑XML文档时也可以看到这个特性。输入一个新元素的开始标记时,Visual Studio会自动添加结束标记。
Visual Studio 2015使这个代码存根的自动完成特性更进一步,允许对接口和方法重载执行相同的操作。当添加特定的代码结构,如在C#类定义中添加接口时,Visual Studio会自动生成实现接口所需的代码。为展示这个过程,下面的步骤使用IntelliSense引擎为一个简单的类生成接口的实现代码。
(1)启动Visual Studio 2015,创建一个C# Windows Forms应用程序项目。IDE生成初始代码后,在代码编辑器里打开Form1.cs。
(2)在文件的顶部,添加using语句,为System.Collections名称空间提供快捷方式。
using System.Collections;
(3)添加下面的代码,开始新类的定义:
public class MyCollection:IEnumerable
输入IEnumerable关键字时,Visual Studio会先添加红色的波浪线,表示存在一个错误。
(4)将鼠标指针悬停在IEnumerable关键字上,稍后就会显示一个灯泡指示器和一条消息(如图7-10所示)。

图7-10
灯泡右边的信息区域描述了Visual Studio检测到的错误。这个文本的细节在很大程度上依赖于错误。对于这个错误,它基本上是说,声明一个类来实现接口(IEnumerable),却没有实现这个接口需要的所有元素。
(5)单击灯泡右边的下拉箭头,或点击文本区域的Show Potential Fixes链接,就显示一个列表(参见图7-11),说明Visual Studio如何更正错误。如果悬停在选项上,右边的文本区就会显示选择更正方式所带来的变化的详细信息。此外,可以预览更改,或使更改应用于文档、项目或整个解决方案。

图7-11
(6)选择Implement interface选项,Visual Studio 2015将自动生成实现该接口所需的最少代码。灯泡不仅会在悬停于错误源上时显示出来。如果把光标放在有错误的代码行上,灯泡就会出现在代码行的左边,如图7-12所示。点击灯泡,会启动与步骤(5)相同的更正过程。

图7-12
生成的属性和类可原样使用,但是会生成方法存根以抛出NotImplemented-Exception异常。
事件处理程序也可以由Visual Studio 2015自动生成。为此,IDE使用类似于实现接口的方式。编写语句的第一部分(如myBase.OnClick +=)时,Visual Studio会提供一个建议的完成方案,按Tab键就可以选择它。
5.从用例中生成
除了从已有的定义中生成代码外,有时从用户使用代码元素的方式来生成该代码元素的定义更方便。如果进行测试驱动开发,为还没有定义的类编写测试程序,这种方式就尤为有效。从测试程序中生成类非常方便,这就是C#和Visual Basic中Generate From Usage的功能。
为理解如何使用这个功能,下面创建一个非常简单的Customer类,我们将编写一些客户端代码,使用该功能从用例中生成Customer类:
(1)启动Visual Studio 2015,创建一个C# Console Application项目,用IDE打开Program.cs文件。
(2)用如下代码更新Main方法:
C#
Customer c = new Customer { FirstName = "Joe", LastName = "Smith" }; Console.WriteLine(c.FullName); c.Save();
(3)在Customer类名的两个实例下方会出现红色的波浪线。右击其中一个实例,从上下文菜单中选择Quick Actions,就会显示如图7-10所示的一组数据。但在这个例子中,单击下拉箭头,选项就更适于创建缺失的Customer类。在YourAppName的Customer中选择Generate class(在新文件中),就会在项目中创建一个新类Customer。如果打开Customer.cs文件,就会看到一个空白的类声明。Visual Studio发现FirstName、LastName、FullName和Save都不是这个类的成员。
(4)对于每个不存在的属性,使用灯泡功能,给Customer类添加属性。现在再次查看Customer.cs文件,注意Visual Studio提供了这些属性的实现代码。
(5)对Save方法重复这个操作,为此,右击它,并从Quick Actions列表中选择Generate ‘Customer.Save’选项。
以这种方式生成方法存根时,该方法始终标记为内部方法,其原因与Microsoft代码生成器采用的“最佳实践”方法有关。特别是,对于将要从调用站点调用的方法,它提供了最低限度的访问。可以从程序集调用内部方法,但是不能从程序集的外部访问内部方法。这就满足了“最小特权”的安全最佳实践。
如果尝试生成的未定义代码是一个类型,就可以选择Generate Class或Generate New Type。如果选择Generate New Type,就会打开Generate Type对话框,如图7-13所示。这个对话框提供了配置新类型的更多选项,包括是要生成类、枚举、接口还是结构,新类型是公共、私有还是内部的,以及新类型应该放在什么地方。

图7-13
7.1.4 参数信息
创建函数调用时,IntelliSense会显示参数信息。问题是参数信息仅在修改函数调用时才会显示。因此,在创建或修改函数调用时,可以看到这个有用的工具提示,但在阅读代码时就看不到了。程序员有时为了查看函数调用的参数信息而有意地修改函数调用,这可能在无意中给代码引入错误。
Visual Studio 2015避免了这个问题,它提供了一个很容易访问的命令,不修改代码就可以显示这个信息。按下Ctrl+K、Ctrl+I快捷键就会显示函数调用的信息,如图7-14所示。也可以通过Edit | IntelliSense | Parameter Info菜单命令访问这个信息。

图7-14
在图7-14中,PrintGreeting方法带两个参数。第二个参数是可选的,方括号中的赋值语句显示了其默认值,表示如果没有给它提供参数值,就使用其默认值。VB程序员很熟悉这个语法,但它是C# 4.0新增的功能。
7.1.5 快速信息
同样,有时希望不修改代码就可以查看某个对象或接口的信息。按下Ctrl+K、Ctrl+I快捷键或将鼠标悬停在对象名称上就会显示简要的工具提示,解释对象及其声明方式(如图7-15所示)。

图7-15
也可以通过Edit | IntelliSense | Quick Info菜单命令显示这个工具提示。