16

Why does this lambda expression not compile?

Action a = () => throw new InvalidOperationException();

Conjecture is fine, but I would really appreciate references to the C# language specification or other documentation.

And yes, I know that the following is valid and will compile:

Action a = () => { throw new InvalidOperationException(); };

The context where I would use something like this is described on this blog post.

Jacob Carpenter
  • 4,134
  • 1
  • 29
  • 30

5 Answers5

19

Hmm. I've got an answer, but it's not great.

I don't believe that there's a "throw" expression. There's a throw statement, but not just an expression. Compare this with "Console.WriteLine()" which is a method invocation expression with a void type.

As a parallel, you can't have a switch statement, or an if statement etc as the body of a lambda on its own. You can only have an expression or a block (section 7.14).

Is that any help?

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • That's what I said a few minutes back. You just need to ask yourself: "What does a statement evaluate to?" the answer being nothing. – leppie Oct 31 '08 at 17:43
  • Well, a call to Console.WriteLine doesn't evaluate to anything either, unless you count "void"... but yes, we were certainly thinking along the same lines, hence my +1 of your answer :) – Jon Skeet Oct 31 '08 at 17:45
  • That is helpful. I still wonder though why assignment to a closure works, since I read "num = 0" as a statement. – Jacob Carpenter Oct 31 '08 at 17:48
  • Console.WriteLine does match the return type though :) – leppie Oct 31 '08 at 17:51
  • "num = 0" is an assignment expression. "num=0;" is a statement. – Jon Skeet Oct 31 '08 at 17:54
15

Here's my take:

throw is a statement, not an expression.

And the reference:

12.3.3.11 Throw statements

For a statement stmt of the form

throw expr;

the definite assignment state of v at the beginning of expr is the same as the definite assignment state of v at the beginning of stmt.

To explain the essence perhaps one should think about what an expression implies within the C# lambda construct. It is simply syntactic sugar for:

delegate () { return XXX; }

where XXX is an expression

leppie
  • 115,091
  • 17
  • 196
  • 297
1

All the references I can find, from here:

http://msdn.microsoft.com/en-us/library/ms364047(VS.80).aspx#cs3spec_topic4

show that you have two options:

Action a = () => { throw new InvalidOperationException(); };

or

Action a = () => throw new InvalidOperationException()

Note the missing ; on the end. Yes, it makes no sense to me either. The examples they give in the spec are:

x => x + 1                     // Implicitly typed, expression body
x => { return x + 1; }         // Implicitly typed, statement body
(int x) => x + 1               // Explicitly typed, expression body
(int x) => { return x + 1; }   // Explicitly typed, statement body
(x, y) => x * y               // Multiple parameters
() => Console.WriteLine()      // No parameters

Dunno how much help that is - I can't tell what context you are using it in, and not putting a ; on the end makes no sense in C#

the difference may be that it's an expression body - not a statement - if it doesn't have the {}. Which means that your throw is not valid there, as it's a statement, not an expression!

Bob King
  • 25,372
  • 6
  • 54
  • 66
Nic Wise
  • 8,061
  • 2
  • 31
  • 30
  • 1
    The no semicolon makes sense in the context of the lambda body, but not as a statement where you are assigning to an Action. The context I would use this in is alluded to in http://jacobcarpenter.wordpress.com/2008/10/06/c-compiler-eccentricity-of-the-day-throwing-lambda/ – Jacob Carpenter Oct 31 '08 at 17:34
  • 2
    I can guarantee you that Action a = () => throw new InvalidOperationException() does NOT compile. –  Oct 31 '08 at 17:36
  • The compiler still expects a ; at the end of the line. –  Oct 31 '08 at 17:37
1

You can't return or throw from an un-scoped lambda.

Think of it this way... If you don't provide a {}, the compiler determines what your implicit return value is. When you throw from within the lambda, there is no return value. You're not even returning void. Why the compiler team didn't handle this situation, I don't know.

0

Not a big surprise. Lambda expressions are an aspect of functional programming. Exceptions are an aspect of procedural programming. The C# mesh between the two styles of programming isn't perfect.

yfeldblum
  • 65,165
  • 12
  • 129
  • 169
  • No, it's not the side-effect aspect which is the problem here. You can't have an "if" statement even if it doesn't have side-effects, but you *can* have an assignment expression which is a side-effect. It's a statement/expression distinction. – Jon Skeet Oct 31 '08 at 18:02
  • Right. In pure functional programming, which is sort of the model which lambda expressions is going for, there are only expressions, no statements (statements are mimicked with monadic expressions). It's a a little tricky to get a statement-based language to play well with pure expressions. – yfeldblum Nov 03 '08 at 12:12