Dim hashData As Object
Set hashData = obj.getHashData
Если getHashData
возвращает HashTable
, то hashData
является поздним HashTable
, и вы можете вызывать его членов , включая его Item
свойство * * 1013
Dim value As Variant
value = hashData.Item("key")
Вы не получаете проверку во время компиляции при вызовах участников с поздним связыванием в отношении Object
, поэтому вам следует быть особенно осторожным при опечатках, поскольку Option Explicit
не может спасти вас, если используется поздняя привязка. Обратитесь к документации HashTable
, указанной выше, чтобы узнать, каких членов вы можете вызвать.
Добавление ссылки на mscorlib.tlb
(вы найдете ее в C:\Windows\Microsoft.NET\Framework\v4.0.30319
или ссылку на эквивалент из \Framework64
, если ваш Excel 64-битный - битность библиотеки должна соответствовать битности хост-приложения ) обычно допускает раннее связывание, но хотя эта библиотека является видимой для COM, она предназначена для использования из управляемого (.net) кода, поэтому вы получаете доступ к этим объектам из интерфейсов - конкретные типы не предоставляют никаких членов напрямую :
Зная, что Hashtable
реализует интерфейс IDictionary
, мы можем использовать раннее связывание и получить проверку во время компиляции и IntelliSense , если объявим hashData As IDictionary
:
Dim hashData As mscorlib.IDictionary
Set hashData = New mscorlib.Hashtable
hashData.Add "foo", 42
Debug.Print hashData.Item("foo") 'prints 42
Обратите внимание, что свойство Item
предоставляется как элемент по умолчанию :
Это означает, что вы можете иметь неявный вызов члена Item
, точно так же, как это можно сделать с любым стандартным объектом коллекции VBA:
Dim hashData As mscorlib.IDictionary
Set hashData = New mscorlib.Hashtable
hashData.Add "foo", 42
Debug.Print hashData("foo") 'prints 42
Ранний код гораздо легче написать, особенно когда вы не знакомы с задействованными типами. Однако, если проект ссылается на 64-битную среду и ваши макросы должны работать в 32-битной версии Excel, вам нужно придерживаться позднего связывания, чтобы избежать проблем с связыванием.
Также обратите внимание, что итерация объекта Hashtable
с циклом For Each
не будет работать, поскольку счетчики работают в VBA по сравнению с тем, как они работают в .NET; коллекции Keys
и Values
являются объектами, реализующими интерфейс ICollection
, поэтому их повторение также будет нетривиальным: цикл For Each
не будет работать, и пока вы можете настроить For i = 0 To hashData.Keys.Count - 1
, Вы не можете получить предмет по индексу i
из ICollection
.
Но мы знаем, что ICollection
наследует IEnumerable
, а IEnumerable
работает с For Each
, поэтому мы можем cast Keys
collection к IEnumerable
и итерируйте все ключи и значения следующим образом:
Dim hashData As mscorlib.IDictionary
Set hashData = obj.getHashData
Dim hashKeys As mscorlib.IEnumerable
Set hashKeys = hashData.Keys
Dim k As Variant
For Each k In hashKeys
Debug.Print k, hashData(k) 'outputs the key and its associated value
Next
Проблема в том, что вы не можете привести к IEnumerable
с кодом с поздней привязкой или без ссылки на mscorlib.tlb
, и поздняя привязка как-то не увидит члена GetEnumerator
, так что это вызывает ошибку 438:
Dim hashKeys As Object
Set hashKeys = hashData.Keys
Dim k As Variant
For Each k In hashKeys ' error 438, hashKeys isn't exposing the enumerator
Debug.Print k, hashData(k)
Next
Вывод: если вам нужен код VBA для запуска на 32- и 64-разрядных хостах, вам придется перепрыгивать через обручи, чтобы заставить работать код с поздней привязкой. Я бы рекомендовал работать с ранним связыванием с 64-битной средой, если вы работаете на 64-битном хосте, и распространять отдельную копию макроса, который ссылается на 32-битную платформу для 32-битных хостов. Немного труднее распространять, но менее болезненно, чем заставить работать код с поздним связыванием.