Event and delegate contravariance in .NET 4.0 and C# 4.0 - c#

While investigating this question I got curious about how the new covariance/contravariance features in C# 4.0 will affect it.
In Beta 1, C# seems to disagree with the CLR. Back in C# 3.0, if you had:
public event EventHandler<ClickEventArgs> Click;
... and then elsewhere you had:
button.Click += new EventHandler<EventArgs>(button_Click);
... the compiler would barf because they're incompatible delegate types. But in C# 4.0, it compiles fine, because in CLR 4.0 the type parameter is now marked as in, so it is contravariant, and so the compiler assumes the multicast delegate += will work.
Here's my test:
public class ClickEventArgs : EventArgs { }
public class Button
{
public event EventHandler<ClickEventArgs> Click;
public void MouseDown()
{
Click(this, new ClickEventArgs());
}
}
class Program
{
static void Main(string[] args)
{
Button button = new Button();
button.Click += new EventHandler<ClickEventArgs>(button_Click);
button.Click += new EventHandler<EventArgs>(button_Click);
button.MouseDown();
}
static void button_Click(object s, EventArgs e)
{
Console.WriteLine("Button was clicked");
}
}
But although it compiles, it doesn't work at runtime (ArgumentException: Delegates must be of the same type).
It's okay if you only add either one of the two delegate types. But the combination of two different types in a multicast causes the exception when the second one is added.
I guess this is a bug in the CLR in beta 1 (the compiler's behaviour looks hopefully right).
Update for Release Candidate:
The above code no longer compiles. It must be that the contravariance of TEventArgs in the EventHandler<TEventArgs> delegate type has been rolled back, so now that delegate has the same definition as in .NET 3.5.
That is, the beta I looked at must have had:
public delegate void EventHandler<in TEventArgs>(object sender, TEventArgs e);
Now it's back to:
public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);
But the Action<T> delegate parameter T is still contravariant:
public delegate void Action<in T>(T obj);
The same goes for Func<T>'s T being covariant.
This compromise makes a lot of sense, as long as we assume that the primary use of multicast delegates is in the context of events. I've personally found that I never use multicast delegates except as events.
So I guess C# coding standards can now adopt a new rule: don't form multicast delegates from multiple delegate types related through covariance/contravariance. And if you don't know what that means, just avoid using Action for events to be on the safe side.
Of course, that conclusion has implications for the original question that this one grew from...

Very interesting. You don't need to use events to see this happening, and indeed I find it simpler to use simple delegates.
Consider Func<string> and Func<object>. In C# 4.0 you can implicitly convert a Func<string> to Func<object> because you can always use a string reference as an object reference. However, things go wrong when you try to combine them. Here's a short but complete program demonstrating the problem in two different ways:
using System;
class Program
{
static void Main(string[] args)
{
Func<string> stringFactory = () => "hello";
Func<object> objectFactory = () => new object();
Func<object> multi1 = stringFactory;
multi1 += objectFactory;
Func<object> multi2 = objectFactory;
multi2 += stringFactory;
}
}
This compiles fine, but both of the Combine calls (hidden by the += syntactic sugar) throw exceptions. (Comment out the first one to see the second one.)
This is definitely a problem, although I'm not exactly sure what the solution should be. It's possible that at execution time the delegate code will need to work out the most appropriate type to use based on the delegate types involved. That's a bit nasty. It would be quite nice to have a generic Delegate.Combine call, but you couldn't really express the relevant types in a meaningful way.
One thing that's worth noting is that the covariant conversion is a reference conversion - in the above, multi1 and stringFactory refer to the same object: it's not the same as writing
Func<object> multi1 = new Func<object>(stringFactory);
(At that point, the following line will execute with no exception.) At execution time, the BCL really does have to deal with a Func<string> and a Func<object> being combined; it has no other information to go on.
It's nasty, and I seriously hope it gets fixed in some way. I'll alert Mads and Eric to this question so we can get some more informed commentary.

I just had to fix this in my application. I did the following:
// variant delegate with variant event args
MyEventHandler<<in T>(object sender, IMyEventArgs<T> a)
// class implementing variant interface
class FiresEvents<T> : IFiresEvents<T>
{
// list instead of event
private readonly List<MyEventHandler<T>> happened = new List<MyEventHandler<T>>();
// custom event implementation
public event MyEventHandler<T> Happened
{
add
{
happened.Add(value);
}
remove
{
happened.Remove(value);
}
}
public void Foo()
{
happened.ForEach(x => x.Invoke(this, new MyEventArgs<T>(t));
}
}
I don't know if there are relevant differences to regular multi-cast events. As far as I used it, it works ...
By the way: I never liked the events in C#. I don't understand why there is a language feature, when it doesn't provide any advantages.

Are you getting the ArgumentException from both? If the exception is being thrown by just the new handler, then I would think that it's backward-compatible.
BTW, I think you have your comments mixed up. In C# 3.0 this:
button.Click += new EventHandler<EventArgs>(button_Click); // old
wouldn't have run. That's c#4.0

The following code actually works if you have the delegates normalized to the same type from within upon attaching/detaching! I didn't expect detaching to work but it does. It may not be the most optimized solution but it is clean and simple and it solves a serious problem I've had with my Foundation where co-variance is important for example IObservableList : IObservableListOut. At some point extensions created different delegate types depending on context T where T can be 2 different interfaces per context and therefore the delegates created ended up of different type. It wasn't until today that I actually understood how this issue resulted. Detaching won't work with plain "- value".
private event MyEventHandler<T> happened;
public event MyEventHandler<T> Happened
{
add => this.happened += new MyEventHandler<T>(value);
remove => this.happened -= new MyEventHandler<T>(value);
}

Related

Is it bad to use the += operator on delegates?

I just realized I don't know any C#.
Is there any difference between using += and Delegate.Combine on delegate instances?
For e.g.
public delegate void Printer(string s);
public class Program
{
public static void Main(string[] args)
{
Printer printer = new Printer((s) => { Console.WriteLine(s); });
// is it wrong to use this += operator
// on the delegate instance printer?
printer += s => {};
// And is this somewhat holier than the previous one?
// why?
Delegate.Combine(printer, new Printer((s) => {});
}
}
I ask this question because it seemed to be implied by serge karalenka's answer on this thread: https://stackoverflow.com/a/15183049/303685
Is it bad? No, not at all. In fact, I prefer it.
One good reason to use it instead of Combine() is that one is less likely to make the mistake you made in your example: failing to actually assign the result to something. I.e. note that the Combine() method returns the new delegate instance. Like string, delegates are immutable and all modifications involve creating a new instance.
While it is possible to make the same mistake with the + operator, most people would not write the code as printer + (s => {}) and even if they did, it would be pretty obvious in that case that the result of the expression was not assigned to anything. The more idiomatic += is of course immune to the problem completely. :)
Using += is perfectly fine. The language wouldn't have a bunch of special rules to make it work properly on delegates, if it was a bad thing.
It also has the advantage of working, while the second one doesn't.

multiple options for event handler [duplicate]

This question already has answers here:
Is there a benefit to explicit use of "new EventHandler" declaration?
(4 answers)
Closed 9 years ago.
I am using c# Win Forms with Resharper and it gives me three options for event handler:
eg:
1.button.Click+= (sender, args) => ;
2.button.Click+= delegate(object sender, EventArgs args) { };
3.button3.Click+= Button3OnClick;
private void Button3OnClick(object sender, EventArgs eventArgs)
{
}
So my questions are:
What are the differences between using one over the others?
Is there a preferred way I should stick with?
Thanks
As everyone will tell you, they are equivalent.
One important thing to note though
from How to: Subscribe to and Unsubscribe from Events (C# Programming Guide)
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.
3) button3.Click += Button3OnClick;
Here you attach delegate which referes to named method Button3OnClick.
2) button.Click += delegate(object sender, EventArgs args) { };
Here you declare anonymous method in .NET 1.1 style. And attach delegate which referes to this anonymous method. Compiler will generate ordinal named method for this anonymous method, so acutally that is same as
button.Click += CompilerGeneratedName;
Where
private void CompilerGeneratedName(object sender, EventArgs args)
{
}
On .NET version > 3 you unlikely will use this syntax, because it was simplified with lambdas.
1) button.Click += (sender, args) => ;
That is exactly same as previous case, but written in lambda syntax. Compiler will generate named method for this anonymous method.
All these options do same - they create and attach delegate which referers to method with required syntax. The only difference is the name of method - method gets name either before compilation, or during compilation.
Is there a preferred way I should stick with?
Usually lambdas (i.e. anonymous methods) are used for very small methods, which just don't worth creating named method manually. But event handlers often not that simple. So, I personally don't like having part of handlers named, and part of attached in-place, because I like to keep code in consistent way. I also like to see usages of event handler, have ability to unsubscribe handler from event, and handler name in stack. So I stick with named handlers. But you always should use same guidlines as your team does.
I would suggest reading up on events and delegates : Delegates and Events
But what it boils down to is that option 3 was always the defacto standard for creating events in .net 2. Since .net 3 delegates got introduced and made it easier to implement with the short hand, option 1 and option 2.
a delegate as used above specifies a place holder containing a signature and return type, the code inside the delegate describes the interactions.
If your learning about events , use option 3 to get your head around it. If you think you have the hang of delegates use option 2. And when it starts boring you to type out everything use option 1
:)

'Delegate 'System.Action' does not take 0 arguments.' Is this a C# compiler bug (lambdas + two projects)?

Consider the code below. Looks like perfectly valid C# code right?
//Project B
using System;
public delegate void ActionSurrogate(Action addEvent);
//public delegate void ActionSurrogate2();
// Using ActionSurrogate2 instead of System.Action results in the same error
// Using a dummy parameter (Action<double, int>) results in the same error
// Project A
public static class Class1 {
public static void ThisWontCompile() {
ActionSurrogate b = (a) =>
{
a(); // Error given here
};
}
}
I get a compiler error 'Delegate 'Action' does not take 0 arguments.' at the indicated position using the (Microsoft) C# 4.0 compiler. Note that you have to declare ActionSurrogate in a different project for this error to manifest.
It gets more interesting:
// Project A, File 1
public static class Class1 {
public static void ThisWontCompile() {
ActionSurrogate b = (a) => { a(); /* Error given here */ };
ActionSurrogate c = (a) => { a(); /* Error given here too */ };
Action d = () => { };
ActionSurrogate c = (a) => { a(); /* No error is given here */ };
}
}
Did I stumble upon a C# compiler bug here?
Note that this is a pretty annoying bug for someone who likes using lambdas a lot and is trying to create a data structures library for future use... (me)
EDIT: removed erronous case.
I copied and stripped my original project down to the minimum to make this happen. This is literally all the code in my new project.
FINAL UPDATE:
The bug has been fixed in C# 5. Apologies again for the inconvenience, and thanks for the report.
Original analysis:
I can reproduce the problem with the command-line compiler. It certainly looks like a bug. It's probably my fault; sorry about that. (I wrote all of the lambda-to-delegate conversion checking code.)
I'm in a coffee shop right now and I don't have access to the compiler sources from here. I'll try to find some time to reproduce this in the debug build tomorrow and see if I can work out what's going on. If I don't find the time, I'll be out of the office until after Christmas.
Your observation that introducing a variable of type Action causes the problem to disappear is extremely interesting. The compiler maintains many caches for both performance reasons and for analysis required by the language specification. Lambdas and local variables in particular have lots of complex caching logic. I'd be willing to bet as much as a dollar that some cache is being initialized or filled in wrong here, and that the use of the local variable fills in the right value in the cache.
Thanks for the report!
UPDATE: I am now on the bus and it just came to me; I think I know exactly what is wrong. The compiler is lazy, particularly when dealing with types that came from metadata. The reason is that there could be hundreds of thousands of types in the referenced assemblies and there is no need to load information about all of them. You're going to use far less than 1% of them probably, so let's not waste a lot of time and memory loading stuff you're never going to use. In fact the laziness goes deeper than that; a type passes through several "stages" before it can be used. First its name is known, then its base type, then whether its base type hierarchy is well-founded (acyclic, etc), then its type parameter constraints, then its members, then whether the members are well-founded (that overrides override something of the same signature, and so on.) I'll bet that the conversion logic is failing to call the method that says "make sure the types of all the delegate parameters have their members known", before it checks the signature of the delegate invoke for compatibility. But the code that makes a local variable probably does do that. I think that during the conversion checking, the Action type might not even have an invoke method as far as the compiler is concerned.
We'll find out shortly.
UPDATE: My psychic powers are strong this morning. When overload resolution attempts to determine if there is an "Invoke" method of the delegate type that takes zero arguments, it finds zero Invoke methods to choose from. We should be ensuring that the delegate type metadata is fully loaded before we do overload resolution. How strange that this has gone unnoticed this long; it repros in C# 3.0. Of course it does not repro in C# 2.0 simply because there were no lambdas; anonymous methods in C# 2.0 require you to state the type explicitly, which creates a local, which we know loads the metadata. But I would imagine that the root cause of the bug - that overload resolution does not force loading metadata for the invoke - goes back to C# 1.0.
Anyway, fascinating bug, thanks for the report. Obviously you've got a workaround. I'll have QA track it from here and we'll try to get it fixed for C# 5. (We have missed the window for Service Pack 1, which is already in beta.)
This probably is a problem with type inference, apperently the compiler infers a as an Action<T> instead of Action (it might think a is ActionSurrogate, which would fit the Action<Action>> signature). Try specifying the type of a explicitly:
ActionSurrogate b = (Action a) =>
{
a();
};
If this is not the case - might check around your project for any self defined Action delegates taking one parameter.
public static void ThisWontCompile()
{
ActionSurrogate b = (Action a) =>
{
a();
};
}
This will compile. Some glitch with the compiler its unable to find the Action delegate without parameters. That's why you are getting the error.
public delegate void Action();
public delegate void Action<T>();
public delegate void Action<T1,T2>();
public delegate void Action<T1,T2,T3>();
public delegate void Action<T1,T2,T3,T4>();

C# anonymus delegates efficiency/safety

I have progress form which delegate:
// in ProgressForm thread
public delegate void executeMethod(object parameters);
private executeMethod method;
public object parameters;
// ...
private void ProgressForm_Shown(object sender, EventArgs e)
{
method.Invoke(parameters);
}
Which way is better (mor efficient or safe) to apply - anonymus delegates call like this:
// in other thread
ProgressForm progress = new ProgressForm();
progress.ExecuteMethod = delegate
{
// to do
}
or using separate method like this:
// in other thread
private void myMethod(object par)
{
// to do
}
progress.ExecuteMethod = this.myMethod;
Ultimately they are surprisingly similar; simply, the compiler creates the hidden method internally for the anonymous case. There is no specific performance difference, although there are cases when it might choose to create a static method (if it doesn't use this or any captured variables) - that may help marginally (but not enough to get excited about)
I would use an anonymous method when:
it is short and is logically related to the code that inits the anonymous method
it perhaps uses additional context (captured variables) that are tricky to pass into myMethod
The main thing to watch is the notorious foreach (typically) captured variable gotcha, but in some cases it is also possible to bleed a reference into the anonymous method, extending the life-time of a big object unexpectedly.
For longer work, I would use the myMethod apprach.
The performance is identical. An anonymous delegate is converted by the compiler into a regular method like any other. If you ILDSM the code you'll see that a real (but hidden) name is generated for the anonymous method.

What is Difference in assign an event...the correct method?

Can you tell me what the difference is between these methods of attaching an event handler?
//Method 1
this.button4.Click += new RoutedEventHandler(button4_Click);
//Method 2
this.button4.Click += button4_Click;
...
void button4_Click(object sender, RoutedEventArgs e) { }
As Anton says, there's no difference.
Just as a bit more background, this isn't specific to events. That's just one use of the feature in C# 2.0, which is implicit conversion of method groups to delegates. So you can similarly use it like this:
EventHandler handler = button4_click;
Another change to delegates in C# 2.0 is that they're now variant - this means that (for example) you can use a method declared with the EventHander signature as a MouseEventHandler:
MouseEventHandler handler = button4_click;
Then of course there's anonymous methods, but that's a whole different ballgame :)
No difference whatsoever - the second is just syntactic shugar added in C# 2.0. And in C# 3.0 lambdas are even more concise.
It is indeed syntactic sugar (it compiles to identical CIL)
The benefit to the first approach is that it is explict at the call site what event type you are using.
The benefit to the second is that, should the delegate type change but stay compatible the code will not require changing, if it changes but is incompatible you need only fix the method, not the places it is attached to the event.
As ever this is a balance as to which pro/con is more use to the situation at hand

Categories

Resources