我知道编译器可以从lambda表达式转换为Predicate。
例如:
Predicate<int> p = x => true;
很好。
但是当我想创建一个包含谓词的元组时。 我试着这样做(简化版):
Tuple<Predicate<int>> t;
t = Tuple.Create(x => true);
我收到了编译错误:
无法从用法推断出方法'System.Tuple.Create(T1)'的类型参数。尝试明确指定类型参数。
我的问题是这是一个错误,这里的歧义在哪里?
(我知道我可以通过施放来修复它:t = Tuple.Create((Predicate<int>)(x => true));
但我想明白为什么第一种方式不好,而且我也不想进行转换以节省输入:)
答案 0 :(得分:7)
这里的歧义在哪里?
这里的含糊之处在于编译器不会尝试根据已经声明所需类型的左侧推断传递给Tuple.Create
的lambda表达式。会发生什么?类型推断算法启动(无论您声明变量的类型),并且找不到与lambda表达式匹配的合适匹配项,因为它没有足够的信息。
通过声明元组的类型并明确告诉编译器如何推断lambda表达式,可以很容易地解决这个问题:
t = Tuple.Create<Predicate<int>>(x => true);
如果你想进入类型推理算法,看看它失败的原因:
假设:
Tr M<X1…Xn>(T1 x1 … Tm xm)
使用M(E1 ... Em)形式的方法调用,类型推断的任务是 为每个类型参数找到唯一的类型参数S1 ... Sn X1 ... Xn使呼叫M(E1 ... Em)有效。
现在我们开始:
7.5.2.1第一阶段:
对于每个方法参数Ei:
如果Ei是匿名函数,则为显式参数类型推断 (§7.5.2.7)由Ei制成Ti
所以我们看一下显式参数类型推断的作用:
7.5.2.7显式参数类型推断
在下面,从表达式E到类型T进行显式参数类型推断 方式:
·如果E是明确键入的匿名函数 参数类型U1 ... Uk和T是委托类型或表达式树类型 参数类型为V1 ... Vk然后为每个Ui进行精确推理 (§7.5.2.8)是从Ui到相应的Vi。
您的匿名函数不是显式类型,因此编译器无法从参数类型Ui..Uk到Tuple.Create
的正确重载进行精确推断。
答案 1 :(得分:3)
Tuple.Create
方法采用泛型类型参数。编译时通常可以猜出这些类型是什么。但是对于谓词,它无法弄明白。一些解决方案是:
Predicate<int> p = x => true;
var t = Tuple.Create(p);
或者我建议您只指定类型参数:
var t = Tuple.Create<Predicate<int>>(x => true);
答案 2 :(得分:2)
提供与现有答案略有不同的观点:
C#语言的设计是为了
t = Tuple.Create(x => true);
Tuple.Create(x => true)
的类型不依赖于t
。这样可以更容易地解析C#,以便更容易推理C#代码,从而更容易为无效的C#代码实现合适的错误消息。
现在,鉴于此,编译器应如何确定Tuple.Create(x => true)
应将x => true
视为Predicate<int>
,而不是Func<int, bool>
?除了在设计编译器不检查的位置之外,没有足够的信息来确定这一点。