Ошибка компиляции VBA, если функция Instr используется с именованным параметром и возвращаемым значением, присвоенным переменной

Предыстория. В VBA функцию 'InStrRev' можно вызывать без именованных параметров или с именованными параметрами.

    'Call without named parameters

     Call InStrRev("AB", "B")                   'No compiler error  
     i = InStrRev("AB", "B")                    'No compiler error

    'Call with named parameters

     Call InStrRev(StringCheck:="AB", StringMatch:="B") 'No compiler error
     i = InStrRev(StringCheck:="AB", StringMatch:="B")  'No compiler error

Проблема: в VBA компилятор возвращает ошибку «Ожидается: разделитель списка», если функция «InStr»:

  • Вызывается с именованными параметрами и
  • Его возвращаемое значение присваивается переменной

    'Call without named parameters
    
     Call InStr("AB", "B")                   'No compiler error  
     i = InStr("AB", "B")                    'No compiler error
    
    'Call with named parameters
    
     Call InStr(String1:="AB", String2:="B") 'No compiler error
     i = InStr(String1:="AB", String2:="B")  'Compiler error : "Expected: list separator"
    

Вопрос. Почему возникает ошибка компилятора VBA, когда функция 'Instr' используется с именованными параметрами, а возвращаемое значение присваивается переменной? Это ограничение языка или ошибка компилятора?

Справка : снимок экрана редактора VBA для подсказок по функциям 'InstrRev' и 'Instr'. Различия выделены красным.

Сравнение подсказок по функциям '*InstrRev*' и '*Instr*' в редакторе VBA

Примечание: 'String1' и 'String2' являются необязательными аргументами для функции 'InStr' в соответствии с приведенной выше подсказкой на снимке экрана. квадратных скобок. Однако они необходимы, как указано в ответе ниже и в справочнике по языку Visual Basic: https://msdn.microsoft.com/EN-US/library/office/gg264811.aspx


person Fabien Launay    schedule 30.01.2016    source источник
comment
Хороший вопрос, такое же поведение в VB6.   -  person Alex K.    schedule 30.01.2016


Ответы (2)


InStr отличается тем, что его первый аргумент (Start) является необязательным, а его последующие аргументы String1/String2 нет (несмотря на [] во всплывающей подсказке). Если бы они были необязательными, InStr(1) проанализировал бы< /em>, но это не так и выдает ту же ошибку, которую вы видите.

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

VB6/A имеет много багажа, перенесенного из QBASIC, и этот язык (который iirc не допускал определяемых пользователем необязательных аргументов) имеет точно такую ​​же подпись для своего INSTR(), поэтому я предполагаю, что поведение, которое вы видите, является артефактом специальных правил синтаксического анализа. который должен существовать для вызовов InStr.

Любопытно, что его полное имя

 i = VBA.Strings.InStr(String1:="AB", String2:="B")` 

выполняет синтаксический анализ, но выдает ошибку во время выполнения, если не указано Start:

i = VBA.Strings.InStr(String1:="AB", String2:="B", Start:=1)` 

который работает, как ожидалось.

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


VBA.X() и X()

Это прекрасно:

ptr = VBA.CLng(AddressOf someFunc)

При этом возникает ошибка времени синтаксического анализа Ожидаемое выражение:

ptr = CLng(AddressOf someFunc)
person Alex K.    schedule 30.01.2016
comment
Благодарю за разъяснение. Еще одно мне непонятно. Как вы объясните следующее? Нет ошибки компиляции для ① 【i = VBA.Strings.InStr(String1:=AB, String2:=B, Start:=1)】, ② 【i = Strings.InStr(String1:=AB, String2:=B, Start :=1)】, ③ 【i = .InStr(String1:=AB, String2:=B, Start:=1)】. Но ошибка компилятора для ④ 【i = InStr(String1:=AB, String2:=B, Start:=1)】. - person Fabien Launay; 30.01.2016
comment
.Anything сам по себе недопустимый синтаксис - person Alex K.; 30.01.2016
comment
Спасибо за замечание для ③. Хотя код работает нормально с ① и ②, компилятор возвращает Expected: list separator error for ④. Однако необязательный аргумент «Пуск» передается после необязательных аргументов «String1» и «String2», как вы любезно предложили. Почему это ? - person Fabien Launay; 30.01.2016
comment
Вероятно, потому что предоставление всех трех аргументов устраняет любую двусмысленность и позволяет компилятору точно знать, какой аргумент какой. - person Alex K.; 30.01.2016
comment
В случаях ①, ② и ④ предоставляются 3 аргумента. Однако компилятор возвращает ошибку только для ④. Означает ли это, что компилятор имеет логику синтаксического анализа, когда Instr вызывается с библиотечным префиксом «Strings» (случаи ① и ②), отличающуюся от логики синтаксического анализа, когда Instr вызывается без библиотечного префикса (случай ④)? - person Fabien Launay; 30.01.2016
comment
Да, есть отличия, добавил пример выше. - person Alex K.; 30.01.2016
comment
Ошибка компиляции Ожидаемое выражение, которое вы добавили в приведенном выше примере, будет менее запутанным, чем Ожидаемое: разделитель списка для случая ④. Это выглядит как неправильное сообщение об ошибке компилятора для пользователя в случае ④... Вам так не кажется? В любом случае, большое спасибо за ваше разъяснение! - person Fabien Launay; 30.01.2016

InStr перегружен параметрами Variant.

Функция InStr имеет 4 необязательных параметра во время разработки, но во время выполнения необходимо указать как минимум 2 аргумента. Первые 3 параметра InStr — это все Variant, что позволяет InStr поддерживать два разных синтаксиса и эффективно имитировать перегруженную функцию. Это одна из причин, по которой String1 и String2 определяются как типы Variant, а не как типы String. Start может быть Long, но это также и Variant тип.

В следующих 4 примерах x всегда присваивается значение 4.

Вариант 1. Использование определенного порядка параметров или значений имен

Сигнатура функции ведет себя так, как она определена:

Функция InStr([Start], [String1], [String2], [Compare As VbCompareMethod = vbBinaryCompare])

x = VBA.InStr(1, "food", "D", vbTextCompare)                                   '4
x = VBA.InStr(Start:=1, String1:="food", String2:="D", Compare:=vbTextCompare) '4

Вариант 2. Использование альтернативного порядка или значения имени

Сигнатура функции ведет себя так, как если бы она была определена следующим образом:

Функция InStr([String1], [String2], , [Сравнить как VbCompareMethod = vbBinaryCompare])

Фактически это означает, что Start следует использовать так, как если бы это было String1, а String1 следует использовать так, как если бы это было String2. Аргумент String2 должен быть опущен, иначе вы получите ошибку Type Mismatch.

x = VBA.InStr("food", "D", , vbTextCompare)                        '4
x = VBA.InStr(Start:="food", String1:="D", Compare:=vbTextCompare) '4

Использование именованных параметров

Но, как вы обнаружили, функция InStr страдает от ошибок синтаксиса и/или компиляции при использовании именованных параметров:

Синтаксическая ошибка: ожидаемый разделитель списка

Когда названы все параметры:

x = InStr(Start:=1, String1:="foo", String1:="foo", Compare:=vbBinaryCompare)

Вы получаете:

Синтаксическая ошибка: ожидаемый разделитель списка

Ошибка компиляции: объект не поддерживает именованные аргументы

Когда некоторым параметрам присвоено имя:

x = InStr(1, String1:="foo", String2:="foo", Compare:=vbBinaryCompare)

Вы получаете:

Ошибка компиляции: объект не поддерживает именованные аргументы.

Те же ошибки с функцией StrComp

Функция StrComp, по-видимому, не имеет какой-либо функциональности перегруженного типа, но имеет те же проблемы с ошибками синтаксиса и компиляции:

x = StrComp(String1:="foo", String2:="foo", Compare:=vbBinaryCompare) 'Syntax Error: Expected List Separator???
x = StrComp("foo", String2:="foo", Compare:=vbBinaryCompare) 'Compile error: Object doesn't support named arguments

Но, как обнаружил ОП, ошибка не возникает с InStrRev.

Итак, что общего у InStr и StrComp, что отличается от InStrRev и казалось бы всех остальных функций VBA?

Что ж, InStr и StrComp оба имеют эти общие черты:

  • Функции определены в первой библиотеке типов, на которую ссылаются.
  • Функции определены в модуле TLB/COM.
  • Все параметры, кроме последнего, относятся к типу Variant.
  • Последний параметр — это Enum со значением по умолчанию.
  • Возвращаемое значение является вариантом

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

Квалификация функции устраняет проблему!?!?

И InStrRev, и StrComp могут использоваться со всеми/некоторыми именованными параметрами, если функция уточняется по имени библиотеки или имени модуля:

'InStr Vanilla usage:
x = Strings.InStr(Start:=1, String1:="food", String2:="D", Compare:=vbTextCompare) '4
x = VBA.InStr(Start:=1, String1:="food", String2:="D", Compare:=vbTextCompare)     '4

'InStr Alternate usage:
x = Strings.InStr(Start:="food", String1:="D", Compare:=vbTextCompare) '4
x = VBA.InStr(Start:="food", String1:="D", Compare:=vbTextCompare)     '4

'StrComp usage
x = Strings.StrComp(String1:="food", String2:="D", Compare:=vbTextCompare)         '1
x = VBA.StrComp(String1:="food", String2:="D", Compare:=vbTextCompare)             '1
person ThunderFrame    schedule 16.09.2016
comment
Вау, это так сломано. - person Mathieu Guindon; 16.09.2016