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.
Comments
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());
@Søren: I like that too!
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 :(
So, ya'know you've got two "James Curran"s following your blog???
Its very nice article.
Thanks,
Thanigainathan.S
Except for the .Aggregate call, the original looks prettier to me: I can see in 3 lines exactly what you're doing. In the second example, it doesn't even all fit on my screen at once. "MapUrlsToScriptTags" is only easier to read if you see and use it a lot -- smashing strings together to make a <script> tag is very recognizable.
I might combine the two Select calls, but I might not.
In place of the Aggregate(...), I'd just call .Join(). I don't remember offhand if that's in the stdlib, or if it's in one of my extension classes, but it's so ridiculously common it ought to be in the std.