
8.3 重构操作
下面将介绍每个重构操作,并通过例子说明如何使用C#和VB提供的内置支持。
8.3.1 Extract Method重构操作
重构一个长方法的最简单方式是把它分解成一些小方法。要进行Extract Method重构操作,应在原方法中选择要提取的代码区域,然后从快速操作选项中选择Extract Method,或者从Refactor上下文菜单中选择Extract Method命令。
命名新方法的机制利用了与代码片段相同的接口。如果熟悉Visual Studio 2013中的Extract Method对话框,就会发现它有点变化。选择Extract Method时,该方法会立即从它的当前位置移除,并填充到新方法中。方法的名称设置为NewMethod,对新方法的调用替换了从原始调用站点提取的代码块。方法的名字会突出显示,如果改变它,调用站点的方法名称也会改变。如果要提取的代码块中的变量在原方法的前面用过,它们会自动在方法签名中显示为变量。
例如,在下面的代码片段中,如果希望将条件逻辑提取到一个单独的方法中,可以选中这些代码(显示为粗体),然后右击并从上下文菜单中选择Refactor | Extract Method命令。
C#
private void button1_Click(object sender, EventArgs e) { string connectionString = Properties.Settings.Default.ConnectionString; if(connectionString == null) { connectionString = "DefaultConnectionString"; } MessageBox.Show(connectionString); /* ...Much longer method ...*/ }
此重构操作的结果如图8-9所示。

图8-9
现在,Extract Method重构就完成了,接着就进入Rename重构操作。这里,Rename与刚才提取的方法名称相关。将光标放在这个新方法的名称上。当改变名字时,这个变化会立即反映到两个地方。注意在图8-9的右上角。这个区域控制Rename重构。它包含的两个选项在在本章后面的“重命名”一节中描述。但更重要的是,它包括一个Apply按钮,必须单击该按钮,确认把方法名从NewMethod重命名为输入的名称。
8.3.2 Encapsulate Field重构操作
另一个常用的重构操作是用属性封装现有的类变量。Encapsulate Field重构操作就用于实现该功能。要执行该操作,应选中要封装的变量,然后从上下文菜单中选择Quick Actions。这会显示重构选项列表,如图8-10所示。

图8-10
选择要使用的封装类型。如usages reference field文本所示,区别是对公共字段的现有引用是继续使用私有字段,还是使用公共属性。从选定的变量名称中生成所创建属性的名称。
8.3.3 Extract Interface重构操作
当项目从原型或者早期开发阶段进展到完整的实现或者成长阶段时,通常需要把类的核心方法提取到接口中,以启用其他的实现方案,或者在不连接的系统间定义边界。过去,只能将整个方法复制到一个新的文件中,然后删除方法内容,只剩下接口存根。Extract Interface重构操作可以根据类中的任意多个方法来提取接口。对类进行重构操作时会显示如图8-11所示的对话框,

图8-11
选择Extract Interface选项,显示如图8-12所示的对话框。可以在这里选择要包含在接口中的方法。选中后,会在新接口中添加这些方法。在原来的类中也会添加新接口。

图8-12
8.3.4 Change Signature重构操作
有时有必要在方法签名中完全重新排列参数或删除参数。这通常是为了美观,但也可以改进代码的可读性,保证接口的实现。也许底层的功能不再需要参数。
修改方法签名时,使用重构函数会大大减少必要的搜索量,避免可能出现的编译错误。当方法有多个重载版本时,这个函数也特别有用,改变签名可能不产生编译错误;这种情况下,可能由于语义错误(而不是语法错误)而发生运行时错误。
要访问Change Signature功能,应选择要修改的方法,再从上下文菜单中选择Quick Actions,显示如图8-13所示的界面。

图8-13
选择Change Signature选项,显示Change Signature对话框,如图8 -14所示。通过此对话框,可以根据需要,在列表中上下移动参数,让它们按照希望的顺序出现,也可以完全删除它们。完成时,单击OK,完成重构。

图8-14
8.3.5 Inline和Explaining Variables重构操作
在Visual Studio 2015中添加了两个重构操作,它们提供了一个常见场景的两个方面。该场景围绕着方法中临时、局部变量的用户。
Inline Temporary Variables重构操作的目的最好通过代码来展示。考虑下面的方法:
C#
public void MethodXYZ(string name) { var data = InternalMethod(2.0); var result = AnotherInternalMethod(name, data); }
内联临时变量data会得到AnotherInternalMethod的参数行,其中包含对InternalMethod的调用,而不是使用data变量。选择变量后,在右击的上下文菜单中通过Quick Actions即可访问重构操作。界面如图8-15所示。图中的预览部分给出了明确的内联表示。

图8-15
Introduce解释变量重构操作则走向另一个方向。这种情况下,应选择要在方法签名中使用的表达式。上下文菜单中的Quick Actions选项(见图8-16)允许创建一个解释变量,放回到方法签名中。

图8-16
如果选定的表达式是一个常量,且能够创建局部变量(作为常量),也可以重构到一个类级别的常量。
8.3.6 Rename重构操作
Rename重构操作用于其他一些重构操作,也可以作为一个独立的方法。要触发独立版本,可以选择一个变量,然后从右击的上下文菜单中选择Rename,显示如图8-17所示的界面。

图8-17
现在输入变量的新名称,单击Apply按钮,完成重构。图8-17右上角的选项用于控制要搜索的区域,以重命名它。如果选择Include Comments选项,而且变量的名字出现在字符串中,它就改为新的变量名。对于Include Comments选项,如果变量名在字符串中,就更新它。如果想预览更改,可以在单击Apply之前选中Preview Changes复选框。
8.3.7 Generate Method Stub重构操作
编写代码时,可能需要调用一个尚未编写的方法。例如,下面的代码片段引用了一个需要以后实现的新方法。
VB
Private Sub MethodA() Dim InputA As String Dim InputB As Double Dim OutputC As Integer = NewMethodIJustThoughtOf(InputA, InputB) End Sub
C#
public void MethodA() { string InputA; double InputB; int OutputC = NewMethodIJustThoughtOf(InputA, InputB); }
当然,前面的代码会生成错误,因为没有定义该方法。通过Generate Method Stub重构操作(可通过上下文菜单中的Quick Action来访问),可以生成一个方法存根。该存根拥有完整的输入参数和输出类型,如下所示:
VB
Private Function NewMethodIJustThoughtOf(ByVal InputA As String, ByVal InputB As Double)As Integer Throw New NotImplementedException End Function
C#
private int NewMethodIJustThoughtOf(string InputA, double InputB) { throw new NotImplementedException(); }
8.3.8 Organize Usings重构操作
最好在每个文件(C#)中维护一组有序的Using语句,并且只在该文件中引用真正需要的名称空间。Organize Usings菜单(在代码编辑器中右击,再从弹出的上下文菜单中选择,如图8-18所示)有助于实现这两个操作。

图8-18
对代码执行主要的重构操作后,代码文件的顶部会出现许多不再使用的using指令。使用Visual Studio中的一个操作就可以删除这些using指令,而无须通过一个反复试错的过程来确定需要删除哪些using指令。在代码编辑器中右击,再从弹出的上下文菜单中选择Organize Usings | Remove Unused Usings(在VB中,选项是Unnecessary Imports而不是Unnecessary Usings),就可以删除代码文件不再使用的using指令、using别名和外部程序集别名。
最好按字母顺序组织using指令,以便管理引用的名称空间。为避免手动执行这个操作,可以在代码编辑器中右击,再从弹出的上下文菜单中选择Organize Usings | Sort Usings(在VB中,选项是Sort Imports而不是Sort Usings),让Visual Studio自动完成该操作。先显示System名称空间的using指令,再按字母顺序显示其他名称空间的using指令。如果为名称空间定义了别名,这些别名就会移到列表底部,如果使用了外部程序集别名(在C#中使用extern关键字),它们就会移到列表的顶部。
要在一个操作中为using指令排序,并删除不再使用的using指令,可以在代码编辑器中右击,再从弹出的上下文菜单中选择Organize Usings | Remove and Sort命令(VB使用Imports而不是Usings)。
默认的Visual Studio模板代码文件把using语句放在文件顶部名称空间块的外部。但如果查看静态代码分析指南,就会发现using语句应包含在名称空间块的内部。Organize Usings操作会根据文件中using语句的当前位置处理这些情况,并保留该位置。