4
votes

Créer une classe de conteneur bouclable dans VBA

J'ai essayé de nettoyer un peu mon code et de le rendre plus similaire au modèle d'objet Excel, et je me demandais s'il était possible de créer une classe de conteneur "bouclable" dans VBA, par exemple comme vous pouvez le faire:

Public Sub MyMethod()

  Dim Stuff As New Container
  Stuff.Add

  Dim Element As ItemType
  For Each Element In Stuff ' <- This will not work
    ' Do something
  Next Element

End Sub

Je veux cette fonctionnalité pour mon propre conteneur.

Disons que je crée ma propre classe appelée Container qui contient des éléments d'une classe ItemType (cela peut simplement être une classe vide pour cet exemple):

' Class Container
' The container contains items of a class I will call ItemType

Private Type MContainer
  Items As Collection ' Could also be implemented in terms of an array
End Type

Private This As MContainer

Public Property Get Item(ByVal Index As Long) As ItemType
Attribute Item.VB_UserMemId = 0 'Makes it so I can access elements like a Collection
  Set Item = This.Items(Index)
End Property

Public Function Add() As ItemType
  This.Items.Add
  Set Add = This.Items(This.Items.Count)
End Function

Private Sub Class_Initialize()
  Set This.Items = New Collection
End Sub

Je veux ensuite parcourir les éléments de mon conteneur avec le For Each ... , mais cela ne fonctionne pas. Voir l'exemple suivant pour savoir comment je souhaite que cela fonctionne idéalement:

Dim Sheet As Worksheet
For Each Sheet In ThisWorkbook.Worksheets
   ' ...
Next Sheet

La dernière boucle For est ce que je cherche à faire fonctionner. Est-ce possible? Fondamentalement, le problème est que je ne peux pas appeler For Each sur ma classe Container de la même manière que vous pouvez avec par exemple la classe Excel.Sheets . Est-ce possible de le réaliser en VBA?


3 commentaires

il vous manque l'implémentation d'énumération sur votre collection, c'est-à-dire les éléments


Pouvez-vous ajouter un exemple reproductible minimal ? Les exemples de code actuels que vous avez ne seront pas compilés


@DavidZemens N'est-il pas un peu difficile de fournir du code de compilation pour VBA? La seule chose que je vois dont vous avez besoin pour compiler est un fichier de classe vide avec le nom ItemType . Sauf évidemment le dernier extrait de code que j'ai fourni comme exemple pour ce que je veux faire compiler, mais pas.


3 Réponses :


3
votes

Ajoutez ceci à votre classe

Public Function NewEnum() As IUnknown
Attribute NewEnum.VB_UserMemId = -4
    Set NewEnum = Items .[_NewEnum]
End Function


5 commentaires

Notez que vous devez ajouter l ' Attribut en dehors de l'IDE VBA; exportez votre module de cours, ouvrez avec n'importe quel éditeur, ajoutez la ligne Attribut (ou la fonction entière), enregistrez le fichier et importez-le dans votre classeur.


Merci, cela fonctionne. Serait-il possible d'obtenir une sorte d'explication? Cela me semble être de la magie noire. Le nom de la fonction doit-il être NewEnum ou est-ce l'indicateur d'attribut qui le marque? Serait-il possible de le nommer _NewEnum comme la collection pour le cacher? Comment NewEnum est-il défini, c'est-à-dire que ferais-je si j'utilisais un tableau comme conteneur?


Les tableaux @JonasGlesaaen sont mieux itérés avec une boucle For ... Next . L'itération For Each sur un tableau est inefficace ... cela annulerait essentiellement les avantages en termes de performances d'un énumérateur d'objets.


@MathieuGuindon Merci pour l'info, cependant, il serait instructif de comprendre ce que NewEnum attend si je n'avais pas la classe Collections à utiliser. C'est à dire. comment pourrais-je l'implémenter moi-même si j'avais écrit un conteneur à partir de zéro.


@JonasGlesaaen est d'accord! ... le problème est que c'est à peine documenté :(



4
votes

Pour chaque itération nécessite une valeur d'attribut de membre spéciale pour fonctionner, et une propriété ou une fonction NewEnum renvoyant un IUnknown .

Tous la classe de collection qui peut être itérée avec une boucle For Each a un membre [_NewEnum] caché (les crochets sont nécessaires pour accéder au membre caché, car le préfixe de soulignement est illégal pour un identifiant dans VBA.

Il n'est pas possible de modifier le module et les attributs des membres directement dans le VBE, vous devez donc supprimer / exporter le module, le modifier par exemple dans Notepad ++, enregistrer les modifications, puis réimportez-le dans votre projet.

Ou, demandez à Rubberduck a > (avertissement: je contribue à ce projet open-source) faites-le pour vous, en utilisant des annotations (alias "commentaires magiques"):

'@Enumerator
'@Description("Gets an enumerator that iterates through the internal object collection.")
Public Property Get NewEnum() As IUnknown
    Set NewEnum = this.Items.[_NewEnum]
End Function

'@DefaultMember
'@Description("Gets/sets the element at the specified index.")
Public Property Get Item(ByVal index As Long) As ItemType
    Set Item = this.Items(index)
End Property

Ensuite, analysez le projet ( Ctrl + `) et afficher la fenêtre d'outils Résultats d'inspection ( C trl + Shift + i ) - il devrait y avoir un certain nombre de résultats "Attribut manquant" sous "Opportunités Rubberduck":

Cliquez sur "Corriger toutes les occurrences dans le module" dans le volet inférieur, pour synchroniser le attributs masqués avec les commentaires d'annotation.

Si vous avez des résultats "Annotation manquante", Rubberduck a déterminé qu'un module / membre a une valeur non par défaut pour un attribut donné, et est capable d'ajouter de la même manière une annotation commentaire qui le surfe / le documente avec un commentaire.

L ' Explorateur de code ( Ctrl + R ), la barre d'outils Rubberduck , et le propre Navigateur d'objets du VBE ( F2 ) affichera le contenu de l'attribut VB_Description , donc @Description les annotations sont particulièrement utiles pour toute procédure publique.

Navigateur d'objets:

Navigateur d'objets affichant m ember description

Explorateur de code:

 Explorateur de code affichant la description du membre

Barre d'outils Rubberduck:

Barre d'outils RD affichant la description du membre


5 commentaires

Je suis moi-même un utilisateur de Rubberduck ^^ Et les commentaires magiques sont quelque chose que je ne savais pas, du moins pas ceux que vous mentionnez ici. Mais ils semblent super utiles car avoir à exporter, modifier et importer à nouveau est un vrai problème. Ce serait génial si vous pouviez documenter tous vos commentaires magiques quelque part, mais cela devrait probablement être un problème dans votre projet github ^^


@JonasGlesaaen Voir "Annotations de surfaçage" - les commentaires sont les bienvenus!


J'ai vu ce problème, mais je le comprends car vous voulez l'ajouter directement dans Rubberduck, ce qui est génial, mais je serais plus que satisfait d'une simple page wiki me disant ce qui existe :)


Hmm, ils ont été brièvement mentionnés dans le Message d'annonce 2.4.0 (sous "Annotations & Attributes", à la fin du post) ... à part ça ... il y a toujours le AnnotationType code source (qui .. est déroutant, étant donné que l'énumération a des membres d'indicateur), mais oui, nous besoin de créer une belle page wiki complète pour eux.


@JonasGlesaaen FYI Je viens d'ajouter une page pour cela dans le wiki du projet =)



0
votes

Une approche alternative à ce problème n'est pas d'utiliser une collection mais un Scripting.Dictionary. L'un des avantages d'un dictionnaire de script est qu'il peut renvoyer des tableaux des clés et des éléments du dictionnaire. Itérer sur un tableau en VBA est un exercice trivial.


0 commentaires