Fun with C# 4.0’s dynamic

(c) Bertrand Le Roy 2003There’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:

  1. Providing syntactic sugar for getting to dynamic members that looks like accessing statically-defined ones.
  2. 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:PropertyGrid displaying a dynamic object.

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

19 Comments

  • @Alex: which type?

  • I think that versioning will be one of the biggest rewards since it will make working with multiple versions (plug-ins) very easy.

    ExpandoObject is one such application:
    http://blog.codinglight.com/2009/05/introducing-systemdynamicexpandoobject.html

  • If it proceeds like this C# will become JScript.NET ...
    Why not just making JScript.NET a "first class citizen"?
    If we would have comprehensive Visual Studio support for JScript.NET , same as C# has, world would be a different pleace ...
    But then, the question will be: Why C# ? ;)

    Regards: DBJ

  • @DBJ: you're preaching to the choir. I would really really like to see a fast, modern JavaScript implementation on .NET.
    But C# is still a very different beast, and your question sounds a little to me like "why is there more than one programming language?" Curly braces are about the only thing JavaScript and C# have in common.

  • @DBJ: as I said, I'd love to see a good JS for .NET too. But I totally disagree with what you're saying about C#. Which is fine, I'll continue using it and you can go your own merry way while still targeting .NET.


  • But ... you/we already have "a good JS for .NET".
    It is called JScript.NET ... this is all I am saying.
    Why don't we use it ?

    DBJ

  • @DBJ: JScript.NET is not JavaScript and I would not call it a "good JS for .NET". It's slow, it has almost zero support in VS, it has issues with global scope and dynamic evaluation of code.

  • Not a problem. Let's start with providing full and real VS2010 support, for JScript.NET.
    After that let's pull it through several major iterations, just like C#.
    This would be a real robust start, me thinks.
    If there were resources for F#, there surely will be for something as commercially important as "javascript server side"?
    It will also go head-to-head with the likes of AIR and Rhino. Etc ...

    Let's just do somethign with it. Or dump-it. Why this strange JScript.NET limbo?

    --DBJ

  • @DBJ: While I would love to see that happen, I'm in no position to make it actually happen. All I can do is push for it (which I did) but then the different teams make their own decisions.
    BTW, you keep saying "C# is bloated beyond repair". What feature(s) would you remove?

  • Let us not forget how the whole .NET/C# started. From the need to produce an answer to Java+JVM. Remember J# ;o) How much the technical excellence, was the part of original requirement? It all started in a rush, unfortunatelly. Then "features" pile-up stage, which is still going on. Together with more "features" which have to be added when older "features" render themselves not so well thought through. Exsmple: generics. I will say it here, why not: C# generics are the most hilarious implementation of Ada templates, ever concieved...

    I am "damaged beyond repair", with C++, I am affraid. Which is also too complex by now ;o) And ECMAScript of course.

    Whenever I had to do something in C#, I feel like swiming with one hand, when compared to the two others above mentioned.


    PS: D looks very promising. For "us" old C++ hands.

  • @DBJ: I do remember J# and J++, which had some fantastic features that Java didn't have, thanks to Anders (delegates to name one). What evidence do you have that it "all started in a rush"? The first demos were given in 2000, so the work must have begun before that, and the 1.0 version was released in 2002. The Java lawsuit having begun in November 1997, they seem to have had ample time to design the platform and language.
    Generics is the last thing I expected you to cite. Their implementation is actually pretty good (no idea what you find "hilarious"), especially when compared to, say, the Java implementation.
    Never could stand C++ myself. The number of different representations for a string, for example, now that's hilarious. Talk about bloated. At least in .NET there's only one sort of string.
    But I guess at this point, this discussion is pretty much a religious one.

  • I found your post as a direct result of needing to show dynamic properties in a property grid. Unfortunately your code sample won't compile, I think due to changes in the RTM version of C#4.0. Any chance you might know how to make it work? Here are the 2 errors (both in DynamicHelper.cs):
    1. 'Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo' does not contain a constructor that takes 2 arguments
    2. The type or namespace name 'CSharpGetMemberBinder' could not be found (are you missing a using directive or an assembly reference?)

  • Do you know if this work with the release version of VS 2010? I downloaded your source and ran into issues with missing types... specifically the CSharpGetMemberBinder type.

  • @andrew: I can't check right now but I think you may need the DLR from CodePlex, which has a few more features than are found in 4.0.

  • Why dynamic properties in PropertyGrid are always read-only?

  • Because the implementation is incomplete: this is just a proof of concept.

  • Try this to solve the error

    public static object GetValue(object dyn, string propName)

    &nbsp; &nbsp; &nbsp; &nbsp;{

    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// Warning: this is rather expensive, and should be cached in a real app

    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;var GetterSite = CallSite&lt;Func&lt;CallSite, object, object&gt;&gt;.Create(Binder.GetMember(CSharpBinderFlags.None, propName, dyn.GetType(),

    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;new CSharpArgumentInfo[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) }));

    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;//var GetterSite = CallSite&lt;Func&lt;CallSite, object, object&gt;&gt;.Create(

    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// &nbsp; &nbsp; &nbsp; &nbsp;new CSharpGetMemberBinder(propName,

    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;dyn.GetType(),

    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;new CSharpArgumentInfo[] {

    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;new CSharpArgumentInfo(CSharpArgumentInfoFlags.None, null)

    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}));

    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;return GetterSite.Target(GetterSite, dyn);

    &nbsp; &nbsp; &nbsp; &nbsp;}

    &nbsp; &nbsp; &nbsp; &nbsp;public static void SetValue(object dyn, string propName, object val)

    &nbsp; &nbsp; &nbsp; &nbsp;{

    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// Warning: this is rather expensive, and should be cached in a real app

    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;var SetterSite = CallSite&lt;Func&lt;CallSite, object, object, object&gt;&gt;.Create(Binder.SetMember(CSharpBinderFlags.None, propName, dyn.GetType(),

    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;new CSharpArgumentInfo[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),

    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.Constant | CSharpArgumentInfoFlags.UseCompileTimeType, null) }));

    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;//CallSite&lt;Func&lt;CallSite, object, object, object&gt;&gt;.Create(

    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// &nbsp; &nbsp;new CSharpSetMemberBinder(propName,

    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// &nbsp; &nbsp; &nbsp; &nbsp;dyn.GetType(),

    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// &nbsp; &nbsp; &nbsp; &nbsp;new CSharpArgumentInfo[] {

    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// &nbsp; &nbsp; &nbsp; &nbsp;new CSharpArgumentInfo(CSharpArgumentInfoFlags.None, null),

    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// &nbsp; &nbsp; &nbsp; &nbsp;new CSharpArgumentInfo(CSharpArgumentInfoFlags.LiteralConstant |

    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; CSharpArgumentInfoFlags.UseCompileTimeType, null)

    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// &nbsp; &nbsp;}));

    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;SetterSite.Target(SetterSite, dyn, val);

    &nbsp; &nbsp; &nbsp; &nbsp;}

    &nbsp; &nbsp;}

  • how can we make the properties editable not read-only??

  • #csharper: this is just a proof of concept. You'd need a more complete implementation of the interfaces in play here.

Comments have been disabled for this content.