Остановить VBA Evaluate от вызова целевой функции дважды - PullRequest
8 голосов
/ 10 апреля 2010

У меня проблемы с тем, чтобы функция VBA Evaluate () выполнялась только один раз; кажется, всегда бегать дважды. Например, рассмотрим тривиальный пример ниже. Если мы запустим подпрограмму RunEval (), она дважды вызовет функцию EvalTest (). Это видно по двум разным случайным числам, которые выводятся в непосредственном окне. Поведение было бы таким же, если бы мы вызывали другую подпрограмму с Evaluate вместо функции. Может кто-нибудь объяснить, как я могу заставить Evaluate выполнить целевую функцию один раз вместо двух? Спасибо.

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

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

Ответы [ 5 ]

8 голосов
/ 13 апреля 2010

Эта ошибка, по-видимому, возникает только с UDF, а не со встроенными функциями. Вы можете обойти это, добавив выражение:

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

Но есть и ряд других ограничений с Evaluate, которые описаны здесь http://www.decisionmodels.com/calcsecretsh.htm

1 голос
/ 08 апреля 2013

Я столкнулся с той же проблемой, после исследования я обнаружил, что функция вызывается дважды, потому что у меня есть раскрывающийся список и значение, используемое в пользовательской функции.

обходя код, приведенный ниже, поместите код в ThisWorkbook

Private Sub Workbook_Open () 'установить расчет вручную, чтобы остановить расчет, когда выпадающий список обновлен, и снова рассчитать для UDF

     Application.Calculation = xlCalculationManual

End Sub

Private Sub Workbook_SheetChange (ByVal Sh As Object, _ Источник ByVal As Range)

'вычислять только при изменении листа

   Calculate

End Sub

1 голос
/ 10 апреля 2010

Я не знаю, как это остановить, но вы можете хотя бы узнать, когда это происходит в большинстве случаев. Это может быть полезно, если ваши вычисления занимают много времени или имеют побочные эффекты, которые вы не хотите, чтобы они происходили дважды, и вы хотите замкнуть их.

(РЕДАКТИРОВАТЬ: Чарльз Уильямс на самом деле имеет ответ на ваш конкретный вопрос. Мой ответ все еще может быть полезен, когда вы не знаете, какой тип данных вы можете получить, или когда вы ожидаете получить что-то вроде массива или диапазон.)

Если вы используете свойство Application.Caller в подпрограмме, вызванной в результате вызова Application.Evaluate, вы увидите, что один из вызовов поступил из верхней левой ячейки фактического диапазона Evaluate вызов сделан, и один из ячейки $ A $ 1 листа, на котором находится диапазон. Если вы вызываете Application.Evaluate из непосредственного окна, как если бы вы вызывали ваш пример Sub, один вызов появляется из верхней левой ячейки текущего выбранного диапазона и один из ячейки $ A $ 1 текущей рабочей таблицы. Я почти уверен, что в обоих случаях это первый звонок, который стоит $ A $ 1. (Я бы проверил это, если это имеет значение.)

Однако из Application.Evaluate будет возвращено только одно значение. Я почти уверен, что это второй эвал. (Я бы тоже это проверил.)

Очевидно, что это не будет работать с вызовами из действительной ячейки $ A $ 1.

(Что касается меня, я хотел бы знать , почему происходит двойная оценка. Мне также хотелось бы знать, почему оценщик выставляется вообще. Кто-нибудь?)

РЕДАКТИРОВАТЬ: я спросил здесь о StackOverflow: Почему метод Excel Evaluate является оценщиком общих выражений?

Надеюсь, это поможет, хотя и не дает прямого ответа на ваш вопрос.

1 голос
/ 10 апреля 2010

Я сделал быстрый поиск и обнаружил, что другие сообщали о похожем поведении и других странных ошибках с Application.Evaluate (см. KB823604 и this ). Вероятно, это не самое важное в списке Microsoft для исправления, так как его видели по крайней мере со времени Excel 2002. Эта статья базы знаний дает обходной путь, который также может работать в вашем случае - поместите выражение для оценки в таблицу и затем получите значение из вот так:

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

Я изменил ваш пример, чтобы он также возвращал случайное значение из функции. Это печатает значение во второй раз, но с добавленным, так что вторая печать происходит из первой подпрограммы. Вы можете написать подпрограмму поддержки, чтобы сделать это для любого выражения.

0 голосов
/ 18 сентября 2014

Увидев, что нет правильного способа обойти эту проблему, я решил ее следующим образом:

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

проблема решена всеми.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...