다음을 통해 공유


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.

Comments

  • Anonymous
    October 28, 2008
    Well, I intended to spend the last three weeks blogging about C# design process in anticipation of our
  • Anonymous
    October 28, 2008
    Yesterday, I made an attempt to introduce the C# dynamic feature by describing what change there has
  • Anonymous
    October 28, 2008
    The comment has been removed
  • Anonymous
    October 28, 2008
    Curious about some implications.Can you nest dynamics as in dynamic x; x.Prop.Func()?Can you make a List<dynamic>?Thanks!
  • Anonymous
    October 29, 2008
    Here are a few good resources that you ought to look at for information about dynamic from PDC: Anders
  • Anonymous
    October 29, 2008
    The other day I was playing around with some office code, and I found myself writing a lot of code much
  • Anonymous
    October 30, 2008
    VB with a syntax of C. Or C# without option strict.I really can't make up my mind.
  • Anonymous
    October 30, 2008
    Steve,Yes and yes. The result of dynamic operations are typed as dynamic, so you can layer them (except conversions, which are typed appropriately). And you can build generic types with dynamic too. I'll talk more about this later.Tanveer,You're right that this is something that VB has done, in a way, for a while. At least one difference is that you get to control it at a finer granularity than "option strict off," which affects your whole compilation unit as I understand it. Another difference is that we target the DLR, not the VB runtime.chris
  • Anonymous
    October 30, 2008
    Seems to me C# is really heading in the wrong direction. Up to 2.0 and some of 3.0 everything was amazing and perfect. Everything was designed perfectly, to where it constrained developers to also design accordingly and to follow rules. This in turn made errors much easier to find especially at compile time (Generics). Now, it seems as if we're just throwing all this out the window and trying to just add every little convenient feature anybody can think up. It's making the framework very messy and it's going to result in less compile time errors caught and more run time errors...
  • Anonymous
    October 31, 2008
    I am trying really hard not to pre-judge, but I have to say that so far I haven't seen anything that makes me think that the "dynamic" type is a good idea. I understand that dynamic languagues are becoming more popular, but that doesn't mean that every language in the world needs to support dynamic typing. Different languages exist for different reasons and cater to different needs. It is not possible for one language to do everything, no matter how much the C# team might want to believe that. There are already projects underway to bring existing dynamic languages into the .NET world, and for that matter VB.NET is already capable of late binding when it is necessary for things like COM compatability. Did the C# team somehow feel the need to compete with these languages? Did they forget the core principles which have made C# so successful? It would have been a far better choice to make the language MORE strict, using the great features being developed in Spec#, rather than trying to build in this dynamic capability which adds little value and carries with it the very real possibility of seriously diluting the language.C# is based on static typing for a reason; tossing that out the window for the sake of the latest fad is irresponsbile at best.
  • Anonymous
    October 31, 2008
    Did the C# team somehow feel the need to compete with these languages?I think you're missing the point. C# is not trying to compete with these languages, it's trying to interoperate with them. With C# 4.0, I can write a class library in Python or Ruby, and then use it from C#, whereas before, it could only be the other way around. And this is cool.
  • Anonymous
    November 03, 2008
    Why anybody in right mind would write class library in Python or Ruby?
  • Anonymous
    November 03, 2008
    Does this new dynamic stuff include an "eval" of any sort?
  • Anonymous
    November 03, 2008
    The comment has been removed
  • Anonymous
    November 03, 2008
    Welcome to the 47th Community Convergence. We had a very successful trip to PDC this year. In this post
  • Anonymous
    November 03, 2008
    The comment has been removed
  • Anonymous
    November 04, 2008
    The comment has been removed
  • Anonymous
    November 04, 2008
    The comment has been removed
  • Anonymous
    November 05, 2008
    David, repeat after me: COMPILATION IS NOT TESTING.
  • Anonymous
    November 05, 2008
    Mark,Semantic compile-time errors such as type checking serve the exact same purpose as unit testing typically does in dynamic languages. I just choose to use the C# compiler to do that first step in the testing for me, rather than throwing out all of those checks that are already built in and then trying to recreate them (DRY anyone?).
  • Anonymous
    November 05, 2008
    The comment has been removed
  • Anonymous
    November 06, 2008
    Today, let's geek out about the language design regarding the dynamic type. Type in the language vs.
  • Anonymous
    November 06, 2008
    I'm just hoping (dreaming?) that C#4 dynamic feature will be OFF by default. And to switch it ON I propose to hide the parameter very deep in a crypted xml config file with a bizzare name. Of course a very long password will be necessary to decrypt the config file and change the switch to ON. The very long password will only be accessible opening an account on a very expensive MS portail.If all those conditions are ok for you, then I'm ok for the dynamic implementation in C# 4 !
  • Anonymous
    November 08, 2008
    Funny how people complain at every new C# feature. Remember back in the pre 3.0 days when people complained of lambda/closure...There's no motivation for a developer to use dynamic unless there is a real need, so I don't see a problem. It's like some of you are affraid us C# programmers will think we're supposed to scrap all type declarations and use dynamic everywhere...Well truly, it is you sunday morning theorists that scare me.Thanks to the C# team for making our lives simpler when it comes to interoperability.Next time I hear office integration, I might want to hurt someone for suggesting we install office in the server farm, but at least not for suggesting we interop with it from C# :)
  • Anonymous
    November 10, 2008
    @Alex,I don't remember there being a significant uproar about lambdas or closures. They were somewhat misunderstood, but I don't remember seeing a lot of people arguing that they should not be added to the language.The problem with dynamic is that there are many C# developers, especially those reluctantly converted from VB, who WILL think they are supposed to scrap all type declarations and use dynamic everywhere! I am NOT saying that simply having a potential for misuse is a reason not to add a feature to a language; I am sick of having my objections cast in that light. What I AM saying is that I don't believe that the feature as it is currently implemented brings enough added value to the language to outweigh the negative effects it will have. I also believe that there were other options for implementing late binding which would fit better into a statically typed language.
  • Anonymous
    November 10, 2008
    Let's look at this: dynamic d = null ; object o = d; // not an implicit conversion Last time , I said
  • Anonymous
    November 14, 2008
    We left off last time with this piece of code public class C { public static void M( int i) { } public
  • Anonymous
    December 15, 2008
    Very good resources for the coming version... Sam Ng Dynamic in C# Part One Dynamic in C# Part Two Chris
  • Anonymous
    December 23, 2008
    This sounds really cool.Tell me please, what is the real world benefit? I am afraid that this is a reintroduction of the VB Variant. But, somehow I think that it is not. I wish that understood this better.
  • Anonymous
    December 27, 2008
    Instead dynamic,can't u make a lot better performance of unboxing operation ?
  • Anonymous
    February 05, 2009
    When I wrote my Excel financial library I agonized over the decision of which numeric type to use to
  • Anonymous
    March 23, 2009
    ASP.NET MVC for RoR developers: do as locals do
  • Anonymous
    March 24, 2009
    It has been a while since I posted anything here, and I have the same excuse everyone else does. Work,
  • Anonymous
    May 20, 2009
    Normal 0 21 false false false FR X-NONE X-NONE Let&rsquo;s have a look to see what the tool NDepend shows