Определение, является ли объект членом коллекции в VBA
Как определить, является ли объект членом коллекции в VBA?
В частности, мне нужно выяснить, является ли определение таблицы членом TableDefs
коллекция.
16 ответов
Лучше всего перебрать членов коллекции и посмотреть, соответствует ли то, что вы ищете. Поверь мне, мне приходилось делать это много раз.
Второе решение (что намного хуже) состоит в том, чтобы отловить ошибку "Предмет не в коллекции", а затем установить флаг, чтобы сказать, что предмет не существует.
Разве это не достаточно хорошо?
Public Function Contains(col As Collection, key As Variant) As Boolean
Dim obj As Variant
On Error GoTo err
Contains = True
obj = col(key)
Exit Function
err:
Contains = False
End Function
Не совсем элегантно, но лучшим (и самым быстрым) решением, которое я смог найти, было использование OnError. Это будет значительно быстрее, чем итерация для любой средней и большой коллекции.
Public Function InCollection(col As Collection, key As String) As Boolean
Dim var As Variant
Dim errNumber As Long
InCollection = False
Set var = Nothing
Err.Clear
On Error Resume Next
var = col.Item(key)
errNumber = CLng(Err.Number)
On Error GoTo 0
'5 is not in, 0 and 438 represent incollection
If errNumber = 5 Then ' it is 5 if not in collection
InCollection = False
Else
InCollection = True
End If
End Function
Это старый вопрос. Я внимательно рассмотрел все ответы и комментарии, проверил решения на производительность.
Я придумал самый быстрый вариант для моей среды, который не выходит из строя, когда в коллекции есть как объекты, так и примитивы.
Public Function ExistsInCollection(col As Collection, key As Variant) As Boolean
On Error GoTo err
ExistsInCollection = True
IsObject(col.item(key))
Exit Function
err:
ExistsInCollection = False
End Function
Кроме того, это решение не зависит от жестко закодированных значений ошибок. Итак, параметр col As Collection
может быть заменен некоторой другой переменной типа коллекции, и функция все еще должна работать. Например, в моем текущем проекте он будет col As ListColumns
,
Вы можете сократить предложенный код для этого, а также обобщить для непредвиденных ошибок. Ну вот:
Public Function InCollection(col As Collection, key As String) As Boolean
On Error GoTo incol
col.Item key
incol:
InCollection = (Err.Number = 0)
End Function
Я создал это решение из вышеупомянутых предложений, смешанных с решением Microsoft для итерации по коллекции.
Public Function InCollection(col As Collection, Optional vItem, Optional vKey) As Boolean
On Error Resume Next
Dim vColItem As Variant
InCollection = False
If Not IsMissing(vKey) Then
col.item vKey
'5 if not in collection, it is 91 if no collection exists
If Err.Number <> 5 And Err.Number <> 91 Then
InCollection = True
End If
ElseIf Not IsMissing(vItem) Then
For Each vColItem In col
If vColItem = vItem Then
InCollection = True
GoTo Exit_Proc
End If
Next vColItem
End If
Exit_Proc:
Exit Function
Err_Handle:
Resume Exit_Proc
End Function
Меня устраивает
Public Function contains(col As Collection, key As Variant) As Boolean
For Each element In col
If (element = key) Then
contains = True
Exit Function
End If
Next
contains = False
End Function
Для случая, когда ключ не используется для сбора:
Public Function Contains(col As Collection, thisItem As Variant) As Boolean
Dim item As Variant
Contains = False
For Each item In col
If item = thisItem Then
Contains = True
Exit Function
End If
Next
End Function
У меня есть некоторые изменения, лучше всего работает для коллекций:
Public Function Contains(col As collection, key As Variant) As Boolean
Dim obj As Object
On Error GoTo err
Contains = True
Set obj = col.Item(key)
Exit Function
err:
Contains = False
End Function
В вашем конкретном случае (TableDefs) итерация коллекции и проверка имени - это хороший подход. Это нормально, потому что ключ для коллекции (Name) является свойством класса в коллекции.
Но в общем случае коллекций VBA ключ не обязательно будет частью объекта в коллекции (например, вы можете использовать коллекцию в качестве словаря с ключом, который не имеет ничего общего с объектом в коллекции). В этом случае у вас нет выбора, кроме как попытаться получить доступ к элементу и отловить ошибку.
Эта версия работает для примитивных типов и для классов (включая короткий тест-метод)
' TODO: change this to the name of your module
Private Const sMODULE As String = "MVbaUtils"
Public Function ExistsInCollection(oCollection As Collection, sKey As String) As Boolean
Const scSOURCE As String = "ExistsInCollection"
Dim lErrNumber As Long
Dim sErrDescription As String
lErrNumber = 0
sErrDescription = "unknown error occurred"
Err.Clear
On Error Resume Next
' note: just access the item - no need to assign it to a dummy value
' and this would not be so easy, because we would need different
' code depending on the type of object
' e.g.
' Dim vItem as Variant
' If VarType(oCollection.Item(sKey)) = vbObject Then
' Set vItem = oCollection.Item(sKey)
' Else
' vItem = oCollection.Item(sKey)
' End If
oCollection.Item sKey
lErrNumber = CLng(Err.Number)
sErrDescription = Err.Description
On Error GoTo 0
If lErrNumber = 5 Then ' 5 = not in collection
ExistsInCollection = False
ElseIf (lErrNumber = 0) Then
ExistsInCollection = True
Else
' Re-raise error
Err.Raise lErrNumber, mscMODULE & ":" & scSOURCE, sErrDescription
End If
End Function
Private Sub Test_ExistsInCollection()
Dim asTest As New Collection
Debug.Assert Not ExistsInCollection(asTest, "")
Debug.Assert Not ExistsInCollection(asTest, "xx")
asTest.Add "item1", "key1"
asTest.Add "item2", "key2"
asTest.Add New Collection, "key3"
asTest.Add Nothing, "key4"
Debug.Assert ExistsInCollection(asTest, "key1")
Debug.Assert ExistsInCollection(asTest, "key2")
Debug.Assert ExistsInCollection(asTest, "key3")
Debug.Assert ExistsInCollection(asTest, "key4")
Debug.Assert Not ExistsInCollection(asTest, "abcx")
Debug.Print "ExistsInCollection is okay"
End Sub
Это требует некоторых дополнительных настроек в случае, если элементы в коллекции не Объекты, а Массивы. Кроме этого он работал нормально для меня.
Public Function CheckExists(vntIndexKey As Variant) As Boolean
On Error Resume Next
Dim cObj As Object
' just get the object
Set cObj = mCol(vntIndexKey)
' here's the key! Trap the Error Code
' when the error code is 5 then the Object is Not Exists
CheckExists = (Err <> 5)
' just to clear the error
If Err <> 0 Then Call Err.Clear
Set cObj = Nothing
End Function
Источник: http://coderstalk.blogspot.com/2007/09/visual-basic-programming-how-to-check.html
Я использовал этот код для преобразования массива в коллекцию и обратно в массив для удаления дубликатов, собранных из различных сообщений здесь (извините, что не дал должного кредита).
Function ArrayRemoveDups(MyArray As Variant) As Variant
Dim nFirst As Long, nLast As Long, i As Long
Dim item As Variant, outputArray() As Variant
Dim Coll As New Collection
'Get First and Last Array Positions
nFirst = LBound(MyArray)
nLast = UBound(MyArray)
ReDim arrTemp(nFirst To nLast)
i = nFirst
'convert to collection
For Each item In MyArray
skipitem = False
For Each key In Coll
If key = item Then skipitem = True
Next
If skipitem = False Then Coll.Add (item)
Next item
'convert back to array
ReDim outputArray(0 To Coll.Count - 1)
For i = 1 To Coll.Count
outputArray(i - 1) = Coll.item(i)
Next
ArrayRemoveDups = outputArray
End Function
Не мой код, но я думаю, что он довольно хорошо написан. Он позволяет выполнять проверку как по ключу, так и по самому элементу Object и обрабатывает как метод On Error, так и итерацию по всем элементам Collection.
https://danwagner.co/how-to-check-if-a-collection-contains-an-object/
Я не буду копировать полное объяснение, так как оно доступно на связанной странице. Само решение копируется на случай, если страница в конечном итоге станет недоступной в будущем.
Я сомневаюсь в коде из-за чрезмерного использования GoTo в первом блоке If, но это легко исправить для любого, поэтому я оставляю исходный код таким, какой он есть.
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'INPUT : Kollection, the collection we would like to examine
' : (Optional) Key, the Key we want to find in the collection
' : (Optional) Item, the Item we want to find in the collection
'OUTPUT : True if Key or Item is found, False if not
'SPECIAL CASE: If both Key and Item are missing, return False
Option Explicit
Public Function CollectionContains(Kollection As Collection, Optional Key As Variant, Optional Item As Variant) As Boolean
Dim strKey As String
Dim var As Variant
'First, investigate assuming a Key was provided
If Not IsMissing(Key) Then
strKey = CStr(Key)
'Handling errors is the strategy here
On Error Resume Next
CollectionContains = True
var = Kollection(strKey) '<~ this is where our (potential) error will occur
If Err.Number = 91 Then GoTo CheckForObject
If Err.Number = 5 Then GoTo NotFound
On Error GoTo 0
Exit Function
CheckForObject:
If IsObject(Kollection(strKey)) Then
CollectionContains = True
On Error GoTo 0
Exit Function
End If
NotFound:
CollectionContains = False
On Error GoTo 0
Exit Function
'If the Item was provided but the Key was not, then...
ElseIf Not IsMissing(Item) Then
CollectionContains = False '<~ assume that we will not find the item
'We have to loop through the collection and check each item against the passed-in Item
For Each var In Kollection
If var = Item Then
CollectionContains = True
Exit Function
End If
Next var
'Otherwise, no Key OR Item was provided, so we default to False
Else
CollectionContains = False
End If
End Function
Я сделал это следующим образом, вариант кода Vadims, но для меня он немного более читабелен:
' Returns TRUE if item is already contained in collection, otherwise FALSE
Public Function Contains(col As Collection, item As String) As Boolean
Dim i As Integer
For i = 1 To col.Count
If col.item(i) = item Then
Contains = True
Exit Function
End If
Next i
Contains = False
End Function
Я написал этот код. Я думаю, это может помочь кому-то...
Public Function VerifyCollection()
For i = 1 To 10 Step 1
MyKey = "A"
On Error GoTo KillError:
Dispersao.Add 1, MyKey
GoTo KeepInForLoop
KillError: 'If My collection already has the key A Then...
count = Dispersao(MyKey)
Dispersao.Remove (MyKey)
Dispersao.Add count + 1, MyKey 'Increase the amount in relationship with my Key
count = Dispersao(MyKey) 'count = new amount
On Error GoTo -1
KeepInForLoop:
Next
End Function