Strings dressed in nested tags

If you read the previous blog post, you might wonder if you can wrap a string in nested tags, you know, something like this:


Func<string, string> nested =
s => s.Tag("td").Colspan("2")
.Width("100")
.Tag("tr")
.Tag("table").Cellpadding("10")
.Border("1");

view raw

NestedFunc.cs

hosted with ❤ by GitHub

And the answer is no. No, you can’t. Well you can, but it’s not going to give you the result you want. For instance, if you apply the transform to the string “Hello”, you’ll get this:

Bad-nesting-round

Which is useless.

The reason is obviously that the Tag method calls following the first one will all be channeled in to the same Tag. Even though there’s an implicit cast to string, there’s nothing in the code triggering that cast. Of course, you could explicitly call ToString on the Tag, like so:


Func<string, string> nested =
s => s.Tag("td").Colspan("2")
.Width("100")
.ToString()
.Tag("tr").ToString()
.Tag("table").Cellpadding("10")
.Border("1");

But that’s admitting defeat, since it breaks the illusion we’re trying to create. Plus it’s ugly.

A better way of working around the problem is to compose simple one-tag transforms, like so:


Func<string, string> cell =
s => s.Tag("td").Colspan("2")
.Width("100");
Func<string, string> row =
s => s.Tag("tr");
Func<string, string> table =
s => s.Tag("table").Cellpadding("10")
.Border("1");
Func<string, string> nested =
s => table(row(cell(s)));

Which is kind of neat and yields the desired result:

Good-nesting-round

But we can attack the problem more directly. There’s not a whole lot we can do to prevent our Tag object from capturing the subsequent method calls to Tag. But we are free to respond to those method calls in any ol’ way we like. A trivial change to TryInvokeMember will do just nicely:


public override bool TryInvokeMember(
InvokeMemberBinder binder,
object[] args,
out object result)
{
string arg = GetValue(args);
string methodName = binder.Name;
if (methodName == "Tag" && arg != null)
{
result = ToString().Tag(arg);
}
else
{
_props[methodName] = arg ?? string.Empty;
result = this;
}
return true;
}

view raw

NestedTags.cs

hosted with ❤ by GitHub

So we just single out calls for a method named Tag with a single string parameter. For those method calls, we’re not going to do the regular fluent collection of method names and parameters thing. Instead, we’ll convert the existing Tag to a string, and return a brand new Tag to wrap that string. And now we can go a-nesting tags as much as we’d like, and still get the result we wanted. Win!


Strings dressed in tags

In a project I’m working on, we needed a simple way of wrapping strings in tags in a custom grid in our ASP.NET MVC application. The strings should only be wrapped given certain conditions. We really wanted to avoid double if checks, you know, once for the opening tag and one for the closing tag?

We ended up using a Func from string to string to perform wrapping as appropriate. By default, the Func would just be the identify function; that is, it would return the string unchanged. When the right conditions were fulfilled, though, we’d replace it with a Func that would create a new string, where the original one was wrapped in the appropriate tag.

The code I came up with lets you write transforms such as this:


Func<string, string> transform =
s => s.Tag("a")
.Href("http://einarwh.posterous.com")
.Style("font-weight: bold");

Which is pretty elegant and compact, don’t you think? Though perhaps a bit unusual. In particular, you might be wondering about the following:

  1. How come there’s a Tag method on the string?
  2. Where do the other methods come from?
  3. How come the return value is a string?

So #1 is easy, right? It has to be an extension method. As you well know, an extension method is just an illusion created by the C# compiler. But it’s a neat illusion that allows for succinct syntax. The extension method looks like this:


public static class StringExtensions
{
public static dynamic Tag(this string content, string name)
{
return new Tag(name, content);
}
}

So it simply creates an instance of the Tag class, passing in the string to be wrapped and the name of the tag. That’s all. So that explains #2 as well, right? Href and Style must be methods defined on the Tag class? Well no. That would be tedious work, since we’d need methods for all possible HTML tag attributes. I’m not doing that.

If you look closely at the signature of the Tag method, you’ll see that it returns an instance of type dynamic. Now what does that mean, exactly? When dynamic was introduced in C# 4, prominent bloggers were all “oooh it’s statically typed as dynamic, my mind is blown, yada yada yada”, you know, posing as if they didn’t have giant brains grokking this stuff pretty easily? It’s not that hard. As usual, the compiler is sugaring the truth for us. Our trusty ol’ friend ILSpy is kind enough to let us figure out what dynamic really means, by revealing all the gunk the compiler spews out in response to it. You’ll find that it introduces a CallSite at the point in code when you’re interacting with the dynamic type, as well as a CallSiteBinder to handle the run-time binding of operations on the CallSite.

We don’t have to deal with all of that, though. Long story short, Tag inherits from DynamicObject, a built-in building block for creating types with potensially interesting dynamic behaviour. DynamicObject exposes several virtual methods that are called during run-time method dispatch. So basically when the run-time is trying to figure out which method to invoke and to invoke it, you’ve got these nice hooks where you can insert your own stuff. Tag, for instance, implements its own version of TryInvokeMember, which is invoked by the run-time to, uh, you know, try to invoke a member? It takes the following arguments:

  • An instance of InvokeMemberBinder (a subtype of CallSiteBinder) which provides run-time binding information.
  • An array of objects containing any arguments passed to the method.
  • An out parameter which should be assigned the return value for the method.

Here is Tag‘s implementation of TryInvokeMember:


public override bool TryInvokeMember(
InvokeMemberBinder binder,
object[] args,
out object result)
{
_props[binder.Name] = GetValue(args) ?? string.Empty;
result = this;
return true;
}
private string GetValue(object[] args)
{
if (args.Length > 0)
{
var arg = args[0] as string;
if (arg != null)
{
return arg;
}
}
return null;
}

What does it do? Well, not a whole lot, really. Essentially it just hamsters values from the method call (the method name and its first argument) in a dictionary. So for instance, when trying to call the Href method in the example above, it’s going to store the value “http://einarwh.posterous.com&#8221; for the key “href”. Simple enough. And what about the return value from the Href method call? We’ll just return the Tag instance itself. That way, we get a nice fluent composition of method calls, all of which end up in the Tag‘s internal dictionary. Finally we return true from TryInvokeMember to indicate that the method call succeeded.

Of course, you’re not going to get any IntelliSense to help you get the attributes for your HTML tags right. If you misspell Href, that’s your problem. There’s no checking of anything, this is all just a trick for getting a compact syntax.

Finally, Tag defines an implicit cast to string, which explains #3. The implicit cast just invokes the ToString method on the Tag instance.


public static implicit operator string(Tag tag)
{
return tag.ToString();
}
public override string ToString()
{
var sb = new StringBuilder();
sb.Append("<").Append(_name);
foreach (var p in _props)
{
sb.Append(" ")
.Append(p.Key.ToLower())
.Append("=\"")
.Append(p.Value)
.Append("\"");
}
sb.Append(">")
.Append(_content)
.Append("</")
.Append(_name)
.Append(">");
return sb.ToString();
}

The ToString method is responsible for actually wrapping the original string in opening and closing tags, as well as injecting any hamstered dictionary entries into the opening tag as attributes.

And that’s it, really. That’s all there is. Here’s the complete code:


namespace DynamicTag
{
class Program
{
static void Main()
{
string s = "blog"
.Tag("a")
.Href("http://einarwh.posterous.com")
.Style("font-weight: bold");
Console.WriteLine(s);
Console.ReadLine();
}
}
public class Tag : DynamicObject
{
private readonly string _name;
private readonly string _content;
private readonly IDictionary<string, string> _props =
new Dictionary<string, string>();
public Tag(string name, string content)
{
_name = name;
_content = content;
}
public override bool TryInvokeMember(
InvokeMemberBinder binder,
object[] args,
out object result)
{
_props[binder.Name] = GetValue(args) ?? string.Empty;
result = this;
return true;
}
private string GetValue(object[] args)
{
if (args.Length > 0)
{
var arg = args[0] as string;
if (arg != null)
{
return arg;
}
}
return null;
}
public override string ToString()
{
var sb = new StringBuilder();
sb.Append("<").Append(_name);
foreach (var p in _props)
{
sb.Append(" ")
.Append(p.Key.ToLower())
.Append("=\"")
.Append(p.Value)
.Append("\"");
}
sb.Append(">")
.Append(_content)
.Append("</")
.Append(_name)
.Append(">");
return sb.ToString();
}
public static implicit operator string(Tag tag)
{
return tag.ToString();
}
}
public static class StringExtensions
{
public static dynamic Tag(this string content, string name)
{
return new Tag(name, content);
}
}
}

view raw

DynamicTag.cs

hosted with ❤ by GitHub