Convenient Uses of C# Extension Methods - Part 1
I've been working with C# since around 2003, but I have to admit that extension methods are not something I used until about 2 years ago.
Extension methods allow you to add methods to an existing type even if you don't have access to the source code of the type.
The Problem
Let's say that we needed to represent a set of user roles in a system. We would probably define them using an enumeration type:
public enum UserRole { Developer, SysAdmin, SeniorManager, Manager, SalesRep, Intern, ChiefExecutiveOfficer }
Now let's say we needed to display a user-friendly version of these, perhaps on a user management page. To do this, we have a few options:
Option #1 - Use A Database
We could simply store user friendly versions of the enumeration types in a database (if our program has one). However, this might be overkill if the the enumeration types rarely change or if they are never edited outside of the code.
Option #2 - Parse the Enumeration Type
We could convert the enumeration type to a string try to make it user-friendly. Here is one way:
private static string ConvertRoleToText(UserRole userRole) { char[] characters = userRole.ToString().Trim().ToCharArray(); StringBuilder sb = new StringBuilder(); foreach (char character in characters) { if (character >= 'A' && character <= 'Z') { sb.Append(" "); } sb.Append(character); } return sb.ToString().Trim(); // Remove leading space }
This produces the following conversions:
Developer Sys Admin Senior Manager Manager Intern Sales Rep Chief Executive Officer
However, this option turns out to be very fragile and has several shortcomings.
First, it's directly tied to how the enumerated types are named. This adds a constraint on the system that the we will need to worry about when adding or modifying the enumeration types.
Second, it does not handle the case where we have abbreviations, like "CEO" instead of "ChiefExecutiveOfficer". For an enumeration type of CEO, the output would be "C E O", which is not standard.
Third, this method is overly complicated. I have to think about what that method is doing - that is, it really needs documentation on what it is doing to make it clear.
Option #3 - Use a GetFriendlyDescription() Method
A more robust way of converting the enumeration types to user-friendly versions is to just write a method that gets a description based on the type.
public string GetDescription(UserRole userRole) { switch (userRole) { case UserRole.Developer: { return "Developer"; } case UserRole.SysAdmin: { return "System Administrator"; } case UserRole.SeniorManager: { return "Senior Manager"; } case UserRole.Manager: { return "Account Manager"; } case UserRole.SalesRep: { return "Sales Representative"; } case UserRole.Intern: { return "Coffee Getter and Code Monkey"; } case UserRole.CEO: { return "Chief Executive Officer"; } default: { throw new ArgumentException("Enumerated type not found."); } } }
This is OK and in the context of our software will work, it's clear, and will be maintainable. Typical usage would look something like this:
var rolesConverter = new RolesConverter(); List userFriendlyRoles = new List(); foreach(var validRole in validRoles) { var description = rolesConverter.GetUserFriendlyDescription(validRole); userFriendlyRoles.Add(description); }
What I do not like about the above option is the GetUserFriendlyDescription has to be made static or defined in a class that has to be instantiated.
Option #4 - Write an Extension Method
With an extension method, you can extend the enumeration type to have a new method which returns the description:
public static class UserRoleExtensions { public static string UserFriendlyDescription(this UserRole userRole) { switch (userRole) { // case statements omitted for brevity...same as in Option #3 } } }
By wrapping the method in a static class and modifying the argument to include the this keyword, we now get to do this:
List userFriendlyRoles = new List(); foreach(var validRole in validRoles) { var description = validRole.UserFriendlyDescription(); userFriendlyRoles.Add(description); }
What I really like about this, is you get IntelliSense on the actual type you are working with. The UserFriendlyDescription() method looks just like it was built into the type itself.
In Part 2 of this series, I show a few more examples of how extension methods can result in more readable and maintainable code.