代码之家  ›  专栏  ›  技术社区  ›  Ulf Åkerstedt

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

  •  7
  • Ulf Åkerstedt  · 技术社区  · 7 年前

    从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<>)重载,指示此处将引发视为表达式。 我怎样才能优雅而明确地强制编译器选择m(action)重载?

    一种方法是

    M(() => { throw new Exception(); return; });
    

    但是返回语句的原因似乎不明显,并且存在被下一个开发人员更改的风险,特别是因为Resharper警告无法访问的代码。

    (当然,我可以更改方法命名以避免重载,但这不是问题所在。-)

    4 回复  |  直到 7 年前
        1
  •  6
  •   DaveShaw Thishin    7 年前

    您可以为添加演员表 Action 尽管它确实有点 利斯比 所有括号:

    M((Action)(() => throw new Exception()));
    

    不太理想,但如果你想要最大的清晰度:

    Action thrw = () => throw new Exception();
    M(thrw);
    
        2
  •  6
  •   marc_s MisterSmith    7 年前

    这与lambda是语句lambda还是表达式lambda没有任何关系(最简洁的方法是将lambda从表达式lambda更改为语句lambda,而不更改行为)。

    有许多方法可以使lambda匹配多个可能的重载。这一个特定于较新的版本,但其他方法自C 1.0起就已应用(自引入匿名方法以来,需要存在对匿名方法的特定处理以及由此产生的过载解决消除歧义)。

    C规范第7.5.3.3节详细说明了确定调用哪个重载的规则。具体来说,当参数是匿名方法时,它总是更喜欢委托(或表达式)具有返回值的重载,而不是没有返回值的重载。无论是语句lambda还是表达式lambda,这都是正确的;它适用于任何形式的匿名函数。

    因此,您要么需要通过使匿名方法对 Func<int> 或显式强制类型为 Action 所以编译器本身并没有消除歧义。

        3
  •  4
  •   antispinwards    7 年前

    一种可能的方法是使用命名参数:

    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());
    }
    

    这可能应该记录在代码中,以免让下一个开发人员感到意外,正如注释中所指出的,编写一个适当的自动测试来验证是否调用了正确的重载。

        4
  •  4
  •   Eric Lippert    7 年前

    添加到所有 合理的 给出答案,这是一个迷人的 不像话 答:

    ((Action<Action>)M)(() => throw new Exception());
    

    那个应该 bake the noodle 任何一个维护程序员来了,他们会离开它。看看你能不能弄明白为什么它能起作用。