I know there are times you need to keep track of a delegate so that it can be unsubscribed properly:
private EventHandler _handler;
public void Foo()
{
if (_handler != null)
{
Something.SomeEvent -= _handler; // Unsubscribe old event
}
_handler = delegate(object sender, EventArgs args) { };;
Something.SomeEvent += _handler;
}
But, is that still necessary if you use a method instead?
public void CustomMethod(object sender, EventArgs args) { ... }
public void Foo()
{
// Not sure how to unsubscribe only if it wasn't subscribed first?
if (some way to check)
{
Something.SomeEvent -= CustomMethod;
}
Something.SomeEvent += CustomMethod;
}
No, it's not necessary. If you are always subscribing/unsubscribing the same method (in the form of a delegate), then you don't need to track the actual delegate instance that was subscribed. The new delegate instances (implicitly created for you by the C# compiler in the += and -= operations) are correctly identified as identical, so that the -= operation removes the delegate that was added in the += operation.
In other words, equality for the Delegate class is not just "reference equality". Two completely different Delegate instances that have the same invocation list are considered equal.
If you wanna check if a specific method subscribed or not you can use GetInvocationList and then Linq:
var mInfo = typeof(SomeType).GetMethod("CustomMethod");
if(Something.SomeEvent.GetInvocationList().Any(x => x.Method == mInfo))
{
}
Related
I've a library that defines ConfigurationChanged event. It used to use EventArgs, but I want to extend it to MyEventArgs, but don't want to bring BC break to customers. They shall still be able to have EventArgs signature to consume the event args.
It works fine as long as they assign their handling method directly. But If some of them pass EventHandler<EventArgs> around, then it won't assign with
Cannot implicitly convert type 'System.EventHandler<System.EventArgs>' to 'System.EventHandler<MyEventArgs>'.
Code snippet
public class Test
{
private static event EventHandler<MyEventArgs> ConfigurationChanged;
public Test(EventHandler<EventArgs> eventHandler)
{
ConfigurationChanged += eventHandler; // Cannot implicitly convert
ConfigurationChanged += OnConfigurationChanged; // Works
}
private static void OnConfigurationChanged(object sender, EventArgs e)
{
}
}
public class MyEventArgs : EventArgs
{
}
Would you have any workaround/best practice around that or there is no way to obey breaking change?
Changing the type of the event is a binary breaking change whatever you do. What you've described is trying to make it not a source breaking change.
What you can do is add the more specific event as a separate event, and proxy event subscription by creating a new EventHandler<MyEventArgs> from the handler that's passed to the add/remove parts. Fortunately, delegate equality still works in that situation, so unsubscribing does the right thing automatically:
using System;
public class MyEventArgs : EventArgs
{
}
public class Test
{
public event EventHandler<EventArgs> GeneralEvent
{
add => SpecificEvent += new EventHandler<MyEventArgs>(value);
remove => SpecificEvent -= new EventHandler<MyEventArgs>(value);
}
public event EventHandler<MyEventArgs> SpecificEvent;
private void OnEvent(MyEventArgs args)
{
SpecificEvent?.Invoke(this, args);
}
public static void Main()
{
var test = new Test();
EventHandler<EventArgs> generalHandler = (sender, args) => Console.WriteLine("General");
EventHandler<MyEventArgs> specificHandler = (sender, args) => Console.WriteLine("Specific");
test.GeneralEvent += generalHandler;
test.SpecificEvent += specificHandler;
Console.WriteLine("Raising event with both subscribed");
test.OnEvent(new MyEventArgs());
test.GeneralEvent -= generalHandler;
test.SpecificEvent -= specificHandler;
Console.WriteLine("Raising event with both unsubscribed");
test.OnEvent(new MyEventArgs());
}
}
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)
I am currently learning about delegates and events in csharp.I have the following set of codes:
using System;
using System.Collections.Generic;
using System.Text;
public delegate void mydel(object sender, EventArgs e);
class event1
{
public event mydel myevent;
public void onfive()
{
Console.WriteLine("I am onfive event");
Console.ReadKey();
if (myevent != null)
{
myevent(this,EventArgs.Empty);
}
}
}
public class test
{
public static void Main()
{
event1 e1 = new event1();
e1.myevent += new mydel(fun1);
Random ran = new Random();
for (int i = 0; i < 10; i++)
{
int rn = ran.Next(6);
Console.WriteLine(rn);
Console.ReadKey();
if (rn == 5)
{
e1.onfive();
}
}
}
public static void fun1(object sender, EventArgs e)
{
Console.WriteLine(" i am surplus function called due to use of '+=' ");
Console.ReadKey();
}
}
Whenever i put the following lines in comment the fun1() function is not called.Why it it so?
if (myevent != null)
{
myevent(this,EventArgs.Empty);
}
what is the purpose of these lines?
That bit of code is what raises the event. If the event is not raised then the event handler is not executed.
A delegate is an object that refers to a method and an event is sort of like a collection of delegates. If you don't add any handlers to an event then there is no collection, hence the check for null. If the event is not null then that means that handlers have been registered. The line inside the if statement raises the event, which means invoking each of the delegates in the collection. As each delegate is invoked, so the method it refers to is executed. The method that gets executed is your event handler.
EventArgs :
The EventArgs are arguments that the implementor of this event may find useful. With OnClick it contains nothing good, but in some events, like say in a GridView 'SelectedIndexChanged', it will contain the new index, or some other useful data.
EventArgs.Empty: Used to Pass the value to event handlers that are associated with events that do not have data.
Your event is of type mydel.
public mydel myevent; // declaring an event of type mydel with signature void mydel()
Hope this might give you some spark.
if (myevent != null) //Checks so you instantiated a event
{
myevent(this,EventArgs.Empty); // {this} will be equal to the sender in delegete myDel, {EventArgs.Empty} is since you are not passing any arguments to your delegate.
}
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.
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.