VBA:为什么 Not 运算符会停止工作?

elu*_*oad 41 vba ms-word boolean-operations

这让我完全不知所措。

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
Run Code Online (Sandbox Code Playgroud)

但我试图在表格单元格(在 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
Run Code Online (Sandbox Code Playgroud)

我只是不明白我做错了什么。

Cin*_*ter 41

我相信这个解释与旧的编程语言(WordBasic 和早期的 VBA)如何存储 True 和 False 的整数值有关。在那些日子里,True = -1 和 False = 0。

较新的编程语言仍然使用 0 表示 False,但 1 表示 True。

Word 的大多数布尔类型属性继续使用 -1 表示 True(例如,Font.Bold),这让使用新语言的 Interop 的程序员感到困惑和沮丧。因此,在某些时候,Microsoft 的一些开发人员决定使用新方法并将整数值 1 分配给 True 以实现某些新功能。比如FitText

考虑以下代码示例,其中X是 typeBooleanytype Integer

  • 如果FitText为 True,则整数值为 1
  • 如果反转值,使用Not表明布尔值保持“真”,因为它的整数值不是 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
Run Code Online (Sandbox Code Playgroud)

  • 是的,这确实很棒。很好的解释。让我很高兴的是我不用 Word 编程。 (8认同)
  • “-2”是因为逻辑运算符执行按位运算 - 符号位很棘手,但基本上翻转“1”的所有位会导致符号位打开,并且“01”变成“10”,即“2” ` - 符号位为“-2”。 (7认同)
  • @BigBen 令人困惑的部分是整数值以某种方式仍然存在于“布尔”变量的引擎盖下的部分。这基本上意味着“Boolean”在 VBA 中不是类型安全的。 (6认同)
  • @MathieuGuindon 考虑到 Office 编程语言的年龄,这应该不足为奇。在过去,“布尔值”是一种关于关/开的“好方法”,它简单地存储为 0...以及其他值,按照惯例 -1。向后兼容性,即使对于 WordBasic 来说,也是一个优先事项,因此没有现代化也就不足为奇了。还要记住,Word 的***核心*** - 他们无法更改的内容,甚至无法修复错误 - 是 **C**。有些东西不从0开始是无法改变的,那成本就太高了。 (5认同)
  • @ZevSpitz 你不应该从“LBound()”开始吗? (3认同)
  • 啊,这一次我没有疯。很高兴知道。太感谢了! (2认同)

thi*_*his 9

为了补充 Cindy 的出色回答,我想指出,虽然 VBA 通常在分配给Boolean数据类型时具有强制执行值的保护措施,但这是可以规避的。基本上,如果您将一个随机值写入一个不属于您的内存地址,那么您应该预期会出现未定义的行为。

为了帮助证明这一点,我们将 (ab)useLSet本质上允许我们在不实际分配的情况下复制值。

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
Run Code Online (Sandbox Code Playgroud)

请注意,该行b = b1.b基本上等同于我们在 OP 代码中所做的

X = Selection.Cells(1).FitText
Run Code Online (Sandbox Code Playgroud)

也就是说,将 a 分配Boolean给 another Boolean。但是,因为我写到b1.busing LSet,绕过 VBA 运行时检查,它不会被强制。在阅读 时Boolean,VBA 确实隐式地将其强制为Trueor False,这似乎具有误导性,但它是正确的,因为任何虚假结果都是等于0(又名False)的结果,而任何真实结果都是不相等的结果。请注意,truthy 的否定表示1-1都是真的。

如果我直接将分配1给一个Boolean变量,VBA 就会将它强制转换为-1/ True,因此不会有问题。但显然使用FitTextor LSet,我们基本上是以不受控制的方式写入内存地址,因此 VBA 开始对这个特定变量表现出奇怪的行为,因为它期望该Boolean变量的内容已经被强制但没有被强制。

  • 那么...错误是在 Word 库中(可修复)还是在 VBA 本身中(不太可能修复)? (2认同)

Cri*_*vão 8

正如 Cindy Meister 所解释的那样,这是因为来自该属性的内部 Long 值。我们应该始终使用 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
Run Code Online (Sandbox Code Playgroud)