In C#, the specialization relationship is implemented using a principle called 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.
You’ll often see the immediate base class referred to as the
parent class, and the derived class referred to as the
child class, while the top-most class, Object
, is called the
root class.
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.”
The derived class inherits all the members of the base class
(both member variables and methods), and methods of the derived class
have access to all the public and protected members of the base class.
The derived class is free to implement its own version of a base class
method. This is called hiding
the
base class method and is accomplished by marking the method with the
keyword new
. (Many C# programmers
advise never hiding base class methods as it is unreliable, hard to
maintain, and confusing.)
This is a different use of the keyword new
than you’ve seen earlier in this book.
In Chapter 7, new
was used to create an object on the
heap; here, new
is used to
replace the base class method. Programmers say the keyword new
is overloaded, which means that the
word has more than one meaning or use.
The new
keyword indicates
that the derived class has intentionally hidden and replaced the base
class method, as shown in the Example 11-1. (The new
keyword is also discussed in the section
"Versioning with new and
override,” later in this chapter.)
Example 11-1. Deriving a new class
using System; public class Window { // 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 ); } // 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; } // ListBox derives from Window public class ListBox : Window { // 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 ); } private string mListBoxContents; // new member variable } 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( ); } }
The output looks like this:
Drawing Window at 5, 10 Drawing Window at 20, 30 Writing string to the listbox: Hello world
Example 11-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
. The program is analyzed in detail in
the following sections.
In Example
11-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 constructor 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.
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 as
the object is constructed. 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. The keyword base
identifies the base class for the
current object.
As discussed in Chapter 7, if you do not declare a constructor of any kind, the compiler creates a default constructor for you. Whether you write it yourself or you use the one provided 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.
You can restrict the visibility of a class and its
members through the use of access modifiers , such as public
,
private
, and protected
. (See Chapter
8 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.
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. If you create a new class, myOtherClass
, that derives from myClass
, like this:
public class MyClass : MyOtherClass { Console.WriteLine("myInt: {0}", myValue); }
MyOtherClass
can access
myValue
, because MyOtherClass
derives from MyClass
, and myValue
is protected. Any class that doesn’t
derive from MyClass
would not be
able to access myValue
.