Clay: malleable C# dynamic objects – part 2
In the first part of this post, I explained what requirements we have for the view models in Orchard and why we think dynamic is a good fit for such an object model.
This time, we’re going to look at Louis’ Clay library and how you can use it to create object graphs and consume them.
But before we do that, I want to address a couple of questions.
1. If we use dynamic, aren’t we losing IntelliSense and compile-time checking and all the good things that come with statically-typed languages? And is C# becoming overloaded with concepts, and trying to be good at everything but becoming good at nothing?
Hush, hush, everything is going to be all right. Relax.
Now think of all the XML/DOM styles of APIs that you know in .NET (or Java for that matter). Except if they are doing code generation, you already don’t get IntelliSense and compile-time checking. Well, you do get them on the meta-model, which you care very little about, but not on the actual data, which you do care a lot about. So it’s ok, we can afford to lose what we didn’t have in the first place. And there’s always testing, right?
As for the evolution of C#, come on. You should be happy it’s still alive and innovating. Take a deep dive into what expression trees really mean for example. It’s a beautiful thing.
2. What’s wrong with ExpandoObject?
Nothing but we can do better. ExpandoObject is actually implemented in a surprising way that makes it very efficient. Hint: no dictionary in there. Other hint: it’s a beautiful thing.
But in terms of API usability it’s not very daring and in particular it does not do much to help you build deep dynamic object graphs. Its behavior is also fixed and can’t be extended.
Clay on the other hand is highly extensible and focuses on creation and consumption of deep graphs.
Let’s get started. The first thing you can do with Clay is create a simple object and set properties on it. Before we do that, we’ll instantiate a factory that will give us some nice syntactic semantic sugar. I wish we could skip this step and use some kind of static API instead but we can’t. Well, small price to pay as you’ll see:
dynamic New = new ClayFactory();
Now this “New” object will help us create new Clay objects, as the name implies (although this name is just a convention).
Here’s something easy and not too surprising:
var person = New.Person(); person.FirstName = "Louis";
person.LastName = "Dejardin";
Nothing you couldn’t do with ExpandoObject but where this gets interesting is that there is more than one way to skin that cat, and that is going to open up a lot of possibilities.
For instance in Clay, indexer syntax and property accessors are equivalent, just as they are in JavaScript. This is very useful when you are writing code that accesses a property by name without knowing that name at compile-time:
var person = New.Person();
person["FirstName"] = "Louis";
person["LastName"] = "Dejardin";
But that’s not all. You can also use properties as chainable setters, jQuery-style:
var person = New.Person() .FirstName("Louis") .LastName("Dejardin");
Or you can pass an anonymous object in if that’s your fancy:
var person = New.Person(new { FirstName = "Louis", LastName = "Dejardin" });
Even better, Clay also understands named arguments, which enables us to write this:
var person = New.Person( FirstName: "Louis", LastName: "Dejardin" );
In summary, there is a lot of ways you can set properties and initialize Clay objects.
As you’d expect, accessing properties can also be done in a number of ways and all of the following are equivalent:
person.FirstName
person["FirstName"]
person.FirstName()
You can also create JavaScript-style arrays:
var people = New.Array( New.Person().FirstName("Louis").LastName("Dejardin"), New.Person().FirstName("Bertrand").LastName("Le Roy") );
That array is also a full Clay object, meaning that you can add properties to it on the fly.
Then you can do this in order to count the items in the array and to access the FirstName property of the first item in the array:
people.Count people[0].FirstName
It’s even easier when you want to create an array property on an existing Clay object:
person.Aliases("bleroy", "BoudinFatal");
If more than one argument gets passed in, Clay is assuming that you’re initializing the property as an array. And if you only have zero or one arguments, just explicitly pass in an array (CLR or Clay):
person.Aliases(new[] {"Lou"});
Contrary to CLR arrays, Clay arrays can grow dynamically:
person.Aliases.Add("loudej");
And they also answer to a number of methods such as AddRange, Insert, Remove, RemoveAt, Contains, IndexOf, or CopyTo.
Getting all this together, we can create a reasonably complex object graph with a fairly expressive and terse syntax:
var directory = New.Array( New.Person( FirstName: "Louis", LastName: "Dejardin", Aliases: new[] { "Lou" } ), New.Person( FirstName: "Bertrand", LastName: "Le Roy" ).Aliases("bleroy", "boudin"), New.Person( FirstName: "Renaud", LastName: "Paquay" ).Aliases("Your Scruminess", "Chef") ).Name("Some Orchard folks");
There’s one last thing I’d like to show that I found really neat and surprising the first time Louis showed it to me.
Imagine that you have a CLR interface that you need to implement, for example:
public interface IPerson { string FirstName { get; set; } string LastName { get; set; } }
but you would like to do it using a Clay object such as one of the persons defined above. Well, you can do this:
IPerson lou = people[0]; var fullName = lou.FirstName + " " + lou.LastName;
What’s extraordinary here is that lou is a perfectly valid statically typed CLR variable. You’ll get full IntelliSense and compile-time checks on that code. It’s just an object that implements IPerson although we never wrote a concrete type implementing that interface.
What makes the magic possible is that Clay is overriding the cast operator and creating a dynamic proxy for the interface (using Castle) that delegates the members to the Clay object.
So there is an actual CLR type but it’s being code-generated on the fly.
That is what enables you to write the following:
foreach(var person in directory) { Trace.Write(person.FirstName); }
What’s happening here is that the “directory” Clay array is being cast into an IEnumerable, for which all the right methods are implemented by the dynamic Clay array object.
We are going to dig deeper into Clay in future posts as there is so much more to do and hack but that should give a satisfactory introduction of the basic intents for the library. I certainly hope you like it and find some crazy ideas about how to use it.
You can find Clay here: http://clay.codeplex.com/
The first part of this post is here:
http://weblogs.asp.net/bleroy/archive/2010/08/16/clay-malleable-c-dynamic-objects-part-1-why-we-need-it.aspx