Fabulous Adventures In Coding
Eric Lippert is a principal developer on the C# compiler team. Learn more about Eric.
Back in November I wrote a bit about a corner case in method type inference which does not work as expected or as specified in C# 3.0. A number of people made blog comments, sent me mail, and entered "Connect" issues with additional problems and ideas for how we could improve this algorithm. (Particular thanks to nikov for his many fascinating and detailed Connect reports.) As a result, as soon as I had time I embarked upon a detailed review of the method type inference specification and the implementation.
The good news is that this resulted in a complete overhaul of both the implementation and the specification; they are now consistent with each other and to the best of my knowledge, correct. Furthermore, the principle concern of commenters on my earlier article has been dealt with: return type inference from a method group to the return type of a delegate type will be legal, but only when the delegate type's input parameters are all completely known. This eliminates the "chicken and egg" problem I discussed in November, whereby overload resolution on the method group must consume the very same unfixed formal parameter types that it is attempting to infer.
The bad news is that these changes to the implementation will not make it in to the service release of C# 3.0; they will have to wait for a later revision of the compiler. (It is not yet clear to me whether or not the changes to the specification will make it into the next edition of the published specification.)
This is one of the most complex areas of the specification and implementation; I'd like to spend some time in this blog going over the specification in detail and explaining where we got things subtly wrong, and how we intend to fix it.
In this series I want to hit on the following points:
Next time, we'll get started with a look back at C# 2.0.
Sounds like a fascinating journey. Can I suggest you add another question to your list? My *guess* is that your meaning of "wrong" in the last item is "technically incorrect". I'd like to add this question:
* How can MS make the spec easier to understand?
As you say, the type inference part of the spec is very complex. I feel I *mostly* understand what it's meant to do, but I get bogged down almost immediately when I try to apply it to a particular situation. A spec which is hard to understand is in some senses "wrong" too.
If, through writing about it and us (your readers) commenting on proposals etc, we can end up with a more readable spec in this area, that would be a significant achievement IMO.
(I say all of this with the deepest respect for everyone involved in the spec. Despite the complexities, the C# spec is one of the most readable I've seen. I've recently been reading RFCs at the other end of the spectrum, and it's a deeply unpleasant experience.)
So this is also the reason why the following doesn't work?
public static void Do<T>(T value, Action<T> action)
public static V Do<T, V>(T value, Func<T, V> action)
static int Double(int value)
return value * 2;
static void Main()
Action<int> test = number => Console.WriteLine(number);
Func<int, int> test2 = number => number * 2;
Do(42, test); // Works
int result = Do(21, test2); // Works
int result2 = Do(21, Double); // Doesn't compile
Yes. As I noted in my previous article, we do not do return type inference on method groups even when the formal parameter types of the target are known. That means that we fail to deduce a value for V, and therefore the only candidate is the one which takes an Action<T>.
>> What did method type inference look like in C# 2.0? Why was it inadequate for LINQ?
Wasn't there a video taken sometime in 2006 that you talked about C# 2.0's type inference and LINQ's new requirements? http://blogs.msdn.com/ericlippert/archive/2006/11/17/a-face-made-for-email-part-three.aspx
Maybe some of the contents from the video can be used in this series of articles?
Would you mind signaling (that is, you don't have to go into details, just mention if it ever the case) when the considerations here are different in VB.NET, and/or when the VB.NET guys decided to go with a different behaviour?