Хаха :). Не, я серьезно. Позвольте мне объяснить.
Variant потрясающий
Много написано о том, почему следует избегать Variant и использовать вместо него более конкретный тип данных. Действительно, нет смысла использовать Variant (явно или нет), когда можно работать с Long, Date или String. Это справедливо для всех внутренних типов данных.
Использование Variant для объектов, вместо интерфейса класса, делает любое обращение к нему, по сути, поздно связанным (late binding, прим. переводчика), то есть он может быть разрешен только на этапе выполнения, так как фактический тип класса/интерфейса не известен во время компиляции… и именно поэтому VBE не будет показывать список автодополнений или информацию о параметрах для всего, где используется Variant. Это, определенно, нехорошо, тем более, что Object – лучший вариант для явного позднего связывания.
Подожди, мне показалось, ты сказал, что он потрясающий?
Ну да. Понимаете, его природа делает его идеальным типом данных для возврата в любой UDF (пользовательской функции) Excel: когда функция завершается без ошибки, она может возвращать подтип Variant, а в случае ошибки, подпрограмма обработки ошибок в UDF может заставить функцию вернуть на лист Excel значение Variant/Error, и да, это действительно потрясающе.
Но подождите, это еще не все!
Массивы. Variant отлично подходит для массивов – потому что типизированные массивы должны умереть.
Хорошо. Что такое типизированный массив?
Это одна из тех языковых причуд, которые делают VBA таким... я бы сказал, восхитительным.
Под "типизированным" я подразумеваю массив, который таким образом объявлен, так что старый добрый "массив строк" подходит под это определение:
Option Explicit
Private DynamicallySizedArray() As String
Private FixedSizeArray(1 To 10) As StringРазличие между фиксированным и динамическим размерами массива важно в спецификациях языка, но давайте просто подчеркнем тот факт, что синтаксис требует указывать границы массива сразу после имени идентификатора.
Это важно, потому что As String(10) - это синтаксическая ошибка, а не массив.
То же самое происходит при объявлении параметра:
Private Sub DoSomething(ByRef Values() As String)
End SubОбратите внимание на явный модификатор ByRef: массивы могут передаваться только по ссылке. Объявление этого параметра ByVal будет ошибкой этапа компиляции, и это приведет к интересным последствиям, когда мы начнем рассматривать вопрос о возможности и способе присваивания массивов.
Поскольку параметр value у property всегда передается по значению (даже если в нем написано ByRef!), типизированные массивы сразу же становятся проблемой при определении свойств:
Public Property Let SomeValues([ByVal] Values() As String)
End PropertyА что, если мы захотим вернуть типизированный массив? Это будет выглядеть примерно так:
Public Function GetSomeValues() As String()
End FunctionПрошу заметить, что указание границ массива или длины строки здесь будет недопустимо, и так как это сигнатура члена класса или модуля, пара скобок сразу после имени идентификатора обозначает список параметров и должна включаться даже тогда, когда параметров нет.
Я хочу сказать, что конечно, на это есть много веских и обоснованных причин, но в итоге проблема сводится к тому, что синтаксис несогласованный и запутанный... и это только начало проблем на этапе компиляции. Дальше – больше.
ReDim
Раз уж мы начали, нужно поговорить и о ReDim. Совсем недавно я работал над системой типов RD3 (RubberDuck 3, прим. переводчика) и перешел к реализации ReDim для массивов, и я был удивлен (нет
