在VBA中,如果我理解正确的话,空意味着变量尚未初始化,即它是赋值之前变量的默认值。
似乎有四种方法可以测试变体是否为空:
IsEmpty(var) = True
VarType(var) = vbEmpty
TypeName(var) = "Empty"
var = Empty
Run Code Online (Sandbox Code Playgroud)
我想知道的是这些方法是否完全等效,或者是否存在细微(或明显)的差异。
看起来它们应该是等价的,但我惊讶地发现微软关于 IsEmpty、vbEmpty
( 1 , 2 ) 和onTypeName
的文档 没有相互引用,我认为如果它们是等价的,它们就会互相引用。
我发现两个参考文献似乎暗示前三个在 VBscript 中是相同的(其中一切都是变体):CodeWiki、Herong。
似乎有些情况是 Excel 特有的。看来Excel中的空也指的是不包含任何内容的单元格,我认为这相当于表示该单元格未启动的变量。但“决策模型”网站表示,空性还指单元格值是否是最新的(“如果计算出的参数引用了未计算的单元格,则该参数为空”)。但是该页面在一个地方说要测试 using vbEmpty
,而在其他地方说要使用IsEmpty
。
IsEmpty
我发现两个 StackOverflow 问题讨论了和Empty
( 1 , 2 )的关系,但没有讨论其他两种方法。
应用于数组时似乎也可能存在细微的差异。
我在 GitHub 上找到了以下代码片段,这意味着 ifVarType(Obj) = vbEmpty
的值IsEmpty(Obj)
仍然可能为 true 或 false:
Select Case VarType(Obj)
Case vbNull
json_toString = "null"
Case vbEmpty
'dkottow check if the cell is empty to evtl. convert to null
If IsEmpty(Obj) Then
json_toString = "null"
Else
json_toString = """"""
End If
Run Code Online (Sandbox Code Playgroud)
所以,相当混乱。
总而言之,我的问题是,在VBA中,以下内容是否等效,或者它们的含义有什么区别?
IsEmpty(var) = True
VarType(var) = vbEmpty
TypeName(var) = "Empty"
var = Empty
Run Code Online (Sandbox Code Playgroud)
Cri*_*use 11
以下所有内容均适用于 VBA,无论宿主应用程序(Excel、Word、AutoCAD 等)以及 VB6 和之前的 VB 版本如何。碰巧 Excel 可以很好地处理变体,但无论如何,下面的情况都成立。
变体
在幕后,Variant是一个结构(标记为 union),可用于表示 VB 中的任何其他数据类型和一些特殊值。
布局是:
VarType
在Variant 上运行时,结果是前 2 个字节,尽管它们返回为Long
4 个字节,但由于 VBA 的内存布局是小端字节序,因此 Long 中的前 2 个字节与 Integer 中的 2 个字节完美重叠。
我们可以使用CopyMemory
API来演示上面的内容:
Option Explicit
#If Mac Then
#If VBA7 Then
Public Declare PtrSafe Function CopyMemory Lib "/usr/lib/libc.dylib" Alias "memmove" (Destination As Any, Source As Any, ByVal Length As LongPtr) As LongPtr
#Else
Public Declare Function CopyMemory Lib "/usr/lib/libc.dylib" Alias "memmove" (Destination As Any, Source As Any, ByVal Length As Long) As Long
#End If
#Else 'Windows
'https://msdn.microsoft.com/en-us/library/mt723419(v=vs.85).aspx
#If VBA7 Then
Public Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As LongPtr)
#Else
Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
#End If
#End If
Sub TestVariantVT()
Dim v As Variant
Dim vt As Integer
CopyMemory vt, v, 2
Debug.Assert vt = VarType(v) 'vbEmpty
v = CInt(2)
CopyMemory vt, v, 2
Debug.Assert vt = VarType(v) 'vbInteger
v = CLng(2)
CopyMemory vt, v, 2
Debug.Assert vt = VarType(v) 'vbLong
v = CDbl(2)
CopyMemory vt, v, 2
Debug.Assert vt = VarType(v) 'vbDouble
End Sub
Run Code Online (Sandbox Code Playgroud)
VARTYPE保存数据类型,但也可以设置 VT_BYREF 标志,这意味着 Variant 通过引用(作为参数)传递给当前方法,这有助于 VB 知道哪些内存需要释放,哪些不需要。退出范围。VarType
不返回 VT_BYREF 标志,但这超出了问题范围。还有一个单独的 VT_ARRAY 标志(如 中vbArray
),可以与其他标志结合使用来描述数组的内容,例如整数数组将设置vbArray
(VT_ARRAY) 和vbInteger
(VT_I2) 标志(如 vbArray + vbInteger)。
与问题无关但与上述相关,VT_BYREF 标志可用于操作内存,如我的VBA-MemoryTools存储库中所示。
是空的
读完上面的内容后,这很容易理解。该IsEmpty
函数只是检查 Variant 的 VARTYPE(前 2 个字节)是否为vbEmpty
(即 0)。
所以是的,这两个条件VarType(var) = vbEmpty
和IsEmpty(var) = True
总是等效的。
我需要引起注意的是,大多数人不使用该IsEmpty(var) = True
语法,因为IsEmpty
已经返回了布尔值。我,至少永远不会写类似的东西If IsEmpty(var) = True Then
,但总是会写If IsEmpty(var) Then
。后者更干净。
变量类型
一些笔记。您可能想知道当我们将非 Variant 传递给VarType函数时会发生什么。好吧,VarName
参数的类型是 Variant,因此如果您传递非 Variant,它实际上会被包装在 Variant 中。检查 VBE7.dll 可以发现: VbVarType _stdcall VarType([in] VARIANT* VarName);
请注意上面链接上的注释:
如果传递的对象具有默认属性,则 VarType(object) 返回该对象默认属性的类型。
这意味着要检查对象,您需要使用IsObject
它来检查 VARTYPE 字节是否设置为vbObject
。在这种特殊情况(对象)中,两者VarType(var) = vbObject
并不IsObject(var)
总是等价的。
然而,上述注释并不影响VarType(var) = vbEmpty
和 的等价性IsEmpty(var)
,因为后者也会检查对象的默认成员。
空的
在 VB* 中,Empty
只是一个关键字,但相当于前 2 个字节设置为 的 Variant vbEmpty
。它的存在是为了方便,以同样的方式Null
(前 2 个字节设置为 的变体vbNull
)。
因此,比较一个变体Empty
就像比较 2 个变体一样。比较 2 个变体时,有一些适用的特殊规则。这里说明:
如果 expression1 和 expression2 都是 Variant 表达式,则它们的基础类型决定了它们的比较方式。下表显示了如何比较表达式或比较结果,具体取决于 Variant 的基础类型。
如果 然后 两个 Variant 表达式都是数字 进行数值比较。 两个 Variant 表达式都是字符串 执行字符串比较。 一个 Variant 表达式是数字,另一个是字符串 数值表达式小于字符串表达式。 一个 Variant 表达式为空,另一个为数字 使用 0 作为 Empty 表达式执行数值比较。 一个 Variant 表达式为空,另一个为字符串 使用零长度字符串 ("") 作为 Empty 表达式执行字符串比较。 两个 Variant 表达式都是空的 表达式是相等的。
所以,var = Empty
不等于VarType(var) = vbEmpty
/ IsEmpty(var)
。简单示例: ifvar
是空字符串 ("") 或空字符串 (vbNullString) thenvar = Empty
返回True
whileVarType(var) = vbEmpty
且IsEmpty(var)
两者都返回False
。
类型名称
TypeName非常不同,因为它返回一个String
.
当与对象一起使用时,它非常有用。例如,如果var
是一个集合,则VarType(var)
返回vbObject
,而TypeName(var)
返回Collection
。因此,TypeName
提供了更多信息。与数组相同:TypeName(Array())
返回,Variant()
但根据数组类型,它可以返回Integer()
或Double()
等等。
这就是为什么您会看到Range
参数是包装在 Variant 中的 Excel.Range。实际的 VARTYPEvbObject
更进一步TypeName
检查对象的类型。
我认为在您的 Excel 示例中,您实际上对该房产感兴趣Range.Value
。如果var
是一个范围,那么TypeName(var.Value) = "Empty"
恰好等于IsEmpty(var.Value)
但只是因为该.Value
属性永远不会返回对象,但如果它返回,那么它们将不再等效。但是,TypeName(var)
永远不会与IsEmpty(var)
if var
is an object 等效。
请注意,TypeName
不会查看对象的默认成员。
结论
VarType(var) = vbEmpty
总是等价于IsEmpty(var)
.var = Empty
遵循比较两个变体的规则,因此与上面的 2 个不同。TypeName(var) = "Empty"
仅当不是对象时才与VarType(var) = vbEmpty
/等效。IsEmpty(var)
var
不见了
只是为了澄清,因为您已经在自己的答案中显示了它,如果变体的类型vbError
(前 2 个字节 VT_ERROR)和 SCODE 成员(字节 9 到 12)设置为 DISP_E_PARAMNOTFOUND (0x80020004),那么 VB* 将其视为特殊Missing
价值。
以下代码返回特殊的 Missing 值:
Public Function Missing() As Variant
Missing = &H80020004 'Sets bytes 9 to 12
CopyMemory Missing, vbError, 2 'Sets first 2 bytes
End Function
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
1970 次 |
最近记录: |