Можно ли определять статические коллекции в программировании макросов VBA для Outlook?
Sub mySub()
Static myCollection As VBA.Collection
Set myCollection = New VBA.Collection
myCollection.Add "entry1"
myCollection.Add "entry2"
myCollection.Add "entry3"
End Sub
Таким образом, myCollection не нужно переопределять каждый раз, когда запускается макрос mySub ().
Переменная Static
в VBA будет сохранять свое значение между вызовами. Обычно переменная, которая выходит за пределы области видимости и нигде не упоминается, уничтожается; Static
изменяет декларация переменной.
Это не меняет того, что делает эта инструкция безусловно:
Set myCollection = New VBA.Collection
Какую бы ссылку ни содержал предыдущий запуск, мы перезаписываем ее каждый раз, отменяя то, что Static
должен был купить нам.
У вас была бы одна и та же проблема независимо от типа задействованной переменной: проблема не в типе, а в безусловность инструкции Set
.
Сделайте это условным:
Static myCollection As VBA.Collection
If myCollection Is Nothing Then
Set myCollection = New VBA.Collection
End If
И теперь myCollection
будет только Nothing
при первом вызове; в последующих запусках ссылка myCollection
будет сохраняться между вызовами.
Примерно то, что вы получаете с переменной уровня модуля:
Option Explicit
Private myCollection As VBA.Collection
Public Sub TestModuleVariable()
If myCollection Is Nothing Then
Set myCollection = New VBA.Collection
End If
With myCollection
.Add "entry" & .Count + 1
Debug.Print .Count
End With
End Sub
Public Sub TestStaticVariable()
Static items As VBA.Collection
If items Is Nothing Then
Set items = New VBA.Collection
End If
With items
.Add "entry" & .Count + 1
Debug.Print .Count
End With
End Sub
Какой из них использовать, зависит от того, что остальная часть модуля должна делать с этим Collection
. Если никому об этом не нужно знать, тогда оставьте это в локальной области.
Рассмотрите делает обязанностью вызывающего пользователя предоставить коллекцию в качестве аргумента, что делает его заботой о том, сколько раз он вызывает эту процедуру.
Public Sub TestParameter(ByRef items As VBA.Collection)
If items Is Nothing Then
Set items = New VBA.Collection
End If
With items
.Add "entry" & .Count + 1
Debug.Print .Count
End With
End Sub
Большой! Спасибо за подробные объяснения и за предоставленный код.
Если у коллекции было начальное значение (при первом вызове), я предполагаю, что предложение [If items Is Nothing Then] также должно включать часть инициализации.
@Barok вы могли бы получить это с Private myCollection As New VBA.Collection
. Однако будьте осторожны, объявления As New
могут иметь непредвиденные последствия: вы не можете Set
их на Nothing
, а затем ссылаться на них при проверке If ... Is Nothing
, потому что эта ссылка автоматически воссоздает экземпляр, и условие всегда будет оцениваться как True
... лучше избегать As New
декларации.
Я хотел бы поместить коллекции в общедоступные подпрограммы, такие как TestStaticVariable (), вместо использования глобальных коллекций, таких как Private myCollection As New VBA.Collection, из-за проблем с обработкой ошибок. Не вызовет ли включение начальных значений в предложение [If items Is Nothing Then] какие-либо непредвиденные проблемы? Что происходит при возникновении исключения? Статическая коллекция умрет?
Блок If items Is Nothing Then
- это именно то место, где вы добавляете любые начальные значения, которые должны быть там, да. Обратите внимание, что "частные" переменные области модуля не являются "глобальными", они доступны только в том модуле, в котором они объявлены. Когда возникает ошибка, вам необходимо обработать ее, если ошибка всплывает на всем протяжении вызова stack, тогда ваша программа (и ее состояние) по сути является тостом.
Я использовал статические словарные объекты в нескольких случаях. Преимущества довольно очевидны по сравнению с поздним связыванием. Однако я все еще очищаю объект словаря с помощью .RemoveAll перед каждым последующим использованием.