Я передаю ключ типа string
Но вы не :) Вы фактически передаете объект Range
здесь:
If Not dMap.Exists(lo.ListColumns("Valid Statuses").DataBodyRange(i))
Случается, что в большинстве случаев (например, при присваивании - a Range) объект Range
возвращает свое свойство по умолчанию (Cells
), которое возвращаетего свойство по умолчанию (Value
), но когда получает Range, это не так надежно.
Вы наткнулись на интересный случай!Хотя Keys
в словаре может на что угодно , кроме массива, как вы невольно заметили, могут возникнуть странные вещи, когда вы используете сложные объекты как Keys
.
A Dictionary
объект является эквивалентом ассоциативного массива PERL.Элементы, которые могут быть любой формы данных, хранятся в массиве.Каждый элемент связан с уникальным ключом.Ключ используется для извлечения отдельного элемента и обычно представляет собой целое число или строку, но может быть чем угодно, кроме массива.
Вот странность: каждый раз, когда вы ссылаетесь на объект Range
, этоучитывая другое место в памяти.Вы можете легко это проверить:
Dim i as Long
Dim r as Range
For i = 1 to 3
Set r = Range("A1")
Debug.Print(ObjPtr(r))
Next
В вашем случае это означает, что метод Exists
будет явно терпеть неудачу (т.е. возвращает False
, когда он, по-видимому, должен вернутьсяTrue
), например, расширив вышесказанное:
Dim i As Long
Dim r As Range
Dim d as Object
Set d = CreateObject("Scripting.Dictionary")
For i = 1 to 3
Set r = Range("A1")
d.Add r, i
Next
Это даст словарь с 3 уникальными ключами, которые все являются указателями на один и тот же Range
объект!
Если вместо этого присваивание Set
равно за пределами цикла, оно завершится с ошибкой, как и ожидалось на второй итерации, поскольку теперь он пытается добавить указатель, который уже существует в словаре.
Set r = Range("A1")
For i = 1 to 3
d.Add r, i
Next
Мораль этой истории такова: будьте осторожны при назначении Object
типов как Keys
в Dictionary
.
Можем ли мы использовать Object
типов как Dictionary.Keys
?
Да, но это, кажется, более сложная реализация.Могут быть и другие способы, но одно очевидное (по крайней мере для меня) решение - сначала создать ключи как массив или коллекцию, а затем выполнить итерацию по этому списку во время тестирования Dictionary.Exists
.
Sub foo2()
Dim d As Dictionary
Dim r As Range, w As Worksheet
Dim i As Long
Dim keys(1 To 3) As Range
Set d = CreateObject("Scripting.Dictionary")
Set r = Range("A1:A3")
' Create our keys in one place
For i = 1 To r.Cells.Count
Set keys(i) = r.Cells(i)
Next
' Iterate the KEYS rather than the range
For i = LBound(keys) To UBound(keys)
d.Add keys(i), i
Next
Debug.Print "The dictionary initially contains " & d.Count & " keys."
'If we test Exist against our keys array, results are as expected:
For i = LBound(keys) To UBound(keys)
If Not d.Exists(keys(i)) Then
d.Add r.Cells(i), i
End If
Next
Debug.Print "The dictionary still contains " & d.Count & " keys."
' Iterate the RANGE now and no error occurs!
For i = 1 To r.Cells.Count
If Not d.Exists(r.Cells(i)) Then
d.Add r.Cells(i), i
End If
Next
' But, our dictionary now has 6 keys, instead of 3!!!
Debug.Print "The dictionary now contains " & d.Count & " keys!"
End Sub