Private Extension Methods

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.

Print | posted @ Monday, October 05, 2009 9:10 PM

Comments on this entry:

Gravatar # re: Private Extension Methods
by James Curran at 10/6/2009 7:01 AM

Clean and awesome.
  
Gravatar # re: Private Extension Methods
by Tom at 10/6/2009 8:28 AM

I've done a similar thing before, but I made them local function variables instead of extension methods, because I didn't want to clutter the class up with extra members.
  
Gravatar # re: Private Extension Methods
by Søren Skovsbøll at 10/6/2009 9:10 AM

How about using 'let'?

var scriptTags = from name in scriptNames
let resourceUrl = _manager.GetWebResourceUrl(typeof (ScriptHelper), name)
let scriptName = String.Format("... src='{0}' ...", resourceUrl)
select scriptName;
return string.Join("", scriptTags.ToArray());
  
Gravatar # re: Private Extension Methods
by scott at 10/6/2009 12:24 PM

@Tom: Good idea!

@Søren: I like that too!
  
Gravatar # re: Private Extension Methods
by Brian Genisio at 10/7/2009 9:12 AM

Yeah, I have used private extension methods like that before and it certainly makes the code more readable. But, what I wish I had were private extension methods within any (non-static) class.

For instance, I might have a private instance method like: if(IsSameAsPrevious(point)) that I would prefer to read more like this: if(point.IsSameAsPrevious()).

That particular extension method on point only makes sense within my current class, and is useless outside. Unfortunately, we can't write private extension methods that way :(
  
Gravatar # re: Private Extension Methods
by James Curran at 10/9/2009 11:48 AM

Hmmm... That's odd... I don't remember visiting here on Tuesday, nor do I remember seeing the article before today, nor, while it is a very cool idea, do I think I'd use the phrase "clean and awesome" to describe it.

So, ya'know you've got two "James Curran"s following your blog???



  
Gravatar # re: Private Extension Methods
by Scott Allen at 10/12/2009 12:08 AM

@James: I'd think that was odd, but I've run into like 5 Scott Allens on the Internet now. :)
  

Your comment:

Title:
Name:
Email:
Website:
 
Italic Underline Blockquote Hyperlink
 
 
Please add 2 and 5 and type the answer here: