3

我正在深入阅读 C#,对亲爱的作者解释的 2 阶段类型推理规则感到困惑。

考虑代码:

static void PrintConvertedValue<TInput,TOutput> (TInput input, Converter<TInput,TOutput> converter) 
{ Console.WriteLine(converter(input)); } 
... 
PrintConvertedValue("I'm a string", x => x.Length);

之后,他解释了使用两阶段推理算法进行推理的过程。

1.第一阶段开始。

2 、第一个参数是TInput类型,第一个参数是字符串类型。我们推断必须存在从字符串到 TInput 的隐式转换。

3.第二个参数是Converter类型,第二个参数是一个隐式类型的lambda表达式。没有进行推理——我们没有足够的信息。

4.第二阶段开始。

5.TInput不依赖任何不固定的类型参数,所以固定为字符串。

6.第二个参数现在有一个固定的输入类型,但一个不固定的输出类型。我们可以认为它是(string x) => x.Length并推断返回类型为 int。因此,必须进行从 int 到 的隐式转换TOutput

7.第二阶段重复。

8.TOutput不依赖任何不固定的东西,所以它固定为int。9现在没有不固定的类型参数,所以推断成功

我对第 2 步和第 5 步感到非常困惑。在第 2 步中,编译器如何进行这样的推断?我的意思是世界转换是如何进入现场的?转换发生 BTW 类型而不是 BTW 类型参数和类型,不是吗?

第 5 步完全让我无法理解,任何人都可以向我展示一个类型参数可以依赖于其他类型参数的简化示例,请解释作者试图暗示什么?

我敢于潜入 C# 规范,但似乎聪明的人为非常相似的聪明人做了它,而不是像我这样的猴子:)

4

1 回答 1

4

转换发生在values上,我们能够使用字符串值作为类型参数的参数来调用该方法,这TInput意味着必须进行从stringto的转换TInput。如果TInput被推断为 be int,那么我们将调用一个需要 a 的方法int,但给它 a string- 这显然是无效的。

请注意,对于一个更简单的示例,只需将其更改为:

void Foo<T>(T input) { }

调用:

Foo("Hello");

必须进行从stringto的转换T才能使调用有效,并且由于没有关于 的其他信息T,我们推断它T就是string.

在一个稍微复杂一点的例子中,你可以有:

public void Bar<T>(T x, T y) {}

Foo("Hello", new object());

在这里,我们推断:

  • 必须有从string到的转换T
  • 必须有从object到的转换T

...所以我们推断那Tobject

至于类型参数依赖,来自 C# 5 规范的第 7.5.2.5 节:

一个非固定类型变量 Xi 直接依赖于一个非固定类型变量 Xj,如果对于某个具有类型 Tk 的参数 Ek,Xj 出现在一个类型为 Tk 的 Ek 的输入类型中,并且 Xi 出现在一个类型为 Tk 的 Ek 的输出类型中。

如果 Xj 直接依赖于 Xi,或者 Xi 直接依赖于 Xk 而 Xk 依赖于 Xj,则 Xj 依赖于 Xi。因此,“取决于”是“直接取决于”的传递闭包但不是自反闭包。

例如:

public void Foo<T1, T2>(T1 value, Func<T1, T2> func)

和:

Foo("foo", x => x.Length);

这里T2取决于T1- 一旦我们推断T1,我们就可以推断T2

于 2013-01-15T18:38:24.490 回答