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 确实隐式地将其强制转换为 TrueFalse,这似乎具有误导性但是正确的,因为任何虚假结果都等于 0(又名 False),任何真实的结果都是不真实的。请注意,真实的否定意味着 1-1 都是真实的。

如果我直接将 1 分配给 Boolean 变量,VBA 就会将其强制转换为 -1/True,因此'没问题。但是显然对于 FitTextLSet,我们基本上是以一种不受控制的方式写入内存地址,因此 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