Extensions methods are methods that we can add to our code or third-party library that allow us to extend or add functionality.
Let's see a simple coding example to show this concept in action.
Let's assume we have a third-party library whose only purpose is to log messages (in our case, to the console). To keep it simple, we have already written the log class. Let's see the code 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}"); } }
To log a particular message, instantiate the Logger class and call the log method.
But what if we want to give special treatment to error messages and print an error. We don't have access to that source code, so we can't change the Logger library.
C# provides us with a way to extend the functionality of the Logger library without modifying the original source code.
Add the following code to the above widget to add the LogError
method.
public static class LoggerExtensions { public static void LogError(this Logger logger, string errorType, string message) { Console.WriteLine($"Error {errorType}: {message}"); } }
Line 1: We create a static class that contains all the extensions we can create for the Logger library.
Line 3: We create a static method, which represents the additional method that we add to the library. Here we have a couple of things to notice:
errorType
and message
.The following code shows the log obtained:
using System; namespace ExtensionMethods { internal class Program { public static void Main(string[] args) { Logger logger = new Logger(); logger.LogError("NullException", message: "object reference was null"); } } } public static class LoggerExtensions { public static void LogError(this Logger, string errorType, string messege) { Console.WriteLine($"Error {errorType}: {message}"); } }
In this case, we extend 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 look at this in practice with our Logger class. Let's modify the following code:
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}"); } }
Line 1: We create the ILogger
interface that contains the important log method.
Line 19: We create the Logger
class.
Line 27: We create the AnotherLogger
class implement the created interface.
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 extend the Logger
class, but now we extend the interface ILogger
, and as a result, 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 code shows a possible execution output:
using System; 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}"); } } namespace ExtensionMethods { internal class Program { public static void Main(string[] args) { Logger logger = new Logger(); logger.LogError("NullException",message: "Object reference was null"); AnotherLogger anotherLogger = new AnotherLogger(); anotherLogger.LogError("DivisionByZero", message: "can't divide by Zero"); } } }
We 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 look at the source code, click here.
It's advised to use the extension methods in the following situations:
It's recommended not to use extension methods in the following situations:
We have covered a lot of ground in this article.
Here are the main takeaways from this:
RELATED TAGS
CONTRIBUTOR
View all Courses