【问题标题】:Acces an item in a dictionary with a custom object key in vba使用 vba 中的自定义对象键访问字典中的项目
【发布时间】:2015-09-24 12:02:55
【问题描述】:

我在 vba 中访问字典中的项目时遇到问题。

我有以下字典:

Dim CResults as dictionary

我添加项目:

CResults.add currentkey, result

Currentkey 是我自己从一个名为 DCRkey 的类中创建的对象

Private loadcase as long
Private csystem as String
Private node as long

result 是来自名为DCR 的类的另一个对象:

Private Fs as double
Private C1 as double
Private C2 as double
...

然后我尝试使用

访问项目
Dim accesskey as DCRKey
accesskey.loadcase=10
accesskey.node = 2000
accesskey.csystem="Global"
Sheets("Results").cells(i,1).value= CResults(accesskey).C1

这就是我遇到错误的地方:runtime error 424 object required

然后我想也许我搜索的键和项目没有导入,所以我决定在 Excel 工作表上显示我的整个字典:

Dim testkey as variant
dim i as integer
i=1
with worksheet("Test")
    For each testkey in CResults.keys
        .cells(i,1)=test.node
        .cells(i,2)=test.loadcase
        .cells(i,3)=test.csystem
        .cells(i,4)=Cresults(testkey).C1
        .cells(i,5)=Cresults(testkey).Fs
        if accesskey.loadcase=testkey.loadcase and accesskey.node=testkey.node and accesskey.csystem=testkey.csystem then
            Msgbox "This key is the same as the accesskey"
        End if
        i=i+1
    Next
End with

我看到的是:

  1. 我之前搜索的键存在于字典中:在工作表上进行目视检查
  2. 我之前搜索的键确实存在于字典中:"This key is the same as the acceskey" 显示一次
  3. for each 循环访问字典中的项目是可行的,因为 C1 和 Fs 在工作表上正确显示

然后我想可能是因为testkey被定义为一个变体而不是DCRKey,所以我尝试了:

dim a as variant
Set a = currentkey
.Cells(i,1) = CResults(a).C1

但它不起作用,我仍然得到runtime error 424

我也尝试过:

CResults.exists(accesskey)

它返回 false 并在字典中创建一个新条目(顺便说一句,我讨厌这样做),使用与 acceskey 相同的键和一个空项。

所以我的问题是:为什么使用自定义类型键访问项目在 for each 循环中而不是在独立调用中有效。我错过了什么?该代码与我编写的代码非常相似,但并不完全相同(为了您更好地理解)。告诉我您是否认为真正的代码可以提供帮助。 感谢您的帮助。

【问题讨论】:

  • 真正的代码总是比你实际使用的代码要好得多。 ;)
  • 一个直接的问题:您是否使用 same object 作为访问该项目的键,就像您将它添加到字典时使用的那样?如果没有,您将收到该错误。
  • 我不确定我是否理解你的问题。关键是DCRKey 实例化项目本身是DCR 实例化,我并没有真正访问项目本身,而是通过 CResults(accesskey).C1 直接访问其属性之一。C1
  • 查看 mielk 的回答。这就是我要问的 - 我认为您正在使用 DCRKey 类的不同实例来访问该项目。它必须是您在添加项目时用作键的同一实例
  • 我刚看到。我现在明白了谢谢。

标签: vba excel dictionary key


【解决方案1】:

您需要记住,一个类的两个实例不是同一个,即使它们的所有属性都设置为相同的值。

让我们考虑下面的例子:

Sub compareSimilarObjects()

    Dim key1 As DCRKey
    Dim key2 As DCRKey

    Set key1 = New DCRKey
    With key1
        .loadcase = 10
        .node = 2000
        .csystem = "Global"
    End With

    Set key2 = New DCRKey
    With key1
        .loadcase = 10
        .node = 2000
        .csystem = "Global"
    End With


    'Debug.Print to check pointer assigne to those variables.
    Debug.Print "Key1: " & ObjPtr(key1)
    Debug.Print "Key2: " & ObjPtr(key2)

End Sub

在此示例中,DCRKey 类的两个对象都将所有属性设置为相同的值。但是,它们并不是您在下面的代码最后运行Debug.Prints 后看到的对象。

在那些Debug.Print 中使用了VBA 内置函数ObjPtr。此函数的目的是返回指向给定对象的指针。每个对象实例都有自己唯一的指针,因此如果下面的代码打印了两个不同的指针,则表示这些对象并不相同。


现在,让我们考虑另一个例子:

Sub compareSimilarObjects()
    Dim key1 As DCRKey
    Dim key2 As DCRKey

    Set key1 = New DCRKey
    With key1
        .loadcase = 10
        .node = 2000
        .csystem = "Global"
    End With

    Set key2 = key1


    'Debug.Print to check pointer assigned to those variables.
    Debug.Print "Key1: " & ObjPtr(key1)
    Debug.Print "Key2: " & ObjPtr(key2)
    'Now those pointers should be the same.

End Sub

在这里,我们将类DCRKey 的新实例分配给变量key1,然后我们将相同的对象分配给变量key2。现在ObjPtr 应该为key1key2 返回相同的值,因为这是同一个对象,它只是分配给了两个不同的变量。


现在,让我们回到字典。

字典查找Object类型键的方式是通过它的指针。

因此,如果您想在字典中查找以对象作为键添加的条目,则需要使用完全相同的对象(而不是具有相同属性的对象)。

例子:

Sub objectsToDictionaryTest()
    Dim CResults As Dictionary
    Dim accessKey As DCRKey
    Dim key As DCRKey
    Dim value As DCR
    '--------------------------------------------------------------------------------


    Set CResults = New Scripting.Dictionary


    'Let's create an object of [DCRKey] class (it will be used as a key when adding to
    'the dictionary) and an object of [DCR] class (it will be used as a value).
    Set accessKey = New DCRKey
    With accessKey
        .loadcase = 10
        .node = 2000
        .csystem = "Global"
    End With

    Set value = New DCR
    With value
        .C1 = 10
        .C2 = 20
        .Fs = 3
    End With


    'Now, let's add a new entry to the dictionary [CResults]
    CResults.Add accessKey, value


    'Let's create the other object of [DCRKey] that have exactly the same properties
    'as object assigned to the variable [accessKey].
    Set key = New DCRKey
    With key
        .loadcase = 10
        .node = 2000
        .csystem = "Global"
    End With



    'Now, let's check how dictionary is acting when we try to find an entry by [accesKey] and [key].
    Debug.Print "[accessKey] exists: " & CResults.Exists(accessKey)         'it should return True.
    Debug.Print "[key] exists: " & CResults.Exists(key)                     'it should return False.

    Debug.Print "[Value for accessKey]: " & CResults.Item(accessKey).Fs     'it should print 3

    'The line below should cause an run-time error 424: Object required.
    Debug.Print "[Value for key]: " & CResults.Item(key).Fs


End Sub

【讨论】:

  • CResults.Exists(key) 应该返回False,不是吗?
  • 非常感谢。很详细的回答。不确定我是否喜欢它的含义。在我的例子中,我使用字典来存储信息,然后在不同的情况下从不同的模块访问它。这意味着我必须找到一种方法将指针一直保存在内存中,并且以后无法重新创建具有所需值的键。我不明白我怎么能做到这一点。这是我第一次使用对象作为键,对于这种特殊情况,我需要 3 个键。 DCR(位移、电荷、反应)适用于一个节点、一个载荷工况和一个坐标系。我得想个办法。有什么想法吗?
  • 也许我可以使用coordinatesystem & " " & Cstr(loadcase) & " " & cstr(Node) 之类的字符串作为键,而不是使用 CDRKey 对象。这应该有效,不是吗?我先试试看。
  • 是的 - 我通常会将项目(带有分隔符)连接成一个唯一的字符串。
  • @Matt 如果你真的想使用一个对象作为键,也许你可以使用嵌套字典。作为免责声明,我从来没有用对象尝试过这个,但我知道它可以用类型来完成。例如,让 Dict1 包含一个带有对象引用的整数键。 Dict2 可以将对象引用作为键,将相应的引用作为它自己的值。调用Dict2(Dict1(1))从dict2返回对应于dict1中的对象的值,键为1
猜你喜欢
  • 1970-01-01
  • 2011-10-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-22
相关资源
最近更新 更多