310 lines
9.8 KiB
Markdown
310 lines
9.8 KiB
Markdown
|
||
# 🎯 EonaCat.EventKit - The Ultimate C# Event Management Toolkit
|
||
|
||
`EonaCat.EventKit` is a hybrid runtime + Roslyn analyzer library
|
||
that makes subscribing/unsubscribing and managing events safer, smarter, and simpler.
|
||
|
||
---
|
||
|
||
## 🚀 Features
|
||
|
||
### ✅ Runtime API
|
||
- 🔄 **Subscribe/Unsubscribe to any event** dynamically
|
||
- 🌐 **Subscribe to all events** of a class with optional filtering
|
||
- 🛡️ **Reentrancy-safe** handlers
|
||
- 💥 **Safe execution** with built-in exception handling
|
||
- 📖 **Track current subscriptions**
|
||
- 🔎 **Event forwarding** for diagnostics/logging
|
||
- 🧹 **Automatic unsubscription on disposal**
|
||
- 🧠 **Weak event subscriptions** for better memory management
|
||
- ⏳ **Event expiry** for timed unsubscription
|
||
- 🔄 **Retry event handling** for transient errors
|
||
- 🔥 **Concurrent event handling** for async operations
|
||
|
||
### 🧠 Roslyn Analyzers
|
||
- ⚠️ **Detect duplicate subscriptions**
|
||
- 🧹 **Detect missing unsubscriptions**
|
||
- 🔧 **CodeFix**: auto-insert `-=` for unsubscribing
|
||
|
||
---
|
||
|
||
## 📦 Installation
|
||
|
||
**Install via NuGet:**
|
||
|
||
```bash
|
||
dotnet add package EonaCat.EventKit
|
||
```
|
||
|
||
## 🛠️ Usage Example
|
||
|
||
Simple Event Subscription
|
||
```csharp
|
||
var button = new Button();
|
||
|
||
EonaCat.EventKit.EventSubscriptionManager.Subscribe(button, "Click", (name, args) =>
|
||
{
|
||
Console.WriteLine($"Clicked: {args}");
|
||
});
|
||
|
||
// Later
|
||
EonaCat.EventKit.EventSubscriptionManager.Unsubscribe(button, "Click");
|
||
```
|
||
|
||
**Subscribe All Events With Filter**
|
||
```csharp
|
||
EonaCat.EventKit.EventSubscriptionManager.SubscribeAll(
|
||
someControl,
|
||
evt => evt.Name.StartsWith("On"),
|
||
(name, args) => Console.WriteLine($"{name} fired!")
|
||
);
|
||
```
|
||
|
||
Concurrent Event Handling
|
||
```csharp
|
||
EonaCat.EventKit.EventSubscriptionManager.SubscribeConcurrently(
|
||
someControl,
|
||
"Click",
|
||
async (name, args) =>
|
||
{
|
||
await SomeAsyncMethod();
|
||
Console.WriteLine($"{name} clicked asynchronously.");
|
||
});
|
||
```
|
||
|
||
**Event Subscription with Expiry**
|
||
```csharp
|
||
EonaCat.EventKit.EventSubscriptionManager.SubscribeWithExpiry(
|
||
someControl,
|
||
"Click",
|
||
(name, args) =>
|
||
{
|
||
Console.WriteLine($"{name} clicked.");
|
||
},
|
||
TimeSpan.FromSeconds(30) // Expire the subscription after 30 seconds
|
||
);
|
||
```
|
||
|
||
**Auto Unsubscribe on Dispose**
|
||
```csharp
|
||
EonaCat.EventKit.EventSubscriptionManager.SubscribeWithAutoUnsubscribeOnDispose(
|
||
someControl,
|
||
"Click",
|
||
(name, args) =>
|
||
{
|
||
Console.WriteLine($"{name} clicked.");
|
||
});
|
||
```
|
||
|
||
🛠️ Advanced Usage
|
||
|
||
Subscribe with Event Count Logging
|
||
```csharp
|
||
EonaCat.EventKit.EventSubscriptionManager.SubscribeWithCountLogging(
|
||
someControl,
|
||
"Click",
|
||
(name, args) =>
|
||
{
|
||
Console.WriteLine($"{name} clicked.");
|
||
});
|
||
```
|
||
|
||
**Retry Logic for Event Handling**
|
||
```csharp
|
||
EonaCat.EventKit.EventSubscriptionManager.SubscribeWithRetry(
|
||
someControl,
|
||
"Click",
|
||
(name, args) =>
|
||
{
|
||
Console.WriteLine($"{name} clicked.");
|
||
});
|
||
```
|
||
|
||
**Subscribe with Execution Context (Thread-Safe)**
|
||
```csharp
|
||
EonaCat.EventKit.EventSubscriptionManager.SubscribeWithExecutionContext(
|
||
someControl,
|
||
"Click",
|
||
(name, args) =>
|
||
{
|
||
Console.WriteLine($"{name} clicked.");
|
||
});
|
||
```
|
||
|
||
## 🔎 Using the EventSubscriptionLogger
|
||
|
||
The EventSubscriptionLogger is a useful tool for tracking and logging event subscriptions. This can help you understand the flow of event subscriptions and ensure that everything is behaving as expected.
|
||
|
||
**Enabling Event Subscription Logging**
|
||
|
||
To enable event subscription logging, use the EventSubscriptionLogger in combination with EventSubscriptionManager to log all subscriptions and unsubscriptions.
|
||
|
||
```csharp
|
||
// Enable event subscription logging
|
||
EonaCat.EventKit.EventSubscriptionLogger.Enable();
|
||
|
||
// Subscribe to an event
|
||
var button = new Button();
|
||
EonaCat.EventKit.EventSubscriptionManager.Subscribe(button, "Click", (name, args) =>
|
||
{
|
||
Console.WriteLine($"Clicked: {args}");
|
||
});
|
||
|
||
// Later, unsubscribe from the event
|
||
EonaCat.EventKit.EventSubscriptionManager.Unsubscribe(button, "Click");
|
||
|
||
// Disable event subscription logging (optional)
|
||
EonaCat.EventKit.EventSubscriptionLogger.Disable();
|
||
```
|
||
|
||
Custom Event Subscription Logger
|
||
|
||
You can also create a custom logger to output the subscription information in a way that fits your needs
|
||
```csharp
|
||
EonaCat.EventKit.EventSubscriptionLogger.Enable(log =>
|
||
{
|
||
// Do custom logging action: For example, log to a file or console
|
||
Console.WriteLine($"Event subscribed: {log.EventName} to {log.Target}");
|
||
});
|
||
```
|
||
|
||
## 👀 Analyzer Diagnostics
|
||
|
||
|ID| TITLE | FIX|
|
||
|--|--|--|
|
||
| EVT001 | Duplicate event subscription | Suggest review|
|
||
| EVT002 | Missing event unsubscription | Auto-fix in code|
|
||
|
||
## 🧹 Memory Management Features
|
||
|
||
##### - 🧹 Automatic unsubscription on disposal to prevent memory leaks.
|
||
##### - 🧠 Weak event subscriptions to help with garbage collection.
|
||
##### - ⏳ Event expiry to automatically unsubscribe after a specified duration.
|
||
##### - 🔄 Retry logic for transient errors during event handling.
|
||
##### - 🔥 Concurrent event handling for async operations.
|
||
##### - 🧠 Weak event subscriptions to prevent memory leaks when the target object is no longer referenced.
|
||
##### - 💡 Reentrancy-safe handling to avoid recursive event triggering during handler execution.
|
||
|
||
## ⚙️ Customization Options
|
||
**SubscribeOptions**
|
||
|
||
- ReentrancySafe : Ensures handlers aren't invoked while already executing for the same event.
|
||
- UseWeakReference: Enables weak references for event subscriptions to help with garbage collection.
|
||
- HandleExceptions: Configures whether exceptions in event handlers should be logged or handled silently.
|
||
|
||
**Example**
|
||
```csharp
|
||
EonaCat.EventKit.EventSubscriptionManager.Subscribe(
|
||
someControl,
|
||
"Click",
|
||
(name, args) =>
|
||
{
|
||
Console.WriteLine($"Clicked: {args}");
|
||
},
|
||
new SubscribeOptions { ReentrancySafe = true, UseWeakReference = true });
|
||
```
|
||
|
||
## 🧑💻 Subscribing to Events in Regular Classes
|
||
|
||
EonaCat.EventKit isn't limited to UI controls; you can subscribe to events in regular classes that define events as well.
|
||
|
||
**Example with a Custom Class (Non-Control)**
|
||
Let’s create a class MyCustomClass that has a few events. We'll use the source generator to generate subscription and unsubscription methods, and then use them in the same way.
|
||
|
||
**Step 1: Define a Custom Class with Events**
|
||
|
||
```csharp
|
||
public class MyCustomClass
|
||
{
|
||
// Define events
|
||
public event EventHandler SomeEvent;
|
||
public event EventHandler<string> MessageEvent;
|
||
|
||
// Methods to fire events
|
||
public void TriggerSomeEvent()
|
||
{
|
||
SomeEvent?.Invoke(this, EventArgs.Empty);
|
||
}
|
||
|
||
public void TriggerMessageEvent(string message)
|
||
{
|
||
MessageEvent?.Invoke(this, message);
|
||
}
|
||
}
|
||
```
|
||
|
||
**Step 2: Generated Subscription Code**
|
||
After running the EventSubscriptionSourceGenerator, you would get a generated class MyCustomClassEventSubscriptions,
|
||
which will contain the subscription and unsubscription methods. Here's what the generated code would look like:
|
||
|
||
```csharp
|
||
namespace GeneratedEventSubscriptions
|
||
{
|
||
public static class MyCustomClassEventSubscriptions
|
||
{
|
||
public static void Subscribe()
|
||
{
|
||
// Subscribing to events
|
||
EonaCat.EventKit.EventSubscriptionManager.SubscribeWeak(MyCustomClass.Instance, "SomeEvent", GeneratedEventHandler_SomeEventHandler);
|
||
EonaCat.EventKit.EventSubscriptionManager.SubscribeWeak(MyCustomClass.Instance, "MessageEvent", GeneratedEventHandler_MessageEventHandler);
|
||
}
|
||
|
||
public static void Unsubscribe()
|
||
{
|
||
// Unsubscribing from events
|
||
EonaCat.EventKit.EventSubscriptionManager.Unsubscribe(MyCustomClass.Instance, "SomeEvent");
|
||
EonaCat.EventKit.EventSubscriptionManager.Unsubscribe(MyCustomClass.Instance, "MessageEvent");
|
||
}
|
||
|
||
// Event Handlers
|
||
private static void GeneratedEventHandler_SomeEventHandler(object sender, EventArgs args)
|
||
{
|
||
Console.WriteLine("SomeEvent triggered.");
|
||
}
|
||
|
||
private static void GeneratedEventHandler_MessageEventHandler(object sender, string message)
|
||
{
|
||
Console.WriteLine($"MessageEvent triggered with message: {message}");
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
**Step 3: Using the Subscription and Unsubscription in Code**
|
||
Now, you can use the generated subscription and unsubscription methods just like before
|
||
but with a regular class instead of a control. Here’s how to use it in your program:
|
||
|
||
```csharp
|
||
using System;
|
||
using GeneratedEventSubscriptions; // Import the generated namespace
|
||
|
||
class Program
|
||
{
|
||
static void Main(string[] args)
|
||
{
|
||
// Create instance of the custom class
|
||
var myClass = new MyCustomClass();
|
||
|
||
// Subscribe to events
|
||
MyCustomClassEventSubscriptions.Subscribe();
|
||
|
||
// Trigger events
|
||
myClass.TriggerSomeEvent(); // Should trigger SomeEvent handler
|
||
myClass.TriggerMessageEvent("Hello, World!"); // Should trigger MessageEvent handler
|
||
|
||
// Optionally, unsubscribe from events
|
||
MyCustomClassEventSubscriptions.Unsubscribe();
|
||
|
||
// Trigger events again after unsubscription (no output as it is unsubscribed)
|
||
myClass.TriggerSomeEvent();
|
||
myClass.TriggerMessageEvent("Goodbye!"); // No output
|
||
}
|
||
}
|
||
```
|
||
|
||
## 🔄 Conclusion
|
||
|
||
EonaCat.EventKit offers a flexible and powerful event management toolkit that works seamlessly with both UI controls and regular classes.
|
||
Whether you're handling events in UI elements or business logic classes,
|
||
the toolkit provides robust features like subscription management, weak references, automatic unsubscription,
|
||
and even retry logic for transient errors. |