Jump to content
  • Advertisement
Sign in to follow this  
Structural

[.net] How to properly deal with unsubscribing classes to events

This topic is 3621 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I just ran a little test to see how subscribing an object to an event affects its lifetime and I found out that the object that is subscribed is not destroyed until it is unsubscribed from the event. (correct me if I'm wrong, my test app was incredibly simple) For me this has never been a real problem because I mostly built apps with mostly static behaviour in C#. But now my personal project is also in C# this is becoming a bit of an issue because the behaviour is much more dynamic. I would like to subscribe game objects to certain events, but since game objects are created and destroyed all the times I have to make sure they are also unsubscribed so they are collected by the GC. Now I'm wondering how to properly deal with this because in order to unsubscribe I need to have a reference of the object I subscribed to, AND the delegate that I subscribed. So unless I'm missing some handy construct in C# to deal with this I'd go for:

class EventThrower
{
  public delegate void EventDelegate();
  public event EventDelegate Event;
}

class EventListener
{
  public EventListener(EventThrower thr)
  {
    subscribedTo = thr; // keep so we can unsubscribe later
    subscribedDelegate = new EventThrower.EventDelegate(this.HandleEvent);
    thr.Event += subscribedDelegate;
  }

  ~EventListener()
  {
    subscribedTo.Event -= subscribedDelegate;
  }

  private EventThrower subscribedTo;
  private EventThrower.EventDelegate subscribedDelegate;
}

Is this the way to go?

Share this post


Link to post
Share on other sites
Advertisement
It's good that you realized this potentially tricky situation in C#. Many people don't realize that it's one of the easiest ways of causing a "memory leak" in C#. However, I'm not sure why you need to make the situation so complex. Simply ensure that whenever you have an object subscribe itself to an event, that it remembers to take itself off when destroyed. The IDisposable pattern may be useful to you here.

Share this post


Link to post
Share on other sites
Except (unless I'm mistaken) the object will never be destroyed since the event holds a reference to it. You'd have to call dispose manually, which kinda defeats the purpose of it being a special pattern.

Personally, most designs I've dealt with are one of two cases. One is that the object binds itself to a set of events, and knows about (or can derive) them. A soldier might know what tile it's in, and when 'killed' knows to remove it's listeners from the tile. The removal then goes in that 'you die now' method, which extracts the soldier from the world so the GC will get it.

The second is when the listener doesn't particularly care. I'll just leave it there until the event dies. Since nothing references the listener (which is just a functor with references to what it works on), no tie ups.

But yes, that is a design problem, and no there's no handy construct I'm aware of.

Share this post


Link to post
Share on other sites
With .NET 2.0 it is considered a best practice to unsubscribe events in the Dispose() method; yep, this requires you to manually invoke the Dispose() method. This is the best place to handle it as the object should be aware of what events it has subscribed to, and which need to be unsubscribed.

With .NET 3.0 and .NET 3.5 you can use the WeakEvent pattern to minimize the pain caused by this particular issue. (A self implemented version of this pattern can be found at Greg Schechter's blog; this works with .NET 2.0.

It should be noted that the if the event publisher is no longer reachable from a root object and the event subscriber is also no longer reachable from a root object that the collection of both objects will proceed normally. It is only when the event publisher has a substantially longer lifetime than the subscriber that the issue becomes extremely hairy. In all cases when the AppDomain is terminated all event publishers and subscribers will be terminated.

This really shouldn't come as a surprise to anyone who's looked at the code generated for the event handlers. There is a solid reference to the subscriber, and it is valid until it is removed.

If you Google for "lapsed listener" you can find quite a few articles and such; this seems to be the "official" name for the issue you've found.

Share this post


Link to post
Share on other sites
Wow, reading through some of those looks like a lot of extra work. :) I think you could also use the add/remove to handle the weak references and using the 2.0 version.

private event EventHandler<BobEvent> bobChanged;

public EventHandler<BobEvent> BobChanged
{
add
{
// Add a weak reference
}
remove { /* remove stuff stuff */ }
}


And then do the proper looping for the delegate processing.

That way, you can choose to use it and you don't have to involve extra classes beyond an inner private class (or some other method). Of course, I haven't really thought about the problem, so I have no clue the full ramifications.

Share this post


Link to post
Share on other sites
Ok, the weak event pattern is a very interesting read, but it isn't making me happy because of the work involved.
The weak event pattern would be a solution, but the 2.0 version from Greg Schechter (which I would like to use) is a load of plumbing to implement for every event+delegate. If I read all that I might just as well be implementing the observer/observable pattern explicitly (interface + subscribe/unsubscribe) for every delegate+event, like one would do in C++. The advantage of that is that the reference is explicit, and thus easier to debug.

There are also a few possible issues with Greg Schechter's implementation when all strong references to an object have been removed it still receives events until it is actually GC'd, but I don't know if that is going to be a real problem.

I never realized this would be a problem. I always believed that you could sprinkle your code with events and subscribe to them and everything would just be fine. I guess I was wrong.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!