Question

Je cherche quelques conseils sur la mise en œuvre d'événements personnalisés dans VB.NET (Visual Studio 2008, .NET 3.5).

Je sais que « réguliers » (non personnalisés) Les événements sont en fait les délégués, donc je pensais à l'aide des délégués lors de la mise en œuvre d'un événement personnalisé. D'autre part, Andrew Troelsen est "Pro VB 2008 et la plate-forme .NET 3.5" livre utilise des types de collecte dans tous ses exemples d'événements personnalisés, et codes d'échantillons correspondre cette ligne de pensée.

Alors, ma question est: quelles sont les considérations que je devrais avoir au moment de choisir une conception sur l'autre? Quels sont les avantages et les inconvénients pour chaque conception? Lequel de ces ressemble la mise en œuvre intérieure des événements « réguliers »?

Voici un exemple de code montrant les deux modèles.

Public Class SomeClass
    Private _SomeEventListeners As EventHandler
    Public Custom Event SomeEvent As EventHandler
        AddHandler(ByVal value As EventHandler)
            _SomeEventListeners = [Delegate].Combine(_SomeEventListeners, value)
        End AddHandler

        RemoveHandler(ByVal value As EventHandler)
            _SomeEventListeners = [Delegate].Remove(_SomeEventListeners, value)
        End RemoveHandler

        RaiseEvent(ByVal sender As Object, ByVal e As System.EventArgs)
            _SomeEventListeners.Invoke(sender, e)
        End RaiseEvent
    End Event

    Private _OtherEventListeners As New List(Of EventHandler)
    Public Custom Event OtherEvent As EventHandler
        AddHandler(ByVal value As EventHandler)
            _OtherEventListeners.Add(value)
        End AddHandler

        RemoveHandler(ByVal value As EventHandler)
            _OtherEventListeners.Remove(value)
        End RemoveHandler

        RaiseEvent(ByVal sender As Object, ByVal e As System.EventArgs)
            For Each handler In _OtherEventListeners
                handler(sender, e)
            Next
        End RaiseEvent
    End Event
End Class
Était-ce utile?

La solution

Je dirais que l'utilisation du simple délégué; simplement - il est déjà (interne) une liste, de sorte que vous dupliquez effort en l'enveloppant dans List<T>. Vous avez également les frais généraux d'objets de liste supplémentaires et des tableaux, qui pourrait être nul, donc plus d'impact sur la collecte des ordures, etc.

En outre, si vous faites beaucoup de cela, considérons EventHandlerList, qui existe pour fournir un accès efficace à clairsemés événements - à savoir où vous voulez exposer beaucoup d'événements, mais beaucoup d'entre eux peut désaffecter.

Le premier exemple est beaucoup plus proche des événements « standard » (bien que vous voudrez peut-être regarder pour les gestionnaires non affectés / null lors de l'appel Invoke, car il pourrait être nul). De plus, notez que certaines langues (Honnêtement, je ne sais pas ce que VB fait ici) applique la synchronisation à des événements, mais en réalité, très peu d'événements vraiment doivent être thread-safe, de sorte que pourrait être surpuissant.

modifier il se produit également qu'il ya un fonctionnel différence entre eux:

  • l'approche des délégués traite différentes instances avec la même méthode cible / instance égale (je ne pense pas que le List<T> sera)
  • Dupliquer délégués: délégué retire la dernière première; liste supprime d'abord plus tôt
  • composites: ajouter A, ajouter B et retirer (A + B) - avec un délégué cela devrait donner nul / vide, mais l'approche de la liste conservera A et B (avec le (A + B) supprimer ne pas trouver quoi que ce soit)

Autres conseils

L'utilisation d'un MulticastDelegate de tenir une liste des événements auxquels vous êtes abonné est certainement une approche viable, mais pas celle que je suis particulièrement désireux de. Pour ajouter ou supprimer un événement d'une MulticastDelegate, il faut le faire de deux choses:

  1. un verrou, créez un nouveau délégué de l'ancien, mais avec un événement ajouté ou supprimé, placez le pointeur de délégué à ce délégué, puis relâchez le verrou
  2. Copier une référence à l'ancien délégué, créer un nouveau délégué, mais avec un événement ajouté ou supprimé, utilisez Interlocked.CompareExchange pour stocker la référence au nouveau si l'ancien est inchangé, et tout recommencer si le CompareExchange a échoué.

Cette dernière approche peut offrir des performances légèrement mieux s'il n'y a pas de conflit, mais peut exécuter mal si plusieurs threads tentent simultanément d'ajouter ou de supprimer des événements. Un avantage de cette dernière approche, cependant, est qu'il n'y a pas de danger d'un fil en train de mourir tout en maintenant un verrou.

Aucune de ces approches semble particulièrement propre. Si l'on envisage d'invoquer tous les événements en invoquant simplement un délégué, la performance invocation d'événement peut compenser l'ajout / suppression de performances. D'autre part, si l'on envisage d'utiliser GetInvocationList pour envelopper les appels d'événements dans try / blocs catch, on peut être préférable de simplement utiliser une liste ou une autre (convenablement verrouillé) telle structure de données.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top