WeakEvent for .NET

repository·master·Indexed 42 days ago

https://github.com/thomaslevesque/weakevent

A .NET library providing a generic weak event source via WeakEventSource<TEventArgs> to prevent memory leaks. It ensures publishers do not hold strong references to subscribers, allowing them to be garbage collected without explicit unsubscription while maintaining standard C# event syntax.

Tokens
860
Snippets
2
Records
3
Agent score
2%

What's inside WeakEvent

  1. Handle anonymous method handlers in weak events

    master

    When subscribing to a weak event using an anonymous method or a lambda expression, the handler itself might be garbage collected prematurely if no other reference to it exists.

    To prevent this, you have two options:

    1. Maintain a reference: Explicitly store the handler (the lambda/delegate) in a variable so it stays alive.
    2. Use a lifetime object: Use the Subscribe overload that accepts a lifetime object. This ensures the handler remains alive as long as the provided lifetime object is alive. Note that using this method requires the publisher to expose a specific subscription method rather than using standard C# event syntax.
  2. How WeakEvent works internally

    master

    The library avoids memory leaks by replacing standard delegates with a custom mechanism involving "weak delegates" and "open-instance delegates":

    1. Weak Delegate: Instead of storing a strong reference to the target object, the library stores a weak reference to the target alongside the method information.
    2. Open-Instance Delegates: To avoid the performance penalty of MethodInfo.Invoke (reflection), the library uses .NET's open-instance delegate feature. An open-instance delegate is bound to an instance method but lacks a target. Its signature includes an extra parameter for the target (the this parameter).
    3. Invocation: When an event is raised, the library checks if the weak reference to the target is still alive. If it is, it invokes the open-instance delegate using the target instance.
    // Conceptual example of open-instance delegate signatures
    public delegate void     FooEventHandler(               object sender, FooEventArgs e);
    public delegate void OpenFooEventHandler(object target, object sender, FooEventArgs e);
  3. Implement a weak event source using WeakEventSource<T>

    master

    To prevent memory leaks caused by strong references in standard .NET events, use WeakEventSource<TEventArgs> to manage event subscriptions. This allows subscribers to be garbage collected even if they haven't explicitly unsubscribed.

    To implement this, follow these three steps:

    1. Create a private WeakEventSource<TEventArgs> field.
    2. Wrap the event's add and remove accessors to call _myEventSource.Subscribe(value) and _myEventSource.Unsubscribe(value) respectively.
    3. Raise the event by calling _myEventSource.Raise(this, e).

    This implementation is transparent to the subscriber; they can use standard C# event syntax (+= and -=) as usual.

    // 1. Define the source
    private readonly WeakEventSource<MyEventArgs> _myEventSource = new WeakEventSource<MyEventArgs>();
    
    // 2. Wrap the event
    public event EventHandler<MyEventArgs> MyEvent
    {
        add { _myEventSource.Subscribe(value); }
        remove { _myEventSource.Unsubscribe(value); }
    }
    
    // 3. Raise the event
    private void OnMyEvent(MyEventArgs e)
    {
        _myEventSource.Raise(this, e);
    }