Tech Point Fundamentals

Sunday, November 21, 2021

Method Overloading and Overloading Dilemma C# - Part 1

Method Overloading and Overloading Dilemma C# - Part 1

method-overloading-part1

Method Overloading is a salient feature in Object-Oriented Programming (OOPS). Method overloading is a form of polymorphism in C#.  In the previous article, we have learned about Static Polymorphism and Types of Overloading

Please read the Static Polymorphism article here

In this article, we will walk through the method overloadings and overloading dilemmas in C#. This is the most common topic for the online test in the interview. I will recommend you to please read our previous article on Static Polymorphism here.

This article is divided into three parts and each part contains 5 different cases. This article will cover only the first part here.





Method Overloading


The process of creating more than one method in a class with the same name or creating a method in a derived class with the same name as a method in the base class but with a different signature is known as method overloading.

The signature of a method does not include the return type, access modifiers, and method modifiers. So two methods must be different in terms of the type of parameter, number of parameters, order of parameter, or kind of parameter.

Method Overloading is also known as compile-time binding, early binding, static binding, static polymorphism, or compile-time polymorphism.



Method Overloading Dilemmas


The compiler decides which one to pick based on the better function member rules which look at what conversions are involved in going from each argument to the corresponding parameter type.

In Overloading, the compiler interacts with different things like type inference, implicit conversions, boxingvariance, subtyping, and polymorphism, etc.

Sometimes different parameter modifiers like REF, OUT, IN, PARAMSDynamic, Object, and Optional Parameters, or Named Parameter may affect the compiler to decide the best matching method.

There are multiple rules to say which conversion is better than the other. Based on these rules, sometimes there might be ambiguity for the compiler to decide which one to call.

For each and everything which compiler do in order to match the best suitable method to call involves cost and precedence that decide which method is going to win in the race condition.

Now, things can get a little bit confusing sometimes when it comes to resolving overloads. In this article, I will point out some of the gotchas you might run into while overloading a method in C#.



Case 1: Implicit Conversion and Overloading Rule


Conversion is a way to convert a small data type to a larger data type. Conversion is done by the compiler automatically. Please watch the conversion vs casting video here.

Sometimes the compiler performs the implicit conversion for matching the best suitable overloaded method. Now in this case compiler will bind that method that has a better conversion.

case-1



In the above example, for Example1(20) a conversion from an expression of type int to int (short) is better than a conversion from int to double, so the first method Example1(int x) wins here. 

If you see the IL code of the above example, the value 20 is converted as short int (ldc.i4.s means Push num onto the stack as int32, short form), and then it is loaded onto the stack. You can also see that compiler has bound the first method taking int as the parameter for it. 

case-1-ILCode


But if you remove the method taking an 'int' parameter i.e Example1(int x), the method taking a 'double' would be called instead.



Case 1A: Numeric vs Alphanumeric Conversion and Overloading


We can convert any numeric data type to a stringThis conversion cannot be done by the compiler automatically. Please watch the ToString () vs Convert.ToString() video here.

case-1a


In the above example, for Example1A(20) a conversion from an expression of type int to double is better than a conversion from int to string because it cannot be done by the compiler implicitly, so the second method Example1A(double x) wins here.

If you see the IL code of the above code, for the int value 20, it is converted to double (float 64), and then it is loaded onto the stack (ldc.r8 means Push num of type float64 onto the stack as F.). The compiler has bound the method taking double type parameter for this call. 

case-1a-ILCode





Case 2. Implicit Boxing and Overloading Rule


Boxing is a way to convert a value type to object type (reference type) or interface type. Boxing can be done by the compiler implicitly also. Please watch the boxing vs unboxing video here

Sometimes the compiler performs the implicit boxing for matching the suitable overloaded method. Now in this case compiler will bind that method that has a better conversion or boxing.

case-2



In the above example, for Example2("Tech Point") a conversion from an expression of type string to string is better than a conversion from string to object, so the first method taking string parameter wins here.  

But if you remove the method taking a 'string' parameter i.e Example2(string x), the method taking an 'object' would be called instead.

If you see the IL code boxing is happening for the values 20 and 20.5 because they are value types.  

case-2a-ILCode





Case 3. Dynamic Parameter and Overloading Rule


Dynamic types are introduced in C# 4.0 with the .Net 4.5The compiler compiles dynamic types into object types in most cases. So internally a dynamic type is an object type. 

Though the object type is a static type itself, an object of type dynamic bypasses static type checking at compile time. 

So you cannot assign any default value explicitly to a dynamic type parameter other than null. You will get the below error if you try to set any default value to the dynamic parameter x:

Error CS1763 'x' is of type 'dynamic'. A default parameter value of a reference type other than string can only be initialized with null

Conversions between dynamic objects and other types are very easy which enables the developer to switch between dynamic and non-dynamic behavior.

In this case overload resolution occurs at run time instead of at compile time if one or more of the arguments in a method call has the type dynamic. But it might be possible that the overload resolution may fail at run time.

case-3



In the above example, for Example3(20) a conversion from an expression of type int to int is better than boxing conversion from int to dynamic (object), so the first method taking the int parameter wins here. 

If you see the IL code the type or the dynamic type is an object, so boxing is happening for the values 20.5 because it is a value type.  

case-3-ILCode



But if you remove the method taking an 'int' parameter i.e Example3(int x), the method taking a 'dynamic' would be called instead.



Case 3A: Numeric Conversion vs Boxing and Overloading


The compiler can convert any data type to the dynamic(object) type. But the precedence rule is applied whether a conversion is better or boxing.

case-3a



In the above example, a conversion from an expression of type int to double is better than a boxing conversion from int to dynamic (object), so the first method taking the double parameter wins here. 

If you see the IL code of the above example the dynamic type is an object type internally.  Also for the values 20 and 20.5, there is no boxing at all but there is an implicit conversion (ldc.r8for the value 20.

case-3a-ILCode





Case 4. Dynamic vs Object Parameter and Overloading Rule


In C# the object type is the super base type (class) for all the data types. So any data type whether it is a value type or reference type can be assigned to the object type.

We have seen in Case 3 that the dynamic type uses the type object internally.  So now I am curious to know what will happen if we use dynamic and object type parameters to overload two methods.

case-4


Although the overload resolution occurs at run time for the dynamic type, the compiler uses the object type for the method signature at compile time itself. So finally the compiler is generating the below errors:

Error     CS0111 Type 'Program' already defines a member called 'Example4' with the same parameter types
Error CS0121 The call is ambiguous between the following methods or properties: 'Program.Example4(object)' and 'Program.Example4(dynamic)'

So two methods cannot be overloaded only based on the dynamic and object type parameters.



Case 5. Optional Parameter and Overloading Rule (Tie Breaking Rule - Ambiguity)


Optional parameters are also introduced in C# 4 which allow a method to declare a default value for some or all of its parameters. The caller can then omit the corresponding arguments if they want to use the default values. Let's understand the optional parameters a little bit here:

Optional Parameter:


    1. The definition of a method, constructor, indexer, or delegate can specify its parameters are mandatory or optional. 
    2. Any call must provide arguments for all required parameters, but can omit arguments for optional parameters.
    3. Each optional parameter has a default value as part of its definition. If no argument is sent for that parameter, the default value is used, otherwise, the value which is passed explicitly.
    4. All the optional parameters are defined at the end of the parameter list i.e only after all the required parameters. 
    5. You can also declare optional parameters by using the .NET Optional attribute class. But Optional attribute parameters do not require a default value.
    6. If the caller provides an argument for any one of a succession of optional parameters, it must provide arguments for all preceding optional parameters. Or the caller has to use a named argument.
    7. Comma-separated gaps in the argument list aren't supported. IntelliSense uses brackets to indicate optional parameters.



Optional Parameter Effects on Overloading Rule:


This affects overload resolution as there may be multiple methods with a different number of parameters that are all applicable. This will lead to ambiguity errors most of the time.

When the compiler is faced with a choice between a method that requires the compiler to fill in the optional parameter values and one which doesn`t, if the methods are there otherwise "tied", and overload resolution will pick the one where the caller has specified all the arguments explicitly. In this case, normal argument conversion will not be decided a winner.

"Optional Parameters always take precedence over Implicit Type Conversion when deciding which method definition to bind"

case-5


If the caller can omit the optional parameter while calling the method taking optional parameters, why we did not face the ambiguity error here like in Case 4?

In the above example, for the first i.e. Example5(20) and second call i.e. Example5("Tech  Point"), the method taking dynamic parameter i.e Example5(dynamic x) is called, because it wins for the additional assignment to be passed as an optional parameter for the method taking object parameter i.e Example5(object x, int y = 5).    

If you see the IL code of the above example, for the first and second call compiler has bound it to the second method taking a dynamic parameter. Please note here that the dynamic parameter also uses the object type internally. 

case-5-ILCode





Case 5A: Explicit vs Implicit Optional Parameter Assignment and Overloading


Now let's change the optional parameter location from the first method to the second method and see what is happening.

case-5a



In the above example, for the same calls, the method taking object parameter i.e Example5A(object x) is called because it wins for the additional assignment to be passed as an optional parameter for the method taking dynamic parameter i.e Example5(dynamic x, int y = 5).  

If you see the IL code of the above example, boxing is happening for the first call and the compiler has bounded the first and second call to the first method taking object parameter.

case-5a-ILCode




Case 5B: Implicit Conversion vs Implicit Optional Parameter Assignment and Overloading


Moving forward, let's add a method taking a more precise type of parameter compared to dynamic and object

case-5b



In the above example, for the value 20 and 20.5 calls, the method taking double parameter i.e Example5B(double x) is called because it wins from the second method for converting values to object type and from the last method for the additional assignment to be passed as an optional parameter.  



Case 5C: Value Type with Optional Parameter and Overloading


Now let's get rid of the boxing types like object and dynamic parameters. Let's use some numeric data types and see how they are going to behave in case of overloading with optional parameters. 

case-5c


If both the methods are having the same type (int) parameter, then why the compiler is calling only the second method for the first call (20)?

In the above example, though both methods are taking the same type of parameters and we know that optional parameters can be omitted by the caller, still for the first call Example5C(10) it is calling the method taking int parameter only i.e 
Example5C(int x) because it wins compare to the second method due to the optional parameter to be passed as an optional parameter by the compiler.  

Here for the second method, the compiler would need to fill in the default argument for the parameter "y" using the default value - whereas the first method doesn't require this, that's why it wins in this race condition. 



Case 5D: Numeric Conversion vs Default Assignment and Overloading


Now let's make the above example more interesting by introducing a double type parameter.

case-5d


Ohh!!! What happens to the compiler here! 
Why the compiler is binding the second method taking optional parameter for the value 20?

In the above example, for the value 20, the method taking int parameter i.e Example5D(int x, int y = 10) is called because it wins from the first method for converting value 20 to double type compared to passing a default parameter.  

When considering the second method, the compiler would need to fill in the argument for the parameter "y" using the default value - whereas the first method doesn't require this but it needs the int value to be converted into doubleThis time the method with the optional parameter is used because the int to int conversion is preferred over the int to double one.



Case 5E: Optional Parameters and Overloading Ambiguity Rule


We have understood the compiler's way of working for optional parameters.  Now the time has come, to put the compiler in a dilemma.

Let me introduce optional parameters in both methods.

case-5e



Ohh!!! What happens to the compiler here! Why it is failing to bind any method for the first call i.e Example5E(5)?

In the above example, for Example5E(5) call, the compiler is confused between the two methods which one to bind with this call because both methods have the same parameter types and in both the case compiler has to assign and pass the optional parameter explicitly, that's why it is generating compile-time ambiguity error:

Error CS0121 The call is ambiguous between the following methods or properties: 'Program.Example5E(int, int)' and 'Program.Example5E(int, int, int)'

This call is ambiguous, because the one argument which has been given in the call (5) is fine for both methods, and both require extra arguments which would be filled in from default values by the compiler.

Just to be clear, this tie-breaking only comes in after the methods have been compared to each other using the C# 4 rules.

Although there is one difference between these two method bindings that for the second method compiler has to fill multiple default values (for both y and z), still it is ignored by the compiler because they are the same level of priority rule i.e explicit optional parameter assignment, doesn't matter how many.



Case 5X: Multiple Parameters and Overloading Rule


When there are multiple parameters involved in overloaded methods, for one method to "beat" another one it has to be at least as good for each parameter, and better for at least one parameter. 

This is done on a method-by-method and parameter-by-parameter comparison: a method doesn't have to be better than all other methods for any single parameter. If no method wins outright, the compiler will report an ambiguity error.

case-5x


In the above example, for the first call i.e Example5X(5, 10) the first method i.e Example5X(int x, int y) wins because it beats the second method on the second parameter and the third method on the first parameter due to implicit conversion of int to double type.



Case 5Y: Multiple Parameters and Overloading Ambiguity Rule


Now let's complicate the scenario for the compiler by removing the first method.

case-5y



Ohh! What happens to the compiler here again! If the compiler is free for implicit conversion, then why does it is failing to bind any method for the first call?

Well, in the above example, for the first call i.e Example5Y(5, 10) the compiler has checked and compared both the methods one by one and found that both require implicit conversion of int type to double for at least one argument. So in this overloading race condition, no one wins that's why the compiler generates the compile-time error: 

Error CS0121 The call is ambiguous between the following methods or properties: 'Program.Example5Y(int, double)' and 'Program.Example5Y(double, int)'




Examples


 


Live Demo


To Be Continued...Part-2...



Recommended Articles


Abstract Class Vs Interface in C# 8
Static Class and Methods in C# 
Constructor and Types of Constructors in C# 
Dynamic Polymorphism C#
Interface Virtual Method in C#
Method Overriding in C#




Thanks for visiting this page. Please follow us on Twitter, Facebook, LinkedIn, Telegram, Youtube, and Quora for regular updates.

No comments:

Post a Comment

Please do not enter any HTML. JavaScript or spam link in the comment box.