Team LiB
Previous Section Next Section

Advanced Topics in Classes and Objects

So far, this chapter has provided a basic introduction to object-oriented programming by taking a glimpse at classes, objects, attributes, and operations. This next section will illustrate some of the more advanced topics. In object-oriented programming, advanced topics include inheritance and polymorphism. Although inheritance and polymorphism are basic necessities in any object-oriented language, they are some of the more difficult things to understand for beginners. In the next few sections, you will take a look at inheritance and polymorphism, and their uses.

Inheriting from Base Classes

When applied to object-oriented programming, inheritance means that if a class inherits from a base class, it inherits all the operations, attributes, properties, events, and their implementations from that base class. The only exceptions are that the class does not inherit instance constructors, destructors, and static constructors, nor does it inherit those things to which it explicitly has been denied access.

Although the derived class inherits its methods, attributes, and so forth from a base class, scoping might not allow the derived class to access the inherited items. For example, if a method is marked as private, no descendants will be able to call this method.

Figure 6.8 shows a class diagram for the inheritance example program in Listing 6.1. The diagram shows a base class, appropriately named BaseClass, with a method named SomeMethod. It also shows a derived class named DerivedClass that inherits from the base class BaseClass. This means that when the DerivedClass class is instantiated, as shown in Listing 6.1, you can invoke the SomeMethod operation from the instance of DerivedClass.

Listing 6.1. Inheritance Example
using System;

namespace SAMS.VisualCSharpDotNetUnleashed.Chapter6
{
  public class BaseClass
  {
    public void SomeMethod()
    {
      System.Console.WriteLine("Hello from the base class.");
    }
  }

  public class DerivedClass : BaseClass
  {
  }

  class InheritanceExample
  {
    [STAThread]
    static void Main(string[] args)
    {
      DerivedClass dc = new DerivedClass();
      dc.SomeMethod();

      System.Console.WriteLine("*** Press the enter key to continue ***");
      System.Console.ReadLine();
    }
  }
}

Figure 6.8. Class diagram for the inheritance example in Listing 6.1.


When using inheritance, it is important to understand that inheritance extends its base class and is transitive. This means that if you have a class C that inherits from class B, which inherits from class A, class C inherits everything from class B and class A. In addition, it is possible for a derived class to hide inherited members by declaring new operations with the same name or signature. The following code snippet demonstrates this principle:

public class BaseClass
{
  public void SomeMethod()
  {
    System.Console.WriteLine("Hello from the base class.");
  }
}
public class DerivedClass : BaseClass
{
  new public void SomeMethod()
  {
    System.Console.WriteLine("Hiding the inherited method.");
  }
}

Because an instance of a class contains all the instance attributes declared in that class as well as all the instance attributes that it inherits, an implicit conversion or cast exists from a derived class type to any of its base class types. For example, if class C inherits from class B and class B inherits from class A, class C could be converted or cast to either class B or class A. Listing 6.2 shows how this could be useful.

Listing 6.2. Inheritance Example 2
using System;

namespace SAMS.VisualCSharpDotNetUnleashed.Chapter6
{
  public class BaseClass
  {
    public void SomeMethod()
    {
      System.Console.WriteLine("Hello from the base class.");
    }
  }

  public class DerivedClass : BaseClass
  {
  }

  class InheritanceExample
  {
    protected static void InvokeSomeMethod(BaseClass bc)
    {
      bc.SomeMethod();
    }

    [STAThread]
    static void Main(string[] args)
    {
      BaseClass bc = new BaseClass();
         DerivedClass dc = new DerivedClass();
         InvokeSomeMethod(bc);
         InvokeSomeMethod(dc);

         System.Console.WriteLine("*** Press the enter key to continue ***");
         System.Console.ReadLine();
       }
     }
   }

Listing 6.2 instantiates two classes: BaseClass and DerivedClass. Because DerivedClass inherits from the base class BaseClass, you can implicitly cast this instance to the type BaseClass and therefore pass it as a parameter to the InvokeSomeMethod method, whose only parameter is of type BaseClass.

Introduction to Polymorphism

Polymorphism is the capability to appear in many forms. In object-oriented languages, polymorphism is the capability to process objects differently, depending on the class or data type. In other words, it is the capability to provide different implementations for a method or property with the same signature. For example, the class structure in Figure 6.9 contains four classes: Shape, Rectangle, triangle, and Circle.

Figure 6.9. The class diagram of the Shape class and its descendents.


The first class, or base class, is Shape. Inside the Shape class is a method named CalculateArea. For the Rectangle class, the calculation of the area is area = length x width. For the triangle class, area = 1/2 x base x height. Finally, for the Circle class, area = x radius2. Without polymorphism, this method would be impossible to implement. It becomes a problem when you have a reference to the base type, Shape, and need to calculate the area of that shape.

Fortunately, there is polymorphism. Polymorphism guarantees that the correct method will be called, regardless of the data type. In Visual C# .NET, there are three types of polymorphism: interface polymorphism, inheritance polymorphism, and abstract class polymorphism.

  • Interface polymorphism In Visual C# .NET, many classes can implement the same interface, and one class can implement many interfaces. For more information on interfaces, please see Chapter 38, "Interface Programming."

  • Inheritance polymorphism Inheritance polymorphism is the most often form of polymorphism. It enables you to provide different implementations of methods by using the virtual keyword. When you inherit from a base class, you inherit all of the attributes, methods, properties, and events that go along with the base class. In addition, you inherit the implementation of those items. Unfortunately, there are times that you do not want to inherit that functionality; or, at the very least, you want to alter it. By marking the method or property virtual (in the base class), you can override the functionality in descendant classes.

  • Abstract class polymorphism An abstract class is one that is marked with a type modifier of abstract. Because an abstract class usually has abstract methods (an abstract method is one that is defined, but does not have an implementation), it cannot be instantiated.

    Implementing polymorphism through an abstract class is similar to doing so with inheritance polymorphism. The difference is the method is not marked virtual, but abstract. An abstract method provides no implementation and is completely reliant on descendant classes to provide functionality. An abstract method is also similar to a method defined in an interface because it provides a contract stating that this method will be implemented in a descendant class.

Making Methods Virtual

When a method is modified by the keyword virtual, it allows descendant classes to override the implementation that it provides. This is an essential part of any object-oriented language. When declaring a method virtual, you assert that you expect this method to be overridden in a descendant class. If you do not expect this to happen, you should not declare a method virtual because it comes with additional overhead.

The overhead comes in the form of checking the runtime type of the object for an overriding method. When this occurs, the overriding method in the most descended class will be called. If this class is the base class, the virtual method in the base class will be called and all the resources expended checking for the type and looking for an overriding method would be wasted. Because the method in the descendant class overrides that in the base class, you must declare the method with the same signature as the virtual method that it overrides. Listing 6.3 shows a sample program that demonstrates the power of polymorphism.

Listing 6.3. Polymorphism in Action
   using System;

   namespace SAMS.VisualCSharpDotNetUnleashed.Chapter6
   {
     public class Animal
     {
       public virtual void Speak()
       {
       }
     }
     public class Dog : Animal
     {
       public override void Speak()
       {
         System.Console.WriteLine("Bark.. Bark");
       }
     }
     public class Cat : Animal
     {
       public override void Speak()
       {
         System.Console.WriteLine("ummmmm... I see a bird!");
       }
     }
     public class Bird : Animal
     {
       public override void Speak()
       {
         System.Console.WriteLine("Please don't eat me mister cat!");
       }
     }
     class PolymorphismExample
       {
       static protected void MakeTheAnimalSpeak(Animal animal)
       {
         animal.Speak();
       }

       [STAThread]
       static void Main(string[] args)
       {
         Dog dog = new Dog();
         Cat cat = new Cat();
         Bird bird = new Bird();

         MakeTheAnimalSpeak(dog);
         MakeTheAnimalSpeak(cat);
         MakeTheAnimalSpeak(bird);

         System.Console.WriteLine("*** Press the enter key to continue ***");
         System.Console.ReadLine();
       }
     }
   }

In the sample program shown in Listing 6.3, there are four classes: Animal , Dog , Cat , and Bird . The class diagram for this listing is depicted in Figure 6.9.

When the application is started, the program instantiates three classes: Dog, Cat, and Bird. After the classes have been instantiated, the program passes the objects to a method named MakeTheAnimalSpeak. This method takes as its only parameter a reference to type Animal. When this method is called, the method asks the animal to speak. Because of polymorphism, the program is able to determine the correct implementation and the animal speaks appropriately.

    Team LiB
    Previous Section Next Section