Polymorphism
Polymorphism is a fundamental concept in object-oriented programming (OOP) that allows objects of different types to be treated as objects of a common base type. In C#, polymorphism is achieved through mechanisms like method overriding and interfaces. There are two main types of polymorphism: compile-time polymorphism (also known as static or early binding) and runtime polymorphism (also known as dynamic or late binding).
Compile-Time Polymorphism:
Method Overloading:
Compile-time polymorphism is achieved through method overloading, where multiple methods in the same class have the same name but different parameter lists. The appropriate method is chosen at compile-time based on the method signature.
public class MathOperations
{
// Method with two integer parameters
public int Add(int a, int b)
{
return a + b;
}
// Overloaded method with two double parameters
public double Add(double a, double b)
{
return a + b;
}
}
class Program
{
static void Main()
{
MathOperations math = new MathOperations();
int result1 = math.Add(2, 3); // Calls the first Add method
double result2 = math.Add(2.5, 3.5); // Calls the second Add method
}
}
Runtime Polymorphism
Runtime polymorphism is achieved through method overriding, where a method in a base class is marked as virtual and can be overridden by a method in a derived class using the override keyword. This type of polymorphism is often associated with upcasting, which means a variable of a base type references an instance of a derived type.
public class Animal
{
public virtual void MakeSound()
{
Console.WriteLine("Some generic sound");
}
}
public class Dog : Animal
{
public override void MakeSound()
{
Console.WriteLine("Woof! Woof!");
}
}
class Program
{
static void Main()
{
Animal myAnimal = new Dog();
// Calls the overridden MakeSound method in Dog
myAnimal.MakeSound();
myAnimal = new Cat();
// Calls the overridden MakeSound method in Cat
myAnimal.MakeSound();
}
}
In this example, the myAnimal
variable is declared as the base type Animal
, and references instances of its derived types. The MakeSound
method is marked as virtual
in the Animal
class and overridden in the Dog
and Cat
classes. At runtime, the appropriate version of the method is called based on the actual type of the object.
When a variable declared as a base type references an instance of a derived type, the reference can only be used to invoke behaviors defined within the base type. Any unique behavior defined in a derived type will not be accessible. For example, if the Cat
class had a behavior Purr
, invoking the behavior with the myAnimal
variable would cause a build error.
class Program
{
static void Main()
{
Animal myAnimal = new Cat();
// This statement will cause a compile error
myAnimal.Purr();
}
}
When the above code is compiled, the variable myAnimal
is only known to be an Animal
. Therefore, the statement is an error because Animal's don't Purr
. In order to make this code work, it would need to be determined that the Animal
is actual a type of animal that can Purr
.
class Program
{
static void Main()
{
Animal myAnimal = new Cat();
// Determine if the object is a Cat
if (myAnimal is Cat)
{
// Type cast object reference
Cat cat = (Cat)myAnimal;
cat.Purr();
}
}
}
In this example, a selection is performed to determine the type of the Animal
. This is done so you don't accidentally try to type cast a different type of Animal
to a Cat
. Inside the selection block, a type cast gets an object reference as the Cat
type, so that the Purr
behavior can be invoked.
Polymorphism in C# provides flexibility and allows for the creation of code that is more generic, extensible, and maintainable. It allows you to work with objects at a higher level of abstraction, treating them based on their common characteristics rather than their specific types.