I was wiring up an event to use a lambda which needed to remove itself after triggering. I couldn't do it by inlining the lambda to the += event (no accessable variable to use to remove the event) so i set up an Action<object, EventArgs> variable and moved the lambda there. The main error was that it could not convert an Action<object, EventArgs> to an EventHandler. I thought lambda expressions were implicitly convertable to event handlers, why doesn't this work?
Action<Object, EventArgs> a = (o, ea) => { };
EventHandler e = a.Invoke;
Lambdas are implicitly convertible to delegate types with the right shape, but two same-shaped delegate types are not implicitly convertible to one another. Just make the local variable have type EventHandler instead.
EventHandler h = (o, ea) => { ... };
e += h;
...
e -= h;
(in case it helps:
Action<object, EventArgs> a = (o, ea) => { };
EventHandler e = a; // not allowed
EventHandler e2 = (o,ea) => a(o,ea); // ok
)
Declare your event as
public event Action<object, EventArgs> e;
Then you can directly add your action:
Action<object, EventArgs> a = something;
e += a;
In general, delegates can't be cast because they have no inheritance tree defining which casts are valid. To that end, you have two choices:
Use a variable of type EventHandler instead of the Action<T1, T2>
Use an inline declaration.
// option 1: local variable
EventHandler eh = (o, ea) => { /* [snip] */ };
obj.event += eh;
obj.event -= eh;
// option 2: inline declaration
obj.event += (o, ea) => { /* [snip] */ };
You can use an anonymous method instead:
Event += (sender, e) =>
{
// Multiple lines
// of code here
};
Related
This question already has an answer here:
How to remove a lambda event handler [duplicate]
(1 answer)
Closed 1 year ago.
Why C# allows to unsubscribe from the event when the event handler is defined as a function, but not when the event handler is defined as a delegate?
Consider the following code that works:
void SomeFunction()
{
var eventRaiser = ClassRaisingEvent.GetEventRaiser();
void handler(object sender, EventArgs ev)
{
ProcessData(ev);
eventRaiser.OnEvent -= handler;
}
eventRaiser.OnEvent += handler
eventRaiser.Process();
}
But this fails to compile at the indicated spot:
void SomeFunction()
{
var eventRaiser = ClassRaisingEvent.GetEventRaiser();
DelegateType handler = (object sender, EventArgs ev) =>
{
ProcessData(ev);
eventRaiser.OnEvent -= handler; // FAILS here with "Use of unassigned local variable 'handler '"
}
eventRaiser.OnEvent += handler
eventRaiser.Process();
}
EDIT: This question is not how to unsubscribe. This is why (in technical sense) function name is captured in the scope of the function, but the delegate's isn't.
EDIT2: Answer here (and esp. pt. 2 in the answer) explains the behavior I'm seeing.
The local function is defined at compile time, just like other functions. The handler variable, however, is only known at runtime. So, as the compiler tells you, you cannot use the handler variable before it's been assigned a value, which means after the closing } of your delegate.
What you could do to get your second snippet working, is to initialize the variable first:
void SomeFunction()
{
var eventRaiser = ClassRaisingEvent.GetEventRaiser();
DelegateType handler = null;
handler = (object sender, EventArgs ev) =>
{
ProcessData(ev);
eventRaiser.OnEvent -= handler;
};
eventRaiser.OnEvent += handler;
eventRaiser.Process();
}
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.
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.
How to use the this (or something of that kind) referring to the delegate instance instead of the class instance ?
instance.OnEventFoo += delegate()
{
if (condition)
{
instance.OnEventBar += this;
}
};
Since you can't refer to a variable before it is declared, you have to:
first declare the variable,
then assign a delegate,
then register the handler with the event.
// Add an anonymous delegate to the events list and auto-removes automatically if item disposed
DataRowChangeEventHandler handler = null;
handler = (sender, args) =>
{
if (condition)
{
// need to remove this delegate instance of the events list
RowChanged -= handler;
}
};
something.RowChanged += handler;
You need to store it in a variable somewhere. For example:
EventHandler rowChanged = null; // to avoid "uninitialized variable" error
rowChanged = (s, e) =>
{
if (condition)
{
// this will unsubscribe from the event as expected
RowChanged -= rowChanged;
}
};