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 Réponses :
Ajoutez ceci à votre classe
Public Function NewEnum() As IUnknown Attribute NewEnum.VB_UserMemId = -4 Set NewEnum = Items .[_NewEnum] End Function
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é :(
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.
'@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:
Explorateur de code:
Barre d'outils Rubberduck:
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 =)
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.
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.