web-dev-qa-db-fra.com

VBA: Pourquoi l'opérateur Not cesserait-il de fonctionner?

Cela m'a complètement déconcerté.

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

Mais j'essaie de basculer la propriété .FitText dans une cellule de tableau (dans Word). .FitText commence comme True.

Si je l'attribue à 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

Je ne comprends tout simplement pas ce que je fais mal.

39
eluhnabroad

Je crois que l'explication a à voir avec la façon dont les anciens langages de programmation (WordBasic et début VBA) stockaient les valeurs entières de True et False. À cette époque, True = -1 et False = 0.

Les langages de programmation plus récents utilisent toujours 0 pour False, mais 1 pour True.

La majorité des propriétés de type booléen de Word continuent d'utiliser -1 pour True (Font.Bold, par exemple), ce qui a été source de confusion et de frustration pour les programmeurs travaillant avec Interop dans des langues plus récentes. Ainsi, à un moment donné, certains développeurs de Microsoft ont décidé d'utiliser la nouvelle méthode et ont attribué la valeur entière 1 à True pour de nouvelles fonctionnalités. Tels que FitText.

Considérant l'exemple de code suivant, où X est de type Boolean et y de type Integer:

  • Si FitText est True, la valeur entière est 1
  • Si l'inversion des valeurs, l'utilisation de Not montre que le booléen reste "True" car sa valeur entière n'est pas 0, c'est -2
  • La définition de la valeur entière directement sur True donne -1

C'est déroutant, en effet, mais cela explique pourquoi Not ne donne pas le résultat attendu.

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

C'est à cause de la valeur Long interne provenant de cette propriété, comme expliqué par Cindy Meister. Nous devons toujours utiliser CInt pour éviter cela.

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
Cristiano Galvão

Pour ajouter à l'excellente réponse de Cindy, je tiens à souligner que bien que VBA dispose normalement de garanties pour contraindre les valeurs lors de l'attribution à un type de données Boolean, cela peut être contourné. Fondamentalement, si vous écrivez une valeur aléatoire dans une adresse mémoire qui n'est pas la vôtre, vous devez vous attendre à un comportement non défini.

Pour aider à le démontrer, nous allons (ab) utiliser LSet qui nous permet essentiellement de copier la valeur sans vraiment l'affecter.

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

Notez la ligne b = b1.b est fondamentalement équivalent à ce que nous avons fait dans le code OP

X = Selection.Cells(1).FitText

C'est-à-dire, affecter un Boolean à un autre Boolean. Cependant, parce que j'ai écrit au b1.b en utilisant LSet, en contournant les vérifications d'exécution VBA, il n'est pas contraint. Lors de la lecture du Boolean, VBA le force implicitement dans True ou False, ce qui semble trompeur mais est correct car tout résultat falsifié est égal à 0 (aka False), et tout résultat véridique n'en est pas un. Notez que le négatif pour véridique signifie que les deux 1 et -1 sont véridiques.

Si j'avais attribué le 1 à une variable Boolean directement, VBA l'aurait forcée à -1/True et donc il n'y aurait pas de problème. Mais évidemment avec FitText ou LSet, nous écrivons essentiellement à l'adresse mémoire de manière incontrôlée, de sorte que VBA commence à se comporter étrangement avec cette variable particulière car il attend le Boolean la variable avait déjà son contenu contraint mais ne l'était pas.

7
this