C# language specification 描述了第7.5.2节中的类型推断。其中有一个我不明白的细节。考虑以下情况:
// declaration
void Method<T>(T obj, Func<string, T> func);
// call
Method("obj", s => (object) s);
Microsoft和Mono C#编译器都正确推断T
= object
,但我对规范中算法的理解会产生T
= string
,然后失败。以下是我的理解:
第一阶段
如果Ei是一个匿名函数,那么显式参数类型推断(第7.5.2.7节)是从Ei到Ti
⇒无效,因为lambda表达式没有明确的参数类型。右
否则,如果Ei的类型为U且xi是值参数,则下限推断从U到Ti。
⇒第一个参数是静态类型string
,所以这会将string
添加到T
的下限,对吗?
第二阶段
所有不固定的类型变量Xi不依赖(§7.5.2.5)任何Xj都是固定的(§7.5.2.10)。
⇒T
不固定; T
不依赖于任何内容......所以T
应该修复,对吧?
§7.5.2.11修复
候选类型Uj的集合以Xi的边界集中的所有类型的集合开始。
⇒{string
(下界)}
然后我们依次检查Xi的每个边界:[...]对于Xi的每个下界U,从候选集中删除没有从U隐式转换的所有类型Uj。 [...]
⇒不会从候选集中删除任何内容,对吗?
如果在剩余的候选类型Uj中有一个唯一的类型V,其中隐式转换为所有其他候选类型,则Xi固定为V.
⇒因为只有一种候选类型,所以这是真空的,因此Xi固定为string
。右
那我哪里出错?
答案 0 :(得分:41)
更新:我今天早上对公交车的初步调查是不完整和错误的。第一阶段规范的文本是正确的。实施是正确的。
规范是错误的,因为它在第二阶段得到了错误的事件顺序。我们应该在修复非依赖参数之前指定输出类型推断。
男人,这个东西很复杂。我已经重新编写了规范的这一部分,而不是我记得的。
之前我已经看过这个问题了,我清楚地记得做出修改,以便用“类型参数”替换不正确的术语“类型变量”。 (类型参数不是内容可以变化的存储位置,因此将它们称为变量是没有意义的。)我想同时我注意到排序错误。可能发生的事情是我们不小心在网上发布了旧版本的规范。许多道歉。
我将与Mads合作以更新规范以匹配实施。我认为第二阶段的正确措辞应该是这样的:
- 如果不存在不固定的类型参数,则类型推断成功。
- 否则,如果存在一个或多个参数Ei 相应的参数类型Ti就是这样 类型为Ti的Ei的输出类型包含至少一个未固定的 类型参数Xj,和 没有类型为Ti的Ei的输入类型包含任何未固定的类型 类型参数Xj, 然后从所有这样的Ei到Ti进行输出类型推断。
上一步是否真的做出了推断,我们 现在必须修复至少一个类型参数,如下所示:
- 如果存在一个或多个类型参数Xi 习未定,并且 Xi有一组非空的边界,并且 习不依赖任何Xj 然后每个这样的Xi都是固定的。如果任何修复操作失败则 类型推断失败。
- 否则,如果存在一个或多个类型参数Xi 习未定,并且 Xi有一组非空的边界,并且 至少有一个类型参数Xj取决于Xi 然后每个这样的Xi都是固定的。如果任何修复操作失败则 类型推断失败。
- 否则,我们无法取得进展 不固定的参数。类型推断失败。
如果类型推断既未失败也未成功,则重复第二阶段。
这里的想法是我们要确保算法永远不会进入无限循环。在第二阶段的每次重复中,它都会成功,失败或取得进展。它不可能循环多于类型参数来修复类型。
感谢您引起我的注意。