VBA.CBlah 和 CBlah 之间的区别

Difference between VBA.CBlah and CBlah

奇怪的观察:

通常当我想将地址保存到变量中的函数时,我会这样做:

Function getAddress(ByVal func As LongPtr) As LongPtr
    getAddress = func
End Function

Sub printAddress()
    Dim functionPointer As LongPtr
    functionPointer = getAddress(AddressOf myFunc)
    Debug.Print functionPointer
End Sub

但是我刚刚发现我可以使用 1-liner

functionPointer = VBA.CLngPtr(AddressOf myFunc)

functionPointer = CLngPtr(AddressOf myFunc)

... 不起作用并加注

Compile error:

Expected: expression

这是怎么回事?据我所知,唯一的区别是 CLngPtr 是在全局变量中声明的(class?)而 VBA.CLngPtr 是明确限定的,但我不知道为什么会导致观察到的行为(它们都指向同一个函数,不是吗?)

如果您使用默认的 IDE 设置,关键字和标识符的设置并没有真正不同,这将不是很明显。这是使用不同颜色时的外观:

您可以看到 CLngPtr 像圣诞树一样亮起,看起来与其他关键字一样。将其与 Abs 进行比较,它也是一个函数,但保持浅蓝色,就好像它只是一个标识符一样。

这暗示 CLngPtr 已被 VBA 编译器优化,因此它实际上是内联方法 1,这就是为什么如果您尝试使用 CLngPtr 作为表达式。但是,VBA.CLngPtr 是一个适当的函数,因此可以用作表达式的一部分,但由于未优化路由,性能损失非常小。

你会看到与 say、CLng 或任何转换函数,甚至 Mid 语句(不是函数)相同的事情。 VBA 中有几个函数可能会被编译器内联,通常会因它们是否转换为关键字而有所不同。另请注意,括号的颜色不同。

哎呀,甚至 Debug.Print 也得到了特殊待遇,如果您熟悉它,您可能知道它不完全是 class 也不是模块,但您不能 Print 没有 Debug.


  1. 当我们在这里提到 "inlinling" 时,我们是在谈论 VBA 编译器在较低级别的工作,低于我们在源代码级别看到的内容。从源码来看,C***()VBA.C***()基本是一回事。但是,VBA 编译器可以并且将尝试通过在内部重新安排用于转换的机器指令(或内联函数正在执行的任何操作)来优化这些位。重新排列指令的效果是它可能不再在所有上下文中兼容。在这种情况下,我可以想象(但不知道事实!)CLngPtr() 的内联指令 returns 一个值,而不是一个引用,它与参数声明不兼容,这是为什么当我们尝试将它用作参数时会出现语法错误。请注意,AddressOf 不会发生这种情况——LHS 上的任何其他函数都会有相同的语法错误,因此它与 AddressOf 以及内联方法的所有内容无关。

不是答案,而是更奇怪的思考...

注释中指出编译错误,这有效 (ref):

Sub TestCasting()
    Dim value As Variant
    Debug.Print "value cast with     CBool  : " & CBool(value)
    Debug.Print "value cast with VBA.CBool  : " & VBA.CBool(value)

    Debug.Print "value cast with     CByte  : " & CByte(value)
    Debug.Print "value cast with VBA.CByte  : " & VBA.CByte(value)

    Debug.Print "value cast with     CCur   : " & CCur(value)
    Debug.Print "value cast with VBA.CCur   : " & VBA.CCur(value)

    Debug.Print "value cast with     CDate  : " & CDate(value)
    Debug.Print "value cast with VBA.CDate  : " & VBA.CDate(value)

    Debug.Print "value cast with     CDbl   : " & CDbl(value)
    Debug.Print "value cast with VBA.CDbl   : " & VBA.CDbl(value)

    Debug.Print "value cast with     CDec   : " & CDec(value)
    Debug.Print "value cast with VBA.CDec   : " & VBA.CDec(value)

    Debug.Print "value cast with     CInt   : " & CInt(value)
    Debug.Print "value cast with VBA.CInt   : " & VBA.CInt(value)

    Debug.Print "value cast with     CLng   : " & CLng(value)
    Debug.Print "value cast with VBA.CLng   : " & VBA.CLng(value)

    '--- Compile Error: Sub or Function not found
    '    (error displayed at run time)
    'Debug.Print "value cast with     CLngLng: " & CLngLng(value)
    'Debug.Print "value cast with VBA.CLngLng: " & VBA.CLngLng(value)

    Debug.Print "value cast with     CLngPtr: " & CLngPtr(value)
    Debug.Print "value cast with VBA.CLngPtr: " & VBA.CLngPtr(value)

    Debug.Print "value cast with     CSng   : " & CSng(value)
    Debug.Print "value cast with VBA.CSng   : " & VBA.CSng(value)

    Debug.Print "value cast with     CStr   : " & CStr(value)
    Debug.Print "value cast with VBA.CStr   : " & VBA.CStr(value)

    Debug.Print "value cast with     CVar   : " & CVar(value)
    Debug.Print "value cast with VBA.CVar   : " & VBA.CVar(value)

End Sub

并添加到上面的代码示例中:

Function myFunc() As String
    myFunc = "help!"
End Function

Function getAddress(ByVal func As LongPtr) As LongPtr
    getAddress = func
End Function

Sub printAddress()
    Dim functionPointer As LongPtr
    functionPointer = getAddress(AddressOf myFunc)
    Debug.Print functionPointer
    functionPointer = VBA.CLngPtr(AddressOf myFunc)

    '--- Compile Error: Syntax Error
    '    (error displayed in red in VBA Editor)
    'debug.Print CLngPtr(AddressOf myFunc)
    'functionPointer = CLngPtr(AddressOf myFunc)
    Debug.Print functionPointer
End Sub