从C#7.0开始,throw
关键字既可以用作表达式也可以用作语句,这很好。
不过,请考虑这些重载
public static void M(Action doIt) { /*use doIt*/ }
public static void M(Func<int> doIt) { /*use doIt*/ }
当这样调用时
M(() => throw new Exception());
甚至是这样(带有lambda语句)
M(() => { throw new Exception(); });
M(Func <>)重载由编译器选择,指示此处将throw视为表达式。 我如何才能优雅而明确地迫使编译器选择M(Action)重载?
做到这一点的一种方法是
M(() => { throw new Exception(); return; });
但是return语句的原因似乎并不明显,并且存在被下一个开发人员更改的风险,特别是因为Resharper警告有关无法访问的代码。
(当然,我可以更改方法命名以避免重载,但这不是问题。:-)
答案 0 :(得分:6)
您可以为Action
添加一个强制类型转换,尽管它确实有点 LISP'y ,并且带有所有括号:
M((Action)(() => throw new Exception()));
不理想,但是如果您想获得最大的清晰度:
Action thrw = () => throw new Exception();
M(thrw);
答案 1 :(得分:6)
这与lambda是语句lambda还是表达式lambda无关(如您将lambda从表达式lambda更改为语句lambda且行为未改变一样,这最简洁地说明了这一点。)
有多种方法可以使lambda匹配多个可能的重载。这是特定于较新版本的,但是自C#1.0起,其他方法也已应用(并且自引入匿名方法以来,必须存在对匿名方法的特定处理以及由此导致的过载解析歧义)。
在C#规范的7.5.3.3节中详细说明了确定调用哪个重载的规则。具体来说,当参数是匿名方法时,它总是会首选重载的委托(或表达式)具有返回值,而不是没有返回值的重载。无论是语句lambda还是表达式lambda都是如此。它适用于任何形式的匿名函数。
因此,您需要通过使匿名方法对Func<int>
无效来防止两个重载匹配,或者显式强制类型为Action
,以便编译器本身不会对其进行歧义处理。
答案 2 :(得分:4)
一种可能的方法是使用命名参数:
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());
}
这可能应该记录在代码中,以免引起下一个开发人员的注意,并且如注释中所述,编写适当的自动化测试以验证是否调用了正确的重载。
答案 3 :(得分:4)