VBA:为什么 Not 运算符会停止工作?
VBA: Why would the Not operator stop working?
这让我百思不得其解。
Sub testChangeBoolean()
Dim X As Boolean ' default value is False
X = True ' X is now True
X = Not X ' X is back to False
End Sub
但我试图在 table 单元格(在 Word 中)中切换 .FitText 属性。 .FitText 以 True 开头。
如果我将它分配给 X:
Sub testChangeBoolean()
Dim X As Boolean ' again, default is False
X = Selection.Cells(1).FitText ' does set X to True
X = Not X ' X is still True!
End Sub
我只是不明白我做错了什么。
我认为解释与较旧的编程语言(WordBasic 和早期 VBA)如何存储 True 和 False 的整数值有关。在那些日子里,True = -1 和 False = 0.
较新的编程语言仍然使用 0 表示 False,但使用 1 表示 True。
大多数 Word 的布尔类型属性继续使用 -1 表示 True(例如,Font.Bold),这让使用较新语言的 Interop 的程序员感到困惑和沮丧。因此,在某些时候,Microsoft 的一些开发人员决定使用新方法并将整数值 1 分配给 True 以实现某些新功能。比如FitText
.
考虑以下代码示例,其中 X
属于 Boolean
类型,y
属于 Integer
类型:
- 如果
FitText
为True,整数值为1
- 如果反转值,使用
Not
表明布尔值仍然是 "True" 因为它的整数值不是 0,它是 -2
- 将整数值直接设置为 True 得到 -1
这确实令人困惑,但确实解释了为什么 Not
没有给出预期的结果。
Sub testChangeBoolean()
Dim X As Boolean ' again, default is False
Dim Y As Integer
X = Selection.Cells(1).FitText ' does set X to True
Y = Selection.Cells(1).FitText
Debug.Print X, Y ' result: True 1
X = Not X ' X is still True!
Y = Not Y
Debug.Print X, Y ' result: True -2
X = False
Y = True
Debug.Print X, Y ' result: False -1
End Sub
为了补充 Cindy 的出色回答,我想指出虽然 VBA 在分配给 Boolean
数据类型时通常有强制值的保护措施,但这可以被规避。基本上,如果您将一个随机值写入不属于您的内存地址,那么您应该预料到未定义的行为。
为了帮助证明这一点,我们将(滥用)使用 LSet
,它实际上允许我们复制值而无需实际分配。
Private Type t1
b As Boolean
End Type
Private Type t2
i As Integer
End Type
Private Sub Demo()
Dim i1 As t2
Dim b1 As t1
Dim b As Boolean
i1.i = 1
LSet b1 = i1
b = b1.b
Debug.Print b, b1.b, i1.i
Debug.Print CInt(b), CInt(b1.b), i1.i
End Sub
注意行 b = b1.b
基本上等同于我们在 OP 代码中所做的
X = Selection.Cells(1).FitText
即把一个Boolean
赋值给另一个Boolean
。但是,因为我使用 LSet
写入 b1.b
,绕过了 VBA 运行时检查,它不会被强制。在读取 Boolean
时,VBA 确实隐式地将其强制转换为 True
或 False
,这似乎具有误导性但是正确的,因为任何虚假结果都等于 0
(又名 False
),任何真实的结果都是不真实的。请注意,真实的否定意味着 1
和 -1
都是真实的。
如果我直接将 1
分配给 Boolean
变量,VBA 就会将其强制转换为 -1
/True
,因此'没问题。但是显然对于 FitText
或 LSet
,我们基本上是以一种不受控制的方式写入内存地址,因此 VBA 开始对这个特定变量表现得很奇怪,因为它期望 Boolean
变量已经强制了它的内容,但没有。
这是因为来自此 属性 的内部 Long 值,正如 Cindy Meister 所解释的那样。我们应该始终使用 CInt 来避免这种情况。
Sub testChangeBoolean2()
Dim X As Boolean ' again, default is False
X = CInt(Selection.Cells(1).FitText) ' [Fixed] does set X to True
X = Not X ' X is False!
End Sub
这让我百思不得其解。
Sub testChangeBoolean()
Dim X As Boolean ' default value is False
X = True ' X is now True
X = Not X ' X is back to False
End Sub
但我试图在 table 单元格(在 Word 中)中切换 .FitText 属性。 .FitText 以 True 开头。
如果我将它分配给 X:
Sub testChangeBoolean()
Dim X As Boolean ' again, default is False
X = Selection.Cells(1).FitText ' does set X to True
X = Not X ' X is still True!
End Sub
我只是不明白我做错了什么。
我认为解释与较旧的编程语言(WordBasic 和早期 VBA)如何存储 True 和 False 的整数值有关。在那些日子里,True = -1 和 False = 0.
较新的编程语言仍然使用 0 表示 False,但使用 1 表示 True。
大多数 Word 的布尔类型属性继续使用 -1 表示 True(例如,Font.Bold),这让使用较新语言的 Interop 的程序员感到困惑和沮丧。因此,在某些时候,Microsoft 的一些开发人员决定使用新方法并将整数值 1 分配给 True 以实现某些新功能。比如FitText
.
考虑以下代码示例,其中 X
属于 Boolean
类型,y
属于 Integer
类型:
- 如果
FitText
为True,整数值为1 - 如果反转值,使用
Not
表明布尔值仍然是 "True" 因为它的整数值不是 0,它是 -2 - 将整数值直接设置为 True 得到 -1
这确实令人困惑,但确实解释了为什么 Not
没有给出预期的结果。
Sub testChangeBoolean()
Dim X As Boolean ' again, default is False
Dim Y As Integer
X = Selection.Cells(1).FitText ' does set X to True
Y = Selection.Cells(1).FitText
Debug.Print X, Y ' result: True 1
X = Not X ' X is still True!
Y = Not Y
Debug.Print X, Y ' result: True -2
X = False
Y = True
Debug.Print X, Y ' result: False -1
End Sub
为了补充 Cindy 的出色回答,我想指出虽然 VBA 在分配给 Boolean
数据类型时通常有强制值的保护措施,但这可以被规避。基本上,如果您将一个随机值写入不属于您的内存地址,那么您应该预料到未定义的行为。
为了帮助证明这一点,我们将(滥用)使用 LSet
,它实际上允许我们复制值而无需实际分配。
Private Type t1
b As Boolean
End Type
Private Type t2
i As Integer
End Type
Private Sub Demo()
Dim i1 As t2
Dim b1 As t1
Dim b As Boolean
i1.i = 1
LSet b1 = i1
b = b1.b
Debug.Print b, b1.b, i1.i
Debug.Print CInt(b), CInt(b1.b), i1.i
End Sub
注意行 b = b1.b
基本上等同于我们在 OP 代码中所做的
X = Selection.Cells(1).FitText
即把一个Boolean
赋值给另一个Boolean
。但是,因为我使用 LSet
写入 b1.b
,绕过了 VBA 运行时检查,它不会被强制。在读取 Boolean
时,VBA 确实隐式地将其强制转换为 True
或 False
,这似乎具有误导性但是正确的,因为任何虚假结果都等于 0
(又名 False
),任何真实的结果都是不真实的。请注意,真实的否定意味着 1
和 -1
都是真实的。
如果我直接将 1
分配给 Boolean
变量,VBA 就会将其强制转换为 -1
/True
,因此'没问题。但是显然对于 FitText
或 LSet
,我们基本上是以一种不受控制的方式写入内存地址,因此 VBA 开始对这个特定变量表现得很奇怪,因为它期望 Boolean
变量已经强制了它的内容,但没有。
这是因为来自此 属性 的内部 Long 值,正如 Cindy Meister 所解释的那样。我们应该始终使用 CInt 来避免这种情况。
Sub testChangeBoolean2()
Dim X As Boolean ' again, default is False
X = CInt(Selection.Cells(1).FitText) ' [Fixed] does set X to True
X = Not X ' X is False!
End Sub