1
votes

Erreur 438 L'objet ne prend pas en charge cette propriété ou méthode - Objet de classe avec dictionnaire

Background:

Ceci est une question de suivi sur cette question récente que j'ai posée sur la façon de renvoyer un tableau de propriétés du module Class directement à partir d'un élément Dictionary .

J'ai maintenant essayé ma façon de travailler avec Property Let et Property Get pour remplir un Private Array pour remplir le Dictionary avec. Cependant, en exécutant certains tests, j'ai rencontré une Erreur 438 .

Code: p>

Imaginez TstClass comme un module de classe avec le code suivant:

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

Puis ce code à tester dans un module: p >

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

Problème:

L'erreur 438 se produira sur dict (keyx) = lst et me dit que l'objet (un dictionnaire) ne prend pas en charge cette Propriété ou Méthode . Le problème me semble douteux car l'objet lst ne semble pas être un problème sur dict.Add x, lst . En fait, la méthode pour changer un Item via sa Key comme celle-ci semble être très pratique courante.

Question: p>

Alors que quelque chose comme Dict.Add x, "Hello" puis Dict (x) = "Hello World" semble fonctionner. Les erreurs de code lors de l'utilisation d'un objet Class dans la deuxième méthode. Quelqu'un sait-il pourquoi et si oui, comment résoudre ce problème?

Merci, JvdV


3 commentaires

Set dict (keyx) = lst puisque lst est un objet


C'est simple ... merci @TimWilliams. Voulez-vous poster comme réponse pour fermer la question? J'ai passé littéralement des heures là-dessus: S


GetArray est incorrect. Vous renvoyez une variable «vals» qui n'est pas déclarée. Si votre intention est de renvoyer le tableau privé de la classe, votre code doit être getarray = lst. dict (keyx) devrait probablement être 'set dict.item (x)' (je préfère ne pas omettre la propriété par défaut). Et arr = dict (clés) .GetArray devrait probablement être arr = dict.item (clé) .GetArray.


3 Réponses :


4
votes
Set dict(keyx) = lst 
Since the variable lst refers to an object, Set is required here.

2 commentaires

Il n'y a pas de variable déclarée comme «clé»


@Freeflow - Oui - cela aussi!



4
votes

Le code OP a pas mal de fautes de frappe, de variables non déclarées et de propriété Let / Get utilisable mais pas «correcte». C'est ainsi que j'écrirais le code de l'op. Ayant maintenant été bien formé par les inspections de code du fantastique addin RubberDuck, je n'utilise plus les propriétés `` par défaut '' mais je m'assure d'utiliser le nom complet.

Code de classe

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

'En général, VBA utilise le pluriel du' nom de l'élément 'lors du renvoi du tableau.

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

Code du module

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


1 commentaires

Merci pour vos commentaires, je l'examinerai quand je serai sur un PC demain plus. Acclamations! Btw, les fautes de frappe sont mon mauvais.



2
votes

Comme Tim le souligne correctement , Set est requis ici, car lst est un objet.

Ce qui n'est pas clair, c'est pourquoi un mot-clé Set manquant entraînerait une erreur d'exécution 438, une erreur généralement rencontrée avec un code à liaison tardive bogué. La raison est, en un mot, let-coercion : en l'absence de mot-clé Set , une affectation est un Let (valeur) implicite affectation.

Si Class1 avait un membre par défaut , dict (keyx) = lst contraindrait le lst objet et stocker la valeur renvoyée par ce membre par défaut dans le dictionnaire.

Puisque Class1 n'a pas de membre par défaut (et que la référence n'est pas Nothing ), l'erreur 438 est générée (ce serait l'erreur 91 si lst était Rien ), car VBA cherche à appeler le membre avec un attribut VB_UserMemId = 0 , et ne peut pas le trouver.

L'erreur 438 se produira sur dict (keyx) = lst et me dit que l'objet (un Dictionary ) ne prend pas en charge cette propriété ou méthode.

L'objet n'est pas le dictionnaire ici, mais votre instance de Class1 ; la "propriété ou méthode" manquante est le membre par défaut de cet objet, implicitement invoqué par let-coercion. Et comme l'appel du membre est implicite, il est facilement manqué, et le numéro / message d'erreur est facilement déroutant.

Rubberduck (projet de complément VBIDE gratuit et open source que je gère) déclenche un résultat d'inspection pour Valeur requise ici, qui explique exactement ce qui se passe:

Dans un contexte qui nécessite un type valeur, l'expression 'lst' de type d'objet 'VBAProject.Class1' est utilisée sans avoir de membre par défaut approprié.

Objet utilisé lorsqu'une valeur est requise

Le compilateur VBA ne génère pas d'erreur si un objet est utilisé à un endroit qui nécessite un type valeur et que le type déclaré de l'objet n'a pas de membre par défaut approprié. Dans presque toutes les circonstances, cela conduit à une erreur d'exécution 91 `` Objet ou avec variable de bloc non définie '' ou 438 `` L'objet ne prend pas en charge cette propriété ou cette méthode '' selon que l'objet a la valeur `` Rien '' ou non, ce qui est plus difficile à détecter et indique un bogue.

En d'autres termes, c'est l'un des nombreux endroits où le compilateur VBA reporte à l'exécution au lieu d'avertir de quelque chose de louche au moment de la compilation. L'analyse de code statique est donc le seul moyen de détecter de manière fiable de tels bogues avant qu'ils n'apparaissent au moment de l'exécution.


1 commentaires

Merci pour l'explication approfondie Mathieu.