On Extension Methods In C#

On Extension Methods In C#

In the last article on static classes in C#, I stated that I am not a big fan of extension methods. In this article, I will try to explain why that is so.

What are extension methods in the first place?

Let us take a look at what Microsoft documentation says:

Extension methods enable you to "add" methods to existing types without creating a new derived type, recompiling, or otherwise modifying the original type.

The above is pretty understandable, I believe.

Extensions methods allow us to add new functionality, which we define by ourselves, to the existing types.

The types being extended are most commonly the fundamental types from the System namespace.

This might seem like a neat thing. At some point in our careers we might have felt the need to add an additional method to an integer, a string, or date and time type - regardless of the programming language that we are coming from.

C# allows us to do that.

However, I personally am not a particular fan of this C# feature.

Here is what I think, and I am not sure if I can express this in an argumentative way to a degree I wish I could.

I feel that there is something utterly wrong with, what to me is, defiling and polluting the API of existing, most commonly System, types.

The following is a very naive example in which we extend the integer type with IsPrime method which checks whether an integer is prime or not.

public static class IntegerExtensions
{
   public static bool IsPrime(this int x)
   {
     if(x == 1)
       return false;

     for (int i = 2; i <= Math.sqrt(x); i++)
       if (x % i == 0)
         return false;  

      return true;  
    }
}

There are three things worth noting:

  • The container that holds extension method definitions, the IntegerExtensions class, is called a sponsor class and it must be marked as static

  • The extension method itself should be marked as static as well, but the client code can call it as if it was an instance method

  • The very first parameter of an extension method is the type that we wish to extend preceded by this keyword

The client code could call IsPrime like this:

int x = 31;
x.IsPrime() // true

An additional example, which I was tempted to implement myself (and did), is an extension method on integer type that checks whether the integer is in a given range. (Assume that both bounds are positive)

public static class IntegerExtensions
{
  public static bool IsInRange(this int x, int lowerBound, int upperBound)
                                => x >= lowerBound && x <= upperBound;
}

So where is the issue?

I feel like these things are polluting the API of a System type.
If the architects of the language/framework wanted the API of type X to have functionallity Y, then I guess they would have had it designed like that in the first place?

I regret adding the IsInRange extension method in the example above.

I could have created a separate Range component that would look something like this, and that we could reuse for other types that can be compared:

public class Range<T> where T : IComparable<T>
{
 private readonly T _lowerBound;
 private readonly T _upperBound;

 public Range (T lowerBound, T upperBound)
 {
   _lowerBound = lowerBound;
   _upperBound = upperBound;
 }

 public bool Contains(T value)
 {
   var comparableValue = (IComparable) value;
   var comparableLowerBound = (IComparable) _lowerBound;
   var comparableUpperBound = (IComparable) _upperBound;

   return comparableValue.CompareTo(comparableLowerBound) >= 0
          && comparableValue.CompareTo(comparableUpperBound) <=0;
 }
}

We could then use it like this:

var range = new Range<int>(2, 7);
range.Contains(5) // true

There is one more example I would like to mention, which was perhaps the trigger for writing this article.

I have recently used a library that added extension methods to the existing HttpClient.
The methods were very specialized - they were used to integrate with Identity Server, and I did not like this. An example was RequestTokenAsync method which was used to obtain a token from the token endpoint on Identity Server.

Now, if you used HttpClient in the same context, for example, to integrate with NASA RESTful API, you would get RequestTokenAsync recommended, as well as all the other extension methods that the library includes.

This simply did not feel right to me, because we are polluting the API of a System type with something that is very specific or business-specific and I feel like that should not be done.

So if we, in a similar way, introduce libraries X, Y, Z which add their own extension methods things could potentially become messy.
We could end up asking ourselves questions such as what is this, how is this here, since when is this here, what does it do and so on.

There are also some additional issues I have with extension methods.

We are creating an additional, hidden, API on the types - until we include the namespace that contains the sponsor class we are not aware of the API as a whole.

We can actually use extensions methods to provide default method implementations on interfaces.
But again, if the architects of the language/framework wanted to have default interface implementations then I guess they would have had it designed like that in the first place, or added it additionally.
This feature was added in C# 8.0 actually.

What if someone starts adding extension methods to System.Object?

What if someone starts adding extension methods on their own types? Now, why would someone do that?

My feeling is that we should think twice about adding our own extension methods.

For the extension methods that are already available in the language, we should use them as usual. LINQ extension methods, for example, are superb, and to be honest I can not imagine development in C# without those.