Upgrade your C# Skills part 1 – Extension Methods
DOWNLOAD the Code!
Now that I have VS2008 and .NET 3.5 installed, I am going to begin a series of articles on some of the new features you can use in C#. My hope is to add one new article a day for the rest of this week. Along the way, we’ll explore some of the new language features for C# 3.0. Truthfully, they aren’t all that new since C# 3.0 has been around for about a year, but support for them is now built in to Visual Studio, so using them is now realistic. Also, with the release of VS208, I’m sure this will be the first time most developers will be exposed to them. I’m going to start by introducing one of my favorite enhancements: Extension Methods.
Extension Methods
Extension methods offer us a way to expand class functionality even if we do not have access to the code for those classes (including sealed classes). In other words, we can add our own functionality to any object type. Have you ever looked at the String class and said “why can’t I do {fill in the blank} with a String?” If so, you probably created your own method, passed it the string in question, and consumed the return value:
string s = "1234"; string z = Reverse(s);
If you’ve ever written code like this, then Extension Methods are for you. In the case above, it should be obvious that Reverse is a static method, since it is not attached to an instance. Wouldn’t it be nice instead to write this?
string s = "1234"; string z = s.Reverse();
I know it is a subtle difference, and in this case not a terribly functional one, but hopefully you can see the difference. Instead of passing the string variable to a static method, you can treat your methods as though they belong to the string class. And these new methods are available in Intellisense: when I type “s.”, I will see my extension methods right alongside all the native methods, which makes finding and using them much more palatable. And you will find in the new Intellisense that Microsoft itself is making heavy use of Extension Methods. You can tell in two ways: first, the icon for extension methods is the familiar purple box but accented with a blue down arrow. Secondly, when the method description pops up in Intellisense, the description is preceded by “(extension)”.
Let’s look at a more reasonable example. Using the Regex class, you can determine whether or not string matches a Regular Expression pattern by passing a string and a pattern to the Regex.IsMatch() method:
string s = "This is awesome"; if (Regex.IsMatch(s, "awe")) { Console.WriteLine("Yep, this is a match!"); } else { Console.WriteLine("Sorry, no match."); }
I think it would be handy on occasion to simply “ask” the string itself if it matches a certain pattern:
string s = "This is awesome"; if (s.IsRegexMatch("awe")) { Console.WriteLine("Yep, this is a match!"); } else { Console.WriteLine("Sorry, no match."); }
Pretty neat, huh? OK, I admit it doesn’t appear to lessen your code, but to me it can make your code make more syntactic sense. And given a more complex example, it could do a lot for you. Richard Hale Shaw showed us an example of a .ForEach extension for IEnumerable<T> collections that would knock your socks off! Imagine being able to loop through an entire Collection and perform some action on each item in a single line of code, without passing the Collection off to a method? It would look something like this:
DirectoryInfo dir = new DirectoryInfo("C:\\"); FileInfo[] files = dir.GetFiles(); files.ForEach<fileinfo>(f => Console.WriteLine("{0} {1}", f.FullName, f.Length));
For now, ignore the code between the () – that’s a Lambda Expression, and we’ll get to those later in the week. Just understand that with this sample above, each FileInfo object in the array will have the passed Action applied to it. How many foreach loops do you think this little nugget could eliminate?
Now, I hope this will get you interested in Extension methods: it didn’t take me too long to think these are very cool! To get you started, I’m adding a new project called DevelopingForDotNet.Extensions to the Free Code page . My take on Richard’s method(s) are included, along with a handful of String and Numeric operations. Nothing too fancy, but hopefully enough to help get you started.
Enough already, how do I do it?
Creating Extension Methods is very simple. First, you must follow these simple rules:
- Extension Methods must be static, defined in a static class
- Extension Methods must be public
- The this keyword precedes the first parameter
We’ll take these one at a time. First, the method must be static because of how the compiler handles extensions. Behind the scenes, whenever an Extension Method is employed, the compiler actually generates code to call the static method. The idea of the Extension Method is just a visual layer for the developer: behind the scenes the actual static method is being called. Also, these methods can be called in a static fashion, rather than as methods attached to an instance. Which brings up another good point: the class name the Extension Methods are in is essentially irrelevant (unless you are going to call them explicitly). I put all my extensions in a single static class (imaginatively called “ExtensionMethods”).
Second, they probably do not absolutely have to be public, but if they aren’t then you are severely limiting the functional scope, so what’s the point?
Finally, preceding the first parameter with “this” is what tells the compiler that this is an Extension Method. It is also probably going to be the main source of initial confusion, because you do not pass this parameter (unless you are calling the method explicitly).
Overall, these are fairly simple rules to follow. Here is a handy method I’ve used for a while:
public static string RightAdjust(string s, int Size, char FillCharacter) { string ch = FillCharacter.ToString(); if (s.Length > Size) { throw new ArgumentException("Size of Value larger than Requested Size."); } while (s.Length < Size) { s = ch + s; } return s; }
This method receives a string and right adjusts it to the given size, filling the leading characters in with the passed char. Calling it looks like this:
string s = "1234"; s = RightAdjust(s, 7, '0'); // Value of s is now "0001234"
Now, let’s convert this to an Extended Method:
public static string RightAdjust(this string s, int Size, char FillCharacter) { string ch = FillCharacter.ToString(); if (s.Length > Size) { throw new ArgumentException("Size of Value larger than Requested Size."); } while (s.Length < Size) { s = ch + s; } return s; }
All we did was add “this” before the first parameter. Now we can call it like so:
string s = "1234"; s = s.RightAdjust(7, '0'); // Value of s is now "0001234"
Naturally, you will need to add a reference to the DLL that contains your Extension Methods in order to find and use them.
Overloading Extension Methods:
Just like other methods, Extension Methods can be easily overloaded. My approach for overloading has always been to put all the functionality in the method that requires the most parameters. I then simply have my overloading method signatures call the primary method, sending it the appropriate parameter values. Looking at the RightAdjust method above, I want to establish a method that will use a blank character as the default fill character if one is not supplied. What’s different about overloading extension methods, is that I actually employ the primary extension method in my overloading methods:
public static string RightAdjust(this string s, int Size) { return s.RightAdjust(Size, ' '); }
So now I can call this method passing it just the Size parameter, and that method then calls the primary method using the Extension Method mechanism.
Conclusion:
Extension Methods can be as simple or complex as you like. They are a nice syntactical enhancement to the language that lets you enhance other objects and use them how you would like. But beware: you could easily go overboard. I mean, there is no reason to create a MakeDirectory method for a TimeStamp instance, but you could. Just use common sense and make sure that the extensions you create apply to the object type and way that you would use them.
Nice intro. I just wanted to point out that you can use the built-in PadLeft to accomplish the same results as your RightAdjust.
Thanks Al. I did learn about PadLeft (and PadRight) after I wrote the article. It’s funny: there is just so much stuff built in to .NET that we can’t know it all. I wonder how often we end up reinventing the wheel?
Well done! The simplest explanation I’ve seen yet on the net! You are certainly a great communicator.
Thanks Jason! If you like this article, be sure to read the rest of the series “Upgrade Your C# Skills”, listed in the sidebar.
Excellent Series. Thanks.