【问题标题】:Error 438 Object doesn't support this property or method - Class Object with Dictionary错误 438 对象不支持此属性或方法 - 带有字典的类对象
【发布时间】:2020-03-09 16:02:42
【问题描述】:

背景:

这是对this 最近的一个问题的后续问题,我询问了如何直接从Dictionary 项返回一组Class 模块属性。

我现在已经尝试使用Property LetProperty Get 填充Private Array 来填充Dictionary。但是,在运行一些测试时我遇到了Error 438

代码:

TstClass想象成一个类模块,代码如下:

Private lst(0 To 2) As Variant

Public Property Let Add(ByVal i As Long, ByVal NewVal As Variant)
    lst(i) = NewVal
End Property
Public Property Get Val(ByVal i As Long) As Variant
    Val = lst(i)
End Property

Public Function GetArray() As Variant
    GetArray = vals
End Function

然后这段代码在一个模块中进行测试:

Sub Test()

Dim x As Long, arr As Variant, lst As Class1
Dim dict As Object: Set dict = CreateObject("Scripting.Dictionary")

For x = 1 To 3
    Set lst = New Class1
    lst.Add(0) = x
    lst.Add(1) = x
    lst.Add(2) = x
    dict.Add x, lst
Next x

For x = 4 To 3 Step -1
    If dict.Exists(x) = False Then
        Set lst = New Class1
        lst.Add(0) = x
        lst.Add(1) = x
        lst.Add(2) = x
        dict.Add x, lst
    Else
        Set lst = dict(x)
        lst.Add(1) = lst.Val(1) + 2
        lst.Add(2) = lst.Val(2) + 2
        dict(x) = lst '< Error 438 on this line
    End If
Next x

For Each key In dict.keys
    arr = dict(keys).GetArray
Next key

End Sub

问题:

dict(keyx) = lst 将出现错误 438,并告诉我对象(字典)不支持此 PropertyMethod。这个问题对我来说似乎很狡猾,因为lst 对象在dict.Add x, lst 上似乎不是问题。事实上,像这样通过Key更改Item的方法似乎是一种非常common的做法。

问题:

而像Dict.Add x, "Hello" 然后Dict(x) = "Hello World" 这样的东西似乎有效。在第二种方法中使用 Class 对象时,代码会出错。有谁知道为什么,如果知道,如何处理这个问题?

谢谢你, JvdV

【问题讨论】:

  • Set dict(keyx) = lst 因为lst 是一个对象
  • 这么简单......谢谢@TimWilliams。愿意张贴作为结束问题的答案吗?我在这上面花了几个小时:S
  • GetArray 不正确。您正在返回一个未声明的变量“vals”。如果您的意图是返回类的私有数组,那么您的代码应该是 getarray=lst。 dict(keyx) 可能应该是 'set dict.item(x)' (我不想省略默认属性)。 arr = dict(keys).GetArray 应该是 arr = dict.item(key).GetArray.

标签: arrays excel vba class dictionary


【解决方案1】:
Set dict(keyx) = lst 

由于变量lst指向一个对象,所以这里需要Set

【讨论】:

  • 没有声明为'keyx'的变量
  • @Freeflow - 是的 - 那也是!
【解决方案2】:

OP 代码有很多拼写错误、未声明的变量和可用但不“正确”的属性 Let/Get。这就是我编写操作代码的方式。现在已经通过出色的 RubberDuck 插件的代码检查得到了很好的训练,我不再使用“默认”属性,而是确保我使用完全限定的名称。

类代码

Option Explicit

Private Type Properties

    List                                As Variant

End Type

Private p                               As Properties


Public Sub Class_Initialize()

    ReDim p.List(2)

End Sub


Public Property Let Item(ByVal i As Long, ByVal NewVal As Variant)
    p.List(i) = NewVal
End Property

Public Property Get Item(ByVal i As Long) As Variant
    Item = p.List(i)
End Property

' 通常 VBA 在返回数组时使用“项目名称”的复数形式。

Public Function Items() As Variant
    Items = p.List
End Function

模块代码

Sub Test()

Dim x As Long, arr As Variant, lst As Class1
Dim dict As Object: Set dict = CreateObject("Scripting.Dictionary")

    For x = 1 To 3
        Set lst = New Class1
        lst.Item(0) = x
        lst.Item(1) = x
        lst.Item(2) = x
        dict.Add x, lst
    Next x

    For x = 4 To 3 Step -1
        If dict.Exists(x) = False Then
            Set lst = New Class1
            lst.Item(0) = x
            lst.Item(1) = x
            lst.Item(2) = x
            dict.Add x, lst
        Else

            With dict.Item(x)

                .Item(1) = lst.Item(1) + 2
                .Item(2) = lst.Item(2) + 2

            End With

        End If

    Next x

Dim myKey                       As Variant

    For Each myKey In dict.Keys
        arr = dict.Item(myKey).GetArray
    Next Key

End Sub

【讨论】:

  • 感谢您的反馈,我会在明天更多使用电脑时进行调查。干杯!顺便说一句,错别字是我的错。
【解决方案3】:

As Tim correctly points out, Set 在这里是必需的,因为lst 是一个对象。

不清楚的是,为什么缺少 Set 关键字会导致运行时错误 438,这种错误通常在有缺陷的后期绑定代码中遇到。总而言之,原因是 let-coercion:在没有 Set 关键字的情况下,赋值是隐式的 Let(值)赋值。

如果Class1 有一个默认成员dict(keyx) = lst 将强制转换lst 对象并将该默认成员返回的值存储在字典中。

由于Class1 没有默认成员(并且引用不是Nothing),因此会引发错误438(如果lstNothing,则会出现错误91),因为VBA 正在使用 VB_UserMemId=0 属性调用成员,但找不到它。

dict(keyx) = lst 将出现错误 438,并告诉我对象(Dictionary)不支持此属性或方法。

这里的对象不是字典,而是你的Class1 实例;缺少的“属性或方法”是该对象的默认成员,通过 let-coercion 隐式调用。而且由于成员调用是隐式的,很容易漏掉,而且错误号/消息容易混淆。

Rubberduck(我管理的免费、开源 VBIDE 插件项目)在此处触发Value Required 的检查结果,这可以准确解释发生了什么:

在需要值类型的上下文中,使用了对象类型“VBAProject.Class1”的表达式“lst”,它没有合适的默认成员。

在需要值时使用的对象

如果在需要值类型的地方使用对象并且对象的声明类型没有合适的默认成员,则 VBA 编译器不会引发错误。在几乎所有情况下,这都会导致运行时错误 91“对象或未设置块变量”或 438“对象不支持此属性或方法”,具体取决于对象是否具有值“无”,这更难检测并表明存在错误。

换句话说,这是 VBA 编译器推迟到运行时而不是在编译时警告一些可疑的地方的许多地方之一。因此,静态代码分析是在运行时出现之前可靠检测此类错误的唯一方法。

【讨论】:

  • 感谢 Mathieu 的详尽解释。
猜你喜欢
  • 2023-04-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多