Click here to Skip to main content
Email Password   helpLost your password?

A picture of the sample application running

Introduction

While moving from C++/MFC to C#/Windows Forms has been a very positive experience for me, it didn't quite live up to the expectation I had that I could send objects in all directions and never have to worry about deleting them when they are no longer required. The problem is, when you add an event handler to an event, the event will hold a reference to the object that contains the event handler, and so long as that reference is there the garbage collector can't collect the object containing the event handler. Thus while the need to call delete is gone, excessive use, on my behalf, of events has led me to a point where I need to track when objects are no longer required so that I can remove their event handlers from the event that they are servicing, and thus enable them to be garbage collected.

For a couple of years, I have been using many workarounds for this problem, including changing my coding style to reduce the incidence of this problem, and keeping lists of weak references of delegates that I iterate over to simulate events, but I finally I decided to sit down and write a simple-to-use solution to this problem that would allow me to use events in the way that I want to.

Background

I am not the first person to try to solve this problem. Microsoftie Greg Schechter has a very inspiring article on his blog about the problem in general, and the solution that he came up with. While the solution was good, it just didn't quite fill my needs as it required changing my existing codebase too much.

Ian Griffiths of Interact Software Ltd. UK had exactly the sort of solution I was looking for on his blog but unfortunately it didn't quite work. Tricky problem this one.

Wesner Moise mentions in his blog that he talked about weak delegates with the .NET Bass Class Library team when he had dinner with them back in March 2005, but I haven't seen any mention anywhere of when such a feature would be implemented so I've decided to go ahead with my own solution. He also mentions on the same page that he has seen two experts screw up an implementation of weak delegates, so here's hoping, with all the possible scrutiny that this article might get, that I haven't added my name to the list of people who have screwed this up.

Using the Code

I have two ways of using weak event handlers depending on whether you need speed, or simplicity. The simple way to use weak event handlers is to use the WeakEventHandlerFactory class which requires that you create one instance of the WeakEventHandlerFactory in your class:

private WeakEventHandlerFactory eventHandlers;

And then add as many event handlers as you want, to any event source, using the AddWeakHandler method:

eventHandlers.AddWeakHandler<MyEventArgs>(eventSource, 
              "EventSourceChanged", eventSource_EventSourceChanged);

To remove an event handler, use RemoveWeakHandler:

eventHandlers.RemoveWeakHandler<MyEventArgs>(eventSource, 
           "EventSourceChanged", eventSource_EventSourceChanged);

Not really too far removed from the code that is normally used to add and remove event handlers, is it? It should be noted that a new feature of the C# 2.0 compiler is that it will convert a function name to an event handler, so eventSource_EventSourceChanged will be converted to new EventHandler<MyEventArgs>(eventSource_EventSourceChanged).

When your object is garbage collected, the WeakEventHandlerFactory will get garbage collected around the same time, and its finalizer will remove all of the event handlers that it has created that haven't been removed yet. It doesn't matter if there is a delay between your object being garbage collected and the event handler factory being garbage collected as the event handler factory will not attempt to pass on events after your object has been garbage collected.

The caveat with using the event handler factory is that it is 40x slower than adding or removing normal event handlers. For some situations this is unacceptable, so I have created a WeakEventHandler<> class that requires a little more code to use, but in fact ends up being 2x faster to add and remove when compared to normal event handlers. To use Weak Event Handlers directly a little more code and care is required. For each event handler in your class, you need to have an object like the following in your class declaration:

private WeakEventHandler<MyEventArgs> eventSourceChangedHandler;

Then in your constructor you initialize the WeakEventHandler<> object in the following way:

eventSourceChangedHandler = new 
  WeakEventHandler<MyEventArgs>(eventSource_EventSourceChanged);

This is similar to the way you initialize a normal event handler, except that you need to keep an instance of the WeakEventHandler<> in your class. To use the WeakEventHandler<> object, simply add it or remove it as you would a normal event handler:

eventSource.EventSourceChanged += eventSourceChangedHandler;
eventSource.EventSourceChanged -= eventSourceChangedHandler;

The difference though is that you must remove the event handler before your object has been finalized otherwise your application will bomb with a null reference exception next time the event source fires an event that is handled by a WeakEventHandler<> in your object. Therefore your finalizer needs to remove the WeakEventHandler<> like so:

~MyClass
{
    eventSource.EventSourceChanged -= eventSourceChangedHandler;
}

Under the Hood

I've written four utility classes for creating Weak Event Handlers:

WeakReferenceToEventHandler

This class is exactly what the class name says. It is a weak reference to an event handler. This class is used by the WeakEventHandlerInternal only, and there should not be any need to use it directly.

This class contains methods to add an event handler to the event source, and remove an event handler from the event source. All the required details such as the event source object and the event name are stored in this class so that it can remove itself from the event source should the original event handler be garbage collected.

As events can't be passed in through methods, the AddHandler method uses reflection to add an intermediate event handler to the event source:

public void AddHandler(object eventSource, string eventName)
{
    // Store the event source details

    this.eventName = eventName;
    weakReferenceToEventSource = new WeakReference(eventSource);

    // Create and intermediate handler that the event source will

    // have a strong reference to.

    EventInfo eventInfo = eventSource.GetType().GetEvent(eventName);
    eventInfo.AddEventHandler(eventSource, 
      new EventHandler<TEventArgs>(IntermediateEventHandler));
}

Similarly, the RemoveHandler method uses reflection to remove the intermediate event handler:

public void RemoveHandler()
{
    if (weakReferenceToEventSource==null)
      return;

    object eventSource = weakReferenceToEventSource.Target;
    if (eventSource != null)
    {
        // Get the event using reflection

        EventInfo eventInfo = eventSource.GetType().GetEvent(eventName);

        // Remove the intermediate event handler, which will dereference

        // this weak reference and allow it to be garbage collected.

        eventInfo.RemoveEventHandler(eventSource, 
          new EventHandler<TEventArgs>(IntermediateEventHandler));
    }
}

When the event gets fired, the intermediate event handler handles it. It's at this point that a test is done to see if the original event handler has been garbage collected, and if so then the intermediate event handler removes itself from the event source.

WeakEventHandlerInternal

The WeakEventHandlerInternal class is used internally for storing weak event handlers. This class should not be used directly, so I made the constructor internal. I have a class called WeakEventHandler to be used directly that is 8x faster than WeakEventHandlerInternal for adding and removing events. The WeakEventHandlerInternal class is a container for the strong reference to the original event handler, and also contains an instance of the WeakReferenceToEventHandler class. It contains methods for adding, removing, and comparing weak event handlers for equality. It is used by the WeakEventHandlerFactory class.

WeakEventHandlerFactory

The WeakEventHandlerFactory is the class that can be used to create weak event handlers. It contains two methods:

The AddWeakHandler method creates a weak reference to the original event handler, but also stores a strong reference to the event handler so that it isn't garbage collected as soon as we leave the method. The weak reference and strong reference are stored together in an object that is added to an internal list.

The RemoveWeakHandler does a search through all the stored event handlers looking for a match, and removes the event handler if it finds a match. This method returns a flag that indicates if it was successful or not.

WeakEventHandler

This class can be used directly, but cautiously, for directly creating weak event handlers. It sacrifices ease of use for speed. An instance of WeakEventHandler must be held for each event handler that is represented by a WeakEventHandler object, and each WeakEventHandler object must be removed from the source event before the event handling object is garbage collected.

Using the Sample Application

If you compile the project linked in at the top of this article, you will get an application that looks like the image at the top. This application demonstrates the usefulness of a weak event handler.

The application has three DataGrid controls that are used for editing the contents of up to three collections. Above each DataGrid is a unique identifier for the collection being edited in that DataGrid, as well as the total of all the values held in that collection.

If you edit one of the collection items, the total displayed above the DataGrid will be updated. If you remove one of the items, you will see a message in the log window indicating that the event handler was successfully removed from the item that you removed from the collection. From this you can deduce that each item in the collection has an event that fires when you change the item. Thus each item has a reference to its parent collection(s) as it is the collection that handles the event and updates the value of the total held in the collection object. The purpose of this application is to show that a weak reference to the event handler is used allowing a container of objects (the collection) to be garbage collected if it is no longer required, even if the items in the collection still exist.

Now for the fun part. Below each DataGrid are four buttons. They allow you to display, or copy one of the other two collections.

When you copy a collection, a new collection is created and the objects from the source collection are copied into it. Hitting the "Force Garbage Collection" button will force the replaced collection to be garbage collected if it is no longer being displayed anywhere, and the log window will show a message indicating which collection was garbage collected.

Editing an item in a copied collection will update the collection it was copied from, but items added to or removed from the new collection will have no effect on the source collection.

When you "View a Set", the actual collection itself will then be displayed in the destination DataGrid, as well as the source DataGrid. Actions of adding/deleting/editing objects in one DataGrid will be mirrored in the other DataGrid.

Speed Testing

The sample application includes a little benchmark to show how fast different event handler types can be added and removed. I tested adding and removing 1,000,000 event handlers on an old 3.0GHz Pentium 4 and got the following results:

Speed Test: Adding and Removing 1000000 Event Handlers

Adding Normally.........Seconds: 0.5781361
Removing Normally.........Seconds: 0.3906325
Adding WeakEventHandler.........Seconds: 0.156253
Removing WeakEventHandler.........Seconds: 0.2343795
Adding WeakEventHandlerInternal.........Seconds: 2.2812938
Removing WeakEventHandlerInternal.........Seconds: 1.9687878
Adding using WeakEventHandlerFactory.........Seconds: 19.7191286
Removing using WeakEventHandlerFactory.........Seconds: 17.1253288

In the sample application I have set the number of event handlers to add and remove to 100,000 so that you don't have to wait too long to see your own results.

Sample Code Included in the Sample Application

There are two collection classes included in the source code for the sample application:

SubTotalCollection uses the EventHandlerFactory to create weak event handlers, and SubTotalCollectionFast uses the WeakEventHandler class directly. SubTotalCollectionFast is a good example of managing WeakEventHandler objects. It adds a handler when a SubTotal object is added to the collection, it removes a handler when a SubTotal object is removed from the collection, and when it is finalized or cleared it removes event handlers from all the SubTotal objects still in the collection.

The sample application just uses the SubTotalCollection class. To make it use the SubTotalCollectionFast class, just do a search and replace in the Form1.cs file replacing SubTotalCollection with SubTotalCollectionFast.

Points of Interest

The new DataGrid control is a lot easier to use than the old one, for editing simple collections. In this sample application, I have objects of type SubTotal in a collection that I want to edit in a DataGrid. There were three steps to doing this. The first was declaring a collection class that inherited from BindingList, like so:

public class SubTotalCollection : BindingList<SubTotal>

Then in Form.cs I created a binding source, attached it to the DataGrid, and attached a collection to the binding source, and that was it:

BindingSource leftSource = new BindingSource();
dataGridViewLeft.DataSource = leftSource;
leftSource.Source = new SubTotalCollection();

Between this version and the first version posted on September 23rd, 2005, I had many attempts at making the factory faster. After putting in a Dictionary for finding matching event handlers in the RemoveHandler method I tried changing it to SortedDictionary<,>, however for this purpose the SortedDictionary<,> seemed to take twice as long as using the Dictionary<,> class.

History

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
QuestionTradeoff: Speed vs. Knowing your sources
Thomas Hamilton
10:03 24 Oct '08  
Just to clarify:

If you use the WeakEventHandler then the handler class has to keep track of all events to which it has subscribed so that it can unsubscribe on destruction?

If you use the WeakEventHandlerFactory you don't have to keep track of sources but event subscriptions are an order of magnitude slower?

And there's no good way to bridge those tradeoffs?
AnswerRe: Tradeoff: Speed vs. Knowing your sources
John Stewien
23:43 26 Oct '08  
I haven't had a chance yet to experiment with the implementation from this article but it might have the answer. There is some benchmarking in that article, so the author has thought about it.
GeneralIt's illegal
uecasm
17:12 11 Jun '07  
You're not allowed to access .NET objects from the finaliser of a different .NET object, since you don't know what order they were finalised in. So while (depending on the lifecycles of the objects) this may work some of the time, it is inherently broken.
GeneralRe: It's illegal
John Stewien
3:27 15 Jun '07  
You're right. Bummer. Throwing an exception in the finalizer is a rather nasty one too.

I could change the finalizer to the following to catch the invalid reference exception:
~MyClass
{
try {
eventSource.EventSourceChanged -= eventSourceChangedHandler;
}
catch(Exception e)
{
}
}
That could cause performance problems though. Maybe as an additional measure I could make the eventSource a weak reference and check it's validity before using it in the finalizer, but that's adding more complexity.
GeneralRe: It's illegal
uecasm
14:03 17 Jun '07  
The thing is that using it may not even throw a catchable exception -- the reference may be pointing to freed memory, and trying to call through it may corrupt the managed stack and abort the program.

I'm not sure if even using a WeakReference is safe. It depends on what stage the WeakReference decides that the object is dead -- and given that .NET supports the concept of object resurrection (by assigning a reference to the object being finalised within some object whose lifetime is known to have not yet finished, thereby cancelling the finalisation) it's probably after the finalisers are called.

So far out of all the alternative implementations I've seen, only Greg's seems to be fully reliable, but it's not really genericisable. (Yours should work ok in cases where you're absolutely certain that the lifetime of the event source exceeds that of the event target, but if there's ever a time when they can both go out of scope together then you're in trouble.)
AnswerRe: It's illegal [modified]
John Stewien
23:01 19 Jun '07  
I've been trying to force an error situation with this, and I just couldn't get an exception or anything. In the end I wrote a simple test that I felt sure would throw an exception. I wrote this class:
public class Peer
{
private string name;
public Peer Peer1;
public Peer Peer2;
public Peer Peer3;

public Peer(string name)
{
this.name = name;
}

public void PrintName()
{
Trace.WriteLine("Printing "+name);
}

~Peer()
{
Trace.WriteLine("Start Finalizing " + name);
if (Peer1 != null)
Peer1.PrintName();
if (Peer2 != null)
Peer2.PrintName();
if (Peer3 != null)
Peer3.PrintName();
Trace.WriteLine("End Finalizing " + name);
}
}

Then I tested it like this:
Peer peer1 = new Peer("Peer 1");
Peer peer2 = new Peer("Peer 2");
Peer peer3 = new Peer("Peer 3");
Peer peer4 = new Peer("Peer 4");

peer1.Peer1 = peer2;
peer1.Peer2 = peer3;
peer1.Peer3 = peer4;

peer2.Peer1 = peer1;
peer2.Peer2 = peer3;
peer2.Peer3 = peer4;

peer3.Peer1 = peer1;
peer3.Peer2 = peer2;
peer3.Peer3 = peer4;

peer4.Peer1 = peer1;
peer4.Peer2 = peer2;
peer4.Peer3 = peer3;

peer1 = null;
peer2 = null;
peer3 = null;
peer4 = null;

GC.Collect();
GC.WaitForPendingFinalizers();
Now given that each peer calls each of the others in the finalizer it should be sure to cause an error because after the first finalization at least one of the objects should be finalized before it is called from each of the other finalizers. Result? No exceptions, just the following output:
Start Finalizing Peer 4
Printing Peer 1
Printing Peer 2
Printing Peer 3
End Finalizing Peer 4
Start Finalizing Peer 3
Printing Peer 1
Printing Peer 2
Printing Peer 4
End Finalizing Peer 3
Start Finalizing Peer 2
Printing Peer 1
Printing Peer 3
Printing Peer 4
End Finalizing Peer 2
Start Finalizing Peer 1
Printing Peer 2
Printing Peer 3
Printing Peer 4
End Finalizing Peer 1
Confused Well I'm floored by that. It would appear as if there isn't going to be any error. An error situation just seems impossible to produce here. Probably why I haven't actually discovered this problem before. There must be another pass by the garbage collector after the finalizer and objects must not be removed until after finalization if they are referenced in a finalizer.


-- modified at 4:08 Wednesday 20th June, 2007
GeneralRe: It's illegal
uecasm
13:47 20 Jun '07  
Try using virtual methods, and possibly throw in some inheritance and overriding as well. Non-virtual methods normally won't cause problems, nor will accessing strings, since both are at fixed addresses Smile

It's also a very intermittent thing, and I'm not sure if your test case is sufficiently GC-aggravating Wink
GeneralRe: It's illegal
John Stewien
19:16 20 Jun '07  
I used this:

public class PeerA
{
protected char[] name;
public PeerA Peer1;
public PeerA Peer2;
public PeerA Peer3;

public PeerA(string name)
{
this.name = name.ToCharArray();
}

public virtual void PrintName()
{
Trace.WriteLine("Printing " + new string(name));
}

~PeerA()
{
if (Peer3 != null)
Peer3.PrintName();
Trace.WriteLine("End Finalizing " + new string(name));
}
}

public class PeerB : PeerA
{

public PeerB(string name): base(name)
{
}

public override void PrintName()
{
Trace.WriteLine("Printing " + new string(name) + " From Override");
}

~PeerB()
{
Trace.WriteLine("Start Finalizing " + new string(name));
if (Peer1 != null)
Peer1.PrintName();
if (Peer2 != null)
Peer2.PrintName();
}
}

and called it like this:

PeerA peer1, peer2, peer3, peer4;

for (int i = 0; i < 10000; ++i)
{
peer1 = new PeerB("Peer 1");
peer2 = new PeerB("Peer 2");
peer3 = new PeerB("Peer 3");
peer4 = new PeerB("Peer 4");

peer1.Peer1 = peer2;
peer1.Peer2 = peer3;
peer1.Peer3 = peer4;

peer2.Peer1 = peer1;
peer2.Peer2 = peer3;
peer2.Peer3 = peer4;

peer3.Peer1 = peer1;
peer3.Peer2 = peer2;
peer3.Peer3 = peer4;

peer4.Peer1 = peer1;
peer4.Peer2 = peer2;
peer4.Peer3 = peer3;
}
peer1 = null;
GC.Collect();
GC.WaitForPendingFinalizers();
peer2 = null;
GC.Collect();
GC.WaitForPendingFinalizers();
peer3 = null;
GC.Collect();
GC.WaitForPendingFinalizers();
peer4 = null;
GC.Collect();
GC.WaitForPendingFinalizers();

Worked no problem D'Oh! , took a while to run but got 10,000 mixed up copies of this in the output debug window:

Start Finalizing Peer 2
Printing Peer 1 From Override
Printing Peer 3 From Override
Printing Peer 4 From Override
End Finalizing Peer 2 Start Finalizing Peer 1 Printing Peer 2 From Override
Printing Peer 3 From Override
Printing Peer 4 From Override
End Finalizing Peer 1 Start Finalizing Peer 4 Printing Peer 1 From Override
Printing Peer 2 From Override
Printing Peer 3 From Override
End Finalizing Peer 4 Start Finalizing Peer 3 Printing Peer 1 From Override
Printing Peer 2 From Override
Printing Peer 4 From Override
End Finalizing Peer 3

I don't think you are going to be able to come up with an example that proves your point.
GeneralRe: It's illegal
uecasm
19:31 20 Jun '07  
Like I said, I don't know what the failure case is exactly but the official documentation says that it's unreliable and that you shouldn't do it. (Which is why the Dispose pattern has the 'disposing' parameter.)

Presumably they know what they're talking about.

GeneralRe: It's illegal
Chernichkin Stanislav
2:00 30 Aug '07  
It's legal. There is even no necessity to use try catch block because finalizer will be translated to:

protected override void Finalize()
{
try
{
// cleanup statements...
}
finally
{
base.Finalize();
}
}

Memory reallocation happens after all finalizers in queue called, so there is no memory issues.
GeneralRe: It's illegal
supercat9
16:11 15 Aug '08  
Memory reallocation happens after all finalizers in queue called, so there is no memory issues.
How do the garbage-collection generations interact? If a Generation 2 object holds a reference to a Generation 0 object, and no other reference exists to either, will the Generation 0 object be immune from collection until the Generation 2 object is collected? Is there any guarantee of that?

If there is no guarantee of such behavior, I would see no reason to believe that the finalization queue could not have been completed after cleaning up one object (allowing its space to be re-used) even though the other object is still around.
GeneralHave you read this article?
Zoltan Balazs
4:59 10 Mar '07  
here on CodeProject[^] it gives a solution for .net 1.1.



GeneralRe: Have you read this article?
John Stewien
5:02 10 Mar '07  
Looks similar to one of the references in my article that is a blog entry by greg schechter posted here in 2004 http://blogs.msdn.com/greg_schechter/archive/2004/05/27/143605.aspx[^]
GeneralRe: Have you read this article?
Zoltan Balazs
5:09 10 Mar '07  
Yes, it references the article and others too.


GeneralRe: Have you read this article?
John Stewien
5:12 10 Mar '07  
I should have read his article more thoroughly before replying to you. It is different. Nice design actually.
GeneralRe: Have you read this article?
Zoltan Balazs
5:23 10 Mar '07  
uhum.
I liked your article too Smile



GeneralRe: Have you read this article?
John Stewien
5:27 10 Mar '07  
Thanks Big Grin I nearly got jealous of the other article OMG
GeneralRe: Have you read this article?
Zoltan Balazs
5:23 10 Mar '07  
John Stewien wrote:
It is different. Nice design actually.
uhum.

I liked your article too Smile



General.How to find Path of a given file in C#
19:55 9 Mar '07  
Hi,
I want to get full path for a given file in C#. Rose
GeneralRe: .How to find Path of a given file in C#
John Stewien
3:14 10 Mar '07  
You could try asking your question in the C# forums as the people in there would be expecting general questions.

http://www.codeproject.com/script/comments/forums.asp?forumid=1649[^]

Also you might want to specify more information like how did you get the file information that you are trying to find the path of, as normally you get passed the full path of a file from an file open dialog box.
QuestionFailure on very basic load test
Stéphane Issartel
6:17 6 Mar '07  
Hello,

a few days ago, I was trying to develop an application where an object (say: class SubsetOfData) was a subset of a bigger object (say: class SetOfData). And I wanted the subset of data to be notified with changes in the bigger set. That's where I fall in the issue your article perfectly describes.  

I have tried your solution and unfortunaltly, this caused my unit tests to fail... You will find below a simplified version of the tests you can almost copy/paste to NUnit to show the issue:

"Even if I clearly remove the event handler before finalization in the subset object, test case "WeakEventHandlerTest2" bombs with a null reference exception at unpredectible values for k."


I really hope to have pointed out a real problem instead of stupidly pointing out a wrong usage of your set of classes. Anyway, I really look forward for your answer.

Best Regards to all,

Stéphane


PS: Maybe another stupid question here but why events have not been created as 'weak references' by design ?

<code>
[TestFixture]
public class WeakHandlersTests
{
      // Class that fires the notification
      public class SetOfData
      {
            public void ForceChanged() { if (Changed != null) { Changed(this, new EventArgs()); } }
            public event EventHandler<EventArgs> Changed;
      }

      // Class that subscribe to the notification                 
      public class SubsetOfData
      {
            public SubsetOfData(SetOfData allData)
            {
                  this.allData = allData;
                  SetOfDataChangedWeakEventHandler = new WeakEventHandler<EventArgs>(OnSetOfDataChanged);
                  allData.Changed += SetOfDataChangedWeakEventHandler;
            }
            ~SubsetOfData()
            {
                  allData.Changed -= SetOfDataChangedWeakEventHandler;
            }
            void OnSetOfDataChanged(Object sender, EventArgs e)
            {
               // SetOfData changed ... do appropriate update stuff here ...
            }

            SetOfData allData;
            WeakEventHandler<EventArgs> SetOfDataChangedWeakEventHandler;
      }

      [Test]
      public void WeakEventHandlerTest1()
      {
            // THIS TEST WILL NOT FAIL
           
            // Create a set of data
            SetOfData set = new SetOfData();
           
            // Repeatively create /destroy subset of data (destroy: upon GC will...)
            for (int k = 0; k < 10000; k++)
            {
                  SubsetOfData subset = new SubsetOfData(set);
            }
      }

      [Test]
      public void WeakEventHandlerTest2()
      {
            // THIS TEST WILL FAIL
            SetOfData set = new SetOfData();

            // Repeatively create /destroy subset of data (and force change event to be fired)           
            for (int k = 0; k < 10000; k++)
            {
                  try
                  {
                        SubsetOfData subset = new SubsetOfData(set);
                        set.ForceChanged();
                  }
                  catch(Exception e)
                  {
                        Assert.Fail(e.Message + ", k=" + k.ToString());
                  }
            }
      }
}
</code>
AnswerRe: Failure on very basic load test
John Stewien
14:26 6 Mar '07  
Hi Stéphane, I think that you have indeed found a real problem with the faster weak event handler. The faster weak event handler was added a month after I wrote the original code, and it would appear that I didn't test it properly D'Oh! . I think the fix might be to change RemoveHandler() in the WeakReferenceToEventHandler class to this:

public void RemoveHandler()
{
if (weakReferenceToEventSource == null)
return;
object eventSource = weakReferenceToEventSource.Target;
if (eventSource != null)
{
// Get the event using reflection
EventInfo eventInfo = eventSource.GetType().GetEvent(eventName);

// Remove the intermediate event handler, which will dereference
// this weak reference and allow it to be garbage collected.
eventInfo.RemoveEventHandler(eventSource, new EventHandler(IntermediateEventHandler));
}
}

And change this propery just to be complete
public object EventSource
{
get
{
if (weakReferenceToEventSource != null)
return weakReferenceToEventSource.Target;
else return null;
}
}

Hopefully I'll get a chance to test this soon. Thanks for finding this. Weak events was just something Anders Hejlsberg didn't consider when he designed C#, he also regrets needing to check to see if an event handler is null before calling it, but once the language is out there it's hard to change these things. There was a lot of pressure at the time to get C# out the door.
AnswerRe: Failure on very basic load test
John Stewien
19:20 6 Mar '07  
The fix I posted earlier works. I did a functional test using your code as a basis and it seems to be ok. I've sent an update of the article to The Code Project with the fix in it for them to post, meanwhile you can use the fix I posted in the comments earlier.

Thanks again for finding this bug. I wonder how many people have tried this over the last 18 months, hit this bug, and then passed off this code as simply not working?
NewsRe: Failure on very basic load test
Stéphane Issartel
20:46 6 Mar '07  
Hi John,

thanks for answering so quickly.

Yesterday I tried to dive into your code, but I was too tired to get what the hell was happening and could not even understand where the event source was set internally.

To me it seemed that AddHandler was never called ... that's why is it 'null' ... maybe I'm not initializing things correctly, I just created the weakreferencehandler object in my class and did a += and -= as describe in the article, I do not call AddHandler manually.

Today, I'll have a deeper look to the code, try your fix and will let you know.

Thx,
Stéphane.
GeneralRe: Failure on very basic load test
John Stewien
20:49 6 Mar '07  
AddHandler() is only called if you use the slower WeakEventHandlerFactory, the faster method you are using was added later as an after thought when I discovered how slow the WeakEventHandlerFactory was.


Last Updated 9 Mar 2007 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010