GOTCHA 47 Signature mismatches can lead to method hiding


GOTCHA #47 Signature mismatches can lead to method hiding

The signature of a method is its parameter list: the number, order, and type of parameters it takes. When deriving from a class, you need to pay special attention to the signatures of its methods. If you want to introduce a method with a different signature in the derived class, it is not as simple as just writing the newer method. Going this route, you may end up hiding the base-class methods [Cline99]. This is demonstrated in Example 6-12.

Example 6-12. Hiding methods due to signature mismatch

C# (MethodSignature)

 //Base.cs using System; namespace MethodSignature {     public class Base     {         public virtual void Method1(double val)         {             Console.WriteLine("Base.Method1(double val) called");         }     } } //Derived.cs using System; namespace MethodSignature {     public class Derived : Base     {         public virtual void Method1(int val)         {             Console.WriteLine("Derived.Method1(int val) called");         }     } } //Test.cs using System; namespace MethodSignature {     class Test     {         [STAThread]         static void Main(string[] args)         {             Derived d = new Derived();             Base b = d;     // b and d refer to the same object now.     b.Method1(3);     d.Method1(3);         }     } } 

VB.NET (MethodSignature)

 'Base.cs Public Class Base     Public Overridable Sub Method1(ByVal val As Double)         Console.WriteLine("Base.Method1(double val) called")     End Sub End Class 'Derived.cs Public Class Derived     Inherits Base     Public Overridable Sub Method1(ByVal val As Integer)         Console.WriteLine("Derived.Method1(int val) called")     End Sub End Class 'Test.vb Module Test     Sub Main()         Dim d As New Derived         Dim b As Base = d         ' b and d refer to the same object now.         b.Method1(3)         d.Method1(3)     End Sub End Module 

The VB.NET code generates a warning (not an error). The message is:

     warning BC40003: sub 'Method1' shadows an overloadable member declared in the base  class 'Base'. 

If you want to overload the base method, the derived method must be declared Overloads. However, adding the Overloads keyword to Derived.Method1() does not change the behavior; the output is still that shown in Figure 6-10.

The C# version does not even generate a warning.

In this example, a class Base has a virtual/overridable method Method1() that has one parameter of type double. In the class Derived, which inherits from Base, there is also a Method1(), but its one parameter is of type int/Integer. Test.Main() creates an object of Derived and calls its Method1() using a reference of type Derived and then a reference of type Base. The output produced by this program is shown in Figure 6-10.

Figure 6-10. Output from Example 6-12


Even though you are dealing with one instance of Derived, you end up calling two different methods. What went wrong? When you call Method1(3) on the base reference, the compiler uses the base class's signature of the method to convert 3 to 3.0 at compile time. (You can see this clearly if you look at the MSIL for Test.Main() in ildasm.exe). However, when Method1(3) is called on the Derived reference, the value of 3 is sent as is. Both the calls are bound dynamically at runtime. Since the derived class has no overriding method that takes a double as a parameter, the call using the Base reference ends up in the Base method itself.

If you really want the method to have a different method signature in the derived class, it is better to overload and override at the same time, as shown in Example 6-13.

Example 6-13. Overriding and overloading to change signature

C# (MethodSignature)

 //Derived.cs using System; namespace MethodSignature {     public class Derived : Base     {         public override void Method1(double val)         {             Console.WriteLine(                 "Derived.Method1(double val) called");         }         public virtual void Method1(int val)         {             Console.WriteLine("Derived.Method1(int val) called");             // You may call Method1((double) val) from here if             // you want consistent behavior.         }     } } 

VB.NET (MethodSignature)

 'Derived.cs Public Class Derived     Inherits Base     Public Overloads Overrides Sub Method1(ByVal val As Double)         Console.WriteLine( _                 "Derived.Method1(double val) called")     End Sub     Public Overridable Overloads Sub Method1(ByVal val As Integer)         Console.WriteLine("Derived.Method1(int val) called")         ' You may call Method1((double) val) from here if         ' you want consistent behavior.     End Sub End Class 

As you can see, in this modified version of the Derived class, you override and overload Method1(). The output is shown in Figure 6-11.

Figure 6-11. Output after modifications in Example 6-13


Now both the calls end up in Derived, though in two different methods. In the Derived class, you can take care of providing consistent and appropriate behavior for these two calls.

IN A NUTSHELL

If you want to change the signature of a method in a derived class, then override and overload at the same time. This assures that all calls to a virtual/overridable method whose signature the derived class is changing consistently execute in that class, regardless of the type of reference used. Otherwise, you may end up hiding the method.

SEE ALSO

Gotcha #12, "Compiler warnings may not be benign" and Gotcha #13, "Ordering of catch processing isn't consist across languages."



    .NET Gotachas
    .NET Gotachas
    ISBN: N/A
    EAN: N/A
    Year: 2005
    Pages: 126

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