Flipkart Search

Search This Blog

Wednesday, March 18, 2009

Polymorphism, Method Hiding and Overriding in C#



Overview

One of the fundamental concepts of object oriented software development is polymorphism. The term polymorphism (from the Greek meaning "having multiple forms") in OO is the characteristic of being able to assign a different meaning or usage to something in different contexts - specifically, to allow a variable to refer to more than one type of object.

Example Class Hierarchy

Let's assume the following simple class hierarchy with classes A, B and C for the discussions in this text. A is the super- or base class, B is derived from A and C is derived from class B. In some of the easier examples, we will only refer to a part of this class hierarchy.

Inherited Methods

A method Foo() which is declared in the base class A and not redeclared in classes B or C is inherited in the two subclasses

using System;
namespace Polymorphism
{
class A
{
public void Foo() { Console.WriteLine("A::Foo()"); }
}

class B : A {}

class Test
{
static void Main(string[] args)
{
A a = new A();
a.Foo(); // output --> "A::Foo()"

B b = new B();
b.Foo(); // output --> "A::Foo()"
}
}
}

The method Foo() can be overridden in classes B and C:

using System;
namespace Polymorphism
{
class A
{
public void Foo() { Console.WriteLine("A::Foo()"); }
}

class B : A
{
public void Foo() { Console.WriteLine("B::Foo()"); }
}

class Test
{
static void Main(string[] args)
{
A a;
B b;

a = new A();
b = new B();
a.Foo(); // output --> "A::Foo()"
b.Foo(); // output --> "B::Foo()"

a = new B();
a.Foo(); // output --> "A::Foo()"
}
}
}

There are two problems with this code.

  • The output is not really what we, say from Java, expected. The method Foo() is a non-virtual method. C# requires the use of the keyword virtual in order for a method to actually be virtual. An example using virtual methods and polymorphism will be given in the next section.
  • Although the code compiles and runs, the compiler produces a warning:

...\polymorphism.cs(11,15): warning CS0108: The keyword new is required on 'Polymorphism.B.Foo()' because it hides inherited member 'Polymorphism.A.Foo()'

This issue will be discussed in section Hiding and Overriding Methods.

Virtual and Overridden Methods

Only if a method is declared virtual, derived classes can override this method if they are explicitly declared to override the virtual base class method with the override keyword.

using System;
namespace Polymorphism
{
class A
{
public virtual void Foo() { Console.WriteLine("A::Foo()"); }
}

class B : A
{
public override void Foo() { Console.WriteLine("B::Foo()"); }
}

class Test
{
static void Main(string[] args)
{
A a;
B b;

a = new A();
b = new B();
a.Foo(); // output --> "A::Foo()"
b.Foo(); // output --> "B::Foo()"

a = new B();
a.Foo(); // output --> "B::Foo()"
}
}
}

Method Hiding

Why did the compiler in the second listing generate a warning? Because C# not only supports method overriding, but also method hiding. Simply put, if a method is not overriding the derived method, it is hiding it. A hiding method has to be declared using the new keyword. The correct class definition in the second listing is thus:

using System;
namespace Polymorphism
{
class A
{
public void Foo() { Console.WriteLine("A::Foo()"); }
}

class B : A
{
public new void Foo() { Console.WriteLine("B::Foo()"); }
}

class Test
{
static void Main(string[] args)
{
A a;
B b;

a = new A();
b = new B();
a.Foo(); // output --> "A::Foo()"
b.Foo(); // output --> "B::Foo()"

a = new B();
a.Foo(); // output --> "A::Foo()"
}
}
}

Combining Method Overriding and Hiding

Methods of a derived class can both be virtual and at the same time hide the derived method. In order to declare such a method, both keywords virtual and new have to be used in the method declaration:

class A
{
public void Foo() {}
}

class B : A
{
public virtual new void Foo() {}
}

A class C can now declare a method Foo() that either overrides or hides Foo() from class B:

class C : B
{
public override void Foo() {}
// or
public new void Foo() {}
}

Conclusion

  • C# is not Java.
  • Only methods in base classes need not override or hide derived methods. All methods in derived classes require to be either defined as new or as override.
  • Know what your doing and look out for compiler warnings.

Classes in C#

A class is a datastructure that may contain
  • Data Members
    • Constants
    • Fields
  • Function Members
    • Methods
    • Properties
    • Events
    • Indexers
    • Operators
    • Instance constructors
    • Static Constructors
    • Destructors
An example of how you can declare a class is shown below.We will discuss about Access Modifiers, Attributes and Class-Modifiers in our later article. Access Modifiers, Attributes and Class-Modifiers are optional in a class declaration.

[Attributes] [Access Modifiers] [Class-Modifiers] class Class-Name
{
//Body of the class
}

In the example below
  • We have a class with name ClassDemo. In this class we have 2 fields, 1 constructor and a method.
  • Out of the 2 fields we have, One field is a constant and the other is a variable of type integer. Fields are generally used to store the data related to the class.
  • The difference between variables and constants is that you can declare a variable at one place, assign a value to it at another place and you can also change the value later in the code. Where as for a constant you can only assign a value when you declare it. This is a common interview question generally asked.
  • The constructor of this class is used to initialize the private data field _radius. Construtors are generally used to initialize the class datafields. Constructors will have the same name as that of a class and does not have return type. This is how we can differentiate a constructor from a method.
  • Finally we have a method CalculateArea() which calculates the area of the circle. For clear understanding of what are methods, Difference between static and instance methods and Different types of method parameters read Methods in C# article.
  • In the Main method we create an instance of ClassDemo using the new operator. When an Instance of class is created the constructor is automatically called. In our example ClassDemo constructor is called passing it a value of 10. The constructor then uses this value to initialize the private data field _radius.

using System;

public class ClassDemo

{

//Data Members for the class

const double PI = 3.14; // Constant field Declaration

private int _radius; // Privta field Declaration

//Instance Constructor Declaration with one parameter

public ClassDemo(int Radius)

{

Console.WriteLine("Constructor Called");

this._radius = Radius;

//The line below will generate a compile time error

//PI = 486;

}

//Instance Method Declaration

public double CalculateArea()

{

//Calculate the area of circle

return (PI * _radius * _radius);

}

}

public class MainClass

{

public static void Main()

{

//Create an instance of the class with new operator

ClassDemo CD = new ClassDemo(10);

Console.WriteLine("Area of the Circle is : " + CD.CalculateArea());

}

}

All about Constructors in a class in C#
There are 4 different types of constructors in a class as listed below
  1. Default Parameter less constructor
  2. Private constructor
  3. Static constructor
  4. Instance constructor
  • Default Parameter less constructor: If we donot have any constructor in a class, .NET provides a default parameter less constructor. This constructor will automatically initialize the class data fields to their default values. In our example as we donot hava an explicit constructor defined, a default parameter less constructor will be provided which will initialize the _Name field to empty string and _Age field to 0. In the example below, we have two properties Name and Age defined for the private data fields _Name and _Age respectively. For a clear understanding of what are properties, why they are used, abstract properties and Interface properties read Properties in C#

    using System;

    public class DefaultConstructorDemo

    {

    private string _Name;

    private int _Age;

    public string Name

    {

    get

    {

    return _Name;

    }

    }

    public int Age

    {

    get

    {

    return _Age;

    }

    }

    }

    public class MainClass

    {

    public static void Main()

    {

    DefaultConstructorDemo DCD = new DefaultConstructorDemo();

    Console.WriteLine("Name = " + DCD.Name + " Age = " + DCD.Age);

    }

    }

  • Private Constructor :
    1. Priavte constructors are created using the private access modifier. Any method, property, constructor or a datafield that has a private access modifier can be accessed only with in the class. We will discuss about access modifiers in our later article.
    2. One common interview question is, How do you prevent a class from being instantiated? The answer is using priavte constructors. As PrivateConstructorDemo() constructor has a private access modifer we cannot create an instance of PrivateConstructorDemo class in MainClass.
    3. Any time we define an explicit constructor to our class, the default parameter less constructor will not be provided by .NET
    4. If a class has only static and constant datafields whose value will not change on a per object basis, it does not make any sense to create multiple instances of that class. So in this case we want to prevent users from creating instances of the class, which can be achieved using a private constructor as shown in the below example.
    5. It is a compile time error to instantiate a class that has a private constructor.

    using System;

    public class PrivateConstructorDemo

    {

    //Private Constructor prevent object instantiation

    private PrivateConstructorDemo()

    {

    }

    public const double PI = 3.14;

    }

    public class MainClass

    {

    public static void Main()

    {

    //Line below will generate a compile time error.

    //PrivateConstructorDemo PV = new PrivateConstructorDemo();

    Console.WriteLine(PrivateConstructorDemo.PI);

    }

    }

  • Static constructor :
    1. Static Constructors are created by prefixing the static keword before the constructor as shown in the below example.
    2. Static Constructors are used to instantiate static data fields in the class. Static Constructors are called only once no matter how many instances you create for that class.
    3. Static Constructors are executed before any other type of constructor. Static Constructors cannot be called explicitly. They are automatically invoked when
      1. An instance of the class is created
      2. Any of the static members of the class are referenced
    4. It is a compile time error to have access modifiers for static constructors.

    using System;

    public class StaticConstructorDemo

    {

    public static int InterestRate;

    //Static Constructor

    static StaticConstructorDemo()

    {

    InterestRate = 10;

    }

    }

    public class MainClass

    {

    public static void Main()

    {

    //Static Constructor is automatically called as we

    //are referencing the static field of the class.

    Console.WriteLine(StaticConstructorDemo.InterestRate);

    }

    }

  • Instance Constructor :
    1. Instance Constructors are used to initialize the object data fields.
    2. It is possible to have multiple instance constructors for a class depending on the number of parameters passed to the constructor as shown below. This is called constructor overloading.
    3. A common interview question: What is constructor overloading. Having multiple instance constructors in a class with the same name but different number of parameters.

    using System;

    public class InstanceConstructorDemo

    {

    public int x, y;

    //Instance Constructor 1

    public InstanceConstructorDemo()

    {

    x = 0;

    y = 0;

    }

    //Instance Constructor 2

    public InstanceConstructorDemo(int x)

    {

    this.x = x;

    this.y = 0;

    }

    //Instance Constructor 3

    public InstanceConstructorDemo(int x,int y)

    {

    this.x = x;

    this.y = y;

    }

    }

    public class MainClass

    {

    public static void Main()

    {

    //Create instance using Instance Constructor 1

    InstanceConstructorDemo ICD1 = new InstanceConstructorDemo();

    Console.WriteLine("x = {0},y = {1}", ICD1.x, ICD1.y);

    //Create instance using Instance Constructor 2

    InstanceConstructorDemo ICD2 = new InstanceConstructorDemo(10);

    Console.WriteLine("x = {0},y = {1}", ICD2.x, ICD2.y);

    //Create instance using Instance Constructor 3

    InstanceConstructorDemo ICD3 = new InstanceConstructorDemo(100,200);

    Console.WriteLine("x = {0},y = {1}", ICD3.x, ICD3.y);

    }

    }
Destructors in C#
  1. A destructor will have the same name of a class prefixed by a tilde (~) symbol and cannot have parameters and return type. As destructors cannot have parameters it is not possible to overload destructors of a class. A class can have, at most, one destructor
  2. A destructor is a class member that implements the actions required to destruct an instance of a class.
  3. Destructors are invoked automatically, and cannot be invoked explicitly. Destructors are called automatically when the garbage collector runs. We will discuss about garbage collector in our later article.
  4. It is a compile time error to have access modifiers on destructors.

    using System;

    public class DestructorDemo

    {

    ~DestructorDemo()

    {

    Console.WriteLine("Destructor called");

    }

    }

    public class MainClass

    {

    public static void Main()

    {

    DestructorDemo DD = new DestructorDemo();

    DD = null;

    GC.Collect();

    }

    }

Polymorphism and inheritance

Polymorphism and inheritance are related. To better understand polymorphism, you must understand inheritance concepts first. If you havent read inheritance article, please read inheritance in C# article before proceeding.
Example to understand Polymorphism

using System;

public class Customer

{

public virtual void CustomerType()

{

Console.WriteLine("I am a customer");

}

}

public class CorporateCustomer : Customer

{

public override void CustomerType()

{

Console.WriteLine("I am a corporate customer");

}

}

public class PersonalCustomer : Customer

{

public override void CustomerType()

{

Console.WriteLine("I am a personal customer");

}

}

public class MainClass

{

public static void Main()

{

Customer[] C = new Customer[3];

C[0] = new CorporateCustomer();

C[1] = new PersonalCustomer();

C[2] = new Customer();

foreach (Customer CustomerObject in C)

{

CustomerObject.CustomerType();

}

}

}

We know that classes are reference types. In the below example we have a customer class. In the main method we create an instance of the Customer class.
Customer C = new Customer();

Here C is a reference variable and not an object by itself. C will be pointing to customer object in the memory. For a clear understanding of Classes and reference variables please read classes in C# article.

As C is a reference variable of type Customer class, it can only point to an object of type Customer and it cannot point to any other object type unless the Customer class is related to some other class thru inheritance.

If we have another class CorporateCustomer and if both Customer and CorporateCustomer classes are related by inheritance as shown in the example below, then Customer class reference variable can be pointed to an object of type CorporateCustomer class.

In our example Customer class is the base class for CorporateCustomer class. As we know from inheritance concepts a base class reference variable can point to a child object type. Based on this, Customer class reference variable can be used to point to CorporateCustomer object.
Customer[] C = new Customer[3];
C[0] = new CorporateCustomer();

We have yet another class, PersonalCustomer which is also deriving from Customer class. For CorporateCustomer class and PersonalCustomer class the base class is the same Customer class. As these classes are related by inheritance the following statements are perfectly legal.
C[0] = new CorporateCustomer();
C[1] = new PersonalCustomer();

If you have noticed all the 3 classes have the same method CustomerType(). This method is marked as virtual method in the Customer class. When we mark a method as virtual in the base class, it means that the derived class which inherits this method can override the method and provide its own implementaion as shown in our example. The derived class can use the inheritted virtual method without overriding.

In our example both the CorporateCustomer class and PersonalCustomer class override the inheritted CustomerType() method and provides their own implementation.

In the Main() method we have created Cutomer array, which can hold 3 objects of type Customer. For discussion on arrays please read arrays in C# article.
Customer[] C = new Customer[3];

In the code snippet below, we are assigning a new CorporateCustomer object and new PersonalCustomer object to a reference variable of type Customer. This is possible as the classes are related by inheritance and we know a parent class reference variable can point to a child class object.
C[0] = new CorporateCustomer();
C[1] = new PersonalCustomer();
C[2] = new Customer();

Finally in the Main() method we loop thru the CustomerObject array using foreach loop and invoke the CustomerType() method. For discussion on loops please read loops in C#.

The ouput of the above program is shown below.
I am a corporate customer
I am a personal customer
I am a customer


We know that in the Customer array
FirstObject is CorporateCustomer class object.
SecondObject is PersonalCustomer class object.
ThirdtObject is Customer class object.

The output shows that the overriden methods from the respective classes are invoked during the run time. This is called as polymorphism.
Definition of Ploymorphism from MSDN:
Through inheritance, a class can be used as more than one type; it can be used as its own type, any base types, or any interface type if it implements interfaces. This is called polymorphism.

In other words, the base class object reference variable can do different things(example invoke different methods) depending on the child class object the reference variable is pointing to.
Key points to remember about Ploymorphism
The derived classes may or may not override the inherted virtual methods. In our example if the the derived class PersonalCustomer doesnot override the CustomerType() method then the base class virtual method will be invoked. To test this just comment the CustomerType() method in PersonalCustomer class and run the program.
.NET Framework examples
We know that every type in .NET is derived from object class directly or indirectly. The object class has 4 methods as listed below.
  1. Equals()
  2. GetHashCode()
  3. GetType()
  4. ToString()
Even the classes that we write will derive from the object class. So all the methods in the object class are inherited to our class as well. So the Customer class below will also have the 4 methods. I am invking the ToString() instance method which is inherited from the object class. For a clear understanding of static and instance methods please read methods in C#.

The output before overriding the ToString() method is:
Customer

This is not useful information at all. When I invoke C.ToString(), I would like to see the customer FirstName and LastName. To achieve this we will have to override the ToString() method in our class. In the object class in .NET framework the ToString() is a virtual method. So you can either use the ToString() method as is, or provide your own implementation by overriding it as shown in the example below.

The output after overriding the ToString() method is:
Prasad Cherukuri

using System;

public class Customer

{

string FirstName;

string LastName;

public Customer()

{

FirstName = "Prasad";

LastName = "Cherukuri";

}

//Comment and uncomment this overriden method to see

//how the out put changes

public override string ToString()

{

return FirstName + " " + LastName;

}

}

public class MainClass

{

public static void Main()

{

Customer C = new Customer();

Console.WriteLine(C.ToString());

}

}