Tuesday, March 10, 2009

polymorphism in C#

Maxotek Blog » C# Tutorial


The term polymorphism means multiple forms. In the world of programming, it refers to the ability of an object to exist in multiple forms. Polymorphism is one one of the key concepts of Object Oriented Programming.

Why do we need polymorphism?

Polymorphism is there to increase the complexity of the already complex world of programming. Just kidding! By using functions, we break down the program into smaller logical parts that are easier to understand, implement and thereby maintain. In this sense, the function is the smallest self sufficient (ideally) logical block of a program. As you get to know more about the real world programming, you’ll find that even these small logical blocks are often very big and rather complex. Consider the following function which is used to calculate the area of a circle from the radius entered as integer.

1

2
3
4
5
double CalcArea(int Radius)


{
double Area = Math.PI * Radius * Radius;

return Area;
}

This function can only handle integers as input. What if the Radius was in double? The function would fail in such a case. We can create another function named CalcAreaDouble to handle the input in double.

1

2
3
4
5
double CalcAreaDouble(double Radius)


{
double Area = Math.PI * Radius * Radius;

return Area;
}

While, this works, there’s something wrong about this method. The name of a function should describe(in brief) what it does and not the type of values it returns or takes as input. Both these functions do the same job, Calculate Area. Hence, they should have the same name. Thanks to Polymorphism, we can declare two functions with the same name, inside the same class.

1

2
3
4
5
6
7
8
9
10
11
double CalcArea(int Radius)


{
double Area = Math.PI * Radius * Radius;

return Area;
}

double CalcArea(double Radius)

{
double Area = Math.PI * Radius * Radius;

return Area;
}

This is called function overloading. For two functions with the same name to co-exist, they must follow certain rules:-

  • The functions must differ in either the type, the sequence or the number of input parameters i.e they must have different function signatures. The CalcArea functions have different types of parameters.

    These two functions differ in the number of parameters:-

    1
    
    2
    3
    4
    5
    6
    7
    8
    9
    int SumOfNum(int Num1, int Num2)
    

    {
    return Num1 + Num2;
    }


    int SumOfNum(int Num1, int Num2, int Num3)

    {
    return Num1 + Num2 + Num3;

    }

    While, these have different sequence:-

    1
    
    2
    3
    4
    5
    6
    7
    8
    9
    float SumOfNum(int Num1, float Num2)
    

    {
    return Num1 + Num2;
    }


    float SumOfNum(float Num1, int Num2)

    {
    return Num1 + Num2;
    }
  • Return type has no influence on function overloading. Hence, these two functions cannot co-exist:-
    1
    
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    double CalcArea(int Radius)
    

    {
    double Area = Math.PI * Radius * Radius;

    return Area;
    }

    int CalcArea(int Radius)

    {
    double Area = Math.PI * Radius * Radius;

    return Area;
    }
Static Polymorphism

Function overloading along with operator overloading which we shall look at in the next tutorial are ways of implementing static polymorphism. Static polymorphism inturn, is one of the two types of Polymorphism, the other being Dynamic polymorphism. Also called as early binding, this concept refers to an entity existing in different forms. Just like a teacher who carries on the role of a father, a husband, a comrade besides being a teacher, the functions exist in different forms, each specializing in handling one type of role. In static polymorphism, the response to a function is decided at compile time and hence it is also called early binding.

Overloading Constructors

Since constructors are also functions and can be parameterized(takes parameters as input), they can be overloaded. This allows flexibility while creating an object. Consider the Rectangle class declared in the System.Drawing namespace. It can be instantiated by either of the two methods.

Method 1: Using default constructor

In this method, the bounds of the rectangle object Rect are individually set.

1

2
3
4
5
Rectangle Rect = new Rectangle();


Rect.X = 10;
Rect.Y = 20;

Rect.Width = 30;
Rect.Height = 40;

Method 2: Using the Parametrized Constructor

Rectangle Rect = new Rectangle(10, 20, 30, 40);

Notice, how much easier this method is. The initialization in this case is handled by the constructor of the Rectangle class.

The rectangle class would have an implementation similar to this one:-

1

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Rectangle

{
public int X;

public int Y;
public int Width;

public int Height;

Rectangle()

{
this.X = 0;

this.Y = 0;

this.Width = 0;

this.Height = 0;

}
Rectangle(int x, int y, int width, int height)

{
this.X = x;

this.Y = y;
this.Width = width;

this.Height = height;
}

}

As you can see, the class has two overloaded constructors, one being the default constructor which takes no parameter and initializes the values of X, Y, Width and Height to 0. The other one initializes these values based on the inputs to it.

Note: This code is just to give you an idea of how the constructors are implemented. In actual fact, the rectangle class would be a lot more complex than this and would require further codes inside the constructors.


Operator Overloading

By default, out of the various C# operators, only the dot operator can be applied to user-defined types. Consider the following code snippet:-

1

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Program


{
static void Main(string[] args)

{
Car Car1 = new Car();

Car Car2 = new Car();


Car Car3 = Car1 + Car2;
}

}

class Car
{
public int Speed;

public string Name;
}

The compiler generates an error at line 8 saying that the operator + cannot be applied to objects of the Car class. How is the compiler supposed to know whether we want to add the speeds of two cars or concatenate their names. This is where the concept of Operator Overloading jumps in. Before we see how the + operator is prepared to work with the objects of the Car class, we need to understand how the concept works. Given below is a code snippet which accomplishes the addition in objects using a specialized method - Add().

1

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

28
29
using System;


class Program

{
static void Main(string[] args)

{
Car Car1 = new Car();

Car Car2 = new Car();

Car1.Speed = 30;
Car2.Speed = 70;


Car Car3 = Car.Add(Car1, Car2);

Console.WriteLine("Car3's Speed = {0}", Car3.Speed);

Console.Read();
}

}

class Car
{
public int Speed;

public string Name;

public static Car Add(Car Car1, Car Car2)

{
Car NewCar = new Car();

NewCar.Speed = Car1.Speed + Car2.Speed;

return NewCar;
}
}

The Add method is declared as static so it can be invoked at the class level as Car.Add(). It takes two parameters of the type Car as inputs. It then creates a new object of the Car class, with a speed equal to the sum of the speeds of the input objects - Car1 and Car2. Finally, the new object is returned. In this way, the addition of two objects can be accomplished. The same concept is used in Operator Overloading, except for the fact that the specialized function in their case are of the format operator .

Note: The logic inside the Add function is totally up to the the programmer.

1

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

28
29
using System;


class Program

{
static void Main(string[] args)

{
Car Car1 = new Car();

Car Car2 = new Car();

Car1.Speed = 30;
Car2.Speed = 70;


Car Car3 = Car1 + Car2;
Console.WriteLine("Car3's Speed = {0}", Car3.Speed);

Console.Read();
}

}

class Car
{
public int Speed;

public string Name;

public static Car operator +(Car Car1, Car Car2)

{
Car NewCar = new Car();

NewCar.Speed = Car1.Speed + Car2.Speed;

return NewCar;
}
}

As you can see, the + operator utilizes a function in the background to do its neat work. Although the concept in both the cases is same, using operators allows much greater ease. The code Car Car3 = Car1 + Car2; is much closer to the real world and its easier to type, isn’t it?

Here’s a table showing which operators can/cannot be overloaded in C#.

Operators Description
+ - ! ~ ++ – These being unary operators take one operand and can be overloaded.
+ - * / % Binary arithmetic operators take two operands and can be overloaded.
== != < > <= >= Comparison operators take two parameters and can also be overloaded.
&& || These need to be overloaded indirectly using the & and |
+= -= *= /= %= Arithmetic assignment operators cannot be overloaded.
= . ?: -> new is sizeof typeof These special operators cannot be overloaded.

Note: The overloading methods must be declared as public and static.

Overloading Prefix Unary Operators

Below is the logic to accomplish unary - operator overloading. This operator changes the sign of the relative speed and is used as: -

1

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
using System;


class Program

{
static void Main(string[] args)

{
Car Car1 = new Car();

Car1.RelativeSpeed = 30;
Car Car2 = -Car1;

}
}

class Car
{
public int RelativeSpeed;

public string Name;

public static Car operator -(Car CarObj)

{
Car NewCar = new Car();

NewCar.RelativeSpeed = -CarObj.RelativeSpeed;

return NewCar;
}
}

Notice, that the overloaded function takes only a single parameter. If another parameter was specified, this function would overload the binary - operator. Structures being user-defined data types can also be overloaded in this fashion. The only difference is that structures being value types, can be implemented in an easier way.

1

2
3
4
5
6
7
8
9
10
11
struct Car


{
public int RelativeSpeed;
public string Name;


public static Car operator -(Car CarObj)

{
CarObj.RelativeSpeed = -CarObj.RelativeSpeed;

return CarObj;
}
}

If the same implementation of the Operator - function was used in a class, both Car1 and Car2 would have the same RelativeSpeed of -30 after line 9. So, in the class implementation we have created a new object of the Car class.

Overloading Pre & Post Increment Operators

Unlike, C++, C# handles the Pre & Post Increment Operators by itself. Hence, we don’t need to overload them separately. Below is the implementation of the ++ operator in the Car structure.

1

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

using System;


class Program

{
static void Main(string[] args)

{
Car Car1 = new Car();

Car1.RelativeSpeed = 30;
Car Car2 = Car1++;

Car Car3 = ++Car1;
Console.WriteLine("Relative speed of Car1 = {0}, Car2 = {1} and Car3 = {2}"

, Car1.RelativeSpeed, Car2.RelativeSpeed, Car3.RelativeSpeed);

Console.Read();
}

}

struct Car
{
public int RelativeSpeed;

public string Name;

public static Car operator ++(Car CarObj)

{
CarObj.RelativeSpeed++;;
return CarObj;

}
}

At line 9, the overloaded function for the object Car1 is invoked. The operation being post increment, Car2 gets assigned the current value of Car1 which has a relative speed of 30. After this the RelativeSpeed is incremented by 1. So, Car1 has a relative speed of 31 now. In the next line, pre increment results in the value of Car1 to be incremented by 1 before the value is assigned to Car3. Car1’s RelativeSpeed is now equal to 32. This value gets copied in Car3. So, the output of the code is: Relative speed of Car1 = 32, Car2 = 30 and Car3 = 32.

If the same logic was used in the Car Class’s overloaded function, the output would be: Relative speed of Car1 = 32, Car2 = 32 and Car3 = 32. Why does this happen? As mentioned before, the class is a reference type. Hence, when we say Car1 = Car2, it implies that Car1 now points to the same object in the memory as does Car2. In structures, the same statement would be equivalent to Copy the value of Car2 in Car1.

Arithmetic Assignment Operators

Recall that, the Arithmetic Assignment operators ( += -= *= /= %= ) cannot be overloaded. This is because A += B is equivalent to A = A + B, A -= B is equivalent to A = A - B and so on. Once, you overload the binary + operator, you can perform the += operation on the objects of the class. Same is true for the rest of the Arithmetic operators.

Logical Operators

Overloading Logical Operators is a bit tricky. Firstly, to overload the && operator the function name should be operator & and for || operator it should be operator |. Secondly, before performing these boolean short-circuit operations, the true and false operators need to be defined.

1

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

32
33
34
35
36
37
38
using System;



class Program
{
static void Main(string[] args)

{
Car Car1 = new Car();

Car Car2 = new Car();

Car1.Running = true;
Car2.Running = false;


Car Car3 = Car1 && Car2;
Console.WriteLine(Car3.Running);

Console.Read();
}

}

class Car
{
public bool Running;


public static Car operator &(Car Car1, Car Car2)

{
Car NewCar = new Car();

NewCar.Running = Car1.Running && Car2.Running;

return NewCar;
}

public static bool operator true(Car CarObj)

{
return true;
}

public static bool operator false(Car CarObj)

{
return false;
}
}

No comments: