[英]How can I force a throw to be a statement and not an expression (in a lambda expression)?
从 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; });
但是返回语句的原因似乎并不明显,并且冒着被下一个开发人员更改的风险,特别是因为 Resharper 警告了无法访问的代码。
(当然我可以更改方法命名以避免重载,但这不是问题。:-)
这与 lambda 是语句 lambda 还是表达式 lambda 无关(正如您将 lambda 从表达式 lambda 更改为语句 lambda 并且行为没有改变最简洁地展示的那样)。
有多种方法可以使 lambda 匹配多个可能的重载。 这是特定于较新版本的,但自 C# 1.0 以来已经应用了其他方法(并且自从引入匿名方法以来,需要存在匿名方法的特定处理和由此产生的重载决议消歧)。
确定调用哪个重载的规则在 C# 规范的第 7.5.3.3 节中有详细说明。 具体来说,当参数是匿名方法时,它总是更喜欢委托(或表达式)有返回值的重载,而不是没有返回值的重载。 无论是语句 lambda 还是表达式 lambda,这都是正确的; 它适用于任何形式的匿名函数。
因此,您要么需要通过使匿名方法对Func<int>
无效来防止重载匹配,要么显式强制类型为Action
,以便编译器不会自行消除歧义。
您可以为Action
添加一个演员表,尽管它确实有点LISP'y与所有括号:
M((Action)(() => throw new Exception()));
不理想,但如果你想要最大的清晰度:
Action thrw = () => throw new Exception();
M(thrw);
一种可能的方法是使用命名参数:
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());
}
这可能应该记录在代码中,以免让下一个开发人员感到惊讶,并且如注释中所述,编写适当的自动化测试来验证调用了正确的重载。
为了添加所有给出的合理答案,这里有一个迷人的不合理的答案:
((Action<Action>)M)(() => throw new Exception());
那个应该烤任何出现的维护程序员的面条,他们会不理会它。 看看你是否能弄清楚它为什么起作用。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.