[ Team LiB ] Previous Section Next Section

5.2 Inheritance

In C#, the specialization relationship is typically implemented using inheritance. This is not the only way to implement specialization, but it is the most common and most natural way to implement this relationship.

Saying that ListBox inherits from (or derives from) Window indicates that it specializes Window. Window is referred to as the base class, and ListBox is referred to as the derived class. That is, ListBox derives its characteristics and behaviors from Window and then specializes to its own particular needs.

5.2.1 Implementing Inheritance

In C#, you create a derived class by adding a colon after the name of the derived class, followed by the name of the base class:

public class ListBox : Window

This code declares a new class, ListBox, that derives from Window. You can read the colon as "derives from."

C and C++ programmers take note: C# has no private or protected inheritance.

The derived class inherits all the members of the base class, both member variables and methods. The derived class is free to implement its own version of a base class method. It does so by marking the new method with the keyword new. (The new keyword is also discussed in Section 5.3.3, later in this chapter.) This indicates that the derived class has intentionally hidden and replaced the base class method, as in Example 5-1.

Example 5-1. Using a derived class
using System;

public class Window
{
   // these members are private and thus invisible
   // to derived class methods; we'll examine this 
   // later in the chapter
   private int top;
   private int left;

   // constructor takes two integers to
   // fix location on the console
   public Window(int top, int left)
   {
      this.top = top;
      this.left = left;
   }

   // simulates drawing the window
   public void DrawWindow( )
   {
      Console.WriteLine("Drawing Window at {0}, {1}",
         top, left);
   }
}

// ListBox derives from Window
public class ListBox : Window
{
   private string mListBoxContents;  // new member variable

   // constructor adds a parameter
   public ListBox(
      int top, 
      int left, 
      string theContents):
      base(top, left)  // call base constructor
   {
      mListBoxContents = theContents;
   }
    
   // a new version (note keyword) because in the
   // derived method we change the behavior
   public new void DrawWindow( )
   {
      base.DrawWindow( );  // invoke the base method
      Console.WriteLine ("Writing string to the listbox: {0}", 
         mListBoxContents);
   }
}

public class Tester
{
   public static void Main( )
   {
      // create a base instance
      Window w = new Window(5,10);
      w.DrawWindow( );

      // create a derived instance
      ListBox lb = new ListBox(20,30,"Hello world");
      lb.DrawWindow( );
   }
}

Output:
Drawing Window at 5, 10
Drawing Window at 20, 30
Writing string to the listbox: Hello world

Example 5-1 starts with the declaration of the base class Window. This class implements a constructor and a simple DrawWindow method. There are two private member variables, top and left.

5.2.2 Calling Base Class Constructors

In Example 5-1, the new class ListBox derives from Window and has its own constructor, which takes three parameters. The ListBox constructor invokes the constructor of its parent by placing a colon (:) after the parameter list and then invoking the base class with the keyword base:

public ListBox(
    int theTop, 
    int theLeft, 
    string theContents):
        base(theTop, theLeft)  // call base constructor

Because classes cannot inherit constructors, a derived class must implement its own constructor and can only make use of the constructor of its base class by calling it explicitly.

Also notice in Example 5-1 that ListBox implements a new version of DrawWindow( ):

public new void DrawWindow( )

The keyword new indicates that the programmer is intentionally creating a new version of this method in the derived class.

If the base class has an accessible default constructor, the derived constructor is not required to invoke the base constructor explicitly; instead, the default constructor is called implicitly. However, if the base class does not have a default constructor, every derived constructor must explicitly invoke one of the base class constructors using the base keyword.

As discussed in Chapter 4, if you do not declare a constructor of any kind, the compiler will create a default constructor for you. Whether you write it yourself or you use the one provided "by default" by the compiler, a default constructor is one that takes no parameters. Note, however, that once you do create a constructor of any kind (with or without parameters), the compiler does not create a default constructor for you.

5.2.3 Calling Base Class Methods

In Example 5-1, the DrawWindow( ) method of ListBox hides and replaces the base class method. When you call DrawWindow( ) on an object of type ListBox, it is ListBox.DrawWindow( ) that will be invoked, not Window.DrawWindow( ). Note, however, that ListBox.DrawWindow( ) can invoke the DrawWindow( ) method of its base class with the code:

base.DrawWindow( );  // invoke the base method

(The keyword base identifies the base class for the current object.)

5.2.4 Controlling Access

The visibility of a class and its members can be restricted through the use of access modifiers, such as public, private, protected, internal, and protected internal. (See Chapter 4 for a discussion of access modifiers.)

As you've seen, public allows a member to be accessed by the member methods of other classes, while private indicates that the member is visible only to member methods of its own class. The protected keyword extends visibility to methods of derived classes, while internal extends visibility to methods of any class in the same assembly.[1]

[1] An assembly (discussed in Chapter 1), is the unit of sharing and reuse in the Common Language Runtime (a logical DLL). Typically, an assembly is a collection of physical files, held in a single directory that includes all the resources (bitmaps, .gif files, etc.) required for an executable, along with the Intermediate Language (IL) and metadata for that program.

The internal protected keyword pair allows access to members of the same assembly (internal) or derived classes (protected). You can think of this designation as internal or protected.

Classes as well as their members can be designated with any of these accessibility levels. If a class member has a different access designation than the class, the more restricted access applies. Thus, if you define a class, myClass, as follows:

public class myClass
{
   // ...
   protected int myValue;
}

the accessibility for myValue is protected even though the class itself is public. A public class is one that is visible to any other class that wishes to interact with it. Occasionally, classes are created that exist only to help other classes in an assembly, and these classes might be marked internal rather than public.

    [ Team LiB ] Previous Section Next Section