Back to Basics: C# 4 method overloading and dynamic types
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:
And here is the tooltip after pressing "." after "calc."
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
About Scott
Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.
About Newsletter
"we're compiling against method signatures that are known at compile type and we're compiling with a most static language,"
Here you say that the method signatures are known at compile [time], which leads me to believe that this is a *good* thing with respect to disambiguating the method signatures. But apparently it's not. Then you say that we're compiling with a "most static language", which I assume should be "most[ly] static language?" So this means that it can't disambiguate the methods because they're dynamic, and the compiler is static, so it doesn't know how to do that? That's what I'm getting, anyway.
So, the jist of what I gathered from this is that the dynamic keyword makes the compiler say "I can't know this type until runtime, so I'm not even going to try to resolve it."
This still doesn't clear up why f(10) isn't ambiguous. Why wouldn't it run into the exact same hangup with this? Because a purely static method trumps a dynamic one?
I think f(10) isn't ambiguous because there is only one argument, and the compiler knows the best candidate between Int32 and object (dynamic). With two arguments, the two methods are exactly the same, in terms of best candidates (the first method is better for the first argument, and the second method is better for the second argument - or vice versa), and this is totally against the logic of C# overload resolution (find THE ONLY best match).
But, since I probably understood 1% of dynamic overload resolution (and I will probably avoid the "dynamic" keyword like malaria), I suggest Eric Lippert's blog, which has a really nice serie of posts about this.
Filini is right - the issue is with having TWO method signatures that are effectively the same thing. They are object, int, object, or int, object, int. When I pass in int, int, int - which one should we choose? Filini nails it, in fact as we are supposed to "find ONLY the BEST match."
It's such a shame that you promote this stuff. You should've seen the horrific devastation that "Variant" caused in the old VB days. Variant single-handedly create job security for so many people since the late 90's, because of the horrible, horrible, horrible things that developers did with that ridiculous, 12-byte data type!
This weak-typing with late-binding is just such a bad idea. I know you'll say "But wait, these are powerful tools that skilled developers can leverage!" - and maybe so, but 98% of the people that truly use these sorts of techniques out in the real world, are unskilled developers making a mess of software all across this great land, because the compiler is so forgiving. This is a huge step backwards for C#, in my opinion - and creates the same scenario VB always did - where it is so forgiving, that it allows developers to write horrible code and you won't so much as see a compiler warning!! I've always tauted that C# was better, simply because it gave the developer "tough love", and forced him/her to be better coder and to "make good choices"! :-)
Specific to this blog post, if you are doing somewhere where you can't even quantify what the data type that is coming back? Guess waht, you've got yourself a bad design. Just because the framework allows you use weak-typing and late-binding, doesn't mean you should - nor should you endorse it's use, in my opinion. I'm just saying, it's a shame that popular "nerd celebrities" like you (and I mean zero offense by that!) - endorse all this loosey-goosey typing. I say that becuase I've never seen a single case where weak typing or late binding: A) made a design better or B) where it didn't make the component or application worse, because it was a looser design.
Just my $.02
-Rob
Surely it's a great idea to have options to enable you to use this sort of thing even if you may never use it. I'm sure there's a good group of people wishing they had this feature where they themselves are forced in to having to use something like this to integrate into another system or other code they need to work with.
Without the "dynamic" keyword, Silverlight 4 would not be able to support COM via IDispatch. IDispatch calls like that are a pretty valid use of the keyword, IMHO.
That siad, it seems that with each new language feature I hear (valid) concern that it's going to make it harder for new folks to get into the language, or n00bs will misuse it. I think that's just the nature of the beast.
IIRC, Variant was so problematic because you could *change* the type at runtime. "Hey, now I'm a string, ooh, now I'm an int! Nope, nope, just kidding. I'm a string! Take that, new programmer". Var doesn't let you do that. I don't believe dynamic does either.
Pete
An example of where dynamics can be very beneficial http://simpable.com/code/mongodb-dynamics/
Any tool can make damage in the wrong hands. Unskilled developers can make a mess of pretty much anything, dynamic or not... Even type or interface inheritance can make a lot of damage in its own way, if you look at it like that. (Or worse... unsafe code)
It's about using the right tool or design at the right time, as always.
I know for sure I will not be using that keyword a lot, except maybe for the underlying CLR method call dispatcher (System.Runtime.CompilerServices.CallSite) which is amazingly fast, compared to what the reflection can do. I used to do that by generating IL code to avoid calling MethodInfo.Invoke, but now it's integrated in the BCL.... Way to go :)
My other two cents.
--Jerome
The damage that can actually be done with it is an entirely different issue.
I thought I'd jump in with a few remarks about your well-reasoned comment.
Not to be pedantic, but there is no such thing as "weak typing" on the CLR. Everything has a statically defined type in the type system. It's just your variables that don't have an explicit binding to objects that conform to the type system enforced by your programming language. In general, we refer to systems like this as being dynamically typed.
Regarding your comments about less-skilled developers not being able to get things done with dynamically typed languages: that certainly was the case before the industry broadly adopted unit testing. Surely, you don't ship your software just because your compiler says it runs?
Anecdotally a developer on my team was writing some code using Haskell the other day (Simon Peyton-Jones visited with our team). Now, Haskell folk look down at languages like C# and say that gee- how do you write correct programs using a language with such a non-strict type system? In general, if a program compiles in Haskell it runs correctly. However, he had a performance bug where the calculation was being done via infinite precision numbers rather than by using floating point numbers: he was missing a type annotation.
A type system is a tool. Sometimes it helps things, sometimes it just flat out gets in the way. Nobody is advocating using dynamic in all cases: use it where appropriate like invoking COM Automation methods in Office.
Thanks for this post.
Might be nice if you could 'rewrite' the "var != Dim" post you link to at the end of your post for C# 4
i wonder when will people finally realize that all old models of governmental and economic systems have become obsolete.
it's time for something new, a system in which technological advancements and automation does not scare people into joblessness.
any gain in effectiveness of harvesting resources (e.g. those new solar panels which are 9 times more efficient than the old) rather must translate immediately and directly into not only into the gross domestic product but also the public weal.
the "dynamic" keyword, as I see it, is just syntactic sugar for reflection calls, and a nice helper to call COM and other libraries.
Personally, I won't use it a lot, since I never had a big need for reflection in my projects, but I don't see it as a world-breaking feature.
No more dangerous than reflection already is, I think...
@Pete
yes, you can change the type at runtime, even without var or dynamic. This is perfectly fine:
object o = new Foo();
o = new Bar();
o = 12;
"You should've seen the horrific devastation that "Variant" caused in the old VB days." --- I would say that hopefully a cultural difference should curve this a bit. Variant was pretty much "by default" in VB, wheras it is much more of a conscious decision in C#.
"I say that becuase I've never seen a single case where weak typing or late binding: A) made a design better" --- Two key scenarios for me. First, interop with APIs beyond your control (e.g. with dynamic libraries or COM) and second, using flexible data formats like XML and json. Writing json code in C# is ugly, difficult and brittle if you just want to extract a few data points. Heck, even doing the same in XML is horrid.
There are dozens and dozens of examples where we pass strings as arguments to pull out specific bits of data and noone bats an eyelid. Microsoft try and make it much nicer, cleaner and consistent, making code more readable and thus more maintainable and people start declaring the end of the world. No, I am not going to declare an XML scheme and classes just to pull out one or two choice bits of information from an XML document just because it's more "type safe".
class Program
{
static void Main(string[] args)
{
dynamic d = new ExpandoObject();
d.Shoot = "Booom!";
DoSomeThing(d);
}
void DoSomeThing(dynamic d)
{
Console.WriteLine(d.Shoot);
}
}
but this won't:
class Program
{
static void Main(string[] args)
{
dynamic d = new ExpandoObject();
d.Shoot = "Booom!";
DoSomeThing(d);
}
}
Check the rest of the argument, here.
Can it be abused? Absolutely! But something is not "evil" because of what you can do with it. Just because you can use a pen as a weapon, does that mean we shouldn't have pens at all? Of course not.
Then there is the case where the dynamic keyword (and what is enabled by it) can make certain programming models a lot easier/more powerful to use. ScottW mentioned testing. Another one is interop with XML. With a smart dynamic XML object, you can simply use properties and indexed properties to retrieve the various node values in an XML fragment quite easily.
It would be great to use the new dynamic keyword instead of dictionaries, and anonymous types.
http://blog.bodurov.com/How-to-Bind-Silverlight-DataGrid-From-IEnumerable-of-IDictionary
In the same way, I'm am continually amazed at the immaturity of the .NET community. The dynamic keyword is bad? Take it away! I'm scared!
Strong typing is a programming paradigm. It isn't a requirement, and with well constructed and tested software, it can be a hinderance. It is training wheels.
Now, I can understand the skepticism some people have of making C# a multi-paradigm programming language. It has some fp ability, and now they are introducing some aspects of a dynamic language as well.
But programming in C# has forged synapses in our brains that prefer the strong-typed approach to things. But that comes at a cost as well. There is a lot of ceremony that strong-typing requires. I want a method that takes an argument that I can perform some operation on - well, arguments are based on a type. I can specify a specific type in the signature, and the compiler will check that I pass something in that is of that type. Or I can specify some sort of expected behavior using an interface, or more precisely with a delegate. But these requirements ripple through our code in offensive ways. We have to introduce a type, or an interface, or a delegate. How many times have you implemented an interface on a type when it already has the implementation prescribed on that interface just to make the compiler happy? All for the benefit of what? A compiler error? Even when I have tests in place that exercise that method in all ways I intend to use it? Or the great crutch of "Intellisense"? Intellisense is the single biggest contributor to violations of the interface segregation principle I know of. I'll just fish around in intellisense until I see a property or method (or several) that get me close to what I want.
When I was a VB 3, VB 4, VB 5, or VB 6 programmer I wasn't that good of a programmer. While the expressiveness of the language perhaps kept me from being able to do some things I would have liked, I never, ever blamed any bugs I produced on the fact that there was a variant data type, or that it was doing a late-bound call. I took responsibility as a professional software developer.
It's like complaining that Word lets you save a blank document or something. Geesh.
With keywords like "dynamic", the CLR team has taken the latch off the cabinet. Are you afraid of what is inside?
And for those that are concerned that other developers are going to write brittle, dangerous code because of this, who, exactly, are you talking about?
I'm going to uninstall Visual Studio now and go back to building web applications in Classic ASP because that's what I know well and I'll never be threatened with a new concept ever again.
/sarcasm ends
Great post highlighting some of the mental hurdles that developers are going to have to overcome in their understanding of dynamic. Sometimes I think we are so in love with compile type typing because it allows us to be lazy (although it does simplify discoverability of APIs). Guess I should really go and learn some Ruby because I need to break my current mental model and reform a new one to incorporate the existence of dynamic elements in my formerly static language.
Looking forward to using and abusing dynamic =)
See this post http://blogs.msdn.com/b/csharpfaq/archive/2004/03/07/85562.aspx re why MI was left out of C#.
I find points 2 and 3 interesting, specially if I replace MI with dynamics/var (don't be literal)
Now I'm really new to dynamics, need to learn more, but it seems that reasons that MI was left out could also apply to dynamics. Or am I missing something?
And I've seen lots of damage done with C# without dynamics or MI at fortune 500 companies. I can hardly wait. :-(
Comments are closed.