如何强制 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<>) 重载由编译器 select 编辑,表明此处抛出被视为表达式。
我怎样才能优雅和明确地强制编译器 select M(Action) 重载?
一种方法是这样
M(() => { throw new Exception(); return; });
但是 return 语句的原因似乎并不明显,并且存在被下一个开发人员更改的风险,特别是因为 Resharper 警告无法访问代码。
(当然我可以更改方法命名以避免重载,但这不是问题所在。:-)
您可以为 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());
}
这可能应该记录在代码中,以免让下一位开发人员感到惊讶,并且如评论中所述,编写适当的自动化测试以验证是否调用了正确的重载。
这与 lambda 是语句 lambda 还是表达式 lambda 无关(最简洁的表现是将 lambda 从表达式 lambda 更改为语句 lambda,并且行为没有改变)。
有多种方法可以使 lambda 匹配多个可能的重载。这一个特定于较新的版本,但自 C# 1.0 以来应用了其他方法(自引入匿名方法以来,需要存在匿名方法的特定处理和由此产生的重载解析消歧)。
C# 规范的第 7.5.3.3 节详细说明了确定调用哪个重载的规则。具体来说,当参数是匿名方法时,它总是更喜欢重载的委托(或表达式)具有 return 值而不是没有 return 值的重载。无论是语句 lambda 还是表达式 lambda 都是如此;它适用于任何形式的匿名函数。
因此,您需要通过使匿名方法对 Func<int>
无效来防止两个重载匹配,或者明确强制类型为 Action
,这样编译器就不会消除歧义本身。
除了给出的所有合理答案之外,还有一个迷人的不合理答案:
((Action<Action>)M)(() => throw new Exception());
任何一起来的维护程序员都应该 bake the noodle,他们不会管它。看看你能不能弄清楚它为什么有效。
从 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<>) 重载由编译器 select 编辑,表明此处抛出被视为表达式。 我怎样才能优雅和明确地强制编译器 select M(Action) 重载?
一种方法是这样
M(() => { throw new Exception(); return; });
但是 return 语句的原因似乎并不明显,并且存在被下一个开发人员更改的风险,特别是因为 Resharper 警告无法访问代码。
(当然我可以更改方法命名以避免重载,但这不是问题所在。:-)
您可以为 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());
}
这可能应该记录在代码中,以免让下一位开发人员感到惊讶,并且如评论中所述,编写适当的自动化测试以验证是否调用了正确的重载。
这与 lambda 是语句 lambda 还是表达式 lambda 无关(最简洁的表现是将 lambda 从表达式 lambda 更改为语句 lambda,并且行为没有改变)。
有多种方法可以使 lambda 匹配多个可能的重载。这一个特定于较新的版本,但自 C# 1.0 以来应用了其他方法(自引入匿名方法以来,需要存在匿名方法的特定处理和由此产生的重载解析消歧)。
C# 规范的第 7.5.3.3 节详细说明了确定调用哪个重载的规则。具体来说,当参数是匿名方法时,它总是更喜欢重载的委托(或表达式)具有 return 值而不是没有 return 值的重载。无论是语句 lambda 还是表达式 lambda 都是如此;它适用于任何形式的匿名函数。
因此,您需要通过使匿名方法对 Func<int>
无效来防止两个重载匹配,或者明确强制类型为 Action
,这样编译器就不会消除歧义本身。
除了给出的所有合理答案之外,还有一个迷人的不合理答案:
((Action<Action>)M)(() => throw new Exception());
任何一起来的维护程序员都应该 bake the noodle,他们不会管它。看看你能不能弄清楚它为什么有效。