选择正确的被重载的方法
今天天气不好下雨了本来打算出去找房子的房子到期了,出去找了一圈发现不好找了,时间过得好快,又是一年毕业季啊。。。回来没事准备写一篇随笔吧。
在面向对象的思想中我们经常会用到方法的重载,但是我们很少去思考编译器是怎样根据方法的签名去决定选择哪个方法的。一般来说发生重载具体用哪个方法是显而易见的,因为只有它的参数数量是正确的所有的实参才能转换成对应的参数类型。但是假如有多个方法看起来都合适就比较麻烦了,这样的情况下就要看每个实参类型转换成参数类型的方式。
举个简单的例子,在同一个Person类中声明了两个方法:
1 public class Person 2 { 3 public void GetNumber(int x) 4 { 5 Console.WriteLine(x.ToString()); 6 } 7 public void GetNumber(double y) 8 { 9 Console.WriteLine(y.ToString()); 10 } 11 }
在调用的时候:
1 Person person = new Person(); 2 person.GetNumber(1.5); 3 person.GetNumber(1);
第一次调用传入的参数是1.5显而易见因为不不在从double到int的隐式转换,所以调用的是第二个方法。但是传入参数为1的时候由于存在从double到int的转换所以上面两个方法似乎都合适,在这种情况下编译器会考虑从int到int的转换以及从int到double的转换。这时候我们需要记住一个原则,从任何类型转换成它本身被认为好于转换成另一个不同的类型,这个原则成为“更好的转换”原则。对于上面的这种调用,GetNumber(int x)被认为好于GetNumber(double y)。
假如有两个或者两个以上等多个参数,编译器需要确保存在最合适的方法,如果一个方法所涉及的所有实参转换都至少与其他方法中相应的转换“一样好”,至少有一个转换严格优于其他方法我们就认为这个方法要比其他方法好。
再举一个例子(对上面的例子的扩展):
1 public void GetNumber(int x, double y) 2 { 3 Console.WriteLine($"x={x.ToString()}, y={y.ToString()}"); 4 } 5 public void GetNumber(double x, int y) 6 { 7 Console.WriteLine($"x={x.ToString()}, y={y.ToString()}"); 8 }
这样的话假如我们再次调用GetNumber(1,1)就会有问题了,因为根据最好转换原则会产生歧义,理论上两个方法都适合。
为了消除歧义编译器需要我们为其中一个参数添加强制转换才可以通过编译去选择想调用哪个方法。
在委托的返回类型中也应用到这样的转换原则:
1 public class Person 2 { 3 public void ReturnValue(Func<int> function) 4 { 5 Console.WriteLine($"Return an int:{function()}"); 6 } 7 public void ReturnValue(Func<double> function) 8 { 9 Console.WriteLine($"Return an double:{function()}"); 10 } 11 }
调用的时候:
1 person.ReturnValue(() => 1);
如果一个匿名函数能转换成参数列表相同但返回类型不同的两个委托类型,就根据从”推断的返回类型”到“委托的返回类型”来判定那个委托转换“更好”。从一个无参数推断类型为int的Lambda表达式转换成泛型委托Func<int>和Func<double>,利用上面所说的"更好的转换"原则可以知道调用的是那个方法。在LINQ中经常用到的都是方法链接,返回的类型我们有的时候看到的并不明显,"更好的转换"原则可以广泛应用在LINQ之中。
小结:主要就是在类型转换时所遵循的一个原则,即“更好的转换”原则,应用于方法的重载决策,带有泛型委托返回类型作为参数的方法等等。