4 votes

Vérifier si un événement donné est souscrit, pendant l'exécution, en utilisant la réflexion

Prenons l'exemple d'une classe qui a des événements. Cette liste d'événements va s'allonger. Certains sont facultatifs. D'autres sont obligatoires.

Pour simplifier la validation initiale, je dispose d'un attribut personnalisé qui marque un événement comme étant obligatoire. Par exemple :

    [RequiredEventSubscription("This event is required!")]
    public event EventHandler ServiceStarted;

Jusqu'à présent, tout va bien. Pour valider tous les événements, j'utilise la réflexion pour itérer la liste des événements et récupérer les attributs personnalisés. Mais j'ai besoin d'un moyen de déterminer si l'événement est souscrit ou non.

Sans réflexion, ServiceStarted.GetInvocationList fait le travail. Mais l'événement doit provenir de cette liste : var eventList = this.GetType().GetEvents().ToList() ;

Existe-t-il un moyen de vérifier si un événement donné, à partir d'une liste d'événements, est souscrit en utilisant la réflexion ?

--[Mise à jour]-- Voici une solution possible basée sur la réponse d'Ami :

    private void CheckIfRequiredEventsAreSubscribed()
    {
        var eventList = GetType().GetEvents().ToList().Where(e => Attribute.IsDefined(e, typeof(RequiredEventSubscription)));

        StringBuilder exceptionMessage = new StringBuilder();
        StringBuilder warnMessage = new StringBuilder();

        foreach (var evt in eventList)
        {
            RequiredEventSubscription reqAttr = (RequiredEventSubscription) evt.GetCustomAttributes(typeof(RequiredEventSubscription), true).First();
            var evtDelegate = this.GetType().GetField(evt.Name, BindingFlags.Instance | BindingFlags.NonPublic);
            if (evtDelegate.GetValue(this) == null)
            {
                warnMessage.AppendLine(reqAttr.warnMess);
                if (reqAttr.throwException) exceptionMessage.AppendLine(reqAttr.warnMess);
            }
        }
        if (warnMessage.Length > 0)
            Console.WriteLine(warnMessage);
        if (exceptionMessage.Length > 0)
            throw new RequiredEventSubscriptionException(exceptionMessage.ToString());
    }

Merci beaucoup !

4voto

Ani Points 59747

Il y a des problèmes de conception majeurs. En général, il n'y a aucun moyen de demander à un objet qui sont les abonnés à ses événements. Il est très inhabituel que quelqu'un veuille cette fonctionnalité, mais si vous la voulez vraiment, vous devriez faire en sorte que les classes l'exposent d'une manière ou d'une autre, par exemple en implémentant une interface avec une méthode telle que :

public IEnumerable<Delegate> GetSubscribers(string eventName);

Quoi qu'il en soit, pour répondre à la question telle qu'elle est posée, vous puede utiliser la réflexion, mais seulement si vous savez exactement comment les abonnés sont maintenus. Par exemple, en supposant que todos sont implémentés avec l'implémentation actuelle des événements de type champ C#, vous pouvez faire quelque chose comme ( fortement découragés) :

object o = ...

var unsubscribedEvents = 
  from e in o.GetType().GetEvents()
  where Attribute.IsDefined(e, typeof(RequiredEventSubscriptionAttribute))
  let field = o.GetType()
               .GetField(e.Name, BindingFlags.NonPublic | BindingFlags.Instance)
               .GetValue(o)
  where field == null
  select field;

var isValid = !unsubscribedEvents.Any();

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X