Now that you have the background of specialization down, and a starting-point example to work with, you can see how to use this idea in your code. 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.
Saying that ListBox
inherits from (or derives from) Control
indicates that it specializes Control
. Control
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 Control
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, whereas the topmost 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 : Control
This code declares a new class, ListBox
, which derives from Control
. You can read the colon as “derives from.”
The derived class inherits all the members of the base class (both member variables and methods). In other words, suppose Control
has member fields called top
and left
, to indicate where on the screen the upper-left corner of the Control
will be drawn. If ListBox
derives from Control, ListBox
also has the member fields top
and left
. The same is true of methods: if Control
has a method called DrawControl( ), ListBox
does too.
Methods of the derived class have access to all the public and protected members of the base class. That means that if the drawControl( )
method in Control
is marked as protected
, the ListBox
class can call that method, whereas a class that doesn’t derive from Control
wouldn’t be able to.
The derived class is free to implement its own version of a base class method—that is, ListBox
can have its own drawControl( )
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.) The new
keyword indicates that the derived class has intentionally hidden and replaced the base class method. (We also discuss the new
keyword in “Versioning with new and override” later in this chapter.)
This is a different use of the keyword new
than you saw earlier in this book. In Chapter 7, we used new
to create an object on the heap; here, we’re using new
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.
Example 11-1 shows the ListBox
class inheriting from Control
, and demonstrates all the features we just talked about. Note that although Control
and ListBox
are the names of legitimate Windows classes, that’s not what we’re showing you here. These are custom classes with familiar names to help you understand the inheritance relationship.
Example 11-1. You can derive a new class ListBox from its parent, Control
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Example_11_1_ _ _ _Inheritance { public class Control { // these members are private and thus invisible // to derived class methods private int top; private int left; // constructor takes two integers to // fix location on the console public Control(int top, int left) { this.top = top; this.left = left; } // simulates drawing the control public void DrawControl( ) { Console.WriteLine("Drawing Control at {0}, {1}", top, left); } } // ListBox derives from Control public class ListBox : Control { private string mListBoxContents; // new member variable // constructor adds a parameter public ListBox(int top, int left, string theContent) : base(top, left) // call base constructor { mListBoxContents = theContent; } // a new version (note the keyword) because in the // derived method we change the behavior public new void DrawControl( ) { base.DrawControl( ); // invoke the base method Console.WriteLine("Writing string to the ListBox: {0}", mListBoxContents); } } public class Tester { public static void Main( ) { // create a base instance Control myControl = new Control(5, 10); myControl.DrawControl( ); // create a derived instance ListBox lb = new ListBox(20, 30, "Hello world"); lb.DrawControl( ); } } }
The output looks like this:
Drawing Control at 5, 10 Drawing Control at 20, 30 Writing string to the ListBox: Hello world
Example 11-1 starts with the declaration of the base class Control
. This class implements a constructor and a simple DrawControl( )
method. There are two private member variables, top
and left
. That’s the basic part; after that, it gets interesting. We’ll analyze the rest of the program in detail in the following sections.
In Example 11-1, the new class ListBox
derives from Control
:
public class ListBox : Control
ListBox
has its own constructor, which takes three parameters, as opposed to two for Control
. This is often the case with derived classes: the constructor does what the parent’s constructor does, plus a bit more. In cases such as these, it saves code for the derived class simply to call the parent class’s constructor, and then do whatever special setup the derived class needs.
In this case, 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 top, int left, string theContent) : base(top, left) // 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 we 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.
As we mentioned, Control
has a simple method called DrawControl( )
, which simulates drawing the control on the screen. The ListBox
inherits the DrawControl( )
method, but the ListBox
also needs to simulate writing text to the ListBox
. Therefore, the ListBox
implements its own DrawControl( )
method, using the new
keyword to indicate that this method hides the parent method:
public new void DrawControl( ) { base.DrawControl( ); // invoke the base method Console.WriteLine("Writing string to the ListBox: {0}", mListBoxContents); }
As we mentioned, hiding the parent class’s method is frowned upon. A better way to implement the ListBox
control’s new method is with a virtual method, which we’ll discuss in a moment.
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, whereas 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. In other words, 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
, which derives from MyClass
, like this:
public class MyClass : MyOtherClass { Console.WriteLine("myValue: {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
.