C# extension methods

In this tutorial, I will explain how we can use extensions methods in C# and aspects that you need to be aware of when doing so.

Definition

Extensions methods are methods that we can add to our code or third-party library, that allow us 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, I wrote the log class but, as I said before, this would've been provided to us. The code can be seen 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}");
        }
    }

If we needed to log a particular message, you just needed to instantiate the Logger class and call the log method.

So far so good.

But what if you wanted to give "special" treatment to error messages and print something like "An error occurred. This was the error: {INSERT_ERROR_MESSAGE}"? You don't have access to that source code, so you can't change de Logger library.

C# provides you 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.

  1. Created a static class that will contain all the extensions we can create for the Logger library.
  2. Created a static method, which represents the additional method we are adding to the library. 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 you can see from the method, I have added two parameters: errorType and message.

The following image shows the log obtained.

Log obtained by executing the Log method inside the Logger Class

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:

  1. Creation of the ILogger interface, containing the important log method.
  2. Make the Logger class implement the created interface.
  3. 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.
  4. 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.

Log obtained by executing the Log method inside the Logger Class and the AnotherLogger Class

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.

When to consider using extension methods

I would advise the usage of extension methods in the following situations:

  • Adding/extending functionality to a third-party library, from which you don't have direct access to the code and, therefore, can't change it.
  • If you are doing extension methods in your code, and not in a third-party library, you have the benefit of adding functionality on top of the existing one, which helps you, not only not introducing bugs on the already existing code, but also can make testing this new functionality much easier.

When not to use extension methods

I would not advise the usage of extension methods in the following situations:

  • To extensively extend the base types existent in the language (ex. strings), unless we are doing this operation multiple times and in multiple places and this saves you time.
  • It is not guaranteed that the extension method code will be executed, as methods directly on the type take precedence (methods with the same signature). Have this into consideration, since it might cause you some nasty unwanted behavior.
  • If you need access to private or protected members, you can't access them from the extension methods.

Stuff to consider

We have covered a lot of ground in this tutorial.

Here are the main takeaways from this:

  1. Extension methods are additional methods that were originally not included with the code/library provided, that you can create to extend your code's functionality.

  2. The first parameter of the extension method must be of the type for which the extension method is applicable, preceded by the this keyword.

  3. Extension methods can be used anywhere in your application if you include the namespace of the extension method.

  4. Extension methods cannot be used to override existing methods. As we have seen, the instance methods have higher precedence.

😁 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!

Social Media Links: LinkedIn Twitter Github