3

This statement doesn't compile in VS2015, but does in VS2017:

var example = new Action( () => throw new Exception()

enter image description here

What had to change in the way labmdas are parsed in order to support throwing an exception inside a labmda expression?

Especially because if I use a lambda body, VS2015 is perfectly happy:

enter image description here


My question is similar to Why can't I throw exceptions from an expression-bodied member?, but my question, is why. What had to happen in the creation of an expression tree from a lambda that necessitated extending the compiler?

Philip Pittle
  • 11,821
  • 8
  • 59
  • 123
  • 3
    It's in the list of C# 7 features: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-7#throw-expressions – milleniumbug Jun 21 '17 at 22:57
  • Closed as a duplicate of this post which was written during the time of C# 6 and which has an answer mentioning the change to this in C# 7 (https://stackoverflow.com/a/39692763/1864167). It explains what's going on behind the scenes and why it wasn't possible before. – Jeroen Vannevel Jun 21 '17 at 22:58
  • 3
    *“but my question, is why”* – `throw` was only a *statement* before, and a lambda *expression* can only contain expressions but not statements. With C# 7, there is now a `throw` *expression*, so you can use it inside of lambda *expressions*. By using curly braces, you create a body which in turn can contain statements (and instead actually requires statements—that’s why you need to use `return` inside a body, but not when just using a lambda expression). – poke Jun 21 '17 at 23:01
  • To the people voting to reopen this: You should leave a comment to explain your action. So far, I have only seen voices in favor of closing this. – poke Jun 21 '17 at 23:05
  • 1
    @poke, I have re-opened this as it was incorrectly marked as a duplicate. Marking a question "why can I do x" as a duplicate of an old question saying "why **can't** I do x" is not helpful. C# 7 changes this behaviour and this question is about that change. – David Arno Jun 22 '17 at 08:04
  • @poke, Whilst that other question does contain an answer that speculates on what would be coming in C# 7, it links to an old blog article. Answers here are referring to what actually is the case with C# 7, including links to up-to-date official info from MS. So I think this question still has a purpose. However, if you strongly feel it still a duplicate, I won't interfere if you re-close it. – David Arno Jun 22 '17 at 08:13
  • @DavidArno I cannot reclose it, and if you look at the history of the question, you could have seen that it has been closed and reopened twice now. So I would have really liked you to explain your reasoning *before* using your gold badge to reopen it. That being said, you are free to update the answers on that other question if you believe that they should be updated with respect to C# 7; or post a new answer that explains the current situation better. Just because it’s not the exact same question that does not mean that this question isn’t completely covered by that other question. – poke Jun 22 '17 at 08:53
  • @poke, that's a good idea. I hadn't thought of doing that. Answer to previous question updated with the newer links from here and I've re-closed this one. – David Arno Jun 22 '17 at 09:09
  • @DavidArno That’s great, thanks a lot :) – poke Jun 22 '17 at 09:11

3 Answers3

8

In C# 6, () => had to be followed by an expression. It could be an expression that did not produce any value, such a call to a method with a return type of void, but that's still an expression.

In C# 6, throw could only appear in a statement. The complete throw new Exception("test"); is a statement. Note the semicolon in there. There was nothing in the grammar to support throw new Exception("test") on its own, the semicolon was an integral part of it.

Therefore, to get this to work, either C# 7 would have to allow statements after () => and would need to support a form of statements without the terminating ;, or C# 7 would need to extend the definition of an expression to allow throw to appear there. The latter is what happened.

  • 1
    In C# 7, `() =>` still has to be followed by an expression. That’s why it’s called a lambda *expression*, or why the expression bodied methods/properties are *expression* bodied. – poke Jun 21 '17 at 23:04
  • @poke Did I suggest otherwise? –  Jun 22 '17 at 05:10
  • 3
    First sentence: “Before C# 7, [it] *had* to be followed” – but it still has, even after C# 7. – poke Jun 22 '17 at 06:46
  • @poke I didn't say that that changed with C# 7, but I see how that was unclear. Is it better like this? –  Jun 22 '17 at 13:51
  • I’m mostly having problems with the past tense in the first paragraph. If you made that present tense (since that restriction still applies to lambdas) and removed the language version there, I’d be perfectly fine with it :) – poke Jun 22 '17 at 14:02
  • @poke It's past tense because it describes how the language worked in the past. Which bits of that are unchanged in C# 7 is not relevant for the first two paragraphs. I'm looking for some way of clarifying that I'm not making any claims about C# 7 in there. –  Jun 22 '17 at 15:50
4

C# didn't consider throw as having a type (not even void), so throw was a statement, not an expression, and as such it made no sense to either create a lambda with it, or compose larger expressions with it as part.

C# 7 allows it to have any type, so both of these are now possible.

Jon Hanna
  • 110,372
  • 10
  • 146
  • 251
  • Interesting. So the `throw` statement now has a return type? Is it void? – Philip Pittle Jun 21 '17 at 23:32
  • @PhilipPittle The throw expression [“is convertible to every type by an implicit conversion”](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.0/throw-expression.md). Something having a type does not make it an expression though. An expression is a syntactically different construct from a statement. – poke Jun 21 '17 at 23:38
  • 1
    @poke it's syntactically different, but it is also necessary that an expression have a type. `x = 1` is both an expression and a valid statement, `x + y` is an expression but not a statement and `int x = 1` is a statement but not an expression. `throw new Exception("test")` used to be a statement but not an expression, but now it's both and yet the syntax hasn't changed. – Jon Hanna Jul 05 '17 at 11:21
1

It was an update in C# 7.0, see the part about "throw expressions": https://blogs.msdn.microsoft.com/dotnet/2017/03/09/new-features-in-c-7-0/

Evan Trimboli
  • 29,900
  • 6
  • 45
  • 66