Skip to content

Extension Methods And Their Benefits In Csharp

extension methods and their benefits in csharp

C# 3.0 introduces extension methods. These are one of the most powerful features that helped C# as a language take a significant leap in the programming space.

In this post, we will discuss the extension methods, their implementation, and their usage in practical scenarios.

What are Extension Methods?

Extension methods are used to add functionality to the existing types, without

  • Knowing or modifying the source code
  • Inheriting
  • Recompiling the source code

How to implement Extension Methods?

Let’s understand the implementation through an example.

public static class MyExtensions
{
  public static int WordsCount(this string sentence)
  {
      return sentence.Split(' ').Length;
  }

  public static int IsExceeded(this string sentence, int limit)
  {
    return sentence.Split(' ').Length > limit;
  }

}

class Program  
{  
  static void Main(string[] args)  
  {  
      string input = "Coderethinked is a great blog!";
      // Calling an extension method without parameter
      int wordsCount = input.WordsCount(); 
      // Calling an extension method with parameter
      bool isExceeded = input.IsExceeded(10); 
  }
} 

Firstly, extension methods should be static (WordsCount() is a static method) and declared in a top-level non-generic static class (MyExtensions is a top-level non-generic static class here).

Secondly, the first parameter should be a binding parameter.

What is a binding parameter? 💡

  • It is prefixed by this keyword.
  • The extended type name follows a this keyword. In the above example, this keyword is followed by string (this string sentence)
  • The binding parameter holds the value implicitly as we call the extension method on a type. There should be only one binding parameter.

Additionally, if needed we can send multiple parameters to the method from the second parameter onwards. In IsExceeded(this string sentence, int limit) method limit is our second parameter.

Okay. This is great, but when should we use them?

When to use extension methods?

1. When we don’t have knowledge of library code

Generally, when we are using a library, we don’t have knowledge of the source code. Regardless, if we want to have a reusable class method in the library we can implement extension methods on top of it.

For example, we are using FileInfo from .NetFramework and we want to have a method that can count the number of lines in a given file as below

public static int FileLength(this FileInfo fileInfo)
{
    return File.ReadAllLines(fileInfo.FullName).Length;
}

2. To avoid forced inheritance

If we inherit the class and implement the method we want, we have to force the consumer to instantiate the subclass.

For example, we have a class that has a method that calculates the total price

public class Checkout
{  
    public int TotalPrice(int productCount, int cost)  
    {  
        return productCount* cost;  
    }  
}

public class DiscountedCheckout: Checkout
{
	public int TotalPriceWithDiscount(int productCount, int cost, int discount)  
	{  
	    return (productCount * cost) - discount;  
	}
} 

class Program  
{  
	static void Main(string[] args)  
	{  
		Checkout checkout= new Checkout();  
		int price = checkout.TotalPrice(4, 1000);
		// To execute the new method, we need to instantiate the subclass
		DiscountedCheckout discCheckout = new DiscountedCheckout(); 
		int discPrice = discCheckout.TotalPriceWithDiscount(4,1000,100);	 
	}
}

In the preceding example, in order to use the new method, the consumer is forced to create a subclass (Line 24). Consider the below example, and let us see how we can avoid unwanted instantiation.

public class Checkout
{  
    public int TotalPrice(int productCount,int cost)  
    {  
        return productCount* cost;  
    }  
}

public static class DiscountedCheckout
{
    public static int TotalPriceWithDiscount(this Checkout checkout, int productCount,int cost, int discount)  
    {  
        return (productCount* cost)-discount;  
    }  
} 

class Program  
{  
    static void Main(string[] args)  
    {  
	Checkout checkout= new Checkout();
	int price = checkout.TotalPrice(4, 1000);
        // Calling the extension method    
        int discPrice = discCheckout.TotalPriceWithDiscount(4,1000,100);		 
    }
}

3. To achieve separation of concerns

We can use extension methods to enhance readability and maintainability through the separation of concerns.

In ASP.NET Core, DI requires registration of services in the Startup.cs. In an enterprise-level application, we have multiple modules and sub-modules, and each has its own service registration.

Consider an example below, which has two modules Module1, and Module2, and each module contains eight services, in total, we have 16 service registrations in Startup.cs file.

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();

    // Module 1 Registrations
    services.AddSingleton<Module1.ISubModule1,Module1.SubModule1>();
    services.AddSingleton<Module1.ISubModule2,Module1.SubModule2>();
    services.AddSingleton<Module1.ISubModule3,Module1.SubModule3>();
    services.AddSingleton<Module1.ISubModule4,Module1.SubModule4>();
    services.AddSingleton<Module1.ISubModule5,Module1.SubModule5>();
    services.AddSingleton<Module1.ISubModule6,Module1.SubModule6>();
    services.AddSingleton<Module1.ISubModule7,Module1.SubModule7>();
    services.AddSingleton<Module1.ISubModule8,Module1.SubModule8>();

    // Module 2 Registrations
    services.AddSingleton<Module2.ISubModule1, Module2.SubModule1>();
    services.AddSingleton<Module2.ISubModule2, Module2.SubModule2>();
    services.AddSingleton<Module2.ISubModule3, Module2.SubModule3>();
    services.AddSingleton<Module2.ISubModule4, Module2.SubModule4>();
    services.AddSingleton<Module2.ISubModule5, Module2.SubModule5>();
    services.AddSingleton<Module2.ISubModule6, Module2.SubModule6>();
    services.AddSingleton<Module2.ISubModule7, Module2.SubModule7>();
    services.AddSingleton<Module2.ISubModule8, Module2.SubModule8>();
}

Exponentially, the number of registrations will increase and become cumbersome to maintain Startup.cs

With extension methods, we can refactor the service registration as below.

public static class Extensions
{
    public static IServiceCollection AddModule1(this IServiceCollection services)
    {
        services.AddSingleton<Module1.ISubModule1, Module1.SubModule1>();
        services.AddSingleton<Module1.ISubModule2, Module1.SubModule2>();
        services.AddSingleton<Module1.ISubModule3, Module1.SubModule3>();
        services.AddSingleton<Module1.ISubModule4, Module1.SubModule4>();
        services.AddSingleton<Module1.ISubModule5, Module1.SubModule5>();
        services.AddSingleton<Module1.ISubModule6, Module1.SubModule6>();
        services.AddSingleton<Module1.ISubModule7, Module1.SubModule7>();
        services.AddSingleton<Module1.ISubModule8, Module1.SubModule8>();
        return services;
    }
}
// Similarly we can create an extension method for Module 2

In the above code, we have created a static class for every module/sub-module according to the complexity and have an extension method that extends IServiceCollections

Now, Startup.cs contains only two methods that are at the module level. The code is Cleaner, Nicer, Maintainable & Readable.

// After implementing extension methods
public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();

    // Module 1 Registrations
    services.AddModule2(); 

    // Module 2 Registrations
    services.AddModule2();
}

4. Chaining

When we want to do a series of operations in a stepwise fashion i.e chain operations, we can opt for extension methods.

Consider the below example, we extended int type, and created three extension methods Sum, Subtract, and Multiply.

public static class CalculatorExtensions
{
    public static int Sum(this int input, int b)
    {
        return input + b;
    }

    public static int Subtract(this int input, int b)
    {
        return input - b;
    }

    public static int Multiply(this int input, int b)
    {
        return input * b;
    }
}

internal class Program
{
    static void Main(string[] args)
    {
        var input = 10;
        var result = input.Add(11)
            .Multiply(12)
            .Subtract(13);
    }


}

Now, with the help of extension methods, we can perform a series of operations through chaining (Line 24-26) and achieve readability.

LINQ is developed on extension methods that extend IEnumerable type. LINQ method syntax combined with chaining is a prominent feature of C#.

Summary

  • To sum up, extension methods are a powerful feature in C# that enables the developer to extend the functionality of an existing type (be it a class or data type, etc).
  • The best example of this feature implementation is LINQ method syntax, which empowered the developer to modify or understand the code in a nicer way. Consider the below snippet, we created a method, that filters all the numbers between 1 and 10, and sorts in ascending with the help of LINQ method syntax.
public IEnumerable<int> ExtensionMethodsLinqExample(List<int> numbers)
{
    return numbers
        .Where(c => c > 1 && c < 10)
        .OrderBy();
}
  • When we want to have a reusable method for different types, that don’t inherit a common base class, we can create extension methods on a common interface. The best example is LINQ. (They extend IEnumerable type)
  • It is advised not to go for this feature when there is a dilemma in what type to extend.
  • Opt this only for relevant types. For instance, it is more relevant to have a method date.AddMonths(numMonths) to extend DateTime rather than numMonths.AddDate(date) extending int .

References

Thanks for reading!

Please comment below and let me know your thoughts on the article!

1 thought on “Extension Methods And Their Benefits In Csharp”

  1. Pingback: Dew Drop – September 20, 2022 (#3768) – Morning Dew by Alvin Ashcraft

Leave a Reply

Your email address will not be published.