Search content within the blog

Friday, April 17, 2009

Polymorphism in c#

You implement polymorphism in two steps:

1. Create a base class with virtual methods.
2. Create derived classes that override the behavior of the base class’s virtual
methods.

Example

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 virtual void DrawWindow()
{
Console.WriteLine( "Window: drawing Window at {0}, {1}",
top, left );
}

// these members are protected and thus visible
// to derived class methods. We'll examine this
// later in the chapter. (Typically, these would be private
// and wrapped in protected properties, but the current approach
// keeps the example simpler.)
protected int top;
protected int left;

} // end Window

// ListBox derives from Window
public class ListBox : Window
{
// constructor adds a parameter
// and calls the base constructor
public ListBox(
int top,
int left,
string contents ) : base( top, left )
{
listBoxContents = contents;
}

// an overridden version (note keyword) because in the
// derived method we change the behavior
public override void DrawWindow()
{
base.DrawWindow(); // invoke the base method
Console.WriteLine( "Writing string to the listbox: {0}",
listBoxContents );
}

private string listBoxContents; // new member variable
} // end ListBox

public class Button : Window
{
public Button(
int top,
int left ) : base( top, left )
{}

// an overridden version (note keyword) because in the
// derived method we change the behavior
public override void DrawWindow()
{
Console.WriteLine( "Drawing a button at {0}, {1}\n",
top, left );
}
} // end Button

public class Tester
{
static void Main()
{
Window win = new Window( 1, 2 );
ListBox lb = new ListBox( 3, 4, "Stand alone list box" );
Button b = new Button( 5, 6 );
win.DrawWindow();
lb.DrawWindow();
b.DrawWindow();

Window[] winArray = new Window[3];
winArray[0] = new Window( 1, 2 );
winArray[1] = new ListBox( 3, 4, "List box in array" );
winArray[2] = new Button( 5, 6 );

for ( int i = 0; i < 3; i++ )
{
winArray[i].DrawWindow();
} // end for
} // end Main
} // end Tester

The output looks like this:

Window: drawing Window at 1, 2
Window: drawing Window at 3, 4
Writing string to the listbox: Stand alone list box
Drawing a button at 5, 6
Window: drawing Window at 1, 2
Window: drawing Window at 3, 4
Writing string to the listbox: List box in array
Drawing a button at 5, 6

In Example 11-2,ListBox derives from Window and implements its own version ofDrawWindow():

public override void DrawWindow()
{
base.DrawWindow(); // invoke the base method
Console.WriteLine ("Writing string to the listbox: {0}",
listBoxContents);
}

The keywordoverridetells the compiler that this class has intentionally overridden howDrawWindow()works. Similarly, you’ll overrideDrawWindow()in another class that derives fromWindow: theButton class.

In the body of the example, you create three objects: aWindow, aListBox, and aButton. Then you callDrawWindow()on each:

Window win = new Window(1,2);
ListBox lb = new ListBox(3,4,"Stand alone list box");
Button b = new Button(5,6);
win.DrawWindow();
lb.DrawWindow();
b.DrawWindow();

This works much as you might expect. The correctDrawWindow()method is called for each. So far, nothing polymorphic has been done (after all, you called theButtonversion ofDrawWindowon aButtonobject). The real magic starts when you create an array ofWindowobjects.

Because aListBoxis a Window, you are free to place aListBoxinto an array ofWindows. Similarly, you can add aButton to a collection ofWindows, because aButtonis a Window.

Window[] winArray = new Window[3];
winArray[0] = new Window(1,2);
winArray[1] = new ListBox(3,4,"List box in array");
winArray[2] = new Button(5,6);

The first line of code declares an array namedwinArray that will hold threeWindowobjects. The next three lines add newWindowobjects to the array. The first adds an object of typeWindow. The second adds an object of typeListBox(which is aWindow becauseListBoxderives fromWindow), and the third adds an object of typeButton, which is also a type ofWindow.

What happens when you callDrawWindow()on each of these objects?

for (int i = 0; i < winArray.Length-1; i++)
{
winArray[i].DrawWindow();
}

This code usesias a counter variable. It callsDrawWindow()on each element in the array in turn. The valueiis evaluated each time through the loop, and that value is used as an index into the array.

All the compiler knows is that it has threeWindowobjects and that you’ve calledDrawWindow()on each. If you had not markedDrawWindow()asvirtual,Window’s originalDrawWindow()method would be called three times.

However, because you did markDrawWindow()as virtual, and because the derived classes override that method, when you callDrawWindow()on the array, the right thing happens for each object in the array. Specifically, the compiler determines the runtime type of the actual objects (aWindow, aListBox, and aButton) and calls the right method on each. This is the essence of polymorphism.

The runtime type of an object is the actual (derived) type. At compile time, you do not have to decide what kind of objects will be added to your collection, so long as they all derive from the declared type (in this case,Window). At runtime, the actual type is discovered and the right method is called. This allows you to pick the actual type of objects to add to the collection while the program is running.

Note that throughout this example, the overridden methods are marked with the keywordoverride:

public override void DrawWindow()

The compiler now knows to use the overridden method when treating these objects polymorphically. The compiler is responsible for tracking the real type of the object and for handling the late binding, so thatListBox.DrawWindow()is called when the Window reference really points to aListBox object.

No comments:

Post a Comment