How can I force a throw to be a statement and not an expression (in a lambda expression)?
This has nothing to do with whether the lambda is a statement lambda or an expression lambda (as is most succinctly shown by you changing the lambda from an expression lambda to a statement lambda and the behavior not changing).
There are numerous ways you can make a lambda match multiple possible overloads. This one is specific to newer versions, but other methods have applied since C# 1.0 (and the specific handling of anonymous methods and the resulting overload resolution disambiguation has needed to exist since the introduction of anonymous methods).
The rules for determining which overload is called are spelled out in section 7.5.3.3 of the C# specs. Specifically, when the parameter is an anonymous method, it will always prefer the overload who's delegate (or expression) has a return value over one that has no return value. This will be true whether it's a statement lambda or expression lambda; it applies to any form of anonymous function.
Thus you either need to prevent both overload from matching by making the anonymous method not valid for a Func<int>
, or explicitly force the type to be an Action
so the compiler is not disambiguating it itself.
You could add a cast to for Action
, although it does get a bit LISP'y with all the parentheses:
M((Action)(() => throw new Exception()));
Not ideal, but if you want maximum clarity:
Action thrw = () => throw new Exception();
M(thrw);
One possible approach is to use named parameters:
public static void M(Action action) { /* do stuff */ }
public static void M(Func<int> func) { /* do stuff */ }
public static void Main()
{
M(action: () => throw new Exception());
}
This should probably be documented in the code so as not to surprise the next developer to come along, and as noted in the comments write an appropriate automated test to verify the correct overload is called.