The overload resolution rules in C# can be a bit tricky. Following is a code snippet which finds the Max between 2 numbers. using the System.Math libraries.
- public double Max(double d1, double d2)
- {
- Console.WriteLine("In Max(double,double)");
- return Math.Max(d1, d2);
- }
-
- public int Max(int i1, int i2)
- {
- Console.WriteLine("In Max(int,int)");
- return Math.Max(i1, i2);
- }
-
- public T Max<T>(T t1, T t2) where T : IComparable<T>
- {
- Console.WriteLine("In Max<T,T>");
- return t1.CompareTo(t2) > 0 ? t1 : t2;
- }
-
- static void Main(string[] args)
- {
- Program p = new Program();
- Console.WriteLine(p.Max(12.1, 12.2));
- Console.WriteLine(p.Max(1.2, 2.0f));
- Console.WriteLine(p.Max(1,2.0f));
- }
Can you guess what the output is?
-The first call to Max goes to Max(double,double).
-The second call to Max goes to Max(double,double) as well since the first paramter is a double and the 2nd paramter is of type float and implicitly convertible to double.
-The third call to Max goes to Max<float> since 1 parameter(float in this case) can be substituted for T
(no coersion is needed) and the compiler is able to implicitly coerce the 2nd parameter int into a float hence the compiler sets T = float. A generic method is always a better match.
If we were to comment out Max<T> , Max(int,int) and Max(double,double) would be the only methods available in the method group. Clearly Max(double,double) is a better match since both int and float are implicitly convertible to double and hence Max(double,double) is the only unique candidate in the method group.
If we introduce a Max(int,float) then that would be considered the best match for the 3rd call to Max instead, since no type coersion of any kind would be needed.
- public double Max(double d1, double d2)
- {
- Console.WriteLine("In Max(double,double)");
- return Math.Max(d1, d2);
- }
-
- public int Max(int i1, int i2)
- {
- Console.WriteLine("In Max(int,int)");
- return Math.Max(i1, i2);
- }
-
- public float Max(int i1, float i2)
- {
- Console.WriteLine("In Max(int,float)");
- return Math.Max(i1, i2);
- }
-
- public T Max<T>(T t1, T t2) where T : IComparable<T>
- {
- Console.WriteLine("In Max<T,T>");
- return t1.CompareTo(t2) > 0 ? t1 : t2;
- }
-
- static void Main(string[] args)
- {
- Program p = new Program();
- Console.WriteLine(p.Max(12.1, 12.2));
- Console.WriteLine(p.Max(1.2, 2.0f));
- Console.WriteLine(p.Max(1,2.0f));
- }
To make things interesting, let's add a generic extension method called Max.
- public static class Extensions
- {
- public static T Max<T>(this Program p, T t1, T t2) where T : IComparable<T>
- {
- Console.WriteLine("In Extension Method Max<T,T>");
-
- return t1.CompareTo(t2) > 0 ? t1 : t2;
- }
- }
-
- public class Program
- {
- public double Max(double d1, double d2)
- {
- Console.WriteLine("In Max(double,double)");
- return Math.Max(d1, d2);
- }
-
- public int Max(int i1, int i2)
- {
- Console.WriteLine("In Max(int,int)");
- return Math.Max(i1, i2);
- }
-
- public T Max<T>(T t1, T t2) where T : IComparable<T>
- {
- Console.WriteLine("In Max<T,T>");
- return t1.CompareTo(t2) > 0 ? t1 : t2;
- }
-
- static void Main(string[] args)
- {
- Program p = new Program();
- Console.WriteLine(p.Max(12.1, 12.2));
- Console.WriteLine(p.Max(1.2, 2.0f));
- Console.WriteLine(p.Max(1,2.0f));
- }
- }
What should the output be now? Turns out that the extension method is never invoked.
Instance methods are always given first preference, including methods that could be a unique match by implicitly coercing types. Comment out the generic instance method and the 3rd call to Max goes to Max(double,double) instead of the extension method.
- public static class Extensions
- {
- public static T Max<T>(this Program p, T t1, T t2) where T : IComparable<T>
- {
- Console.WriteLine("In Extension Method Max<T,T>");
-
- return t1.CompareTo(t2) > 0 ? t1 : t2;
- }
- }
-
- public class Program
- {
- public double Max(double d1, double d2)
- {
- Console.WriteLine("In Max(double,double)");
- return Math.Max(d1, d2);
- }
-
- public int Max(int i1, int i2)
- {
- Console.WriteLine("In Max(int,int)");
- return Math.Max(i1, i2);
- }
-
- static void Main(string[] args)
- {
- Program p = new Program();
- Console.WriteLine(p.Max(12.1, 12.2));
- Console.WriteLine(p.Max(1.2, 2.0f));
- Console.WriteLine(p.Max(1,2.0f));
- }
- }
Happy overloading...