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.
Extension methods are used to add functionality to the existing types, without
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.
this
keyword.this
keyword. In the above example, this
keyword is followed by string
(this string sentence
)Additionally, if needed we can send multiple parameters to the method from the second parameter onwards. In
method limit is our second parameter.IsExceeded(this string sentence, int limit)
Okay. This is great, but when should we use them?
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; }
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); } }
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(); }
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#.
public IEnumerable<int> ExtensionMethodsLinqExample(List<int> numbers) { return numbers .Where(c => c > 1 && c < 10) .OrderBy(); }
IEnumerable
type)DateTime
rather than numMonths.AddDate(date) extending int
.Thanks for reading!
Please comment below and let me know your thoughts on the article!
My name is Mani Sagar Pratha. My areas of competence as a full-stack developer include web apps, .NET Core, JavaScript, Vue JS, Angular, and HTML/CSS. Additionally, I have practical knowledge with cloud technologies like AWS and Azure. I’m a lifelong learner who likes to food, flight, and try out different eateries in my city.
In this post, we’ll see how to test gRPC Server applications using different clients. And… Read More
In this post, we'll create a new gRPC project in ASP.NET Core and see what's… Read More
In this blog post, we’ll see how to run dotnet core projects without opening visual… Read More
Programmatically evaluating policies is useful when we want to provide access or hide some data… Read More
We saw how we could set up policy-based authorization in our previous article. In this… Read More
What is policy-based authorization and how to set up policy-based authorization with handlers and policies… Read More
This website uses cookies.