VBA 语言的哪些特性要么记录不充分,要么根本不经常使用?
16 回答
This trick only works in Access VBA, Excel and others won't allow it. But you can make a Standard Module hidden from the object browser by prefixing the Module name with an underscore. The module will then only be visible if you change the object browser to show hidden objects.
This trick works with Enums in all vb6 based version of VBA. You can create a hidden member of an Enum by encasing it's name in brackets, then prefixing it with an underscore. Example:
Public Enum MyEnum
meDefault = 0
meThing1 = 1
meThing2 = 2
meThing3 = 3
[_Min] = meDefault
[_Max] = meThing3
End Enum
Public Function IsValidOption(ByVal myOption As MyEnum) As Boolean
If myOption >= MyEnum.[_Min] Then IsValidOption myOption <= MyEnum.[_Max]
End Function
In Excel-VBA you can reference cells by enclosing them in brackets, the brackets also function as an evaluate command allowing you to evaluate formula syntax:
Public Sub Example()
[A1] = "Foo"
MsgBox [VLOOKUP(A1,A1,1,0)]
End Sub
Also you can pass around raw data without using MemCopy (RtlMoveMemory) by combining LSet with User Defined Types of the same size:
Public Sub Example()
Dim b() As Byte
b = LongToByteArray(8675309)
MsgBox b(1)
End Sub
Private Function LongToByteArray(ByVal value As Long) As Byte()
Dim tl As TypedLong
Dim bl As ByteLong
tl.value = value
LSet bl = tl
LongToByteArray = bl.value
End Function
Octal & Hex Literals are actually unsigned types, these will both output -32768:
Public Sub Example()
Debug.Print &H8000
Debug.Print &O100000
End Sub
As mentioned, passing a variable inside parenthesis causes it to be passed ByVal:
Sub PredictTheOutput()
Dim i&, j&, k&
i = 10: j = i: k = i
MySub (i)
MySub j
MySub k + 20
MsgBox Join(Array(i, j, k), vbNewLine), vbQuestion, "Did You Get It Right?"
End Sub
Public Sub MySub(ByRef foo As Long)
foo = 5
End Sub
You can assign a string directly into a byte array and vice-versa:
Public Sub Example()
Dim myString As String
Dim myBytArr() As Byte
myBytArr = "I am a string."
myString = myBytArr
MsgBox myString
End Sub
"Mid" is also an operator. Using it you overwrite specific portions of strings without VBA's notoriously slow string concatenation:
Public Sub Example1()
''// This takes about 47% of time Example2 does:
Dim myString As String
myString = "I liek pie."
Mid(myString, 5, 2) = "ke"
Mid(myString, 11, 1) = "!"
MsgBox myString
End Sub
Public Sub Example2()
Dim myString As String
myString = "I liek pie."
myString = "I li" & "ke" & " pie" & "!"
MsgBox myString
End Sub
Mid() 语句有一个重要但几乎总是被遗漏的特性。这就是 Mid() 出现在赋值的左侧,而不是 Mid() 函数出现在右侧或表达式中的位置。
规则是如果目标字符串不是字符串文字,并且这是对目标字符串的唯一引用,并且插入的段的长度与被替换的段的长度匹配,则该字符串将被视为对于操作是可变的。
这意味着什么?这意味着,如果您将大型报告或大量字符串列表构建为单个字符串值,那么利用它将使您的字符串处理速度更快。
这是一个从中受益的简单类。它为您的 VBA 提供了与 .Net 相同的 StringBuilder 功能。
' Class: StringBuilder
Option Explicit
Private Const initialLength As Long = 32
Private totalLength As Long ' Length of the buffer
Private curLength As Long ' Length of the string value within the buffer
Private buffer As String ' The buffer
Private Sub Class_Initialize()
' We set the buffer up to it's initial size and the string value ""
totalLength = initialLength
buffer = Space(totalLength)
curLength = 0
End Sub
Public Sub Append(Text As String)
Dim incLen As Long ' The length that the value will be increased by
Dim newLen As Long ' The length of the value after being appended
incLen = Len(Text)
newLen = curLength + incLen
' Will the new value fit in the remaining free space within the current buffer
If newLen <= totalLength Then
' Buffer has room so just insert the new value
Mid(buffer, curLength + 1, incLen) = Text
Else
' Buffer does not have enough room so
' first calculate the new buffer size by doubling until its big enough
' then build the new buffer
While totalLength < newLen
totalLength = totalLength + totalLength
Wend
buffer = Left(buffer, curLength) & Text & Space(totalLength - newLen)
End If
curLength = newLen
End Sub
Public Property Get Length() As Integer
Length = curLength
End Property
Public Property Get Text() As String
Text = Left(buffer, curLength)
End Property
Public Sub Clear()
totalLength = initialLength
buffer = Space(totalLength)
curLength = 0
End Sub
这是一个如何使用它的例子:
Dim i As Long
Dim sb As StringBuilder
Dim result As String
Set sb = New StringBuilder
For i = 1 to 100000
sb.Append CStr( i)
Next i
result = sb.Text
VBA 本身似乎是一个隐藏功能。我认识的多年来一直使用 Office 产品的人都不知道它甚至是套件的一部分。
我在这里发布了多个问题,但对象浏览器是我的秘密武器。如果我需要 ninja 快速编写代码,但不熟悉 dll,对象浏览器可以挽救我的生命。它比 MSDN 更容易学习类结构。
Locals 窗口也非常适合调试。在您的代码中暂停一下,它将显示当前命名空间中的所有变量、它们的名称以及它们的当前值和类型。
谁能忘记我们的好朋友即时窗口?它不仅对 Debug.Print 标准输出非常有用,而且您也可以在其中输入命令。需要知道什么是 VariableX?
?VariableX
需要知道那个细胞是什么颜色?
?Application.ActiveCell.Interior.Color
事实上,所有这些窗口都是使用 VBA 提高生产力的好工具。
这不是一个功能,而是我在 VBA(和 VB6)中多次看到错误的事情:在方法调用上添加了括号,它将改变语义:
Sub Foo()
Dim str As String
str = "Hello"
Bar (str)
Debug.Print str 'prints "Hello" because str is evaluated and a copy is passed
Bar str 'or Call Bar(str)
Debug.Print str 'prints "Hello World"
End Sub
Sub Bar(ByRef param As String)
param = param + " World"
End Sub
VBA 中记录最少的功能可能是那些只能通过在 VBA 对象浏览器上选择“显示隐藏成员”来公开的功能。隐藏成员是那些在 VBA 中但不受支持的函数。您可以使用它们,但微软可能会随时删除它们。他们都没有提供任何文档,但你可以在网上找到一些。这些隐藏功能中谈论最多的可能是提供对 VBA 中指针的访问。要获得体面的文章,请查看;不是那么轻量级-Shlwapi.dll
记录在案,但可能更模糊(无论如何在 excel 中)是使用 ExecuteExcel4Macro 访问属于整个 Excel 应用程序实例而不是特定工作簿的隐藏全局命名空间。
隐藏的功能
- 虽然它是“基本”,但您可以使用 OOP - 类和对象
- 您可以进行 API 调用
字典。没有它们,VBA 几乎一文不值!
参考 Microsoft Scripting Runtime,Scripting.Dictionary
用于任何足够复杂的任务,并从此过上幸福的生活。
Scripting Runtime 还为您提供了 FileSystemObject,这也是强烈推荐的。
从这里开始,然后挖掘一下......
http://msdn.microsoft.com/en-us/library/aa164509%28office.10%29.aspx
Implements
您可以使用关键字实现接口。
键入VBA.
将显示所有内置函数和常量的智能感知列表。
- Save 4 whole keystrokes by typing
debug.? xxx
instead ofdebug.print xxx
. - Crash it by adding:
enum foo: me=0: end enum
to the top of a module containing any other code.
通过一些工作,您可以像这样迭代自定义集合:
' Write some text in Word first.'
Sub test()
Dim c As New clsMyCollection
c.AddItems ActiveDocument.Characters(1), _
ActiveDocument.Characters(2), _
ActiveDocument.Characters(3), _
ActiveDocument.Characters(4)
Dim el As Range
For Each el In c
Debug.Print el.Text
Next
Set c = Nothing
End Sub
您的自定义集合代码(在一个名为 的类中clsMyCollection
):
Option Explicit
Dim m_myCollection As Collection
Public Property Get NewEnum() As IUnknown
' This property allows you to enumerate
' this collection with the For...Each syntax
' Put the following line in the exported module
' file (.cls)!'
'Attribute NewEnum.VB_UserMemId = -4
Set NewEnum = m_myCollection.[_NewEnum]
End Property
Public Sub AddItems(ParamArray items() As Variant)
Dim i As Variant
On Error Resume Next
For Each i In items
m_myCollection.Add i
Next
On Error GoTo 0
End Sub
Private Sub Class_Initialize()
Set m_myCollection = New Collection
End Sub
支持本地化版本,该版本(至少在上个世纪)支持使用本地化值的表达式。就像 Pravda for True 和 Fałszywy(不太确定,但至少它确实有有趣的 L)在波兰语中 False ......实际上英语版本将能够阅读任何语言的宏,并即时转换。但是,其他本地化版本无法处理。
失败。
VBE(Visual Basic 可扩展性)对象模型是一个鲜为人知和/或未充分利用的特性。它允许您编写 VBA 代码来操作 VBA 代码、模块和项目。我曾经写过一个 Excel 项目,它可以从一组模块文件中组装其他 Excel 项目。
对象模型也适用于 VBScript 和 HTA。我一次写了一个 HTA 来帮助我跟踪大量的 Word、Excel 和 Access 项目。许多项目会使用通用代码模块,模块很容易在一个系统中“增长”,然后需要迁移到其他系统。我的 HTA 将允许我导出项目中的所有模块,将它们与公共文件夹中的版本进行比较并合并更新的例程(使用 BeyondCompare),然后重新导入更新的模块。
VBE 对象模型在 Word、Excel 和 Access 之间的工作方式略有不同,遗憾的是根本不能与 Outlook 一起工作,但仍然提供了很好的代码管理功能。
IsDate("13.50")
返回True
但IsDate("12.25.2010")
返回False
这是因为IsDate
可以更准确地命名IsDateTime
。并且因为句点 ( .
) 被视为时间分隔符而不是日期分隔符。有关完整说明,请参见此处。
VBA 支持按位运算符来比较两个值的二进制数字(位)。例如,表达式 4 And 7 计算 4 (0100) 和 7 (0111) 的位值并返回 4(这两个数字中的位)。类似地,表达式 4 Or 8 计算 4 (0100) 中的位值) 和 8 (1000) 并返回 12 (1100),即其中任一为真的位。
不幸的是,位运算符在逻辑比较运算符中具有相同的名称:And、Eqv、Imp、Not、Or 和 Xor。这可能会导致模棱两可,甚至是矛盾的结果。
例如,打开即时窗口 (Ctrl+G) 并输入: ? (2 和 4) 这将返回零,因为 2 (0010) 和 4 (0100) 之间没有共同的位。
This feature exists presumably for backwards-compatibility. Or to write hopelessly obfuscated spaghetti code. Your pick.