views:

573

answers:

3

I don't have access to the C# 4.0 preview yet. But I am curious, what does the C# 4.0 runtime do when invoking an overloaded method in the following case. Does it resolve to the generic overload ... or the specialized overload.

public class Foo<T>
{
  protected string BarImpl( T value ) { return "Bar(T) says: " + value.ToString(); }

  protected string BarImpl( int value ) { return "Bar(int) says: " + value.ToString(); }

  public string Bar( T value )
  {
    dynamic foo = this;
    return foo.BarImpl( value );
  }
}

public static void Main( string args[] )
{
  var f = new Foo<int>();

  Console.WriteLine( f.Bar( 0 ) );
}
+3  A: 

Sam Ng has a great series of blog posts about this. I forget the exact details (and they may still change before release of course) but that blog series goes into quite a lot of depth, including generics stuff.

In general, my understanding that the result should (whenever possible) be the same as the result would be if you compiled the same code with just the dynamic expressions replaced by expressions of the type that the dynamic value has at execution time. (Statically-known types are preserved in the call site info.)

In this particular case, having just your code with .NET 4.0b1, the result is:

Bar(int) says: 0

However, having looked at this again (and checked which bit is actually dynamic) I'm slightly confused. I think it's one of those situations where I'd have to look very carefully at the spec to understand what the correct behaviour is. Unfortunately I don't know when the C# 4.0 spec will be available.

It's a tricky one to reason about, and I suspect the key part is whether at execution time the binder is able to work out that the value is of type T for the same T as the receiver, rather than type int. Because the receiver is dynamic in this case, the compiler doesn't do any overload resolution at all. Hmm. Tricky one, definitely.

Jon Skeet
I've read the series ... there wasn't a clear case analogous to my example, so I can't be certain. The impression I get from what I've read is that Bar( int ) will get called. Which would be very nice, because it would make it possible for me to use dynamic as a way to implement C# generic type specialization ... something that up until now hasn't been very easy (or elegant) to do.
LBushkin
Yup, Bar(int) is called. I've edited my answer because I'd slightly misread your question. And yes, it's handy to use dynamic to do all kinds of things like this. I like the case where you know something is a Foo<T> really, but you don't know what T is, so you can't call any generic methods...
Jon Skeet
I hadn't thought of this sort of specialization before though... very interesting. I wonder how efficient it'll turn out to be. I've seen pretty good performance from the DLR so far.
Jon Skeet
This behavior is different from C# 3.5 without the dynamic keyword. Calling foo.Bar(0) returns "Bar(T) says 0" in that case.
LBushkin
I've recently implemented a version of generic type specialization that uses Linq.Expression to build dynamic delegates for .NET 3.5 - it works, I'm just trying to optimize the performance before I post an article online about the technique. My hope is that with C# 4.0 - dynamic will be a better and more performance way to achieve the same results.
LBushkin
@LBushkin:foo.Bar(0) returns "Bar(T) says 0" because the compile-time type of "value" is T. If you change the compile-time type to int, it will return "Bar(int) says 0". That's what I meant by the bit of my answer where I talk about dynamic expressions being replaced by values of the type that the dynamic value has at execution time.
Jon Skeet
(Having said which, I think the final bit of my answer is incorrect... I'll edit.)
Jon Skeet
Yup, I get how the compiler resolves the overloads - I just think it's interesting that the DLR resolves the calls differently. I suspect that can introduce unexpected behavioral differences in an application - just by switching from using strong typing to dynamic typing. Not something that most people would expect - and a tricky thing to debug I imagine. However, this behavior can also be useful in certain contexts - such as for my idea to use it for generic type specialization.
LBushkin
Just don't forget that it could still change before C# 4 finally ships. Unfortunately I could really do with understanding it better than I do, in order to write about it sensibly.
Jon Skeet
+2  A: 

Check out this blog post:

http://blog.davemorton.net/2009/05/dynamic-type-and-runtime-overload.html

His blog touches on exactly this kind of scenario and he compares the difference of the current overload resolution, to the way it'll be in .NET 4.0. Check it out...

BFree
A: 

I have another blog on a similar topic... this time with a caveat, and another small nuance that I find a little frightening. It is close to Halloween, after all:

http://blog.codinglight.com/2009/10/scary-halloween-style-c-40-code.html

David Morton