One shot events using Lambda in C# - c#

I find myself doing this sort of thing quite often:-
EventHandler eh = null; //can't assign lambda directly since it uses eh
eh = (s, args) =>
{
//small snippet of code here
((SomeType)s).SomeEvent -= eh;
}
variableOfSomeType.SomeEvent += eh;
Basically I only want to attach an event handler to listen for one shot from the event, I no longer want to stay attached after that. Quite often that "snippert of code" is just one line.
My mind is going a bit numb, I'm sure there must be something I can do so I don't need to repeat all this overhead. Bear in mind that EventHandler may well be EventHandler<T>.
Any ideas how I can tidy up the repeative part of the code and just leave the snippet in a Lambda?

You could attache a permanent event handler to the event. The event handler then invokes "one shot event handlers" that are added to an internal queue:
OneShotHandlerQueue<EventArgs> queue = new OneShotHandlerQueue<EventArgs>();
Test test = new Test();
// attach permanent event handler
test.Done += queue.Handle;
// add a "one shot" event handler
queue.Add((sender, e) => Console.WriteLine(e));
test.Start();
// add another "one shot" event handler
queue.Add((sender, e) => Console.WriteLine(e));
test.Start();
Code:
class OneShotHandlerQueue<TEventArgs> where TEventArgs : EventArgs {
private ConcurrentQueue<EventHandler<TEventArgs>> queue;
public OneShotHandlerQueue() {
this.queue = new ConcurrentQueue<EventHandler<TEventArgs>>();
}
public void Handle(object sender, TEventArgs e) {
EventHandler<TEventArgs> handler;
if (this.queue.TryDequeue(out handler) && (handler != null))
handler(sender, e);
}
public void Add(EventHandler<TEventArgs> handler) {
this.queue.Enqueue(handler);
}
}
Test class:
class Test {
public event EventHandler Done;
public void Start() {
this.OnDone(new EventArgs());
}
protected virtual void OnDone(EventArgs e) {
EventHandler handler = this.Done;
if (handler != null)
handler(this, e);
}
}

You can use reflection:
public static class Listener {
public static void ListenOnce(this object eventSource, string eventName, EventHandler handler) {
var eventInfo = eventSource.GetType().GetEvent(eventName);
EventHandler internalHandler = null;
internalHandler = (src, args) => {
eventInfo.RemoveEventHandler(eventSource, internalHandler);
handler(src, args);
};
eventInfo.AddEventHandler(eventSource, internalHandler);
}
public static void ListenOnce<TEventArgs>(this object eventSource, string eventName, EventHandler<TEventArgs> handler) where TEventArgs : EventArgs {
var eventInfo = eventSource.GetType().GetEvent(eventName);
EventHandler<TEventArgs> internalHandler = null;
internalHandler = (src, args) => {
eventInfo.RemoveEventHandler(eventSource, internalHandler);
handler(src, args);
};
eventInfo.AddEventHandler(eventSource, internalHandler);
}
}
Use it like so:
variableOfSomeType.ListenOnce("SomeEvent",
(s, args) => Console.WriteLine("I should print only once!"));
variableOfSomeType.ListenOnce<InterestingEventArgs>("SomeOtherEvent",
(s, args) => Console.WriteLine("I should print only once!"));

If you can use the Reactive Extensions for .NET, you can simplify this.
You can make an Observable from an event, and only listen for the first element using .Take(1), to do your small snippet of code. This turns this entire process into a couple of lines of code.
Edit: In order to demonstrate, I've made a full sample program (I'll paste below).
I moved the observable creation and subscription into a method (HandleOneShot). This lets you do what you're attempting with a single method call. For demonstrating, I made a class with two properties that implements INotifyPropertyChanged, and am listening for the first property changed event, writing to the console when it occurs.
This takes your code, and changes it to:
HandleOneShot<SomeEventArgs>(variableOfSomeType, "SomeEvent", e => {
// Small snippet of code here
});
Notice that all of the subscription/unsubscription happens automatically for you behind the scenes. There's no need to handle putting in the subscription manually - just Subscribe to the Observable, and Rx takes care of this for you.
When run, this code prints:
Setup...
Setting first property...
**** Prop2 Changed! /new val
Setting second property...
Setting first property again.
Press ENTER to continue...
You only get a single, one shot trigger of your event.
namespace ConsoleApplication1
{
using System;
using System.ComponentModel;
using System.Linq;
class Test : INotifyPropertyChanged
{
private string prop2;
private string prop;
public string Prop
{
get {
return prop;
}
set
{
if (prop != value)
{
prop = value;
if (PropertyChanged!=null)
PropertyChanged(this, new PropertyChangedEventArgs("Prop"));
}
}
}
public string Prop2
{
get
{
return prop2;
}
set
{
if (prop2 != value)
{
prop2 = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("Prop2"));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
class Program
{
static void HandleOneShot<TEventArgs>(object target, string eventName, Action<TEventArgs> action) where TEventArgs : EventArgs
{
var obsEvent = Observable.FromEvent<TEventArgs>(target, eventName).Take(1);
obsEvent.Subscribe(a => action(a.EventArgs));
}
static void Main(string[] args)
{
Test test = new Test();
Console.WriteLine("Setup...");
HandleOneShot<PropertyChangedEventArgs>(
test,
"PropertyChanged",
e =>
{
Console.WriteLine(" **** {0} Changed! {1}/{2}!", e.PropertyName, test.Prop, test.Prop2);
});
Console.WriteLine("Setting first property...");
test.Prop2 = "new value";
Console.WriteLine("Setting second property...");
test.Prop = "second value";
Console.WriteLine("Setting first property again...");
test.Prop2 = "other value";
Console.WriteLine("Press ENTER to continue...");
Console.ReadLine();
}
}
}

Another user encountered a very similar problem, and I believe the solution in that thread applies here.
In particular, what you have is not an instance of the publish/subscribe pattern, its a message queue. Its easy enough to create your own message queue using a Queue{EventHandler}, where you dequeue events as you invoke them.
So instead of hooking on to an event handler, your "one-shot" events should expose a method allowing clients to add an function to the message queue.

Does it work? If so, then I say go for it. For a one-shot event that looks to be quite elegant.
What I like...
If s is garbage collected, so will the event handler.
The detaching code is right next to the attaching code, making it easy to see what you are are doing.
You might be able to generalize it, but I'm not entierly sure how to because I can't seem to get a pointer to a event.

Personally, I just create a specialized extension method for whatever type has the event I'm dealing with.
Here's a basic version of something I am using right now:
namespace MyLibrary
{
public static class FrameworkElementExtensions
{
public static void HandleWhenLoaded(this FrameworkElement el, RoutedEventHandler handler)
{
RoutedEventHandler wrapperHandler = null;
wrapperHandler = delegate
{
el.Loaded -= wrapperHandler;
handler(el, null);
};
el.Loaded += wrapperHandler;
}
}
}
The reason I think this is the best solution is because you often don't need to just handle the event one time. You also often need to check if the event has already passed... For instance, here is another version of the above extension method that uses an attached property to check if the element is already loaded, in which case it just calls the given handler right away:
namespace MyLibraryOrApplication
{
public static class FrameworkElementExtensions
{
public static void HandleWhenLoaded(this FrameworkElement el, RoutedEventHandler handler)
{
if ((bool)el.GetValue(View.IsLoadedProperty))
{
// el already loaded, call the handler now.
handler(el, null);
return;
}
// el not loaded yet. Attach a wrapper handler that can be removed upon execution.
RoutedEventHandler wrapperHandler = null;
wrapperHandler = delegate
{
el.Loaded -= wrapperHandler;
el.SetValue(View.IsLoadedProperty, true);
handler(el, null);
};
el.Loaded += wrapperHandler;
}
}
}

You probably want to work with the new async/await idioms.
Usually when I need to execute an event handler one-shot like you described, what I really need is something like:
await variableOfSomeSort.SomeMethodAsync();
//small snippet of code here

Why not do use the delegate stack built into the event?
Something like...
private void OnCheckedIn(object sender, Session e)
{
EventHandler<Session> nextInLine = null;
lock (_syncLock)
{
if (SessionCheckedIn != null)
{
nextInLine = (EventHandler<Session>)SessionCheckedIn.GetInvocationList()[0];
SessionCheckedIn -= nextInLine;
}
}
if ( nextInLine != null )
{
nextInLine(this, e);
}
}

Related

How to represent any event and any function in C#?

I had the idea of developing a class to manage events unsubscriptions in a safe way so I don't have to manually write -= to each event subscription of this system when the time comes (in this system there are groups of events that are subscribed and unsubscribed together).
I thought of creating a class that manages a Dictionary where the key is an event (any possible event) and the value is a method (any possible method). This way I would have a method to subscribe a function to an event and it will do that alongside registering the pair in the Dictionary; and another method to unsubscribe all events added with this class (which would just iterate the dictionary and unsubscribe all events).
Thing is: I can't find how to have a common type of any event that I could use as generic argument to the dictonary's key and also I don't know how to represent any function in C# to set as the dictionary's value. In C/C++ I could just treat them both as void pointers as all I would need is the pointer of the function itself.
Is that even possible to do using C#? Are there existing better methods/strategies to accomplish that?
You will have to come up with a way of generating the key for the event, since you can't use a plain event as a dictionary key. One way to do that would be to concatenate the event's declaring class name with the name of the event.
Then you could use a dictionary using that as a key, with a List<Action> as the value. Here, the Action would be a delegate that you could call to unsubscribe.
You could wrap that dictionary in a class to provide Subscribe() and Unsubscribe() methods like so:
using System;
using System.Collections.Generic;
namespace ConsoleApp2
{
class Program
{
static void Main()
{
string eventKey = test.GetType().FullName + '.' + nameof(test.MyEvent);
subs.Subscribe(eventKey, () => test.MyEvent += handler, () => test.MyEvent -= handler);
subs.Subscribe(eventKey, () => test.MyEvent += handler, () => test.MyEvent -= handler);
test.RaiseEvent(); // Two handlers called.
subs.Unsubscribe(eventKey);
test.RaiseEvent(); // No handlers called (both were unsubscribed).
}
static void handler(object sender, EventArgs args)
{
Console.WriteLine("Handling event.");
}
static readonly Test test = new Test();
static readonly EventSubscriptions subs = new EventSubscriptions();
}
public class EventSubscriptions
{
public void Subscribe(string key, Action subscribe, Action unsubscribe)
{
subscriptions.TryGetValue(key, out var subs);
if (subs == null)
{
subs = new List<Action>();
subscriptions.Add(key, subs);
}
subscribe();
subs.Add(unsubscribe);
}
public void Unsubscribe(string key)
{
subscriptions.TryGetValue(key, out var subs);
if (subs != null)
{
foreach (var unsub in subs)
{
unsub();
}
subscriptions.Remove(key);
}
}
readonly Dictionary<string, List<Action>> subscriptions = new Dictionary<string, List<Action>>();
}
class Test
{
public event EventHandler<EventArgs> MyEvent;
public void RaiseEvent()
{
MyEvent?.Invoke(this, EventArgs.Empty);
}
}
}
Assuming all of your event handlers are of the void MyHandler(object sender, EventArgs e) variety, your dictionary would be Dictionary<string, Action<object,EventArgs>> (assuming you wanted the key to be string - anything else would work too)

trig event when created or deleted

I wish to trig event when class instance is created (or deleted), but my the class itself.
It seems impossible (because no other class instance can simultaneously create object and add handler on the event of created class). Is there an other way ?
public class MyClass {
public delegate void delegate_MyClassCreated( MyClass me);
public delegate_MyClassCreated event_MyClassCreated;
public delegate void delegate_MyClassDeleted( MyClass me);
public delegate_MyClassDeleted event_MyClassDeleted;
public MyClass() {
//... some initialisations here...
try {
event_MyClassCreated( this);
}
catch {}
}
~ MyClass() {
try {
event_MyClassDeleted( this);
}
catch {}
}
}
//...
// in other class
((MyClass)new MyClass()).event_MyClassCreated += callback_MyClassCreated;
// ...
void callback_MyClassCreated( MyClass me) {
// action on me
}
Why don't you use static events? Something like that:
public class MyClass {
...
// static event doesn't need any instance and so
// could be called within constructor
public static event EventHandler MyClassCreated;
public MyClass() {
...
if (!Object.ReferenceEquals(null, MyClassCreated))
MyClassCreated(this, EventArgs.Empty);
}
}
...
// Assigning callback for the event (no MyClass instance required)
MyClass.MyClassCreated += callback_MyClassCreated;
Use an IoC container such as Autofac :-)
I am unsure about Unity, but at least Autofac has so-called Activation Events, where you may handle the creation of an instance either in a general manner or type-specific.
Have a look at this blog post.
If you wish to not use e.g. Autofac, you are probably left with a DIY implementation. - This could be implementing factories for your classes, which as part of the instantiation process fires an event.
Quick'n'Dirty DIY Example using static events:
using System;
namespace ConsoleApplication2
{
internal class Program
{
private static void Main(string[] args)
{
var myCreatingEventHandler = new EventHandler((sender, eventArgs) => Console.WriteLine("Creating"));
var myCreatingEventHandler2 = new EventHandler((sender, eventArgs) => Console.WriteLine("Creating2"));
var myDisposingEventHandler = new EventHandler((sender, eventArgs) => Console.WriteLine("Disposing"));
var myDisposingEventHandler2 = new EventHandler((sender, eventArgs) => Console.WriteLine("Disposing2"));
MyClass.Creating += myCreatingEventHandler;
MyClass.Disposing += myDisposingEventHandler;
using (var test = new MyClass())
{
// Prints "Creating" and "Disposing".
}
Console.WriteLine();
MyClass.Creating += myCreatingEventHandler2;
MyClass.Disposing += myDisposingEventHandler2;
using (var test = new MyClass())
{
// Prints "Creating", "Creating2", "Disposing" and "Disposing2".
}
Console.WriteLine();
MyClass.Creating -= myCreatingEventHandler;
MyClass.Disposing -= myDisposingEventHandler;
using (var test = new MyClass()) {
// Prints "Creating2" and "Disposing2".
}
Console.WriteLine();
MyClass.Creating -= myCreatingEventHandler2;
MyClass.Disposing -= myDisposingEventHandler2;
using (var test = new MyClass())
{
// Prints nothing (removed subscriptions to avoid "memory leak".
}
// You may choose call such a method to ensure that ALL handlers are removed from the invocation list.
MyClass.RemoveSubscriptions();
Console.ReadKey();
}
}
internal class MyClass : IDisposable
{
public static event EventHandler Creating;
private static void OnCreating()
{
EventHandler handler = Creating;
if (handler != null) handler(null, EventArgs.Empty);
}
public static event EventHandler Disposing;
private static void OnDisposing()
{
EventHandler handler = Disposing;
if (handler != null) handler(null, EventArgs.Empty);
}
public MyClass()
{
OnCreating();
}
public void Dispose()
{
OnDisposing();
}
public static void RemoveSubscriptions()
{
// Setting the events to null can only be done from within the class. From the outside only += and -= are allowed.
Creating = null;
Disposing = null;
}
}
}
However, I do not recommend going with the static event approach. Why? - Because you have to be very careful when using static events. If you do not remove the handlers explicitly, you will have the managed equivalent to a memory leak. It may produce unpredictable behavior. You don't want that ;-)
My recommendation as the safest choice would be going with e.g. Autofac and gain a several other goodies in the process ;-)
However, it does not handle when the instance is disposed. - Any reason you wish to listen for that event?

How do I use Dynamic Fifo in C#? [duplicate]

I need to be able to trigger a event whenever an object is added to a Queue<Delegate>.
I created a new class that extends Queue:
public delegate void ChangedEventHandler(object sender, EventArgs e);
public class QueueWithChange<Delegate> : Queue<Delegate>
{
public event ChangedEventHandler Changed;
protected virtual void OnChanged(EventArgs e) {
if (Changed != null)
{
Changed(this, e);
}
}
}
And then attached the event from another class, like such:
QueueWithChange<TimerDelegate> eventQueue = new QueueWithChange<TimerDelegate>();
//
eventQueue.Changed += new ChangedEventHandler(delegate(object s, EventArgs ex) {
//This event is not being triggered, so this code is unreachable atm...and that is my problem
if (eventQueue.Count > 0)
{
eventQueue.Dequeue().Invoke(new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(5) });
actionTimer.Stop();
}
});
But whenever I enqueue an object (eventQueue.Enqueue(something)), the attached event is not being fired.
What am I missing here?
If you mean the non-generic Queue class, then you can just override Enqueue:
public override void Enqueue(object obj)
{
base.Enqueue(obj);
OnChanged(EventArgs.Empty);
}
However, if you mean the generic Queue<T> class, then note that there is no suitable virtual method to override. You might do better to encapsulate the queue with your own class:
(** important edit: removed base-class!!! **)
class Foo<T>
{
private readonly Queue<T> queue = new Queue<T>();
public event EventHandler Changed;
protected virtual void OnChanged()
{
if (Changed != null) Changed(this, EventArgs.Empty);
}
public virtual void Enqueue(T item)
{
queue.Enqueue(item);
OnChanged();
}
public int Count { get { return queue.Count; } }
public virtual T Dequeue()
{
T item = queue.Dequeue();
OnChanged();
return item;
}
}
However, looking at your code, it seems possible that you are using multiple threads here. If that is the case, consider a threaded queue instead.
I just did write up on what I call a TriggeredQueue. It's inspired the answer by Marc Gravell.
You can find my post here: http://joesauve.com/triggeredqueuet
And the Gist here: http://gist.github.com/jsauve/b2e8496172fdabd370c4
It has four events:
WillEnqueue
WillDequeue
DidEnqueue
DidDequeue
You can hook into any of these like so:
YourQueue.WillEnqueue += (sender, e) => {
// kick off some process
};
YourQueue.DidEnqueue += (sender, e) => {
// kick off some process
// e.Item provides access to the enqueued item, if you like
};
YourQueue.WillDequeue += (sender, e) => {
// kick off some process
};
YourQueue.DidDequeue += (sender, e) => {
// kick off some process
// e.Item provides access to the dequeued item, if you like
};
One neat trick is that you can use the DidDequeue method to kick off some process to ensure that the queue is full by making a web request or loading some data from a filesystem, etc. I use this class in Xamarin mobile apps to ensure that data and images are pre-cached in order to provide a smooth user experience, instead of loading images AFTER they scroll onto the screen (like you might see in Facebook and countless other apps).
try
public new void Enqueue(Delegate d)
{
base.Enqueue(d);
OnChanged(EventArgs.Empty);
}
You have to override Enqueue, to call OnChanged.

How do I unbind all instances of an event handler? Why doesn't -= remove all the instances?

Consider this snippet:
class Foo
{
public event Action Event;
public void TriggerEvent()
{
if (Event != null) {
Event();
}
}
}
static void Handler()
{
Console.WriteLine("hi!");
}
static void Main()
{
var obj = new Foo();
obj.Event += Handler;
obj.Event += Handler;
obj.TriggerEvent();
Console.WriteLine("---");
obj.Event -= Handler;
obj.TriggerEvent();
}
The output I get:
hi!
hi!
---
hi!
The last "hi!" was quite unexpected. To remove it I have to call Event -= Handler; one more time. But what if I don't know how many times handler was bound?
UPDATE: Would be interesting to know the reasons behind this a bit counterintuitive behavior: why doesn't -= remove all the instances?
UPDATE 2: I realized that I find this behavior counterintuitive because of the difference with jQuery.
var handler = function() { console.log('hi!'); }, obj = {};
$(obj).on("event", handler).on("event", handler).trigger("event");
console.log("---");
$(obj).off("event", handler).trigger("event");
Output:
hi!
hi!
---
I think I understand why you might consider your example to be counter-intuitive.
Consider this modification
var del = new Action(Handler);
obj.Event += del;
obj.Event += del;
obj.TriggerEvent();
Console.WriteLine("---");
obj.Event -= del;
obj.TriggerEvent();
It works exactly the same as yours, but why?
When you used
obj.Event += Handler
The compiler did something behind your back. It created a new instance of Action(Handler) three times (two add, one remove). In the modification we use exactly the same delegate object.
So the real question is: In your example, why did the remove even work? You're passing an object to remove that wasn't used to add. The answer is that delegates have value equality.
var del1 = new Action(Handler);
var del2 = new Action(Handler);
Console.WriteLine("Reference equal? {0}, Value equal? {1}", Object.ReferenceEquals(del1, del2), del1.Equals(del2));
// Reference equal? False, Value equal? True
So now you might be thinking, "Why were two event handlers added? Shouldn't there be only one since they are the same handler?"
The answer is, "No". A multi-cast delegate doesn't care if you add the same handler multiple times, it's not a set, it's a list.
When you removed one handler, it recognized that there are two identical handlers in its list and removed one of them.
try this solution Removing Event Handlers using Reflection
or
Delegate[] dellist = myEvent.GetInvocationList();
foreach (Delegate d in v)
myEvent-= (d as MyDelegate);//MyDelegate is type of delegate
Delegates combine all handlers that you assign to it. If you assign the same handler twice it will be called twice and has to be removed twice. I don't think this is counterintuitive.
If you have control over the class that defines the event you can use something like the following to remove all instances of a specific handler at once:
private Action _Event;
public event Action Event
{
add
{
_Event += value;
}
remove
{
while (_Event != null && _Event.GetInvocationList().Contains(value))
{
_Event -= value;
}
}
}
If you do not have control over the event then you have to accept that the -= operator removes only one instance of the handler. This is by design of the language and can not be changed.
It is like adding the same string to a List<string> multiple times. If you want to remove all instances of that string you have to call the Remove method multiple times.
I would not recommend the above code if your Foo class will be used by others because it behaves different from any other class.
Delegates should be "wrapped" in events like instances fields are wrapped in properties. Then you can control them.
public class Test
{
public class Foo
{
private Action _event;
public event Action Event
{
add { _event += value; }
remove { _event -= value; }
}
public void DoEvent()
{
if (_event != null)
_event ();
}
public void ClearEvent()
{
_event = null;
}
}
static void Handler() {
Console.WriteLine("hi!");
}
static void Main()
{
var foo = new Foo();
foo.Event += Handler;
foo.Event += Handler;
foo.DoEvent();
Console.WriteLine("---");
foo.ClearEvent();
foo.DoEvent();
Console.Read();
}
}
Event = Delegate.RemoveAll(Event, handler);
Note that this is not thread-safe, and that it will only work within the class that declares the event.

Unsubscribe anonymous method in C#

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.

Categories