What are extension methods
Extension methods are a feature of C# that allows us to add behaviors to existing types without inheriting from that type or modifying the source code of the object.
By using extension methods we can define new methods for an already existing type. Extension methods are static methods, but they are called as if they were instance methods of the type they extend. It is important to remember that extension methods are just syntactic sugar, this makes the following two pieces of code equivalent, as the lower code before compilation is the same for both of those.
Extension method:
int foo = 2;
int result = foo.MultiplyByTen();
public static class IntExtensionMethods
{
public static int MultiplyByTen(this int number)
{
return number * 10;
}
}
Classic static class:
int foo = 2;
int result = IntMethods.MultiplyByTen(foo);
public static class IntMethods
{
public static int MultiplyByTen(int number)
{
return number * 10;
}
}
Extension methods are created as static methods that their first argument is the type they extend with the this
keyword in front.
A thing to notice here, is that the foo variable isn’t multiplied by ten. Because int is a value type, the extension method would return an int that its value is the foo’s value multiplied by ten. If we wanted to make the foo variable ten times greater, then we need to pass it by reference:
int foo = 2;
foo.MultiplyReferenceByTen();
Console.WriteLine(foo);
public static class IntExtensionMethods
{
public static ref int MultiplyReferenceByTen(ref this int number)
{
ref int result = ref number;
result *= 10;
return ref result;
}
}
This code, makes the foo int 20. It all depends on what exactly we want to do.
Most common use case
The most common use case for using extension methods, is for adding methods to types that we don’t have access to their implementations and cannot be inherited from, but why use extension methods when they are just syntactic sugar?
Using normal static methods that have the same effect and take the type as a parameter, may create the same behavior for the type we want, but makes our code less readable. Extension methods, exist so that our code base is cleaner.
Compare this:
bool result = foo.AddPi().MultiplyByTen().CheckBetween(60, 80)
that is using the following extension methods:
public static class MyExtensionMethods
{
public static float AddPi(this int number)
{
return number + 3.14f;
}
public static float MultiplyByTen(this float number)
{
return number * 10;
}
public static bool CheckBetween(this float number1, int number2, int number3)
{
return number1 > number2 && number1 < number3;
}
}
with this:
bool result = MyMethods.CheckBetween(MyMethods.MultiplyByTen(MyMethods.AddPi(foo)),60,80)
that is using the “classical” approach:
public static class MyMethods
{
public static float AddPi(int number)
{
return number + 3.14f;
}
public static float MultiplyByTen(float number)
{
return number * 10;
}
public static bool CheckBetween(float number1, int number2, int number3)
{
return number1 > number2 && number1 < number3;
}
}
4 Ways to use them in your own defined types
There are certain cases, that extension methods can help with more clean code, other than extending already defined types. I have found four use cases, that extension methods help to make my code cleaner, while using them in my own types, instead on adding those methods directly to my types or creating a static class that has methods that take those types as a parameter.
1) Add methods to enums
Enums in C# are very useful, but don’t allow to have their own methods. Now with the help of the extension methods, we can create more readable code. For example let’s suppose that we have an enum of bus stops. Given a bus stop we want to have methods that get the arrival time of the bus at the next stop. With the help of extension methods our code will look something like this:
The enum:
public enum BusStop
{
firstStop,
secondStop,
thirdStop,
fourthStop
}
BusStop currentStop = BusStop.firstStop;
var result = currentStop.GetNext().GetTimeOfArrival();
I find this much more readable than creating methods that accept a BusStop
parameter.
2) Better architecture with extension methods
Let’s suppose that we have a game with different type of enemies. All enemies implement the IEnemy
interface, but some of our enemies have a special attack. This special attack, will be executed in some parts of our code if the enemy has it, but if it doesn’t the normal attack will be executed.
One way to solve this would be for the IEnemy
interface to have a SpecialAttack
method that calls the special attack for the enemies that have it, or the normal attack if this type of enemy doesn’t have a special attack. This creates a problem that we need to check the implementation of each enemy so that we can know whenever we call the special attack method if a normal attack gets executed or not. Another problem with this approach, is the potential violation of The Liskov Substitution Principle, the special attack doesn’t exist for some implementation, it is just a copy of the normal attack.
Another way to solve this, is to check the type of IEnemy
at each part of our code that a special attack is needed, for example let’s say that we have the IEnemy
interface with two implementations:
public interface IEnemy
{
void Attack();
}
public class Skeleton : IEnemy
{
public void Attack()
{
Console.WriteLine("The skeleton Attacks");
}
}
public class Goblin : IEnemy
{
public void Attack()
{
Console.WriteLine("The Goblin Attacks");
}
public void GoblinSpecialAttack()
{
Console.WriteLine("The goblin performs a special attack");
}
}
and a list that has our enemies:
List<IEnemy> enemiesList = new List<IEnemy>()
{
new Skeleton(),
new Skeleton(),
new Goblin()
};
our code then will have to do in the parts of the game we need the special attack, something like this:
foreach (IEnemy enemy in enemiesList)
{
if (enemy is Goblin)
((Goblin)enemy).GoblinSpecialAttack();
else
enemy.Attack();
}
This is a problem. Suddenly a few parts of our code don’t depend on abstractions only, but we create dependencies on concrete implementations.
By using extension methods, this dependency with the concrete implementations, can be localized in one static class:
public static class IEnemyExtensions
{
public static void SpecialAttack(this IEnemy enemy)
{
if (enemy is Goblin)
((Goblin)enemy).GoblinSpecialAttack();
else
enemy.Attack();
}
}
Now for the different parts of our code the above foreach loop can be written like this:
foreach (IEnemy enemy in enemiesList)
{
enemy.SpecialAttack();
}
and the output will be:
The skeleton Attacks
The skeleton Attacks
The goblin performs a special attack
Now, we have created a layer in our program, that is responsible for choosing the right attack, when we need to have our enemies perform their special attacks if they have it.
Whenever a change is needed, there is only one class to change, the IEnemyExtensions
class and not every implementation of our enemies or every implementation for the classes that have the code that executes the attack.
3) Use extension methods to add behavior to null references
Extension methods, are also useful to provide default behaviors, when one of our references is null. Instead of relying on Null checks or implementing a null object pattern, we can add a behavior to our type that gets executed if it is null, that seems like it is defined in our type. For example let’s suppose that in the above foreach loop our list had a null reference:
List<IEnemy> enemiesList = new List<IEnemy>()
{
new Skeleton(),
new Skeleton(),
null,
new Goblin()
};
by changing our extension method to this:
public static class IEnemyExtensions
{
public static void SpecialAttack(this IEnemy enemy)
{
if (enemy is null)
{
Console.WriteLine("Do nothing or default behaviour");
return;
}
if (enemy is Goblin)
((Goblin)enemy).GoblinSpecialAttack();
else
enemy.Attack();
}
}
now the output of the foreach loop will be:
foreach (IEnemy enemy in enemiesList)
{
enemy.SpecialAttack();
}
The skeleton Attacks
The skeleton Attacks
Do nothing or default behaviour
The goblin performs a special attack
This may seem weird, because null.DoSomething()
is not valid code, but remember that extension methods are just syntactic sugar, the code gets lowered before compilation to this:
SpecialAttack(enemy)
which is perfectly valid code, even if the enemy is null.
4) Differentiate between development and production code easily
A final use case where I have found extension methods to be useful, is for easily having different code for development and production builds, whenever that is needed, without polluting my code base with #if
directives.
If there is a case of a class that has a lot of different methods, that have different implementations for a debug or a release build the code gets harder to read with all those directives. For example:
public class Goblin
{
public void Attack()
{
#if DEBUG
Console.WriteLine("The Goblin Attacks in debug build");
#else
Console.WriteLine("The Goblin Attacks in release build");
#endif
}
public void GoblinSpecialAttack()
{
#if DEBUG
Console.WriteLine("The goblin performs a special attack in debug build");
#else
Console.WriteLine("The goblin performs a special attack in release build");
#endif
}
public void Move()
{
#if DEBUG
Console.WriteLine("The goblin moves in debug build");
#else
Console.WriteLine("The goblin moves in release build");
#endif
}
}
Instead of having all those #if
directives, I create the same static class with the same extension methods in two different namespaces. Now there is only one #if
directive at the start of my code that uses the correct namespace depending on the build. The above code for example would be:
namespace GoblinDebugExtensions
{
public static class GoblinExtensions
{
public static void Attack(this Goblin goblin)
{
Console.WriteLine("The Goblin Attacks in debug build");
}
public static void GoblinSpecialAttack(this Goblin goblin)
{
Console.WriteLine("The goblin performs a special attack in debug build");
}
public static void Move(this Goblin goblin)
{
Console.WriteLine("The goblin moves in debug build");
}
}
}
namespace GoblinReleaseExtensions
{
public static class GoblinExtensions
{
public static void Attack(this Goblin goblin)
{
Console.WriteLine("The Goblin Attacks in release build");
}
public static void GoblinSpecialAttack(this Goblin goblin)
{
Console.WriteLine("The goblin performs a special attack in release build");
}
public static void Move(this Goblin goblin)
{
Console.WriteLine("The goblin moves in release build");
}
}
}
now the only thing that I need to have, is at the start of my file:
#if DEBUG
using GoblinDebugExtensions;
#else
using GoblinReleaseExtensions;
#endif
This does not only make my code less cluttered, but also the debug code is in a different class from the release code. If some goblin methods need to be different in release and debug builds and others are the same, then instead of creating two goblin classes, one for release and one for develop and having duplicate code for the methods that are the same, by using the extension methods like this, I can easily change the code that I need, without fear of forgetting to change duplicated code or cluttering my code base with all those #if
directives.
How to Deal With Conflicting Extension Methods
There is always a chance, that one of our extension methods becomes obsolete, because the author of the library that contains the type that we are extending adds his own extension method. If we want to remove our extension method without breaking binary compatibility, we can do it, by removing the this
keyword.
As mentioned above, extension methods are lowered to normal static method calls, everything already compiled with our extension method will keep working, and any new code will have to be written either by using the new extension method of the library’s author, or use our static method normally, as calls to this method cannot be made anymore, using the extension methods syntax.
Conclusion
Those are some ways I have found extension methods to be useful, other than extending already existing types. If anyone has found any other uses that can make the program easier to read, cleaner or help with the architecture of the code I would love to hear about it.
This post got a little bigger than the usual so if you reached the end, thank you for reading and as always if you have any questions or comments you can use the comments section or contact me directly via the contact form or email, also if you don’t want to miss any of the new articles, you can always subscribe to my newsletter or the RSS feed.