Click here to Skip to main content
14,452,838 members

Extension Methods: Understanding the Benefits and Proper Usage

Rate this:
4.86 (18 votes)
Please Sign up or sign in to vote.
4.86 (18 votes)
17 Nov 2015CPOL
Tips and tricks for using extension methods properly along with a few of my favorites

Introduction

Extension methods allow for adding methods to existing classes -- sort of. However, they have some limitations that regular methods do not have and some nice benefits beyond what a regular method is capable of. This tip examines those differences as well as when you should and should not use an extension method.

Background

The purpose of this tip is not to explain extension methods at an introductory level, but as a quick summary. An extension method is a static method in a static class that has a 'this' keyword in front of the first parameter to specify what type of class it is extending. A simple example shows this quite clearly:

//
// Declaring the extension method
//

public static class MyExtensions
{
     public static bool IsWellDefined(this string str)
     {
          return !string.IsNullOrWhiteSpace(str);
     }
}

//
// Using the extension method
//

public class Testing
{
    public void DoStuff(string name)
    {
        if (!name.IsWellDefined())
           throw new ArgumentException(...)
        else
           ...
    }
}

Drawbacks and Benefits of Extension Methods

Drawbacks

Namespace Referencing

Perhaps the biggest drawback to the use of extension methods is that you need to make sure that the namespace containing the static class in which the extension method is defined is included in the "using" clause at the beginning of your file (unless the static class already resides in the namespace hierarchy of the class you are working in). When first working with extension methods, this can be confusing, since a method will show up in Intellisense on one file and not another, and even though Intellisense clearly marks that it is an extension method, it can be overlooked.

For many systems, however, this is not a major issue, as often there is some sort of core functionality assembly that is generally a part of each file.

So if you keep all of your commonly-used goodies in the assembly MyCompany.Core.dll, and that assembly is referenced on just about every source code file with "using MyCompany.Core", then you simply need to put your extension method classes in the namespace MyCompany.Core and they will practically be available from just about everywhere.

Lack of "ref" Capability

You are not permitted to define an extension method with a "ref" tag on the same parameter as the "this" parameter. So you cannot define an extension method that would do this:

public void foo()
{
     int value = 3;
     value.IncrementBy2(); // having an extension method like this to turn value to 5 isn't possible
}

I have not experimented with the new 'ref' features in 7.0 and how they would work in conjunction with extension methods.  It might be interesting to see how they interplay.

Benefits

Third Party Classes

This is a pretty easy one, but you may be using assemblies from third party sources where you don't have the option to modify the classes directly, but you have some simple functionality that is reused in a way consistent with extension method usage (see below), and so the ability to add extension methods here can greatly improve the readability of your code.

Null Referencing

It is critical to remember that extension methods are really nothing more than "syntactic sugar", and at compile time, the appropriate static method is actually called, so all the rules of regular static method calls apply. In particular, you can deal with null values. Consider the example above in the Background section. If you were to call DoStuff(null), you might expect that the "if" statement would throw an object reference not found exception, but it will not. This is being turned into if (MyExtensions.IsWellDefined(null)), in which it is perfectly valid to pass in a null value. The ability to have null values referenced can lead to all sorts of useful extension methods. For example, I have an extension method in my library called ToUpperIfNotNull(), which returns null when the incoming value is null and translates to upper case when it is not. Similarly, I have an extension method like this:

public static string ToString(this DateTime? dt, string format)
{
     return dt.HasValue ? dt.Value.ToString(format) : null;
}

In cases where there are frequent null checks occurring, this extension method syntax can make your code much cleaner.

Reducing Exception Checking / Handling

Throwing exceptions is a natural part of well-written code, but often we have common situations in which we would like to avoid exception handling without complicating our code. Here's a common example. The string.Substring method will throw an exception if you ever go outside of the boundary regions. So the following will throw an exception:

string newValue = "hello".Substring(0, 10);

Of course, you would never do that directly, but a more generalized usage would be something like this:

public static string GetLeftCharacters(string str, int numChars)
{
     if (str != null && str.Length >= numChars)
        return str.Substring(0, numChars);
     else
        return str;
}

But since this probably comes up often, a better solution is to turn this into an extension method with just a slight signature change:

public static string Left(this string str, int numChars)
{
     if (str != null && str.Length >= numChars)
        return str.Substring(0, numChars);
     else
        return str;
}

This brings us back to good old days of VB where we can now do this without ever having to worry about an exception being thrown:

public void DoSomething(string str)
{
     if (str.Left(3) == "ABC")
        ...
     else
        ...
}

Another very common problem is the following:

public static void DoSomething(IDictionary<int, string> myDictionary)
{
     // Method 1: this is clean, but does 2 lookups

     string twoValue = myDictionary.ContainsKey(2) ? myDictionary[2] : "defaultValue";

     // Method 2: one lookup, but messy

     string outValue;
     if (!myDictionary.TryGetValue(2, out outValue))
        outValue = "defaultValue";

     // Method 3: using the extension method below

     string thisTwoValue = myDictionary.GetValueOrDefault(2, "defaultValue");
}

The GetValueOrDefault extension method above not only is cleaner, but extends the dictionary using a naming convention similar to what is used elsewhere in .NET, which makes it even nicer.

public static VAL GetValueOrDefault<KEY, VAL>(this IDictionary<KEY, VAL> dict, 
                                              KEY key, VAL defaultValue)
{
    VAL val = default(VAL);
    return (dict != null && key != null && dict.TryGetValue(key, out val)) ? val : defaultValue;
}

And this brings me to the last benefit I'm going to discuss about extension methods...

Extension Methods on an Interface

Any purist will tell you that even if C# were to support the concept of putting method definitions into interfaces, it's just wrong, wrong, wrong. Well, in general, I think the arguments are valid, but there are definitely situations where a slight bending of that rule can yield some great benefits.

Case in point is the last example above. Sure, I could define the extension method GetValueOrDefault on a Dictionary<KEY, VALUE> directly, but what is gained? Remember that extension methods are syntactic sugar for static method calls and having method parameters that are interfaces is preferable to concrete classes in almost every case. So having extension methods on interfaces is preferable in many cases to the concrete class equivalent (in my opinion...).

In fact, LINQ is exactly that - extension methods on IEnumerable<T>!

How to Use Extension Methods

I really am not going to delve into this topic too deeply, as it's covered elsewhere in great depth, but it suffices to say that an extension method should be a relatively simple extension to your existing classes that has a similar feel and functionality to the existing methods. You should use the same naming conventions as the existing methods both on the method name and the parameters, and it is crucial to add the /// comments so that Intellisense can help guide the user of your extension method in its usage. If you find your extension method is covering more than one screen, most likely this is not a good candidate for an extension method.

On the string object, extension methods like "Left", "Right", "ToUpperIfNotNull", etc. are all that would be generally considered natural extensions of the string class. However, an extension method named "IsAValidPhoneNumber" would NOT be a good candidate for an extension method, and at that point you would correctly realize that you need an external validation class for that.

These Are A Few of My Favorite Extensions...

Over the years, I have developed quite an extensive collection of extension methods. Some of the ones I use most often are shown above. With properly defined extension methods, it becomes so natural that you forget that they aren't built in. Yes, there are plenty of those online, but if there is interest, I could write some articles breaking them down by category. This includes DateTime, string, reflection, IEnumerable, XmlDocument / XDocument, and more.

History

  • 17th November, 2015: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Michael S. Post
Software Developer (Senior) Micro-Office Systems. Inc.
United States United States
Michael Post has been working as a professional programmer since 1989, and since 2002 as the Chief Software Architect at Micro-Office Systems, Inc., a company that specializes in medical conversions and interfaces as well as services like statements and phone reminders.

Comments and Discussions

 
GeneralExtension and OOP Pin
Mirza Merdovic12-Jul-16 22:45
professionalMirza Merdovic12-Jul-16 22:45 
AnswerUsing "this." in front of an extension Pin
Michael S. Post19-Nov-15 9:43
MemberMichael S. Post19-Nov-15 9:43 
QuestionAs an example? Pin
Ice Diamond19-Nov-15 1:02
professionalIce Diamond19-Nov-15 1:02 
AnswerRe: As an example? Pin
Michael S. Post19-Nov-15 2:48
MemberMichael S. Post19-Nov-15 2:48 
GeneralRe: As an example? Pin
Ice Diamond19-Nov-15 18:56
professionalIce Diamond19-Nov-15 18:56 
QuestionYou got me interested Pin
Harm Salomons18-Nov-15 6:39
MemberHarm Salomons18-Nov-15 6:39 
Generaltwo things... Pin
John Torjo17-Nov-15 11:52
professionalJohn Torjo17-Nov-15 11:52 
GeneralRe: two things... Pin
Matt T Heffron19-Nov-15 8:31
professionalMatt T Heffron19-Nov-15 8:31 
GeneralRe: two things... Pin
John Torjo19-Nov-15 9:10
professionalJohn Torjo19-Nov-15 9:10 
GeneralRe: two things... Pin
Matt T Heffron19-Nov-15 9:26
professionalMatt T Heffron19-Nov-15 9:26 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Article
Posted 17 Nov 2015

Tagged as

Stats

17.6K views
18 bookmarked