Tech Point Fundamentals

Sunday, November 28, 2021

Method Overloading and Overloading Dilemma C# - Part 2

Method Overloading and Overloading Dilemma C# - Part 2

method-overloading-part2

This is the continuation of the "Method Overloading and Overloading Dilemma" article series. This is the second last part of this series. Please read the previous part by below link:


I will recommend you to please read the first part of this article before continuing this second part here.




Introduction


Till now we have seen below Overloading Dilemmas in Part-1:

  1. Case 1: Implicit Conversion and Overloading Rule
  2. Case 2: Implicit Boxing and Overloading Rule
  3. Case 3: Dynamic Parameter and Overloading Rule
  4. Case 4: Object Parameter and Overloading Rule
  5. Case 5: Optional Parameter and Overloading Rule


In this part, we will continue with Case 6.



Case 6. Named Argument and Overloading Rule


Let's understand the named argument a little bit here before jumping on the overloading rule:

Named Argument:


    1. The Named Argument is another interesting and important feature introduced in C# 4 along with the optional parameters. 
    2. Named Arguments enables you to specify an argument for a parameter by matching the argument with its name rather than with its position in the parameter list. 
    3. Named Argument is very helpful in the case of a method having optional parameters or having too many parameters.
    4. Named parameters can be used with methods, indexers, constructors, and delegates.
    5. When you use named and optional arguments, the arguments are evaluated in the order in which they appear in the argument list, not the parameter list.
    6. Named arguments free you from matching the order of parameters in the parameter lists of called methods. 
    7. The parameter for each argument can be specified by parameter name. Named and optional parameters enable you to supply arguments for selected parameters only. 
    8. It can be used to effectively reduce the set of applicable function parameters by ruling out ones that have the "wrong" parameter names.



Named Argument Effects on Overloading Rule:


When you use a named argument to call the overloaded method, it will check the method having the same argument name, rather than type first, if it is compatible it will be bound at compile time otherwise an error will be generated at compile time itself.

Obviously, this technique only works if the parameters in the methods have different names though. So the name of the parameter matters here.

Now let's consider the previous example of Case 5E (Optional Parameters and Overloading Ambiguity Rule) again, I have just changed the argument name (a) for the first method instead of (x). 

case-6



Why we are not getting the ambiguity error here like Case 5E?

Though I am passing the single int type argument (x: 6) and (a: 5) this time we are not getting any ambiguity error here like in Case 5E

The reason behind this is that we have passed the named argument, and the compiler has to bind the method if there is any with the matching name only. That's why for the first call it has bound the second method and for the second call, it has bound the first method, though both have optional parameter overheads.

Let's verify that by the IL code  for the above example:

case6-ilcode



You can clearly see for the first call (x: 6) compiler has bound the method having argument name x (second method) and for the second call (a: 5) the method having argument name as "a" (first method).


But still, if you call the method without a named argument (Examle6 (5)), you will get the ambiguity error same as the Case 5E here also.



Case 6A: Named Argument and Overloading Collision Rule


Now let's pass the second parameters also as named argument.

case-6a



Why we are getting compile-time errors for the second named parameter here?

For all the method calls in the above example, till the first parameter (x: 6 and a: 5 ) the compiler is fine because it is matching with both methods' definitions. But the problem occurs only with the second argument ( b: 4 and y: 8) because there is no matching method with the second-named argument. So finally the compiler generates a specific compile-time error here:

Error CS1739 The best overload for 'Example6A' does not have a parameter named 'b'
Error CS1739 The best overload for 'Example6A' does not have a parameter named 'y'



Case 7. PARAMS Parameter and Overloading Rule


In C# the params keyword is used to specify a parameter that takes a variable number of arguments. Let me brief something more about the param keyword here:

PARAMS Parameter:


  1. The params parameter type must be a single-dimensional array otherwise you will get a compile-time error. It is an optional parameter and a final parameter.
  2. No additional parameters are permitted after the params keyword in a method declaration. So the params parameter must be the last parameter in the method definition.
  3. Only one params type parameter is permitted in any method definition.
  4. Since the params parameter is an optional parameter, if no argument is passed, the length of the params list is always zero by default.
  5. For the params parameter, you can pass either a comma-separated (CSV) list of arguments or an array of the type specified or a single value itself.

case-7


In the above example, for the first call i.e Example7(10) the method taking the params type parameter is called because it beats from the first method taking double type parameter for the conversion of int type to double. though the compiler has to create the array and then pass it to the method.

If you see the IL code for the above example, a new array is created of type int for value (10) and (20, 30) and then the method taking params parameter (int []) is bound at compile time.

case-7-ilcode





Case 7A: Default Optional Parameter vs Params Optional Parameter and Overloading


Let's tweak the above example a little bit and get rid of the double type parameter and add an optional parameter.

case-7a



In the above example, for the first call i.e Example7A(10) the first method taking the optional int type parameter i.e Example7A(int a, int b = 0) is wins because it beats from the second method taking params type parameter due to creating an array for the second method.

However, if you remove the first method, the second method taking params parameter is called for the first call. If you remove the optional parameter from the first method, still it will be invoked by the first call i.e Example7A(10).



Case 7B: Implicit Optional Array vs Implicit Optional Value and Overloading


Now let's tweak the above example a little bit again and add one mandatory parameter in the second method taking optional params type parameter also.

case-7b


In the above example, for the first call i.e. Example7B(20)  the first method wins for the second optional parameter from the params type parameter from the second method. 

Here for the third call i.e Example7B(20, 30, 40), the first argument is assigned to x and others are to y. 

For the last call Example7B(x: 60) an empty array is assigned to the y parameter. 



Case 8. Reference Parameters and Overloading Rule


In C# REF, OUT and IN are known as reference parameters that are used for passing an argument by reference.

Pass by reference and reference type are totally different things. These two concepts are not the same. Please watch the Pass by Reference vs Reference Type video here. 

I will post separate articles on these topics later but for now, let me brief them shortly:

REF Parameter Fundamentals:


  1. A method parameter can be modified by REF regardless of whether it is a value type or a reference type. There is no boxing of a value type when it is passed by reference.
  2. An argument that is passed to a REF parameter must be initialized before it is passed.
  3. To use a REF parameter, both the method definition and the calling method must explicitly use the REF keyword.
  4. REF is used to state that the parameter passed may be modified by the method. So the REF parameter may or may not be modified by the called method.

Please watch the REF vs OUT vs IN video here.



OUT Parameter Fundamentals:


  1. Variables passed as OUT arguments do not have to be initialized before being passed in a method call. 
  2. The OUT is used to state that the parameter passed must be modified by the called method. So the called method is required to assign a value before the method returns.
  3. To use an OUT parameter, both the method definition and the calling method must explicitly use the OUT keyword. 
  4. The OUT keyword can also be used with a generic type parameter to specify that the type parameter is covariant.

Please read more about Covariance here.

IN Parameter Fundamental:


  1.  The IN parameter modifier is only available in C# 7.2 and later.  
  2. The IN keyword causes arguments to be passed by reference but ensures the argument is not modified. So the IN keyword is used to state that the parameter passed cannot be modified by the called method.
  3. Variables passed as IN arguments must be initialized before being passed in a method call like REF.
  4. The IN keyword can also be used with a generic type parameter to specify that the type parameter is contravariant.
  5. To specify the IN for arguments at the caller site is typically optional unlike REF and OUT.  It is only required in the method declaration. 
  6. However, you can explicitly add the IN modifier at the caller site to ensure the argument is passed by reference, not by value. It enforces the compiler to select a method defined with a matching IN parameter. Otherwise, when two methods differ only by the IN, the call by value overload method will be a better match hence will win.

Please read more about the Contravariance here.



Although IN, OUT, and REF parameter modifiers are considered part of a method signature, but methods of a class can't have signatures that differ only by the REF, OUT, or IN type parameter.

Therefore, methods cannot be overloaded if the only difference is that one method takes an OUT or IN argument and the other takes a REF argument.

Because REF, OUT, and IN are treated differently at run-time but they are treated as the same at compile time (CLR doesn't differentiate between the two while it created IL code for REF, OUT, and IN). 

However, methods can be overloaded when one method has a REF, IN, or OUT parameter and the other has a parameter that is passed by value.


case-8


Why compiler is generating the compile-time error for the second overloaded method?

The above two methods are identical in terms of compilation by the CLR. that's why it is getting a compile-time error:

Error CS0663 'Program' cannot define an overloaded method that differs only on parameter modifiers 'out' and 'ref'



Case 8A: REF vs OUT Parameter and Overloading


Let me fix this issue for the compiler and see the IL code generated by the compiler.

case-8a



The compiler decided the best match based on the ref and out modifier here. But let's see the IL code for the above example:

case-8a-ilcode


In the above IL code, you can see that the type for both REF and OUT is the same i.e pointer to Int32 (int32&). That is the reason for the compile-time error in the previous Case 8.



Case 8B: REF vs OUT vs IN Parameters and Overloading


Now let's introduce the IN modifier also in our journey.

case-8b


Why compiler is generating the compile-time error for the second and third overloaded methods again?

All the above methods are identical in terms of compilation by the CLR. that's why you are getting a compile-time error again:

Error CS0663 'Program' cannot define an overloaded method that differs only on parameter modifiers 'out' and 'ref'
Error CS0663 'Program' cannot define an overloaded method that differs only on parameter modifiers 'in' and 'ref'



Case 8C: IN Reference Type Parameter vs Value Type Parameter and Overloading 


Now let me do a favor for the compiler in order to resolve this issue. 

case-8c


Ohh! What happens to the compiler. If specifying IN keyword at the caller side is optional then why it is not binding the second method for the second call i.e Example8C(value)?

Before going to that discussion, let's check the IL code for the above example:

case-8c-ilcode


First, if you see the IL code, for the IN type parameter also the compiler has used the pointer to Int32 (int32&) same like ref and out, that's why we were getting compile-time error in the previous Case 8B.

Second, if you specify the IN modifier explicitly at the caller side, only then the compile is forcefully binding to the matching method having IN parameter, otherwise, it will bind the method having value type because it beats the method having reference type. (Point No 6 of the IN fundamentals). 

For the third and fourth call, the compiler is passing memory address instead of value (ldloca.s - means Load address of the local variable with index indx, short form.). For the fourth call, the named argument rule is applied here.

However, if you remove the first method taking int value parameter, the second method having IN parameter will be called instead for all the cases.



Case 9. Return Types and Overloading Rule


The return type is an obvious part of the method signature. But the return type of a method is not considered to be part of a method's signature in case of overloading, because an overload is determined before the compiler checks whether or not the return type will cause an error in the wider context of the method call.

case-9


Hence, we are getting the below compile-time error for the above-overloaded methods:

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



Case 9A: Return Type Checking Precedence and Overloading


Let me tweak the above code and see the different scenarios of the return type.

case-9a


Here for the first call i.e result1, the first overloaded method i.e string Example9A(int x) is chosen and then the compiler works out that it cannot assign a string to a variable of type int That's why the compiler is generating the specific compile-time error:

Error CS0029 Cannot implicitly convert type 'string' to 'int'

Similarly, for the second call i.e result2the second overloaded method i.e int Example9A(double y) is chosen and then the compiler works out that it cannot assign an int to a variable of type string That's why the compiler is generating the compile-time error:

Error CS0029 Cannot implicitly convert type 'int' to 'string'

This proves that overloading is resolved before the return type is checked by the compiler.

For the third call, the compiler is generating an error because the compiler cannot do a casting implicitly from double to int.

Error CS1503 Argument 1: cannot convert from 'double' to 'int'

If you comment out the first three calls, you will get the output from the program:

case-9a-result


For the first call i.e result1Y, there is no error because we have used a named parameter. For the last two calls, we are not getting any error because we have used var type instead of any fixed data type explicitly.



Case 10. Method Modifiers and Overloading Rule


There are multiple method modifiers like static, sealed, virtual, abstract, partial, etc. These are not considered as a part of the method signature. So you cannot overload two methods based on these method modifiers.

case-10


In the above example since both overloaded methods are only based on the method modifiers i.e static vs instance method. That's why the compiler is generating a compile-time error:

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




Case 10A: Static vs Non-Static Modifiers and Overloading


A method declared with the static modifier is known as a static method. A static method can only access static data members. 

Static data members and methods can only be accessed by the class name itself, they cannot be accessed by the object of the type.

Please read more about the static method here.

Overloading is allowed for the static methods but two methods cannot be overloaded only based on the static and non-static modifiers.

Now let me help out the compiler to resolve the above issue by tweaking the above code. 

case-10a


Why compiler is generating the error while calling the overloaded methods for the second and third calls?

In the above example for the second call i.e Example10A(20.5) compiler found the second method as the best-overloaded method. Since this method is an instance method, it requires the object of the class in order to call this method. That's why it is generating an error:

Error CS0120 An object reference is required for the non-static field, method, or property 'Program.Example10A(double)'

 Similarly for the third call i.e new Program().Example10A(30)  compiler found the first method as the best-overloaded method. Since this method is a static method, it requires the class name in order to call this method. That's why it is generating an error:

Error CS0176 Member 'Program.Example10A(int)' cannot be accessed with an instance reference; qualify it with a type name instead

You will get the above errors till C# 7.2. However, you will get a compile-time error only for the second call in C# 7.3 and above versions of C# not for the third call.




Case 10B: Dynamic vs Numeric Parameter and Overloading in Static vs Instance Method


Let me solve the above problem by using dynamic type instead of static data type.

case-10b


In the above example compiler is implicitly converting the value 10 to double for the best-matched method.   



Case 10C: Virtual vs Non-Virtual Method and Overloading


Virtual methods are used to implement dynamic binding in C#. It allows the derived class to override the base class method definitions in the derived class.

Please read more about the virtual method here.

Overloadings are allowed for virtual methods also but two methods cannot be overloaded only based on the virtual and non-virtual method modifiers. 

case-10c


Hence, we are getting a compile-time error here:

Error CS0111 Type 'Program' already defines a member called 'Example10C' with the same parameter types




Case 10D: Abstract vs Concrete Method and Overloading


A method without body definition and having an "abstract" modifier is known as an abstract method. Until C# 7 only abstract classes can have abstract methods, but with the introduction of C# 8 interfaces can also have abstract members. 

The inheriting class must have to implement the abstract members with an override keyword because an abstract method is virtual implicitly.

Please read more about the abstract method here.

Abstract methods can be overloaded but two methods cannot be overloaded only based on the abstract and concrete method modifiers. 

case-10d


Hence, we are getting a compile-time error here also:

Error CS0111 Type 'Program.Core' already defines a member called 'Example10D' with the same parameter types




Case 10E: Sealed vs Non-Sealed Method and Overloading


A method modified by the "sealed" keyword is known as a sealed method. A sealed method is used to define the overriding level of a virtual method in the inheritance hierarchy

The sealed keyword must be used for a method to prevent it from being overridden in the derived class. The sealed methods in C# cannot be overridden further, but they must be used with an override keyword in the method. 

Please read more about the sealed method here.

Two methods cannot be overloaded only based on the sealed and normal non-sealed method modifiers also. 

case-10e


Hene, you are getting a compile-time error here also:

Error CS0111 Type 'Program.Client' already defines a member called 'Example10E' with the same parameter types




Case 10F: Partial vs Non-Partial Method and Overloading


In C# a method defined with the keyword partial is known as a partial method. A partial method is a way to split any method into two separate code files of partial types.  

Let me brief something about the partial method first here before going to the overloading rule:

  1. A partial method can only be created in partial types (partial class, partial struct, or partial interface).
  2. The return type of a partial method must be void.
  3. You cannot specify any access modifiers explicitly even private to a partial method, they are private implicitly by default.
  4. Both declaration and implementation of a partial method must use the partial keyword. Method signatures in both parts of the partial type must match.
  5. You cannot provide the definition along with the implementation for a partial method. Both definition and implementation of a partial method must be in separate partial type files or in the same partial type but separately.

Please read more about the partial method here.

Method overloading is allowed for partial methods also but two methods cannot be overloaded only based on the partial modifier.

case-10f



Hence, for the above example, we are getting the below compile-time error:

Error CS0111 Type 'Program.Case10FExample' already defines a member called 'Example10F' with the same parameter types




Case 10G: Action Methods and Overloading


Action methods are the public action methods of any controller which is not decorated by the [NonAction] attribute. Action methods cannot be static.

In both Web API and MVC, action methods cannot be overloaded based on parameters similar to other methods overloading in C#, because overloading or polymorphism is a feature of OOPS programming languages like C# and Java which is understandable only by the compiler or CLR. 

The HTTP client or web browser, cannot understand the polymorphic behavior of the language directly. So there are different ways to overload any action method:

  1. By using different HTTP Verbs
  2. By using the [ActionName] attribute
  3. By using the [NonAction] attribute
  4. By using attribute routing

I have already posted a video on this. Please watch the action methods overloading video here.



Examples


 




No comments:

Post a Comment

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