Subscribing to events using anonymous methods - c#

I am having problems understanding how the below works (questions below code):
private delegate void MyDelegate(int i);
private event MyDelegate myEvent;
public void EventTests() {
//Option One
myEvent += One;
//Option Two
myEvent += delegate{ Two(true); };
//Option Three
myEvent += () => { Two(true); };
}
private void One(int i) { }
private void Two(bool j) { }
Questions:
I can understand why Option One works, as the event myEvent expects a delegate with an int parameter to be attached to it. Why does Option Two work though? it is attaching an anonymous method with incorrect signature to the event, no?
So if Option Two works, why does option Three not work? It seems that the signature needs to be (int i) => { Two(true); }; as opposed to () as written above. But Option Two worked without the right signature, so why does this anonymous method cause an error?
Thanks a lot.

Option two works because the compiler automatically figures out what the function signature is when the parameter list is omitted. Once you add the () to the delegate keyword, you've defined a specific parameter list of none and the compiler throws a fit.
Option three is a lambda expression with an incorrect parameter list defined.
Microsoft's C# Programming Guide states:
There is one case in which an anonymous method provides functionality not found in lambda expressions. Anonymous methods enable you to omit the parameter list. This means that an anonymous method can be converted to delegates with a variety of signatures. This is not possible with lambda expressions.

Related

Need help understanding an event being assigned "delegate { }"

While looking at an UWP example app, I came across this code. What I don't understand and can't seem to find in google is line 40:
public event PropertyChangedEventHandler PropertyChanged = delegate { };
Says in the comments that it is a multicast event. What does assigning delegate { } do? Does it have anything to do with the comment saying the event is multicast?
What does assigning delegate { } do?
That is simply adding an anonymous function to the event, with an empty body. Since C#3, one would generally use a lambda expression instead:
public event PropertyChangedEventHandler PropertyChanged = (s, e) => { };
However, lambda expressions require each parameter to be elicited, whereas using delegate does not. So if you don't need to use the arguments, then delegate may be more concise.
Microsoft highlight this as the single remaining use case of the delegate syntax for defining an anonymous function:
When you use the delegate operator, you might omit the parameter list. If you do that, the created anonymous method can be converted to a delegate type with any list of parameters. That's the only functionality of anonymous methods that is not supported by lambda expressions. In all other cases, a lambda expression is a preferred way to write inline code.
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/delegate-operator
Does it have anything to do with the comment saying the event is multicast?
No; multicast means that you can add more than one handling function to the event, and when the event is raised, each one is invoked.
For example:
class Example
{
public event PropertyChangedEventHandler PropertyChanged;
public void RaiseEvent()
{
PropertyChanged?.Invoke(default, default);
}
}
var example = new Example();
// Use the += operator to add a new handler (rather than = which overwrites)
example += delegate { Console.Writeline("Handler 1"); };
example += delegate { Console.Writeline("Handler 2"); };
example.RaiseEvent();
// Output:
// Handler 1
// Handler 2
Follow up question: What is the purpose of assigning an anonymous function to the event?
If no handler has been added, invoking the event can cause a NullReferenceException, so assigning an empty handler could remove the necessity to handle null.
So the event can be raised like this:
PropertyChanged.Invoke(default, default);
Or this:
PropertyChanged(default, default);
Rather than this:
PropertyChanged?.Invoke(default, default);
It's just an empty delegate, but the compiler will derive delegates from MulticastDelegate. So all delegates in C# are multicast.
Action d = delegate { };
// True
Console.WriteLine(d.GetType().BaseType == typeof(MulticastDelegate));

Passing Actions into generic functions

I'm trying to wrap my head around different concepts in Csharp by trying different things. A create a generic function that takes in an action. The action has one input parameter and returns void. I create a simple action that is linked to a lambda function (returns void has one parameter x). I am able to run the action but when I pass the function to my generic function I am not sure how to add the input parameter. act("Some Int") doesn't work.
How do I pass in a value to an action?
public MainWindow()
{
InitializeComponent();
Action<int> myAction = (x) => Console.WriteLine(x);
myAction(13);
test(myAction);
}
private static void test<T>(Action<T> act)
{
act(); // How do i pass in an int Here?
}
Simply calling act("Some Int") as you have just required the Action act to be a genric function. Therefore you cannot specifically invoke it with one fixed variable type. You can solve your problem by modifying the test-method
private static void test<T>(Action<T> act, T value)
{
act(value); // How do i pass in an int Here?
}
...
test(myAction,integerValue);
Now you can call the Action with a given intvalue.
I can see what you are trying to do, and just wanted to throw this pattern up, since we often do this when we have to use closures and the parameters could be wildly different.
In those cases, rather than define an Action<T> which kind of ties you down from being able to use closures, you would just simply define your method as Action. So test would look like this:
private static void test(Action act)
{
act(); // yup, that's all there is to it!
}
So how would you pass in the parameter(s)? Simple: use closures. Like this:
public MainWindow()
{
InitializeComponent();
var x = 13; // this defined outside now...
Action myAction = () => Console.WriteLine(x); // you're basically using the closure here.
myAction();
test(myAction);
}
We often use this sort of approach when we're context switching (aka thread jumping), and need the thread continuation to pick up one or more variable values at the point it executes. That's just one example, there's quite a few other valid use cases as well.
Your experimental example, if I'm reading it correctly, could also qualify as a situation where closures could be a good fit.

Using anonymous event in function

public static void OnAutoScrollToEndChanged(DependencyObject s, DependencyPropertyChangedEventArgs e)
{
/* ... */
var scrollToEndHandler = new NotifyCollectionChangedEventHandler((sender, args) => // 수정
{
if (listBox.Items.Count > 0)
{
object lastItem = listBox.Items[listBox.Items.Count - 1];
listBoxItems.MoveCurrentTo(lastItem);
listBox.ScrollIntoView(lastItem);
}
});
if (isAutoScroll)
{
source.CollectionChanged += scrollToEndHandler; // A
}
else
{
source.CollectionChanged -= scrollToEndHandler; //B
}
}
https://michlg.wordpress.com/2010/01/17/listbox-automatically-scroll-to-bottom/
This code is referenced by upper URL.
A(scrollToEndHandler) and B(scrollToEndHandler) are in a function.
When 'AutoScrollToEndProperty' is changed, 'OnAutoScrollToEndChanged' will be called all the time.
I wondering whether These are same reference. Thanks.
If your question is basically, "Does unsubscription actually work here?" the answer is C# compiler implementation-specific, theoretically.
On a practical basis, the body of the lambda expression doesn't capture any local variables, but does capture this (by referring to listBox)... so I'd expect the compiler to generate an instance method containing the body of the code in the lambda expression.
If the method is called multiple times on the same target (i.e. this refers to the same object), I'd expect scrollToEndHandler to be a distinct but equal delegate each time - in other words, it would create a new delegate object in each call (which it probably wouldn't if the lambda expression didn't capture anything, and could be implemented as a static method and the delegate cached)... but the event subscription/unsubscription will still work because the delegates are equal (referring to the same target for the same method).
If the lambda expression referred to any local variables in the method, then event handling wouldn't work, because the compiler would capture those local variables via a separate nested class containing the relevant variables and a method for the delegate code, and each method invocation of OnAutoScrollToEndChanged would create a new instance of that nested class, leading to unequal delegates.
As I say though, that's all implementation-specific... it would be better to just move that code into a separate instance method, to make it clearer that it would work.

Multicast delegate weird behavior in C#?

I have this simple event :
public class ClassA
{
public event Func<string, int> Ev;
public int Do(string l)
{
return Ev(l);
}
}
And 2 Methods :
static int Display(string k)
{
return k.Length;
}
static int Display_2(string k)
{
return k.Length*10;
}
Im registering this event :
ClassA a = new ClassA();
a.Ev += Display;
a.Ev += Display_2;
Now , I'm executing :
Console.WriteLine(a.Do("aaa"));
the output :
What ???
he has in invocation list 2 methods ! it did run them , but why does it shows only the result from the last registration ?
Where does the result of "3" has gone ? ( the first invocation ) ? ( although both display+display_2 was executed... I didn't expect console.write to iterate through results . but also didn't expect him to decide which to show.)
edit :
There are three aspects at play here:
The implementation of the event
The behaviour of delegate combination
The behaviour of invoking a delegate whose invocation list has multiple entries
For point 1, you have a field-like event. Section 10.8.1 of the C# 4 spec gives an example, and states that:
Outside the declaration of the Button class, the Click member can be used only on the left-hand saide of the += and -= operators, as in
b.Click += new EventHandler(...);
which appends a delegate to the invocation list of the Click event
(emphasis mine). The spec also makes it clear that a field-like event creates a delegate field, which is used from within the class for invocation.
More generally (point 2), section 7.8.4 of the C# 4 spec talks about delegate combination via + and +=:
Delegate combination. Every delegate type implicitly provides the following predefined operator, where D is the delegate type:
D operator +(D x, D y)
The binary + operato performs delegate combination when both operands are of some delegate type D. [... skip bits where x or y are null ...] Otherwise, the result of the operation is a new delegate that, when invoked, invokes the first operand and then invokes the second operand.
(Again, emphasis mine.)
Finally, point 3 - event invocation and return values. Section 15.4 of the C# spec states:
If the delegate invocation includes output parameters or a return value, their final value will come from the invocation of the last delegate in the list.
More generally, it depends on the event implementation. If you use an event implementation which uses the "normal" delegate combination/removal steps, everything is guaranteed. If you start writing a custom implementation which does crazy things, that's a different matter.
As a general rule, it doesn't make sense for events to return a value.
If you want to get information from an event handler it makes more sense for the event handlers to mutate an input parameter, or just call another method of whatever object fired the event to communicate something else.
Normally this doesn't even come up because events logically are passing information to the event handlers, and don't have any need to get information from the event handlers. It's honestly a sign of code smell. An event shouldn't care if anyone has subscribed to it, who they might be, what they might be doing, or even if there are any subscribers. Relying on a return value from them then just creates overly tight coupling.
Invoking a multicast non-void delegate returns the value of the last handler that was executed.
You have very little control over who is first or last. It is a lousy system to use.
That is why most events and delegates return void.
The events are just iterated in the order they were attached. Because you are using a return value, the value you get is the last invoked.
This is not really a normal pattern for events though. You probably want something a bit more like:
public class MyEventArgs : EventArgs
{
public MyEventArgs()
{
Results = new List<int>();
}
public string InputString{get;set;}
public List<int> Results{get;set;}
}
public event EventHandler<MyEventArgs> Ev
public int Do(string l)
{
MyEventArgs e = new MyEventArgs();
e.InputString = l;
if(Ev != null) Ev(this, e);
return e.Results.Sum();
}
and then
static int Display(object sender, MyEventArgs e)
{
return e.Results.Add(k.Length);
}
static int Display_2(object sender, MyEventArgs e)
{
return e.Results.Add(k.Length*10);
}

Can a method be attached to a delegate with predefined parameters?

Sometimes I encounter cases where I have to attach a method to a delegate but the signature doesn't match, like trying to attach abc down there to somedelegate with the string parameter being "hi".
public class test
{
//...
public void abc(int i, string x)
{
//Do Something
}
//...
}
public class test2
{
somedelegate x;
//...
public test2()
{
//Do Something
test y = new test();
x += y.abc(,"hi");
}
delegate void somedelegate(int i);
}
I can work it around by creating another delegate with the correct signature then attaching it but it seems so unnecessarily complex. Can you do something like this in C#? Thanks.
EDIT: I guess there closest to what I wanted to achieve is:
x += (int i) => abc(i, "hi");
Yes, you can do this with closures
[there's a nice treatment of the subject on msdn, but like anything else in there it's hard to find]
The Big Picture
Write a method that can take all the parameters you need
Inside that method you return an anonymous method with the delegate-target signature it requires
This method's call is itself the parameter in the delegate instantiation
Yes, this is a bit Matrix-y. But way cool.
delegate void somedelegate (int i);
protected somedelegate DelegateSignatureAdapter ( string b, bool yesOrNo, ...) {
// the parameters are local to this method, so we'll go w/ that.
// our target delegate requires a single int parameter and void return
return (int a) => {
// your custom code here
// all calling arguements are in scope - use them as needed
}; // don't forget the semicolon!
}
// our delegate call
somedelegate myWarpedDelegate = new somedelegate (DelegateSignatureAdapter("someString", true));
myWarpedDelegate (2543);
myWarpedDelegate(15);
Just Googling for '.net delegate optional parameters' returns some results that may be useful:
Can a Delegate have an optional parameter?
VB.NET - Is there a way to utilize optional parameters in delegates? (Or Plans to Allow this?)
Optional Parameters and Delegates
Update (researching this some more, and helped by the first link above):
Could you perhaps use the Invoke method, which accepts any delegate?

Categories