У меня есть этот упрощенный класс с именем clsWarehouseSum.
Option Compare Database
Option Explicit
Private wh_units As Scripting.Dictionary
Public Function availableUnits(warehouse As String) As Long
'Debug.Print wh_units(warehouse)
If wh_units Is Nothing Then Set wh_units = New Scripting.Dictionary
If Not wh_units.Exists(warehouse) Then
Dim SQL As String
Dim RS As DAO.Recordset
SQL = "SELECT sum(units) as tot_units " _
& "FROM warehouse " _
& "WHERE warehouse = '" & warehouse & "' "
Set RS = CurrentDb.OpenRecordset(SQL)
wh_units.Add (warehouse), RS("tot_units")
End If
availableUnits = wh_units(warehouse)
End Function
Я пытаюсь использовать это так:
Sub test()
Dim wh As New clsWarehouseSum
Debug.Print wh.availableUnits("Cohasset")
Debug.Print wh.availableUnits("Cohasset")
End Sub
В то время как первый Debug.Print печатает то, что ожидается, второй выдает ошибку:
Run time error 3420, Object Invalid or no longer set.
Когда я выполняю код, он правильно оценивает оба утверждения if
как false
. Тем не менее, последняя строка функции дает мне упомянутую выше ошибку. Что я делаю не так?
Почему?
Поскольку словарь является частным для класса, пусть класс управляет своим состоянием. Это означает, что словарь должен быть создан в методе конструктора Class_Initialize()
и ничего не установлен в методе деструктора Class_Terminate()
.
Кристиан, ты был прав. Тип был Field3
. Почему это было? Обобщаемое поле — Long
. Но в любом случае функция CLng
решила проблему. Пожалуйста, вставьте это в ответ, чтобы я мог вознаградить вас правильным ответом
Словарь может содержать несколько типов вещей, поэтому, когда вы добавляете RS(“tot units”)
, вы добавляете объект Field, а не значение этого поля. Вам нужно быть более явным и использовать RS(“tot units”).Value
@user2395238 user2395238 Возможно, Field3
— это просто оболочка или, что еще более вероятно, вы используете позднее связывание, что вызывает проблемы с получением правильных интерфейсов. Я удалил комментарий и разместил его как ответ. Рад, что вы решили это.
Ошибка Object Invalid or no longer set
звучит для меня так, как будто RS("tot_units")
будет не само значение, а ссылка. Поэтому во второй раз, когда вы вызываете wh.availableUnits("Cohasset")
, эта ссылка не устанавливается (поскольку RS
является локальной переменной и становится недоступной, когда выходит за рамки). Так что проверяйте, что вы на самом деле добавляете с помощью RS("tot_units")
. Вам нужно добавить само значение Long
... которое вы, вероятно, и так знаете :).
Добавьте Debug.Print TypeName(wh_units(warehouse))
перед строкой availableUnits = wh_units(warehouse)
, и если в окне Immediate будет напечатано что-то еще, кроме Long
, вы можете захотеть выполнить приведение к Long
с помощью CLng
, хотя у вас также есть какой-то обработчик ошибок.
Или вы можете убедиться, что строка wh_units.Add (warehouse), RS("tot_units")
добавляет Long
в ваш словарь, поэтому вам следует проверить тип, прежде чем добавлять.
Как правило, когда вы возвращаете определенный тип данных из словаря или коллекции, у вас всегда должны быть проверки либо при добавлении данных в dict/coll, либо при их возврате, чтобы избежать несовместимости типов и ошибок времени выполнения. .
Он отлично работает для меня, используя ваш точный код (сокращение SQL, чтобы просто добавить 5). Обновлено: я также изменил dict на объект и использовал
CreateObject("Scripting.Dictionary")
, потому что мне лень добавлять ссылку.