Extension methods provide a way to add functionality to existing types without modifying their source code, creating derived types, or recompiling the original type. These are special static methods that can be invoked as if they were instance methods on the extended type.
Motivasion for Extension Methods
Before extension methods, developers commonly created utility classes (like StringHelper or DateTimeHelper) to add functionality to existing types. This approach required instantiating these helper classes. Extension methods eliminate this overhead by allowing direct method calls on the target types themselves.
Key Characteristics
- Defined as static methods within static classes
- The first parameter specifies the type being extended, preceded by the 'this' keyword
- Only methods can be extended, not properties or events
- Have access to all public members of the extended type
- When signatures match, instance methods take precedence over extension methods
- Methods extending base classes or interfaces are accessible to derived types and implementations
- Ultimately compiled as static method calls:
StaticClass.StaticMethod()
Practical Example
Let's demonstrate extension method behavior through a concrete example:
Define the IAnimal Interface
using System;
namespace ExtensionDemo
{
public interface IAnimal
{
void Eat();
}
}
Create Person Class Implementing IAnimal
using System;
namespace ExtensionDemo
{
public class Person : IAnimal
{
public void Eat()
{
Console.WriteLine("Person is eating");
}
}
}
Implement Extension Methods
using System;
namespace ExtensionDemo
{
public static class AnimalExtensions
{
public static void Eat(this IAnimal animal)
{
Console.WriteLine("Animal extension - Eating");
}
public static void Rest(this IAnimal animal)
{
Console.WriteLine("Animal extension - Resting");
}
public static void Eat(this Person person)
{
Console.WriteLine("Person extension - Eating");
}
public static void Rest(this Person person)
{
Console.WriteLine("Person extension - Resting");
}
}
}
Testing the Implementation
using System;
namespace ExtensionDemo
{
class Program
{
static void Main(string[] args)
{
IAnimal creature = new Person();
creature.Eat(); // Calls instance method: "Person is eating"
creature.Rest(); // Calls extension: "Animal extension - Resting"
Console.WriteLine("---- Boundary ----");
Person individual = new Person();
individual.Eat(); // Calls instance method: "Person is eating"
individual.Rest(); // Calls extension: "Person extension - Resting"
Console.WriteLine("---- Boundary ----");
// Explicit static calls
AnimalExtensions.Eat(creature);
AnimalExtensions.Rest(creature);
AnimalExtensions.Eat(individual);
AnimalExtensions.Rest(individual);
Console.ReadLine();
}
}
}
Execution Results Analysis
The output demonstrates important resolution rules:
- Instance methods always take precedence over extension methods with identical signatures
- Type-specific extensions have higher priority than base type extensions
- Interface extensions are inherited by implementing classes
Behavior with Removed Extensions
If we comment out the Person-specific extensions:
public static class AnimalExtensions
{
public static void Eat(this IAnimal animal)
{
Console.WriteLine("Animal extension - Eating");
}
public static void Rest(this IAnimal animal)
{
Console.WriteLine("Animal extension - Resting");
}
// Person-specific extensions removed
}
In this scenario, both IAnimal and Person variables will use the interface extension methods, demonstrating that interface extensions are automatically available to implementers.