C# "dynamic"

PDC 2008 arrives today, and that means that I am finally able to talk publicly about some of the things I've been working on for the next version of C#. What a relief! I am excited for the world to see some of this stuff, and to start receiving more customer feedback about it.

This afternoon, Anders Hejlsberg presented a talk called "The Future of C#" that I confess I have not seen a dry-run of. I'm sure it went fabulously, though. In that talk, he introduced the "dynamic" feature in C# 4. The short story is that we plan to allow users to perform operations, the details of which are not known until runtime, using the dispatch mechanism supported by the DLR (Jim Hugunin is giving another talk today called "Deep Dive: Dynamic Languages in Microsoft .NET").

Charlie Calvert and Mads Torgersen let slip a few months ago that this was coming, and at the time we got a lot of feedback about the particulars of that proposal (which you might like to go back and look at, but keep in mind that it is out-dated). In fact, we've been through a number of designs, and we've implemented a prototype and a few false starts before we settled on what we believe is the best design: dynamic is a type.

The type "dynamic"

So here's an example of what you might see in C# 4, the result of which is a runtime/late-bound dispatch to the method "Foo" on some local variable:

 dynamic d = GetADynamicThing();
d.Foo();

As you can see, the local variable "d" is of type dynamic, which is a proper type supported by the compiler, and which you can mention in most places that you can mention a type. This code compiles, and it would have compiled no matter what method name you had used. In that sense, "dynamic" is a very special type. It appears to support almost any method name, any property or field, in fact, most anything that you could do with some variable, you can do with a dynamic variable.

The difference of course is that instead of emitting an IL call to something called Foo, which the compiler clearly cannot do since there is no type involved that defines anything called Foo, the compiler emits a "dynamic call site" that manages that operation at runtime, using the C# Runtime binder in conjunction with the DLR, both of which are library components. More details on this later.

This allows you to use the C# language, with the brevity and syntax you've become accustomed to, to invoke methods on python or ruby objects, or on any COM IDispatch object, even without an interop assembly, or on any other object that "knows how to dispatch itself" (you could imagine, say, a DOM that uses this to expose its content as properties). And you can do this on regular old .NET objects too.

And since dynamic is a type, you can use it in more that just locals. For example, this compiles:

 class C
{
    public dynamic myField;
    public dynamic MyProp { get; set; }
    public dynamic MyMethod(dynamic d)
    {
        return d.Foo();
    }
    public delegate dynamic MyDelegate(dynamic d);
}

If you were to get the value of MyProp from some instance of C, and then do anything to it (maybe you could call the method Foo?), then that would be a dynamic dispatch.

A little behind-the-scenes

There is a lot going on here that I am omitting, the details of which I plan to bore/fascinate you with over the course of the next few weeks. Let me at least show you what one of these things looks like if we crack open the assembly. Assume the following class definition:

 class C
{
    public dynamic MyMethod(dynamic d)
    {
        return d.Foo();
    }
}

That's one method that includes some dynamic params as well as a single "dynamic call site" to call the method Foo. Reflector tells you that the assembly actually looks something like this (simplified a bit):

 class C
{
    [return: Dynamic]
    public object MyMethod([Dynamic] object d)
    {
        if (MyMethodo__SiteContainer0.p__Site1 == null)
        {
            MyMethodo__SiteContainer0.p__Site1 =
              CallSite<Func<CallSite, object, object>>
              .Create(new CSharpCallPayload(
                CSharpCallFlags.None, "Foo", typeof(object), null,
                new CSharpArgumentInfo[] { 
                  new CSharpArgumentInfo(CSharpArgumentInfoFlags.None,
                null) }));
        }
        return MyMethodo__SiteContainer0.p__Site1
          .Target(MyMethodo__SiteContainer0.p__Site1, d);
    }

    [CompilerGenerated]
    private static class MyMethodo__SiteContainer0
    {
        public static CallSite<Func<CallSite, object, object>> p__Site1;
    }
}

As you can see, the publicly visible members that use the type dynamic actually, behind the scenes, use the type object when they are emitted. There is no framework type "dynamic." However, those "objects" are all decorated in such a way that the compiler (or anyone else) can tell that they are meant to be handled dynamically.

There is also a static field to hold one of the DLR's dynamic call sites for each dynamic operation that you perform. In this case, there is just one, and it corresponds to the method call to Foo. It is initialized inline with the code that uses it in a lazy manner, and invoked with the "Target" field which is actually a delegate that does the right work.

I admit that code looks scary! But the important thing is that it's something that you don't have to write. The compiler does it all for you. Again, lots of detail is being left out here--for instance, what really happens at runtime, the perf characteristics, and lots of juicy language considerations--but we'll get to all that later.

A scenario we made better by all this

I want to end this post by responding to something that Scott Hanselman posted a little while ago, about the pain of programming against COM in C#. Go look at this post:

https://www.hanselman.com/blog/BackToBasicsVarDim.aspx

...and consider the 53-line horror of what you need to write to talk to Word via its COM object model. In C# anyway. Especially the last half, where there are a few calls to "InvokeMember." Well, one of the benefits we get from the dynamic feature is that all this InvokeMember nonsense is no longer required. Check out the same 53 lines in C# 4 (now considerably smaller):

 ApplicationClass WordApp = new ApplicationClass();
WordApp.Visible = true;
string fileName = Path.Combine(
  AppDomain.CurrentDomain.BaseDirectory, @"..\..\..\NewTest.doc");
Document aDoc = WordApp.Documents.Open(
  fileName, ReadOnly: false, Visible: true);

aDoc.Activate();

string propertyValue = 
  aDoc.CustomDocumentProperties["CustomProperty1"].Value;
aDoc.CustomDocumentProperties["CustomProperty1"] = "Hanselman";

foreach (Range r in aDoc.StoryRanges)
{
    r.Fields.Update();
}

If you look closely, you'll notice that we don't even mention the dynamic keyword in this code snippet. It comes in anyway, though, given that we import COM interop interfaces specially so that you automatically get dynamic calls where appropriate.

If you look closely again, you'll see that there's more here than just dynamic. How about that syntax on the Open() call? More on that later.

Like I said above several times, there's a lot about this that I've left unsaid, and in fact there are a few bits that are even yet-undesigned. I'll follow up with more information soon. If you have feedback or questions, please let me know! And happy PDCing if you're in LA this week.

PS, I feel obliged to tell you that this post describes a product that has not shipped and is not yet complete. I cannot make any claim to know exactly what C# 4 will look like when it does ship. The code here corresponds roughly to what you can do with the PDC 2008 release of Visual Studio 2010 CTP. This posting is provided "AS IS" with no warranties, and confers no rights.