C# Extension Methods: Explained

The intention of the article is to give an idea of what extension methods are and their benefits.

This article assumes that the user is familiar with C# programming. The intention of the article is not to stress extension methods too much, but to give an idea of what extension methods are and their benefits and the most important part of the article is not to reinvent the wheel by coding the methods which are already developed and shared by other developers and contributed for the benefit of the Microsoft development community. CodePlex provides the source openly for the developers.

The best resource for any of the Microsoft articles is the MSDN and to learn more about extension methods in Microsoft's words please refer to the URL:
http://msdn.microsoft.com/en-us/library/bb383977.aspx.

Before extension methods, we used to write our own methods in the class whenever needed in our projects. There are many built-in functions provided by the Microsoft .NET framework which can be used by developers without having them to rewrite the code for achieving basic functionality like all primitive date types can be converted into a string object using the ToString() method or to check if both the string objects are equal by Equals(object object) and so on.

Before the invention of extension methods

Let us see how a normal developer writes code to achieve functionality without using the extension methods.

Here in this example we look at two ways of coding:

  1. Writing lines of code without modularity i.e., just writing lines of code whenever needed by duplicating the efforts when the same code is to be used in some other module or project in the application.
  2. Converting the common lines of code into a method which can be reused whenever needed.

Create a Console application project using Visual Studio 2008 and modify the file Program.CS with the following code:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace ExtensionMethods

{

    class Program

    {

        static void Main(string[] args)

        {

            string userName = "John";

            string emptyString = "";

            int? number = 5;

            int? number2 = null;

 

            //Normally we use the following condition to check if a string

            //is not an empty value

            Console.WriteLine("****Writing lines of code as needed on the fly****");

            if (!string.IsNullOrEmpty(userName))

                Console.WriteLine("User Name is not Empty");

            else

                Console.WriteLine("User Name is Empty");

 

            if (!string.IsNullOrEmpty(emptyString))

                Console.WriteLine("EmptyString is not Empty");

            else

                Console.WriteLine("EmptyString is Empty");

 

            //Similarly to check if an integer is null then return default value

            //else convert the integer value to string value.

            if (number == null)

                Console.WriteLine("Number is null and so it is empty");

            else

                Console.WriteLine("Converted string value of number is {0}", number.ToString());

 

            if (number2 == null)

                Console.WriteLine("number2 is null and so it is empty");

            else

                Console.WriteLine("Converted string value of number2 is {0}", number2.ToString());

 

            Console.WriteLine("*************************************************");

 

            Console.WriteLine("****Calling a method to reuse the same code*******");

            //Otherwise, we write a method that checks if the string is not null or empty

            //More better way to write code

            Console.WriteLine("User Name is Not Empty?? {0}", IsNotNullOrEmpty(userName));

            Console.WriteLine("EmptyString is Not Empty?? {0}", IsNotNullOrEmpty(emptyString));

 

            //Better way to write code. Convert the lines of code into a method and call it.

            Console.WriteLine("String value of number is {1}", number, ToString(number,"empty"));

            Console.WriteLine("String value of number2 is {1}", number2, ToString(number2, "empty"));

            Console.WriteLine("***********************************************");

 

            Console.ReadLine();

        }

        /// <summary>

        /// Method to check if a string is not null or empty.

        /// </summary>

        /// <param name="input"></param>

        /// <returns></returns>

        static bool IsNotNullOrEmpty(string input)

        {

            return !string.IsNullOrEmpty(input);

        }

        /// <summary>

        /// Method to convert an integer value to string if it is not null

        /// otherwise return default value.

        /// </summary>

        /// <param name="value"></param>

        /// <param name="defaultvalue"></param>

        /// <returns></returns>

        static string ToString(int? value, string defaultvalue)

        {

            if (value == null) return defaultvalue;

            return value.Value.ToString();

 

        }

    }

}

Now run the program:

ExtMtd1.jpg

Tip: There are two beautiful and helpful tools provided by Microsoft for the developer community to beautify the code to make it more clean and maintainable. One is the Code Analyzer and the other one Code Metrics. Always use the Code Analyzer and Code Metrics tools in the Visual Studio to fine-tune your code so that your code will be clean and maintainable. You are not compelled to do all the changes, but you can follow whenever you feel it is good to change. As a developer, we sometimes ignore the best practices in a rush to complete the code within the deadline. For example, when I the run code analysis on the above project it shows me the following warning:

CA1305 : Microsoft.Globalization : Because the behavior of 'int.ToString()' could vary based on the current user's locale settings, replace this call in 'Program.ToString(int?, string)' with a call to 'int.ToString(IFormatProvider)'. If the result of 'int.ToString(IFormatProvider)' will be displayed to the user, specify 'CultureInfo.CurrentCulture' as the 'IFormatProvider' parameter. Otherwise, if the result will be stored and accessed by software, such as when it is persisted to disk or to a database, specify 'CultureInfo.InvariantCulture'.

Back to the topic. Now in my project, I have to check the string value and show on the UI the value if it is not empty or null otherwise, show that it is empty. To check that we use the built-in method for the string class IsNullOrEmpty and accordingly we will display the value in the UI. Similarly, I have a requirement that I check if a integer value is null then show it as "No records found" in the UI or convert the integer to a string and append to your message like "5 records found". To do this we check if an integer is null then return a default value otherwise, convert the integer to a string using the method ToString() and append to the string message whatever you would like to show in the UI.

Note: In general you cannot assign a null value to an int variable. Microsoft has introduced nullable types in the .NET framework to support assigning null to value types. Note that reference types already support null values. To learn more about the Nullable types refer to the MSDN article http://msdn.microsoft.com/en-us/library/1t3y8s4s%28v=vs.80%29.aspx.

Now what? If I have to use the same methods in various projects, I cannot rewrite the methods in each class and so we need to place all these methods in one place. For that we need to create a class library and add a static class and include all these methods in the class. Then reference these methods by adding the reference to the class library project.

Note: In many projects, we name this static class as "Helper" since it helps all the projects in providing the helpful methods in achieving the functionality. It is not necessary that the class be a static class, but the best practices from Microsoft says that any class that only supports some operations and does not need to be instantiated, you can make the class static. You can learn more about this from the MSDN article http://msdn.microsoft.com/en-us/library/79b3xss3(v=vs.80).aspx. Also note that even if you do not create a static class and instantiate every time to call the methods, when you run the Code Analyzer tool, it suggests you make the class as static and use the methods without instantiating the class. See how the tool helps you even if you forget something. That is the great thing about Microsoft which is committed to making the developer's life easy.

So, our next step is to create a class library project and add a static class called "Helper.CS" and add these methods in the class. I name my project as "Common" and modified the name of the file "Class1.CS" to "Helper.cs".

Here is my new class:
 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace Common

{

    public static class Helper

    {

        /// <summary>

        /// Method to check if a string is not null or empty.

        /// </summary>

        /// <param name="input"></param>

        /// <returns></returns>

       public static bool IsNotNullOrEmpty(string input)

        {

            return !string.IsNullOrEmpty(input);

        }

        /// <summary>

        /// Method to convert an integer value to string if it is not null

        /// otherwise return default value.

        /// </summary>

        /// <param name="value"></param>

        /// <param name="defaultvalue"></param>

        /// <returns></returns>

       public static string ToString(int? value, string defaultvalue)

        {

            if (value == null) return defaultvalue;

            return value.Value.ToString();

 

        }

    }

}


Now add the project reference to the Common project from the ExtensionMethods Console application and modify your "Program.cs" to add the reference and use the Helper class methods.

Here is my changed code:
 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using Common;

 

namespace ExtensionMethods

{

    class Program

    {

        static void Main(string[] args)

        {

            string userName = "John";

            string emptyString = "";

            int? number = 5;

            int? number2 = null;

 

            //Normally we use the following condition to check if a string

            //is not an empty value

            Console.WriteLine("****Writing lines of code as needed on the fly****");

            if (!string.IsNullOrEmpty(userName))

                Console.WriteLine("User Name is not Empty");

            else

                Console.WriteLine("User Name is Empty");

 

            if (!string.IsNullOrEmpty(emptyString))

                Console.WriteLine("EmptyString is not Empty");

            else

                Console.WriteLine("EmptyString is Empty");

 

            //Similarly to check if an integer is null then return default value

            //else convert the integer value to string value.

            if (number == null)

                Console.WriteLine("Number is null and so it is empty");

            else

                Console.WriteLine("Converted string value of number is {0}", number.ToString());

 

            if (number2 == null)

                Console.WriteLine("number2 is null and so it is empty");

            else

                Console.WriteLine("Converted string value of number2 is {0}", number2.ToString());

 

            Console.WriteLine("*************************************************");

 

            Console.WriteLine("****Calling a method to reuse the same code*******");

            //Otherwise, we write a function that checks if the string is not null or empty

            //More better way to write code

            Console.WriteLine("User Name is Not Empty?? {0}", Helper.IsNotNullOrEmpty(userName));

            Console.WriteLine("EmptyString is Not Empty?? {0}", Helper.IsNotNullOrEmpty(emptyString));

 

            //Better way to write code. Convert the lines of code into a method and call it.

            Console.WriteLine("String value of number is {1}", number, Helper.ToString(number, "empty"));

            Console.WriteLine("String value of number2 is {1}", number2, Helper.ToString(number2, "empty"));

            Console.WriteLine("***********************************************");

 

            Console.ReadLine();

        }

      

    }

}


I am done now. Now I have created a separate class library project and I am able to reference the common methods used by any of the projects within the solution without much effort. I am even following the best standards of making the class static and referring to the methods without instantiating the class thus avoiding the memory issues too. Then why do I need extension methods? I am happy with the code in my project and it was deployed in production and it is performing well.

One fine day, another developer who is new to the project or imagine a developer who is new to C# coding itself have joined our team. What he visualizes is that Microsoft's tool for .NET so called the "Visual Studio 2008" is so developer friendly and whenever he wants to use the built-in methods provided by the framework, he simply must put "." after the variable and it shows all the methods and properties supported. You might have told him that you did a great job by adding all the common methods in the static class "Helper" and he can use that whenever he must to write the similar kind of functionality. But he may miss that part and plans to reinvent the wheel by writing his own methods in the code. (This happened in one of my projects and believe me, when that developer left the team it required a substantial effort to change his code to remove the redundancy). So, how easy it would have been when he put "." after the variable, the intellisense have shown him the methods that I have placed in the Helper file for the same purpose? Definitely, it would have made everyone's life easy. Most of the projects have these kind of code integration issues.

Here are the reasons why I go for extension methods:

  • Extension methods helps to add methods to existing types without creating a new derived types.

  • If I am using a Third party library file in my project and I do not have source code available for the library, the best thing is to extend the existing types and write your own methods without understanding their code.

  • It makes developer's life easy to check what are all the methods supported by the type variable he is using at one place instead of searching in the project for the similar kind of method.

  • It helps the first time programmers concentrate on building the solution without putting so much emphasis on how to write that functionality in a separate method explicitly.

  • The main point is that I do not want to differentiate between my own methods and the built-in methods provided by the framework.

  • With extension methods, now you can add supported methods to value types which could not have been possible without it. For example, I have a variable of type double and I want to calculate the percentage of the value or any other functionality and if I check the operations supported, they are only few as below:

    ExtMtd2.jpg

I would like to extend the methods that are supported by the variable of type Double. You cannot do it in simple way without using extension methods.

Hi to Extension methods

The basic steps needed to write extension methods are as below:

  1. You have to include the "This" keyword as the first parameter in the extension method parameter list preceded by the variable type that you are extending.

  2. Make the extension method as static.

That's it and you are done with the extension methods for the types used in your project.

Now, I am going to modify the static helper class to make the methods as extension methods. My Helper class now looks like:

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace Common

{

    public static class Helper

    {

        /// <summary>

        /// Method to check if a string is not null or empty.

        /// </summary>

        /// <param name="input"></param>

        /// <returns></returns>

       public static bool IsNotNullOrEmpty(this string input)

        {

            return !string.IsNullOrEmpty(input);

        }

        /// <summary>

        /// Method to convert an integer value to string if it is not null

        /// otherwise return default value.

        /// </summary>

        /// <param name="value"></param>

        /// <param name="defaultvalue"></param>

        /// <returns></returns>

       public static string ToString(this int? value, string defaultvalue)

        {

            if (value == null) return defaultvalue;

            return value.Value.ToString();

 

        }

    }

}


Now go to the Program.cs file and try to check the methods supported by the types we have used earlier. You find that they support the methods that are defined in the helper class.

ExtMtd3.jpg

ExtMtd4.jpg

Now modify your code and run the program. You will find that the output remained the same. But, now you are able to use the methods as if they are supported by the framework classes itself. That is the beauty of extension methods.

ExtMtd5.jpg

Now coming to the last section of this article, the main intention of this article. CodePlex provides you the source code for the already defined extension methods that are shared by the developer's community to help other developers. You can use these methods in your projects without investing time on reinventing the wheel. If you have ever developed any other methods needed for your projects, share your code here so that it would be beneficial for other developers.


You can find the source code of the extension methods from the following URLs:


http://extensionoverflow.codeplex.com/SourceControl/changeset/view/53192
http://dnpextensions.codeplex.com/SourceControl/changeset/view/69562
http://extensionmethods.codeplex.com/SourceControl/changeset/view/15132
http://dotnetext.codeplex.com/SourceControl/changeset/view/67635 
http://extensionmethodsyay.codeplex.com/SourceControl/changeset/view/18197