In this tutorial, we will discuss how to use extensions methods in C# and the aspects to be aware of when doing so.
Definition
Extensions methods are methods that we can add to our code or third-party library, that allows to extend or add functionality.
When to use them or not is something that we will address later on, but for now, let's see a simple coding example to show this concept in action.
Coding example
Let's assume we are handed a third-party library whose only purpose is to log messages (in our case to the console). For simplicity's sake, the log class is written down below.
/*
* Let's assume this is given in a third party library
*/
public class Logger
{
public void Log(string message)
{
Console.WriteLine($"Logged message: {message}");
}
}
Line 4: Declaration of the Logger class.
Line 6 to 8: Log method, that logs the message passed as argument.
If we needed to log a particular message, we just needed to instantiate the Logger class and call the log method.
So far so good.
But what if we wanted to give "special" treatment to error messages and print something like "An error occurred. This was the error: {INSERT_ERROR_MESSAGE}"? We don't have access to that source code, so we can't change the Logger library.
C# provides us a way of extending the functionality of the Logger library without modifying the original source code. Add the following code to the previous one shown for the library. The result of adding this would be having an extra method (the LogError method).
public static class LoggerExtensions
{
public static void LogError(this Logger logger, string errorType, string message)
{
Console.WriteLine($"Error {errorType}: {message}");
}
}
Let's analyze what we just did.
Line 1: Created a static class that will contain all the extensions we can create for the Logger library.
Line 3 to 6: Created a static method, which represents the additional method we are adding to the library, that logs the error type and an error message.
Here we have a couple of things to notice:
- The first parameter has to be the class to be extended. To do so, we must first use the keyword this and then insert the class to be extended like a traditional parameter.
- After the first parameter, we must add the remaining wanted parameters, like a "regular" method. As we can see from the method, two parameters have been added: errorType and message.
The following image shows the log obtained.
In this case, we extended the functionality of a particular class, but we can also abstract this even more and consider extending functionality to interfaces. This means that every class that implements that interface will also be able to use the extended method.
Let's have a look at this in practice, with our Logger class.
Have in consideration the following code to be added and modified.
public interface ILogger
{
void Log(string message);
}
public static class LoggerExtensions
{
public static void LogError(this ILogger logger, string errorType, string message)
{
Console.WriteLine($"Error {errorType}: {message}");
}
}
/*
* Let's assume this is given in a third party library
*/
public class Logger : ILogger
{
public void Log(string message)
{
Console.WriteLine($"Logged message: {message}");
}
}
public class AnotherLogger : ILogger
{
public void Log(string message)
{
Console.WriteLine($"Logged message in another logger: {message}");
}
}
Changes that were inserted:
Line 1: Creation of the ILogger interface, containing the important log method.
Line 3: Log method to be implemented.
Line 5: Extension class that will contain our extension methods.
Line 7 to 10: Extension method created. As we can see from the method's signature, everything that implements the ILogger interface will have this method available.
Line 16: Created a Logger class that implements the aforementioned interface.
Line 24: Create another Logger class. The purpose of this is to check that every class that implements that interface will have the extension method created available.
Notice the static class we created for extensions and compare it with the same class from the previous example. Notice the difference? In our first example, we were extending the Logger class, but now we extended the interface ILogger, and the impact of this is that, as mentioned before, every class that implements the interface will access the extension method.
If we now instantiate Logger and AnotherLogger objects, we can see that the extension method is available to us.
The following image shows precisely a possible execution output.
One last final piece of information to have in consideration is that you can also have extension methods in enums. The rationale behind it is the same as with interface, so we won't go into much detail (also because it's not as common to do extension methods in enums).
Before moving on, if you want to take a look at the source code, you can find it here.
😁 I hope this has helped!
That's everything for now! Thank you so much for reading.
If you have any questions please feel free to drop them off.
Follow me if you want to read more about these kinds of topics!
Top comments (0)