Dynamic in C#

Dynamic in C#

Rate This
  • Comments 23

The other day I was playing around with some office code, and I found myself writing a lot of code much like the following sample that Anders used at his PDC talk:

static void Main(string[] args)
{
    var xl = new Excel.Application();

    ((Excel.Range)xl.Cells[1, 1]).Value2 = "Process Name";
    ((Excel.Range)xl.Cells[1, 2]).Value2 = "Memory Usage";
}

As you can imagine, it very quickly became tiresome assigning the results of each call to a local variable, debugging and finding out what type it returns for my scenarios, making the cast to the strong type so that I can call certain methods on it, rinse, and repeat.

This pattern is common in dynamic APIs, and cause a lot of excess code to be written that essentially is used just to make the type system happy. Wouldn't it be nice if instead, we could write something like this?

static void Main(string[] args)
{
    var xl = new Excel.Application();

    xl.Cells[1, 1].Value2 = "Process Name";
    xl.Cells[1, 2].Value2 = "Memory Usage";
}

Well, in C# 4.0, we now allow you to write exactly that. One of the main features that we're working on in C# 4.0 is the dynamic late binding feature. This feature allows you to tell the compiler that the thing that I'm returning really ought to be treated like a dynamic type, and that any dispatch on it should be done dynamically. The runtime will then do the binding for you based on the runtime type of the object instead of the static compile time return type, and if the binding succeeds, then the code will succeed. This gives us exactly what we want.

So how does this feature work?

Firstly, we've introduced a dynamic type into the type system. This type indicates to the compiler that all operations based on the type should be bound dynamically and not at compile time. Secondly, we've created a C# runtime binder which does the late binding for you. Lastly, we've baked in the usage of the DLR and are making full use of their caching and dynamic dispatch capabilities, so that you can interop with dynamic objects (objects created from Iron Python for instance).

The dynamic type

In order to start using the dynamic binding, we've got to have some way to signify to the compiler that we want our object or expression to be bound dynamically. Enter the dynamic type.

The dynamic type is just a regular type that you can use in your code to denote local variables, fields, method return values etc. It tells the compiler that everything to do with that object or expression should be done dynamically. Consider the following example:

static void Main(string[] args)
{
    dynamic d = SomeInitializingStatement;

    d.Foo(1, 2, 3); // (1)
    d.Prop = 10; // (2)
    var x = d + 10; // (3)
    int y = d; // (4)
    string y = (string)d; // (5)
    Console.WriteLine(d); // (6)
}

In this example, each of the statements has some element of the dynamic type flowing through it, and is therefore dispatched dynamically. Lets consider each of them.

  1. Since the receiver in this example is typed dynamic, the compiler will indicate to the runtime that it needs to bind some method named "Foo" on whatever the runtime type of d happens to be, with the arguments {1, 2, 3} applied to it.
  2. This example also has a dynamic receiver, and so the compiler indicates to the runtime that it needs to bind a property-looking-thing (could be a field or property) named "Prop", and that it wants to set the value to 10.
  3. In this example, the operator "+" becomes a dynamically bound operation because one of its arguments is dynamically typed. The runtime then does the normal operator overload resolution rules for "+", finding any user-defined operators named "+" on the runtime type of d, and considering that along with the regular predefined binary operators for int.
  4. In this example, we have an implicit conversion from the runtime type of d to int. The compiler signifies to the runtime that it should consider all implicit conversions on int and on the runtime type of d, and determine if there is a conversion to int.
  5. This example highlights an explicit conversion to string. The compiler encodes this cast and tells the runtime to consider explicit casts to string.
  6. In this example, despite the fact that we're calling a statically known method at compile time, we have dynamic arguments. As such, we cannot perform overload resolution correctly at compile time, and so the dynamic-ness of d flows out to its containing call, and we end up dispatching Console.WriteLine dynamically as well.

There are several other scenarios that dynamic flows out to, but I've listed these to give you a general idea of what the dynamic type's implications are.

Now, we should note that the dynamic type is really just syntactic sugar to signify to the compiler that it should treat bindings dynamically. In metadata, dynamic is just object with an attribute signifying its dynamicity (if that's even a word... I don't think it is though!).

What happens at compile time?

For each dynamic operation, the compiler generates calls into the DLR, and takes advantage of its call sites. For more information about the DLR and what call sites are, my colleague Jim Hugunin gave an excellent talk at PDC about it - you can view his blog here, and watch his talk here.

The DLR call site takes a set of standard actions which indicate what type of dynamic action we want to take. The C# compiler emits a subclass of these standard actions, annotated with some C# specific details, and emits invocations of the call sites in place of the call that the user makes. For instance, this code sample gets translated into something like the following pseudocode:

// This code...
static void Main(string[] args)
{
    dynamic d = SomeInitializingStatement;
    d.Foo(1, 2, d);
}

// transforms into this code.
static void Main(string[] args)
{
    dynamic d = SomeInitializingStatement;
    _csharpCallAction = new CSharpCallAction("Foo");
    _dlrSite<T> = new Site<T>(_csharpCallAction); // Create the site. 
    _dlrSite.Target(1, 2, d); // Invoke the delegate. 
}

If you want more information on this, my colleague Chris Burrows has written an excellent blog on the dynamic type and what the compiler generates.

Note that the site creation pseudocode specifies a generic argument, T. This argument is a delegate type that represents the signature of the call. So in our example, our call takes 2 integer arguments and a dynamic argument, and has a dynamic receiver. T would then be a delegate that represents that.

Invoking that delegate invokes the C# runtime binder, which binds the expression based on the runtime types of the arguments and the receiver.

What happens at runtime?

When the DLR delegate gets invoked, it does a couple of cool things that I'll describe briefly. For more information, check out Jim Hugunin's blog.

  1. The DLR checks a cache to see if the given action has already been bound against the current set of arguments. So in our example, we would do a type match based on 1, 2, and the runtime type of d. If we have a cache hit, then we return the cached result.
  2. If we do not have a cache hit, then the DLR checks to see if the receiver is an IDynamicObject. These guys are essentially objects which know how to take care of their own binding, such as COM IDispatch objects, real dynamic objects such as Ruby or Python ones, or some .NET object that implements the IDynamicObject interface. If it is any of these, then the DLR calls off to the IDO and asks it to bind the action.

    Note that the result of invoking the IDO to bind is an expression tree that represents the result of the binding.
  3. If it is not an IDO, then the DLR calls into the language binder (in our case, the C# runtime binder) to bind the operation. The C# runtime binder will bind the action, and will return an expression tree representing the result of the bind.
  4. Once step 2 or 3 have happened, the resulting expression tree is merged into the caching mechanism so that any subsequent calls can run against the cache instead of being rebound.

Jim gives a great description of points 1, 2, and 4, which deal with the DLR specifics, so I'm going to elaborate on what happens in step 3.

The C# runtime binder

The C# runtime binder uses Reflection to populate its internal symbol table to determine what to bind to. Each of the C# specific actions encodes the type of the binding, along with extra information that allows us to determine how to bind the action.

For example, if the argument is known at compile time to have a static type, then that type will be marked in the C# action, and will be used as the type of the argument during runtime binding. If it is known at compile time to be typed dynamic (ie it is a variable of type dynamic, or is an expression that returns dynamic), then the runtime binder will use reflection to determine its runtime type and use that type as the type of the argument.

The runtime binder populates its symbol table as needed. For instance, in our example, we were calling the method Foo. The runtime binder will load all members named Foo on the type of the receiver into the symbol table.

It then populates the necessary conversions for each of the argument types. Since we may need to coerce the arguments to types that match the method calls (using user-defined conversions as necessary), the binder loads those conversions into the symbol table as well.

It then performs overload resolution exactly like the static compiler does. That means that we get the exact same semantics as the static compiler. It also means that we get the same error semantics and messages - a failed binding at runtime results in an exception being thrown, which encapsulates the error message that you would have gotten at compile time.

It then takes the result of overload resolution and generates an expression tree that represents the result, and returns that back to the DLR.

A summary

So that's a brief summary of what the dynamic pipeline looks like. Of course, I've glossed over a lot of the details, but I'll be covering those details in my future posts. Until next time, some questions to ponder:

What happens when the receiver is known statically but the arguments are dynamic? What happens if the methods we're trying to bind against are private? What about operators - how does resolution work on them?

These, and more, I'll aim to address in my subsequent posts.

As always, happy coding!

kick it on DotNetKicks.com
Leave a Comment
  • Please add 6 and 6 and type the answer here:
  • Post
  • I didn't see anything about this, so I was wondering whether one could use 'dynamic' values for multimethods.

  • What happens if I declare a delegate type with dynamic arguments? I.e.:

     delegate void D(dynamic x);

    Can I then do this?

     void Foo(int x) { ... }

     void Bar(float x) { ... }

     D d = (D)Foo + (D)Bar;

     d(123); // a separate implicit conversion for every method in the list?

    What happens if there is an overloaded method group instead?

     void Foo(int x) { ... }

     void Foo(float x) { ... }

     D d = Foo; // ambiguity?

     d(123); // call Foo(int)?

     d(123f); // call Foo(float)?

  • Please tell me something akin to "option strict" will exist.

  • GrayShade - if you were to have something like the following:

    dynamic d = ...;

    d.Foo(x);

    the dispatch would happen using the runtime type of d as the receiver, and the runtime type of x as the argument. This is essentially the multidispatch mechanism - if you were to change the runtime type of d, then you'd dispatch the same call to a different method.

  • int19h -

    Your delegate example will not work. Dynamic isn't a wildcard type that matches everything, including method signatures etc for overload resolution. It is a way to signify simply that the expression is to be bound at runtime.

    Specifically, dynamic in a delegate signature wont really have much effect. When you use the dynamic type in a method argument, it means that inside the body of the method, that parameter is to be bound dynamically. From a caller's perspective however, they can invoke the method with object or dynamic - it really doesn't affect them.

    For your second example -

    In this release of the language, we have opted out of providing support for dynamic method groups. The reason is that currently there is no support for the notion of runtime method groups (ie what would the runtime type of d be?).

    If you were to do something like:

    dynamic d = x.Foo;

    and x.Foo results in a method group, you'd get a runtime exception. The expression is valid however, because x.Foo can be a field or a property.

  • Tanveer -

    We're debating whether or not to put an "option strict"-like thing on dynamic. Still in the process of that - will let you guys know when we've decided!

  • TBH I don't see much point in "option strict" here. In VB it's needed because they have dynamic dispatch enabled by default for Object, which is generally useful for other reasons (i.e. you often want to declare something as Object and not have DD on it). C# 4.0 design is much better in that respect as you have to request DD explicitly.

    The only subtle moment where it might matter is when you call a library function that returns "dynamic". E.g.:

    // SomeThirdPartyLib

    dynamic GetSomething() { ... }

    // MyApp

    class Foo

    {

      public void Bar(int x) { ... }

      public void Bar(float x) { ... }

    }

    Foo foo = new Foo();

    foo.Bar(GetSomething()); // dynamic dispatch - code compiles okay, overload resolution at runtime!

    The problem here is that I get DD in my code even though there's no mention of "dynamic" anywhere in it.

  • > In this release of the language, we have opted out of providing support for dynamic method groups. The reason is that currently there is no support for the notion of runtime method groups (ie what would the runtime type of d be?).

    The runtime of d would obviously be Action<dynamic> (or a similar automatically generated delegate type) ;)

    The obvious way to do it would be to simply generate a lambda that dynamically dispatches the call. I.e., in this case, the line:

     D d = Foo; // ambiguity?

    is silently replaced with:

     D d = (x => Foo((dynamic)x));

    but i can certainly see why you would want to avoid that sort of thing, "dynamic" already being as complicated as it is :)

  • int19h -

    You've hit it dead on - the one place that dynamic can "leak" into your app is when you call someone else that returns a dynamic. This seems like a good place for IDE tools to help us figure out what calls are dynamic and which arent!

  • int19h -

    D d = Foo; // This is fine, cause you have no dynamics here. But if you did:

    dynamic d = Foo; // Assuming this is what you meant.

    d(1, 2, 3); // Here is the first time you can figure out the signature.

    If you do this, you dont know what delegate to type d as, because Foo may have different signatures. You wont know which signature to use until d is invoked.

    The only way I can think of to do this is to have some intermediate representation, typing d as "MethodGroupType" or something :) And yes, that makes things much more complicated :)

  • > D d = Foo; // This is fine, cause you have no dynamics here.

    The point was that I do have them there - in the signature of delegate type D.

    But overall, I see the point. Basically, the way you did it for C# is that the client only sees the difference between "object" and "dynamic" for out-values of a call (return and out/ref args), but there's no difference whatsoever for input arguments (the difference is only for the code within the method). It can be said that the same goes for delegates.

  • Exactly. Note that we COULD have gotten this particular scenario to work - recognize that its an assignment conversion and that we have a delegate type, so perform the conversion. But that wouldn't work in more general scenarios, such as passing a methodgroup to a method call, and performing overload resolution on the outer method first in order to determine the delegate type.

  • Welcome to the 47th Community Convergence. We had a very successful trip to PDC this year. In this post

  • can yu tag this post with c# 4.0

  • C# 4.0 Dynamic Lookup I really like the way the C# team tackled bring dynamic programming to the language

Page 1 of 2 (23 items) 12