Extension methods – ForEach statement

One of the features introduced along with .Net framework 3.5 is Extension methods which allows to extend a class by adding new methods without actually deriving from the class or changing the source code.  They are extremely handy to extend multiple classes by extending the common interface, .. ok lets take them one at a time.

I will show by example in the following order

1. To extend a class

2. To extend an Interface

1. Extend a Class

Step 1:

Consider a User class with two properties, FirstName and LastName and a method for getting the full name, FullName

namespace ExtensionMethods
{
    public class User
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
 
        public string FullName()
        {
            return FirstName + " " + LastName;
        }
    }
}

Given the above class, we can write a test method to verify the full name,

        [Test]
        public void Given_FirstName_And_LastName_Verifies_The_Full_Name()
        {
            User user = new User()
                               {
                                   FirstName = "Sujith",
                                   LastName = "Jagini"
                               };
            Assert.AreEqual("Sujith Jagini", user.FullName());
        }
    

All is well, the test completes successfully. 

Step 2:

Let’s say the User class is being provided without method, FullName and we don't have access to the source to include or request for.

    public class User
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
 
        //public string FullName()
        //{
        //    return FirstName + " " + LastName;
        //}
    }

No worries, we can calculate the full name

        [Test]
        public void Given_FirstName_And_LastName_Calculates_The_Full_Name()
        {
            User user = new User
                               {
                                   FirstName = "Arnold",
                                   LastName = "Schwarzenegger"
                               };
 
            Assert.AreEqual("Arnold Schwarzenegger", user.FirstName + " " + user.LastName);
        }

As you can see from the above test method, it is ugly that when ever we need full name we need to calculate it from first and last name and to remember the number of spaces between first and last name.

Step 3:

To avoid repetition of code, we can introduce a UserHelper class which holds all the helper methods for the User class.

namespace ExtensionMethods
{
    public class UserHelper
    {
        public static string GetFullName(User user)
        {
            return user.FirstName + " " + user.LastName;
        }
    }
}

And our test case shall utilize this new Helper class,

        [Test]
        public void Given_UserInstance_Gets_The_FullName_Using_UserHelper()
        {
            User user = new User
            {
                FirstName = "Tom",
                LastName = "Cruise"
            };
 
            Assert.AreEqual("Tom Cruise", UserHelper.GetFullName(user));
        }
 

Even though this works  the functionality defined in separate classes, User and UserHelper which are not very close to each other.

Step 4:

Welcome to Extension methods. With the help of Extension methods we can extend the class without actually touching it.

namespace ExtensionMethods
{
    public static class UserExtension
    {
        public static string FullName(this User user)
        {
            return user.FirstName + " " + user.LastName;
        }
    }
}
 

Extension methods have to be defined in static class with a static method. The first parameter of the method with the this User tells the compiler that we are extending the User class with FullName.  

We can have a test method that can use the FullName Extension method.

 
        [Test]
        public void Given_UserInstance_Gets_The_FullName_Using_UserExtension()
        {
            User user = new User
            {
                FirstName = "George",
                LastName = "Clooney"
            };
 
            Assert.AreEqual("George Clooney", user.FullName());
        }
 

2. Extend an Interface

The beauty of extensions methods not lies in extending classes, it's the interfaces which can be extended gives the real kicker. 

Let’s say we have an array of Users and like to get their Full Name in upper case letter. We can use the foreach loop.

        [Test]
        public void Iterate_Over_Array_Of_Users_Using_foreach_Loop()
        {
            User[] users = new User[]
                               {
                                   new User(){FirstName ="Angelina",LastName = "Jolie"}, 
                                   new User(){FirstName ="Jennifer",LastName = "Jolie"},
                                   new User(){FirstName ="Halle",LastName = "Berry"}, 
                               };
 
            foreach (User user in users)
            {
                Debug.WriteLine(user.FullName().ToUpper());
            }
        }

We can rewrite the above foreach loop for code readability and Linq chaining. For this we need to convert Array to List to make use of the ForEach method which takes Action as delegate

        [Test]
        public void Iterate_Over_Array_Of_Users_Using_ForEach_Method_On_List()
        {
            User[] users = new User[]
                               {
                                   new User(){FirstName ="Angelina",LastName = "Jolie"}, 
                                   new User(){FirstName ="Jennifer",LastName = "Jolie"},
                                   new User(){FirstName ="Halle",LastName = "Berry"}, 
                               };
            users.ToList().ForEach(u => Debug.WriteLine(u.FullName().ToUpper()));
        }

Converting Array to List is costly, to overcome this we can extend the built in IEnumerable interface to introduce a new ForEach method.

using System;
using System.Collections.Generic;
 
namespace ExtensionMethods
{
    public static class IEnumerableExtension
    {
        public static void ForEach<T>(this IEnumerable<T> list, Action<T> action )
        {
            if (action == null)
                throw new ArgumentNullException("action");
 
            foreach (T item in list)
            {
                action(item);
            }
        }
    }
}

And we can rewrite the test method using the ForEach Extension, notice we are not converting array to list.

        [Test]
        public void Iterate_Over_Array_Of_Users_Using_ForEach_Extension()
        {
            User[] users = new User[]
                               {
                                   new User(){FirstName ="Angelina",LastName = "Jolie"}, 
                                   new User(){FirstName ="Jennifer",LastName = "Jolie"},
                                   new User(){FirstName ="Halle",LastName = "Berry"}, 
                               };
            users.ForEach(u => Debug.WriteLine(u.FullName().ToUpper()));
        }
 

There you go,  you have extended the IEnumerable Interface without changing it’s structure.

By extending an interface we are able to extend all the classes that implement the interface like Collections, Dictionaries etc. Linq2Objects is actually implemented through extension methods in Enumerable class extending IEnumerable interface with lots of query operators including Sum, Where, Order By etc.

 

Published Sunday, July 19, 2009 1:41 PM by skjagini

Comments

# re: Extension methods – ForEach statement

Monday, July 20, 2009 12:14 PM by Issa Qandil

Good implementation, and Great example.

# re: Extension methods – ForEach statement

Monday, July 20, 2009 3:36 PM by lj

what is linq chaining anyway?

# re: Extension methods – ForEach statement

Monday, July 20, 2009 10:05 PM by Michael

Don't you think that in your example, FullName should be a method/property of User? IMHO Extension methods should be used to "extend" some class functionalities,  for examples for interfaces or third party components.

In that regards, your example with ForEach makes perfect sense.

Cheers,

# re: Extension methods – ForEach statement

Sunday, September 13, 2009 10:50 PM by singhgurd

Good example. This helped me while using a WCF service designed by someone else.

Leave a Comment

(required) 
(required) 
(optional)
(required)