2010-04-10 5 views
7

J'ai des difficultés à exécuter la fonction Evaluate() de VBA uniquement une fois; il semble toujours courir deux fois. Par exemple, considérons l'exemple trivial ci-dessous. Si nous exécutons le sous-programme RunEval(), il appellera la fonction EvalTest() deux fois. Cela peut être vu par les deux différents nombres aléatoires qui sont imprimés dans la fenêtre immédiate. Le comportement serait le même si nous appelions un autre sous-programme avec Evaluate au lieu d'une fonction. Est-ce que quelqu'un peut expliquer comment je peux évaluer pour exécuter la fonction cible une fois au lieu de deux fois? Je vous remercie.Arrêter VBA Évaluez deux fois la fonction cible de l'appel

Sub RunEval() 
    Evaluate "EvalTest()" 
End Sub 

Public Function EvalTest() 
    Debug.Print Rnd() 
End Function 

Répondre

7

Ce bug semble se produire uniquement avec les fonctions UDF, et non avec les fonctions intégrées. Vous pouvez le contourner en ajoutant une expression:

Sub RunEval() 
    ActiveSheet.Evaluate "0+EvalTest()" 
End Sub 

Mais il y a aussi un certain nombre d'autres limitations Évaluer, documentées ici http://www.decisionmodels.com/calcsecretsh.htm

+0

Est-ce considéré comme un bug, ou juste un comportement non documenté? – jtolle

+0

Je considérerais cela comme un bug ... –

+0

Appeler la fonction avec la syntaxe de parenthèse (par exemple '[EvalTest]') obtient le même résultat –

1

je fait une recherche rapide et a constaté que d'autres ont signalé un comportement similaire et d'autres bugs bizarres avec Application.Evaluate (voir KB823604 et this). Cet article de la base de connaissances propose une solution de contournement qui peut également fonctionner dans votre cas - mettez l'expression à évaluer dans une feuille de calcul, puis récupérez la valeur à partir de celle-ci. cela, comme ceci:

Sub RunEval() 
    Dim d As Double 

    Range("A1").Formula = "=EvalTest()" 
    d = Range("A1").Value 
    Range("A1").Clear 
    Debug.Print d 
End Sub 

Public Function EvalTest() As Double 
    Dim d As Double 
    d = Rnd() 
    Debug.Print d 
    EvalTest = d + 1 
End Function 

J'ai modifié votre exemple pour retourner également la valeur aléatoire de la fonction. Ceci imprime la valeur une seconde fois mais avec la valeur ajoutée de sorte que la deuxième impression provient du premier sous-programme. Vous pourriez écrire une routine de support pour faire ceci pour n'importe quelle expression.

+0

Merci Kevin, dommage que cela n'ait pas été résolu, mais je suis content qu'il y ait une solution de contournement. – Abiel

1

Je ne connais pas un moyen de l'arrêter, mais vous pouvez au moins reconnaître quand cela se passe la plupart du temps. Cela peut être utile si votre calcul prend du temps ou a des effets secondaires que vous ne voulez pas avoir deux fois et que vous voulez court-circuiter. (Réponse: Charles Williams a une réponse à votre question spécifique.) Ma réponse pourrait être utile si vous ne savez pas quel type de données vous pourriez récupérer, ou quand vous prévoyez d'obtenir quelque chose comme un tableau ou Si vous utilisez la propriété Application.Caller dans une routine appelée à la suite d'un appel à Application.Evaluate, vous verrez que l'un des appels semble provenir de la cellule supérieure gauche de la plage réelle. Evaluate l'appel est fait à partir de, et un à partir de la cellule $ A $ 1 de la feuille de cette gamme est sur. Si vous appelez Application.Evaluate à partir de la fenêtre immédiate, comme vous appelleriez votre exemple Sub, un appel semble provenir de la cellule supérieure gauche de la plage actuellement sélectionnée et un de la cellule $ A $ 1 de la feuille de calcul en cours. Je suis à peu près sûr que c'est le premier appel qui coûte $ 1 dans les deux cas. (Je le testerais si c'est important.)

Cependant, une seule valeur sera retournée par Application.Evaluate. Je suis à peu près sûr que c'est celui du deuxième eval. (Je le testerais aussi.)

Évidemment, cela ne fonctionnera pas avec les appels faits à partir de la cellule réelle $ A $ 1.

(Quant à moi, j'aimerais savoir pourquoi la double évaluation arrive, j'aimerais aussi savoir pourquoi l'évaluateur est exposé à tout le monde..?)

EDIT: J'ai demandé sur StackOverflow ici : Why is Excel's 'Evaluate' method a general expression evaluator?

J'espère que cela aide, même si elle ne répond pas directement à votre question.

1

je fais face au même problème, après enquête que j'ai trouvé la fonction appelée deux fois parce que j'ai une liste déroulante et la valeur utilisée dans une fonction définie par l'utilisateur.

travail autour du soufflet de code, mettre le code dans ThisWorkbook

Private Sub Workbook_Open() « définir le calcul manuel pour arrêter le calcul si dropdownlist updeated et calculer à nouveau pour l'UDF

 Application.Calculation = xlCalculationManual 

End Sub

Sous Workbook_SheetChange privé (ByVal Sh comme objet, _ ByVal Source Comme Range)

« calculte que lorsque la feuille a changé

Calculate 

End Sub

-1

Après avoir vu il n'y a pas façon de contourner ce problème, je l'ai résolu par ce qui suit:

Dim RunEval as boolean 

Sub RunEval() 
    RunEval = True 
    Evaluate "EvalTest()" 
End Sub 

Public Function EvalTest() 
    if RunEval = true then 
    Debug.Print Rnd() 
    RunEval = False 
    end if 
End Function 

problème résolu tout le monde.

Questions connexes