web-dev-qa-db-ja.com

VBA:Notオペレーターが機能しなくなるのはなぜですか?

これは私を完全に困惑させました。

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

しかし、私は(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

私は何が間違っているのか理解できません。

39
eluhnabroad

説明は、古いプログラミング言語(WordBasicと初期のVBA)がTrueとFalseの整数値をどのように格納したかに関係していると思います。当時、True = -1およびFalse = 0でした。

新しいプログラミング言語では、Falseに0を使用していますが、Trueには1を使用しています。

Wordのブール型プロパティの大部分は、True(たとえば、Font.Bold)に-1を引き続き使用します。これは、新しい言語でInteropを使用するプログラマーに混乱とフラストレーションの原因となってきました。したがって、ある時点で、Microsoftの一部の開発者は新しい方法を使用することを決定し、いくつかの新しい機能のために整数値1をTrueに割り当てました。 FitTextなど。

次のコードサンプルを考えてみましょう。ここで、XBooleanタイプで、yIntegerタイプです。

  • FitTextがTrueの場合、整数値は1です
  • 値を逆にする場合、Notを使用すると、ブール値は整数値が0ではないため「True」のままであることが示され、-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
37
Cindy Meister

これは、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
7

Cindyの優れた答えに付け加えると、VBAは通常、Booleanデータ型に割り当てるときに値を強制する保護手段を備えていますが、これは回避できることを指摘しておきます。基本的に、自分ではないメモリアドレスにランダムな値を書き込むと、予期しない動作が予想されます。

これを実証するために、LSetを(ab)使用します。これにより、実際に割り当てることなく値をコピーできます。

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の両方が真実であることを意味することに注意してください。

1Boolean変数に直接割り当てた場合、VBAはそれを-1/Trueに強制変換していたため、問題はありませんでした。しかし、明らかにFitTextまたはLSetを使用すると、基本的には制御されていない方法でメモリアドレスに書き込みます。そのため、VVARは、Boolean変数の内容が強制されているが、そうではなかったため、この特定の変数で奇妙な動作を開始します。

7
this