会员登录 - 用户注册 - 网站地图 Office中国(office-cn.net),专业Office论坛
当前位置:主页 > 技巧 > Access技巧 > 编程心得绝招 > 实际编程 > 正文

代码与速度优化

时间:2003-12-20 23:41 来源:Access911 作者:Access91… 阅读:
编程人员从程序开发中积累了许多非常实用的经验与技巧

用Mid$命令超速字符串添加操作
从头开始删除集合项目
用InStr函数实现代码减肥
精用Boolean表达式,让代码再减肥
函数名巧做局部变量
火眼识破隐藏的Variant变量
GoSub在编译程序中速度变慢
减少DoEvents语句的数量
And、Or和Xor:让我们来优化表达式
静态变量慢于动态变量
善用"Assume No Aliasing"编译选项
为常量定义合适的类型
你真正理解"Allow Unrounded Floating Point Operations"选项的含义吗?
除法运算符"\"与"/"的区别
使用"$-类型"字符串函数会更快
妙用Replace函数替代字符串连接操作符&
固定长度字符串数组:赋值快,释放快
创建任意长度重复字符串的简洁方法
未公开的返回数组型函数加速秘诀
深入使用LIKE操作符
另辟蹊径处理字符串中的字符:字节数组法
快速清除数组部分内容
快速初始化Variant和String类型数组
访问简单变量总是快于数组元素值
创建新表时,快速拷贝字段
无闪烁地快速附加字符串到textbox控件
快速找到选中的OptionButton
表单及控件的引用阻止了表单的卸载
重定义编译DLL文件的基地址
快速调入TreeView控件以及ListView控件的子项内容
Friend过程快于Public过程
使用Objptr函数快速查找集合中的对象
使用ObjPtr检测2个对象变量是否指向同一对象
读取文件内容的简洁方法
字体对象克隆招法
精用Boolean表达式,让代码再减肥 

当设置基于表达式结果的Boolean型数值时,要避免使用多余的If/Then/Else语句结果。比如: 
If SomeVar > SomeOtherVar Then 
 BoolVal = True 
Else 
 BoolVal = False 
End If 

上面这段代码就很烦琐,它们可以使用下面的一行代码来替代: 
BoolVal = (SomeVar > SomeOtherVar) 

括号不是必须的,但可以增加可读性。根据表达式中的操作数不同,后者比前者执行起来大约快50%到85%。后者中的括号对速度没有影响。 

有时,使用这个技术实现代码的简练并非很明显。关键是要牢记:所有的比较操作结果或者是0(false),或者是-1(True)。所以,下面例子中的2段代码是完全相同的,但是第2段要运行得快些: 

1、传统方法: 
If SomeVar > SomeOtherVar Then 
 x = x + 1 
End If 

2、更简练的方法 
x = x - (SomeVar > SomeOtherVar)

减少DoEvents语句的数量 

不要在代码中放置不必要的DoEvents语句,尤其是在时间要求高的循环中。遵循这个原则,至少能在循环中的每N次反复时才执行DoEvents语句,从而增强效率。比如使用下面的语句: 
If (loopNdx Mod 10) = 0 Then DoEvents 

如果只是使用DoEvents来屏蔽鼠标以及键盘操作,那么就可以在事件队列中存在待处理项目时调用它。通过API函数GetInputState来检查这个条件的发生: 
Declare Function GetInputState Lib "user32" Alias "GetInputState" () As Long 

If GetInputState() Then DoEvents 

为常量定义合适的类型 

VB在内部使用最简单、最可能的数据类型保存符号数值,这意味着最通常的数字类型-比如0或者1-都按照Integer类型存储。如果在浮点表达式中使用这些常量,可以通过常量的合适类型来加速程序运行,就象下面的代码: 
value# = value# + 1#

这个语句强迫编译器按照Double格式存储常量,这样就省却了运行时的隐含转换工作。还有另外的一种处理方法就是:在常量声明时就进行相应类型的定义,代码如下: 
Const ONE As Double = 1

你真正理解"Allow Unrounded Floating Point Operations"选项的含义吗? 

来自微软的资料鼓吹:高级优化对话框中的所有编译选项都被认为是不稳定的,它们可能导致不正确的结果,甚至程序崩溃。对于其中的大多数,这种说法是正确的,但是经常有一个叫做"Allow Unrounded Floating Point Operations"的选项能够给予正确的结果,防止应用程序产生bug。考虑下面的代码段: 

Dim x As Double, y As Double, I As Integer 

x = 10 ^ 18 

y = x + 1 ’ this can’t be expressed with 64 bits 

MsgBox (y = x) ’ 显示 "True" (不正确的结果) 

严格地说,由于X和Y变量不包含相同的数值,MsgBox将显示False。可问题是,由于数值1E18与1E18+1都以相同的64位浮点Double类型来表示,它们最终包含了几乎相同的数值,最后的MsgBox结果将是True。 

如果打开了"Allow Unrounded Floating Point Operations"编译选项,VB就能重用已在数学协处理器堆栈中的数值,而不是内存中的数值(比如:变量)。因为FPU堆栈具备80位的精度,因此就可以区分出这2个数值的不同: 

’ if the program is compiled using the 

’ "Allow Unrounded Floating Point Operations" compiler option 

MsgBox (y = x) ’ 显示 "False" (正确的结果) 

总结一下:当以解释模式、或者编译的p-code模式、或者编译的native代码模式但关掉"Allow Unrounded Floating Point Operations"选项这3种方式运行一个程序时,所有浮点数字运算在内部都以80位的精度进行处理。但如果有一个数值是存储在64位Double变量中,结果就是接近的了,并且,随后使用那个变量的表达式也将产生近似的结果,而不是绝对正确的结果。 

相反,如果打开"Allow Unrounded Floating Point Operations"编译选项后运行一段native编译代码,在随后的表达式中VB就经常能重用内部的80位数值,而忽略存储在变量中的当前数值。注意:我们并不能完全控制这个功能,VB也许对此生效,也许就不生效,这要取决于表达式的复杂程度以及最初分配数值语句与随后产生结果的表达式语句的距离远近。

[NextPage]

未公开的返回数组型函数加速秘诀 

在VB6中,函数是能够返回数组对象的。这种情况下,我们不能象返回对象或者数值的其他函数一样使用函数名当做局部变量来存储中间结果,因此不得不生成一个临时局部数组,函数退出前再分配这个数组给函数名,就象下面的代码一样

'返回一个数组,其中含有N个随即元素 
'并且将平均值保存在AVG中 
Function GetRandomArray(ByVal n As Long, avg As Single) As Single() 
Dim I As Long, sum As Single 
ReDim res(1 To n) As Single 

'以随机数填充数组,计算总和 
Randomize Timer 
For I = 1 To n 
 res(I) = Rnd 
 sum = sum + res(I) 
Next 
'赋值结果数组,计算平均值 
GetRandomArray = res 
avg = sum / n 
End Function 

难以置信的是,只需要简单地颠倒最后2条语句的顺序,就能使上面这段程序变得快些: 
'赋值结果数组,计算平均值 
avg = sum / n 
GetRandomArray = res 
End Function 

例如,在一个Pentium II 333MHz 机器上,当N=100,000时,前段程序运行时间为0.72秒,后段程序则为0.66秒,前后相差10% 

原因何在呢?前段程序中,VB将拷贝res数组到GetRandomArray对应的结果中,当数组很大时,花费的时间是很长的。后段程序中,由于GetRandomArray = res是过程的最后一条语句,VB编译器就能确认res数组不会被再使用,因此将直接交换res和GetRandomArray的地址数值,从而节省了数组元素的物理拷贝操作以及随后的res数组释放操作。 

总结:当编写返回数组的函数时,一定要将分配临时数组到函数名的语句放在最后,就是其后紧挨者Exit Function 或者End Function的位置

快速清除数组部分内容 

清除动态数组的最快方法是使用ReDim,清除静态数组则是使用删除。但是如果只想清除数组的一部分内容,怎么办呢?看上去似乎只能使用For-Next循环了

如果处理的是数字数组,有一个较快的方法。它基于ZeroMemory函数,正如函数名所示,它能将一块内存区域填充为0。看看怎么应用这个函数来清除一个Long类型数组的一部分内容
Private Declare Sub ZeroMemory Lib "kernel32" Alias "RtlZeroMemory" (dest As Any, ByVal Bytes
 As Long) 

'定义数组,填充数据

Dim a(1000) As Long 

For I = 1 To Ubound(a) 
  a(I) = I 
Next 

'从a(200)开始清除100个元素内容
ZeroMemory a(200), 100 * Len(a(1)) 

请注意上述代码中清除长度的表示方法,使用Len()保证了当数组类型改变时代码仍然有效

创建新表时,快速拷贝字段 

在VB6中,无需离开开发环境就可以创建新的SQL Server和Oracle表。方法很简单:打开DataView窗口,用鼠标右键单击数据库的表文件夹,再选择新表格菜单命令。 

当处理相似表格时,就是说具有许多相同字段的表格,我们完全可以在很短的时间内容完成设定操作。具体步骤是:在设计模式下打开源表格,加亮选择要拷贝字段对应的行,按Ctrl-C拷贝信息到粘贴板;然后,在设计模式打开目标表格,将光标置于要粘贴字段所在的位置,按Ctrl-V。 

这样,就拷贝了所有的字段名称以及它们所带的属性。 

无闪烁地快速附加字符串到textbox控件 

附加文本到TextBox或者RichTextBox控件的通常方法是在当前内容上连接上新的字符串
Text1.Text = Text1.Text & newString 

但还有一个更快的方法,并且会减少连接操作的闪烁感,如下
Text1.SelStart = Len(Text1.Text) 
Text1.SelText = newString 

快速找到选中的OptionButton 
OptionButton控件经常是作为控件数组存在的,要快速找到其中的哪一个被选中,可以使用下面的代码 
’假设控件数组包含3个OptionButton控件 
intSelected = Option(0).value * 0 - Option(1).value * 1 - Option(2).value * 2 

注意,因为第一个操作数总是0,所以上述代码可以精简
intSelected = -Option(1).value - Option(2).value * 2 

Friend过程快于Public过程 

你可能会非常惊奇:Friend类型过程的执行速度要明显快于Public类型。这可以通过创建一个带有Private类和Public类 (设定Instancing = MultiUse)的ActiveX EXE工程看到,在2个类模块中添加下面的代码: 

Public Sub PublicSub(ByVal value As Long) 
End Sub 
Public Function PublicFun(ByVal value As Long) As Long 
End Function 

Friend Sub FriendSub(ByVal value As Long) 
End Sub 
Friend Function FriendFun(ByVal value As Long) As Long 
End Function 

然后,在表单模块中创建一个循环,执行每个例程许多次。比如,要在一个Pentium II机器上查看执行时间上的区别,可以调用每个例程1,000,000次。下面是测试的结果: 

Private类模块中,反复调用1,000,000次Public Sub或者Function耗费了0.46秒,而调用内容相同的Friend类型模块则分别只有0.05秒和0.06秒。前后竟然相差了8-9倍之多!对于MultiUse类型的Public类模块,也是一样的结果。 

对于这个不可思议的结果的可能解释是:Friend型过程没有处理汇集和拆装代码的消耗(Public过程可以从当前工程外被调用,因此COM必须要来回地汇集数据)。 

但是在多数情况下,这些时间差别是不明显的,特别是程序中包含一些复杂和耗时的语句时。 

即使这样,Friend型过程仍有其他的优势高于Public类型,比如:接受和返回在BAS模块中定义的UDT变量的能力。

(责任编辑:admin)

顶一下
(0)
0%
踩一下
(0)
0%
发表评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价: