GOTCHA #25 Access to static/Shared members isn't enforced consistentlyA static/Shared method belongs to a class and not to any specific instance. Furthermore, it is never polymorphic. C# has taken the high road of disallowing the call to static members using an object reference. Unfortunately, VB.NET does not impose the same restriction. The downside to calling Shared members on a reference is that it may lead to confusion at times. Consider Example 3-17. Example 3-17. Invoking Shared member using a referenceC# (Shared) C# does not allow the call to static members using an object reference. So this is not an issue for C# programmers. It only concerns VB.NET, C++, and Java programmers. C# (VB.NET (Shared)) 'Base.vb Public Class Base Public Overridable Sub Method1() Console.WriteLine("Base.Method1") End Sub Public Shared Sub Method2() Console.WriteLine("Base Method2") End Sub End Class 'Derived.vbPublic Class Derived Inherits Base Public Overrides Sub Method1() Console.WriteLine("Derived.Method1") End Sub Public Shared Sub Method2() "Derived Method2") End Sub End Class 'Test.vb Public Class Test Public Shared Sub Run(ByVal b As Base) b.Method1() b.Method2() End Sub Public Shared Sub Main() Dim object1 As New Derived Console.WriteLine("--------- Using object of Derived") object1.Method1() object1.Method2() Run(object1) End Sub End Class In the VB.NET version of Example 3-17, there is a class named Base and a class named Derived that inherits from Base. Base has an overridable method named Method1() and a Shared method named Method2(). The Derived class overrides Method1() and also provides a Shared method named Method2(). In the Test code, you create a Derived object, then call Method1() and Method2() on it using the object reference object1. Next you pass the reference object1 to a method Run(), which treats it as a Base type. Within the Run() method you are still dealing with an object of Derived. When the Run() method invokes the two methods using the Base type reference, the method invoked for the call to Method1() is on Derived due to polymorphism. However, the method invoked for the call to Method2() is on Base, not on Derived, even though the object being pointed to by the reference is of type Derived. When the program executes, you get the output shown in Figure 3-12. Figure 3-12. Output from Example 3-17Note that when the code is compiled a warning is generated for Method2() of Derived. It recommends that you mark Method2() with the Shadows keyword. Marking it Shadows will not change the output of the program, however. The call to Method1() using the Base reference is polymorphic and goes to the Derived class's Method2(). However, the call to Method2() is not polymorphic. Within the Run() method, it is statically bound to the method of Base at compile time. As a result, Method2() of Base is invoked rather than Method2() of Derived. In fact, when a Shared method is accessed using an object reference, the compiler replaces the object reference with the class name at the time of compilation. In this example, b.Method2() is replaced by Base.Method2(). While the call b.Method1() results in a polymorphic callvirt instruction in MSIL, the call to b.Method2() simply becomes a static call. Figure 3-13 shows the MSIL generated from the above code. Figure 3-13. MSIL for Run() method in Example 3-17Given that static/shared methods are not polymorphic, it is easy to get confused if they are invoked using an object reference, especially if the static/Shared methods are part of the derived class as well. As a good coding practice, you should refrain from calling Shared members using an object reference in VB.NET (and C++ and Java as well). Instead, use the class to access them. Instead of calling b.Method2(), write Base.Method2(). How does this differ in .NET 2.0 Beta 1? The VB.NET compiler issues a warning (not an error) if you access a Shared member using an object reference. The warning generated is: warning BC42025: Access of shared member through an instance; qualifying expression will not be evaluated. If you configure Visual Studio to treat warnings as errors (see Gotcha #12, "Compiler warnings may not be benign"), you will avoid this gotcha. IN A NUTSHELLRefrain from accessing Shared members of a class through an object reference. Use the class to access them. SEE ALSOGotcha #12, "Compiler warnings may not be benign, Gotcha #27, "Object initialization sequence isn't consistent," and Gotcha #28, "Polymorphism kicks in prematurely." |