Search

Polymorphism in C#

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 C#, every type is polymorphic. Types can be used as their own type or as a Object instance, because any type automatically treats Object as a base type.

Polymorphism is important not only to the derived classes, but to the base classes as well. Anyone using the base class could, in fact, be using an object of the derived class that has been cast to the base class type. Designers of a base class can anticipate the aspects of their base class that are likely to change for a derived type. For example, a base class for cars might contain behavior that is subject to change when the car in question is a minivan or a convertible. A base class can mark those class members as virtual, allowing derived classes representing convertibles and minivans to override that behavior.

Overview

When a derived class inherits from a base class, it gains all the methods, fields, properties and events of the base class. To change the data and behavior of a base class, you have two choices: you can replace the base member with a new derived member, or you can override a virtual base member.

Replacing a member of a base class with a new derived member requires the new keyword. If a base class defines a method, field, or property, the new keyword is used to create a new definition of that method, field, or property on a derived class. The new keyword is placed before the return type of a class member that is being replaced. For example:


public class BaseClass
{
public void DoWork() { }
public int WorkField;
public int WorkProperty
{
get { return 0; }
}
}

public class DerivedClass : BaseClass
{
public new void DoWork() { }
public new int WorkField;
public new int WorkProperty
{
get { return 0; }
}
}





When the new keyword is used, the new class members are called instead of the base class members that have been replaced. Those base class members are called hidden members. Hidden class members can still be called if an instance of the derived class is cast to an instance of the base class. For example:



DerivedClass B = new DerivedClass();
B.DoWork(); // Calls the new method.

BaseClass A = (BaseClass)B;
A.DoWork(); // Calls the old method.



In order for an instance of a derived class to completely take over a class member from a base class, the base class has to declare that member as virtual. This is accomplished by adding the virtual keyword before the return type of the member. A derived class then has the option of using the override keyword, instead of new, to replace the base class implementation with its own. For example:

public class BaseClass
{
public virtual void DoWork() { }
public virtual int WorkProperty
{
get { return 0; }
}
}
public class DerivedClass : BaseClass
{
public override void DoWork() { }
public override int WorkProperty
{
get { return 0; }
}
}





Fields cannot be virtual; only methods, properties, events and indexers can be virtual. When a derived class overrides a virtual member, that member is called even when an instance of that class is being accessed as an instance of the base class. For example:



DerivedClass B = new DerivedClass();
B.DoWork(); // Calls the new method.

BaseClass A = (BaseClass)B;
A.DoWork(); // Also calls the new method.




Virtual methods and properties allow you to plan ahead for future expansion. Because a virtual member is called regardless of which type the caller is using, it gives derived classes the option to completely change the apparent behavior of the base class.


Virtual members remain virtual indefinitely, no matter how many classes have been declared between the class that originally declared the virtual member. If class A declares a virtual member, and class B derives from A, and class C derives from B, class C inherits the virtual member, and has the option to override it, regardless of whether class B declared an override for that member. For example:




public class A
{
public virtual void DoWork() { }
}
public class B : A
{
public override void DoWork() { }
}

//--
public class C : B
{
public override void DoWork() { }
}





A derived class can stop virtual inheritance by declaring an override as sealed. This requires putting the sealed keyword before the override keyword in the class member declaration. For example:




public class C : B
{
public sealed override void DoWork() { }
}




In the previous example, the method DoWork is no longer virtual to any class derived from C. It is still virtual for instances of C, even if they are cast to type B or type A. Sealed methods can be replaced by derived classes using the new keyword, as the following example shows:




public class D : C
{
public new void DoWork() { }
}





In this case, if DoWork is called on D using a variable of type D, the new DoWork is called. If a variable of type C, B, or A is used to access an instance of D, a call to DoWork will follow the rules of virtual inheritance, routing those calls to the implementation of DoWork on class C.


A derived class that has replaced or overridden a method or property can still access the method or property on the base class using the base keyword. For example:

public class A
{
public virtual void DoWork() { }
}
public class B : A
{
public override void DoWork() { }
}


//--2


public class C : B
{
public override void DoWork()
{
// Call DoWork on B to get B's behavior:
base.DoWork();

// DoWork behavior specific to C goes here:
// ...
}
}