Blue Theme Orange Theme Green Theme Red Theme
 
Ads by Lake Quincy Media
Home | Forums | Videos | Photos | Downloads | Blogs | Interviews | Jobs | Beginners | Training
 | Consulting  
Submit an Article Submit a Blog 
 Login Close
User Id:
Password:
 
Forgot Password
Forgot Username
Why Register
 Jump to
Skip Navigation Links
TechnologyExpand Technology
WebsiteExpand Website
 Resources  
Close
 Our Network  
Close
Search :       Advanced Search »
Home » C# Language » Polymorphism, Up-casting and Down-casting

Polymorphism, Up-casting and Down-casting

This article gives an introduction to polymorphism in C#. Using the example created in this introduction, the article then goes on to describe how we up-cast and down-cast objects.

Total page views :  35828
Total downloads :  174
   Print Read/Post comments Post a comment  Similar Articles  
   Email to a friend  Bookmark  Author's other articles  
Download Files:
Polymorphism.zip
 
Become a Sponsor

The original aim of this article was to explain Up-casting and Down-casting. Having started writing the article, I was struggling to find an ideal way to describe these two concepts. Having thought further about it, one way I found was to introduce the concept of polymorphism to help explain up and down-casting. This has expanded the article further and when you have finished reading it, you should hopefully understand the three concepts better.

Polymorphism

Polymorphism is a powerful aspect of object oriented programming. According to many searches on the Internet to find a definitive meaning, I have found two that seem to explain it quite nicely, these are "Having many forms" and "Having multiple forms".

 

Consider the following. Ask yourself, what is a circle? Most would say a circle is a shape. A square, rectangle and a triangle are also shapes. So, what we are saying is a shape can take on many forms or has multiple forms. So how do we implement this concept in C#?

 

First we need to design our base class, this will be called Shape. Our shape class will implement a constructor that will accept an X and Y co-ordinate and a method that will draw our shape. Below is the code for our shape class.

 

public class Shape

{

    protected int m_xpos;

    protected int m_ypos;

 

    public Shape()

    {

    }

 

    public Shape(int x, int y)

    {

        m_xpos = x;

        m_ypos = y;

    }

 

    public virtual void Draw()

    {

        Console.WriteLine("Drawing a SHAPE at {0},{1}", m_xpos, m_ypos);

    }

}

We now need to make our Draw method behave polymorphically. To do this, we declare our method with the keyword virtual in our base class. When we derive a class from shape we are able to implement a specific version of Draw by overriding the base class implementation of Draw. To do this, we use the keyword override when declaring our Draw method in our derived classes. 

What we will now do is implement two derived classes from our shape base class. We will implement Circle and Square. This will show how we override our base class Draw method.

Circle and Square derived classes

 

One thing to note about the base class is the fact that I have used protected variables for the X and Y co-ordinates. Ideally you would use public properties and declare m_xpos and m_ypos as private.

 

Here are our two derived classes.

 

public class Square : Shape

{

    public Square()

    {

    }

 

    public Square(int x, int y) : base(x, y)

    {

    }

 

    public override void Draw()

    {

        Console.WriteLine("Drawing a SQUARE at {0},{1}", m_xpos, m_ypos);

    }

}

And finally Circle...
 

public class Circle : Shape

{

    public Circle()

    {

    }

 

    public Circle(int x, int y) : base(x, y)

    {

    }

 

    public override void Draw()

    {

        Console.WriteLine("Drawing a CIRCLE at {0},{1}", m_xpos, m_ypos);

    }

} 

 

Notice, the highlighted code blocks. We have overridden the base class implementation of Draw in each of our derived classes. Basically override tells the compiler that we are intentionally overriding the behaviour of Draw.

 

We will now test the classes we have written, by putting together a simple piece of code that will create an instance of Shape, Circle and Square and call the Draw methods for each type.

 

class Program

{

    static void Main(string[] args)

    {

        Shape sh = new Shape(100, 100);

        sh.Draw();

 

        Square sq = new Square(200, 200);

        sq.Draw();

 

        Circle ci = new Circle(300, 300);

        ci.Draw();

    }

}

The output generated by the test code is:

 

Drawing a SHAPE at 100,100

Drawing a SQUARE at 200,200

Drawing a CIRCLE at 300,300

 

So what's happened that's polymorphic? Well, at this point absolutely nothing! Consider the code below.

 

class Program

{

    static void Main(string[] args)

    {

        Shape[] shapes = new Shape[3];

        shapes[0] = new Shape(100, 100);

        shapes[1] = new Square(200, 200);

        shapes[2] = new Circle(300, 300);

 

        foreach (Shape shape in shapes)

        shape.Draw();

    }

}

What we have done is to create an array of the type Shape. Because Square and Circle are derived from Shape, we are able to put them in our array. What we are then doing is looping through all the elements of our array and calling Draw for each of our types. Because we have overridden the Draw method in each of our derived classes the output of our code is:

 

Drawing a SHAPE at 100,100

Drawing a SQUARE at 200,200

Drawing a CIRCLE at 300,300

 

If we did not override Draw in one of our derived classes, the base class implementation of Draw would be called. For example, if we declared Draw in our Circle class as follows:

 

public void Draw()

{

    Console.WriteLine("Drawing a CIRCLE at {0},{1}", m_xpos, m_ypos);

}

 

Our output would be:

 

Drawing a SHAPE at 100,100

Drawing a SQUARE at 200,200

Drawing a SHAPE at 300,300

 

By declaring the method as shown above, we will receive a compiler warning as follows:

 

Polymorphism.Circle.Draw() : Hides inherited member.

  

Polymorphism.Shape.Draw() :To make the current member override that implementation, add the override keyword. Otherwise add the new keyword.

 

If we do not want to override the base class method, we need to use the new keyword when declaring our method, for example:

 

public new void Draw()

{

    Console.WriteLine("Drawing a CIRCLE at {0},{1}", m_xpos, m_ypos);

}

This will remove the compiler warning. Basically we are telling the compiler that we are not overriding the base class implementation.

 

So what have we achieved with polymorphism? What we have been able to do is create an array of shapes and add a specific shape to each element of the array. When drawing each shape, we have not concerned ourselves with the fact that the shape could be a circle or a square. We have simply said, "here is an array of shapes, please draw them!"  It is the responsibility of the compiler to discover the real type and to ensure that the correct implementation is called.  

Up-casting 

Now we come on to one of the original goals of the article. With the simple example above of polymorphism, you should be able to quickly understand what up-casting is, in fact we have already used up-casting in our example.

 

The diagram below is a UML diagram for our polymorphism example.



Figure 1: UML Diagram For Shape.
 


Consider the following code:

 

Shape s = new Circle(100, 100);

We have cast Circle to the type Shape. This is perfectly legal code (as we saw in the Polymorphism example). This is possible, because Circle has been derived from Shape and you expect all methods and properties of Shape to exist in Circle. Executing the Draw method by doing s.Draw() gives the following output:

 

Drawing a CIRCLE at 100,100

 

If we had declared the Draw method in Circle as follows, public new void Draw() the output would have been:

 

Drawing a SHAPE at 100,100

 

As we have already mentioned, marking the method with new, tells the compiler that we are not overriding the base class implementation of the method.

 

So why is this called up-casting? Consider the diagram above. From Circle, we are moving up the object hierarchy to the type Shape, so we are casting our object "upwards" to its parent type.

 

Up-casting is implicit and is safe. What do we mean by safe? Well, we can happily cast Circle to Shape and expect all the properties and methods of Shape to be available. 

Down-casting 

The flip side of the coin to up-casting is ...yes you guessed it, down-casting. Down-casting takes a little more understanding and you have to be very careful when down-casting types.

 

To help us better understand down-casting, we are going to add a new method to our Circle class. This will be a simple method called FillCircle.

 

public void FillCircle()

{

    Console.WriteLine("Filling CIRCLE at {0},{1}", m_xpos, m_ypos);

}

Using the example from up-casting, we know that we are able to write the following:

 

Shape s = new Circle(100, 100);

 

We are then free to call the Draw method. Having added the FillCircle method to our Circle class, are we able to call this method by doing the following?

 

s.FillCircle ();

 

In short, the answer is no. Because we have cast Circle to the type Shape, we are only able to use methods found in Shape, that is, Circle has inherited all the properties and methods of Shape. If we want to call FillCircle, we need to down-cast our type to Circle. Why is it called down-casting? Quite simply, we are moving down the object hierarchy, from Shape down to Circle.

 

So how do we code a down-cast from Shape to Circle? The code for doing this is quite simple:

 

Circle c;

c = (Circle)s;

 

Simply, we are declaring c as the type Circle and explicitly casting s to this type. We are now able to call the FillCircle method by doing the following:

 

c.FillCircle();

 

This gives us the following output:

 

Drawing a CIRCLE at 100,100

Filling CIRCLE at 100,100

 

We could also write ((Circle)s).FillCircle() reducing the lines of code needed to down-cast our type and call the required method.

 

Down-casting is potentially unsafe, because you could attempt to use a method that the derived class does not actually implement. With this in mind, down-casting is always explicit, that is, we are always specifying the type we are down-casting to. 

The as and is keywords

 

To demonstrate as and is, lets implement a FillSquare method in our Square class. Our code could be:

 

Public void FillSquare()

{

    Console.WriteLine("Filling SQUARE at {0},{1}", m_xpos, m_ypos);

}

 

Taking some code from our polymorphism example, as follows:

 

class Program

{

        static void Main(string[] args)

        {

            Shape[] shapes = new Shape[3];

            shapes[0] = new Shape(100, 100);

            shapes[1] = new Square(200, 200);

            shapes[2] = new Circle(300, 300);

 

            foreach (Shape shape in shapes)

                shape.Draw();

           

        }

}

We have Circle and Square in our array of type Shape. How do we know what type we have to down-cast to in order to call the correct routine to fill the shape?

 

Within the foreach statement, we have to test the type of shape being drawn before we down-cast, we will use the is keyword to test type. We will modify the code above, to ensure the correct method is called to fill our shapes as they are drawn.

 

foreach (Shape shape in shapes)

{

    shape.Draw();

 

    if (shape is Circle)

        ((Circle)shape).FillCircle();

 

        if (shape is Square)

            ((Square)shape).FillSquare();

}
 

Within the loop, we are performing a test to see if the type of shape is a Circle or a Square, depending on the result, we are down-casting our type in order to call the correct fill routine. Our output now is:

 

Drawing a SHAPE at 100,100
Drawing a SQUARE at 200,200
Filling SQUARE at 200,200

Drawing a CIRCLE at 300,300

Filling CIRCLE at 300,300

 

I have highlighted the changes our code has made to the output. The alternative to the is keyword is as. We can write:

 

Circle c = shape as Circle;

 

The difference is that as tries to cast shape to a type (in our example Circle). If the cast fails, c is set to null. For example:

 

Circle c = shape as Circle;

If(c!=null)

  c.FillCircle(); 

Conclusion

 

Quite a lot of ground has been covered in this article. Whilst the polymorphism example is very simple, it gives you an understanding of what polymorphism is and how you could implement it in your design.

 

The down-casting example is a little contrived. Some of you may be thinking why not just implement a base class method for filling the shapes and override it in the derived classes. The idea of implementing the fill routines as I did was to aid in the explanation of down-casting and has only been done for this reason.

 

I hope you have enjoyed reading this article and have learned something from it.


Login to add your contents and source code to this article
 About the author
 
Phil Curnow
Phil is a programmer with 15 years professional experience. He currently works for a Local Authority in the United Kingdom. His programming language of choice is C#, but over the years he has used many languages within his work. His web site and blog can be found at http://www.curnow.biz
Looking for C# Consulting?
C# Consulting is founded in 2002 by the founders of C# Corner. Unlike a traditional consulting company, our consultants are well-known experts in .NET and many of them are MVPs, authors, and trainers. We specialize in Microsoft .NET development and utilize Agile Development and Extreme Programming practices to provide fast pace quick turnaround results. Our software development model is a mix of Agile Development, traditional SDLC, and Waterfall models.
Click here to learn more about C# Consulting.
 
Introducing MaxV - one click. infinite control. Hyper-V Hosting from MaximumASP.
Finally – a virtual platform that delivers next-generation Windows Server 2008 Hyper-V virtualization technology from a managed hosting partner you can truly depend on. Visit www.maximumasp.com/max for a FREE 30 day trial. Hurry offer ends soon. Climb aboard the MaxV platform and take advantage of High Availability, Intelligent Monitoring, Recurrent Backups, and Scalability – with no hassle or hidden fees. As a managed hosting partner focused solely on Microsoft technologies since 2000, MaximumASP is uniquely qualified to provide the superior support that our business is built on. Unparalleled expertise with Microsoft technologies lead to working directly with Microsoft as first to offer IIS 7 and SQL 2008 betas in a hosted environment; partnering in the Go Live Program for Hyper-V; and product co-launches built on WS 2008 with Hyper-V technology.
Dynamic PDF
ceTE software specializes in components for dynamic PDF generation and manipulation. The DynamicPDF™ product line allows you to dynamically generate PDF documents, merge PDF documents and new content to existing PDF documents from within your applications.
Go.NET
Build custom interactive diagrams, network, workflow editors, flowcharts, or software design tools. Includes many predefined kinds of nodes, links, and basic shapes. Supports layers, scrolling, zooming, selection, drag-and-drop, clipboard, in-place editing, tooltips, grids, printing, overview window, palette. 100% implemented in C# as a managed .NET Control. Document/View/Tool architecture with many properties&events. Optional automatic layout.
Dundas Software
Dundas Chart for .NET is the most advanced .NET charting package available today.  With an extremely complete feature set, elegant architecture and easy implementation, Dundas Chart can quickly add advanced Charting functionality to enhance and transform ASP.NET and Windows Forms applications.  Whether you are implementing charting into internal projects, or building applications for clients, Dundas Chart offers advanced technology and advanced results to get the most out of data.
Clickatell's SMS Gateway
Clickatell's Developer Solutions allow you to SMS enable any website or application via a range of API's. Learn More about our API connections.
Free access to .NET Memory Management video
Everything you need to know about Garbage Collection, Temporary Objects, Fragmentation, Finalization and common causes of memory leaks in .NET. Watch the video here.
Microsoft Visual Studio 2010 Professional
Microsoft Visual Studio 2010 Professional will launch on April 12, but you can beat the rush and secure your copy today by pre-ordering at the affordable estimated retail price of $549 (US). Pre-order now.
Nevron Chart for .NET 2010.1 Now Available
The leading .NET charting control now features PDF, Flash and Silverlight export, visualization of large datasets and more. Deliver true charting functionality to your BI, Scorecard, Presentation or Scientific apps. Download evaluation now.
Developer-Ready ASP.NET 2.0 Web Hosting with 3 MONTHS FREE
Now supporting .NET 3.0 Framework with Windows Workflow Foundation, Windows Communication Foundation (WCF), Windows Presentation Foundation (WPF), windows CardSpace (WCS)! Providing more flexibility for Developers with Web Services Support and a User/Permission Manger. Also supporting MS SQL 2005/2000 with Real-Time Backups, FREE Automated Attach .MDF Tool, FREE SQL Restore and Shrink SQL DB Tools, and SQL
 
   Print Read/Post comments Post a comment  Similar Articles  
   Email to a friend  Bookmark  Author's other articles  
Download Files:
Polymorphism.zip
 
 Post a Feedback, Comment, or Question about this article
Subject:  
Comment:  
Become a Sponsor
 Comments
Polymorphism by Willy On June 25, 2007
This was a good article about Polymorphism. I tried to research on books and even on internet resources but somehow, it was a little bit complicated topic, to add the fact that some concepts where mislead or used interchangeably on other OOP design and principles. I can say this is a two-thumbs up and a little bit simple and nice code! -Willy David Jr
Reply | Email | Delete | Modify | 
Very good by Navdeep On June 26, 2007
Excellent article !! You have achieved what you were aiming. Congrats ..
Reply | Email | Delete | Modify | 
Short, concise and well written by Roland On August 20, 2007
I found the sections on the use of "is" and "as" very helpful.
Reply | Email | Delete | Modify | 
Great Article by Mahesh On August 30, 2007
Good work Phil. I like the sample examples.
Reply | Email | Delete | Modify | 
Many thanks by Phil On August 31, 2007
Many thanks to everyone who has left comments on the article. It really does 'fire you up' to write more articles and share your knowledge. This site is a great resource and community of developers and a site I always recommend to fellow developers.
Reply | Email | Delete | Modify | 
Thank you very much by maha On September 24, 2007

Thank you very much for your explanation, Phil Curnow. This is an excellent article. Your endeavour to explain the subject is much appreciated.

In the example, you have clearly shown the difference between the keyword override and new.

 

Reply | Email | Delete | Modify | 
nice one by sri On November 5, 2008
clear cut basic of upcasting and downcasting ,thank you
Reply | Email | Delete | Modify | 
thanks by Vijay On October 28, 2009
very well explained!
Reply | Email | Delete | Modify | 

 Hosted by MaximumASP  |  Found a broken link?  |  Contact Us  |  Terms & conditions  |  Privacy Policy  |  Site Map  |  Suggest an Idea  |  Media Kit
Current Version: 5.2009.6.2
 © 2010  contents copyright of their authors. Rest everything copyright Mindcracker. All rights reserved.