I have a class that implements PropertyChanged. I do something similar to this to subscribe to it:
p.PropertyChanged += (s, a) => {
switch ( a.PropertyName) {
...
}
}
How can I later unsubscribe the above code from the p.PropertyChanged ?
Equivalent of (which clearly won't work):
p.PropertyChanged -= (s, a) => {
switch ( a.PropertyName) {
...
}
}
You must put it in a variable:
PropertyChangedEventHandler eventHandler = (s, a) => {
...
};
// ...
// subscribe
p.PropertyChanged += eventHandler;
// unsubscribe
p.PropertyChanged -= eventHandler;
From the docs:
It is important to notice that you cannot easily unsubscribe from an event if you used an anonymous function to subscribe to it. To unsubscribe in this scenario, it is necessary to go back to the code where you subscribe to the event, store the anonymous method in a delegate variable, and then add the delegate to the event. In general, we recommend that you do not use anonymous functions to subscribe to events if you will have to unsubscribe from the event at some later point in your code.
As an addition to #Sweeper's answer, you can accomplish the same using event handler method, without the burden of lambda expressions:
private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
...
}
}
Which you can then use to subscribe to the PropertyChanged event:
p.PropertyChanged += OnPropertyChanged;
And to unsubscribe:
p.PropertyChanged -= OnPropertyChanged;
Additional info from the docs:
To respond to an event, you define an event handler method in the event receiver. This method must match the signature of the delegate for the event you are handling. In the event handler, you perform the actions that are required when the event is raised, such as collecting user input after the user clicks a button. To receive notifications when the event occurs, your event handler method must subscribe to the event.
Related
In C# 5, what is the behavior of the -= operator when unsubscribing from events.
Assume subscribing to the same event multiple times is valid for this application logic, such as follows:
Property_Saved += Property_Saved_Handler;
Property_Saved += Property_Saved_Handler;
Property_Saved += Property_Saved_Handler;
Now we are subscribed three times.
After unsubscribing with the following one line of code:
Property_Saved -= Property_Saved_Handler;
How many subscriptions are left? 2? none? ...?
Two are left after that. Each -= only removes one subscription. At least, that's the case if it's using just a regular delegate to back the event.
You can see this easily without really involving events:
using System;
public class Program
{
public static void Main(string[] args)
{
Action action = () => Console.WriteLine("Foo");
// This is a stand-in for the event.
Action x = null;
x += action;
x += action;
x += action;
x -= action;
x(); // Prints Foo twice
}
}
Strictly speaking, an event subscription could do anything. You could implement an event like this:
private EventHandler weirdEvent;
public event EventHandler WeirdEvent
{
add { weirdEvent += value; } // Subscribe as normal
remove { weirdEvent = null; } // I'm bored with *all* the handlers
}
But normally events just delegate to Delegate.Combine and Delegate.Remove, which are the methods that += and -= are syntactic sugar for in C#.
My article on events and delegates contains more details about exactly what happens with combination and removal.
private void button1_Click(object sender, EventArgs e)
{
// set breakpoint
}
this.button1.Click += new System.EventHandler(this.button1_Click);
this.button1.Click += new System.EventHandler(this.button1_Click);
this.button1.Click += new System.EventHandler(this.button1_Click);
this.button1.Click -= new System.EventHandler(this.button1_Click);
Invoking the click event will show the breakpoint hit twice.
This should be also safe.
Property_Saved += Property_Saved_Handler;
Property_Saved -= Property_Saved_Handler;
Property_Saved -= Property_Saved_Handler;
Just make your own test using GetInvocationList
public delegate void MyEventHandler(string s);
public event MyEventHandler MyEvent;
MyEventHandler #event = s => { };
MyEvent += #event;
Console.WriteLine(MyEvent.GetInvocationList().Length);
MyEvent += #event;
Console.WriteLine(MyEvent.GetInvocationList().Length);
MyEvent -= #event;
Console.WriteLine(MyEvent.GetInvocationList().Length);
This will print
1
2
1
What is the purpose of having such a signature in Observable.FromEvent?
For example:
var appActivated = Observable.FromEvent(
h => Application.Current.Activated += h,
h => Application.Current.Activated -= h);
In particular, what is h? And why +=, then -=? Do we make Observable from event or from event handler? If from event, why not just have a signature like:
var appActivated = Observable.FromEvent(Application.Current.Activated);
That's because there's no way to pass in an event as a parameter to a method. You could pass in the event as a delegate but that doesn't give you the ability to subscribe/unsubscribe to the event. See this answer by Eric Lippert.
Observable.From basically says "Ok, I will give you an observable that is a wrapper around the event, but you need to provide me with two delegates: 1) a delegate for me to subscribe my handler to the event, and 2) a delegate for me to unsubscribe my handler when I need to".
So in this case h => Application.Current.Activated += h is a lambda expression that gets compiled into a delegate. h (handler) is the input parameter, and the delegate takes that input parameter and subcribes it to the Activated event. And the second delegate is the same thing, except it unsubscribes the handler.
Eren's answer is correct; I want to make sure that all your questions are answered:
In particular, what is h?
h is a parameter to the delegates which add and remove handlers. When invoked, h will be a reference to a handler delegate.
And why +=, then -=?
The observable requires the ability to both subscribe and unsubscribe handlers to the event.
Do we make Observable from event or from event handler?
From an event.
If from event, why not just have a signature like: var appActivated = Observable.FromEvent(Application.Current.Activated); ?
Because that would pass the handler, not the event. An "event" is three things: the ability to invoke a handler list, the ability to add a new handler to the list, and the ability to remove a handler from the list. The observable needs the last two; your proposal is to pass the first. So the observable takes delegates which do the last two.
Observables are first-class types in .NET - meaning that you can keep a reference to them and pass them around as parameters to any constructor/method you like.
Events are not first-class types. They can only be attached and detached from in the scope that you can reference their containing object in.
So this means I cannot do this:
public void SomeMethod(EventHandler handler)
{
handler += (s, e) => { /* Handler Code */ };
}
public void SomeOtherMethod()
{
SomeMethod(Application.Current.Activated);
}
If I try that I get the error:
The event 'Application.Activated' can only appear on the left hand side of += or -=
That should let you know why you can't do var appActivated = Observable.FromEvent(Application.Current.Activated);.
So, how can I work around this to attach events in SomeMethod?
Here's how:
public void SomeMethod(Action<EventHandler> addHandler)
{
addHandler((s, e) => { /* Handler Code */ });
}
public void SomeOtherMethod()
{
SomeMethod(h => Application.Current.Activated += h);
}
Basically, in the method SomeMethod the parameter is no longer EventHandler, but Action<EventHandler>. This means I am no longer trying to pass the event itself - instead I'm passing a way for the called code to attach itself to my event. The h in the call to SomeMethod is a promise that in the future if I were to have a valid handler then I can attach it by invoking the Action<EventHandler>.
So let's say that I now want to write some code that knows how to attach and detach from an event. I now need this code:
public void SomeMethod(Action<EventHandler> addHandler, Action<EventHandler> removeHandler)
{
EventHandler handler = (s, e) => { /* Handler Code */ };
addHandler(handler);
/* Some Intervening Code */
removeHandler(handler);
}
public void SomeOtherMethod()
{
SomeMethod(h => Application.Current.Activated += h, h => Application.Current.Activated -= h);
}
In the /* Some Intervening Code */ code the handler is attached, and after it is detached.
This brings us to your code in your question:
var appActivated = Observable.FromEvent(
h => Application.Current.Activated += h,
h => Application.Current.Activated -= h);
This is very much the same as the SomeMethod call above - FromEvent needs a way for it to attach and detach from the event. The h is a promise that says "hey, FromEvent, if you can provide a handler, when you need it in the future, I promise that this code will attach it correctly." Or, detach, as the case may be.
Now, just to be a bit pedantic, your code should actually be:
IObservable<EventPattern<EventArgs>> appActivated =
Observable
.FromEventPattern<EventHandler, EventArgs>(
h => Application.Current.Activated += h,
h => Application.Current.Activated -= h);
Now that I have a IObservable<EventPattern<EventArgs>> I can rewrite SomeMethod to take this as a parameter and write it like this:
public IDisposable SomeMethod(IObservable<EventPattern<EventArgs>> appActivated)
{
return appActivated.Subscribe(ep => { /* Handler Code */ });
}
Now all of the power of Rx can be seen. The .Subscribe method doesn't need any reference to the original event's containing object, but it will ultimately call h => Application.Current.Activated += h to attach and h => Application.Current.Activated -= h to detach as and when it needs. I can now effectively pass around events as first-class types in .NET.
I have some code in my project that saves an object to the database, once this is done I want it to call a series of other methods.
I was thinking about allowing other methods to subscribe to this event so I can add more as I need to. The idea I had for this was to create an array of Delegates allowing other methods to register, then when the object had been saved it could loop through the delegates and call each one.
Would this be bad practise or is there a better way of doing this?
Thanks
Event delegates are multicast, which means that they can hold references to more than one event handling method - see the MSDN documentation for Delegate and MulticastDelegate.
The syntax for subscribing to an event gives a clue:
MyEvent += MyHandler; // subscribe to an event
MyEvent -= MyHandler; // unsubscribe from an event
You can subscribe as many delegates as you want to a single event. Under the hood .Net keeps these as an ordered collection anyway.
The standard pattern for this is:
//in your class
public EventHandler<MyEvtArgs> MyEvent;
protected void OnMyEvent ( MyEvtArgs args ) {
if(MyEvent != null) //make sure there is at least 1 subscriber
MyEvent(this, args);
}
Then to call this:
var myInstance = new MyClass();
// all of these can convert to EventHandler<MyEvtArgs> (object, MyEvtArgs)
myInstance.MyEvent += (sender, e) => { /* do something 1 */ };
myInstance.MyEvent += (sender, e) => { /* do something 2 */ };
myInstance.MyEvent += localMethod;
Now when your protected OnMyEvent method is called inside your class all of these events will fire - in order.
You can also remove items from this collection:
myInstance.MyEvent -= localMethod;
You don't need an array. Just let anyone subscribe to one.
You can use a normal event for this. The runtime will handle looping over all fields.
public event EventHandler<EventArgs> WritingToDatabaseFinished;
protected void OnWritingToDatabaseFinished(EventArgs args)
{
EventHandler<EvetnArgs> handler = WritingToDatabaseFinished;
if (handler != null)
{
handler(this, args);
}
}
Your code calls
OnWritingToDatabaseFinished(args);
All methods that want to be informed have to register to the event:
WritingToDatabaseFinished += new EventHandler<EventArgs>(handlermethod);
Every handler that has been registered in the above way will be called when you call OnWritingToDatabaseFinished.
All you need is a multicast delegate. You don't need an array at all. You use += to add a reference to the delegate, and a -= to remove the reference.
I would recommend that you look at using a weak event handler instead. Take a look at this link to see why you'd use a weak event instead of a weak delegate.
Delegates are already multicast, so no need for a delegate array. Now, the recommended way of adding event support to your class is:
Add a public delegate (this will be your event handler)
public delegate void DatabaseEventHandler(object sender, DatabaseEventArgs e);
Add a public event, of your delegate type.
public event DatabaseEventHandler DatabaseDone;
Now, the event should send 2 parameters. The sender (this, usually), and the EventArgs. You should create a new class from System.EventArgs, and send the appropriate information within that class.
I have the following code:
public List<IWFResourceInstance> FindStepsByType(IWFResource res)
{
List<IWFResourceInstance> retval = new List<IWFResourceInstance>();
this.FoundStep += delegate(object sender, WalkerStepEventArgs e)
{
if (e.Step.ResourceType == res) retval.Add(e.Step);
};
this.Start();
return retval;
}
Notice how I register my event member (FoundStep) to local in-place anonymous function.
My question is: when the function 'FindStepByType' will end - will the anonymous function be removed automatically from the delegate list of the event or I have to manually remove it before steping out the function? (and how do I do that?)
I hope my question was clear.
Your code has a few problems (some you and others have identified):
The anonymous delegate cannot be removed from the event as coded.
The anonymous delegate will live longer than the life of the method calling it because you've added it to FoundStep which is a member of this.
Every entry into FindStepsByType adds another anonymous delegate to FoundStep.
The anonymous delegate is a closure and effectively extends the lifetime of retval, so even if you stop referencing retval elsewhere in your code, it's still held by the anonymous delegate.
To fix this, and still use an anonymous delegate, assign it to a local variable, and then remove the handler inside a finally block (necessary in case the handler throws an exception):
public List<IWFResourceInstance> FindStepsByType(IWFResource res)
{
List<IWFResourceInstance> retval = new List<IWFResourceInstance>();
EventHandler<WalkerStepEventArgs> handler = (sender, e) =>
{
if (e.Step.ResourceType == res) retval.Add(e.Step);
};
this.FoundStep += handler;
try
{
this.Start();
}
finally
{
this.FoundStep -= handler;
}
return retval;
}
With C# 7.0+ you can replace the anonymous delegate with a local function, achieving the same effect:
public List<IWFResourceInstance> FindStepsByType(IWFResource res)
{
var retval = new List<IWFResourceInstance>();
void Handler(object sender, WalkerStepEventArgs e)
{
if (e.Step.ResourceType == res) retval.Add(e.Step);
}
FoundStep += Handler;
try
{
this.Start();
}
finally
{
FoundStep -= Handler;
}
return retval;
}
Below is approach about how unsubscribe event in anonymous method:
DispatcherTimer _timer = new DispatcherTimer();
_timer.Interval = TimeSpan.FromMilliseconds(1000);
EventHandler handler = null;
int i = 0;
_timer.Tick += handler = new EventHandler(delegate(object s, EventArgs ev)
{
i++;
if(i==10)
_timer.Tick -= handler;
});
_timer.Start();
No, it will not be removed automatically. In this sense, there's not a difference between an anonymous method and a "normal" method. If you want, you should manually unsubscribe from the event.
Actually, it'll capture other variables (e.g. res in your example) and keep them alive (prevents garbage collector from collecting them) too.
When using an anonymous delegate (or a lambda expression) to subscribe to an event does not allow you to easily unsubscribe from that event later. An event handler is never automatically unsubscribed.
If you look at your code, even though you declare and subscribe to the event in a function, the event you are subscribing to is on the class, so once subscribed it will always be subscribed even after the function exits. The other important thing to realize is that each time this function is called, it will subscribe to the event again. This is perfectly legal since events are essentially multicast delegates and allow multiple subscribers. (This may or may not be what you intend.)
In order to unsubscribe from the delegate before you exit the function, you would need to store the anonymous delegate in a delegate variable and add the delegate to the event. You should then be able to remove the delegate from the event before the function exits.
For these reasons, if you will have to unsubscribe from the event at some later point it is not recommended to use anonymous delegates. See How to: Subscribe to and Unsubscribe from Events (C# Programming Guide) (specifically the section titled "To subscribe to events by using an anonymous method").
C# 2.0 has a neat feature called anonymous functions. This is intended to be used mostly with events:
Button.Click += delegate(System.Object o, System.EventArgs e)
{ System.Windows.Forms.MessageBox.Show("Click!"); };
Now, suppose that Button is a static member, then adding delegates to it would count as unmanaged resources. Normally, I would have to deregister the handler before regestring it again. This is a pretty common use case for GUI programming.
What are the guidelines with anonymous functions? Does the framework deregrister it automatically? If so, when?
No, anonymous functions will not get deregistered automatically. You should make sure to do it yourself, if the event should not be hooked up for the whole lifetime of your application.
To do this, of course, you would have to store the delegate reference, to be able to de-register it.
Something like:
EventHandler handler = delegate(System.Object o, System.EventArgs e)
{ System.Windows.Forms.MessageBox.Show("Click!"); };
Button.Click += handler;
// ... program code
Button.Click -= handler;
Also, see this question.
If I recall correctly (and I can recall where I read this) inline anonymous delegates cannot be removed.
You would need to assign to a (static) delegate field.
private static EventHandler<EventArgs> myHandler = (a,b) => { ... }
myButton.Click += myhandler;
...
myButton.Click -= myHandler;