Click here to Skip to main content
15,867,568 members
Articles / Programming Languages / C#

Fluent.Xml.Linq - Exploring the limits of C# syntax

Rate me:
Please Sign up or sign in to vote.
5.00/5 (23 votes)
14 Feb 2010CPOL10 min read 36.3K   76   34   8
Exploration of the limits of C# - how much it could be pushed to create Fluent APIs or Internal Domain Specific Languages.

Introduction

This is an exploration of the limits of C# - how much it could be pushed to create Fluent APIs or Internal Domain Specific Languages (DSLs) that have different kinds of flow than the traditional use of the language. On the way we look at indexers, implicit cast, delegates and operator overloading, and use them - sometimes in a bit unexpected ways.

The example that I use is an API for generation of XML / (X)HTML with more Fluent syntax. Let's call it Fluent.Xml.Linq.

I have included some code as screenshots, because styling is so important for the purposes of this article, but you can find all the source in the accompanied ZIP-file.

Motivation

I love C#, I am a huge fan of Anders Hejlsberg, but there is one thing I am envious of Visual Basic developers of: Quite often you need to produce XML or (X)HTML within code, and they've got this great language feature called XML literals. You are probably familiar with that, but here is an example just in case:

Visual Basic example: VBExample.vb

Now, if the software that you are writing does a lot of XML production, you should really consider template technologies - my favorite is StringTemplate - but often, it is just some XML blocks here and there, and it is not really worth the extra DLL references. String concatenation is one option, but let's say we want to stay somewhat strongly typed...

I was thinking to first show here the above VB-example using System.Xml.XmlDocument. It would probably be dozens of lines of code. But then, I thought it is waste of space: most of you - if not all - are already familiar with System.Xml.Linq, so this is what the above example would look using LINQ to XML:

LinQ example: LinqToXmlSample.cs

Well, it is not bad compared to System.Xml, but there are some problems:

  • Readability is not really good: what jumps to your face is all those "new", "XElement" and "XAttribute" terms instead of the actual point: the XML elements and attributes.
  • Tag and attribute names are strings - not strongly typed, so it is easy to make such mistakes as "Type", "types" or "tyoe" instead of "type"

Really, what I would like to write is something like this:

Fluent example

It would be easy to create a DSL like that - using Oslo/MGrammar tools, or my favorite Antlr tools. But everyone who has tried creating DSLs knows that while there are quite good tools for creating the actual parsing of DSLs and using DSLs as stand-alone programs, the deployment, maintenance, and IDE integration are still very hard.

Hopefully, in the future, Microsoft will get their DSL story straight so that it would be easy to mix, e.g., C# and DSLs within Visual Studio. But for now, what I would really like to do is to be able to write this kind of syntax in C#.

Well, this article is an exploration of how you could do that.

Attribute syntax - indexer and implicit cast

The first thing that we should tackle is the easiest part - the attributes. It is easy to create an indexer to get the attrName["attrValue"] syntax. You could just create a class like this:

C#
public class FXAttrDefinition
{
    private XName name;

    public FXAttrDefinition(XName name)
    {
        this.name = name;
    }

    public XAttribute this[object value]
    {
        get
        {
            return new XAttribute(this.name, value);
        }
    }
}

... which would allow you to create the attributes like this:

C#
FXAttrDefinition type = new FXAttrDefinition("type"), value = new FXAttrDefinition("value");
XElement input = new XElement("input", type["text"], value["Default value"]);

but the constructors are really long to write like that - they take too much space. You could easily have dozens of different attributes even for simple HTML. Couldn't we replace them with something nicer?

What we can do is make an implicit cast so that our FXAttrDefinition class can be automatically converted from string the same way as double is converted from integer or XName is automatically converted from a string. You do it like this:

C#
public static implicit operator FXAttrDefinition(string name)
{
    return new FXAttrDefinition(name);
}

public static implicit operator FXAttrDefinition(XName name)
{
    return new FXAttrDefinition(name);
}

I decided to also allow the cast from XName, so that you can use the XNamespace + string => XName construct, which is the way of LINQ to XML to create namespaces. So, now you could use the code like this:

C#
FXAttrDefinition tpe = "type", val = "value";
XElement input = new XElement("input", tpe["text"], val["Default value"]);

Now, that starts to be quite succinct as far as the attributes are concerned.

Next, we should take care of the XElement. You could of course use the exact same structure as with attributes - there is an example of that as the FXElemDefinition2 class in the sample files. However, it is not quite what I am looking for. Just look at the result:

Intermediate fluent result: FluentSample2.cs

Firstly, we are using an object array (marked "params") as the indexer! Yes - to my surprise - it is allowed, and it works, but it is starting to be a bit strange if you think about the intended purpose of indexer as a language feature.

More importantly, to my quest for that perfect XML generation syntax, readability is better in my first example where we have round brackets () around element children and square brackets are reserved for attribute value.

So, how can I get those round brackets?

Element syntax - delegates

Of course, you could just create static methods to your class for each of the elements you are going to use. But then, you would end up having each of the XML generating classes cluttered with dozens of methods like form(), input() etc. Unacceptable!

Or, if there was a separate class, you would need to first instantiate it" var x = new FunctionCollection();. Then, you would use syntax like x.form(). OK, best option so far, but I really want to get rid of that x..

Delegates are an important language feature introduced in C# 2.0. They are basically functions assigned to a variable. These functions can be static, from instantiated objects, or even anonymous. We can use them like this to get our nice function-syntax on elements:

C#
delegate XElement FXElemDefinition(params object[] children);

XElement pDel(params object[] children)
{
    return new XElement("p", children);
}

XElement inputDel(params object[] children)
{
    return new XElement("input", children);
}

private void getElemExample()
{
    // Element definitions
    FXElemDefinition p = pDel, ipt = inputDel; ;

    // Creating the <p><input type="text" /></p> with nice syntax
    XElement elem = p(ipt(new XAttribute("type", "text")));
}

We define a delegate named FXElemDefinition and we define functions for paragraph and input. OK, so now our element syntax is the way I want it, but how can we get rid of that boilerplate code of defining functions?

Let's take a step back and think what we are doing: We need to call a constructor (= function) new XElement() with two parameters: name and children. But, we assign the variable p to be another function that already knows the element name "p", and thus we can call it just with the children parameter. So essentially, we want to reduce the function from having two parameters to only one parameter. This is called currying, and it is a much used mechanism in functional languages. In C#, it is a bit limited. For example, it does not play well together with the "params" keyword, and I had real trouble implementing it first. But fortunately, there is one syntax that we can use in our case:

C#
FXElemDefinition p = x => new XElement("p", x);
FXElemDefinition ipt = x => new XElement("input", x);

But that's really messy, the syntax is not intuitive unless you have worked with lambdas a lot. And again, any real-life XML generation scenario would have a lot of different elements. Can we use implicit cast again to make it shorter?

Unfortunately: not! The delegate class is marked sealed so we cannot touch that (probably for a good reason :-). We cannot do casts or operator overloading unless we are able to extend the class. We can, however, create a helper class like this:

C#
public delegate XElement FXElemDefinition(params object[] children);

public class FXElementHelper
{
    public FXElemDefinition this[XName elementName]
    {
        get
        {
            return remainingChildrenParam => 
              new XElement(elementName, remainingChildrenParam);
        }
    }
}

and then we can call it like this:

C#
// Element definitions
var h = new FXElementHelper();
FXElemDefinition p = h["p"], ipt = h["input"];

// Creating the <p><input type="text" /></p>
XElement elem = p(ipt(new XAttribute("type", "text")));

So now, we have achieved our goal and can create XML/XHTML, with quite nice syntax like this (FluentSample.cs in the source code):

Fluent result: FluentSample.cs

If I knew how, I would get rid of the FXElementHelper and h[""], but I can live with this syntax. It is nice to read - you get the structure of the HTML immediately: wlement and attribute names are first defined, and after that, they are strongly typed. It is quite fast to write, and supports intellisense nicely.

Operator overloading

I actually started writing this article thinking that the DSL that I would demonstrate would use operator overloading. You know, the XML element would be created like an expression:

Operator overloading: Old/Testcase.cs

There is an unfinished - but somewhat working - example of this in the "Old" folder of the source code if you are interested. But I just couldn't get it "flowing fluently" enough using operators. The problem is the hierarchical structure of XML: the Nested Functions approach is much more natural.

But I am sure there are good uses for operator overloading in Internal DSLs that have other purposes than XML. I also think that this method could be used more than it is now to produce more intuitive and naturally flowing syntax. One good example I already mentioned above is the XNamespace, which overloads the plus operator when the second operand is string:

C#
XNamespace ns = "MyNameSpace";
XName qualifiedName = ns + "tagName";
Console.WriteLine(new XElement(qualifiedName));
// produces <tagName xmlns="MyNameSpace" />

But you could get so much further with this: overloading different operators (*, /, -, ==) and so on to build complete expressions. Perhaps, I will figure out a good example and write about it someday.

Background on Fluent APIs / Internal DSLs

The term of Fluent Interfaces (or Internal DSLs) was introduced by Martin Fowler as he, in 2005, described the then new style of interfaces that were often characterized by the use of method chaining. I decided not to cover method chaining here as it is so well known these days. Just put "Fluent" in the CodeProject search box or "Fluent API" in Google, and you will find several examples. I am sure you will recognize the style - even if you didn't recognize the term.

I would say LINQ to XML is already an example of a Fluent interface. Sure, it does not use method chaining using dots, but it uses the most natural way of constructing XML trees: hierarchical function structure (or Nested Functions, as Fowler describes in this article). In that sense, it satisfies Fowler's description of "intent is to do something along the lines of an internal Domain Specific Language" and "the API is primarily designed to be readable and to flow".

What I tried to demonstrate above is ways to take that "fluency" one step further using other language features that are available to us in this great language of C#.

To me, the pros of these kinds of approaches are clear: APIs like this are easier to read and write:

  • Less time
  • Less money
  • Better understanding for someone else than the person who wrote it
  • ... or for yourself, if you need to look at old code years later

The main downside is as Fowler wrote in the original article:

"One of the problems of methods in a fluent interface is that they don't make much sense on their own. Looking at a method browser of a method by method documentation doesn't show much sense to [function named] with. Indeed sitting there on its own, I'd argue that it's a badly named method that doesn't communicate its intent at all well. It's only in the context of the fluent action that it shows its strengths."

Well said. I would only like to add to this a few aspects: Often in Fluent APIs, certainly in the examples above, you are "bending" the original intent and conventions of the language (say, for example, using indexers when there is really no collection :-). For someone who is not familiar with your DSL and the intention of it, this might be confusing.

Another thing is that making a good Fluent API is an effort. It is much faster to create a traditional API. In fact, Fowler suggests - and I agree - it makes sense to first create a traditional API and then create the Fluent API on top of it.

Conclusion

So, there are serious pros and cons in Fluent interfaces. This is still kind of a new approach - experience is limited. Whether you use this stuff or not in your projects is in your discretion - well, like any other pattern I suppose. It's just an idea.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Founder OPE AG
Switzerland Switzerland
Olli is a .Net developer and architect.

He specializes in Asp.net MVC and other web technologies, XML and lately Domain Specific Languages.

Olli is originally from Finland, but currently works for his own one-man-initiative OPE AG (www.ope.ag) from Switzerland. He has over 10 years of experience as one of the founding partners and Chief Technology Officer of Quartal group of companies (www.quartal.com).

Comments and Discussions

 
GeneralAlternative workaround Pin
Jaime Olivares31-May-10 10:18
Jaime Olivares31-May-10 10:18 
GeneralRe: Alternative workaround Pin
OPerttilä1-Jun-10 1:07
OPerttilä1-Jun-10 1:07 
GeneralBravo! Pin
Marcelo Ricardo de Oliveira17-Feb-10 7:57
mvaMarcelo Ricardo de Oliveira17-Feb-10 7:57 
GeneralReally nice idea Pin
Daniel Vaughan16-Feb-10 6:03
Daniel Vaughan16-Feb-10 6:03 
GeneralAllow me to be the first to rate it 5 Pin
Som Shekhar14-Feb-10 2:08
Som Shekhar14-Feb-10 2:08 
GeneralAnd me to be the third ;-) Pin
Uwe Keim14-Feb-10 2:56
sitebuilderUwe Keim14-Feb-10 2:56 
GeneralRe: Allow me to be the first to rate it 5 Pin
OPerttilä14-Feb-10 3:24
OPerttilä14-Feb-10 3:24 
GeneralRe: Allow me to be the first to rate it 5 Pin
Sacha Barber16-Feb-10 2:38
Sacha Barber16-Feb-10 2:38 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.