今天来结束第九章,聊下我们经常忽略,但是编译器会帮我们完成的"类型判断和重载决策",理解编译器如何帮我们完成,相信在写代码时会更明确,避免一些编译出错,排查的问题,让我们开发更给力。
我们知道隐式类型的数组以及将方法组转换为委托类型都需要类型推断,但将方法组作为其它方法的参数进行转换时,会显得极其复杂,我们由浅入深,一步一步来看,编译器是如何帮我们做的一些推断。
1 //定义一个泛型方法,参数分别为TInput类型和一个Func<TInput, TResult>类型的委托 2 public static TResult Get<TInput, TResult>(TInput input, Func<TInput, TResult> func) 3 { 4 return func(input); 5 } 6 static void Main(string[] args) 7 { 8 var a = Get("111", x => int.Parse(x)); 9 //第一个参数是字符串,所以可以推断TInput类型有到string类型的隐式转换 10 //则方法签名为:public static TResult Get<string, TResult>(string input, Func<string, TResult> func) 11 //第二个参数是一个Lambda表达式,根据返回的类型是一个int,所以可以推断TResult类型有到int类型的隐式转换 12 //则方法签名为:public static int Get<string, int>(string input, Func<string, int> func) 13 //根据上述的步骤,我们推断出了TInput和TResult类型 14 }
返回类型集合
从上述代码中,可以看到TResult类型是由Lambda表达式的返回值类型来推断的,那如果我有多个返回类型又是一种什么情况呢?请看下面的代码。
1 var b = Get(1111, delegate (int x) { 2 if (x == 1111) 3 { 4 return "1111"; 5 } 6 else 7 { 8 return new object(); 9 } 10 }); 11 //由上可以推断出TInput类型有到int类型的隐式转换,则TInput为int 12 //而返回类型有两个,一个是string类型,一个是object类型,但是string类型有到object类型的隐式转换,则TResult为object类型 13 //编译器帮我们把所有的返回类型放入一个集合,对集合中的类型进行判断,判断是否有一个类型是可由剩余其它类型隐式转换可得,则把这个类型作为返回值的类型
如何选择正确的重载
如果重载具有二义性,那编译肯定是过不了的,要么强制转换某个参数以符合某个方法的签名,或者修改重载方法。从参数或者返回类型考虑,请参照下面一条:
从任何类型"转换成它本身"被认为好于"转换成其它类型",如
1 static void Debug0(int x) { } 2 static void Debug0(double x) { } 3 4 Debug0(5); //static void Debug0(int x) 5 Debug0(5.0); //static void Debug0(double x) 6 //int有到double的隐式转换,所以存在二义性,但根据int=>int是好于int=>double的,所以选择使用static void Debug0(int x)
同理于返回类型,作用于委托或者Lambda表达式,如
1 static int Debug1(Func<int> func) { 2 return func(); 3 } 4 5 static double Debug1(Func<double> func) 6 { 7 return func(); 8 } 9 10 Debug1(() => 1.1); 11 Debug1(delegate () { return 1.1; }); 12 //根据Lambda表达式和匿名方法,Debug1(() => 1.1)和Debug1(delegate () { return 1.1; })返回的类型是double类型,则Lambda表达式和匿名方法转换成Func<double>类型的实例,调用的则是static double Debug1(Func<double> func) 13 Debug1(() => 1); 14 Debug1(delegate () { return 1; }); 15 //根据Lambda表达式和匿名方法,Debug1(() => 1)和Debug1(delegate () { return 1; })返回的类型是int类型,则Lambda表达式和匿名方法转换成Func<int>类型的实例,调用的则是static double Debug1(Func<int> func)
请斧正。