4

Consider a class that has some events. This event list is going to grow. Some are optional. Others are required.

To simplify some initial validation I have a custom attribute that marks an event as a required one. For example:

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

So far so good. To validate all events, using reflection I iterate the event list and grab the custom attributes. But I need a way to determine if the event is subscribed or not.

Without reflection the ServiceStarted.GetInvocationList does the job. But the event must come from this list: var eventList = this.GetType().GetEvents().ToList();

Is there any way to check if a given event, from an event list, is subscribed using reflection?

--[Update]-- Here's a possible solution based on Ami's answer:

    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());
    }

Many thanks!!

Bruno
  • 445
  • 4
  • 10
  • 3
    IMHO, if subscribing to an event is required, don't make it an event in the first place but a delegate parameter to the constructor of your class. – Daniel Hilgarth Mar 15 '11 at 15:12
  • What is doing the validating, and what does "required" mean in this context? Required for whom/what? – David Pope Mar 15 '11 at 15:12
  • @Daniel, that was my first approach, but sending so many delegates throughout the code lead me to this experiment. – Bruno Mar 15 '11 at 16:59
  • @David, for now the validation process just has to log a warning. – Bruno Mar 15 '11 at 17:01

1 Answers1

4

There are some major design issues here. In general, there's no way to ask an object who its events' subscribers are. It's highly unusual that anyone would want this functionality, but if you really want it, you should get classes to somehow expose this, for example, by implementing an interface with a method such as:

public IEnumerable<Delegate> GetSubscribers(string eventName);

Anyway, to answer the question as asked, you can use reflection, but only if you know exactly how the subscribers are being maintained. For example, assuming that all events are implemented with the current implementation of C# field-like events, you can do something like (strongly discouraged):

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();
Ani
  • 111,048
  • 26
  • 262
  • 307
  • I see your point about the design flaw :/ Still your solution lead me in a good direction! The GetField(-eventname-) and the binding flags solved the problem! – Bruno Mar 15 '11 at 17:33