|
|
Comments and Discussions
|
|
 |

|
this article[^] explains in more detail how an event could cause
memory leaks (i.e. delayed finalization) unless special action is taken.
Weak events is one way to solve the potential problem.
[ADDED]
And this article[^] offers a practical implementation.
[/ADDED]
modified on Saturday, October 11, 2008 4:03 PM
|
|
|
|

|
Luc,
Your article got me thinking. Would i need to unsubscribe to the backgroundWorker events at the bottom of the 'using' block? Or because i am disposing of the object, i dont need to...
using (BackgroundWorker backgroundWorker = CreateBackgroundWorker())
using (OpenFileDialog openFileDialog = new OpenFileDialog())
{
openFileDialog.Multiselect = true;
openFileDialog.Title = "By Song";
openFileDialog.Filter = "MPEG-1 Audio Layer 3 (*.mp3)|*.mp3|All (*.*)|*.*";
DialogResult result = openFileDialog.ShowDialog(this);
if (result == DialogResult.OK)
{
backgroundWorker.RunWorkerAsync(openFileDialog.FileNames);
}
}
private BackgroundWorker CreateBackgroundWorker()
{
BackgroundWorker backgroundWorker = new BackgroundWorker();
backgroundWorker.WorkerReportsProgress = true;
backgroundWorker.WorkerSupportsCancellation = true;
backgroundWorker.ProgressChanged += new ProgressChangedEventHandler(OnBackgroundWorkerProgressChanged);
backgroundWorker.DoWork += new DoWorkEventHandler(OnBackgroundWorkerDoWork);
backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(OnBackgroundWorkerCompleted);
Trace.WriteLine(MethodBase.GetCurrentMethod().DeclaringType.Name + "." + MethodInfo.GetCurrentMethod().Name
+ "; Successfull.");
return backgroundWorker;
}
Regards,
Gareth.
(FKA gareth111)
|
|
|
|

|
Hi Gareth,
don't worry, you don't have to unsubscribe here.
Your Form creates a BGW and subscribes to it; the subscriptions contain the "this" and some method
of your form, and are held by the BGW. They don't prevent the BGW to do its job, to terminate,
and to get disposed of (e.g. by the using statement). So all is fine.
On the other hand they do prevent your form from being disposed of as long as the BGW lives.
So if your BGW would be running and never terminate, your Form, even when closed, would survive
and remain in memory. Running threads (as in BGW) remain in existence, even if you don't hold a
reference to them.
BTW: I would put the using (BackgroundWorker backgroundWorker = CreateBackgroundWorker())
line way below, after the DialogResult check; doing so you don't create a BGW for a user who cancels
the OFD.
Regards,
|
|
|
|

|
Cool, Cheers Luc.
Regards,
Gareth.
(FKA gareth111)
|
|
|
|

|
You're welcome.
|
|
|
|

|
.
Make it simple, as simple as possible, but not simpler.
|
|
|
|

|
Thanks for reading my article, your five, and Einstein's quote
|
|
|
|

|
Hello Luc!
I just added it to my bookmarks, and I'm sure I will use it very often when answering to questions in the forum.
What I like most, is:
The final question: do we have to remove event handlers?
As I do a lot of memory profiling on our project, I know this is the most often mistake which causes memory leaks.
Maybe you can point out the bad combination of:
A popular mistake: adding the same delegate more than once
and,
The final question: do we have to remove event handlers?
Because also if you remove the event once, the reference is holded by the multiply added delegate.
So, thanks for your time and work!
All the best,
Martin
|
|
|
|

|
Hi Martin,
thanks for reading my article.
Greetings,
Luc
|
|
|
|

|
Here are some suggestions for your article.
In your article, you state, "The event works somewhat like a collection: you can add delegates to it and, later on when you are no longer interested, you can remove them again." To me, this implies that an event is different from a delegate in that the event is a collection of delegates. In C#, both events and delegates (specifically instances of types declared with the "delegate" keyword) are collections, or more specifically, linked lists. So the difference between an event and a delegate is that an event restricts the adding and removing of a method to the += and -= operators. Furthermore, an event can only be invoked from the class where it is declared. Other than minor differences like the ones I mentioned, events and delegates are practically the same (both act like collections). In C#, declaring a type using the "delegate" keyword actually generates a class that derives from MulticastDelegate.
Microsoft furthers common misconceptions about a delegate by stating, "A delegate declaration defines a reference type that can be used to encapsulate a method with a specific signature. A delegate instance encapsulates a static or an instance method."
This is technically incorrect, since a delegate declaration is in essence a MulticastDelegate capable of encapsulating one or more static or instance methods.
Delegates (types declared using the keyword "delegate") are different than C++ function pointers in that delegates are much more than simple function pointers. MulticastDelegates are classes with linked list structures, and a relatively significant amount of code for managing linked list structures.
Regards,
Fernando
|
|
|
|

|
Hi Fernando,
thanks for reading my article, and sharing your additional information.
Only part of it was known to me, but even so I chose not to include it at the time;
I decided to orient my article to the practical aspects, inspired by several
questions that popped up in the discussion boards.
If I decide to give the article a more academic twist, I would present the theory
of operation and would then be very glad to include the facts you mentioned.
Best regards,
Luc Pattyn
|
|
|
|

|
Hi,
Nice clear article! A question related to the concludion "you should remove delegates from events when they reach outside the class itself; i.e. when you subscribe to external events, you should end your subscription when you are done."
I frequently have the situation where you don´t know when your done because your waiting on the event. For example Class1 adds delegate to some event for class2, and then must wait for class2 to fire the event. class2 may never do so for various reasons so there is no clear way (to me anyway when to end the subscription. Further, class1 may need to re-subscribe in the future to class2 event (leading to multiple calls to the class1 handler as described).
So far I have bypassed the problem by trying to remove delegates before I add them:
// remove a click handler if it already exists
button.Click -= button_Click;
// add a click handler
button.Click += button_Click;
Comments welcome, thanks!
Keith.
|
|
|
|

|
Your question about when to unregister:
That is actually the problem that the garbage collector should resolve: throw away the object if it is no longer used. But unfortunately, events prevent the gc from doing it's job (as you described it - there is still a reference on the listening object). The only solution to this is that this link would be a weak reference. There are some articles around that discuss this approach (sorry - I'm not good at remembering URLs )
Your question about multiple registrations:
We do it the same way if it is not possible to tell whether we already registered from the state of the object itself (some sort of process flow)
Another solution could be to write an explicit event add/remove in the class that provides the event (but not sure about it).
-^-^-^-^-^-
no risk no funk
|
|
|
|

|
Hi,
thank you for reading my article.
On the class1-class2 question:
In the most common scheme of things, there are two instances of different classes (I'll call
them master and slave), one instance (master) is responsible for both both adding to
and removing from the event, and normally the event is a member of the slave object.
So it is the master (the party that wants to get something) that takes the add initiative,
and later on the removal initiative.
Example: a form (master) can create a periodic timer (slave), and add some form delegate
to the timer's tick event. When the form is no longer interested in ticks, it can remove the
delegate. The timer would not know when to do that, it does not know what the form
is doing, it does not even know what a form is ("separation of concern", one of the
OO principles).
On the remove-add question:
this is quite acceptable; if you need a delegate called and are not sure whether you
have already added the delegate in the past, you can remove one then add one; the removal
would either succeed (if there was one), or it would silently fail. In both cases, you
would have none of your delegates present until you add one (that is in the assumption
you never added more than one, since a single remove removes at most one delegate).
Cheers !
|
|
|
|

|
I just scimmed over it, so pardon if this question is already answered.
What are your thoughts on
btn.Click = null;
|
|
|
|

|
Hi Marc,
right now the compiler does not like it; it generates an error:
"The event 'System.Windows.Forms.Control.Click' can only
appear on the left hand side of += or -="
And I dont think I ever wanted to be able to do it; although I could imagine
situations where you might want that capability.
I think there are two reasons why it is not provided:
1. I dont think it would really fit OO concepts: when others have expressed their
interest in an event, who has the right to terminate such interest ?
2. The next remark then would be btn.Click=new EventHandler(...); meaning forget
all earlier delegates, just set this one. But then a lot of people would do that
all the time, effectively ending someone elses subscription, this time by mistake.
I would rather have the opposite: this.Unsubscribe() basically telling the world I am
ending all my subscriptions (i.e. all the delegates I added to events in other objects
should be removed, so I can retire and be garbage collected without having to remove
the delegates myself one by one).
Or as an alternative: a weak subscription: give me Click event notification for as
long as I am alive (similar to weak references, and background threads).
|
|
|
|

|
TEST (UNRELATED TO ARTICLE)
in preparation of a future article I needed some code snippets
with varying tags, that is what this is about.
// without any tags
for (int i=0; i<10; i++) {
for (int j=0; j<10; j++) {
int k=i*j;
Console.WriteLine(k);
}
}
Notice the change in font and background
color when we open the PRE block here:
for (int i=0; i<10; i++) {
for (int j=0; j<10; j++) {
int k=i*j;
Console.WriteLine(k);
}
}
This is the line following the PRE block.
for (int i=0; i<10; i++) {
for (int j=0; j<10; j++) {
int k=i*j;
Console.WriteLine(k);
}
}
// with code tags, indentation uses 4 spaces or 1 tab
for (int i=0; i<10; i++) {
for (int j=0; j<10; j++) {
int k=i*j;
Console.WriteLine(k);
}
}
<br>for (int i=0; i<10; i++) {<br> for (int j=0; j<10; j++) {<br> int k=i*j;<br> Console.WriteLine(k);<br> }<br>}<br>
end of test
-- modified on Sunday 29th July, 2007
|
|
|
|

|
Hmmm, where have I seen this before
BTW, I found this by reading this article. Very well done job on the article, Luc.
"Real programmers just throw a bunch of 1s and 0s at the computer to see what sticks" - Pete O'Hanlon
|
|
|
|

|
Thanks Paul
In the mean time I did clean up a bit, without leaving your message dangling
of course. I can confirm when you delete a message, your total message count
gets decremented correctly.
|
|
|
|

|
Hi, you are right. The compiler doesn't like it to assign an other value to an event. When you design an event you have to do 3 steps 1. declare a private member for the event private event EventHandler<MyEventType> _EventName; 2. declare public accessors for adding and removing handlers public event EventHandler<MyEventType> EventName { add { _EventName += value; } remove { _EventName -= value; } } 3. provide a protected virtual OnXXX emthod protected virtual void OnEventName(MyEventType e) { if(_EventName != null) { try { _EventName(this,e); } catch(Exception e) { //your code here } } } The second point is the reason why the compiler doesn't like it when you assing null to an event.
|
|
|
|
 |
|
|
General News Suggestion Question Bug Answer Joke Rant Admin
Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.
|
Delegates: how to add them to an event, how they get removed and when that is necessary
| Type | Article |
| Licence | CPOL |
| First Posted | 24 Jul 2007 |
| Views | 49,891 |
| Downloads | 427 |
| Bookmarked | 62 times |
|
|