153

I have the following code:

Func<string, bool> comparer = delegate(string value) {
    return value != "0";
};

However, the following does not compile:

var comparer = delegate(string value) {
    return value != "0";
};

Why can't the compiler figure out it is a Func<string, bool>? It takes one string parameter, and returns a boolean. Instead, it gives me the error:

Cannot assign anonymous method to an implicitly-typed local variable.

I have one guess and that is if the var version compiled, it would lack consistency if I had the following:

var comparer = delegate(string arg1, string arg2, string arg3, string arg4, string arg5) {
    return false;
};

The above wouldn't make sense since Func<> allows only up to 4 arguments (in .NET 3.5, which is what I am using). Perhaps someone could clarify the problem. Thanks.

BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
Marlon
  • 19,924
  • 12
  • 70
  • 101

8 Answers8

164

UPDATE: This answer was written over ten years ago and should be considered to be of historical interest; in C# 10 the compiler will infer some delegate types.


Others have already pointed out that there are infinitely many possible delegate types that you could have meant; what is so special about Func that it deserves to be the default instead of Predicate or Action or any other possibility? And, for lambdas, why is it obvious that the intention is to choose the delegate form, rather than the expression tree form?

But we could say that Func is special, and that the inferred type of a lambda or anonymous method is Func of something. We'd still have all kinds of problems. What types would you like to be inferred for the following cases?

var x1 = (ref int y)=>123;

There is no Func<T> type that takes a ref anything.

var x2 = y=>123;

We don't know the type of the formal parameter, though we do know the return. (Or do we? Is the return int? long? short? byte?)

var x3 = (int y)=>null;

We don't know the return type, but it can't be void. The return type could be any reference type or any nullable value type.

var x4 = (int y)=>{ throw new Exception(); }

Again, we don't know the return type, and this time it can be void.

var x5 = (int y)=> q += y;

Is that intended to be a void-returning statement lambda or something that returns the value that was assigned to q? Both are legal; which should we choose?

Now, you might say, well, just don't support any of those features. Just support "normal" cases where the types can be worked out. That doesn't help. How does that make my life easier? If the feature works sometimes and fails sometimes then I still have to write the code to detect all of those failure situations and give a meaningful error message for each. We still have to specify all that behaviour, document it, write tests for it, and so on. This is a very expensive feature that saves the user maybe half a dozen keystrokes. We have better ways to add value to the language than spending a lot of time writing test cases for a feature that doesn't work half the time and doesn't provide hardly any benefit in cases where it does work.

The situation where it is actually useful is:

var xAnon = (int y)=>new { Y = y };

because there is no "speakable" type for that thing. But we have this problem all the time, and we just use method type inference to deduce the type:

Func<A, R> WorkItOut<A, R>(Func<A, R> f) { return f; }
...
var xAnon = WorkItOut((int y)=>new { Y = y });

and now method type inference works out what the func type is.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • 3
    +1 Great answer. With everyone calling your name we knew you'd show up. – The Scrum Meister Feb 11 '11 at 07:05
  • 49
    When are you going to compile your SO answers into a book? I'd buy it :) – Matt Greer Feb 11 '11 at 16:13
  • 13
    I second the proposal for an Eric Lippert book of SO answers. Suggested title: "Reflections From The Stack" – Adam Rackis Feb 17 '11 at 20:54
  • 27
    @Eric: Good answer, but it's *slightly* misleading to illustrate this as something that's not possible, as this actually works completely fine in D. It's just that you guys didn't choose to give delegate literals their own type, and instead made them depend on their contexts... so IMHO the answer should be "because that's how we made it" more than anything else. :) – user541686 Aug 01 '11 at 11:24
  • What would probably be something interesting to see would be `dynamic foo = x => x + y` - you could probably turn this into a trampoline that returns runtime compiled delegates - although it would probably only be interesting and nothing else. – Jonathan Dickinson Nov 08 '11 at 08:12
  • That means that we still have to define the WorkItOut<> generic as many times as we need different numbers of parameters, right ? – Raphaël Saint-Pierre May 02 '12 at 19:31
  • @ Raphaël Saint-Pierre: Right. C# don't support variadic template. – turbanoff May 31 '12 at 06:19
  • "...Or do we? Is the return `int?` `long?` `short?` `byte?`" `int`, `long`, `short` or `byte` seems more appropriate than the nullable alternatives :p (yeah yeah I know those are question marks and not part of the type) – Rune FS May 02 '13 at 11:02
  • *//Now, you might say, well, just don't support any of those features. Just support "normal" cases where the types can be worked out. That doesn't help. How does that make my life easier? //* There are a few scenarios in C# where compiler throws ambiguity error (which means C# is not oblivious to making things easier only at times). Well if this feature doesn't make your benefit to cost ratio, then its a fair point.. – nawfal May 29 '13 at 12:26
  • 4
    @nawfal: You're missing my point. Writing that ambiguity checker is *also* work. The hardest part of writing the overload resolution algorithm in C# is not writing it to get the *correct* cases working. The code that works out *what error to give when the code is wrong* is longer and more complicated than the code that works out the correct case. Ambiguity checkers are very difficult to get right *because the code is ambiguous*. – Eric Lippert May 29 '13 at 13:49
  • 1
    @Eric: If we assume that Func is the most common intent when assigning a lambda to a 'var' local, then it seems relatively easy for the C# compiler to essentially employ the behavior of your "WorkItOut" template in variadic form, since it works today in the current C# compiler. When passed to WorkItOut, each of the problematic examples you list produces the error "type arguments cannot be inferred from usage" except the last one (+=), which returns the resulting sum. This all seems quite reasonable, and I will be using my own "variadic" form of WorkItOut (multiple overloads) in the meantime. – michael Jan 05 '16 at 19:14
  • var x2 = (int y) =>123; Why would this not work for a function when it works for standard variables such as: var x2 = 123; ? – ATD May 11 '16 at 15:53
  • 1
    @ATD: This answer answers that question: sure, the feature could be made to work for simple cases. **That does not make the compiler developer's job easier.** It makes it harder because now a "simple case detector" must be designed, specified, written, tested and documented. All that work takes away effort from features that actually would make a difference to developers' lives. – Eric Lippert May 11 '16 at 16:06
  • But it should for for straightforward no param delegates () => { } ==> void delegate().After all, it is just an Action which is longer than var. – AbstractDissonance Jun 17 '17 at 14:14
  • @AbstractDissonance: See my previous comment. – Eric Lippert Jun 17 '17 at 14:44
  • 3
    @EricLippert That isn't an answer, that is a cop out. If you want to use that logic, you can use it to justify not having anything. Sorry, it's true... even though you will try to squirm out of the truth. – AbstractDissonance Jun 17 '17 at 18:25
  • 3
    @abstractdissonance I assure you it is an answer. If its an answer you don't like then why not post an answer that you do like? That way I and everyone else can benefit from your insights on a question that plainly you care about. – Eric Lippert Jun 17 '17 at 18:28
  • 5
    @abstractdissonance I note also that the compiler is open source. If you care about this feature then you can donate the necessary time and effort to make it happen. I encourage you to submit a pull request. – Eric Lippert Jun 17 '17 at 18:41
  • 2
    @AbstractDissonance: Finally, your general claim is that I can use the logic "features have costs and benefits, and we try to implement only those features that give good benefits for reasonable costs" to justify not doing any feature. **That is 100% correct**. I *do* use that logic to not justify doing any feature whose costs exceed its benefits. There's no truth to "squirm out of" there; I have said dozens -- hundreds? -- of times on this site that features have costs and must be justified by their benefits. I not only embrace that truth, I state it emphatically over and over again! – Eric Lippert Jun 18 '17 at 00:27
  • 1
    What you are really saying is simply: "It's not worth our time, we have better things WE *want* to do"... and that is not a logical argument but simply an emphatic choice. At least be honest with yourself instead of trying to justify your choice with logic. – AbstractDissonance Jun 18 '17 at 10:49
  • 8
    @AbstractDissonance: We measured the cost in terms of the limited resources: developers and time. This responsibility was not granted by god; it was imposed by the vice president of the developer division. The notion that somehow the C# team could ignore a budget process is a strange one. I assure you, tradeoffs were and still are made by the careful, thoughtful consideration of experts who had the C# communities expressed wishes, the strategic mission for Microsoft, and their own excellent taste in design in mind. – Eric Lippert Jun 18 '17 at 14:01
  • Ok, then we agree. They are made up rules to be congruent with other made up rules. I will have to disagree though about Microsoft. Microsoft does not have excellent taste in design(many windows products are a prime example). If we are specifically talking about C#'s design, then I would agree in relatively terms as it is a well designed language as compared to most(Although the IL representation and performance can be issues at times). – AbstractDissonance Jun 18 '17 at 17:05
  • 1
    This limitation is no longer there in C# 10 (https://stackoverflow.com/a/72714640/3834) – Graviton Jul 18 '22 at 06:31
32

Only Eric Lippert knows for sure, but I think it's because the signature of the delegate type doesn't uniquely determine the type.

Consider your example:

var comparer = delegate(string value) { return value != "0"; };

Here are two possible inferences for what the var should be:

Predicate<string> comparer  = delegate(string value) { return value != "0"; };  // okay
Func<string, bool> comparer = delegate(string value) { return value != "0"; };  // also okay

Which one should the compiler infer? There's no good reason to choose one or the other. And although a Predicate<T> is functionally equivalent to a Func<T, bool>, they are still different types at the level of the .NET type system. The compiler therefore cannot unambiguously resolve the delegate type, and must fail the type inference.

itowlson
  • 73,686
  • 17
  • 161
  • 157
  • 1
    I'm sure quite a few other people at Microsoft also know for sure. ;) But yes, you allude to a chief reason, the compile time type cannot be determined because there is none. Section 8.5.1 of the language specification specifically highlights this reason for disallowing anonymous functions from being used in implicitly typed variable declarations. – Anthony Pegram Feb 11 '11 at 04:47
  • 3
    Yep. And even worse, for lambdas we don't even know if it is going to a delegate type; it might be an expression tree. – Eric Lippert Feb 11 '11 at 06:45
  • For anyone interested, I wrote up a bit more about this and how the C# and F# approaches contrast at http://www.mindscapehq.com/blog/index.php/2011/02/23/first-class-functions-in-f-part-0/ – itowlson Feb 23 '11 at 23:33
  • why can't the compiler just fabricate a new unique type like C++ does for its lambda function – Weipeng Jul 25 '18 at 17:29
  • How do they differ "at the level of the .NET type system"? – arao6 Dec 01 '19 at 08:39
6

Eric Lippert has an old post about it where he says

And in fact the C# 2.0 specification calls this out. Method group expressions and anonymous method expressions are typeless expressions in C# 2.0, and lambda expressions join them in C# 3.0. Therefore it is illegal for them to appear "naked" on the right hand side of an implicit declaration.

Brian Rasmussen
  • 114,645
  • 34
  • 221
  • 317
  • And this is underscored by section 8.5.1 of the language specification. "The initializer expression must have a compile-time type" in order to be used for a implicitly typed local variable. – Anthony Pegram Feb 11 '11 at 04:49
5

Different delegates are considered different types. e.g., Action is different than MethodInvoker, and an instance of Action can't be assigned to a variable of type MethodInvoker.

So, given an anonymous delegate (or lambda) like () => {}, is it an Action or a MethodInvoker? The compiler can't tell.

Similarly, if I declare a delegate type taking a string argument and returning a bool, how would the compiler know you really wanted a Func<string, bool> instead of my delegate type? It can't infer the delegate type.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
2

The following points are from the MSDN regarding Implicitly Typed Local Variables:

  1. var can only be used when a local variable is declared and initialized in the same statement; the variable cannot be initialized to null, or to a method group or an anonymous function.
  2. The var keyword instructs the compiler to infer the type of the variable from the expression on the right side of the initialization statement.
  3. It is important to understand that the var keyword does not mean "variant" and does not indicate that the variable is loosely typed, or late-bound. It just means that the compiler determines and assigns the most appropriate type.

MSDN Reference: Implicitly Typed Local Variables

Considering the following regarding Anonymous Methods:

  1. Anonymous methods enable you to omit the parameter list.

MSDN Reference: Anonymous Methods

I would suspect that since the anonymous method may actually have different method signatures, the compiler is unable to properly infer what the most appropriate type to assign would be.

Community
  • 1
  • 1
nybbler
  • 4,793
  • 28
  • 23
1

My post doesn't answer the actual question, but it does answer the underlying question of :

"How do I avoid having to type out some fugly type like Func<string, string, int, CustomInputType, bool, ReturnType>?" [1]

Being the lazy/hacky programmer that I am, I experimented with using Func<dynamic, object> - which takes a single input parameter and returns an object.

For multiple arguments, you can use it like so:

dynamic myParams = new ExpandoObject();
myParams.arg0 = "whatever";
myParams.arg1 = 3;
Func<dynamic, object> y = (dynObj) =>
{
    return dynObj.arg0.ToUpper() + (dynObj.arg1 * 45); //screw type casting, amirite?
};
Console.WriteLine(y(myParams));

Tip: You can use Action<dynamic> if you don't need to return an object.

Yeah I know it probably goes against your programming principles, but this makes sense to me and probably some Python coders.

I'm pretty novice at delegates... just wanted to share what I learned.


[1] This assumes that you aren't calling a method that requires a predefined Func as a parameter, in which case, you'll have to type that fugly string :/

Ambrose Leung
  • 3,704
  • 2
  • 25
  • 36
1

Other answers were correct at the time they were written, but starting from C# 10.0 (from 2021), the compiler can infer a suitable delegate type (like some Func<...>, Action<...> or generated delegate type) in such cases.

See C# 10 Features - Lambda improvements.

var comparer = delegate(string value) {
    return value != "0";
};   // OK in C# 10.0, picks 'Func<string, bool>' in this case

Of course the more usual syntax is to us =>, so:

var comparer = (string value) => {
    return value != "0";
};   // OK in C# 10.0, picks 'Func<string, bool>' in this case
Jeppe Stig Nielsen
  • 60,409
  • 11
  • 110
  • 181
0

How is about that?

var item = new
    {
        toolisn = 100,
        LangId = "ENG",
        toolPath = (Func<int, string, string>) delegate(int toolisn, string LangId)
        {
              var path = "/Content/Tool_" + toolisn + "_" + LangId + "/story.html";
              return File.Exists(Server.MapPath(path)) ? "<a style=\"vertical-align:super\" href=\"" + path + "\" target=\"_blank\">execute example</a> " : "";
        }
};

string result = item.toolPath(item.toolisn, item.LangId);
mmm
  • 61
  • 1
  • 9