C# 带有同步和异步委托的重载解析类型推断问题
我有两个方法C# 带有同步和异步委托的重载解析类型推断问题,c#,compilation,type-inference,overload-resolution,C#,Compilation,Type Inference,Overload Resolution,我有两个方法Foo的重载,一个用于同步,一个用于异步委托: public static void Foo<T>(Func<T> function, List<T> list = null) { } public static void Foo<T>(Func<Task<T>> function, List<T> list = null) { } …除非我尝试显式传递null作为第二个参数: Foo(async
Foo
的重载,一个用于同步,一个用于异步委托:
public static void Foo<T>(Func<T> function, List<T> list = null) { }
public static void Foo<T>(Func<Task<T>> function, List<T> list = null) { }
…除非我尝试显式传递null
作为第二个参数:
Foo(async () => { await Task.CompletedTask; return 0;}, null); // Error CS0121
在这种情况下,我会得到以下编译错误:
以下方法或属性之间的调用不明确:'Test.Foo(Func,List)
和'Test.Foo(Func,List)
奇怪的是,null
是这个参数的默认值
这是编译器的一个可以修复的限制,还是一个无法解决的歧义情况,除了编译错误之外,其他任何方法都无法处理
C#8、Visual Studio 16.3.9、.NET Framework 4.8、.NET Core 3.0
更新:是对这个问题的一个有用的回答。您不能从“null”判断这是
List
还是List
,编译器也不能这样做。这是后一种观点
而且它无法确定您的
任务
是否应该匹配Func
或Func
,而不是唯一的,因此这是不明确的。T=Task或R=T都是可能的 那么,如果省略null
参数,为什么不会出现错误呢?不管怎样,它的默认值是null
,所以在这种情况下我也应该得到一个错误。啊,我明白了,除了“无错误”,它还绑定到Func版本?推断出什么?Task.CompletedTask的类型为“Task”,因此它肯定不是“Task”类型,因此必须绑定到Func?正确的?我无法解释是否传递默认参数的效果。当我省略null
时,编译器会正确推断类型为int
。当我包含null
时,编译器认为beyondT是int
也有可能T是Task
(第一个重载接受同步委托):-/是的,你有一个Func。如果删除async staff,一切都会按预期进行。异步lambda确实隐式地创建了一个任务,“异步int”的类型与任务相同。你真的认为你的第一个重载是固定的同步?不可能是任务吗?我们将要解释为什么歧义是显而易见的,我们只是不知道为什么它消失了,然后省略了第二个参数。请认真考虑编译器错误。如果您查看的重载,有两个类似于myFoo
overloads。如果编译器不明白最好使用接受异步委托的重载,那么每次调用任务时,我们都必须显式地编写
。运行!重载解析发生在填充默认参数之前。必须这样做,因为对于位置参数,默认值可能完全不同,具体取决于选择的重载。一旦选择了重载,就不会有歧义(根据定义)。但是如果您提供参数值,现在编译器无法知道哪种方法重载更好,因为推理规则在返回到T
之前就依赖null
值。请参阅同一问题的标记副本。@PeterDuniho我仔细阅读了被称为副本的两个问题,包括Eric Lippert的冗长而详细的答案,但我不认为这些问题与我的问题重复。第一个问题涉及params
关键字,我在问题中没有提到。这些问题都没有提到可选的论点,它们是我问题的核心。所以我仍然不知道我的问题的答案。这是编译器的一个可以修复的限制,还是一个无法解决的问题,只能通过编译错误来处理?@PeterDuniho您的评论是有道理的。你能把它扩展成一个答案吗?我发现标记的副本充分满足了需要。编译器无法在您的场景中执行重载解析,错误消息清楚地表明了这一点。解决方案是,像往常一样,并在标记副本的其他地方解释的那样,向编译器提供足够的类型信息,以便它能够。例如,将null
强制转换为您期望的类型。或者更好,只是不指定参数。既然这是默认值,为什么要显式传递null
。编译器没有执行重载解析,但不是因为myparams
-include方法的正常形式和扩展形式之间存在歧义。我的示例中没有params
。这些被称为重复的问题充其量只能提供一些提示,而不是对我所观察到的错误的清晰而令人信服的解释。如果可能的话,我希望有这样一个解释作为答案,而不是评论。
Foo(async () => { await Task.CompletedTask; return 0;}, null); // Error CS0121