OdeToCode IC Logo

Private Extension Methods

Monday, October 5, 2009

I spent a few hours on a failed experiment recently, but one concept that outlived the spike was how to make use of private extension methods. By private I mean the extension methods have a private access modifier and are only visible inside of a single class.

Sounds pointless, right?

Consider the code in the following class that is attempting to build multiple script tags out of the names of JavaScript files embedded as resources in an assembly.

public static class ScriptHelper
{
    public static string AsyncUploadScripts(this HtmlHelper helper)
    {
        var scriptNames = new string[] 
        {
            "Baz.Biz.js",
            "Bar.Foo.js",
            "Foo.Bar.js"
        };

        return scriptNames
            .Select(name =>
                _manager.GetWebResourceUrl(typeof(ScriptHelper), name))
            .Select(url => String.Format("... src='{0}' ...", url))
            .Aggregate(new StringBuilder(),
                       (sb, tag) => sb.Append(tag),
                        sb => sb.ToString());                          
    }

    // ...
}

That LINQ query is not pretty. Something with ON ERROR GOTO statements would be easier on the eyes.

I thought it would be nice if I could have descriptive names for the operations being performed. But, I didn’t want to create new extension methods for use outside the query.

Fortunately, extension methods as static private members work well inside the same class.

public static class ScriptHelper
{
    public static string AsyncUploadScripts(this HtmlHelper helper)
    {            
        var scriptTags = GetScriptResourceNames()
                            .MapNamesToResourceUrls()
                            .MapUrlsToScriptTags()
                            .CombineScriptTags();
        return scriptTags;                                              
    }

    static string CombineScriptTags(this IEnumerable<string> tags)
    {
        return tags.Aggregate(new StringBuilder(),
                    (sb, tag) => sb.Append(tag),
                    sb => sb.ToString());  
    }

    static IEnumerable<string> MapUrlsToScriptTags(
                                    this IEnumerable<string> urls)
    {
        return urls.Select(url =>
            String.Format("... src='{0}' ...", url));
    }

    static IEnumerable<string> MapNamesToResourceUrls(
                                    this IEnumerable<string> names)
    {
        return names.Select(name =>
                _manager.GetWebResourceUrl(typeof(ScriptHelper), name));
    }

    static IEnumerable<string> GetScriptResourceNames()
    {
        return new string[]
        {               
            "Baz.Bif.js",
            "Foo.Bar.js",
            "Zak.Wak.js",
        };            
    }

    // ...

The code is now in the junk pile, but the idea will stick with me forever … or at least until I switch to something else.