C# Polymorphism
Polymorphism is the ability of an action to have multiple different forms or manifestations.
Polymorphism means having multiple forms. In object-oriented programming, polymorphism often manifests as "one interface, multiple functions."
Polymorphism can be static or dynamic. In static polymorphism, the response of a function is determined at compile time. In dynamic polymorphism, the response of a function is determined at runtime.
In C#, every type is polymorphic because all types, including user-defined types, inherit from Object.
Polymorphism is the same interface, performing different operations with different instances, as shown:
>
In reality, consider pressing the F1 key:
- If you are in the Flash interface, the AS 3 help document pops up;
- If you are in Word, the Word help pops up;
- If you are in Windows, the Windows Help and Support pops up.
The same event produces different results on different objects.
Static Polymorphism
At compile time, the mechanism of connecting a function with an object is known as early binding, also called static binding. C# provides two techniques to achieve static polymorphism. These are:
- Function Overloading
- Operator Overloading
Operator overloading will be discussed in the next chapter, but now we will discuss function overloading.
Function Overloading
You can have multiple definitions for the same function name in the same scope. The definitions of the function must differ from each other by the types and/or the number of arguments in the parameter list. You cannot overload function declarations that differ only by return type.
The following example demonstrates several functions Add(), which are used to add different numbers of parameters:
Example
using System;
namespace PolymorphismApplication
{
public class TestData
{
public int Add(int a, int b, int c)
{
return a + b + c;
}
public int Add(int a, int b)
{
return a + b;
}
}
class Program
{
static void Main(string[] args)
{
TestData dataClass = new TestData();
int add1 = dataClass.Add(1, 2);
int add2 = dataClass.Add(1, 2, 3);
Console.WriteLine("add1 :" + add1);
Console.WriteLine("add2 :" + add2);
}
}
}
The following example demonstrates several functions print(), which are used to print different data types:
Example
using System;
namespace PolymorphismApplication
{
class Printdata
{
void print(int i)
{
Console.WriteLine("Printing int: {0}", i );
}
void print(double f)
{
Console.WriteLine("Printing float: {0}" , f);
}
void print(string s)
{
Console.WriteLine("Printing string: {0}", s);
}
static void Main(string[] args)
{
Printdata p = new Printdata();
// Call print to print integer
p.print(1);
// Call print to print float
p.print(1.23);
// Call print to print string
p.print("Hello C#");
Console.ReadKey();
}
}
}
When the above code is compiled and executed, it produces the following result:
Printing int: 1
Printing float: 1.23
Printing string: Hello C#
Dynamic Polymorphism
Dynamic polymorphism is discussed in the next section. C# allows you to create abstract classes using the keyword abstract, which are used to provide partial implementations of interfaces. When a derived class inherits from this abstract class, the implementation is completed. Abstract classes contain abstract methods that can be implemented by derived classes, which have more specialized functionality.
Please note the following rules about abstract classes:
- You cannot create an instance of an abstract class.
- You cannot declare an abstract method outside of an abstract class.
- By placing the keyword sealed before the class definition, you can declare the class as a sealed class. When a class is declared as sealed, it cannot be inherited. Abstract classes cannot be declared as sealed.
The following program demonstrates an abstract class:
Example
using System;
namespace PolymorphismApplication
{
abstract class Shape
{
abstract public int area();
}
class Rectangle: Shape
{
private int length;
private int width;
public Rectangle( int a=0, int b=0)
{
length = a;
width = b;
}
public override int area ()
{
Console.WriteLine("Rectangle class area:");
return (width * length);
}
}
class RectangleTester
{
static void Main(string[] args)
{
Rectangle r = new Rectangle(10, 7);
double a = r.area();
Console.WriteLine("Area: {0}",a);
Console.ReadKey();
}
}
}
When the above code is compiled and executed, it produces the following result:
Rectangle class area:
Area: 70
When there is a function defined in a class that needs to be implemented in the inherited class, you can use a virtual method.
A virtual method is declared using the keyword virtual.
A virtual method can have different implementations in different inherited classes.
Calls to a virtual method are resolved at runtime.
Dynamic polymorphism is achieved through abstract classes and virtual methods.
The following example creates a base class Shape and derived classes Circle, Rectangle, and Triangle. The Shape class provides a virtual method called Draw, which is overridden in each derived class to draw the specific shape of the class.
Example
using System;
using System.Collections.Generic;
public class Shape
{
public int X { get; private set; }
public int Y { get; private set; }
public int Height { get; set; }
public int Width { get; set; }
// Virtual method
public virtual void Draw()
{
Console.WriteLine("Performing base class drawing tasks");
}
}
class Circle : Shape
{
public override void Draw()
{
Console.WriteLine("Drawing a circle");
base.Draw();
}
}
class Rectangle : Shape
{
public override void Draw()
{
Console.WriteLine("Drawing a rectangle");
base.Draw();
}
}
class Triangle : Shape
{
public override void Draw()
{
Console.WriteLine("Drawing a triangle");
base.Draw();
}
}
class Program
{
static void Main(string[] args)
{
// Create a List<Shape> object and add Circle, Triangle, and Rectangle to it
var shapes = new List<Shape>
{
new Rectangle(),
new Triangle(),
new Circle()
};
// Use a foreach loop to iterate through the derived classes in the list and call the Draw method for each Shape object
foreach (var shape in shapes)
{
shape.Draw();
}
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
When the above code is compiled and executed, it produces the following result:
Draw a rectangle
Execute the base class drawing task
Draw a triangle
Execute the base class drawing task
Draw a circle
Execute the base class drawing task
Press any key to exit.
The following program demonstrates the calculation of the area of different shape images through the virtual method area():
## Example
using System;
namespace PolymorphismApplication
{
class Shape
{
protected int width, height;
public Shape( int a=0, int b=0)
{
width = a;
height = b;
}
public virtual int area()
{
Console.WriteLine("Parent class area:");
return 0;
}
}
class Rectangle: Shape
{
public Rectangle( int a=0, int b=0): base(a, b)
{
}
public override int area ()
{
Console.WriteLine("Rectangle class area:");
return (width * height);
}
}
class Triangle: Shape
{
public Triangle(int a = 0, int b = 0): base(a, b)
{
}
public override int area()
{
Console.WriteLine("Triangle class area:");
return (width * height / 2);
}
}
class Caller
{
public void CallArea(Shape sh)
{
int a;
a = sh.area();
Console.WriteLine("Area: {0}", a);
}
}
class Tester
{
static void Main(string[] args)
{
Caller c = new Caller();
Rectangle r = new Rectangle(10, 7);
Triangle t = new Triangle(10, 5);
c.CallArea(r);
c.CallArea(t);
Console.ReadKey();
}
}
}
When the above code is compiled and executed, it produces the following result:
Rectangle class area:
Area: 70
Triangle class area:
Area: 25