Introduction to LINQ: Part III - Extension Methods

This is part three of my introduction to LINQ tutorial. If you have not read parts one and two and you do not have a decent understanding of delegates and/or lambda expressions, then please head over and read those first.

Table of Contents

Resources


As I did in parts one and two I will start off part three with a quote from the MSDN:

Extension methods enable you to "add" methods to existing types without creating a new derived type, recompiling, or otherwise modifying the original type. Extension methods are a special kind of static method, but they are called as if they were instance methods on the extended type. For client code written in C# and Visual Basic, there is no apparent difference between calling an extension method and the methods that are actually defined in a type.

This description is a little cryptic. To break this down let me provide a simple example of a working extension method. Lets say that I often have a need to convert a string variable into an integer. The standard way to do that would be to use Convert.ToInt32(string);. So if I wrote a console application to gather the ages of five people and calculate the average age, it would look something like this:

static void Main(string[] args)  
{
    Console.WriteLine("Please enter the first person's age:");
    int age1 = Convert.ToInt32(Console.ReadLine());

    Console.WriteLine("Please enter the second person's age:");
    int age2 = Convert.ToInt32(Console.ReadLine());

    Console.WriteLine("Please enter the third person's age:");
    int age3 = Convert.ToInt32(Console.ReadLine());

    Console.WriteLine("Please enter the fourth person's age:");
    int age4 = Convert.ToInt32(Console.ReadLine());

    Console.WriteLine("Please enter the fifth person's age:");
    int age5 = Convert.ToInt32(Console.ReadLine());

    Console.WriteLine();
    int averageAge = (age1 + age2 + age3 + age4 + age5) / 5;
    Console.WriteLine("The average age is: {0}", averageAge);
    Console.ReadLine();
}

The input that comes in from Console.ReadLine() is always a string. Even if I know it is a number contained within the string I still have to convert it to an integer. For the sake of this example we will ignore instances where users supply input that is not a valid integer. For now lets simplify how we convert the string to an integer using extension methods. In .NET every object has a .ToString() method and it comes in very handy for things like turning booleans and integers into strings quickly. Unfortunately there is not anything similar for turning strings into other data types. However, we can simulate this through the use of an extension method.

Before I show you the extension method I want to cover a couple rules. Rule number one is that all extension methods MUST be public static methods on a public static class. The name of the class does not matter so long as it is static. For our extension method, add this class to your console application:

static class ExtensionMethods  
{
    public static int ToInt(this string value)
    {
        return Convert.ToInt32(value);
    }
}

You can either add this class to the same namespace as your console application, or a different namespace. If you add it to a different namespace then you will have to include a using statement at the top which points to the new namespace.

So what did adding this class do? Let's find out. Go back to your console application and modify it to look like this:

static void Main(string[] args)  
{
    Console.WriteLine("Please enter the first person's age:");
    int age1 = Console.ReadLine().ToInt();

    Console.WriteLine("Please enter the second person's age:");
    int age2 = Console.ReadLine().ToInt();

    Console.WriteLine("Please enter the third person's age:");
    int age3 = Console.ReadLine().ToInt();

    Console.WriteLine("Please enter the fourth person's age:");
    int age4 = Console.ReadLine().ToInt();

    Console.WriteLine("Please enter the fifth person's age:");
    int age5 = Console.ReadLine().ToInt();

    Console.WriteLine();
    int averageAge = (age1 + age2 + age3 + age4 + age5) / 5;
    Console.WriteLine("The average age is: {0}", averageAge);
    Console.ReadLine();
}

Do you see what we did there? It's obvious that we are no longer calling Convert.ToInt32() but how is this possible? Believe it or not our extension method actually extended the string type. We don't have direct access to the code that defines a string yet we were still able to tack a method onto the type so that all strings have access to it as if it were an instance method.

There are three things that make our method an extension method. First the method must be in a static class. Second, our method must also be static. And finally, it must contain an argument prefixed with the this keyword. This keyword is what tells .NET it is an extension method. The type following the this keyword must be the type you want to extend. You can extend any type, even types you create yourself (though when you have access to the code, it's usually easier to just add the method to the actual type instead of making an extension). The argument that is defined using the this keyword is the argument that will be passed in containing the instance of whatever you are extending.

Notice that when we call .ToInt() we pass in no arguments. That is because .NET knows it is an extension method and passes in the object instance you are calling the method from in the first place. The concept is pretty simple, but just like lambdas they are a tiny thing that can become very powerful when used creatively.

Extension methods are simple enough that you probably have a decent idea of how to use them already, but before I wrap up part three lets put extension methods to use in a some more practical ways. What if we wanted to gather ages for more than five people and then average those together? Think we could use extension methods to make this process a little easier? You bet we can.

Modify your console application to look like this:

class Program  
{
    static void Main(string[] args)
    {
        // Create a list to add ages to.
        List<int> ageList = new List<int>();

        // How many people to gather ages for?
        Console.WriteLine("How many people would you like to average?");
        int numberOfPeople = Console.ReadLine().ToInt();

        // Loop through, gather ages of each person, add them to the list.
        for (int count = 0; count < numberOfPeople; count++)
        {
            Console.WriteLine("Enter the age of person {0}:", count);
            ageList.Add(Console.ReadLine().ToInt());
        }

        // Show the average.
        Console.WriteLine();
        Console.WriteLine("The average age is: {0}", ageList.Average());
        Console.ReadLine();
    }
}

static class ExtensionMethods  
{
    public static int ToInt(this string value)
    {
        return Convert.ToInt32(value);
    }

    public static int Average(this List<int> list)
    {
        int sum = 0;
        foreach (int age in list)
            sum += age;
        return sum / list.Count;
    }
}

If you are a little familiar with LINQ already you will know that LINQ has an extension method for calculating averages. For this example please ignore that and remove the using System.Linq; statement from your sample project if you are building one. For now we will use our own custom extension method for calculating the average of all integers in the list.

See how easy that was? We were able to factor out all the averaging code into an extension method. This extension could be used all over your application wherever you had a need to calculate the average on a list of integers. Hopefully by now you are realizing the power of extension methods. LINQ combines the power of extension methods with the power of lambda expressions to make language integrated queries a possibility.

Part IV - Putting it All Together

Chev

Read more posts by this author.

comments powered by Disqus