Inheritance in C#

Inheritance and polymorphism are important concepts in object-oriented programming. In this first of two parts, you will learn about inheritance. This article is excerpted from chapter 11 of Learning C# 2005, Second Edition, written by Jesse Liberty and Brian MacDonald (O'Reilly, 2006; ISBN: 0596102097). Copyright © 2006 O'Reilly Media, Inc. All rights reserved. Used with permission from the publisher. Available from booksellers or direct from O'Reilly Media.

Contributed by
Rating: 4 stars4 stars4 stars4 stars4 stars / 25
May 10, 2007
Rate this Article:
MEH MEH++


SEARCH ASP FREE
TOOLS YOU CAN USE

advertisement

In Chapter 7, you learned how to create new types by declaring classes, and in Chapter 6, you saw a discussion of the principle object relationships of association, aggregation, and specialization. This chapter focuses on specialization, which is implemented in C# through inheritance. This chapter also explains how instances of more specialized classes can be treated as if they were instances of more general classes, a process known as polymorphism. This chapter ends with a consideration of sealed classes, which cannot be specialized, and a discussion of the root of all classes, the Object class.

Specialization and Generalization

Classes and their instances (objects) do not exist in a vacuum, but rather in a network of interdependencies and relationships, just as we, as social animals, live in a world of relationships and categories.

One of the most important relationships among objects in the real world is specialization, which can be described as the is-a relationship. When we say that a dog is a mammal, we mean that the dog is a specialized kind of mammal. It has all the characteristics of any mammal (it bears live young, nurses with milk, has hair), but it specializes these characteristics to the familiar characteristics of canis domesticus. A cat is also a mammal. As such, we expect it to share certain characteristics with the dog that are generalized in Mammal, but to differ in those characteristics that are specialized in cats.

The specialization and generalization relationships are both reciprocal and hierarchical. Specialization is just the other side of the generalization coin: Mammal generalizes what is common between dogs and cats, and dogs and cats specialize mammals to their own specific subtypes.

These relationships are hierarchical because they create a relationship tree, with specialized types branching off from more generalized types. As you move “up” the hierarchy, you achieve greater generalization. You move up toward Mammal to generalize that dogs, cats, and horses all bear live young. As you move “down” the hierarchy you specialize. Thus, the cat specializes Mammal in having claws (a characteristic) and purring (a behavior).

Similarly, when you say thatListBoxandButtonare Windows, you indicate that there are characteristics and behaviors of Windows that you expect to find in both of these types. In other words, Window generalizes the shared characteristics of bothListBoxandButton, while each specializes its own particular characteristics and behaviors.

The Unified Modeling Language

The Unified Modeling Language (UML) is a standardized language for describing an object-oriented system. The UML has many different visual representations, but in this case, all you need to know is that classes are represented as boxes. The name of the class appears at the top of the box, and (optionally) methods and members can be listed in the sections within the box.

In the UML, you model specialization relationships, as shown in Figure 11-1. Note that the arrow points from the more specialized class up to the more general class. In the figure, the more specializedButton andListBoxclasses point up to the more general Window class.


Figure 11-1.  An is-a relationship

It is not uncommon for two classes to share functionality. When this occurs, you can factor out these commonalities into a shared base class, which is more general than the specialized classes. This provides you with greater reuse of common code and gives you code that is easier to maintain, because the changes are located in a single class rather than scattered among numerous classes.

For example, suppose you started out creating a series of objects, as illustrated in Figure 11-2. After working with RadioButtons, CheckBoxes, and Command buttons for a while, you realize that they share certain characteristics and behaviors that are more specialized than Window, but more general than any of the three. You might factor these common traits and behaviors into a common base class,Button, and rearrange your inheritance hierarchy, as shown in Figure 11-3. This is an example of how generalization is used in object-oriented development.

 


Figure 11-2.  Objects deriving from Window


Figure 11-3.  Factoring a Button class

The UML diagram in Figure 11-3 depicts the relationship among the factored classes and shows that bothListBoxandButton derive from Window, and thatButtonis specialized into CheckBox and Command. Finally, RadioButton derives from Check-Box. You can thus say that RadioButton is a CheckBox, which in turn is aButton, and thatButtons are Windows.

This is not the only, or even necessarily the best, organization for these objects, but it is a reasonable starting point for understanding how these types (classes) relate to one another.

Actually, although this discussion might reflect how some widget hierarchies are organized, I am very skeptical of any system in which the model does not reflect how I perceive reality, and when I find myself saying that a RadioButton is a CheckBox, I have to think long and hard about whether that makes sense. I suppose a RadioButton is a kind of CheckBox. It is a checkbox that supports the idiom of mutually exclusive choices. That said, it is a bit of a stretch and might be a sign of a shaky design.

Inheritance

 

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 thatListBoxinherits from (or derives from)Windowindicates that it specializesWindow.Windowis referred to as the base class, andListBoxis referred to as the derived class. That is,ListBoxderives 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 topmost class,Object, is called the root class.

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 fromWindow. 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 keywordnew. (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 keywordnewthan you’ve seen earlier in this book. In Chapter 7,newwas used to create an object on the heap; here,newis used to replace the base class method. Programmers say the keywordnewis overloaded, which means that the word has more than one meaning or use.

Thenewkeyword indicates that the derived class has intentionally hidden and replaced the base class method, as shown in the Example 11-1. (Thenew 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 classWindow. This class implements a constructor and a simpleDrawWindow()method. There are two private member variables,topandleft. The program is analyzed in detail in the following sections.

Calling Base Class Constructors

In Example 11-1, the new class ListBoxderives fromWindowand has its own constructor, which takes three parameters. TheListBoxconstructor invokes the constructor of its parent by placing a colon (:) after the parameter list and then invoking the base class constructor with the keywordbase:

  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 thebasekeyword. The keywordbaseidentifies 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.

Controlling Access

You can restrict the visibility of a class and its members through the use of access modifiers, such as public,private, andprotected. (See Chapter 8 for a discussion of access modifiers.)

As you’ve seen,publicallows a member to be accessed by the member methods of other classes, whileprivate indicates that the member is visible only to member methods of its own class. Theprotectedkeyword 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 formyValueis 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 frommyClass, like this:

  public class MyClass : MyOtherClass
  {
   
Console.WriteLine("myInt: {0}", myInt);
  }

MyOtherClasscan accessmyInt, becauseMyOtherClassderives fromMyClass, andmyIntis protected. Any class that doesn’t derive fromMyClass would not be able to accessmyInt.

It is more common to make properties and methods protected than it is to make member variables protected. Member variables are almost always private.

Please check back next week for the conclusion to this article.

blog comments powered by Disqus
C# ARTICLES

- Beginning C#
- ASP.NET RedirectPermanent Method using C# an...
- C Programming Language and UNIX Pioneer Pass...
- Using Facebook JavaScript SDK in ASP.NET wit...
- ASP.NET Export to Excel and Word using VB.NE...
- WAV and MP3 Streaming with ASP.Net and C#
- Game Programming using SDL: the File I/O API
- C# and Java Developer Jobs on the Rise
- The Future Evolution of C# and VB.NET
- C# If and Else-if Statements
- How To Use the C# String Replace Method
- 5 Ways to Parse XML in C#
- C# Meets Design Patterns
- Coding a CRC-Generating Algorithm in C
- Cyclic Redundancy Check

ASP Web Hosting ASP.Net Web Hosting Windows Web Hosting
 
 
 

ASP Free Forums 
 RSS  Tutorials RSS
 RSS  Forums RSS
 RSS  All Feeds
Site Map 
Request Media Kit
Write For Us Get Paid 
Weekly Newsletter
 
Developer Updates  
Free Website Content 
Privacy Policy 
Support 


© 2003-2012 by Developer Shed. All rights reserved. DS Cluster 6 - Follow our Sitemap
Most Popular Topics
All ASP.Net Tutorials