【发布时间】:2020-06-30 10:21:46
【问题描述】:
问题
考虑这两个扩展方法,它们只是从任何类型 T1 到 T2 的简单映射,加上一个重载以流畅地映射到 Task<T>:
public static class Ext {
public static T2 Map<T1, T2>(this T1 x, Func<T1, T2> f)
=> f(x);
public static async Task<T2> Map<T1, T2>(this Task<T1> x, Func<T1, T2> f)
=> (await x).Map(f);
}
现在,当我使用带有引用类型映射的第二个重载时...
var a = Task
.FromResult("foo")
.Map(x => $"hello {x}"); // ERROR
var b = Task
.FromResult(1)
.Map(x => x.ToString()); // ERROR
...我收到以下错误:
CS0121:以下方法或属性之间的调用不明确:“Ext.Map(T1, Func)”和“Ext.Map(Task, Func)”
映射到值类型可以正常工作:
var c = Task
.FromResult(1)
.Map(x => x + 1); // works
var d = Task
.FromResult("foo")
.Map(x => x.Length); // works
但只要映射实际使用输入来产生输出:
var e = Task
.FromResult(1)
.Map(_ => 0); // ERROR
问题
谁能给我解释一下这里发生了什么?我已经放弃为这个错误寻找可行的修复方法,但至少我想了解这个混乱的根本原因。
附加说明
到目前为止,我发现了三个在my use case 中不可接受的变通方法。首先是明确指定Task<T1>.Map<T1,T2>()的类型参数:
var f = Task
.FromResult("foo")
.Map<string, string>(x => $"hello {x}"); // works
var g = Task
.FromResult(1)
.Map<int, int>(_ => 0); // works
另一种解决方法是不使用 lambda:
string foo(string x) => $"hello {x}";
var h = Task
.FromResult("foo")
.Map(foo); // works
第三个选项是将映射限制为内函数(即Func<T, T>):
public static class Ext2 {
public static T Map2<T>(this T x, Func<T, T> f)
=> f(x);
public static async Task<T> Map2<T>(this Task<T> x, Func<T, T> f)
=> (await x).Map2(f);
}
我created a .NET Fiddle,您可以自己尝试以上所有示例。
【问题讨论】:
-
拜托,看看下面的帖子,Overloaded method-group argument confuses overload resolution 和Why is Func<T> ambiguous with Func<IEnumerable<T>> 我想主要的想法和你的问题很相似
-
这些读物很有趣,但除了错误消息之外,我看不出与我的问题有密切的联系。有人可以给 Eric Lippert 打电话吗? :D
-
致匿名投票者:请向我解释如何使这个问题更加集中。 “这是一个编译器错误和一些最小的示例代码。请帮助我让它消失,或者至少帮助我理解这里出了什么问题。”究竟是什么。不够专注?
-
强制 API 的客户端几乎总是指定通常可以推断的类型参数对我来说更像是一个丑陋的解决方法。无论如何,我终于找到了一个更简单的例子,并将重写整个问题。
-
@GoodNightNerdPride 请参阅此question。我认为这与您的问题有关。
标签: c# generics overload-resolution