23

Consider the following lambda expression that is being assigned to an event.

foo.BarEvent += (s, e) => if (e.Value == true) DoSomething();

This appears pretty straight-forward and consists of only one line of code. So why am I getting the following 2 errors from the debugger?

Invalid expression term 'if'


Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement

To fix this problem, all you have to do is a wrap your if statement in brackets.

foo.BarEvent += (s, e) => { if (e.Value == true) DoSomething(); };
//Errors now disappear!

I understand what these error messages are stating. What I don't understand is why a single-condition if statement would be a problem for the compiler and why the first lambda assignment is considered broken.

Could someone please explain the problem?

RLH
  • 15,230
  • 22
  • 98
  • 182
  • 14
    You consider a conditional AND function call a single statement? – Simon Whitehead Aug 15 '13 at 13:33
  • 2
    @SimonWhitehead: Ok, yes, I understand your point. However, this is a single, 1-line statement, from the perspective that each C# "statement" must end with a semi-colon. If my term is incorrect, please elaborate. I don't mind making a minor update to reflect the point I'm trying to make. – RLH Aug 15 '13 at 13:35
  • 5
    On the other hand, why should it be allowed? As Eric Lippert is fond of pointing out, all features are unimplemented by default. – SWeko Aug 15 '13 at 13:35
  • @RLH Could you convert that into a ternary operation? – Andre Calil Aug 15 '13 at 13:36
  • Well, I just started using lambdas about 2 weeks ago (I've unfortunately been stuck in 2004 for to long!) That's why I'm asking these questions. I just assumed that any "statement" (or whatever you want to call it, that consists of one line of code, could be implemented as a lambda, without the bracket wrapper. – RLH Aug 15 '13 at 13:38
  • 2
    @AndreCalil Sure, there's always away to make something a ternary statement. However, wrapping my expression with brackets isn't a problem, which is actually takes much fewer characters. However, I'm trying to figure out why this necessary to begin with. – RLH Aug 15 '13 at 13:39
  • Lambdas are, an expression or a statement block. Your example is a statement block.. so thats pretty much the answer. – Simon Whitehead Aug 15 '13 at 13:39
  • @SimonWhitehead To your first comment, yes, actually it *is* a single statement. It's just not an expression, which is why it needs to be a statement lambda and not an expression lambda. – Servy Aug 15 '13 at 13:48
  • @Servy: isn't it referred to as a "Compound Statement"? It has two parts.. a conditional and a function call.. happy to be wrong.. just always thought there was a defined difference. – Simon Whitehead Aug 15 '13 at 13:53
  • @SimonWhitehead It is made up of more than one expression, but it is not two statements. Two statements would have more than one semicolon. – Servy Aug 15 '13 at 13:57
  • @RLH I believe that a ternary operation is considered as a single expression. Thus, you wouldn't need the braces – Andre Calil Aug 15 '13 at 13:58
  • @AndreCalil No, it's a single *expression*, and is *not* a statement. Thus is can be used for an expression lambda rather than a statement lambda. – Servy Aug 15 '13 at 14:00
  • btw, you need not `== true` – RiaD Aug 15 '13 at 14:03
  • Thanks @Servy. Good to get my terminology straight :) – Simon Whitehead Aug 15 '13 at 14:04
  • @RiaD: I'm just trying to give explicit sample code. Yes, I'm well aware that if you ever use any conditional statement that looks like `... == true` or `... == false`, your doing it wrong. – RLH Aug 15 '13 at 16:08
  • By the way, adding this feature would introduce yet more dangling else problems to the language. – luiscubal Aug 15 '13 at 19:05
  • @RiaD Maybe `e.Value` is a nullable bool? – luiscubal Aug 15 '13 at 19:06

4 Answers4

38

Without { } you declare an expression body, with { } it's a statement body. See Lambda Expressions (C# Programming Guide):

  • An expression lambda returns the result of the expression

  • A statement lambda resembles an expression lambda except that the statement(s) is enclosed in braces [...] The body of a statement lambda can consist of any number of statements.

So, if you want a statement rather than an expression, use braces.

CodeCaster
  • 147,647
  • 23
  • 218
  • 272
  • 1
    So much faster than me typing on my phone!! +1 – Simon Whitehead Aug 15 '13 at 13:38
  • 12
    It's not "if you want more than one statement ....", it's more simply "if you want a *statement* rather than an *expression* ..." – Damien_The_Unbeliever Aug 15 '13 at 13:41
  • 2
    I see. This certainly helps clarify the situation. So the bottom line is, if the "line of code" doesn't evaluate to a final value, wrap it in brackets. I assume this is because the compiler interprets the brackets to really mean `void (s, e) { ...}` which is returning a `void` pointer after operation. – RLH Aug 15 '13 at 13:42
  • 3
    @RLH I think it would help understand the issue better if you read more about statements and expressions and what's the difference between the two. – JJJ Aug 15 '13 at 13:43
  • @Damien_The_Unbeliever I still mix up the terminology sometimes, thanks. I meant to say a statement exists out of one or more expressions, but that also isn't true (ref. [Expressions (C# Programming Guide)](http://msdn.microsoft.com/en-us/library/ms173144.aspx)). – CodeCaster Aug 15 '13 at 13:50
  • 2
    @RLH It doesn't need to be void at all. It can have a `return` keyword within it. Likewise, an expression lambda can resolve to `void` easily enough. – Servy Aug 15 '13 at 13:50
  • A `return` statement is, essentially, a `return void;` statement. Well, it is for most languages. I'd assume this is true for C# as well but I haven't dug in the documentation to verify. – RLH Aug 15 '13 at 14:02
  • @RLH You miss the point entirely. I could have a statement such as `()=>{return 0;}`. A statement lambda doesn't need to be void returning. Likewise, an expression lambda *can* be void returning, i.e. `()=>someVoidMethod`. Either lambda type can return a value, or be `void`. – Servy Aug 15 '13 at 18:27
  • @Simon Whitehead: [:3](http://stackoverflow.com/questions/7371732/why-does-foo-alink-foo-avisited-selector-override-ahover-aactive-s/7371846#7371846) – BoltClock Aug 15 '13 at 18:30
  • @Servy, I do understand but forgive me if I'm not stating this clearly. I was initially stating that "I get it now". The difference between a "statement" and an "expression" is that the expression evaluates to something being returned (or returnable) by the code. In the case of an if-block, nothing is being returned. When you wrap your code in `{}` this converts the statement to an expression because the brackets essentially define a block of code that returns `void` which an if-block does not do. It doesn't have to be `void` but it does need to return *something*. – RLH Aug 15 '13 at 18:36
  • @RLH And I'm saying that's false. An expression, and thus an expression lambda, does not need to return a value. This snippet: `()=> Console.WriteLine("hi");` creates an expression lambda for an expression that has no return value at all. The reason you can't use an `if` isn't because it doesn't return a value; it's because the grammar of C# simply states that an `if` is a statement, not an expression. – Servy Aug 15 '13 at 18:40
  • @Servy: After reading your claims, I decided to ask and do a bit more research (see: http://stackoverflow.com/questions/18260043/return-nothing-or-return-void-what-exactly-does-c-sharp-do-at-the-end-of-a-voi) What?! After reading Reed's response, I assume that the braces around the lambda code "signal" to the compiler that the code might not return a value and is, thus, a series of statements. I feel like I'm going further down this rabbit hole than I intended, but in a good way. Thanks for the push-back. It's made this endeavour quite informative. – RLH Aug 15 '13 at 19:51
  • @RLH They signal no such thing. For an expression lambda it looks at the return value (if any) of that expression and uses that. By enclosing the expression in braces you force it to be a statement, in which the return value of the statement block is whatever is returned via `return`. It doesn't change the fact that either a statement or an expression lambda can be void or can return a value. – Servy Aug 15 '13 at 19:53
  • When you don't declare { } compiler expects some value that can be returned by default, you cannot return if. In Ruby for comparison everything return result, but not in C#. – AuthorProxy Aug 15 '13 at 21:32
9

See Lambda Expressions.

There are two types of lambdas, Expression Lambdas (no {}) and Statement Lambdas (with {}).

The key difference between them is that Expression Lambdas are defined to return the value computed by their right hand side. But if() is a statement, not an expression. There's no value associated with an if. You'll notice that all of the "allowed" language elements in the error message you provide are expressions: they compute a value.

Damien_The_Unbeliever
  • 234,701
  • 27
  • 340
  • 448
0

As I understand it, it would not be a problem to implement this. The C# team have already implemented way scarier functionalities (LINQ for example)

But the problem is where do you draw the line?

if

b => if (b) DoSomething()` 

is allowed, then, why shouldn't

b => if (b) {DoSomething1(); DoSomething2();}

be allowed. Or

list => foreach (var item in list) DoSomething(item)

etc, etc, etc... And before you know it, you have a whole team supporting a basically trivial feature.

The only sure way not to have bugs in a feature is to not implement the feature.

The current implementation is the optimum, as it works in the majority of cases, and it's not too cumbersome in the rest of them (a simple {} does not hinder readability too much, while making it easier for the compiler).

SWeko
  • 30,434
  • 10
  • 71
  • 106
  • 1
    Could you explain what you consider scarey in LINQ? – Gusdor Oct 03 '13 at 12:58
  • It's almost like adding a second language within the language. Parsing it and transforming it in a deterministic way, and providing intelisense for it is, IMHO, tough to implement. – SWeko Oct 03 '13 at 13:10
0

Look at this this way. You have a conditional statement that fires one line if correct.

if (stuff) do stuff;

Which can also be written

if (stuff)
     do stuff;

Because you're only calling one line of code you don't need two brackets. If it were more, then you would have to use brackets.

if (stuff)
{
     do stuff;
     do more stuff;
}

Then you're combining it with an anonymous method, that behaves in a similar fashion

() => do stuff;

More than one statement, uses brackets just like the conditional.

() => {do stuff; do more stuff;}

But now you're nesting them, but still expecting to be able to use the short cut. Which is really what it is, a short cut that implies the brackets for one single statement.

So you're nesting one shortcut inside of another shortcut. C# is just making forcing you to explicitly spell out the nested shortcut.

Smeegs
  • 9,151
  • 5
  • 42
  • 78
  • 6
    Yes and no. You are again (as a lot of people do) defining the requirement in "lines of code". The real definition is a case of expression vs. statement. – Simon Whitehead Aug 15 '13 at 13:49