41 votes

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

Cela me laisse totalement perplexe.

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 faire basculer la propriété .FitText dans une cellule de tableau (dans Word). La propriété .FitText est au départ 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 pas ce que je fais de travers.

0 votes

Répondre à la clôture comme dupliqué par un badge d'or : Alors que l'autre question porte sur le moment où Not ne fonctionne pas comme prévu, il ne gère pas le pourquoi derrière ce comportement : Le modèle d'objet de Word ne se comporte pas selon les règles de VBA. Une discussion sur cet aspect est totalement absente de la question liée en double.

2 votes

@Cindy Le "pourquoi" est exactement la même chose dans les deux cas. Il s'agit d'un problème classique avec Visual Basic et VBA. False est égal à 0 (tous les bits sont effacés), et True est défini comme suit Not False Cela pose également des problèmes d'interopérabilité avec WinAPI. Toute valeur non nulle est traitée comme True Le problème ne se pose donc que si l'on commence à manipuler des bits sans comprendre ce qui se passe, ce qui est aggravé par l'absence de distinction entre les opérateurs logiques et les opérateurs bitwise. Je ne vois pas l'intérêt de rouvrir ce dossier alors que la cause première et la solution sont fondamentalement identiques.

0 votes

@CindyMeister S'il n'avait pas déjà été expliqué ici que c'est le modèle d'objet de Word qui génère un message malformé, il n'y aurait pas de problème. Boolean J'aurais ajouté ce commentaire en plus du vote de clôture. Mais comme il y avait déjà une excellente réponse qui l'expliquait, je n'ai pas eu à le faire. La raison sous-jacente est identique, comme l'a mentionné Cody Gray, et en plus de cela, je vois la fermeture des doublons comme un moyen de relier les questions utiles entre elles plutôt que de punir le dernier demandeur.

41voto

Cindy Meister Points 12465

Je pense que l'explication tient à la façon dont les anciens langages de programmation (WordBasic et les premiers VBA) stockaient les valeurs entières de True et False. À l'époque, True = -1 et False = 0.

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

La majorité des propriétés booléennes de Word continuent d'utiliser -1 pour True (Font.Bold, par exemple), ce qui est source de confusion et de frustration pour les programmeurs travaillant avec l'interop dans des langages plus récents. À un moment donné, certains développeurs de Microsoft ont donc décidé d'utiliser la nouvelle méthode et d'attribuer la valeur entière de 1 à True pour certaines nouvelles fonctionnalités. Par exemple FitText .

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

  • Si FitText est Vrai, la valeur entière est 1
  • En cas d'inversion des valeurs, l'utilisation de Not montre que le booléen reste "vrai" parce que sa valeur entière n'est pas 0, mais -2.
  • En fixant la valeur entière directement à True, on obtient -1

C'est effectivement déroutant, mais cela explique pourquoi Not ne donne pas le résultat escompté.

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

9voto

this Points 22

Pour compléter l'excellente réponse de Cindy, je tiens à souligner que si VBA dispose normalement de protections pour contraindre les valeurs lors de l'affectation à un fichier Boolean il est possible de contourner ce problème. En fait, si vous écrivez une valeur aléatoire à une adresse mémoire qui n'est pas la vôtre, vous devez vous attendre à un comportement indéfini.

Pour le démontrer, nous allons (ab)utiliser LSet qui nous permettent essentiellement de copier la valeur sans l'assigner.

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 essentiellement équivalent à ce que nous avons fait dans le code de l'OP

X = Selection.Cells(1).FitText

En d'autres termes, l'attribution d'un Boolean à un autre Boolean . Cependant, comme j'ai écrit au b1.b en utilisant LSet En outre, en contournant les contrôles d'exécution de VBA, il n'est pas contraint. Lors de la lecture du Boolean VBA l'oblige implicitement à choisir l'une ou l'autre des options suivantes True o False ce qui semble trompeur mais est correct car tout résultat erroné est un résultat égal à 0 (alias False ), et tout résultat véridique est un résultat qui ne l'est pas. Notez que la négation de "vrai" signifie que les deux 1 y -1 sont véridiques.

Si j'avais assigné le 1 à un Boolean directement, VBA l'aurait forcée à devenir une variable. -1 / True et il n'y aurait donc pas de problème. Mais manifestement, avec FitText o LSet nous écrivons à l'adresse mémoire de manière incontrôlée, de sorte que VBA commence à se comporter de manière étrange avec cette variable particulière, puisqu'il s'attend à ce que la variable Boolean variable dont le contenu a déjà été contraint, mais qui ne l'a pas été.

8voto

C'est en raison de la valeur interne de Long qui provient de cette propriété, comme l'explique Cindy Meister. Nous devrions 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

0 votes

Upvoted pour l'inclusion d'une solution facile pour obtenir le comportement désiré.

-4voto

Gene Points 352

Tout d'abord, le L'opérateur ne fonctionne pas correctement , exactement comme il se doit.

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

Pour basculer, cela fonctionne très bien :

If Selection.Cells(1).FitText Then
    Selection.Cells(1).FitText = False
Else
    Selection.Cells(1).FitText = True
End If

Quant à la raison pour laquelle vous ne voyez pas le résultat que vous attendiez, elle est liée au moulage de type, aux conversions de type implicites et au passage d'une valeur d'un type à une variable du même type. Dans votre cas, la propriété de la cellule est booléenne. Puisque vous explicitement définir le type de la variable réceptrice comme étant booléenne, non implicite est en cours : Le booléen X reçoit la valeur interne valeur de la propriété booléenne, qui ( Cindy Meister est correcte) s'est avérée être 1, et 1 s'évalue bien à Vrai en tant que variable booléenne.

Aujourd'hui, Mathieu Guindon a fait remarquer à juste titre que "Le -2 est dû au fait que les opérateurs logiques effectuent des opérations par bits..." . Donc, Not 1 = -2, et -2 s'évalue également à Vrai ; en effet, tout nombre stocké en interne pour une variable booléenne autre que 0 rend la valeur de cette variable Vrai comme dans un CBool(any_number_but_0) = True explicite.

Le problème est donc le passage de booléen à booléen de la valeur numérique 1 (la représentation interne) de l'un à l'autre. Vous devez forcer la réévaluation du type de variable implicitement ou explicitement, et tout fonctionnera comme vous le souhaitez :

' Illustration: a few possible solutions
Dim X As Variant  
' implicit re-evauation
X = Selection.Cells(1).FitText ' X=True, a Variant sub-type Boolean,  CInt(X)=-1
X = Not X                      ' X=False, CInt(X)=0 

Dim X As Boolean  
' explicit re-evaluation
X = Selection.Cells(1).FitTex  ' X=True, CInt(X)=1
X = Not CBool(CInt(X))         ' X=False, CInt(X)=0

' explicit re-evaluation    
X = Selection.Cells(1).FitText ' X=True, CInt(X)=1
X = Not CBool(CStr(X))         ' X=False, CInt(X)=0

' explicit and implicit (by the "+" operator) re-evaluation
X = Selection.Cells(1).FitText ' X=True, CInt(X)=1
X = Not (CBool(X) + 0)         ' X=False, CInt(X)=0; same as Not (True + 0) 

etc.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X