Dynamic in C# VI: What dynamic does NOT do

As I mentioned last time, there are a few gotchas that we'll need to look at in order to get a full understanding of the dynamic feature and its capabilities. Today we'll take a look at some of those limitations. As we go along, I'll try to shed some insights as to how the decision making process came about, and why we feel these calls are the right ones.

Mutating values using dynamic

Consider the following code:

 static void Main()
{
    dynamic d = 10;
    d++;
}

What should happen here?

Intuitively, we'd expect d to contain the value 11. However, recall that under the covers, dynamic is really object. That means that the first line will generate a boxing conversion from int to object. The local variable d contains a boxed copy of the integral value specified. The second line, then, is really an application of the ++ operator on the local variable d. At runtime, calling operator ++ on d will result in an unbox of the object d into an integer, and a call to the operator ++ on the unboxed value. But this value is not copied back inside the box!

Turns out that what you expect to happen isn't really what would happen if we naively implemented this in the runtime binder. Good thing your trusty C# compiler team isn't naive!!

The solution to this little problem then, is essentially to pass things by ref to the runtime binder so that the runtime binder can write back into the value. This gets us half-way there - we still have the problem of boxed structs. Luckily for us, the architecture of the runtime binder is such that we return expression trees to the DLR (for more information on this, read my post that talks about this). There is an expression tree that performs an unbox and modifies the boxed value, and puts that value back into the box, allowing you to mutate the boxed values of these things.

Nested struct mutation

If we take our above example to the next level however, things start getting a bit trickier. Because of the nature of our architecture, dotted expressions get broken up into parts and bound in segments. So that means that for the expression A.B.C.D, the compiler will encode a site for A.B, use that result as the receiver for a second site for .C, and use the result of that as the receiver for the third site for .D.

That seems like a sensible architecture, doesn't it? Indeed, it is the same architecture that the compiler uses when it does it's binding. However, the runtime architecture has the limitation that it does not have the ability to return values by ref. (Well, this really isn't a limitation in the CLR, as they already have the support for this. This is more a limitation in the .NET languages as none of them provide the ability to have ref returns).

This means that if any of the dotted expressions were to bind to a value type, that value will be boxed (and hence a copy would be made), and further dots into it would be made on the copy of the value, and not the initial value as would be expected. Consider the following code:

 public struct S
{
    public int i;
}

public class D
{
    public S s;
    public static void Main()
    {
        dynamic d = new D();
        d.s = default(S);
        d.s.i = 10;
        Console.WriteLine(d.s.i);
    }
}

We would intuitively expect the value '10' to be printed in the console. However, the value '0' is printed instead. We're currently working on determining the best way to fix this issue, and are also debating whether or not this is a critical enough of a scenario to fix.

The rule of thumb? Remember that dynamic is like object, and so boxing happens!

Base calls

There is a restriction in the CLR that prevents the compiler from generating non-virtual calls on virtual methods. This means that there is no way to call a base overload dynamically. This means that one cannot call a base call with any dynamically typed arguments, as it will trigger a dynamic binding.

The possible solution (which we have chosen not to implement) would be somewhat akin to the solution we performed for lambdas. Recall that if you had the lambda: x => base.M(x), the compiler will generate a private method that performs the call to the base access, and will have the lambda body call the generated method. The down side, however, is that for lambdas, we knew exactly which call the user was trying to make. In the dynamic scenario, we would be doing overload resolution at runtime, and so we would have to generate a base call stub for each possible overload. This solution is quite ugly, and since we currently lack an extremely compelling scenario, we have opted not to do this and simply give a compile time error when the user attempts to make a base call with any dynamic arguments.

Explicitly implemented interface methods

As one avid reader commented in one of my previous posts, explicitly implemented interfaces kinda get the shaft again here. Because interfaces are really compile time constructs, and have no runtime representation, explicitly implemented interface members get the short end of the stick at runtime. Consider the following:

 interface IFoo
{
    void M();
}

class C : IFoo
{
    void IFoo.M() { }
}

Because of the way the compiler implements explicitly implemented interfaces, C.M gets its name removed (making it uncallable via a C pointer). Now this is fine at compile time, because the compiler can see when a receiver is known to be an IFoo pointer. However, at runtime, there is no notion of interfaces, and so there is no IFoo available for the runtime binder to use to dispatch methods. Combined with the fact that C.M's name has been removed, this makes the method entirely uncallable dynamically.

Accessibility

This last topic isn't really a limitation yet. We are still working on drawing the line between doing the pragmatic thing and doing the most consistent thing on this issue. The CTP implementation of dynamic currently performs accessibility checks only on the member that you are accessing. This means that the runtime binder checks to verify that any member you're trying to use is public.

Namely, we do not do accessibility checks on the type itself (ie if you really ought to be able to access the type of the object in your current context, or should it just look like an opaque object to you), and do not allow any non-public members to be used dynamically.

The down side of this scenario is that you could make a call with a static receiver to a private method that you know you can access from your context, but because a dynamic argument is given, the runtime binder will prevent you from calling the method. Below is an example:

 public class C
{
    private void M(int x) { }

    static void Main()
    {
        dynamic d = 10;
        C c = new C();
        c.M(d);
    }
}

When the compiler encounters this expression at compile time, it will do the verification and know that C.M is accessible from the calling context, but because the argument is dynamic, the call gets resolved at runtime. Because of the public-only policy of the current binder, overload resolution will not bind to the method.

Conclusions?

As always, I love getting your feedback, whether positive or negative. But this post in particular I would love to get your thoughts on! The design is not set in stone, so any of your thoughts will definitely be personally brought to the design team by yours truly. Thanks for your comments in advance, and as always, happy coding!

kick it on DotNetKicks.com