在Excel VBA中,检查一个键是否在集合中的通用方法。
在Excel VBA中,检查一个键是否在集合中的通用方法。
我的代码中有不同的集合。一些保存对象(各种各样的对象),而其他集合中包含类型(例如Long)。\n有没有办法检查集合中是否包含某个键,既适用于类型也适用于对象?\n到目前为止,我有两个函数。\n第一个函数:\n
Private Function ContainsObject(objCollection As Object, strName As String) As Boolean Dim o As Object On Error Resume Next Set o = objCollection(strName) ContainsObject = (Err.Number = 0) Err.Clear End Function
\n第二个函数:\n
Private Function ContainsLong(AllItems As Collection, TheKey As String) As Boolean Dim TheValue As Long On Error Resume Next TheValue = AllItems.Item(TheKey) ContainsLong = (Err.Number = 0) Err.Clear End Function
\n之所以有两个函数,是因为如果我传递一个包含Long键值对的集合给ContainsObject函数,它似乎不起作用(该函数总是返回False)。\n附注:第一个函数是从Test or check if sheet exists的第三个答案中复制的。
问题的出现原因是在Excel VBA中,需要一种通用的方法来检查一个键是否存在于一个集合中。而解决方法是使用IsObject函数和错误处理来检查键是否存在于集合中。
以下是整理后的文章:
在Excel VBA中,我们经常需要检查一个键是否存在于一个集合中。然而,Excel VBA并没有提供一个直接的方法来实现这个功能。因此,我们需要自己编写一个通用的方法来解决这个问题。
首先,让我们来看一下一个错误的解决方法。Robin提供的答案是错误的,因为它不适用于通用对象。但是,它在Excel的Range对象中可以正常工作,因为Range对象会返回单元格的值。
接下来,让我们来看一下一个正确的解决方法。Apostle提供了一个使用IsObject函数的方法来检查键是否存在于集合中,并且我也认同这个方法。然而,这段代码有点过于复杂。
下面是一个简化的版本的代码:
Function HasKey(col As Collection, Key As String) As Boolean Dim v As Variant On Error Resume Next v = IsObject(col.Item(Key)) HasKey = Not IsEmpty(v) End Function
这段代码的原理是,如果键存在于集合中,IsObject函数会将变量v设置为True或False,否则将会发生错误,并且错误会被忽略,导致变量v为空。
最后,通过检查变量v是否为空,我们可以确定键是否存在于集合中。如果变量v不为空,则说明键存在于集合中,函数返回True;否则,返回False。
使用这个通用的方法,我们可以方便地检查一个键是否存在于一个集合中,从而更好地控制我们的Excel VBA代码。
在Excel VBA中,检查一个键是否在集合中有通用的方法。使用键在集合中有以下三个主要优点:
- 如果顺序发生变化,你的代码仍然可以访问正确的项
- 您可以直接访问该项,而无需阅读整个集合
- 它可以使您的代码更易读
但是,使用键在集合中也有三个主要问题:
- 无法检查键是否存在
- 无法更改键
- 无法检索键
根据Pearson的文章,集合的键是只写的,没有办法获取集合的现有键的列表。可以使用第二个集合来存储存储在Coll Collection中的CFile对象的键。我们需要这个第二个集合,因为集合的键是只写的,没有办法获取集合的现有键的列表。CFiles提供的其中一个改进是能够检索集合的键列表。
解决方法之一是遍历集合的成员,查看是否存在您要查找的内容,另一种方法是捕获"Item not in collection"错误,然后设置一个标志来表示该项不存在。对于这些方法,人们的意见不一。有些人认为捕获错误不是一种好的方法,而其他人则认为与迭代相比,对于任何中等到大型集合,捕获错误的方法会更快。
因此,如果我们选择捕获错误的方法,那么我们得到的错误号取决于错误的具体原因。我们需要一个检查错误的代码例程。最简单的方法是:
'c1 is the collection For i = 1 To c1.Count Debug.Print Err.Number, Err.Description If Err.Number <> 0 Then Err.Clear Next i
各种专业人士提出的捕获错误例程在考虑重要的错误号时有所不同。与集合对象相关的各种常见错误号包括:
- 错误5:无效的过程调用或参数。如果尝试调用当前平台上无效的过程,也可能发生此错误。例如,某些过程可能仅对Microsoft Windows或Macintosh等平台有效。
- 错误438:"对象不支持此属性或方法"。对象是类实例。类实例支持类类型定义中定义的一些属性,并且不支持这个属性。
- 错误457:该键已与此集合的一个元素关联。您为已经标识集合的另一个成员的集合成员指定了一个键。为该成员选择一个不同的键。
- 错误91:对象变量或With块变量未设置。创建对象变量有两个步骤。首先,您必须声明对象变量。然后,您必须使用Set语句为对象变量分配一个有效的引用。您尝试使用尚未引用有效对象的对象变量。
- 错误450:参数数量错误或无效的属性赋值。调用过程中的参数数量与过程所需的参数数量不同。如果尝试将值分配给只读属性,则会出现此错误。
在上述错误中,错误号438被认为很重要,另一个是5。我在我的示例测试程序中使用了Mark Nold在2008年发布的一种函数例程,该例程在SO问题"Determining whether an object is a member of a collection in VBA"中提到,并对他表示感谢。
某些错误,如错误457,在程序测试运行时将不被允许。我尝试使用重复的键数据来填充,它在程序测试时就会出错,如快照所示。
删除之后,它显示正确的输出,如快照所示。
对于一个普通的集合来说,可能无法获取集合的键列表,除非在独立数组中存储键值。最简单的替代方法是添加对Microsoft Scripting Runtime的引用,并使用一个更强大的字典。
我在我的程序中包含了这种获取键列表的方法。
在填充集合时,必须确保键是第二个参数,并且必须是唯一的字符串。
完整的程序代码如下:
Sub Generic_key_check() Dim arr As Variant Dim c1 As New Collection Dim dic As Object With Application .ScreenUpdating = False End With Set dic = CreateObject("Scripting.Dictionary") dic.CompareMode = vbTextCompare 'Populate the collection c1.Add "sheet1", "sheet1" c1.Add "sheet2", "sheet2" c1.Add "sheet3", "sheet3" c1.Add "sheet4", "sheet4" c1.Add "sheet5", "sheet5" c1.Add 2014001, "Long1" c1.Add 2015001, "Long2" c1.Add 2016001, "Long3" c1.Add 2015002, "Long4" c1.Add 2016002, "Long5" 'Populate the dictionary dic.Add "sheet1", "sheet1" dic.Add "sheet2", "sheet2" dic.Add "sheet3", "sheet3" dic.Add "sheet4", "sheet4" dic.Add "sheet5", "sheet5" dic.Add "Long1", 2014001 dic.Add "Long2", 2015001 dic.Add "Long3", 2016001 dic.Add "Long4", 2015002 dic.Add "Long5", 2016002 ' Get a list of key items by Dictionary Method Dim N As Variant For Each N In dic.Keys Debug.Print "Key: " & N, "Value: " & dic.item(N) Next 'Test for two types of data whether key exists or not. If InCollection(c1, "Long1") Then 'If Exists("Long1", c1) Then Debug.Print "Good" Else ' If there is error then print out the error number and its description. Debug.Print Err.Number, Err.Description Debug.Print "Not Good" End If If InCollection(c1, "sheet2") Then Debug.Print "Good" Else Debug.Print Err.Number, Err.Description Debug.Print "Not Good" End If 'Checking whether desired key has populated correctly Debug.Print c1("Sheet1") Debug.Print c1("Long3") 'Listing out collection items to check theyexist in the collection. For i = 1 To c1.Count Debug.Print c1.item(i) Next i With Application .ScreenUpdating = True End With Set c1 = Nothing End Sub 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
程序的最终输出如下所示:
Key: sheet1, Value: sheet1 Key: sheet2, Value: sheet2 Key: sheet3, Value: sheet3 Key: sheet4, Value: sheet4 Key: sheet5, Value: sheet5 Key: Long1, Value: 2014001 Key: Long2, Value: 2015001 Key: Long3, Value: 2016001 Key: Long4, Value: 2015002 Key: Long5, Value: 2016002 Good Good Sheet1 2016001 sheet1 sheet2 sheet3 sheet4 sheet5 2014001 2015001 2016001 2015002 2016002
对于显示了Scripting Runtime库中Dictionary的应用程序,给予+1。真遗憾这不是Excel VBA的默认部分。
在Excel VBA中,检查集合中是否存在一个键的通用方法是使用Variant类型。可以将一个对象分配给Variant类型,例如,下面的代码不会出错:
Sub Test() Dim var As Variant Dim obj As Object Set obj = Application var = Application Debug.Print var End Sub
但是,下面的代码会出现“类型不匹配”(Type Mismatch)的编译错误,即试图将Long类型赋值给一个Object类型:
Sub Test() Dim obj As Object Dim lng As Long lng = 3 Set obj = lng End Sub
因此,为了实现一个通用的函数来检查Collection中的键是否有效,可以使用以下代码:
Function HasKey(coll As Collection, strKey As String) As Boolean Dim var As Variant On Error Resume Next var = coll(strKey) HasKey = (Err.Number = 0) Err.Clear End Function
测试代码如下:
Sub Test() Dim coll1 As New Collection coll1.Add Item:=Sheet1.Range("A1"), Key:="1" coll1.Add Item:=Sheet1.Range("A2"), Key:="2" Debug.Print HasKey(coll1, "1") Dim coll2 As New Collection coll2.Add Item:=1, Key:="1" coll2.Add Item:=2, Key:="2" Debug.Print HasKey(coll2, "1") End Sub
在MSDN上有一篇有关此问题的有用文章。虽然上下文是VB6,但与VBA相关联。
对于您明确且直截了当的答案,我已经点赞了。
- 感谢您的答案建议使用Dictionary对象,我同意这是首选的方法。OP正在使用Collection对象,可能没有机会切换到Dictionary。正如他们所说,不总是可以“中途换马”。