views:

40

answers:

2

I need to know what handlers are subsribed to the CollectionChanged event of the ObservableCollection class. The only solution I found would be to use Delegate.GetInvocationList() on the delegate of the event. The problem is, I can't get Reflection to find the compiler generated delegate. AFAIK the delegate has the same name as the event. I used the following piece of code:

PropertyInfo notifyCollectionChangedDelegate = collection.GetType().GetProperty("CollectionChanged", BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
+1  A: 

The whole point of events is that they encapsulate the publish/subscribe nature without exposing the currently subscribed handlers. You shouldn't need to know the subscribed handlers - if you do, you should use your own type instead of ObservableCollection. What are you trying to do?

There's nothing to guarantee that there is a compiler-generated delegate field. It may not have been declared using a field-like event - indeed, there may not even be a single field for the backing delegate at all. (There probably is, given that there aren't many events on ObservableCollection - but WinForms controls use a lazily allocated map to avoid having to have one field per event, when most events won't have subscribed handlers.)

Jon Skeet
My aim is to manage subscription to the NotifyCollectionChanged event, but I need to subscribe only once. I don't want to manage a boolean field for every ObservableCollection to hold this information. (Subscription happens in places which get called multiple times.)So the simplest is to check if I'm already subscribed with a handler. Either I'm missing something very simple here, or a .net framework event would really need to expose a property which gives back the subscribed handlers.(BTW I found an article describing the WinForms delegate system earlier, but I needed this.)
Sandor Davidhazi
+2  A: 

It is not a property, it is a field. This works:

using System;
using System.Collections.ObjectModel;  // Add reference to WindowsBase
using System.Collections.Specialized;
using System.Reflection;

namespace ConsoleApplication1 {
  class Program {
    static void Main(string[] args) {
      var coll = new ObservableCollection<int>();
      coll.CollectionChanged += coll_CollectionChanged;
      coll.Add(42);
      FieldInfo fi = coll.GetType().GetField("CollectionChanged", BindingFlags.NonPublic | BindingFlags.Instance);
      NotifyCollectionChangedEventHandler handler = fi.GetValue(coll) as NotifyCollectionChangedEventHandler;
      handler.Invoke(coll, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
    }

    static void coll_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) {
      Console.WriteLine("Changed {0}", e.Action);
    }
  }
}

Don't use it.

Hans Passant