3

I have some factory code along the lines of:

public MyHandlingClass Create()
{ 
  var myHandler = new MyHandlingClass();
  var myNotifier = new MyNotifyingClass();
  myNotifier.Notify += myHandler.HandleNotification;

  return myHandler;
}

The idea was to allow the MyHandlingClass to react to external signals without caring about the type of the sender. In different applications the factory class containing the above method could then replace MyNotifyingClass with something else able to raise a similar type of event.

In the actual code, the MyNotifyingClass handles events from an unmanaged DLL:

public class MyNotifyingClass
{
    private EventHandler notify;
    private UnmanagedEventHandler unmanagedNotify;

    public event EventHandler Notify
    {
        add
        {
            this.notify += value;

            // Hold on to a strong reference to the delegate passed into the
            // unmanaged DLL, to avoid garbage collection of the delegate:
            this.unmanagedNotify = HandleUnmanagedNotify;
            NativeMethods.SetNotifyCallback(this.unmanagedNotify);
        }
        remove
        {
            this.notify -= value;
        }
    }

    private void HandleUnmanagedNotify(IntPtr sender, IntPtr eventArgs)
    {
        this.notify(this, eventArgs.Empty);
    }
}

But when I return from the factory method, there are no longer any strong references to the myNotifier instance, and it will eventually be garbage collected, resulting in access violations when my unmanaged DLL tries to invoke the callback.

I would like to somehow enforce that the myNotifier instance has the same lifetime as the myHandler instance. But I don't particularly like the idea of letting the MyNotifierClass holding on to a strong reference to the handler object. After all, it has no further use for the actual object once the wiring is in place...

Can I somehow create a strong relationship between the two classes and still have them not know about each others existence?

(EDIT: To cut a long story short, this code snippet seems to reproduce my problem:)

[TestClass]
public class MyTests
{
  public class MyHandler
  {
    public void Handle(object sender, EventArgs e)
    { }
  }

  public class MyNotifier
  {
    public event EventHandler Notify;
  }

  [TestMethod]
  public void TestsSomeCondition()
  {
    var myHandler = new MyHandler();
    var myNotifier = new MyNotifier();

    myNotifier.Notify += myHandler.Handle;

    var weakNotifierRef = new WeakReference(myNotifier);
    myNotifier = null;

    GC.Collect();

    Assert.IsTrue(weakNotifierRef.IsAlive);
  }
}
Mathias Falkenberg
  • 1,110
  • 1
  • 11
  • 25
  • Perhaps weak events might help: http://stackoverflow.com/questions/1089309/weak-events-in-net. – Steven Sep 08 '11 at 09:13
  • 1
    Registering an event is already linking the objects. – Jan Christian Selke Sep 08 '11 at 09:16
  • 2
    @steven : That seems like the opposite problem. My problem is rather that the listener _does not_ keep the notifier alive... – Mathias Falkenberg Sep 08 '11 at 09:17
  • @jan : I would have thought so - but it seems to not be the case here. After I return from the factory method, the notifier references other objects, but no one references it - apparently making it elegible for GC... – Mathias Falkenberg Sep 08 '11 at 09:19
  • 1
    @Steven : I believe the problem is opposite to WeakEvents – sll Sep 08 '11 at 09:20
  • I believe you can leverage Reactive Extensiton feature that Subject object could be passed and returned as reference unlike for events, spend 10minutes to overview key concepts of RxExtensions perhaps you'll get insights – sll Sep 08 '11 at 09:25
  • I can't see how your test reproduces a problem, this is behavior I would expect. Notifier is the event **source**, if you make it null it is gone. Lifetime based on events is from **consumers -> sources** – flq Sep 08 '11 at 09:40
  • @flq : you're right. It doesn't reproduce _a_ problem with the .NET framework - but it reproduces _my_ problem. I want the event source to share a lifetime with the consumer. Preferably without the consumer knowing or caring about the concrete source... – Mathias Falkenberg Sep 08 '11 at 09:54

2 Answers2

2

When you attach to an event you are creating a delegate and passing it to the event source. The delegate holds a reference to the object to be called when the event is fired. That is the object reference goes like "source has a reference on target". If you throw the source away, the target doesn't care.

Since you already introduce the factory it should be the factory's job to keep the notifiers alive, i.e. by keeping them in a static list or more elaborate schemes of reusing notifiers etc.

As suggested in a comment, the observer / observable pattern may be of interest to you. The factory implements IObservable<T> , the consumer IObserver<T> . As long as you keep the observable alive, any referenced observers will be notified. If you add Rx-Extensions to the equation you get a rich set of methods to deal with Observables. They will e.g. usually allow to dispose of an observation, removing the reference that Observable has an Observer.

flq
  • 22,247
  • 8
  • 55
  • 77
1

You could create a base class for MyNotifyingClass which is the type stored in the MyHandlingClass:

class BaseNotifyingClass // or maybe an interface type
{
  // put event in here
}

class MyNotifyingClass : public BaseNotifyingClass
{
  // as before, without event
}

class MyHandlingClass
{
  public MyHandlingClass (BaseNotifyingClass notifier)
  {
     m_notifier = notifier;
     m_notifier.Notify += HandleNotification;
  }
  // etc...
}

class SomeFactory
{
  public MyHandlingClass Create()
  { 
    var myNotifier = new MyNotifyingClass();
    var myHandler = new MyHandlingClass(myNotifier);

    return myHandler;
  }
  // etc...
}

This has the added benefit of encapsulating the link between the two classes within the classes themselves, and the factory becomes what the name suggests, a service for creating objects. This means that changing the way the two classes are linked does not require changing the factory class.

Skizz
  • 69,698
  • 10
  • 71
  • 108
  • As I orignally wrote, I am not too fond of adding a strong reference to the notifier from the handler. Once the wiring has been set up, the handler has no further use for the notifier instance (except to establish a lifetime relationship, which I really don't think should be the responsibility of the handler class). It seems a little like a LoD violation to me... – Mathias Falkenberg Sep 08 '11 at 10:04