GOTCHA 43 Using newshadows causes


GOTCHA #43 Using new/shadows causes "hideous hiding"

If a method is marked virtual/overridable, the base class tells the compiler not to bind to it statically at compile time, since a derived class may override that method. The method to be called is resolved at runtime. However, if a method is not marked virtual/overridable, then the compiler binds to it statically at compile time.

In C++, hiding occurs when a programmer writes a method in the derived class with the same name and signature as a method not marked virtual in the base class. Hiding can also result from virtual methods that differ in signature between base and derived classes (see Gotcha #47, "Signature mismatches can lead to method hiding"). Hiding of non-virtual methods in C++ is generally an accident and (hopefully) not the intent. Unfortunately, even though the managed compilers warn you against such mistakes, .NET gives you a legal way to violate the principles of good object-oriented programmingthe new/shadows keyword. (The .NET approach to method hiding is like the laws in Las Vegas: "Oh yeah, that's illegal in most parts of the world, but here that's just fine and you are most welcome!")

You may argue that hiding facilitates versioningit allows a base class to introduce, in a later version, a method that is already present in the derived class. However, this facility leads to more trouble than it's worth, as discussed below. I prefer my code to behave consistently in an object-oriented manner (or fail compilation) than to quietly misbehave.


You should expect the same method to be executed on an object no matter how you invoke itwhether through a direct reference to its type, or through a reference to its base type. This is the essence of polymorphism, which says that the actual method invoked is based on the type of the object and not the type of the reference. Hiding works against this. It makes the method that is invoked dependent on the type of the reference and not on the real type of the object. Consider Example 6-5.

Example 6-5. Hiding methods

C# (HidingMess)

 //Base.cs namespace Hiding {     public class Base     {         public virtual void Method1()         {             Console.WriteLine("Base.Method1 called");         }         public virtual void Method2()         {             Console.WriteLine("Base.Method2 called");         }     } } //Derived.cs namespace Hiding {     public class Derived : Base     {         public override void Method1()         {             Console.WriteLine("Derived.Method1 called");         }         public new void Method2()         {             Console.WriteLine("Derived.Method2 called");         }     } } //Test.cs namespace Hiding {     class Test     {         static void Main(string[] args)         {     Derived d = new Derived();     Base b = d;             d.Method1();     d.Method2();             b.Method1();     b.Method2();         }     } } 

VB.NET (HidingMess)

 'Base.vb Public Class Base     Public Overridable Sub Method1()         Console.WriteLine("Base.Method1 called")     End Sub     Public Overridable Sub Method2()         Console.WriteLine("Base.Method2 called")     End Sub End Class 'Derived.vb Public Class Derived     Inherits Base     Public Overrides Sub method1()         Console.WriteLine("Derived.Method1 called")     End Sub     Public Shadows Sub method2()         Console.WriteLine("Derived.Method2 called")     End Sub End Class 'Test.vb  Module Test     Sub Main()         Dim d As New Derived         Dim b As Base = d         d.Method1()         d.method2()         b.Method1()         b.Method2()     End Sub End Module 

In this example, Method2() in Derived hides Method2() of Base, while Method1() in Derived overrides Method1() in Base. In Test, you have only one instance of Derived, but two references to it. One reference named d is of type Derived, and the other reference named b is of type Base. Regardless of how you invoke Method1(), the same method Method1() in Derived is called, as shown in the output in Figure 6-3. However, the call to Method2() goes to Base.Method2() if called using b, and to Derived.Method2() if called using d. The method that is actually executed, in the latter case, depends on the type of the reference instead of the type of the object that it refers to; this is an example of hiding.

Figure 6-3. Output from Example 6-5


Hiding is very anti-object-oriented. After all, the reason you mark a method virtual/overridable is to allow derived classes to provide alternate implementations. If the base class uses virtual/overridable correctly, and the derived class uses override/overrides, a consistent method is invoked on that object without regard to how and where it is accessed. Hiding fundamentally breaks that tenet.

IN A NUTSHELL

Do not mark a method as new/shadows. Avoid this insidious feature.

SEE ALSO

Gotcha #42, "Runtime Type Identification can hurt extensibility," Gotcha #44, "Compilers are lenient toward forgotten override/overrides," Gotcha #45, "Compilers lean toward hiding virtual methods," Gotcha #46, "Exception handling can break polymorphism," and Gotcha #47, "Signature mismatches can lead to method hiding."



    .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