Method Overloading


In C#, two or more methods within the same class can share the same name, as long as their parameter declarations are different. When this is the case, the methods are said to be overloaded, and the process is referred to as method overloading. Method overloading is one of the ways that C# implements polymorphism.

In general, to overload a method, simply declare different versions of it. The compiler takes care of the rest. You must observe one important restriction: the type and/or number of the parameters of each overloaded method must differ. It is not sufficient for two methods to differ only in their return types. They must differ in the types or number of their parameters. (Return types do not provide sufficient information in all cases for C# to decide which method to use.) Of course, overloaded methods may differ in their return types, too. When an overloaded method is called, the version of the method executed is the one whose parameters match the arguments.

Here is a simple example that illustrates method overloading:

 // Demonstrate method overloading. using System; class Overload {   public void ovlDemo() {     Console.WriteLine("No parameters");   }   // Overload ovlDemo for one integer parameter.   public void ovlDemo(int a) {     Console.WriteLine("One parameter: " + a);   }   // Overload ovlDemo for two integer parameters.   public int ovlDemo(int a, int b) {     Console.WriteLine("Two parameters: " + a + " " + b);     return a + b;   }   // Overload ovlDemo for two double parameters.   public double ovlDemo(double a, double b) {     Console.WriteLine("Two double parameters: " +                        a + " " + b);     return a + b;   } } class OverloadDemo {   public static void Main() {     Overload ob = new Overload();     int resI;     double resD;     // call all versions of ovlDemo()     ob.ovlDemo();     Console.WriteLine();     ob.ovlDemo(2);     Console.WriteLine();     resI = ob.ovlDemo(4, 6);     Console.WriteLine("Result of ob.ovlDemo(4, 6): " +                        resI);     Console.WriteLine();     resD = ob.ovlDemo(1.1, 2.32);     Console.WriteLine("Result of ob.ovlDemo(1.1, 2.32): " +                        resD);   } }

This program generates the following output:

 No parameters One parameter: 2 Two parameters: 4 6 Result of ob.ovlDemo(4, 6): 10 Two double parameters: 1.1 2.32 Result of ob.ovlDemo(1.1, 2.32): 3.42

As you can see, ovlDemo( ) is overloaded four times. The first version takes no parameters, the second takes one integer parameter, the third takes two integer parameters, and the fourth takes two double parameters. Notice that the first two versions of ovlDemo( ) return void, and the second two return a value. This is perfectly valid, but as explained, overloading is not affected one way or the other by the return type of a method. Thus, attempting to use these two versions of ovlDemo( ) will cause an error:

 // One ovlDemo(int) is OK. public void ovlDemo(int a) {   Console.WriteLine("One parameter: " + a); } /* Error! Two ovlDemo(int)s are not OK even though     return types differ. */ public int ovlDemo(int a) {   Console.WriteLine("One parameter: " + a);   return a * a; }

As the comments suggest, the difference in their return types is an insufficient difference for the purposes of overloading.

As you will recall from Chapter 3, C# provides certain automatic type conversions. These conversions also apply to parameters of overloaded methods. For example, consider the following:

 /* Automatic type conversions can affect    overloaded method resolution. */ using System; class Overload2 {   public void f(int x) {     Console.WriteLine("Inside f(int): " + x);   }   public void f(double x) {     Console.WriteLine("Inside f(double): " + x);   } } class TypeConv {   public static void Main() {     Overload2 ob = new Overload2();     int i = 10;     double d = 10.1;     byte b = 99;     short s = 10;     float f = 11.5F;     ob.f(i); // calls ob.f(int)     ob.f(d); // calls ob.f(double)     ob.f(b); // calls ob.f(int) -- type conversion     ob.f(s); // calls ob.f(int) -- type conversion     ob.f(f); // calls ob.f(double) -- type conversion   } }

The output from the program is shown here:

 Inside f(int): 10 Inside f(double): 10.1 Inside f(int): 99 Inside f(int): 10 Inside f(double): 11.5

In this example, only two versions of f( ) are defined: one that has an int parameter and one that has a double parameter. However, it is possible to pass f( ) a byte, short, or float value. In the case of byte and short, C# automatically converts them to int. Thus, f(int) is invoked. In the case of float, the value is converted to double and f(double) is called.

It is important to understand, however, that the automatic conversions apply only if there is no direct match between a parameter and an argument. For example, here is the preceding program with the addition of a version of f( ) that specifies a byte parameter:

 // Add f(byte). using System; class Overload2 {   public void f(byte x) {     Console.WriteLine("Inside f(byte): " + x);   }   public void f(int x) {     Console.WriteLine("Inside f(int): " + x);   }   public void f(double x) {     Console.WriteLine("Inside f(double): " + x);   } } class TypeConv {   public static void Main() {     Overload2 ob = new Overload2();     int i = 10;     double d = 10.1;     byte b = 99;     short s = 10;     float f = 11.5F;     ob.f(i); // calls ob.f(int)     ob.f(d); // calls ob.f(double)     ob.f(b); // calls ob.f(byte) -- now, no type conversion     ob.f(s); // calls ob.f(int) -- type conversion     ob.f(f); // calls ob.f(double) -- type conversion   } }

Now when the program is run, the following output is produced:

 Inside f(int): 10 Inside f(double): 10.1 Inside f(byte): 99 Inside f(int): 10 Inside f(double): 11.5

In this version, since there is a version of f( ) that takes a byte argument, when f( ) is called with a byte argument, f(byte) is invoked and the automatic conversion to int does not occur.

Both ref and out participate in overload resolution. For example, the following defines two distinct and separate methods:

 public void f(int x) {   Console.WriteLine("Inside f(int): " + x); } public void f(ref int x) {   Console.WriteLine("Inside f(ref int): " + x); }

Thus,

 ob.f(i)

invokes f(int x), but

 ob.f(ref i)

invokes f(ref int x).

Although ref and out participate in overload resolution, the difference between the two alone is not sufficient. For example, these two versions of f( ) are invalid:

 // Wrong! public void f(out int x) { // ... public void f(ref int x) { // ...

In this case, the compiler cannot differentiate between the two versions of f( ) simply because one uses an out int parameter and the other uses a ref int parameter.

Method overloading supports polymorphism because it is one way that C# implements the “one interface, multiple methods” paradigm. To understand how, consider the following. In languages that do not support method overloading, each method must be given a unique name. However, frequently you will want to implement essentially the same method for different types of data. Consider the absolute value function. In languages that do not support overloading, there are usually three or more versions of this function, each with a slightly different name. For instance, in C, the function abs( ) returns the absolute value of an integer, labs( ) returns the absolute value of a long integer, and fabs( ) returns the absolute value of a floating-point value. Since C does not support overloading, each function has to have its own name, even though all three functions do essentially the same thing. This makes the situation more complex, conceptually, than it actually is. Although the underlying concept of each function is the same, you still have three names to remember. This situation does not occur in C#, because each absolute value method can use the same name. Indeed, C#’s standard class library includes an absolute value method, called Abs( ). This method is overloaded by C#’s System.Math class to handle the numeric types. C# determines which version of Abs( ) to call based upon the type of argument.

A principal value of overloading is that it allows related methods to be accessed by use of a common name. Thus, the name Abs represents the general action that is being performed. It is left to the compiler to choose the right specific version for a particular circumstance. You, the programmer, need only remember the general operation being performed. Through the application of polymorphism, several names have been reduced to one. Although this example is fairly simple, if you expand the concept, you can see how overloading can help manage greater complexity.

When you overload a method, each version of that method can perform any activity you desire. There is no rule stating that overloaded methods must relate to one another. However, from a stylistic point of view, method overloading implies a relationship. Thus, while you can use the same name to overload unrelated methods, you should not. For example, you could use the name sqr to create methods that return the square of an integer and the square root of a floating-point value. But these two operations are fundamentally different. Applying method overloading in this manner defeats its original purpose. In practice, you should only overload closely related operations.

C# defines the term signature, which is the name of a method plus its parameter list. Thus, for the purposes of overloading, no two methods within the same class can have the same signature. Notice that a signature does not include the return type since it is not used by C# for overload resolution. Also, a signature does not include a params parameter if one is present. Thus, params does not participate in overload resolution.




C# 2.0(c) The Complete Reference
C# 2.0: The Complete Reference (Complete Reference Series)
ISBN: 0072262095
EAN: 2147483647
Year: 2006
Pages: 300

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net