From 95c28747d77a90b9dff5c4188f691c0692b540cd Mon Sep 17 00:00:00 2001 From: Dusty <21290407+TheDusty01@users.noreply.github.com> Date: Sat, 3 Dec 2022 17:49:08 +0100 Subject: [PATCH] Refactored the library and upgraded to .NET Standard 2.1 * Removed the requirement to inherit your event classes from AsyncEventArgs * Structs can now be used for event args * Added AsyncEventHandlers without event args * Updated samples --- .../AsyncEventHandlers.Benchmarks.csproj | 2 +- AsyncEventHandlers.Benchmarks/Program.cs | 56 ++-- AsyncEventHandlers/AsyncEvent.cs | 29 ++ AsyncEventHandlers/AsyncEventArgs.cs | 26 +- AsyncEventHandlers/AsyncEventHandler.cs | 270 ++++++++++-------- .../AsyncEventHandlerDelegate.cs | 24 +- AsyncEventHandlers/AsyncEventHandlers.csproj | 8 +- AsyncEventHandlers/WeakAsyncEventHandler.cs | 135 +++++++++ README.md | 26 +- ...AsyncEventHandlers.Samples.Delegate.csproj | 2 +- Samples/Delegate/FakeWebsocketServer.cs | 77 +++-- Samples/Delegate/Program.cs | 73 +++-- .../AsyncEventHandlers.Samples.Type.csproj | 2 +- Samples/Type/FakeWebsocketServer.cs | 81 +++--- Samples/Type/Program.cs | 92 +++--- 15 files changed, 545 insertions(+), 358 deletions(-) create mode 100644 AsyncEventHandlers/AsyncEvent.cs create mode 100644 AsyncEventHandlers/WeakAsyncEventHandler.cs diff --git a/AsyncEventHandlers.Benchmarks/AsyncEventHandlers.Benchmarks.csproj b/AsyncEventHandlers.Benchmarks/AsyncEventHandlers.Benchmarks.csproj index dd92046..dbf8090 100644 --- a/AsyncEventHandlers.Benchmarks/AsyncEventHandlers.Benchmarks.csproj +++ b/AsyncEventHandlers.Benchmarks/AsyncEventHandlers.Benchmarks.csproj @@ -2,7 +2,7 @@ Exe - net6.0 + net7.0 enable enable diff --git a/AsyncEventHandlers.Benchmarks/Program.cs b/AsyncEventHandlers.Benchmarks/Program.cs index a9790a1..365aa3d 100644 --- a/AsyncEventHandlers.Benchmarks/Program.cs +++ b/AsyncEventHandlers.Benchmarks/Program.cs @@ -11,24 +11,36 @@ public static void Main() BenchmarkRunner.Run(); } - private static AsyncEventHandler Struct_AsyncEventHandler = new AsyncEventHandler(); - private static event AsyncEventHandlerDelegate Delegate_AsyncEventHandler; + private static AsyncEventHandler Struct_AsyncEventHandler = new AsyncEventHandler(); + private static WeakAsyncEventHandler WeakAsyncEventHandler = new WeakAsyncEventHandler(); + private static event AsyncEventHandlerDelegate Delegate_AsyncEventHandler; + + private readonly List eventSources = new List(); public Program() { for (int i = 0; i < 100; i++) { + eventSources.Add(new object()); + + Struct_AsyncEventHandler += Struct_AsyncEventHandler_Event; + WeakAsyncEventHandler.Register(eventSources[i], WeakAsyncEventHandler_Event); Delegate_AsyncEventHandler += Delegate_AsyncEventHandler_Event; } } - private Task Struct_AsyncEventHandler_Event(object? sender, AsyncEventArgs e) + private Task Struct_AsyncEventHandler_Event(CancellationToken ct) { return Task.CompletedTask; } - private Task Delegate_AsyncEventHandler_Event(object? sender, AsyncEventArgs e) + private Task WeakAsyncEventHandler_Event(CancellationToken ct) + { + return Task.CompletedTask; + } + + private Task Delegate_AsyncEventHandler_Event(object? sender, IAsyncEventArgs e) { return Task.CompletedTask; } @@ -36,8 +48,12 @@ private Task Delegate_AsyncEventHandler_Event(object? sender, AsyncEventArgs e) private Task Struct_Call() { - var args = new AsyncEventArgs(); - return Struct_AsyncEventHandler.InvokeAsync(this, args); + return Struct_AsyncEventHandler.InvokeAsync(); + } + + private Task Weak_Call() + { + return WeakAsyncEventHandler.InvokeAsync(); } private ValueTask Delegate_Call() @@ -53,24 +69,30 @@ public async Task Struct_Call_1() } [Benchmark] - public async Task Delegate_Call_1() + public async Task Weak_Call_1() { - await Delegate_Call(); + await Weak_Call(); } [Benchmark] - public async Task Struct_Call_100() + public async Task Delegate_Call_1() { - for (int i = 0; i < 100; i++) - await Struct_Call(); + await Delegate_Call(); } - [Benchmark] - public async Task Delegate_Call_100() - { - for (int i = 0; i < 100; i++) - await Delegate_Call(); - } + //[Benchmark] + //public async Task Struct_Call_100() + //{ + // for (int i = 0; i < 100; i++) + // await Struct_Call(); + //} + + //[Benchmark] + //public async Task Delegate_Call_100() + //{ + // for (int i = 0; i < 100; i++) + // await Delegate_Call(); + //} } } \ No newline at end of file diff --git a/AsyncEventHandlers/AsyncEvent.cs b/AsyncEventHandlers/AsyncEvent.cs new file mode 100644 index 0000000..d6aae04 --- /dev/null +++ b/AsyncEventHandlers/AsyncEvent.cs @@ -0,0 +1,29 @@ +using System.Threading.Tasks; +using System.Threading; + +namespace AsyncEventHandlers; + +/// +/// The "action" delegate which should be supplied to the +/// and +/// methods. +/// +/// This shouldn't be used with the keyword. +/// +/// Event data to pass to each subscriber. +/// An object that contains the event data. +/// The to communicate cancellation of the async operation. +/// +public delegate Task AsyncEvent(TEventData data, CancellationToken cancellationToken); + +/// +/// The "action" delegate which should be supplied to the +/// and +/// methods. +/// +/// This shouldn't be used with the keyword. +/// +/// Event data to pass to each subscriber. +/// The to communicate cancellation of the async operation. +/// +public delegate Task AsyncEvent(CancellationToken cancellationToken); \ No newline at end of file diff --git a/AsyncEventHandlers/AsyncEventArgs.cs b/AsyncEventHandlers/AsyncEventArgs.cs index ebf4ce6..b02c392 100644 --- a/AsyncEventHandlers/AsyncEventArgs.cs +++ b/AsyncEventHandlers/AsyncEventArgs.cs @@ -1,15 +1,17 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Threading; +using System.Threading; -namespace AsyncEventHandlers +namespace AsyncEventHandlers; + +/// +/// The is automatically set. +/// +public interface IAsyncEventArgs { - /// - /// The is automatically set. - /// - public class AsyncEventArgs : EventArgs - { - public CancellationToken CancellationToken { get; internal set; } - } + CancellationToken CancellationToken { get; internal set; } } + +/// +public class AsyncEventArgs : IAsyncEventArgs +{ + public CancellationToken CancellationToken { get; set; } +} \ No newline at end of file diff --git a/AsyncEventHandlers/AsyncEventHandler.cs b/AsyncEventHandlers/AsyncEventHandler.cs index b2ac5a3..4f9b1fc 100644 --- a/AsyncEventHandlers/AsyncEventHandler.cs +++ b/AsyncEventHandlers/AsyncEventHandler.cs @@ -1,156 +1,176 @@ using System; using System.Collections.Generic; -using System.Text; using System.Threading; using System.Threading.Tasks; -namespace AsyncEventHandlers +namespace AsyncEventHandlers; + +/// +/// A thread-safe asynchronus event handler. +/// +public struct AsyncEventHandler { /// - /// The "action" delegate which should be supplied to the - /// and - /// methods. - /// - /// This shouldn't be used with the keyword. + /// All registered events. + /// Do not manually add items without locking. /// - /// or a derived type - /// The source of the event. - /// An object that contains the event data. - /// - public delegate Task AsyncEvent(object? sender, TEventArgs e) - where TEventArgs : AsyncEventArgs; + public HashSet Callbacks { get; private set; } = new HashSet(); + public AsyncEventHandler() + { + } /// - /// A thread-safe asynchronus event handler. + /// Subscribe to the event. /// - /// The generic type which holds the event data. - public struct AsyncEventHandler - where TEventArgs : AsyncEventArgs + /// The action which should get executed when the event fires. + public void Register(AsyncEvent callback) { - /// - /// All registered events. - /// - public HashSet> Callbacks { get; private set; } = new HashSet>(); + Callbacks ??= new HashSet(); - public AsyncEventHandler() - { - } + lock (Callbacks) + Callbacks.Add(callback); + } - /// - /// Subscribe to the event. - /// - /// The action which should get executed when the event fires. - public void Register(AsyncEvent callback) - { - if (Callbacks is null) - Callbacks = new HashSet>(); + /// + /// Unsubscribe from the event. + /// + /// The action which shouldn't get executed anymore when the event fires. + public void Unregister(AsyncEvent callback) + { + Callbacks ??= new HashSet(); - lock (Callbacks) - Callbacks.Add(callback); - } + lock (Callbacks) + Callbacks.Remove(callback); + } + + public static AsyncEventHandler operator +(AsyncEventHandler asyncEventHandler, AsyncEvent callback) + { + asyncEventHandler.Register(callback); + + return asyncEventHandler; + } + + public static AsyncEventHandler? operator -(AsyncEventHandler asyncEventHandler, AsyncEvent callback) + { + asyncEventHandler.Unregister(callback); + + return asyncEventHandler; + } - /// - /// Unsubscribe from the event. - /// - /// The action which shouldn't get executed anymore when the event fires. - public void Unregister(AsyncEvent callback) + /// + /// Invokes all registered events. + /// + /// Throws if the supplied was cancelled or + /// if the supplied was disposed or + /// if any of the registered event handlers threw an exception. + /// + /// The to communicate cancellation of the async operation. + /// + /// + /// + /// A which represents the completion of all registered events. + public Task InvokeAsync(CancellationToken cancellationToken = default) + { + Task[] tasks; + lock (Callbacks) { - lock (Callbacks) - Callbacks.Remove(callback); + tasks = new Task[Callbacks.Count]; + int i = 0; + foreach (var callback in Callbacks) + { + cancellationToken.ThrowIfCancellationRequested(); + tasks[i] = callback(cancellationToken); + i++; + } } - public static AsyncEventHandler operator +(AsyncEventHandler asyncEventHandler, AsyncEvent callback) - { - asyncEventHandler.Register(callback); + return Task.WhenAll(tasks); + } +} - return asyncEventHandler; - } +/// +/// A thread-safe asynchronus event handler. +/// +/// The generic type which holds the event data. +public struct AsyncEventHandler +{ + /// + /// All registered events. + /// Do not manually add items without locking. + /// + public HashSet> Callbacks { get; private set; } = new HashSet>(); - public static AsyncEventHandler? operator -(AsyncEventHandler asyncEventHandler, AsyncEvent callback) - { - asyncEventHandler.Unregister(callback); + public AsyncEventHandler() + { + } - return asyncEventHandler; - } + /// + /// Subscribe to the event. + /// + /// The action which should get executed when the event fires. + public void Register(AsyncEvent callback) + { + Callbacks ??= new HashSet>(); - /// - /// Invokes all registered events. - /// - /// Throws if the supplied was cancelled or - /// if the supplied was disposed or - /// if any of the registered event handlers threw an exception. - /// - /// The source of the event. - /// An object that contains the event data. - /// The to communicate cancellation of the async operation. - /// - /// - /// - /// A which represents the completion of all registered events. - public async Task InvokeAsync(object? sender, TEventArgs e, CancellationToken? cancellationToken = null) - { - cancellationToken ??= CancellationToken.None; - e.CancellationToken = cancellationToken.Value; + lock (Callbacks) + Callbacks.Add(callback); + } - cancellationToken.Value.ThrowIfCancellationRequested(); + /// + /// Unsubscribe from the event. + /// + /// The action which shouldn't get executed anymore when the event fires. + public void Unregister(AsyncEvent callback) + { + Callbacks ??= new HashSet>(); - Task[] tasks; - lock (Callbacks) - { - tasks = new Task[Callbacks.Count]; - int i = 0; - foreach (var callback in Callbacks) - { - cancellationToken.Value.ThrowIfCancellationRequested(); - tasks[i] = callback.Invoke(sender, e); - i++; - } - } + lock (Callbacks) + Callbacks.Remove(callback); + } - await Task.WhenAll(tasks); - } + public static AsyncEventHandler operator +(AsyncEventHandler asyncEventHandler, AsyncEvent callback) + { + asyncEventHandler.Register(callback); + + return asyncEventHandler; + } + + public static AsyncEventHandler? operator -(AsyncEventHandler asyncEventHandler, AsyncEvent callback) + { + asyncEventHandler.Unregister(callback); + + return asyncEventHandler; } + /// + /// Invokes all registered events. + /// + /// Throws if the supplied was cancelled or + /// if the supplied was disposed or + /// if any of the registered event handlers threw an exception. + /// + /// An object that contains the event data. + /// The to communicate cancellation of the async operation. + /// + /// + /// + /// A which represents the completion of all registered events. + public async Task InvokeAsync(TEventData data, CancellationToken cancellationToken = default) + { + Task[] tasks; + lock (Callbacks) + { + tasks = new Task[Callbacks.Count]; + int i = 0; + foreach (var callback in Callbacks) + { + cancellationToken.ThrowIfCancellationRequested(); + tasks[i] = callback(data, cancellationToken); + i++; + } + } - //public static class AsyncEventHandlerExtensions - //{ - // /// - // /// Invokes all registered events. - // /// - // /// Throws if the supplied was cancelled or - // /// if the supplied was disposed or - // /// if any of the registered event handlers threw an exception. - // /// - // /// The generic type which holds the event data. - // /// The source of the event. - // /// An object that contains the event data. - // /// The to communicate cancellation of the async operation. - // /// - // /// - // /// - // /// A which represents the completion of all registered events. - // public static async Task InvokeAsync(this AsyncEventHandler asyncEventHandler, object? sender, TEventArgs e, CancellationToken? cancellationToken = null) - // where TEventArgs : AsyncEventArgs - // { - // cancellationToken ??= CancellationToken.None; - // e.CancellationToken = cancellationToken.Value; - - // cancellationToken.Value.ThrowIfCancellationRequested(); - - // Task[] tasks; - // lock (asyncEventHandler.Callbacks) - // { - // tasks = new Task[asyncEventHandler.Callbacks.Count]; - // for (int i = 0; i < asyncEventHandler.Callbacks.Count; i++) - // { - // cancellationToken.Value.ThrowIfCancellationRequested(); - // tasks[i] = asyncEventHandler.Callbacks[i].Invoke(sender, e); - // } - // } - - // await Task.WhenAll(tasks); - // } - - //} + await Task.WhenAll(tasks); + } } \ No newline at end of file diff --git a/AsyncEventHandlers/AsyncEventHandlerDelegate.cs b/AsyncEventHandlers/AsyncEventHandlerDelegate.cs index bfec377..bfa7172 100644 --- a/AsyncEventHandlers/AsyncEventHandlerDelegate.cs +++ b/AsyncEventHandlers/AsyncEventHandlerDelegate.cs @@ -11,7 +11,7 @@ namespace AsyncEventHandlers /// /// /// A that represents the event. - public delegate Task AsyncEventHandlerDelegate(object? sender, AsyncEventArgs e); + public delegate Task AsyncEventHandlerDelegate(object? sender, IAsyncEventArgs e); /// /// Represents the method that will handle an event when the event provides data. @@ -20,7 +20,7 @@ namespace AsyncEventHandlers /// /// /// A that represents the event. - public delegate Task AsyncEventHandlerDelegate(object? sender, TEventArgs e) where TEventArgs : AsyncEventArgs; + public delegate Task AsyncEventHandlerDelegate(object? sender, TEventArgs e) where TEventArgs : IAsyncEventArgs; public static class AsyncEventHandlerDelegateExtensions { @@ -37,24 +37,23 @@ public static class AsyncEventHandlerDelegateExtensions /// /// /// A which represents the completion of all registered events. - public static async ValueTask InvokeAsync(this AsyncEventHandlerDelegate asyncEventHandler, object? sender, AsyncEventArgs e, CancellationToken? cancellationToken = null) + public static async ValueTask InvokeAsync(this AsyncEventHandlerDelegate asyncEventHandler, object? sender, IAsyncEventArgs e, CancellationToken cancellationToken = default) { if (asyncEventHandler is null) { return; } - cancellationToken ??= CancellationToken.None; - e.CancellationToken = cancellationToken.Value; + e.CancellationToken = cancellationToken; - cancellationToken.Value.ThrowIfCancellationRequested(); + cancellationToken.ThrowIfCancellationRequested(); Delegate[]? callbacks = asyncEventHandler.GetInvocationList(); Task[] tasks = new Task[callbacks.Length]; for (int i = 0; i < callbacks.Length; i++) { - cancellationToken.Value.ThrowIfCancellationRequested(); + cancellationToken.ThrowIfCancellationRequested(); AsyncEventHandlerDelegate cb = (AsyncEventHandlerDelegate)callbacks[i]; tasks[i] = cb.Invoke(sender, e); } @@ -77,25 +76,24 @@ public static async ValueTask InvokeAsync(this AsyncEventHandlerDelegate asyncEv /// /// /// A which represents the completion of all registered events. - public static async ValueTask InvokeAsync(this AsyncEventHandlerDelegate asyncEventHandler, object? sender, TEventArgs e, CancellationToken? cancellationToken = null) - where TEventArgs : AsyncEventArgs + public static async ValueTask InvokeAsync(this AsyncEventHandlerDelegate asyncEventHandler, object? sender, TEventArgs e, CancellationToken cancellationToken = default) + where TEventArgs : IAsyncEventArgs { if (asyncEventHandler is null) { return; } - cancellationToken ??= CancellationToken.None; - e.CancellationToken = cancellationToken.Value; + e.CancellationToken = cancellationToken; - cancellationToken.Value.ThrowIfCancellationRequested(); + cancellationToken.ThrowIfCancellationRequested(); Delegate[]? callbacks = asyncEventHandler.GetInvocationList(); Task[] tasks = new Task[callbacks.Length]; for (int i = 0; i < callbacks.Length; i++) { - cancellationToken.Value.ThrowIfCancellationRequested(); + cancellationToken.ThrowIfCancellationRequested(); AsyncEventHandlerDelegate cb = (AsyncEventHandlerDelegate)callbacks[i]; tasks[i] = cb.Invoke(sender, e); } diff --git a/AsyncEventHandlers/AsyncEventHandlers.csproj b/AsyncEventHandlers/AsyncEventHandlers.csproj index 010cf43..042f130 100644 --- a/AsyncEventHandlers/AsyncEventHandlers.csproj +++ b/AsyncEventHandlers/AsyncEventHandlers.csproj @@ -1,9 +1,10 @@  - netstandard2.0 - 10 + netstandard2.1 + 11 enable + true True Dusty @@ -16,9 +17,8 @@ False False https://github.com/TheDusty01/AsyncEventHandlers - 2.0.0 + 3.0.1 - True diff --git a/AsyncEventHandlers/WeakAsyncEventHandler.cs b/AsyncEventHandlers/WeakAsyncEventHandler.cs new file mode 100644 index 0000000..2221e1b --- /dev/null +++ b/AsyncEventHandlers/WeakAsyncEventHandler.cs @@ -0,0 +1,135 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; + +namespace AsyncEventHandlers; + +/// +/// A thread-safe, weak and asynchronus event handler. +/// +public struct WeakAsyncEventHandler +{ + /// + /// All registered events. + /// + public ConditionalWeakTable Callbacks { get; private set; } = new ConditionalWeakTable(); + + public WeakAsyncEventHandler() + { + } + + /// + /// Subscribe to the event. + /// + /// The source of the event. + /// The action which should get executed when the event fires. + public void Register(object instance, AsyncEvent callback) + { + if (Callbacks is null) + Callbacks = new ConditionalWeakTable(); + + Callbacks.AddOrUpdate(instance, callback); + } + + /// + /// Unsubscribe from the event. + /// + /// The instance which contains the events shouldn't get executed anymore when the event fires. + public bool Unregister(object instance) + { + return Callbacks.Remove(instance); + } + + /// + /// Invokes all registered events. + /// + /// Throws if the supplied was cancelled or + /// if the supplied was disposed or + /// if any of the registered event handlers threw an exception. + /// + /// The to communicate cancellation of the async operation. + /// + /// + /// + /// A which represents the completion of all registered events. + public Task InvokeAsync(CancellationToken cancellationToken = default) + { + cancellationToken.ThrowIfCancellationRequested(); + + HashSet tasks = new HashSet(); + foreach ((var instance, var callback) in Callbacks) + { + cancellationToken.ThrowIfCancellationRequested(); + tasks.Add(callback.Invoke(cancellationToken)); + } + + return Task.WhenAll(tasks); + } +} + +/// +/// A thread-safe, weak and asynchronus event handler. +/// +/// The generic type which holds the event data. +public struct WeakAsyncEventHandler +{ + /// + /// All registered events. + /// + public ConditionalWeakTable> Callbacks { get; private set; } = new ConditionalWeakTable>(); + + public WeakAsyncEventHandler() + { + } + + /// + /// Subscribe to the event. + /// + /// The source of the event. + /// The action which should get executed when the event fires. + public void Register(object instance, AsyncEvent callback) + { + if (Callbacks is null) + Callbacks = new ConditionalWeakTable>(); + + Callbacks.AddOrUpdate(instance, callback); + } + + /// + /// Unsubscribe from the event. + /// + /// The instance which contains the events shouldn't get executed anymore when the event fires. + public bool Unregister(object instance) + { + return Callbacks.Remove(instance); + } + + /// + /// Invokes all registered events. + /// + /// Throws if the supplied was cancelled or + /// if the supplied was disposed or + /// if any of the registered event handlers threw an exception. + /// + /// An object that contains the event data. + /// The to communicate cancellation of the async operation. + /// + /// + /// + /// A which represents the completion of all registered events. + public Task InvokeAsync(TEventData data, CancellationToken cancellationToken = default) + { + cancellationToken.ThrowIfCancellationRequested(); + + HashSet tasks = new HashSet(); + foreach ((var instance, var callback) in Callbacks) + { + cancellationToken.ThrowIfCancellationRequested(); + tasks.Add(callback.Invoke(data, cancellationToken)); + } + + return Task.WhenAll(tasks); + } +} diff --git a/README.md b/README.md index 7b72b95..95509fe 100644 --- a/README.md +++ b/README.md @@ -14,8 +14,8 @@ The ``AsyncEventHandler`` type is completely thread-safe, no matter how many ev Declaring an event: ```cs -private readonly AsyncEventHandler myEvent = new AsyncEventHandler(); -public event AsyncEvent MyEvent +private readonly AsyncEventHandler myEvent = new AsyncEventHandler(); +public event AsyncEvent MyEvent { add { myEvent.Register(value); } remove { myEvent.Unregister(value); } @@ -24,26 +24,26 @@ public event AsyncEvent MyEvent You can also just expose the ``AsyncEventHandler`` struct directly but that way it is not treated as an C# event and subscribers outside of your class can even invoke your event. ```cs -public AsyncEventHandler MyEvent = new AsyncEventHandler(); -public AsyncEventHandler AnotherEvent; // You can even do this! +public AsyncEventHandler MyEvent = new AsyncEventHandler(); +public AsyncEventHandler AnotherEvent; // You can even do this! // Or with custom event args: -public class MyAsyncEventArgs : AsyncEventArgs +public class MyType { public string? Message { get; set; } } -public AsyncEventHandler MyCustomEvent = new AsyncEventHandler(); +public AsyncEventHandler MyCustomEvent = new AsyncEventHandler(); ``` Registering/Unregistering to the event: ```cs class WebsocketServer { - public AsyncEventHandler MyExposedEvent = new AsyncEventHandler(); + public AsyncEventHandler MyExposedEvent = new AsyncEventHandler(); - private readonly AsyncEventHandler myEvent = new AsyncEventHandler(); - public event AsyncEvent MyEvent + private readonly AsyncEventHandler myEvent = new AsyncEventHandler(); + public event AsyncEvent MyEvent { add { myEvent.Register(value); } remove { myEvent.Unregister(value); } @@ -61,9 +61,9 @@ ws.MyExposedEvent -= Ws_MyEvent; // Unregister ws.MyExposedEvent.Register(Ws_MyEvent); ws.MyExposedEvent.Unregister(Ws_MyEvent); -static Task Ws_MyEvent(object? sender, AsyncEventArgs e) +static Task Ws_MyEvent(MyType data, CancellationToken cancellationToken) { - if (e.CancellationToken.IsCancellationRequested) + if (cancellationToken.IsCancellationRequested) { Console.WriteLine("The cancellation token was cancelled!"); } @@ -77,10 +77,10 @@ Invoking the event: \ ```cs try { - await myEvent.InvokeAsync(this, new AsyncEventArgs()); + await myEvent.InvokeAsync(new MyType()); // or with a cancellation token var cts = new CancellationTokenSource(); - await myEvent.InvokeAsync(this, new AsyncEventArgs(), cts.Token); + await myEvent.InvokeAsync(new MyType(), cts.Token); } catch (OperationCanceledException) { diff --git a/Samples/Delegate/AsyncEventHandlers.Samples.Delegate.csproj b/Samples/Delegate/AsyncEventHandlers.Samples.Delegate.csproj index 466b75c..5710718 100644 --- a/Samples/Delegate/AsyncEventHandlers.Samples.Delegate.csproj +++ b/Samples/Delegate/AsyncEventHandlers.Samples.Delegate.csproj @@ -2,7 +2,7 @@ Exe - net6.0 + net7.0 enable enable diff --git a/Samples/Delegate/FakeWebsocketServer.cs b/Samples/Delegate/FakeWebsocketServer.cs index a93fb33..954f500 100644 --- a/Samples/Delegate/FakeWebsocketServer.cs +++ b/Samples/Delegate/FakeWebsocketServer.cs @@ -1,57 +1,50 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +namespace AsyncEventHandlers.Samples.Delegate; -namespace AsyncEventHandlers.Samples.Delegate +public class MessageAsyncEventArgs : AsyncEventArgs { - public class MessageAsyncEventArgs : AsyncEventArgs - { - public string? Message { get; set; } - } + public string? Message { get; set; } +} + +public class ClientConnectedAsyncEventArgs : AsyncEventArgs +{ + public int ClientId { get; set; } +} + +public class FakeWebsocketServer +{ + public event AsyncEventHandlerDelegate? Started; + public event AsyncEventHandlerDelegate? ClientConnected; + public event AsyncEventHandlerDelegate? MessageReceived; - public class ClientConnectedAsyncEventArgs : AsyncEventArgs + public void Run(CancellationToken cancellationToken) { - public int ClientId { get; set; } + _ = Task.Run(() => SimulateTest(cancellationToken), cancellationToken); } - public class FakeWebsocketServer + private async Task SimulateTest(CancellationToken cancellationToken) { - public event AsyncEventHandlerDelegate? Started; - public event AsyncEventHandlerDelegate? ClientConnected; - public event AsyncEventHandlerDelegate? MessageReceived; - - public void Run(CancellationToken cancellationToken) + try { - _ = Task.Run(() => SimulateTest(cancellationToken), cancellationToken); + await Started!.InvokeAsync(this, new AsyncEventArgs(), cancellationToken); } - - private async Task SimulateTest(CancellationToken cancellationToken) + catch (Exception ex) { - try - { - await Started!.InvokeAsync(this, new AsyncEventArgs(), cancellationToken); - } - catch (Exception ex) - { - Console.WriteLine($"Started event threw an exception: {ex}"); - } + Console.WriteLine($"Started event threw an exception: {ex}"); + } - // Simulate client connecting - await Task.Delay(1000); - await ClientConnected!.InvokeAsync(this, new ClientConnectedAsyncEventArgs { ClientId = 1 }, cancellationToken); + // Simulate client connecting + await Task.Delay(1000); + await ClientConnected!.InvokeAsync(this, new ClientConnectedAsyncEventArgs { ClientId = 1 }, cancellationToken); - // Simulate client message - await Task.Delay(1000); - try - { - await MessageReceived!.InvokeAsync(this, new MessageAsyncEventArgs { Message = "Hello!" }, cancellationToken); - } - catch (OperationCanceledException) - { - Console.WriteLine("Token was cancelled"); - } + // Simulate client message + await Task.Delay(1000); + try + { + await MessageReceived!.InvokeAsync(this, new MessageAsyncEventArgs { Message = "Hello!" }, cancellationToken); + } + catch (OperationCanceledException) + { + Console.WriteLine("Token was cancelled"); } } } diff --git a/Samples/Delegate/Program.cs b/Samples/Delegate/Program.cs index b706f70..b34b954 100644 --- a/Samples/Delegate/Program.cs +++ b/Samples/Delegate/Program.cs @@ -1,52 +1,51 @@ -namespace AsyncEventHandlers.Samples.Delegate +namespace AsyncEventHandlers.Samples.Delegate; + +public class Program { - public class Program - { - private static readonly CancellationTokenSource cts = new(); + private static readonly CancellationTokenSource cts = new(); - public static void Main() - { - var ws = new FakeWebsocketServer(); + public static void Main() + { + var ws = new FakeWebsocketServer(); - ws.Started += Ws_Started; - ws.Started += Ws_Started_Second; + ws.Started += Ws_Started; + ws.Started += Ws_Started_Second; - ws.ClientConnected += Ws_ClientConnected; - ws.MessageReceived += Ws_MessageReceived; + ws.ClientConnected += Ws_ClientConnected; + ws.MessageReceived += Ws_MessageReceived; - ws.Run(cts.Token); + ws.Run(cts.Token); - Console.WriteLine("Press any key to exit"); - Console.ReadKey(true); - } + Console.WriteLine("Press any key to exit"); + Console.ReadKey(true); + } - private static Task Ws_Started(object? sender, AsyncEventArgs e) - { - throw new Exception("Something went wrong!"); - } + private static Task Ws_Started(object? sender, IAsyncEventArgs e) + { + throw new Exception("Something went wrong!"); + } - private static async Task Ws_Started_Second(object? sender, AsyncEventArgs e) - { - // Simulate long running request (e.g. sending something to an api) - await Task.Delay(1000, e.CancellationToken); - Console.WriteLine("Second Started event done!"); - } + private static async Task Ws_Started_Second(object? sender, IAsyncEventArgs e) + { + // Simulate long running request (e.g. sending something to an api) + await Task.Delay(1000, e.CancellationToken); + Console.WriteLine("Second Started event done!"); + } - private static Task Ws_ClientConnected(object? sender, ClientConnectedAsyncEventArgs e) - { - Console.WriteLine($"Client connected: {e.ClientId}"); + private static Task Ws_ClientConnected(object? sender, ClientConnectedAsyncEventArgs e) + { + Console.WriteLine($"Client connected: {e.ClientId}"); - // Cancel cts, the MessageRecieved shouldn't get executed afterwards - cts.Cancel(); + // Cancel cts, the MessageRecieved shouldn't get executed afterwards + cts.Cancel(); - return Task.CompletedTask; - } + return Task.CompletedTask; + } - private static Task Ws_MessageReceived(object? sender, MessageAsyncEventArgs e) - { - Console.WriteLine($"New message: {e.Message}"); - return Task.CompletedTask; - } + private static Task Ws_MessageReceived(object? sender, MessageAsyncEventArgs e) + { + Console.WriteLine($"New message: {e.Message}"); + return Task.CompletedTask; } } \ No newline at end of file diff --git a/Samples/Type/AsyncEventHandlers.Samples.Type.csproj b/Samples/Type/AsyncEventHandlers.Samples.Type.csproj index 466b75c..5710718 100644 --- a/Samples/Type/AsyncEventHandlers.Samples.Type.csproj +++ b/Samples/Type/AsyncEventHandlers.Samples.Type.csproj @@ -2,7 +2,7 @@ Exe - net6.0 + net7.0 enable enable diff --git a/Samples/Type/FakeWebsocketServer.cs b/Samples/Type/FakeWebsocketServer.cs index 16b3ba0..f172876 100644 --- a/Samples/Type/FakeWebsocketServer.cs +++ b/Samples/Type/FakeWebsocketServer.cs @@ -1,62 +1,55 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +namespace AsyncEventHandlers.Samples.Type; -namespace AsyncEventHandlers.Samples.Type +public class MessageData { - public class MessageAsyncEventArgs : AsyncEventArgs + public string? Message { get; set; } +} + +public class ClientConnectedData +{ + public int ClientId { get; set; } +} + +public class FakeWebsocketServer +{ + public AsyncEventHandler Started = default; + private readonly AsyncEventHandler clientConnected = new AsyncEventHandler(); + public event AsyncEvent ClientConnected { - public string? Message { get; set; } + add { clientConnected.Register(value); } + remove { clientConnected.Unregister(value); } } + public AsyncEventHandler MessageReceived = new AsyncEventHandler(); - public class ClientConnectedAsyncEventArgs : AsyncEventArgs + public void Run(CancellationToken cancellationToken) { - public int ClientId { get; set; } + _ = Task.Run(() => SimulateTest(cancellationToken), cancellationToken); } - public class FakeWebsocketServer + private async Task SimulateTest(CancellationToken cancellationToken) { - public AsyncEventHandler Started = default; - private readonly AsyncEventHandler clientConnected = new AsyncEventHandler(); - public event AsyncEvent ClientConnected + try { - add { clientConnected.Register(value); } - remove { clientConnected.Unregister(value); } + await Started!.InvokeAsync(cancellationToken); } - public AsyncEventHandler MessageReceived = new AsyncEventHandler(); - - public void Run(CancellationToken cancellationToken) + catch (Exception ex) { - _ = Task.Run(() => SimulateTest(cancellationToken), cancellationToken); + Console.WriteLine($"Started event threw an exception: {ex}"); } - private async Task SimulateTest(CancellationToken cancellationToken) - { - try - { - await Started!.InvokeAsync(this, new AsyncEventArgs(), cancellationToken); - } - catch (Exception ex) - { - Console.WriteLine($"Started event threw an exception: {ex}"); - } - - // Simulate client connecting - await Task.Delay(1000); - await clientConnected.InvokeAsync(this, new ClientConnectedAsyncEventArgs { ClientId = 1 }, cancellationToken); + // Simulate client connecting + await Task.Delay(1000); + await clientConnected.InvokeAsync(new ClientConnectedData { ClientId = 1 }, cancellationToken); - // Simulate client message - await Task.Delay(1000); - try - { - await MessageReceived.InvokeAsync(this, new MessageAsyncEventArgs { Message = "Hello!" }, cancellationToken); - } - catch (OperationCanceledException) - { - Console.WriteLine("Token was cancelled"); - } + // Simulate client message + await Task.Delay(1000); + try + { + await MessageReceived.InvokeAsync(new MessageData { Message = "Hello!" }, cancellationToken); + } + catch (OperationCanceledException) + { + Console.WriteLine("Token was cancelled"); } } } diff --git a/Samples/Type/Program.cs b/Samples/Type/Program.cs index 1f7891b..4c66ee9 100644 --- a/Samples/Type/Program.cs +++ b/Samples/Type/Program.cs @@ -1,63 +1,59 @@ -namespace AsyncEventHandlers.Samples.Type -{ - public class Program - { +namespace AsyncEventHandlers.Samples.Type; - private static readonly CancellationTokenSource cts = new(); +public class Program +{ - public static void Main() - { - var ws = new FakeWebsocketServer(); + private static readonly CancellationTokenSource cts = new(); + public static void Main() + { + var ws = new FakeWebsocketServer(); -#pragma warning disable CS8604 // Possible nullreference - // Automatically creates an instance for ws.Started if null, even thought the compiler warns you - ws.Started += Ws_Started; -#pragma warning restore CS8604 // Possible nullreference - ws.Started += Ws_Started_Second; + // Automatically creates an instance for ws.Started if null, even thought the compiler warns you + ws.Started += Ws_Started; + ws.Started += Ws_Started_Second; - ws.Started.Register((s, e) => - { - Console.WriteLine("Adding the started event via the register method also works!"); - return Task.CompletedTask; - }); + ws.Started.Register(e => + { + Console.WriteLine("Adding the started event via the register method also works!"); + return Task.CompletedTask; + }); - ws.ClientConnected += Ws_ClientConnected; - ws.MessageReceived += Ws_MessageReceived; - - ws.Run(cts.Token); + ws.ClientConnected += Ws_ClientConnected; + ws.MessageReceived += Ws_MessageReceived; + + ws.Run(cts.Token); - Console.WriteLine("Press any key to exit"); - Console.ReadKey(true); - } + Console.WriteLine("Press any key to exit"); + Console.ReadKey(true); + } - private static Task Ws_Started(object? sender, AsyncEventArgs e) - { - //throw new Exception("Something went wrong!"); - return Task.CompletedTask; - } + private static Task Ws_Started(CancellationToken cancellationToken) + { + //throw new Exception("Something went wrong!"); + return Task.CompletedTask; + } - private static async Task Ws_Started_Second(object? sender, AsyncEventArgs e) - { - // Simulate long running request (e.g. sending something to an api) - await Task.Delay(1000, e.CancellationToken); - Console.WriteLine("Second Started event done!"); - } + private static async Task Ws_Started_Second(CancellationToken cancellationToken) + { + // Simulate long running request (e.g. sending something to an api) + await Task.Delay(1000, cancellationToken); + Console.WriteLine("Second Started event done!"); + } - private static Task Ws_ClientConnected(object? sender, ClientConnectedAsyncEventArgs e) - { - Console.WriteLine($"Client connected: {e.ClientId}"); + private static Task Ws_ClientConnected(ClientConnectedData e, CancellationToken cancellationToken) + { + Console.WriteLine($"Client connected: {e.ClientId}"); - // Cancel cts, the MessageRecieved shouldn't get executed afterwards - cts.Cancel(); + // Cancel cts, the MessageRecieved shouldn't get executed afterwards + cts.Cancel(); - return Task.CompletedTask; - } + return Task.CompletedTask; + } - private static Task Ws_MessageReceived(object? sender, MessageAsyncEventArgs e) - { - Console.WriteLine($"New message: {e.Message}"); - return Task.CompletedTask; - } + private static Task Ws_MessageReceived(MessageData e, CancellationToken cancellationToken) + { + Console.WriteLine($"New message: {e.Message}"); + return Task.CompletedTask; } } \ No newline at end of file