繁体   English   中英

如何强制 throw 成为语句而不是表达式(在 lambda 表达式中)?

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM