|
楼主 |
发表于 2009-7-25 21:03
|
显示全部楼层
在执行循环时考虑如何能够尽可能地节省资源
(1)分析循环以查看是否正在不必要地执行一些消耗内存的重复操作。例如,是否可以在循环外(而不是在循环中)设置某些变量?每次都通过循环执行的转换过程是否可以在循环之外执行?
(2)考虑是否必须在满足特定的条件时才执行循环。如果是,也许可以更早地退出循环。例如,假设正在对一个不应该包含数字字符的字符串进行数据验证。如果循环要检查字符串中的每个字符以确定其中是否包含数字字符,那么您可以在找到第一个数字字符时立即退出循环。
(3)如果必须在循环中引用数组的元素,可以创建一个临时变量存储该元素的值,而不是引用数组中的值。从数组中检索值比从相同类型的变量读取值要慢。
(4) 将属性和方法放在循环外部
在代码运行时,获取变量的值快于获取属性的值。因此,如果您的代码在循环内部获取属性的值,您可以在循环外部将该属性的值先指定给一个变量,然后在循环内部使用此变量代替属性的值,这样的代码将运行得更快。
下面所示的代码运行较慢,因为在每次重复循环时都必须获取Sheet的Range属性的值。
Sub TryThisSlow()
Dim Start As Double, Finish As Double
Start = Timer
'--------------------------------------
Dim MyLoop As Long
For MyLoop = 2 To 4001
Cells(MyLoop, 2) = Sheet1.Range("B1")
Next
'--------------------------------------
Finish = Timer
MsgBox "本次运行的时间是" & Finish - Start
End Sub
下面的示例与上面所产生的结果相同,但比上面的要更快,因为在循环开始以前我们已经将Sheet的Range属性的值指定给了单独的变量MyVar。这样,代码将在每次重复循环时利用该变量的值,而不必每次都要调有属性。
Sub TryThisFaster()
'快约35%以上
Dim Start As Double, Finish As Double
Start = Timer
'--------------------------------------
Dim MyVar As String, MyLoop As Long
MyVar = Sheet1.Range("B1")
For MyLoop = 2 To 4001
Cells(MyLoop, 2) = MyVar
Next
'--------------------------------------
Finish = Timer
MsgBox "本次运行的时间是" & Finish - Start
End Sub
如果您在一个循环内部使用多个对象访问,您也可以使用With…End With将您能够移动的对象移到循环外部。下面的示例在每次循环重复时都调用Sheets对象和Cells属性。
Sub NowTryThisSlow()
Dim Start As Double, Finish As Double
Start = Timer
'--------------------------------------
Dim c As Long
For c = 1 To 8000
Sheet1.Cells(c, 5) = c
Next
'--------------------------------------
Finish = Timer
MsgBox "本次运行的时间是" & Finish - Start
End Sub
对上面的代码改写如下,使用With语句将调用Sheets对象移到循环外部,只剩余调用Cells。
Sub NowTryThisFaster()
'约快3倍
Dim Start As Double, Finish As Double
Start = Timer
'--------------------------------------
Dim c As Long
With Sheet1
For c = 1 To 8000
.Cells(c, 5) = c
Next
End With
'--------------------------------------
Finish = Timer
MsgBox "本次运行时间为" & Finish - Start
End Sub
注:您也能通过使用对象变量在循环外部调用该对象。
使用With…End With语句
可以使用With…End With语句来尽量减少对象引用。使用With语句对指定的对象完成一系列的任务,而不用重复引用对象。也可以使用嵌套的With语句进一步提高程序代码的效率。例如,下面的使用With…End With语句是在同一个单元格中执行多个操作。
With Workbooks("Book1.xls").Worksheets("Sheet1").Range("A1")
.Formula="=SQRT(20)"
With .Font
.Name="Arial"
.Bold=True
.Size=10
End With
End With
同理,可使用With…End With语句在同一个单元格区域中执行多个操作。
尽量减少OLE引用
调用每个VBA方法或属性都需要一个或多个OLE引用,这样在代码中会有多个点运算符,而每次代码调用都需要对这些点运算符进行解析,这将花费更多的时间。因此,在调用方法或属性时减少引用长度将是使您的程序运行更快的一种好方法。
可以通过尽量减少在VBA程序代码中使用OLE(对象链接与嵌入自动识别)引用来优化程序代码。VBA语句中所调用的方法和属性越多,执行语句所用的时间就越多。例如下面的两个语句:
语句1:
Workbooks(1).Sheets(1).Range("A1").value="10"
语句2:
ActiveWindow.Left=200
执行时,语句2比语句1快。
同样,上面所讲的对重复使用的对象引用指定一个变量,通过调用变量从而保证避免多次进行对象引用。
编写高效Excel VBA代码的最佳实践(二)
尽可能少使用“.”,使用对象变量
在前面已经介绍过的对长对象引用使用对象变量以及使用With…End With等都是简化”.”的方法。因为在代码中的每个句点都表示至少一个(而且可能是多个)过程调用,而这些过程调用必须在后台执行。真正好的做法是在局部进行缓存对象引用,例如,应该把对象模型中较高层次的对象引用保存到局部对象变量中,然后用这些对象引用创建其他较低层次的对象引用。例如,引用某单元格数据时,可用如下代码:
Dim i As Long
For i=1 to 10
Workbooks("Book1.xls").Worksheets("Sheet1").Cells(1,i).Value=i
Next i
但下面的代码运行效率更高,因为代码中引用Workbook对象和Worksheet对象的调用命令只执行一次,而上面的代码中却要执行10次。
Dim ws As Worksheet
Dim i As Long
Set ws= Workbooks("Book1.xls").Worksheets("Sheet1")
For i=1 to 10
ws.Cells(1,i).Value=i
Next i
当您一遍又一遍的使用相同对象引用时,您可以将该对象引用设置成一个变量,然后使用该变量代替对象引用。这样,您在代码中只需对该对象变量进行引用即可。
例如,下面的示例在每行中调用Workbook对象的Sheets属性、Range属性和Value属性三次,当您循环1000次时,总共要调用属性6000次。
Sub DoThis1()
Dim Start As Double, Finish As Double
Start = Timer
'--------------------------------------
Dim N As Long
For N = 1 To 1000
Workbooks("Book1").Sheets(1).Range("c5").Value = 10
Workbooks("Book1").Sheets(1).Range("d10").Value = 12
Next
'--------------------------------------
Finish = Timer
MsgBox "本次运行的时间是" & Finish - Start
End Sub
您能在循环开始前通过设置Workbooks(“Book1”).Sheets(1)作为一个对象变量来优化上面的例子,下面的示例在每行仅调用一个Range属性,当循环1000次时,总共只调用该属性2000次。
注意,“Value”是一个缺省属性,通常不需要明确指定它,它将被自动调用。因此,该属性在下面的代码中被忽略。然而,就养成良好的编程习惯而言,还是建议您最好写明该属性。
Sub DoThis2()
'快约35%以上
Dim Start As Double, Finish As Double
Start = Timer
'--------------------------------------
Dim ThisBookSheet As Object, N As Long
Set ThisBookSheet = Workbooks("Book1").Sheets(1)
For N = 1 To 1000
ThisBookSheet.Range("c5") = 10
ThisBookSheet.Range("d10") = 12
Next
'--------------------------------------
Finish = Timer
MsgBox "本次运行的时间是" & Finish - Start
End Sub
您可以比较这两个示例的运行速度,它们都得到同样的结果,但在我的机子上运行时,第二个示例比第一个快60%。当然,您还能使用With…End With语句获得相同的结果。
您也能不设置明确的对象变量,而是使用With语句减少对象的重复引用。上面的示例也能使用下面的代码,该代码仅调用Workbooks属性和Sheets属性一次,当循环1000次时,总共调用1000次属性。
Sub DoThis3()
'快约35%以上
Dim Start As Double, Finish As Double
Start = Timer
'--------------------------------------
Dim N As Long
With Workbooks("Book1").Sheets(1)
For N = 1 To 1000
.Range("c5") = 10
.Range("d10") = 12
Next
End With
'--------------------------------------
Finish = Timer
MsgBox "本次运行的时间是" & Finish - Start
End Sub
上述三个示例均得到相同的结果,但在我的机子上运行时,本示例比第一个示例快50%以上。
在一个语句中进行复制或者粘贴
在用宏录制代码时,首先是选择一个区域,然后再执行ActiveSheet.Paste。在使用Copy方法时,可以在一个语句中指定复制的内容及要复制到的目的地。
例如,将B5:C6区域的内容复制到以单元格B8开始的区域中,使用宏录制器的代码为:
Range("B5:C6").Select
Selection.Copy
Range("B8").Select
ActiveSheet.Paste
经修改后的最佳代码是:
Range("B5:C6").Copy Destination:=Range("B8")
合理地使用消息框和窗体
在一个很长的程序中,尝试着将消息框或者窗体安排显示在程序的最开始或最后面,避免干扰用户。此外,尽管窗体提供了许多功能,但它们能够导致文件大小迅速增加。还有就是尽量避免给工作表单元格链接用户窗体控件,因为这样将会导致链接更新操作,影响程序运行速度。
尽可能加速对数字的运算
(1)当对整数进行除法时,您可以使用整型除法运算符(\)而不是浮点除法运算符(/),因为无论参与除法运算的数值类型如何,浮点除法运算符总会返回Double类型的值。
(2)在任何具有整数值的算术表达式中使用Single或Double值时,整数均将被转换成Single或Double值,最后的结果将是Single或Double值。如果要对作为算术运算结果的数字执行多次操作,可能需要明确地将该数字转换为较小的数据类型。
提高字符串操作的性能
(1)尽可能少使用连接操作。可以在等号左边使用Mid函数替换字符串中的字符,而不是将它们连接在一起。使用 Mid 函数的缺点是替换字符串必须与要替换的子字符串的长度相同。例如,
Dim strText As String
strText = "this is a test"
Mid(strText, 11, 4) = "tent"
(2)VBA提供许多可用来替换函数调用的内部字符串常量。例如,可以使用vbCrLf常量来表示字符串中的回车/换行组合,而不是使用Chr(13) & Chr(10)。
(3)字符串比较操作的执行速度很慢。有时,可以通过将字符串中的字符转换为 ANSI 值来避免这些操作。例如,下列代码会检查字符串中的第一个字符是否为空格:
If Asc(strText) = 32 Then
上面的代码会比以下代码更快:
If Left(strText, 1) = " " Then
使用Asc()检验ANSI的值
在VBA中,可以使用Chr$()函数把数转换成字符,并确定ANSI的值,但是更好的是使用Asc()函数把字符串转换成数值,然后确定它的ANSI值。如果需要进行有限次数的这种检验,对程序代码的效率可能不会产生很大影响,但是,如果需要在多个循环内进行这种检验时,这将节省处理时间并且有助于程序代码更快地执行。
使用Len()检验空串
尽管有多种方法可检验空串,但首选的是使用Len()函数。为了测试零长度的串,可以选择把串与””相比较,或者比较串的长度是否为0,但这些方法比用Len()函数要用更多的执行时间。当对字符串应用Len()函数并且函数返回0值时,说明该字符串是空的或者是零长度的字符串。
并且,因为在If语句内非零值被认为是True,所以直接使用Len()函数而不必与””或0比较,减少了处理时间,因此执行更快。
有效地使用数组
用VBA数组而不是单元格区域来处理数据,即可以先将数据写入到某个数组,然后用一个语句就可以将数组中的数据传递到单元格区域中。(前文已述)
在创建已知元素的确定数组时,使用Array函数对于节约空间和时间以及写出更具效率的代码是非常理想的。例如,
Dim Names As Variant
Names=Array("Fan","Yang","Wu","Shen")
此外,应该尽量使用固定大小的数组。如果确实选择使用了动态数组,应该避免数组每增加一个元素就改变一次数组的大小,最好是每次增加一定数量的元素。 |
|