• Stars
    star
    182
  • Rank 211,154 (Top 5 %)
  • Language
    C#
  • License
    Apache License 2.0
  • Created over 9 years ago
  • Updated over 4 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Generic weak event implementation

WeakEvent

Logo

NuGet version AppVeyor build AppVeyor tests

Events are the most common source of memory leaks in .NET apps: the lifetime of the subscriber is extended to that of the publisher, unless you unsubscribe from the event. That's because the publisher maintains a strong reference to the subscriber, via the delegate, which prevents garbage collection of the subscriber.

This library provides a generic weak event source that can be used to publish events without affecting the lifetime of the subscriber. In other words, if there is no other reference to the subscriber, the fact that it has subscribed to the event doesn't prevent it from being garbage collected.

How to use it

Instead of declaring your event like this:

public event EventHandler<MyEventArgs> MyEvent;

Declare it like this:

private readonly WeakEventSource<MyEventArgs> _myEventSource = new WeakEventSource<MyEventArgs>();
public event EventHandler<MyEventArgs> MyEvent
{
    add { _myEventSource.Subscribe(value); }
    remove { _myEventSource.Unsubscribe(value); }
}

And raise it like this:

private void OnMyEvent(MyEventArgs e)
{
    _myEventSource.Raise(this, e);
}

That's it, you have a weak event! Client code can subscribe to it as usual, this is completely transparent from the subscriber's point of view.

Special case: anonymous method handlers

If you're subscribing to the event with an anonymous method (e.g. a lambda expression), make sure to keep a reference to the handler, otherwise it will be collected too soon. Alternatively, you can use the overload of Subscribe that accepts a lifetime object; this will cause the handler to stay alive as long as the lifetime object is alive (of course, in this case you can't use the event syntax; the event publisher will need to expose a specific method).

How does it work

A delegate is made of two things:

  • the method that will be called when the delegate is invoked
  • the target on which the method will be called (null for static methods)

If we store the delegate directly, we end up storing a strong reference to the target. So, instead, we store a "weak delegate", which is made of these things:

  • the method that will be called when the delegate is invoked
  • a weak reference to the target, so that we can access it when needed without preventing its garbage collection

So far, so good.

Now, how do we invoke this "weak delegate"? We could use MethodInfo.Invoke to call the method on the target through reflection, but it's very slow compared to a direct delegate call. So instead we take advantage of a little known feature of .NET: open-instance delegates. Basically, an open-instance delegate is a delegate that is bound to an instance method, but doesn't have a target. The signature of an open-instance delegate is the same as the equivalent normal delegate, with an extra parameter for the target (the this parameter). An example will make things clearer:

public delegate void     FooEventHandler(               object sender, FooEventArgs e);
public delegate void OpenFooEventHandler(object target, object sender, FooEventArgs e);

So, when someone subscribes to our weak event by passing a normal delegate, we create a weak delegate that wraps a weak reference to the target, and an open-instance delegate that is bound to the original delegate's method. When we need to invoke the weak delegate, we check if the target is still alive, and if it is, we invoke the open-instance delegate on it.

Credits

The package logo is made from two icons:

More Repositories

1

NHotkey

A managed library to handle global hotkeys in Windows Forms and WPF applications
C#
311
star
2

Linq.Extras

A set of extension methods to complement the ones from System.Linq.Enumerable
C#
172
star
3

AspNetCore.AsyncInitialization

Async initialization in ASP.NET Core 2.x
C#
87
star
4

CosmosDBStudio

A tool to browse and query Azure Cosmos DB databases
C#
54
star
5

Extensions.Hosting.AsyncInitialization

Async initialization for .NET 6.0+ apps using the generic host (e.g. ASP.NET Core apps)
C#
45
star
6

NString

A collection of utilities for working with strings in .NET.
C#
37
star
7

GenerateCSharpErrors

Just a small program to generate the full list of C# errors and warnings
C#
35
star
8

Iso8601DurationHelper

Small library to handle ISO8601 durations in C#
C#
33
star
9

HumanBytes

A library to convert byte sizes to a human readable form
C#
32
star
10

AutoRunCustomTool

A Visual Studio extension that automatically runs the custom tool on a file when a trigger file is modified
C#
30
star
11

InternalsVisibleTo.MSBuild

Enables declaring 'InternalsVisibleTo' items in a .NET project file, rather than declaring them to an AssemblyInfo.cs file.
28
star
12

AspNetCore.SignalR.AzureServiceBus

Provides scale-out support for ASP.NET Core SignalR using an Azure Service Bus topic to dispatch messages to all server instances.
C#
15
star
13

AspNetCore.SystemTextJson.MultipleSettings

Use different JSON serialization settings in ASP.NET Core per controller or per action
C#
11
star
14

DontMergeMeYet

A GitHub app that adds an "in progress" status on a pull request to avoid merging it prematurely
C#
11
star
15

Hamlet

“To be, or not to be, that is the question”. A simple Option type for .NET
C#
10
star
16

EssentialMVVM

Minimalist MVVM framework
C#
7
star
17

Quack

[POC] Duck-typing framework for .NET
C#
6
star
18

blog

My personal blog, made with Hugo
HTML
5
star
19

TestDeviceFlow

Code for the blog article at
C#
5
star
20

FluentIL

A thin fluent wrapper for ILGenerator (Reflection.Emit)
C#
5
star
21

TestLinqPerf

Small benchmark to compare Linq performance in .NET Core vs. .NET 4.6.2
C#
4
star
22

TypeScriptCosmosDBStoredProceduresArticle

Code for the article "Using TypeScript to write Cosmos DB stored procedures with async/await"
TypeScript
4
star
23

CleanUpNetCoreSdk

A tool to remove outdated .NET Core SDKs
C#
3
star
24

OAuthUI

A portable class library that provides a UI for OAuth authentication on desktop and mobile platforms.
C#
2
star
25

ToastHelper

A strongly typed helper for toast notifications in Windows Store apps
C#
2
star
26

AyendeCsvIndexQuestion

My attempt at solving Ayende's interview question on how to implement an index to quickly search a large CSV file
C#
2
star
27

GitLog

A simple tool to show the commit history of a Git repository, including changes in submodules
C#
2
star
28

Extensions.Caching.Extras

Adds features to Microsoft.Extensions.Caching such as partitioning and eviction
C#
2
star
29

thomaslevesque

2
star
30

NSettings

Extensible application settings framework with minimal boilerplate code
C#
2
star
31

Solo

Simple library to run app as single-instance and notify the existing instance, if any.
C#
2
star
32

BrowserSelector

[WORK IN PROGRESS] Simple tool to control in which browser a URL is opened, instead of always using the system default browser
C#
2
star
33

blog-code-samples

Sample code for my blog
C#
1
star
34

SharpADS

A library that provides access to NTFS alternate data streams
C#
1
star
35

GitBrowse

A command line tool to open the webpage for a GitHub repo
C#
1
star
36

GemBox

This repository contains various bits of code that don't fit anywhere else. Don't expect consistency here, there isn't any ;)
C#
1
star
37

TarSharp

A .NET library to read and write TAR archives
C#
1
star
38

blog-fr

L'archive de mon blog personnel en français, réalisée avec Hugo https://thomaslevesque.fr
HTML
1
star
39

Unity.Extras.AutoFactory

A Unity extension to automatically generate strongly typed factories at runtime.
C#
1
star
40

TestDeploy

Dummy repo to test automated deployment
C#
1
star
41

dotnet-project-templates

Project templates for my personal use. Probably only useful to me, but feel free to use if you want.
C#
1
star