Overloading Dynamic

Writing

If you’ve been checking out Visual Studio 2010 (or reading my blog) then you might have noticed the new “dynamic” keyword in C# 4.0. So what is the dynamic keyword? The dynamic keyword allows us to perform late-binding in C#! What is late-binding you ask? Well, that means that operations on the variable aren’t bound at compile time, they are instead bound at runtime. By “bound” I mean that which particular member to invoke is decided while the application is running, not during compilation.

In the past, you might have seen examples of dynamic using a sample like this:

dynamic value = "test string";
value.DoSomethingSuper();

Now obviously the String class does not have a method called “DoSomethingSuper”, but this code will compile. It will blow up at runtime with an error saying that the string class does not contain a definition for “DoSomethingSuper”. If you want a more in depth look at the basic usage of the keyword, see the linked post above.

So Much Dynamicness

What is really interesting is that the dynamic keyword isn’t just for declaring local variables. We can use it for method parameters, return types, and almost anywhere that we can specify a type. Which means that we could actually write something like this (note that you might now want to, you could):

public static dynamic DoSomethingDynamic(dynamic dyn1, dynamic dyn2)
{
    return dyn1 + dyn2;
}

Interesting. So this method does basically what we would find in any dynamic language such as Python or Ruby. I can call it like this:

DoSomethingDynamic(3, 5);

Or I can call it like this:

DoSomethingDynamic(3.5, 5.2);

Or even like this:

DoSomethingDynamic("hello", "there");

And guess what, it works like you would expect. The first two calls are added, and the third call is concatenated together. It truly does allow you to have fully dynamic behavior in C#. We can even support fully dynamic classes with (ala method_missing) using DynamicObject.

An Overload Of Dynamic

But there is one little wrinkle that C# has to deal with that traditional dynamic languages like Ruby and Python don’t have to deal with. And that wrinkle is method overloading. Think about that, in a dynamic language, you don’t specify types on method parameters, so there is nothing to overload. The only thing that methods signatures are based off of is the number of parameters.

But C# still has types. And it has a dynamic type. Hmmmmmmm. That is interesting. So what happens when we declare the above method, but then we declare something like this?

public static dynamic DoSomethingDynamic(string dyn1, dynamic dyn2)
{
    return "nope!";
}

Interesting. Method overloading with dynamic. Suddenly we are in a situation where we have to factor in dynamic as part of the process. So in the above, what happens? Well, thankfully they implemented it in the most obvious way. Types take precedence over dynamic. So if we have the above method, and call it like this:

Console.WriteLine(DoSomethingDynamic("hello", 5));

Then instead of picking the (dynamic, dynamic) overload, the C# compiler picks the overload that matches the most types. But what happens if we implemented these methods:

public static dynamic DoSomethingDynamic(string dyn1, dynamic dyn2)
{
    return "string first!";
}

public static dynamic DoSomethingDynamic(dynamic dyn1, string dyn2)
{
    return "string second!";
}

Looks like we’ve got a bit of a conundrum. If we call this method with (string, string) how would we know which method to call? Well, we can’t, and the C# compiler just throws its hands up and says “The call is ambiguous between the following methods or properties” Well that stinks.

The easiest solution would just be to implement an overload that implemented (string,string) and then you couldn’t find yourself in this situation. So, you may be thinking, well, wouldn’t this always be caught by the compiler?

Can The Compiler Save Us?

Well, let’s consider the situation where you have an overload with (dynamic, dynamic), (string, dynamic), and (dynamic,string). Then we have some code that looks like this:

dynamic val1 = "test";
dynamic val2 = "test2";
Console.WriteLine(DoSomethingDynamic(val1, val2));

Ahhhhhhhhhh! Brain teaser. What do you think will happen? Well, we are dealing with all dynamic variables here, so this is going to compile. And when we run it, do you think that the method with the (dynamic, dynamic) signature will be called? That might make sense at first, but consider that dynamic variables perform method overload resolution at runtime. So, those variables are dynamically typed, but they are strings.

So what happens at runtime is that we determine that those variables are strings, and we try to find out what method overloads are available… and we find the ambiguity… at runtime! Here is the proof:

image

Did you happen to notice the references to “object” in the method signatures? Where did that come from? Well, it just so happens that dynamic isn’t really a type. It is just an object with a bit of extra behavior added during compilation.

Dynamic Is Special

So, if we were to look at the reflected code for this we would see that the two variable declarations look like this:

object val1 = "test";
object val2 = "test2";

Okay, but what about that special behavior we were talking about? Well that comes in where these variables are used. In this case we have two method calls. The first one is to DoSomethingDynamic and the next is to Console.WriteLine. In order to invoke these methods with dynamic variables we need to create these things called “call sites”. Call sites are merely objects that represent a call to a method which is created at runtime during the first invoke. These classes are what allow the method resolution and caching on each call to occur at runtime. They look something like this (truncated for brevity):

private static void Main(string[] args)
{
    object val1 = "test";
    object val2 = "test2";
    if (<Main>o__SiteContainer0.<>p__Site1 == null)
    {
        <Main>o__SiteContainer0.<>p__Site1 = 
            CallSite<Action<CallSite, Type, object>>.Create(...);
    }
    if (<Main>o__SiteContainer0.<>p__Site2 == null)
    {
        <Main>o__SiteContainer0.<>p__Site2 = 
            CallSite<Func<CallSite, Type, object, object, object>>
                .Create(...);
    }
    <Main>o__SiteContainer0.<>p__Site1.Target.Invoke(...);
}

The importance of this is that when you compile a method which has dynamic parameters but is being passes statically typed variables, there doesn’t really need to be any special behavior when the method is invoked. It is inside of the method where we are dealing with these dynamic paramters that we will start seeing CallSites get created. So, the method will look like this:

[return: Dynamic]
public static object DoSomethingDynamic([Dynamic] object dyn1, [Dynamic] object dyn2)
{
    if (<DoSomethingDynamic>o__SiteContainer4.<>p__Site5 == null)
    {
        <DoSomethingDynamic>o__SiteContainer4.<>p__Site5 = 
            CallSite<Func<CallSite, object, object, object>>.Create(...);
    }
    return <DoSomethingDynamic>o__SiteContainer4
                .<>p__Site5.Target.Invoke(...);
}

Hmmm, so when we are doing method resolution the (dynamic, dynamic) method just looks like (object, object)! So does that mean if we did this:

dynamic val1 = new Object();
dynamic val2 = new Object();
Console.WriteLine(DoSomethingDynamic(val1, val2));

That it would then call the method with the (dynamic, dynamic) signature? Well, yes it does. 🙂 Phew. And with that, we can now get a better picture of the implications of method overloading and our use of dynamic.

Reflecting

So, what does all of this mean? Why is it important? Well, it depends on how you look at it. Are you going to have to deal with method overloading involving dynamic very often? Probably not. Is it interesting to see how much thought and effort it takes in order to design a feature like this? You bet it is.

Loved the article? Hated it? Didn’t even read it?

We’d love to hear from you.

Reach Out

Comments (5)

  1. The most important feature (or the only good feature) about dynamic is that it allows multiple dispatch/multi-methods.

    So while having dynamic as a parameter type seems like an antipattern to me, actually passing dynamic to a method could be insanely useful for visitors if the performance is ok.

  2. @Andrey I agree that dynamic allowing multiple dispatch is huge for several common patterns. But I’m not sure that I would go as far as saying that this is the only good use for it.

    And as far as having dynamic as a method parameter being an anti-pattern… well, you aren’t big on dynamic languages, are you?

Leave a comment

Leave a Reply

Your email address will not be published. Required fields are marked *

More Insights

View All