I'm trying to devise a generalized way of implementing what I believe is a pretty common pattern of needing to handle a class event exactly once in order to set a TaskCompletionSource result, and unsubscribe immediately. The pattern looks like this:
Task DoSomethingAfterAnEventHasBeenTriggeredOnceAsync()
{
var tcs = new TaskCompletionSource<object>();
SomeEventHandlerDelegate handler = null;
handler = new SomeEventHandlerDelegate((p1,p2,p3) =>
{
// do my thing
// ...
someObj.SomeEvent -= handler;
tcs.SetResult(null);
});
someObj.SomeEvent += handler;
return tcs.Task;
}
My initial thought was to make a generic method along these lines:
public static Task SubscribeOnceAsync<Tsender, Tdel>(
Tdel handler,
Action<Tdel> addHandler,
Action<Tdel> removeHandler)
where Tdel: System.Delegate
{
TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
// ???
// somehow create a new delegate to send to
// "addHandler" that calls "handler",
// then calls "removeHandler", and then
// sets the tcs result
// ???
}
That would be consumed like this:
INotifyPropertyChanged inpc;
// ...
await SubscribeOnceAsync<INotifyPropertyChanged, PropertyChangedEventHandler>(
(s, e) =>
{
// do my one time thing
},
(s, h) => inpc.PropertyChanged += h,
(s, h) => inpc.PropertyChanged -= h);
The problem is in dynamically creating the delegate that can be provided to addHandler and removeHandler. With Delegate.Combine the two delegates have to be the same type, so I actually would have to dynamically create a delegate of type Tdel that called removeHandler and tcs.SetResult.
I suppose it might be possible to do something with dynamic compilation but this is ultimately going to be used with .NET WASM so I'm wary of going down that route given how ill tempered that runtime can be.
So I'm not even sure if my initial thinking is the best way to do this but can't think of any other ways to tackle this. Any ideas?
Note this would need to work with existing code that use events, so the IObservable pattern is not an option.
I assume the event to be declared as
public EventHandler<SomeEventArgs> SomeEvent;
My idea is to provide a proxy delegate which unsubscribes the event and then executes the original delegate (I called it action).
public static Task SubscribeOnceAsync<TArgs>(
Action<EventHandler<TArgs>> addHandler,
Action<EventHandler<TArgs>> removeHandler,
Action<object, TArgs> action) where TArgs : EventArgs
{
TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
addHandler(Proxy);
void Proxy(object sender, TArgs args)
{
removeHandler(Proxy);
action(sender, args);
tcs.SetResult(null);
}
return tcs.Task;
}
Note that Proxy is declared as local function which captures removeHandler and action. Therefore, it does not require them to be passed as extra parameters and has therefore the same signature as the original event handler.
It would be consumed like this:
static async Task TestSubscribeOnceAsync()
{
var someObj = new EventClass();
await SubscribeOnceAsync<SomeEventArgs>(
eh => someObj.SomeEvent += eh,
eh => someObj.SomeEvent -= eh,
(sender, args) => {
Console.WriteLine("do my thing");
}
);
}
This is what I came up with, which uses classes instead of static methods. Delegate typing turns out to be a significant challenge here if we want to support any generalized event, in existing code or elsehwere.
This isn't the most elegant looking code, and it does have an inherent limitation, but it's pretty usable in practice.
First, the base class:
public class OneTimeObserver<Tsender, Tdel, Tdelarg1, Tdelarg2, Tdelarg3, Tdelarg4, Tdelarg5>
where Tdel : Delegate
{
Tdel _theirDel;
Tdel _ourDel;
Action<Tsender, Tdel> _unsub;
TaskCompletionSource<object> _tcs;
Tsender _sender;
protected OneTimeObserver(
Tsender sender,
Tdel del,
Action<Tsender, Tdel> sub,
Action<Tsender, Tdel> unsub,
int argCount)
{
_sender = sender;
_unsub = unsub;
_theirDel = del;
_tcs = new TaskCompletionSource<object>();
string methodName = $"Observe{argCount}";
_ourDel = (Tdel)Delegate.CreateDelegate(typeof(Tdel), this, methodName);
sub(_sender, _ourDel);
}
public Task Task => _tcs.Task;
protected void Observe0()
{
_unsub(_sender, _ourDel);
_theirDel?.DynamicInvoke();
_tcs.SetResult(null);
}
protected void Observe1(Tdelarg1 arg1)
{
_unsub(_sender, _ourDel);
_theirDel?.DynamicInvoke(arg1);
_tcs.SetResult(null);
}
protected void Observe2(Tdelarg1 arg1, Tdelarg2 arg2)
{
_unsub(_sender, _ourDel);
_theirDel?.DynamicInvoke(arg1, arg2);
_tcs.SetResult(null);
}
protected void Observe3(Tdelarg1 arg1, Tdelarg2 arg2, Tdelarg3 arg3)
{
_unsub(_sender, _ourDel);
_theirDel?.DynamicInvoke(arg1, arg2, arg3);
_tcs.SetResult(null);
}
protected void Observe4(Tdelarg1 arg1, Tdelarg2 arg2, Tdelarg3 arg3, Tdelarg4 arg4)
{
_unsub(_sender, _ourDel);
_theirDel?.DynamicInvoke(arg1, arg2, arg3, arg4);
_tcs.SetResult(null);
}
protected void Observe5(Tdelarg1 arg1, Tdelarg2 arg2, Tdelarg3 arg3, Tdelarg4 arg4, Tdelarg5 arg5)
{
_unsub(_sender, _ourDel);
_theirDel?.DynamicInvoke(arg1, arg2, arg3, arg4, arg5);
_tcs.SetResult(null);
}
}
The limitation of course is the number of arguments supported, and the inelegance comes from the fact that we need an Observe{N} method for each possible number of arguments, only one of which winds up being invoked depending on the number of arguments in the delegate type (so type parameters beyond that are just ignored and can be all set to object).
On its own it would be very clunky to use, but if I declare a subclass for each object/event combination I want to support, things get a lot more manageable, for example:
public class OneTimePropertyChangeObserver : OneTimeObserver<
INotifyPropertyChanged,
PropertyChangedEventHandler,
object,
PropertyChangedEventArgs,
object,
object,
object>
{
public OneTimePropertyChangeObserver(INotifyPropertyChanged sender, PropertyChangedEventHandler handler) :
base(
sender,
handler,
(inpc, d) => inpc.PropertyChanged += d,
(inpc, d) => inpc.PropertyChanged -= d,
2)
{
}
}
which can be consumed:
await new OneTimePropertyChangeObserver(obj, (sender, e) =>
{
// do my thing
}).Task;
If anyone can think of a way to get around the argument/delegate issue and still support arbitrary event delegate patterns I'd definitely love to see it!
Related
I would like to create an Observable for an event defined as follows:
public event Func<Exception, Task> Closed;
The current code I have is this:
Observable.FromEvent<Func<Exception, Task>, Unit>(h => hub.Closed += h, h=> hub.Closed -= h);
It compiles OK, but it throws this runtime exception:
System.ArgumentException: 'Cannot bind to the target method because
its signature or security transparency is not compatible with that of
the delegate type.'
I feel that I'm doing it wrong. I'm not used to create observables from events that don't follow the EventArgs pattern 😔
EDIT: Just for clarification purposes, this is the complete code with how the classic event handling would look:
class Program
{
static async Task Main(string[] args)
{
var hub = new HubConnectionBuilder().WithUrl("http://localhost:49791/hubs/status")
.Build();
hub.On<Status>("SendAction", status => Console.WriteLine($"Altitude: {status.Altitude:F} m"));
await hub.StartAsync();
hub.Closed += HubOnClosed;
while (true)
{
}
}
private static Task HubOnClosed(Exception arg)
{
Console.WriteLine("The connection to the hub has been closed");
return Task.CompletedTask;
}
}
You need the conversion overload. I shutter every time I look this thing up:
IObservable<TEventArgs> Observable.FromEvent<TDelegate, TEventArgs>(
Func<Action<TEventArgs>, TDelegate> conversion,
Action<TDelegate> addHandler,
Action<TDelegate> removeHandler>
)
So in our case, TEventArgs is Exception, and TDelegate is Func<Exception, Task>, so you need to convert Action<Exception> to Func<Exception, Task>>, in other words: Func<Action<Exception>, Func<Exception, Task>>. I'm assuming that conversion looks like this: a => e => {a(e); return Task.CompletedTask; }.
System.Reactive needs this conversion function because it needs to subscribe to the event with a proper delegate, and somehow hook in your code/RX Plumbing code. In this case, a(e) is basically RX plumbing which then passes on the Exception to be handled later in the reactive pipeline.
Full code:
class Program
{
static async Task Main(string[] args)
{
Program.Closed += Program.HubOnClosed;
Observable.FromEvent<Func<Exception, Task>, Exception>(
a => e => {a(e); return Task.CompletedTask; },
h => Program.Closed += h,
h => Program.Closed -= h
)
.Subscribe(e =>
{
Console.WriteLine("Rx: The connection to the hub has been closed");
});
Program.Closed.Invoke(null);
Program.Closed.Invoke(null);
}
private static Task HubOnClosed(Exception arg)
{
Console.WriteLine("The connection to the hub has been closed");
return Task.CompletedTask;
}
public static event Func<Exception, Task> Closed;
}
Does something like this do the trick?
class Program
{
public event Func<Exception, Task> Closed;
static void Main(string[] args)
{
Program p = new Program();
IObservable<Unit> closedObservable = Observable.Create<Unit>(
observer =>
{
Func<Exception, Task> handler = ex =>
{
observer.OnNext(Unit.Default);
return Task.CompletedTask;
};
p.Closed += handler;
return () => p.Closed -= handler;
});
}
}
Observable.Create() is a useful fallback for unusual cases like this.
As an aside, it's very strange to have an event with a non-void returning delegate, since the code that raises the event would only see the value of the last handler to run - unless it raises the event in some non-standard way. But, since it's library code, that's out of your hands!
Try the following, not using the signature you want but something to try:
class Program
{
public delegate void ClosedEventHandler(object sender, Func<Exception, Task> e);
public ClosedEventHandler Closed { get; set; }
static void Main(string[] args)
{
Program hub = new Program();
hub.Closed = hub.SomethingToDoWhenClosed;
Observable
.FromEventPattern<ClosedEventHandler, Func<Exception, Task>>(
h => hub.Closed += h,
h => hub.Closed -= h)
.Subscribe(x =>
{
// this is hit
});
hub.Closed(hub, e => null);
}
public void SomethingToDoWhenClosed(object sender, Func<Exception, Task> e)
{
}
}
Can anybody please tell me what is happening in below code in simple English, specifically around the usage of => and += symbols:
var ls = new LibraryServiceClient(AppSettings.Get("LibraryServiceBaseAddress"),
SessionId, App.Id, _user.UUID);
ls.MakingRequest += (s, e) =>
{
LogStash.LogDebug("Library Service | Before making request : {0}",
DateTime.UtcNow.ToString("HH:mm:ss.fff"));
};
You assign a new delegate to the event:
ls.MakingRequest +=
You create a lambda expression, a function having two parameters, s and e:
(s, e) =>
Where the action of the lambda expression is:
{ LogStash.LogDebug("Library Service | Before making request : {0}", DateTime.UtcNow.ToString("HH:mm:ss.fff"));
(s,e) => { /*expresion*/ }
is a lambda function.
It's type is Action<object, EventArgs>.
ls.MakingRequest
is an event.
With += you register a handler to this event.
When the event is fired all registered handlers will execute.
A handler has the same signature as the action - it takes an object sender and an EventArgs eventArgs and returns void.
Thus, the lambda function type is compatible, so it will be called when the event is fired.
It is a syntactical sugar to make chained extension methods look more readable.
Below code will explain its evolution:
public class Program
{
public void Main(string[] args)
{
// named delegate
Tasker t = new Tasker();
t.doer += DoProvider.DoThis;
t.CallDoer("I am doing something");
// anonymous method
Tasker t2 = new Tasker();
t2.doer += delegate(string s){
Console.WriteLine (s);
};
t2.CallDoer("I am doing something again");
// syntactical sugar over anonymous methods aka lambda expressions
Tasker t3 = new Tasker();
t3.doer += (s)=>{
Console.WriteLine (s);
};
t3.CallDoer("I am doing yet another thing");
}
}
public delegate void DoSomething(string Foo);
public class Tasker
{
public event DoSomething doer;
public void CallDoer(string s)
{
doer.Invoke(s);
}
}
public static class DoProvider
{
public static void DoThis(string Bar)
{
Console.WriteLine (Bar);
}
}
I know this is slightly a duplicate of this question here: Blocking and waiting for an event
However, I was in the process of writing a EventWaiter and ran into a problem. Here is a (majorly) simplified version of what I've been working on:
public class EventWaiter
{
private AutoResetEvent _autoResetEvent = new AutoResetEvent(false);
private EventInfo _event = null;
private object _eventContainer = null;
public EventWaiter(object eventContainer, string eventName)
{
_eventContainer = eventContainer;
_event = eventContainer.GetType().GetEvent(eventName);
}
public void WaitForEvent()
{
MethodInfo method = this.GetType().GetMethod("DynamicCaller");
Delegate handler = Delegate.CreateDelegate(this._event.EventHandlerType, this, method);
_event.AddEventHandler(_eventContainer, handler);
_autoResetEvent.WaitOne();
_event.RemoveEventHandler(_eventContainer, _handler);
}
public void DynamicCaller(/* insert magic here */)
{
_autoResetEvent.Set();
}
}
The usage would simply be:
EventWaiter ew = new EventWaiter(someClass, "someEvent");
ew.WaitForEvent();
Basically what is happening, is its registering the DynamicCaller void as a handler for this event. The problem is, events have different signatures, and I want to be able to handle the event regardless of the delegate used.
I can get the type of the delegate with this._event.EventHandlerType but how can I use to that create a completely reusable class no matter what the delegate is? If the DynamicCaller parameters are not exactly the same as the event delegate parameters i get an exception.
As a side note, I did a bunch of looking into code in the framework, and if i had access to some of that I think this would be easy. Too bad that alot of the classes I would need are all internal to the framework.
Since all events that respect the recommended pattern have a parameter of type object and a parameter of a type that derives from EventArgs, you should be able to handle all these events with this signature:
void DynamicCaller(object sender, EventArgs e)
Of course it won't work for non-standard event signatures...
EDIT: here's an example with a dynamically generated handler:
public class EventWaiter
{
private AutoResetEvent _autoResetEvent = new AutoResetEvent(false);
private EventInfo _event = null;
private object _eventContainer = null;
public EventWaiter(object eventContainer, string eventName)
{
_eventContainer = eventContainer;
_event = eventContainer.GetType().GetEvent(eventName);
}
public void WaitForEvent()
{
Delegate handler = CreateHandler();
_event.AddEventHandler(_eventContainer, handler);
_autoResetEvent.WaitOne();
_event.RemoveEventHandler(_eventContainer, handler);
}
private Delegate CreateHandler()
{
var invokeMethod = _event.EventHandlerType.GetMethod("Invoke");
var invokeParameters = invokeMethod.GetParameters();
var handlerParameters = invokeParameters.Select(p => Expression.Parameter(p.ParameterType, p.Name)).ToArray();
var body = Expression.Call(Expression.Constant(_autoResetEvent), "Set", null);
var handlerExpression = Expression.Lambda(_event.EventHandlerType, body, handlerParameters);
return handlerExpression.Compile();
}
}
EDIT: SLaks was faster than me ;)
You should use expression trees to compile a method with an arbitrary set of parameters that calls your callback:
Expression.Lambda(
_event.EventHandlerType,
Expression.Call(Exrpession.Constant(_autoResetEvent),
typeof(AutoResetEvent).GetMethod("Set")),
_event.EventHandlerType.GetMethod("Invoke")
.GetParameters()
.Select(p => Expression.Parameter(p.ParameterType))
).Compile();
Note that you can make your system type-safe using generics and expression trees:
new EventWaiter(_ => someObject.SomeEvent += _)
Where _ is an ordinary (but short) parameter name.
You could do what you want with TaskCompletionSource:
TaskCompletionSource<string> tcs =
new TaskCompletionSource<string>();
WebClient client = new WebClient();
client.DownloadStringCompleted += (sender, args) => {
if (args.Error != null) tcs.SetException(args.Error);
else if (args.Cancelled) tcs.SetCanceled();
else tcs.SetResult(args.Result);
};
client.DownloadStringAsync(address);
tcs.Task.Wait(); // WaitForEvent
The solutions here are good, but for me, using strings, reflection has a bit of a code smell, so I'll go for a generic version:
public class EventWaiter
{
public enum Mode
{
Wait,
Detach
}
public static Func<Mode, TEventArgs> Create<TDelegate, TEventArgs>(
Func<Action<object, TEventArgs>, TDelegate> converter,
Action<TDelegate> addHandler,
Action<TDelegate> removeHandler
)
{
AutoResetEvent semaphore = new AutoResetEvent(false);
TEventArgs args = default(TEventArgs);
TDelegate handler = converter((s, e) => { args = e; semaphore.Set(); });
addHandler(handler);
return mode =>
{
if (mode == Mode.Wait)
{
semaphore.WaitOne();
return args;
}
else
{
removeHandler(handler);
return default(TEventArgs);
}
};
}
Usage:
var evt =
EventWaiter.Create<SerialDataReceivedEventHandler, SerialDataReceivedEventArgs>
(handler => (s, e) => handler(s, e),
h => port.DataReceived += h,
h => port.DataReceived -= h);
var firstArgument = evt(EventWaiter.Mode.Wait); //Wait for first event
var secondArgument = evt(EventWaiter.Mode.Wait); //Wait for second event
evt(EventWaiter.Mode.Detach); //Dispose
I know about EventInfo.AddEventHandler(...) method which can be used to attach handler to an event. But what should be done if i can not even define proper signature of the event handler, as in, i don't even have reference to the event args expected by the handler?
I will explain the problem with the proper code.
// Scenario when I have everything available in my solution, Zero Reflection Scenario.
internal class SendCommentsManager
{
public void Customize(IRFQWindowManager rfqWindowManager)
{
rfqWindowManager.SendComment += HandleRfqSendComment;
}
private void HandleRfqSendComment(object sender, SendCommentEventArgs args)
{
args.Cancel = true;
}
}
Now, I want to achieve the same objective by using reflection. I have been able to figure out most of it but when i attach a delegate to the event (using AddEventHandler) it throws "Error binding to target method." exception.
I understand the reason behind this exception, attaching a wrong delegate to an event. But there must be some way to achieve this.
internal class SendCommentsManagerUsingReflection
{
public void Customize(IRFQWindowManager rfqWindowManager)
{
EventInfo eventInfo = rfqWindowManager.GetType().GetEvent("SendComment");
eventInfo.AddEventHandler(rfqWindowManager,
Delegate.CreateDelegate(eventInfo.EventHandlerType, this, "HandleRfqSendComment"));
//<<<<<<<<<<ABOVE LINE IS WHERE I AM GOING WRONG>>>>>>>>>>>>>>
}
private void HandleRfqSendComment(object sender, object args)
{
Type sendCommentArgsType = args.GetType();
PropertyInfo cancelProperty = sendCommentArgsType.GetProperty("Cancel");
cancelProperty.SetValue(args, true, null);
}
}
I think your code is failing because the HandleRfqSendComment is private. Instead you could directly create a delegate to that method, without passing its name to CreateDelegate. You would then need to convert the delegate to the required type, using the following method :
public static Delegate ConvertDelegate(Delegate originalDelegate, Type targetDelegateType)
{
return Delegate.CreateDelegate(
targetDelegateType,
originalDelegate.Target,
originalDelegate.Method);
}
In your code, you could use this method as follows :
EventInfo eventInfo = rfqWindowManager.GetType().GetEvent("SendComment");
Action<object, object> handler = HandleRfqSendComment;
Delegate convertedHandler = ConvertDelegate(handler, eventInfo.EventHandlerType);
eventInfo.AddEventHandler(rfqWindowManager, convertedHandler);
A small addition to the already awesome answers here. Here's a helper class you could use to subscribe to events with actions.
public static partial class ReactiveExtensions
{
public static EventHandler<TEvent> CreateGenericHandler<TEvent>(object target, MethodInfo method)
{
return (EventHandler<TEvent>)Delegate
.CreateDelegate(typeof(EventHandler<TEvent>),
target, method);
}
public static EventHandler CreateHandler(object target, MethodInfo method)
{
return (EventHandler)Delegate
.CreateDelegate(typeof(EventHandler),
target, method);
}
static void BindEventToAction(object target, EventInfo eventInfo, Delegate action)
{
MethodInfo method;
if (eventInfo.EventHandlerType.IsGenericType)
{
method = typeof(ReactiveExtensions)
.GetMethod(nameof(CreateGenericHandler))
.MakeGenericMethod(
eventInfo.EventHandlerType.GetGenericArguments());
}
else
{
method = typeof(ReactiveExtensions)
.GetMethod(nameof(CreateHandler));
}
Delegate #delegate = (Delegate)method.Invoke(null,
new object[] { action.Target, action.Method });
eventInfo.AddEventHandler(target, #delegate);
}
}
Here's a sample on how to use this:
public static partial class ReactiveExtensions
{
public static void Subscribe<T>(T source, string eventName)
{
EventInfo eventInfo = typeof(T).GetEvent(eventName);
Action<object, object> action = (s, e) =>
{
Console.WriteLine("Event Called");
};
BindEventToAction(source, eventInfo, action);
}
}
Is it possible to unsubscribe an anonymous method from an event?
If I subscribe to an event like this:
void MyMethod()
{
Console.WriteLine("I did it!");
}
MyEvent += MyMethod;
I can un-subscribe like this:
MyEvent -= MyMethod;
But if I subscribe using an anonymous method:
MyEvent += delegate(){Console.WriteLine("I did it!");};
is it possible to unsubscribe this anonymous method? If so, how?
Action myDelegate = delegate(){Console.WriteLine("I did it!");};
MyEvent += myDelegate;
// .... later
MyEvent -= myDelegate;
Just keep a reference to the delegate around.
One technique is to declare a variable to hold the anonymous method which would then be available inside the anonymous method itself. This worked for me because the desired behavior was to unsubscribe after the event was handled.
Example:
MyEventHandler foo = null;
foo = delegate(object s, MyEventArgs ev)
{
Console.WriteLine("I did it!");
MyEvent -= foo;
};
MyEvent += foo;
Since C# 7.0 local functions feature has been released, the approach suggested by J c becomes really neat.
void foo(object s, MyEventArgs ev)
{
Console.WriteLine("I did it!");
MyEvent -= foo;
};
MyEvent += foo;
So, honestly, you do not have an anonymous function as a variable here. But I suppose the motivation to use it in your case can be applied to local functions.
From memory, the specification explicitly doesn't guarantee the behaviour either way when it comes to equivalence of delegates created with anonymous methods.
If you need to unsubscribe, you should either use a "normal" method or retain the delegate somewhere else so you can unsubscribe with exactly the same delegate you used to subscribe.
In 3.0 can be shortened to:
MyHandler myDelegate = ()=>Console.WriteLine("I did it!");
MyEvent += myDelegate;
...
MyEvent -= myDelegate;
Instead of keeping a reference to any delegate you can instrument your class in order to give the event's invocation list back to the caller. Basically you can write something like this (assuming that MyEvent is declared inside MyClass):
public class MyClass
{
public event EventHandler MyEvent;
public IEnumerable<EventHandler> GetMyEventHandlers()
{
return from d in MyEvent.GetInvocationList()
select (EventHandler)d;
}
}
So you can access the whole invocation list from outside MyClass and unsubscribe any handler you want. For instance:
myClass.MyEvent -= myClass.GetMyEventHandlers().Last();
I've written a full post about this tecnique here.
Kind of lame approach:
public class SomeClass
{
private readonly IList<Action> _eventList = new List<Action>();
...
public event Action OnDoSomething
{
add {
_eventList.Add(value);
}
remove {
_eventList.Remove(value);
}
}
}
Override the event add/remove methods.
Keep a list of those event handlers.
When needed, clear them all and re-add the others.
This may not work or be the most efficient method, but should get the job done.
If you want to be able to control unsubscription then you need to go the route indicated in your accepted answer. However, if you are just concerned about clearing up references when your subscribing class goes out of scope, then there is another (slightly convoluted) solution which involves using weak references. I've just posted a question and answer on this topic.
One simple solution:
just pass the eventhandle variable as parameter to itself.
Event if you have the case that you cannot access the original created variable because of multithreading, you can use this:
MyEventHandler foo = null;
foo = (s, ev, mehi) => MyMethod(s, ev, foo);
MyEvent += foo;
void MyMethod(object s, MyEventArgs ev, MyEventHandler myEventHandlerInstance)
{
MyEvent -= myEventHandlerInstance;
Console.WriteLine("I did it!");
}
If the best way is to keep a reference on the subscribed eventHandler, this can be achieved using a Dictionary.
In this example, I have to use a anonymous method to include the mergeColumn parameter for a set of DataGridViews.
Using the MergeColumn method with the enable parameter set to true enables the event while using it with false disables it.
static Dictionary<DataGridView, PaintEventHandler> subscriptions = new Dictionary<DataGridView, PaintEventHandler>();
public static void MergeColumns(this DataGridView dg, bool enable, params ColumnGroup[] mergedColumns) {
if(enable) {
subscriptions[dg] = (s, e) => Dg_Paint(s, e, mergedColumns);
dg.Paint += subscriptions[dg];
}
else {
if(subscriptions.ContainsKey(dg)) {
dg.Paint -= subscriptions[dg];
subscriptions.Remove(dg);
}
}
}
if you want refer to some object with this delegate, may be you can use Delegate.CreateDelegate(Type, Object target, MethodInfo methodInfo)
.net consider the delegate equals by target and methodInfo
There is a way to solve this by implementing the closure yourself instead of a lambda expression.
Assume that the class to be used as a capture variable is as follows.
public class A
{
public void DoSomething()
{
...
}
}
public class B
{
public void DoSomething()
{
...
}
}
public class C
{
public void DoSomething()
{
...
}
}
These classes will be used as capture variables, so we instantiate them.
A a = new A();
B b = new B();
C c = new C();
Implement the closure class as shown below.
private class EventHandlerClosure
{
public A a;
public B b;
public C c;
public event EventHandler Finished;
public void MyMethod(object, MyEventArgs args)
{
a.DoSomething();
b.DoSomething();
c.DoSomething();
Console.WriteLine("I did it!");
Finished?.Invoke(this, EventArgs.Empty);
}
}
Instantiate the closure class, create a handler, then subscribe to the event and subscribe to the lambda expression that unsubscribes from the closure class's Finished event.
var closure = new EventHandlerClosure
{
a = a,
b = b,
c = c
};
var handler = new MyEventHandler(closure.MyMethod);
MyEvent += handler;
closure.Finished += (s, e)
{
MyEvent -= handler;
}
I discovered this quite old thread recently for a C# project and found all the answers very useful. However, there was one aspect that didn't work well for my particular use case - they all put the burden of unsubscribing from an event on the subscriber. I understand that one could make the argument that it's the subscribers job to handle this, however that isn't realistic for my project.
My primary use case for events is for listening to timers to sequence animations (it's a game). In this scenario, I use a lot of anonymous delegates to chain together sequences. Storing a reference to these isn't very practical.
In order to solve this, I've created a wrapper class around an event that lets you subscribe for a single invocation.
internal class EventWrapper<TEventArgs> {
private event EventHandler<TEventArgs> Event;
private readonly HashSet<EventHandler<TEventArgs>> _subscribeOnces;
internal EventWrapper() {
_subscribeOnces = new HashSet<EventHandler<TEventArgs>>();
}
internal void Subscribe(EventHandler<TEventArgs> eventHandler) {
Event += eventHandler;
}
internal void SubscribeOnce(EventHandler<TEventArgs> eventHandler) {
_subscribeOnces.Add(eventHandler);
Event += eventHandler;
}
internal void Unsubscribe(EventHandler<TEventArgs> eventHandler) {
Event -= eventHandler;
}
internal void UnsubscribeAll() {
foreach (EventHandler<TEventArgs> eventHandler in Event?.GetInvocationList()) {
Event -= eventHandler;
}
}
internal void Invoke(Object sender, TEventArgs e) {
Event?.Invoke(sender, e);
if(_subscribeOnces.Count > 0) {
foreach (EventHandler<TEventArgs> eventHandler in _subscribeOnces) {
Event -= eventHandler;
}
_subscribeOnces.Clear();
}
}
internal void Remove() {
UnsubscribeAll();
_subscribeOnces.Clear();
}
}
The side benefit of having this in a class is that you can make it private and expose only the functionality you want. For example, only expose the SubscribeOnce (and not the Subscribe) method.
public class MyClass {
private EventWrapper<MyEventEventArgs> myEvent = new EventWrapper<MyEventEventArgs>();
public void FireMyEvent() {
myEvent.Invoke(this, new MyEventEventArgs(1000, DateTime.Now));
}
public void SubscribeOnce(EventHandler<MyEventEventArgs> eventHandler) {
myEvent.SubscribeOnce(eventHandler);
}
public class MyEventEventArgs : EventArgs {
public int MyInt;
public DateTime MyDateTime;
public MyEventEventArgs(int myInt, DateTime myDateTime) {
MyInt = myInt;
MyDateTime = myDateTime;
}
}
}
The tradeoff here is more overhead for having an instance of this for each event, however in my scenario - this is an acceptable tradeoff to ensure that garbage gets collected efficiently and the code is more maintainable on the subscriber side. Full example here.
Here is a simple solution, which removes all assigned methods from an event. Also anonymous methods.
Use this code and adjust the names.
if (MyEvent != null)
foreach (Delegate del in MyEvent.GetInvocationList())
MyEvent -= (EventHandler<MyEventHandlerType>)del;
Example usage
public class SomeClass
{
public event EventHandler<NiceEventArgs> NiceEvent;
public void RemoveHandlers()
{
if (NiceEvent != null)
foreach (Delegate del in NiceEvent.GetInvocationList())
NiceEvent -= (EventHandler<NiceEventArgs>)del;
}
}
Thanks to hemme's answer, which I used as inspiration.