【问题标题】:Why does adding throw inside a lambda without a return value get inferred as a Func<T> and not as Action? [duplicate]为什么在没有返回值的 lambda 中添加 throw 会被推断为 Func<T> 而不是 Action? [复制]
【发布时间】:2020-06-07 02:14:07
【问题描述】:

我遇到了一个问题,即我正在编写的库的某些测试代码由于调用不明确而无法编译,但我的用法似乎很清楚。经过进一步调查,我发现在没有返回值的 lambda 中添加throw 似乎被推断为任何TFunc&lt;T&gt;,而不是我所期望的Action

下面的人为示例(可以粘贴到 .NET Fiddle)

using System;

public class Program
{
    class Foo
    {

        public void Method(Action action)
        {
            Console.WriteLine("Method A: " + action.GetType());
        }

        public void Method(Func<int> func)
        {
            Console.WriteLine("Method B: " + func.GetType());
        }

        /* // second call to Method becomes ambiguous if this is commented out.
        public void Method(Func<bool> func)
        {
            Console.WriteLine(func.GetType());
        }
        */

    }

    public static void Main()
    {
        var foo = new Foo();
        foo.Method(() => { });
        foo.Method(() => { throw new Exception("Foo!"); });
    }
}

这会导致

Method A: System.Action
Method B: System.Func`1[System.Int32]

也许它假设Func&lt;object&gt; 因为通过抛出它不能推断任何返回类型......但为什么不能呢?为什么它会推断并调用具体的Func&lt;int&gt;


此外,如果我尝试像这样创建隐式 Func&lt;string&gt;

foo.Method(() => 
{ 
    if (false) 
    { 
        throw new Exception("Foo!");
    }
    return "foo";
});

我得到了三个我以前没有遇到过的单独的编译错误:

Compilation error (line 38, col 16): Cannot implicitly convert type 'string' to 'int'
Compilation error (line 38, col 16): Cannot convert lambda expression to intended delegate type because some of the return types in the block are not implicitly convertible to the delegate return type
Compilation error (line 38, col 9): Anonymous function converted to a void returning delegate cannot return a value

在上面的人为示例中研究这些仍然没有多大意义,因为这些错误有点自相矛盾。如果编译器可以确定它正在返回 string 并且无法转换为 int,那么为什么它会对不同的返回类型或返回值的 void 委托感到不安呢?

谁能解释为什么编译器似乎无法理解我的意图?这是 C# 限制还是我没有看到歧义?

【问题讨论】:

  • Func&lt;string&gt; 错误的发生是因为有两个重载并且它们都不接受返回字符串的函数。前两个错误:编译器解释了为什么它无法解决采用Func&lt;int&gt; 的重载。第三个错误:编译器解释了为什么它无法解决采用Action 的重载。
  • 您是否尝试过检查代码生成的 IL?
  • @AluanHaddad 我个人理解这些错误,但是当它们一起出现时,它们似乎相互矛盾。如果第一个错误为真,那么编译器似乎将类型推断为Func&lt;string&gt;,这使得第三个错误令人困惑,因为它暗示它已将 lambda 推断为返回voidvoid。此外,孤立的第二个错误似乎很奇怪,只有一种返回类型。为什么添加throw会让编译器认为有多种返回类型?
  • 当编译器不知道如何推断参数时,它会显示与它们相关的所有错误,以便不区分另一个。我同意,这里的分析器可以改进以将错误合并为一个:Cannot infer lambda expression argument from usage. Did you mean Action or Func&lt;int&gt;?
  • @silkfire 谢谢你的澄清,这就是我的意思。

标签: c# lambda action type-inference func


【解决方案1】:

有些情况你没有考虑。 它们是:

  • Action 不返回值或者它可以返回一个值 被忽略(MSDN)
  • Func&lt;T&gt; 封装了一个没有参数的方法,返回一个 值(MSDN)

引用案例,如下代码

var foo = new Foo();
foo.Method(() => { });
foo.Method(() => { throw new Exception("Foo!"); }); 

在第二个匿名方法调用中,因为它只抛出异常,Compiler 认为它是一个只返回异常的方法,候选 Func&lt;TResult&gt; 传递调用,因为Func&lt;TResult&gt; 返回一个值。为了确认我在下面测试:

foo.Method(() => { if (DateTime.Now <= DateTime.UtcNow) { throw new Exception(); } else {} });

上面的Action是候选人,if(DateTime.Now&lt;=DateTime.UtcNow)可能是truefalse,并且匿名方法不仅返回异常。不过,在接下来的测试中

foo.Method(() => { if (true) { throw new Exception(); } else {} });

Func&lt;TResult&gt; 是现在到期的候选人它只返回异常

让我们修复其他异常:

Compilation error (line 38, col 16): Cannot implicitly convert type 'string' to 'int'
Compilation error (line 38, col 16): Cannot convert lambda expression to intended delegate type because some of the return types in the block are not implicitly convertible to the delegate return type
Compilation error (line 38, col 9): Anonymous function converted to a void returning delegate cannot return a value 

以上异常是因为bad output types。在测试中,您调用了以下匿名方法,该方法返回 string,但它应该返回 int,因为您的 func&lt;int&gt; 返回 int

foo.Method(() => 
{ 
    if (false) 
    { 
        throw new Exception("Foo!");
    }
    return "foo";
});

为避免异常,您应该返回 int

foo.Method(() => 
{ 
    if (false) 
    { 
        throw new Exception("Foo!");
    }
    return 1;
});

参考文献

【讨论】:

  • 我对这个答案有点困惑,因为在 OP 中,所有代表都是无效的,所以这种情况与 Action&lt;T&gt;Func&lt;TResult&gt; 无关。我也不明白你为什么说throw 意味着一个返回值。也许我错过了什么......
  • 但是他超载的动作是Action。它是无效的。
  • @AluanHaddad 是的,我确实考虑过任何情况,无论是无参数还是有参数。
  • 好的。但问题不涉及委托参数类型。它们与返回类型推断无关
  • @AluanHaddad 我已经编辑了答案并添加了一些新的事实。我想听听你的意见。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-10-22
  • 1970-01-01
  • 2023-04-10
  • 2011-07-03
相关资源
最近更新 更多