|
楼主 |
发表于 2009-12-7 15:05
|
显示全部楼层
下面是一些关于控件和……其他不好分类的东东
快速删除集合——从头开始删除集合项目
        删除集合中的所有内容有许多方法,其中有些非常得迅速.来看看一个包含10,000个项目的集合:
下面这段是创建一个集合- Dim col As New Collection, i As Long
- For i = 1 To 10000
- col.Add i, CStr(i)
- Next
复制代码 可以从末尾位置为起点删除集合内容,如下:- For i = col.Count To 1 Step -1
- col.Remove i
- Next
复制代码 也可以从开始位置为起点删除集合内容,如下:- For i = 1 To col.Count Step 1
- col.Remove 1 '这里原文是错的,原文是 col.remove i
- Next
复制代码 测试证明,后者要快于前者百倍多(我也试过,太震撼了),比如0.06秒比4.1秒.原因在于:当引用接近末尾位置的集合项目时,VB必须要从第1个项目开始遍历整个的项目链.更有趣的是,如果集合项目的数量加倍,那么从末尾开始删除与从头开始删除,前者要比后者花费的时间将成倍增长,比如前者是24秒,后者可能为0.12秒这么短!
        最后提醒您:删除集合的所有内容的最快方法就是"毁灭"它,使用下面的语句:对于一个包含20,000个项目的集合,上述操作仅仅耗时0.05秒,这比使用最快的循环操作进行删除也要快2倍左右.
========================================================
GoSub在编译程序中速度变慢这个对VBA似乎没有借鉴意义,但为了完整性还是贴上来
        编译为本地代码的VB应用程序中,如果使用 Go Subs 命令,就会比通常的 Subs 或者 Function 调用慢5-6倍;相反,如果是p-code模式,就会相当快.
        ——注:我从来没有在VB/VBA中用过go sub (不记得到底是什么了难道是:goto sub?),以前在Qbasic中似乎用过
==========================下面这个关于Doevents=======================
减少DoEvents语句的数量
        不要在代码中放置不必要的DoEvents语句,尤其是在时间要求高的循环中.遵循这个原则,至少能在循环中的每N次反复时才执行DoEvents语句,从而增强效率.比如使用下面的语句:- If (loopNdx Mod 10) = 0 Then DoEvents
复制代码 如果只是使用DoEvents来屏蔽鼠标以及键盘操作,那么就可以在事件队列中存在待处理项目时调用它.通过API函数GetInputState来检查这个条件的发生:- Declare Function GetInputState Lib "user32" Alias "GetInputState" () As Long
- ' ...上一句是API声明
- '下面这句可以写在一个循环中
- If GetInputState() Then DoEvents
复制代码 ======================================================================
读写文件 如果数据量大的时候,读取文件内容的简洁方法
        读取text文件的最快方法是使用Input$函数,就象下面的过程:- Function FileText (filename$) As String
- Dim handle As Integer
- handle = FreeFile
- Open filename$ For Input As #handle
- FileText = Input$(LOF(handle), handle)
- Close #handle
- End Function
复制代码 使用上述方法要比使用Input命令读取文件每一行的方法快很多.下面是应用这个函数读取Autoexec.bat的内容到多行textbox控件的例子:- Text1.Text = FileText("c:\autoexec.bat")
复制代码 但请注意:当文件包含Ctrl-Z(EOF)字符时,上面的函数代码可能会发生错误.因此,要修改一下代码:- Function FileText(ByVal filename As String) As String
- Dim handle As Integer
- ' 判断文件存在性
- If Len(Dir$(filename)) = 0 Then
- Err.Raise 53 '文件没有找到
- End If
- ' 以binary模式打开文件
- handle = FreeFile
- Open filename$ For Binary As #handle
- ' 读取内容,关闭文件
- FileText = Space$(LOF(handle))
- Get #handle, , FileText
- Close #handle
- End Function
复制代码 ====================================================================
字体对象克隆招法
        当要应用一个控件的字体到另一控件时,最直接的方法就是直接赋值:- Set Text2.Font = Text1.Font
复制代码 但多数情况下这种方法并不奏效,因为这实际上是将同一字体的引用分配给了2个控件.换言之,当随后修改其中之一控件的字体时,另外一个控件也受到影响.因此,要实现我们的目的,需要做的就是克隆字体对象并赋值给需要的控件. 
        最简单的克隆字体的方法是手工地拷贝所有单独的字体属性,就象下面一样:- Function CloneFont(Font As StdFont) As StdFont
- Set CloneFont = New StdFont
- CloneFont.Name = Font.Name
- CloneFont.Size = Font.Size
- CloneFont.Bold = Font.Bold
- CloneFont.Italic = Font.Italic
- CloneFont.Underline = Font.Underline
- CloneFont.Strikethrough = Font.Strikethrough
- End Function
复制代码 '函数的应用- Set Text2.Font = CloneFont(Text1.Font)
复制代码 如果使用VB6,就可以使用PropertyBag对象快速拷贝所有字体属性,并且代码会很简练、速度也快2倍:- Function CloneFont(Font As StdFont) As StdFont
- Dim pb As New PropertyBag
- '拷贝字体到PropertyBag对象中
- pb.WriteProperty "Font", Font
- '恢复字体对象到新控件
- Set CloneFont = pb.ReadProperty("Font")
- End Function
复制代码 但是我们还能进一步地对代码进行优化,方法就是使用可被所有StdFont对象识别的隐藏IFont接口.这个接口具有一个Clone方法,用它就可以精确地实现我们的目的.它以非正常方式执行:创建一个克隆Font对象,然后返回相应的引用.这可能是实现克隆目的的最简洁代码了,而且,执行速度也是这里列举的3种方法中最快的一个,要比使用PropertyBag对象的方法快大约3倍左右.来看看具体代码:- Function CloneFont(Font As IFont) As StdFont
- Font.Clone CloneFont
- End Function
复制代码 ====================================================================
你真正理解"Allow Unrounded Floating Point Operations"选项的含义吗?
        注:这似乎是VB编译器选项 允许不舍入的浮点操作 反正我是不理解的。我们用VBA的看看就算了吧。’
        来自微软的资料鼓吹:高级优化对话框中的所有编译选项都被认为是不稳定的,它们可能导致不正确的结果,甚至程序崩溃.对于其中的大多数,这种说法是正确的,但是经常有一个叫做"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也许对此生效,也许就不生效,这要取决于表达式的复杂程度以及最初分配数值语句与随后产生结果的表达式语句的距离远近. 
=====================================================================================
除法运算符"\"与"/"的区别
        ——注:本人历来以为 \ 是整除运算符   结果只取整数部分,难道不对?
        整数间执行除法运算时(不就是整除么?),要使用 "\" 而不是 "/". "/" 运算符要求返回一个单一数值,所以,表面上看似简单的一行代码:实际上包含了3个隐含的转换操作:2个为除法运算做准备,从Integer转换到Single;一个完成最后的赋值操作,从Integer转换到Single.但是如果使用了"\"操作符,情况就大不相同了!不仅不会有这么多中间步骤,而且执行速度大大提高. 
        同时请记住:使用"/"操作符做除法运算时,如果其中之一是Double类型,那么结果就将是Double类型.所以,当2个Integer或者Single类型数值做除法运算时,如果想得到高精度的结果,就需要手工强迫其中之一转换为Double类型: 
'结果为 0.3333333
Print 1 / 3
'结果为 0,333333333333333
Print 1 / 3#
============================================================
Friend过程快于Public过程
        ——注:本人很少用到,主要是没有达到随手写类的能力,每次写的时候还是先到VB6中用类创建工具搭好框架再写代码的
        你可能会非常惊奇:Friend类型过程的执行速度要明显快于Public类型.这可以通过创建一个带有Private类和Public类 (设定Instancing = MultiUse)的ActiveX EXE工程看到,在2个类模块中添加下面的代码:- Public Sub PublicSub(ByVal value As Long)
- '
- End Sub
- Public Function PublicFunction(ByVal value As Long) As Long
- '
- End Function
- Friend Sub FriendSub(ByVal value As Long)
- '
- End Sub
- Friend Function FriendFunction(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变量的能力.
=============================================================
使用Objptr函数快速查找集合中的对象,ObjPtr妙用
        ObjPtr函数的一个最简单但是却最有效的用途就是提供快速寻找集合中对象的关键字.假设有一个对象集合,它没有可以当做关键字以从集合中取回的属性.那么,我们就可以使用ObjPtr函数的返回值作为集合中的关键字:- Dim col As New Collection
- Dim obj As CPerson '这篇文章的原作者非常可能是C出身的:)
- '创建新的CPerson对象,并添加到集合中
- Set obj = New CPerson
- obj.Name = "John Smith"
- col.Add obj, CStr(ObjPtr(obj)) '关键字必须是字符串
复制代码 因为任何对象都有一个明确的ObjPtr数值,而且它是不变的,所以,我们可以容易地、快速地从集合中取回它:- ' 删除集合中的对象
- col.Remove CStr(ObjPtr(obj))
复制代码 这个技巧可以适用于任何类型的对象,包括VB中的表单和控件,以及外部对象. 
使用ObjPtr检测2个对象变量是否指向同一对象
        判断2个对象变量释放指向同一对象的方法是使用Is操作符,代码如下:但当2个对象是同一类型时,或者指向同一个二级接口时,我们就可以利用ObjPtr()函数对代码进行一些优化处理:- If ObjPtr(obj1) = ObjPtr(obj2) Then ...
复制代码 后者的执行速度将比前种方法快40%多.但是请注意,2种方法原本就是很有效率的,只有在时间要求非常严格的上百成千次的循环中,才会体现出这种差别. 
==========================================================================
创建新表时,快速拷贝字段 这是VB用的,我们VBA看看算了吧
        在VB6中,无需离开开发环境就可以创建新的SQL Server和Oracle表.方法很简单:打开DataView窗口,用鼠标右键单击数据库的表文件夹,再选择新表格菜单命令. 
        当处理相似表格时,就是说具有许多相同字段的表格,我们完全可以在很短的时间内容完成设定操作.具体步骤是:在设计模式下打开源表格,加亮选择要拷贝字段对应的行,按Ctrl-C拷贝信息到粘贴板;然后,在设计模式打开目标表格,将光标置于要粘贴字段所在的位置,按Ctrl-V. 
        这样,就拷贝了所有的字段名称以及它们所带的属性.
==================================================================
快速找到选中的OptionButton
        ——注:下面的代码是VB中用的,VBA要借鉴还需要加工一下
        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
复制代码 =======================================================================
表单及控件的引用阻止了表单的卸载
        ——注:这里的表单其实就是VBA中的UserForm,VB中的Form。
        当指派表单或者表单上的控件到该表单模块以外的一个对象变量中时,如果要卸载表单,就必须首先将那个变量设置为 to Nothing.也就是说,如果不设置为Nothing,即使看不到这个对象了,但它仍旧是保存在内存中的. 
        注意:这并非是一个bug,这仅仅是COM引用规则的一个结果.唯一要注意的就是引用的这个控件将阻止整个表单的卸载操作,它将依赖于它的父表单而存在. 
=======================================================
快速调入TreeView控件以及ListView控件的子项内容
        有一个简单但仍未发现的技巧可用于在TreeView控件中装载多个节点,或者在ListView控件中装载多个ListItems.这种方法要比传统做法快.先看看下面这个传统方法:- For i = 1 To 5000
- TreeView1.Nodes.Add , , , "Node " & i
- Next
复制代码 改进一下,代替重复引用TreeView1对象的Nodes集合,我们可以先将之保存在临时对象变量中:- Dim nods As MSComctlLib.Nodes
- Set nods = TreeView1.Nodes
- For i = 1 To 5000
- nods.Add , , , "Node " & i
- Next
复制代码 甚至,如果使用With代码块,还可以不需要临时变量:- With TreeView1.Nodes
- For i = 1 To 5000
- .Add , , , "Node " & i
- Next
- End With
复制代码 经测试,优化的循环代码要比传统方法执行速度快40%左右.原因在于:将Nodes集合对象保存在临时变量中,或者应用With代码块后VB将使用隐藏的临时变量后,就可以避免在循环中重复绑定Nodes对象到它的父TreeView1对象上.由于这种绑定是低效率的,因此省却它就能节省大量的执行时间. 
        同样的道理对于其他ActiveX控件也生效: 
ListView控件的ListItems、ListSubItems以及ColumnHeaders集合
Toolbar控件的Buttons和ButtonMenus集合
ImageList的ListImages集合
StatusBar控件的Panels集合
TabStrip控件的Tabs集合
Let's End,下楼贴一些总结代码(原来网站网友跟帖总结的代码)
[ 本帖最后由 Moneky 于 2009-12-8 12:04 编辑 ] |
|