473,395 Members | 1,783 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,395 software developers and data experts.

C# events need generic "Unsubscribe" method

The garbage handler in the .NET framework is handy. When objects fall out of
scope, they are automatically destroyed, and the programmer doesn't have to
worry about deallocating the memory space for those objects. In fact, all
the programmer has to worry about is the total sum of objects loaded into
RAM at any known point. Memory leaks are not a problem.

.... So one would like to think. The reality is that delegates and event
subscriptions bring about a whole new problem. When an object subscribes to
another object's events, the "other object" will not fall out of scope until
unsubscription occurs.

For instance, if in a Windows Forms application I have a MainForm that
creates a DocumentClass with a subscription to a Reset event, the
DocumentClass will not fall out of scope when it is closed until the
MainForm unsubscribes from the DocumentClass's Reset event.

The reason why this is so is because there is a circular reference in place.
MainForm created a DocumentClass, DocumentClass added MainForm's delegate to
its event, DocumentClass is no longer in MainForm's scope, but DocumentClass
still "remembers" MainForm due to the delegate in its event subscriptions
collection and will stick around until MainForm is unloaded.

If this were not so then the months I have spent trying to kill the memory
leaks I had created by forgetting to unsubscribe from events would have had
no effect, but they did have an effect -- the [DocumentClass] objects are
now getting unloaded, whereas they previously were not.

The biggest problem with this situation is that keeping track of delegates
created for event subscriptions so that timely unsubscriptions can occur is
an incredibly arduous chore. The delegates must either be kept in a
collection and referenced when a particular event occurs or when a
particular method is executed, or the delegates must be noted in-method, the
object's asynchronous use must be waited upon in a DoEvents / Sleep loop,
and then the individually noted delegates must unsubscribe from each of the
asynchronous events.

There are ways to automate the process of subscribing / unsubscribing
to/from events, such as creating a method like this (untested) ...

public void SubscribeEvent(object subject, string eventName, delegate
subscription) {
[ ... assign subscription using Reflection ... ]
}

... and an unsubscribe method ...

public void UnsubscribeEvent(object subject, string eventName, delegate
subscription) {
[ ... check for subscription -- if exists, remove ...]
}

I have not utilized this yet because I don't know if this is the best
approach.

However, -- and this is the main reason why I'm posting this -- I personally
wish the C# language supported a method in an event's interface where you
could simply pass it a reference to a host object and all subscriptions from
that host object would be unsubscribed.

For instance, in MainForm ...

myDocumentClass.ResetEvent.Unsubscribe(this);
// Iterate through all delegate subscriptions, and if the
// delegate's owner object is same as "this", remove
// (unsubscribe).

This would save me so much pain and headache of tracking the specific
delegate instance. Granted, a delegate is more than just the subscribing
object--it is the subscribing METHOD--but no method exists outside of a
Type, and in the case of a type instance--a System.Object--this interface
would be usable only and strictly and predictably for the circumstance of a
subscribing object instance.

Mean time, I can work around this with a generic class/method, like I said
above. But the interface for this is far more elaborate and it is still a
chore than merely passing a "this" to an Unsubscribe() method on the
object's event.

Thanks for reading this. Comments and corrections are welcome.

Jon
Nov 15 '05 #1
2 9137
> The garbage handler in the .NET framework is handy. When objects fall out
of
scope, they are automatically destroyed, and the programmer doesn't have to worry about deallocating the memory space for those objects. In fact, all
the programmer has to worry about is the total sum of objects loaded into
RAM at any known point. Memory leaks are not a problem.
This is a blanket statement. It is not always true. It is mostly true,
except when you wrap unmanaged resources. It's also not true for COM interop
where references are held on the other side. I believe, based on your post,
that you realize this. Others may not, hence my 2pence.
However, -- and this is the main reason why I'm posting this -- I personally wish the C# language supported a method in an event's interface where you
could simply pass it a reference to a host object and all subscriptions from that host object would be unsubscribed.
True, but I think the framers of the constitution...uhh, i mean the
framework realized that putting in this code would not help the majority of
us and so it was left out simply because the owner of the object to which
the delegate is attached will automatically clean up when it goes away.
Normally, this is mostly right because for most situations, as long as the
object is around, you may need to subscribe to the event. If you choose not
to, you can unsubscribe manually. In atypical situation like yours, you are
better served implementing this generic unchaining yourself. In short, it
isn't needed in most cases and requiring the framework to implement it for
special cases is a fruitless exercise for the majority.
--
-----------
Got TidBits?
Get it here: www.networkip.net/tidbits
"Jon Davis" <jo*@REMOVE.ME.PLEASE.jondavis.net> wrote in message
news:es**************@TK2MSFTNGP12.phx.gbl... The garbage handler in the .NET framework is handy. When objects fall out of scope, they are automatically destroyed, and the programmer doesn't have to worry about deallocating the memory space for those objects. In fact, all
the programmer has to worry about is the total sum of objects loaded into
RAM at any known point. Memory leaks are not a problem.

... So one would like to think. The reality is that delegates and event
subscriptions bring about a whole new problem. When an object subscribes to another object's events, the "other object" will not fall out of scope until unsubscription occurs.

For instance, if in a Windows Forms application I have a MainForm that
creates a DocumentClass with a subscription to a Reset event, the
DocumentClass will not fall out of scope when it is closed until the
MainForm unsubscribes from the DocumentClass's Reset event.

The reason why this is so is because there is a circular reference in place. MainForm created a DocumentClass, DocumentClass added MainForm's delegate to its event, DocumentClass is no longer in MainForm's scope, but DocumentClass still "remembers" MainForm due to the delegate in its event subscriptions
collection and will stick around until MainForm is unloaded.

If this were not so then the months I have spent trying to kill the memory
leaks I had created by forgetting to unsubscribe from events would have had no effect, but they did have an effect -- the [DocumentClass] objects are
now getting unloaded, whereas they previously were not.

The biggest problem with this situation is that keeping track of delegates
created for event subscriptions so that timely unsubscriptions can occur is an incredibly arduous chore. The delegates must either be kept in a
collection and referenced when a particular event occurs or when a
particular method is executed, or the delegates must be noted in-method, the object's asynchronous use must be waited upon in a DoEvents / Sleep loop,
and then the individually noted delegates must unsubscribe from each of the asynchronous events.

There are ways to automate the process of subscribing / unsubscribing
to/from events, such as creating a method like this (untested) ...

public void SubscribeEvent(object subject, string eventName, delegate
subscription) {
[ ... assign subscription using Reflection ... ]
}

.. and an unsubscribe method ...

public void UnsubscribeEvent(object subject, string eventName, delegate
subscription) {
[ ... check for subscription -- if exists, remove ...]
}

I have not utilized this yet because I don't know if this is the best
approach.

However, -- and this is the main reason why I'm posting this -- I personally wish the C# language supported a method in an event's interface where you
could simply pass it a reference to a host object and all subscriptions from that host object would be unsubscribed.

For instance, in MainForm ...

myDocumentClass.ResetEvent.Unsubscribe(this);
// Iterate through all delegate subscriptions, and if the
// delegate's owner object is same as "this", remove
// (unsubscribe).

This would save me so much pain and headache of tracking the specific
delegate instance. Granted, a delegate is more than just the subscribing
object--it is the subscribing METHOD--but no method exists outside of a
Type, and in the case of a type instance--a System.Object--this interface
would be usable only and strictly and predictably for the circumstance of a subscribing object instance.

Mean time, I can work around this with a generic class/method, like I said
above. But the interface for this is far more elaborate and it is still a
chore than merely passing a "this" to an Unsubscribe() method on the
object's event.

Thanks for reading this. Comments and corrections are welcome.

Jon

Nov 15 '05 #2
In article <es**************@TK2MSFTNGP12.phx.gbl>, Jon Davis wrote:

I thought I'd add just a few random thoughts to this, since you asked
for comments...
... So one would like to think. The reality is that delegates and event
subscriptions bring about a whole new problem. When an object subscribes to
another object's events, the "other object" will not fall out of scope until
unsubscription occurs.
Right. The plus side here is that you don't have to keep explicit
references to all your objects just to catch events. This is generally
the correct behavior.
For instance, if in a Windows Forms application I have a MainForm that
creates a DocumentClass with a subscription to a Reset event, the
DocumentClass will not fall out of scope when it is closed until the
MainForm unsubscribes from the DocumentClass's Reset event.

The reason why this is so is because there is a circular reference in place.
MainForm created a DocumentClass, DocumentClass added MainForm's delegate to
its event, DocumentClass is no longer in MainForm's scope, but DocumentClass
still "remembers" MainForm due to the delegate in its event subscriptions
collection and will stick around until MainForm is unloaded.
Well, but the phrase "fall out of scope" is being used pretty loosely
here. There's pretty much only two options here, I think:

One, the DocClass object reference is a local variable in a function, in
which case you *need* this event behavior in order to catch the events
after you leave the function. And in this case, your suggestion about a
new framework function doesn't really make sense (since MainForm has no
object reference to call the function on), so I'll assume we're not
talking about this case where the reference has really fallen out of
scope.

Two, the DocClass object reference is a member variable of the MainForm
object. In this case, it can't really "fall out of scope" until the
MainForm goes away, at most you could re-assign the reference variable
(possibly to null). In other words, to me it seems that you haven't
*implicitly* forgotten to delete an object (which, vaguely speaking, is
what GC is for), you're *explicitly* deleting the object reference,
which seems to be a much simpler problem to track down.

(Of course, there's the static variable option as well, but that doesn't
seem to be immediately relevant here).
If this were not so then the months I have spent trying to kill the memory
leaks I had created by forgetting to unsubscribe from events would have had
no effect, but they did have an effect -- the [DocumentClass] objects are
now getting unloaded, whereas they previously were not.
I'm definitely missing something here. Since you're explicitly deleting
the object reference, why not just ask the DocumentClass object to
kill all known delegates from the owner before you kill the reference.

In other words,

// from MainForm
myDocClassObject.Unsubscribe(this);
myDocClassObject = null;
// and in DocumentClass
public void Unsubscribe(object obj)
{
// for each relevant event
foreach(Delegate d in MyEvent.GetInvocationList())
{
if(d.Target.Equals(obj))
MyEvent -= (EventHandler) d;
}
}

OK, there might be some performance problems there if you have a ton of
events (which you could clear somewhat by specifying event names in the
call), but in general this makes the problem no more difficult than the
various classes one has to Close() after using.

Is there some other reason this won't work?

The biggest problem with this situation is that keeping track of delegates
created for event subscriptions so that timely unsubscriptions can occur is
an incredibly arduous chore. The delegates must either be kept in a
collection and referenced when a particular event occurs or when a
particular method is executed,
But the delegates *are* kept in a collection in the class that owns the
event, which is how it should be, IMHO.
However, -- and this is the main reason why I'm posting this -- I personally
wish the C# language supported a method in an event's interface where you
could simply pass it a reference to a host object and all subscriptions from
that host object would be unsubscribed.

For instance, in MainForm ...

myDocumentClass.ResetEvent.Unsubscribe(this);
// Iterate through all delegate subscriptions, and if the
// delegate's owner object is same as "this", remove
// (unsubscribe).

This would save me so much pain and headache of tracking the specific
delegate instance.


Of course, that's not going to effect the "garbage collection" aspect of
the problem, which is how this posted started. The user of a class
would still need to remember to call this function, it wouldn't happen
automatically.

--
David
dfoster at
hotpop dot com
Nov 15 '05 #3

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

15
by: Rhy Mednick | last post by:
I have a class (let's call it ClassA) that I've written which has events. In another class (let's call it ClassB) I create a collection of ClassA objects. In a third class (ClassC) I create a...
16
by: anonymous.user0 | last post by:
The way I understand it, if I have an object Listener that has registered as a listener for some event Event that's produced by an object Emitter, as long as Emitter is still allocated Listener...
4
by: DeveloperX | last post by:
Hi, I've just written the following code to describe the issue I have. In a nutshell if an object subscribes to an event on a long lived object and I then ditch the reference to the subscriber (set...
5
by: Daniel | last post by:
Hey guys When you hook an event (c# 2.0 syntax): myEvent += MyMethodToFire; You need to also unsubscribe it to avoid a resource leak so that the object it is in gets garbage collected like so...
4
by: Douglas Peterson | last post by:
I created the following code: private struct StackItem { public EventHandler theEvent, theHandler; public StackItem(EventHandler theEvent, EventHandler theHandler) { this.theEvent = theEvent;...
3
by: Jose Fernandez | last post by:
Hello. I would like to know how could i get all the subscriptions that my form has with the events of their controls. For example. I have a form with a textbox, a button and a dropdown. I...
11
by: MikeT | last post by:
This may sound very elementary, but can you trap when your object is set to null within the object? I have created a class that registers an event from an object passed in the constructor. When...
4
by: Gianni Mariani | last post by:
Two issues here: a) What is the accepted definition of "observer pattern". While I can't point to anything specific, I do remember having issues with inconsistency in the definition. b)...
4
by: FullBandwidth | last post by:
I have been perusing various blogs and MSDN pages discussing the use of event properties and the EventHandlerList class. I don't believe there's anything special about the EventHandlerList class in...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: ryjfgjl | last post by:
If we have dozens or hundreds of excel to import into the database, if we use the excel import function provided by database editors such as navicat, it will be extremely tedious and time-consuming...
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.