62

C# 语言规范在第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 是匿名函数,则从 Ei 到 Ti 进行显式参数类型推断(第 7.5.2.7 节)

    ⇒ 没有效果,因为 lambda 表达式没有明确的参数类型。正确的?

  • 否则,如果 Ei 具有类型 U 并且 xi 是值参数,则从 U 到 Ti 进行下限推断。

    ⇒ 第一个参数是静态类型的string,所以这增加string了 的下限T,对吧?

第二阶段

  • 所有不依赖于(§7.5.2.5)任何 Xj 的未固定类型变量Xi 都是固定的(§7.5.2.10)。

    T不固定;T不依赖于任何东西......所以T应该修复,对吧?

§7.5.2.11 固定

  • 候选类型 Uj 的集合开始是 Xi 的边界集合中所有类型的集合。

    ⇒ { string(下限)}

  • 然后,我们依次检查 Xi 的每个界限:[...] 对于 Xi 的每个下限 U,所有类型 Uj 都从候选集中删除,其中没有从 U 隐式转换到。[...]

    ⇒ 不会从候选集中删除任何东西,对吧?

  • 如果在剩余的候选类型 Uj 中存在唯一类型 V,从该类型隐式转换到所有其他候选类型,则 Xi 固定为 V。

    ⇒ 由于只有一种候选类型,这是空洞的,所以 Xi 固定为string。正确的?


那么我哪里错了?

4

1 回答 1

41

更新:我今天早上对公共汽车的初步调查是不完整和错误的。第一阶段规范的文字是正确的。执行是正确的。

规范是错误的,因为它在第二阶段得到了错误的事件顺序。我们应该指定在修复非依赖参数之前进行输出类型推断。

伙计,这东西很复杂。我重写规范的这一部分的次数比我记得的要多。

我以前见过这个问题,我清楚地记得进行了修改,以至于不正确的术语“类型变量”在任何地方都被“类型参数”替换。(类型参数不是内容可以变化的存储位置,所以称它们为变量是没有意义的。)我认为同时我注意到排序是错误的。可能发生的事情是我们不小心在网络上发布了旧版本的规范。许多道歉。

我将与 Mads 合作更新规范以匹配实施。我认为第二阶段的正确措辞应该是这样的:

  • 如果不存在未固定的类型参数,则类型推断成功。
  • 否则,如果存在一个或多个对应形参类型为 Ti 的实参 Ei,使得类型为 Ti 的 Ei 的输出类型至少包含一个非固定类型参数 Xj,且类型为 Ti 的 Ei 的输入类型均不包含任何非固定类型参数Xj,然后从所有这样的 Ei 到 Ti 进行输出类型推断。

无论上一步是否真的做出了推断,我们现在必须至少修复一个类型参数,如下所示:

  • 如果存在一个或多个类型参数 Xi 使得 Xi 不固定,并且 Xi 具有一组非空边界,并且 Xi 不依赖于任何 Xj,那么每个这样的 Xi 都是固定的。如果任何修复操作失败,则类型推断失败。
  • 否则,如果存在一个或多个类型参数 Xi 使得 Xi 不固定,并且 Xi 具有一组非空边界,并且至少存在一个依赖于 Xi 的类型参数 Xj,那么每个这样的 Xi 都是固定的。如果任何修复操作失败,则类型推断失败。
  • 否则,我们无法取得进展,并且存在不固定的参数。类型推断失败。

如果类型推断既不失败也不成功,则重复第二阶段。

这里的想法是我们要确保算法永远不会进入无限循环。在第二阶段的每一次重复中,它要么成功,要么失败,要么取得进展。它的循环次数不可能超过要固定到类型的类型参数。

感谢您引起我的注意。

于 2010-09-13T14:23:48.793 回答