在较高层次上,方法类型推断的工作方式如下。
首先,我们列出所有的参数 -- 您提供的表达式 -- 以及它们对应的形式参数类型。
让我们看一个比你提供的更有趣的例子。 假设我们有:
class Person {}
class Employee : Person {}
...
Person p = whatever
Employee p2 = whatever
并且进行相同的调用。因此我们建立对应关系:
p --> T1
p2 --> T1
5 --> T2
然后我们列出每个类型参数的“边界”以及它们是否“固定”。我们有两个类型参数,并且一开始没有上限、下限或确切的边界。
T1: (unfixed) upper { } lower { } exact { }
T2: (unfixed) upper { } lower { } exact { }
(回想一下我们在另一个问题中讨论的类型相对大小的问题,类型的相对大小取决于其是否更具限制性; 更具限制性的类型比不那么限制性的类型更小。因为比起长颈鹿,动物的种类更多,所以动物比长颈鹿更小。"上"和"下"界限集就是这样: 对于给定类型参数的类型推导问题的解决方案必须
大于或等于每个下界并且
小于或等于每个上界,并且
等于每个精确边界。)
然后我们看每个参数及其相应的类型。(如果参数是lambda表达式,则我们可能需要确定查看参数的顺序,但是你没有任何lambda表达式,因此让我们忽略该细节) 对于每个参数,我们对形式参数类型进行推导,并将我们推导出的关于该推导的事实添加到界限集合中。因此,在查看第一个参数之后,我们推导出以下界限:
T1: (unfixed) upper { } lower { Person } exact { }
T2: (unfixed) upper { } lower { } exact { }
在第二个参数之后,我们推导出界限。
T1: (unfixed) upper { } lower { Person, Employee } exact { }
T2: (unfixed) upper { } lower { } exact { }
在第三个参数之后,我们推断出边界:
T1: (unfixed) upper { } lower { Person, Employee } exact { }
T2: (unfixed) upper { } lower { int } exact { }
在我们尽可能地进行进展后,我们会“修复”边界,通过找到边界集中满足每个边界的最佳类型来实现。
对于T1,边界集中有两种类型,即
Person
和
Employee
。是否有一个类型能够满足边界集中的所有边界条件呢?是的。
Employee
不能满足
Person
的边界条件,因为
Employee
是比
Person
更小的类型;
Person
是一个下边界——它表示
没有比Person
更小的类型是合法的。
Person
可以满足所有的边界条件:
Person
与
Person
相同,并且比
Employee
大,因此它同时满足这两个边界条件。对于T1,满足所有边界条件的最佳类型是
Person
,而对于T2,显然是
int
,因为在边界集中只有一种类型。因此,我们接下来修复类型参数。
T1: (fixed) Person
T2: (fixed) int
我们问: "对于每个类型参数,我们是否有一个固定的边界?"
如果我将第一个参数的类型更改为dynamic
,那么T1是如何被推断出来的?
如果任何一个参数是动态的,那么T1和T2的推断会被推迟到运行时,此时语义分析器会将值的最具体可访问运行时类型视为动态参数提供的下限所需的类型。
如果您对此主题感兴趣并希望了解更多信息,可以在这里找到我解释C# 3版本算法的视频:
http://blogs.msdn.com/b/ericlippert/archive/2006/11/17/a-face-made-for-email-part-three.aspx
(C# 3没有上限,只有下限和精确边界;除此之外,算法基本相同。)
我写的有关类型推断问题的文章在这里:
http://blogs.msdn.com/b/ericlippert/archive/tags/type+inference/
MyFunc(p1, "", 5)
或者MyFunc("", p2, 5)
。 - H H