VBA 中的范围界定是否被破坏?
Is scoping broken in VBA?
假设您在名为 Module1
:
的模块中有此代码
Option Explicit
Private Type TSomething
Foo As Integer
Bar As Integer
End Type
Public Something As TSomething
在等效的 C# 代码中,如果您将 Something
字段设置为 public
,代码将不再编译,因为 不一致的可访问性 - type 字段比字段本身更难访问。有道理。
但是在 VBA 中,您可以在 Module2
中使用此代码:
Sub DoSomething()
Module1.Something.Bar = 42
Debug.Print Module1.Something.Bar
End Sub
你在输入的时候会得到 IntelliSense,然后编译,运行,然后输出 42
。
为什么?从 COM 的角度来看,它是如何工作的?它是语言规范的一部分吗?
根据我的评论,VBA 公开了一个私有类型,就像它公开了一个私有枚举一样。
VBA 假定您可以在使用上下文中使用 TypeInfo,但它不允许您声明或创建这些类型或枚举的实例。
此 C++ answer 部分信息:
Access Control is applied to names
The access specifier for the name has nothing to do with it's type
但将标准模块中的私有类型视为类似“PublicNotCreatable”class 的东西可能很有用。如果您提供 public 包装器,则可以在主机模块外部访问该类型。
但是当类型在 Public Class 模块中时,VBA 处理事情的方式不同!
这是你的 Module1
扩展:
Option Explicit
Private Type TSomething
Foo As Integer
Bar As Integer
End Type
Public Type TOtherThing
Foo As Integer
Bar As Integer
End Type
Public Type TWrapperThing
Something As TSomething
End Type
Public Something As TSomething
Public Otherthing As TOtherThing
Public Wrapperthing As TWrapperThing
Public Function GetSomething() As TSomething
GetSomething.Foo = 1
End Function
Public Function GetOtherthing() As TOtherThing
GetOtherthing.Foo = 1
End Function
并且Module2
展开:
Option Explicit
Sub DoThings()
'Compile Error: User-defined type not defined
'Dim oSomething As TSomething
Dim vSomething As Variant
Dim oOtherthing As Module1.TOtherThing
Dim vOtherthing As Variant
Dim oWrapperthing As Module1.TWrapperThing
Module1.Something.Foo = 42
Module1.Otherthing.Foo = 42
Module1.Wrapperthing.Something.Foo = 42
'Compile Error: Only user-defined types defined in public object modules can be coerced to or from a variant or passed to late-bound functions
'vSomething = Module1.Something
'vOtherthing = Module1.Otherthing
oOtherthing = Module1.Otherthing
oOtherthing.Foo = 43
'Is 43 > 42?
Debug.Assert oOtherthing.Foo > Module1.Otherthing.Foo
'Compile Errors: "GetSomething" User-defined type not defined
'Module1.GetSomething.Foo = 42
'Module1.GetSomething().Foo = 42
Module1.GetOtherthing.Foo = 42
Module1.GetOtherthing().Foo = 42
End Sub
假设您在名为 Module1
:
Option Explicit
Private Type TSomething
Foo As Integer
Bar As Integer
End Type
Public Something As TSomething
在等效的 C# 代码中,如果您将 Something
字段设置为 public
,代码将不再编译,因为 不一致的可访问性 - type 字段比字段本身更难访问。有道理。
但是在 VBA 中,您可以在 Module2
中使用此代码:
Sub DoSomething()
Module1.Something.Bar = 42
Debug.Print Module1.Something.Bar
End Sub
你在输入的时候会得到 IntelliSense,然后编译,运行,然后输出 42
。
为什么?从 COM 的角度来看,它是如何工作的?它是语言规范的一部分吗?
根据我的评论,VBA 公开了一个私有类型,就像它公开了一个私有枚举一样。
VBA 假定您可以在使用上下文中使用 TypeInfo,但它不允许您声明或创建这些类型或枚举的实例。
此 C++ answer 部分信息:
Access Control is applied to names
The access specifier for the name has nothing to do with it's type
但将标准模块中的私有类型视为类似“PublicNotCreatable”class 的东西可能很有用。如果您提供 public 包装器,则可以在主机模块外部访问该类型。
但是当类型在 Public Class 模块中时,VBA 处理事情的方式不同!
这是你的 Module1
扩展:
Option Explicit
Private Type TSomething
Foo As Integer
Bar As Integer
End Type
Public Type TOtherThing
Foo As Integer
Bar As Integer
End Type
Public Type TWrapperThing
Something As TSomething
End Type
Public Something As TSomething
Public Otherthing As TOtherThing
Public Wrapperthing As TWrapperThing
Public Function GetSomething() As TSomething
GetSomething.Foo = 1
End Function
Public Function GetOtherthing() As TOtherThing
GetOtherthing.Foo = 1
End Function
并且Module2
展开:
Option Explicit
Sub DoThings()
'Compile Error: User-defined type not defined
'Dim oSomething As TSomething
Dim vSomething As Variant
Dim oOtherthing As Module1.TOtherThing
Dim vOtherthing As Variant
Dim oWrapperthing As Module1.TWrapperThing
Module1.Something.Foo = 42
Module1.Otherthing.Foo = 42
Module1.Wrapperthing.Something.Foo = 42
'Compile Error: Only user-defined types defined in public object modules can be coerced to or from a variant or passed to late-bound functions
'vSomething = Module1.Something
'vOtherthing = Module1.Otherthing
oOtherthing = Module1.Otherthing
oOtherthing.Foo = 43
'Is 43 > 42?
Debug.Assert oOtherthing.Foo > Module1.Otherthing.Foo
'Compile Errors: "GetSomething" User-defined type not defined
'Module1.GetSomething.Foo = 42
'Module1.GetSomething().Foo = 42
Module1.GetOtherthing.Foo = 42
Module1.GetOtherthing().Foo = 42
End Sub