< Day Day Up > |
In general, a type cannot contain more than one member with the same name . For example, if a module contained a field named Cost and a method named Cost , it would be difficult or impossible for the compiler (and even more difficult to someone reading the code) to figure out which one was being referenced at any one time. However, when you are dealing with methods, sometimes a caller may want to supply some parameters but not others, or may want to supply different types of arguments at different times. Optional parameters can be used to deal with the first situation, but using optional parameters can result in methods with long parameter lists that require named arguments to be used effectively. Parameters declared as Object can be used to deal with the second situation; this requires the overhead of casting values back and forth from Object and the loss of strong typing when calling the method. Another way of dealing with this situation is to use overloading. Overloading is the ability to define more than one method in a class with the same name. Hence, the method name is overloaded with multiple meanings. Each method overloaded on a particular name must have a unique set of parameter types to distinguish it from other methods. For example: Module Test Sub Print(ByVal i As Integer) Console.WriteLine("Integer: " & i) End Sub Sub Print(ByVal d As Double) Console.WriteLine("Double: " & d) End Sub Sub Print(ByVal s As String) Console.WriteLine("String: " & s) End Sub Sub Main() Print(10) ' Prints "Integer: 10" Print(40.40) ' Prints "Double: 40.40" Print("abc") ' Prints "String: abc" End Sub End Module In this example, the name Print is overloaded with three methods, each of which takes a different type and prints something different. Declaring overloaded methods requires no special syntaxjust reusing a previously used name is sufficient. Overloaded methods must all be distinct from one another. Two methods are distinct if the types in their parameter lists are distinctthat is, they are not exactly the same types in exactly the same order. Overloading depends on the parameter types because the types of the arguments supplied to a method call are the only things that the compiler can use when deciding which of the overloaded methods it needs to call. For example, the following methods cannot be overloaded. Module Test Sub Print(ByVal x As Integer) ... End Sub ' Error: Invalid overload. Sub Print(ByRef y As Integer) ... End Sub End Module These two methods cannot overload each other because each takes an Integer parameter. The fact that the parameters have different names or that one parameter is ByRef and another is ByVal is irrelevant when you are deciding whether two methods can be overloaded. Similarly, the return type of functions is irrelevant when you are determining whether two methods can overload each other. The following two methods cannot overload one another, because their parameter lists are the same (i.e., empty) even though they return different types. In the following example, the function GetValue is incorrectly overloaded because the two functions have the same (empty) parameter list and only differ by return type. Functions cannot be overloaded on return types, because there would be many situations in which calls would be ambiguous. In the following example, the call to GetValue in Main cannot be resolved, because it is not clear whether the Integer or Double function should be called, since both convert to Object . Module Test Function GetValue() As Integer ... End Function ' Error: Invalid overload. Function GetValue() As Double ... End Function Sub Main() Dim o As Object o = GetValue() End Sub End Module The first example in this section showed the use of overloading to handle different kinds of types. The following shows the use of overloading to deal with parameters that may or may not be supplied by a caller. Contrast this with the example in the section on optional parameters earlier in this chapter. Sub PrintList(ByVal Number As Integer, ByVal NewLine as Boolean) If NewLine Then Console.WriteLine(Number) Else Console.Write(Number & ",") End If End Sub Sub PrintList(ByVal Number As Integer) PrintList(Number, False) End Sub Sub Main() PrintList(10) PrintList(20) PrintList(30) PrintList(40, True) End Sub
Overload ResolutionWhen a compiler encounters a call to a method that has several overloads, it must choose between the overloaded methods. In some situations, choosing may be very easy because of the number and types of the arguments supplied. Module Test Sub Print(ByVal i As Single) Console.WriteLine("Single: " & i) End Sub Sub Print(ByVal d As Double) Console.WriteLine("Double: " & d) End Sub Sub Print(ByVal t As Date) Console.WriteLine("Date: " & t) End Sub Sub Print() Console.WriteLine() End Sub Sub Main() Print() Print(#8/23/70#) Print(10D) End Sub End Module In the case of the first two calls to Print , the correct overload to use is obviousthe first call has no arguments, so it can only call the parameterless Print method; the second call has a Date argument that has no conversion to Single or Double , so it can only call the Print method that takes a Date . But the third call is more difficult because its argument is Decimal , and Decimal converts to both Single and Double . How does the language choose between the two overloads? The following steps describe how the call is resolved. If after any step only one method remains under consideration, that method is called.
Method A is considered more specific than method B if the type of each of A 's parameters either is the same type as B 's corresponding parameter or has a widening conversion to the type of B 's corresponding parameter. If the language encounters two methods where neither method is more specific than the other, it gives up and gives an ambiguity error.
The overload resolution rules may seem obscure, but the overall intent is simpler than it sounds. In general, the rules are designed to choose an overload that most closely matches the given argument list. In the preceding example, the language will choose to call the Print method that takes a Single for the Print(10D) call, because Single is closer to Decimal than Double is. ( Single is closer to Double because it has a widening conversion to Double , whereas Double has a narrowing conversion to Single .) When choosing between overloads, however, the language can only choose between widening conversions because those are the only ones that are guaranteed to succeed. For example: Module Test Sub Print(ByVal i As Integer) Console.WriteLine("Integer: " & i) End Sub Sub Print(ByVal s As Short) Console.WriteLine("Short: " & s) End Sub Sub Main() Dim x As Long = CLng(Console.ReadLine()) Print(x) End Sub End Module In this example, the argument x is of type Long , which narrows to both Integer and Short . It is not clear from context which method should be called Short is the more specific of the two methods, but it is possible that the value input may not fit into a Short . The value input may not fit into an Integer either, for that matter. Rather than try to pick a winner and be wrong some of the time, the language simply gives an error and require the developer to choose which overload they want to call by inserting a conversion. For example, changing the line to Print(CInt(x)) would make it clear which overload to call. |
< Day Day Up > |