Skip to content

Back to Basics: C# 4 method overloading and dynamic types

February 13, 2010

C# 3.0 introduced the implicit type "var". I’ve explained var as saying:

"I’m too lazy to tell you the type of this variable, so you figure it out, compiler."

However, it’s more useful than just promoting terseness or laziness:

var i = 10; // implicitly typed
int i = 10; //explicitly typed

Although var is a great way to start arguments about coding standards at your work, there are times when it is required around anonymous types. Here’s an example from MSDN:

/ Example: var is required because
// the select clause specifies an anonymous type
var custQuery = from cust in customers
where cust.City == "Phoenix"
select new { cust.Name, cust.Phone };

// var must be used because each item
// in the sequence is an anonymous type
foreach (var item in custQuery)
{
Console.WriteLine("Name={0}, Phone={1}", item.Name, item.Phone);
}

C# 4 (not 4.0, the marketing folks say it’s .NET 4, etc.) adds the dynamic keyword. I’ve explained this saying:

"There’s no way for you or I to know the type of this now, compiler, so let’s hope that the runtime figures it out."

Here’s how this looks from an Intellisense point of view. Here I’m hovering over the dynamic keyword:

dynamiccalc1

And here is the tooltip after pressing "." after "calc."

dynamiccalc2

Now, to the interesting question of the day. Christoff Turner asked this question, essentially:

"I noticed the following while doing some research within C# 4.0:"

using System;
namespace ConsoleApplication1
{
class Program
{
static void f(Int32 x) { }
static void f(dynamic x) {}
static void f(Int32 x, dynamic y) {}
static void f(dynamic x, Int32 y) {}
static void f(Int32 x, dynamic y, Int32 z) {}
static void f(dynamic x, Int32 y, dynamic z) {}
static void Main(string[] args)
{
f(10); // Works - obvious
f(10, 10); // Ambiguous - obvious
f(10, 10, 10); // Ambiguous - not so obvious - since it should be possible to resolve
}
}
}

"Looking at f(10,10,10), what is the reasoning behind this call being ambiguous?"

I stared it it a while longer, then realized what is happening, and called Mads Torgersen to confirm. Mads says this.

In short, the behavior is totally by design:

  • dynamic in method signatures doesn’t come into it: it behaves like System.Object does.
  • Given that, neither of the ternary signatures is better because each fits better than the other on some arguments (Int32 fits 10 better than object does)

The key point here, in bold, because it’s significant is: having the type dynamic means “use my runtime type for binding”.

The problem in the context of method calls is that you can’t use the runtime type of something until, um, runtime.😉 Binding with dynamic expressions (dynamic binding) happens at runtime.  In this case, we’re compiling against method signatures that are known at compile type and we’re compiling with a (mostly) static language, so there’s no dynamic method dispatching happening that could select a different method overload at runtime.

The dynamic type is statically-typed as dynamic. The compile-time type is "dynamic" (but really it’s object, hence the method overloading trouble.)

Another way to look at this is with Reflector. This C# code:

static void f(Int32 x, dynamic y, Int32 z) {}

is essentially this, from a method signature point of view:

static void f(int x, [Dynamic] object y, int z) {}

and if there was a method that returned dynamic, it’d look like this:

[return: Dynamic]
private static object GetCalculator() {}

So, given how dynamic works as a type, how the DLR works in the context of method dispatching, etc, you can see why things didn’t work like Christoff thought they would.

Enjoy!

Related Links


© 2009 Scott Hanselman. All rights reserved.


Comments are closed.

%d bloggers like this: