I know many people get upset at the very idea of Extension Methods, but I already consider them to be an indispensable part of my programming toolkit. I’m seeing so many times where a quick extension method can make code be much more readable. Other times an extension method against even a core BCL type seems to fill in a natural hole in the official API’s and knocks out a lot of duplication.
Example 1: Custom Attributes
I do a lot of work with reflection in both StructureMap and my normal development. Using custom attributes has always been an important extension point in .Net frameworks, but the code to access attributes is awkward – until Extension Methods. I’ll bet you anything that at least a dozen other people reading this have written something exactly like this:
public static T GetAttribute<T>(this ICustomAttributeProvider provider) where T : Attribute
{
var atts = provider.GetCustomAttributes(typeof(T), true);
return atts.Length > 0 ? atts[0] as T : null;
}
public static void ForAttributesOf<T>(this ICustomAttributeProvider provider, Action<T> action) where T : Attribute
{
foreach (T attribute in provider.GetCustomAttributes(typeof(T), true))
{
action(attribute);
}
}
so that you can access attributes like:
var formatAs = method.GetAttribute<FormatAsAttribute>();
and work with the attribute objects without all the icky casting and the goofy GetCustomAttributes business.
Example 2: Writing Xml
Here’s another example for Xml manipulation from what became Fluent NHibernate:
public static XmlElement AddElement(this XmlNode element, string name)
{
XmlElement child = element.OwnerDocument.CreateElement(name);
element.AppendChild(child);
return child;
}
Programmatically building XmlDocument objects has always been tedious because of all the repetitious steps (create element, append element to its parent, set some attributes, rinse, repeat…) and the verbose API. I found that a few extension methods shrank that code down into things like this:
element.AddElement("key").AddElement("column").WithAtt("name", foreignKeyName).WithAtt("not-null", "true");
With the old Xml “push button API” this is about 6-7 lines of code.
Example 3: Reading and Working from Xml
Here’s one last example. When StructureMap loads an Xml configuration file, it will will allow you to “include” additional Xml files by specifying the files in an <Include> tag like this:
<StructureMap Id="Master">
<Include File="Include1.xml" />
<Include File="Include2.xml" />
</StructureMap>
In StructureMap 2.0 the code that acted on the <Instance> nodes looked like this:
Old .Net 2.0-ish Code
string includedPath = null;
try
{
XmlNodeList includeNodes = node.SelectNodes(XmlConstants.INCLUDE_NODE);
foreach (XmlElement includeElement in includeNodes)
{
XmlDocument includedDoc = new XmlDocument();
string fileName = includeElement.GetAttribute("File");
if (fileName == string.Empty)
{
throw new ApplicationException("The File attribute on the Include node is required");
}
try
{
includedPath = Path.Combine(folder, fileName);
includedDoc.Load(includedPath);
ConfigurationParser parser = new ConfigurationParser(includedDoc.DocumentElement);
list.Add(parser);
}
catch (Exception ex)
{
throw new StructureMapException(150, ex, fileName);
}
}
}
catch (Exception ex)
{
throw new StructureMapException(100, includedPath, ex);
}
Earlier this year I did a massive overhaul of the StructureMap core and used some .Net 3.5 language features to do this:
New .Net 3.5 Code
First, I wanted to separate the Xml manipulation code away from the code that locates and loads the additional Xml files. I wrote an extension method against XmlNode (plus a little bit of a Fluent Interface) like this:
public static XmlTextExpression ForTextInChild(this XmlNode node, string xpath)
{
return new XmlTextExpression(node, xpath);
}
public class XmlTextExpression
{
private readonly XmlNodeList _list;
internal XmlTextExpression(XmlNode parent, string attributePath)
{
_list = parent.SelectNodes(attributePath);
}
public void Do(Action<string> action)
{
if (_list == null) return;
foreach (XmlNode node in _list)
{
action(node.InnerText);
}
}
}
That’s a lot more code for Xml manipulation than the original sample, but the great thing is that I can reuse this code up above in other scenarios.
This extension method on XmlNode enabled me to write code that made working with the Xml data become almost declarative. Instead of doing monotonous code to walk the Xml DOM and yank out the right nodes and attributes, I just say “do this thing (an Action<string> lambda) with each value of this attribute in every child node named ‘Include.’”
_structureMapNode.ForTextInChild("Include/@File").Do(fileName =>
{
string includedFile = Path.Combine(includePath, fileName);
action(includedFile);
});
and
_structureMapNode.ForTextInChild("Assembly/@Name").Do(name => builder.AddAssembly(name));
and
pluginElement.ForTextInChild("Setter/@Name").Do(prop => plugin.Setters.MarkSetterAsMandatory(prop));
But Wait!
But wait, extension methods are weird and my team won’t be able to understand it! Get over it, this technique has been used successfully in other languages for years (Ruby, Objective C, even Javascript)
But wait, extension methods aren’t discoverable! I’ll concede this one a little bit. ReSharper has nice functionality to bring up extension methods with the CTRL-ALT-SPACE keyboard shortcut, and CTRL-B still works. I think that this yet another example of how ReSharper (or the equivalent) usage radically changes the rules for programming.