C# Extension Methods … Portability Aid?

In my first post about Paint.NET v4, I mentioned that I was learning a lot about C# 3.0 and .NET 3.5. One of the things I’m absolutely falling head-over-heels for is extension methods.

One use of these is to add utility functionality on to existing classes that you don’t own the implementation for. As an example, consider the System.Uri class. In my opinion, it could use an Append method:

public static class UriExtensions
{
    public static Uri Append(this Uri baseUri, string relativePath)
    {
        return new Uri(baseUri, relativePath);
    }
}

You can then use code such as this:

Uri donateUri = new Uri("http://www.getpaint.net").Append("/donate.html");

Instead of:

Uri donateUri = new Uri(new Uri("http://www.getpaint.net"), "/donate.html");

(Bear in mind that the string literals would probably be pulled out of a list of constants, and not inlined like in the code above.) In this case, we’re simply enabling a choice of coding style and not really doing anything new. We are avoiding pathological nesting such as "new Uri(new Uri(new Uri(…" for when we need to paste together multiple relative Uri paths.

One place I’m finding extensions methods to be very useful is in defining and implementing interfaces with only strictly orthogonal functionality. You don’t need to add utility methods to interfaces, you can simply define them as extension methods.

For example, my resource manager is contained in an IResourcesService interface that has GetString() and GetBinaryStream() methods. And that’s it.

using System.IO;

namespace PaintDotNet.Framework
{
    public interface IResourcesService
    {
        string GetString(string stringID);
        Stream GetBinaryStream(string streamID);
    }
}

All resources other than strings are stored as binary streams, such as icons, bitmaps, text files, etc. If you want to load a bitmap, then you can use the handy extension method:

using System.Drawing; // pull in GDI+

namespace PaintDotNet.WinForms
{
    public static class ResourcesServiceExtensions
    {
        public static Bitmap GetBitmap(this IResourcesService resources, string streamID)
        {
            return new Bitmap(resources.GetBinaryStream(streamID));
        }

        // similarly for GetIcon, GetCursor, etc.
    }
}

… elsewhere in the code …

menuItem.Image = Resources.GetBitmap("Icons.MenuFileExitIcon.png");
    // Resources is a local property that gets the IResourcesService assigned to us

I could have multiple implementations of IResourcesService. For example, I could have the "normal" implementation that pulls from the local file system, then a test version that does all sorts of fakery, and then maybe experiment with one that loads all my resources via HTTP GET. But, I only ever need one implementation of GetBitmap() because it is just a helper method sitting on top of GetBinaryStream().

But wait, there’s more!

GetBitmap() returns a reference to a System.Drawing.Bitmap object. You generally only care about this type if you’re writing a WinForms application (which I am doing). Or, put another way, you wouldn’t want an object of this type if you were writing a WPF application. You’d want a BitmapSource or something. I am not writing a WPF version of Paint.NET v4, but one of my goals is to have the ability to write one. This is where the "separation of UI and logic" comes into play from my previous post. I refer to the WinForms UI as the shell. The "business logic" portion of the code base is referred to as the application (app for short). You could potentially have a WPF shell, or a command-line shell. Or you could write your own UI toolkit that P/Invokes straight to the Win32 API and bypasses WinForms completely (I believe the latest Zune UI does this).

So, if we wanted a WPF shell for Paint.NET, we could still use the same interface and implementation for IResourcesService. No changes needed anywhere. Why should the resource loader care about what UI you’re using? Its job is to give you strings and binary streams. It’s your job to parse them. It shouldn’t need a reference to the likes of System.Drawing.dll or PresentationCore.dll.

Thus, we’d probably find the following extension method setup useful in our hypothetical WPF shell:

using System.Windows.Media.Imaging;
namespace PaintDotNet.Wpf
{
    public static class ResourcesServiceExtensions
    {
        public static BitmapSource GetBitmapSource(this IResourcesService resources, string streamID)
        {
            return …; // not sure what code goes here exactly 🙂
        }
    }
}

The WPF-based shell would never pull in the PaintDotNet.WinForms namespace, and would instead use the PaintDotNet.Wpf namespace. They’d almost certainly be in separate DLL’s. So these two static extension classes will never collide, even if they have similarly named methods.

Using extension methods this way is probably preferable to "interface inheritance" for adding utility methods. For example, now I don’t need to declare a IWinFormsResourceService that derives from IResourcesService. I can avoid putting all the utility functionality into the base interface which could cause headaches.

As another example of only implementing "strictly orthogonal functionality", consider the case of the System.Windows.Threading.Dispatcher class and its CheckAccesss() and VerifyAccess() methods. The latter basically calls the former and converts true/false into return/throw. It could be done as an extension method. In fact, I have my own IDispatcher interface in Paint.NET v4, and this is exactly what I do. For sections of my code that are P/Invoke heavy, I’ve found it useful to have a SizeOf() method attached to any struct, as opposed to calling Marshal.SizeOf(Type). Less keystrokes, and I think it improves readability.

Advertisement

6 thoughts on “C# Extension Methods … Portability Aid?

  1. Dean says:

    You know, I always wondered about the point of extension methods. It always just seemed like useless syntactic sugar to me…

    I mean, it obviously *is* just syntactic sugar, but this example shows where it would actually be useful… something I’d never actually seen on any of the MS/C# blogs or documentation. Thanks!!

  2. Michael says:

    What’s the difference between exposing extension methods for portability, versus having static utility functions for portability? You could already selectively open up namespaces before extension methods.

  3. SamerAZ says:

    can someone please proved us “newbies” as to where to start. I could not find the main program so I can see how paint.net works.

    please help…thanks

  4. Matthew W. Jackson says:

    While it’s probably an abuse of operator overloading, the too-clever-for-his-own-good part of me thinks that Uri should have an overloaded “+” operator for appending.

    Actually, that’s not nearly as bad of an abuse as my idea that Uri have an overloaded “/” operator for appending child paths (along with the “/” path separator).

    This would allow the following:

    Uri pdnUri = new Uri(“http://www.getpaint.net”);
    string donatePath = “donate.html”;

    Uri donateUri = pdnUri/donatePath;

    Actually, I originally had this idea for replacing System.IO.Path.Combine (and other calls) with a Path class that handled proper case comparison and path separators based on the current operating system.

  5. Ben Cherry says:

    @Dean –

    I think that this example shows a good separation of concerns. I’ve actually been using these in an ASP.NET app to really pull business details out of UI code. I have a pattern of “filter” methods, which are just extension methods for collection types that remove entries. If I had a collection of Products (IEnumerable), and I wanted to filter to just the ones on selling really well, then I can create an extension method like the following:
    public static IEnumerable WhereSellingWell(this IEnumerable products)
    {
    return products.Where(/*some business logic here*/);
    }

    Then I can use that method in my UI to filter out those products, but not have to worry about the details of how you determine what’s selling well. The alternative would be for Product to have a property IsSellingWell that is computed, but compare:
    foreach(var p in products.Where(p => p.IsSellingWell)) { /* do something */ }
    vs.
    foreach(var p in products.WhereIsSellingWell()) { /* do something */ }

    [yeah, kind of late to the party, but I thought it’s some valuable advice anyways]

Comments are closed.