Fun with C# 4.0’s dynamic
There’s been some debate recently on the new “dynamic” keyword in C# 4.0. As has been the case with many features before it, some love it, some hate it, some say it bloats the language, yadda yadda yadda. People said that about lambdas. Me, I’ll just use it where I see a use case, thank you very much.
In the case of dynamic, another frequent comment is that a statically-typed language should not try to look like a dynamic language. Well, I just don’t believe in that distinction.
Being dynamic is a trait that a language can have, and some have it more than others. But as soon as a language has a dictionary type or indexers, and most modern languages do, it starts having dynamicity. What people call a dynamic language is just one where it’s the only available type.
Now there is type safety of course, but to me that’s a different feature that is not as coupled with “statically-typed-ness” as we usually tend to think. Case in point, many modern JavaScript engines analyze the code in order to apply many of the optimization techniques that static languages have been applying at compile-time.
So just how dynamic was C# before 4.0? Well, there are dictionaries and indexers, as I’ve said, but there are also other interesting and less known features such as custom type descriptors. Go look them up if you don’t know about them. Type descriptors are for example used to provide a level of indirection between a design surface and the components being edited.
But type descriptors can also be built completely dynamically and decide to expose a completely virtual object model that is entirely built at runtime. Of course, the client code for that object has to go through the right APIs to make use of that quasi-dynamic model. And of course those APIs are not simple. Implementing a custom type descriptor in itself is not exactly trivial.
If anything, the dynamic keyword is going to provide a friendlier syntax for this sort of thing. One problem is that it has no relationship whatsoever with custom type descriptors.
So I thought I’d try to bridge that gap. How can we take a type that exposes a custom type descriptor, such as DataRowView, and transform that into something that plays nice with the dynamic keyword? What we want to be able to write is something like the following:
var table = new DataTable("People"); table.Columns.Add("FirstName", typeof(string)); table.Columns.Add("LastName", typeof(string)); table.Columns.Add("Children", typeof(int)); table.LoadDataRow(
new object[] {"Bertrand", "Le Roy", 2},
LoadOption.OverwriteChanges); var tableView = new DataView(table);
dynamic dynRecord =
new TypeDescriptorDynamicWrapper(tableView[0]);
Console.WriteLine("{0} {1} has {2} children.", dynRecord.FirstName, dynRecord.LastName, dynRecord.Children);
To do that, I built the TypeDescriptorDynamicWrapper that you see being used above. What is does is pretty simple, as it just inherits from DynamicObject and implements TryGetMember as follows:
public override bool TryGetMember(
GetMemberBinder binder, out object result) {
var name = binder.Name; var property = _properties.Find( p => p.Name.Equals(name, binder.IgnoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal)); if (property == null) { result = null; return false; } result = property.GetValue(
_descriptor.GetPropertyOwner(null)); return true; }
As you can see, the method just finds the right property on the type descriptor and delegates to it if it finds it. I didn’t implement the setting of properties but it would be fairly easy to do so.
Using that simple wrapper, the program above just works and displays:
Bertrand Le Roy has 2 children.
That was easy. Now what about the reverse, getting any dynamic object to expose a custom type descriptor?
Well, that’s where things get tricky.
The dynamic capabilities of C# 4.0 are targeted at two things:
- Providing syntactic sugar for getting to dynamic members that looks like accessing statically-defined ones.
- An easy way to create dynamic types by implementing a form of method_missing.
But there is one thing that exists in DLR but didn’t make it to C# yet, which is the ability to easily reflect on dynamic objects (other than by using the #1 syntactic sugar above).
For example, in JavaScript, a.foo and a[“foo”] are exactly the same thing, which enables easy reflection: you can enumerate the members of an arbitrary object and do stuff like a[someStringVariable].
Doing the same thing with C# on dynamic objects is much more difficult. The equivalent of a.foo is easy, but not the equivalent of a[someStringVariable].
Forget about using reflection, it doesn’t work on dynamic objects because dynamic really is a compiler trick. There is no such thing as a “dynamic” type to reflect on. It’s a keyword that tells the compiler to relax type-checking and generate code to access those dynamic members at runtime. If you try to reflect on an instance of one of the dynamic-friendly type, such as ExpandoObject, you’ll see the statically-defined members of that type, but not the dynamic ones. In a way, you’re looking at the messenger instead of the message.
This is a problem because in order to wrap an arbitrary dynamic object into a custom type descriptor, we need to be able to reflect on the dynamic members of that object.
To solve the problem, and with some help from the nice folks who are building the DLR, I compiled a simple program that sets and gets a property on a dynamic object, an then I looked at the generated code from Reflector (“luckily”, Reflector is not yet aware of dynamic and shows the generated code instead of something closer to the actual code you wrote).
I was then able to refactor that code and replace the string constants for the accessed member with a method parameter, thus creating a generic way to access dynamic object properties:
public static object GetValue(
object dyn, string propName) {
// Warning: this is rather expensive,
// and should be cached in a real app var GetterSite =
CallSite<Func<CallSite, object, object>>
.Create( new CSharpGetMemberBinder(propName, dyn.GetType(), new CSharpArgumentInfo[] { new CSharpArgumentInfo(
CSharpArgumentInfoFlags.None, null) })); return GetterSite.Target(GetterSite, dyn); } public static void SetValue(
object dyn, string propName, object val) {
// Warning: this is rather expensive,
// and should be cached in a real app var SetterSite =
CallSite<Func<CallSite, object, object, object>>
.Create( new CSharpSetMemberBinder(propName, dyn.GetType(), new CSharpArgumentInfo[] { new CSharpArgumentInfo(
CSharpArgumentInfoFlags.None, null), new CSharpArgumentInfo(
CSharpArgumentInfoFlags.LiteralConstant | CSharpArgumentInfoFlags.UseCompileTimeType,
null) })); SetterSite.Target(SetterSite, dyn, val); }
Once we have that, implementing the custom type provider is relatively straightforward. The only part that is not immediately obvious is how to enumerate the properties of the dynamic object.
This is done as follows. Dynamic objects implement IDynamicMetaObjetProvider, which has a GetMetaObject method, which returns a DynamicMetaObject object on which you can call GetDynamicMemberNames to get the list of members.
We can now take any dynamic object and for example present it in a property grid, which is one of those components that know how to handle custom type descriptors but not the new dynamic objects:
dynamic person = new ExpandoObject(); person.FirstName = "Bertrand"; person.LastName = "Le Roy"; person.Children = 2; propertyGrid1.SelectedObject =
new DynamicTypeDescriptorWrapper(person);
And here’s what the results look like:
In conclusion, I’d say that although we are having a healthy debate on the dynamic keyword (and we should have that debate), I’m confident that in one or two years, we’ll have some incredibly imaginative uses of the feature that will simply blow us away and will make the current conversation a little sillier than it seems today. The feature itself is also in its infancy and not only will the use cases emerge way beyond what even its designers envisioned, it will also grow and become more usable with future versions of the language, as is made clear by the gap in functionality that still exists today with the full DLR project.
You can download the code for this article here:
http://weblogs.asp.net/blogs/bleroy/Samples/Bleroy.Sample.Dynamic.zip