VBA: функция, которая принимает несколько типов ввода?

#vba

Вопрос:

У меня есть несколько пользовательских типов и функция для каждого, которая принимает эти типы и что-то с ними делает.

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

Как мне это сделать? Я попытался принять параметр как Function Foo(Param As Object) , но затем он терпит неудачу, когда я пытаюсь передать в функцию определяемый пользователем тип.

Комментарии:

1. Использование variant подойдет

2. Только этого не произойдет. Обратите внимание, что TypeName на UDT тоже нельзя использовать.

Ответ №1:

Определяемые пользователем типы (UDT) не являются классами (они похожи Structures на C или Records на Pascal), поэтому экземпляры UDT не являются объектами — вот причина, по которой вы не можете использовать их в качестве аргумента своей функции foo .

VBA имеет универсальный тип данных Variant . Поэтому , когда у вас есть Function Foo(Param As Variant) , вы можете передать функции все, что угодно, Foo 3 или Foo ActiveSheet или Foo "Hello world" . Что угодно, кроме УДТ… Если вы попытаетесь это сделать, вы получите сообщение об ошибке компилятора, в котором говорится: «Только пользовательские типы, определенные в модулях общедоступных объектов, могут быть принудительно добавлены к варианту или из него или переданы функциям с поздней привязкой».:

 Public Type tMyType
    i As Integer
    s As String
End Type

Sub Test
    Dim bar as tMyType
    ' Compiler complains
    foo bar

    Dim myUniversalVariant as Variant
    ' Compiler complains also
    myUniversalVariant = bar
End Sub
 

В официальной документации Variant говорится, что «Типы вариантов теперь поддерживают пользовательские типы»., однако, по крайней мере, в Excel 2016 это не работает (или, по крайней мере, я не смог этого сделать).

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

В модуле класса (я назвал его clsMyType ):

  public i As Integer
 public s As String
 

В обычном модуле:

  Sub Test
    Dim bar as clsMyType
    Set bar = new clsMyType
    foo bar

    Dim myUniversalVariant as Variant
    Set myUniversalVariant = bar
    foo myUniversalVariant 
End Sub
 

Теперь в зависимости от того, хочет ли ваша функция получать только объект или другие данные (например, целые числа или строки), типом параметра может быть Вариант или объект

 Function foo(Param as Object) 
    Debug.Print TypeName(Param)
End Function