Skip navigation

What Are Extension Methods

Extension Methods are a new feature in C# 3 and VB 9. To put it concisely, an extension method is a static helper function that can be invoked as if it were declared on any object that has a type matching that of its first parameter. It’s pure syntactic sugar. Extension methods basically allow you to change this:

List<String> injuredSecondary = new List<String>
{
  "Mike Brown",
  "Nathan Vasher",
  "Charles Tillman"
};
//Note that select is a static method on the System.Linq.Enumerable class
IEnumerable<String> namesake = Enumerable.Select(injuredSecondary, x => x == "Mike Brown");

To this:

List<String> injuredSecondary = new List<String>
{
  "Mike Brown",
  "Nathan Vasher",
  "Charles Tillman"
};
//Note that we are calling select as if it were a method defined on the injuredSecondary object.
IEnumerable<String> namesake = injuredSecondary.Select(x => x == "Mike Brown");

As the code shows, the Select function is defined on a class called Enumerable (in the System.Linq namespace). However the C# compiler lets you reference it as if it were declared on IEnumerable itself. The methods appear to extend IEnumerable without requiring a new IEnumerable interface to be created.

Digging Deeper

The previous statement should give a big hint as to why extension methods even exist. Let’s say you’ve made a framework or library that is intended for use by other developers. The library has been a huge success, with millions of developers using it. Lets also say that there is an interface within your library that is used practically everywhere within your framework as well as by those millions of developers. Finally, let’s say that you want to add new functionality to that interface in your new library release. You have four options (in order of desirability):

  1. Add the new functionality to the interface directly. This immediately breaks the existing implementations (because as you know interfaces are purely virtual, they can only declare method signatures, and cannot provide implementation).
  2. Extend the interface, this doesn’t break existing implementations, but users of your library can’t automatically benefit from the new features.
  3. Implement your extensions as helper functions allowing users of your library to access the new functionality if they want, or ignore it if they don’t OR:
  4. In addition to 3, if you also control the spec for the languages that leverage your framework, enhance the language to provide a syntax whereby static helper methods can be invoked as if they were declared directly on the object.

By adding the extension method syntax to C# 3 and Visual Basic 9, Microsoft was able to extend IEnumerable without breaking IEnumerable. Quite a feat if I must say so myself. The great thing is that we get the same power in our hands so without further ado. Let’s look at a sample.

Intersecting Rectangles

Let’s say we have two rectangles defined by the System.Windows.Rect structure; and we want to determine if they overlap or not. We could write the code that does it inline, or we could refactor it out to a helper method that takes two rectangles and compares them. Even nicer would be if we could add a convenience method to Rect that does the check for us. Of course the problem is that extending Rect means that it can no longer be a structure. Extension methods to the rescue. Here is the definition of an extension method that does what we want. (Code not optimized for clarity’s sake).

    public static class Extensions
    {
        /// <summary>
        /// Determines if two <see cref="Rect"/>s overlap each other
        /// </summary>
        /// <remarks>
        /// The order of subject and target do not matter.
        /// </remarks>
        /// <returns>True if the Rectangles overlap, false otherwise.</returns>
        public static bool Overlaps(this Rect target, Rect subject)
        {
            Double innerRight = Math.Min(target.Right, subject.Right);
            Double innerLeft = Math.Max(target.Left, subject.Left);
            Double innerBottom = Math.Min(target.Bottom, subject.Bottom);
            Double innerTop = Math.Max(target.Top, subject.Top);
            return innerRight > innerLeft && innerBottom > innerTop;
        }

    }

There are a few things to note here. First you’ll notice the ‘this’ keyword being used in the signature for overlaps. In this case ‘this’ tells the compiler that the function can be used as an extension method on any variable of type Rect. Slightly less out of place, but still significant is the fact that the Extensions class is declared static itself. Extension methods can only be declared in static classes. Below is the unit test for the Overlaps function. What it doesn’t show is one final point: to use an extension method, you must import the namespace in which the method is declared.

        /// <summary>
        ///A test for Overlaps
        ///</summary>
        [TestMethod()]
        public void OverlapsTest()
        {
            //A rectangle overlaps itself
            Rect target = new Rect
            {
                Location = new Point { X = 0, Y = 0 },
                Size = new Size { Height = 20, Width = 40 }
            }; 
            Assert.IsTrue(target.Overlaps(target),"A rectangle overlaps itself");
            Rect subject = new Rect
            {
                Location = new Point { X = 40, Y = 0 },
                Size = new Size { Height = 20, Width = 40 }
            };
            //Rectangles that share an edge do not overlap.
            Assert.IsFalse(
                target.Overlaps(subject),
                "Rectangles that share an edge do not overlap.");
            Assert.AreEqual<bool>(
                target.Overlaps(subject), 
                subject.Overlaps(target),"Overlaps is Symmetric",subject,target);

            subject.Location = new Point { X = 10, Y = 5 };
            subject.Size = new Size { Height = 10, Width = 20 };
            //Rectangles overlap when one is entirely inside the other.
            Assert.IsTrue(
                target.Overlaps(subject),
                "Rectangles overlap when one is entirely inside the other.");
            Assert.AreEqual<bool>(
                target.Overlaps(subject), 
                subject.Overlaps(target), 
                "Overlaps is Symmetric", subject, target);
            
            subject.Location = new Point { X = 30, Y = 15 };
            //Rectangles overlap when one is partially inside of the other.
            Assert.IsTrue(
                target.Overlaps(subject), 
                "Rectangles overlap when one is partially inside of the other.");
            Assert.AreEqual<bool>(
                target.Overlaps(subject), 
                subject.Overlaps(target), "Overlaps is Symmetric", subject, target);

            subject.Location = new Point { X = 100, Y = 500 };
            //Rectangles do not overlap when they share no points in common.
            Assert.IsFalse(
                target.Overlaps(subject), 
                "Rectangles do not overlap when they share no points in common.");
            Assert.AreEqual<bool>(
                target.Overlaps(subject), 
                subject.Overlaps(target), "Overlaps is Symmetric", subject, target);
        }

Nutritious AND Delicious.

Extension methods are a powerful tool for your arsenal. If you’re doing framework/systems coding, I’d recommend that you definitely look at using extension methods rather than derivation when possible when targeting a .NET Framework class (like Rect). In the case of Rect, you get the added benefit of keeping it as a structure rather than requiring it to be a class. In general, it allows users of your framework to take advantage of its capabilities without changing their object model hierarchy.

For those wondering, no I’m not leaving the WPF trail. This was just a tangent that I decided to talk about while writing something else about WPF. More of the usual will be headed your way soon.

Advertisements

One Comment

  1. Thanks for posting this. I was looking for a quick snip of code to check for Rectangle Overlaps and kudos for the Extension.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: