Extension Methods in C#

What & Why

So the question is: What is an extension method and why do we need them?

According to MSDN: “Extension methods enable you to "add" methods to existing types without creating a new derived type, recompiling, or otherwise modifying the original type.” What this means is that using extension methods we can easily add methods to classes that we don't own or that we are not allowed to modify. An example of such a class is String that is owned by the .Net Framework or a third-party class that we don't own and we cannot modify directly.

It's important to point out here that, as the name suggests, extension methods just allow us to extend the functionality of a class and it does not allow us to access or modify the set of rules defined in a class in any manner. Also, it allows to add methods & we cannot add member variables as extensions. Extension methods are static methods and they are wrapped inside a static class, also the first parameter of the static class is the Type name prefixed with the this keyword that tells the compiler the type that this extension method supports.

Road Map

I will try to explain extension methods with three examples in this article viz.

  1. A simple extension method applied to a custom class.

  2. An extension method applied on an interface.

  3. An extension method for a HTML helper class that can be used in ASP.NET MVC applications.

Extension methods are most commonly used in LINQ queries. Since LINQ queries are applied on collections, all extension methods are useful because the standard extension method is developed for the "IEnumerable" interface and most of the collection classes implement this interface thus we can use all these extension methods. A complete list of extension methods with examples is available in the MSDN documentation at the following link: Enumerable Methods.

Also, please note that extension methods have a different symbol compared to the conventional methods and generally (extension) is written in front of the method description.

Example 1: Extension method for Custom Class

In the first example, I will try to explain how to implement an extension method to a custom class on which we don't have any control. Suppose we have the following two classes owned by a third-party:

  1. public class Product  
  2. {  
  3.     public int ProductId { getset; }  
  4.     public string ProductName { getset; }  
  5.     public decimal Price { getset; }  
  6.     public string Category { getset; }  
  7. }  
  9. public class ShoppingCart  
  10. {  
  11.     public List<Product> Products { getset; }  
  12. }  
Here ShoppingCart consists of a collection of products; we want to calculate the total price of each product present in the Shopping Cart. Suppose we have the following sample data:
  1. ShoppingCart cart = new ShoppingCart  
  2. {  
  3.     Products = new List<Product>   
  4.   {  
  5.      new Product { ProductId =1,  ProductName = "Milk", Price = 20, Category = "Beverages", },  
  6.      new Product { ProductId =2,  ProductName = "Television", Price = 2000, Category = "Electronic" },  
  7.      new Product { ProductId =3,  ProductName = "Inception", Price = 200, Category = "Media" },  
  8.     new Product { ProductId =4,  ProductName = "Coke", Price = 30, Category = "Beverages" },  
  9.    new Product { ProductId =5,  ProductName = "Dell Inspiron", Price = 26000, Category = "Electronic" }  
  10.    }  
  11. };  
Here is the extension method for calculating that:
  1. public static decimal GetTotal(this ShoppingCart cart)  
  2. {  
  3.     decimal totalPrice = 0;  
  4.     foreach (var item in cart.Products)  
  5.     {  
  6.         totalPrice += item.Price;  
  7.     }  
  8.     return totalPrice;  
  9. }  
Please note that we have made this method static and is inside a static class (that has nothing to do with our third-party class). Also, note the first parameter is ShoppingCart and it is prefixed with this keyword that denotes this method can be applied on objects of type "ShoppingCart". The implementation is straightforward; we are looping through the products collection and summing up the price.

Now, we can directly use this method as:
  1. decimal totalPrice = cart.GetTotal();  
Example 2: Extension method on an interface

In this example, we will see how an extension method can be developed for an interface to be applied to all the classes that implements that interface. Extension methods such as Where, OrderBy, Any and so on are all developed like this. As explained earlier all these methods are defined for the interface "IEnumerable".

Suppose we have the following interface and two classes:
  1. public interface IMotorBike  
  2. {  
  3.    string PrintCompanyName();  
  4. }  
  6. public class HeroMotoCorp : IMotorBike  
  7. {  
  8.    public int ModelId { getset; }  
  9.    public string ModelName { getset; }  
  10.    public decimal Price { getset; }  
  11.    public string PrintCompanyName()  
  12.    {  
  13.       return "Hero Moto Corp.";  
  14.    }  
  15. }  
  17. public class Bajaj : IMotorBike  
  18. {  
  19.     public int ModelId { getset; }  
  20.     public string ModelName { getset; }  
  21.     public decimal Price { getset; }  
  22.     public string PrintCompanyName()  
  23.     {  
  24.        return"Bajaj Motorbikes";  
  25.     }  
  26. }  
This implementation is unrealistic and does not make any sense but here I am just trying to explain the concept of how a single extension method can be applied to all those Types that implements that interface.

Now, suppose we want to write an extension method that returns the company name of the Type passed, we can implement it something like this:
  1. public static string GetBikesCompanyName(this IMotorBike moto)  
  2. {  
  3.    if (moto is HeroMotoCorp)  
  4.    {  
  5.       return ((HeroMotoCorp)moto).PrintCompanyName();  
  6.    }  
  7.    else   
  8.    {  
  9.       return ((Bajaj)moto).PrintCompanyName();  
  10.    }  
  11. }  
Here, we are just identifying the object type that will be passed at runtime and based on that returning the company name. Suppose we have two separate objects for each type that implements this interface as:
  1. var bike1 = new HeroMotoCorp { ModelId = 3, ModelName = "Karizma", Price = 92000 };  
  2. var bike2 = new Bajaj { ModelId = 1, ModelName = "Pulsar", Price = 82000 };  
Here, for both Types we can use the same extension method we just developed because both HeroMotoCorp and Bajaj implements IMotorBike. Here is how we invoke the method:
  1. Console.WriteLine("Bike1 Company Name is: {0}", bike1.GetBikesCompanyName());  
  2. Console.WriteLine("Bike2 Company Name is: {0}", bike2.GetBikesCompanyName());  
We will get the following output:


Example 3: Extension Methods for HTML Helper class

Last but not the least, we have a very important use of extension method in ASP.NET MVC. We don't have pre-built controls in MVC as we have in Web Forms for generating HTML controls, so we use HTML helper classes. If we notice closely, methods like TextBox, DropDownList and so on used in HTML helper classes are nothing but extension methods. We can create our own extension methods to generate HTML controls. Here I am taking an example of generating an image extension method to render a HTML image control.

Here is the extension method:
  1. public static MvcHtmlString Image(this HtmlHelper helper, string src, string alt)  
  2. {  
  3.    TagBuilder tag = new TagBuilder("img");  
  4.    tag.Attributes.Add("src", src);  
  5.    tag.Attributes.Add("alt", alt);  
  6.    return new MvcHtmlString(tag.ToString(TagRenderMode.SelfClosing));  
  7. }  
Here, we are using the TagBuilder class to build the HTML tag "img". Other attributes are added to this tag, like src and alt. We are returning an MvcHtmlString instead of String because by default Razor encodes everything, by using MvcHtmlString in your HTML helpers you can tell razor that it doesn't need to encode it.

Now, we can simply use this method in our View like this:
  1. @Html.Image(@Url.Content("~/Content/Images/Excel-icon.png"), "test")  
Please let me know in case of any issue, I have uploaded the complete source code for these examples.

Happy Coding.