views:

554

answers:

4

There is some strange behavior with the C# 4.0 dynamic usage:

using System;

class Program {
  public void Baz() { Console.WriteLine("Baz1"); }
  static void CallBaz(dynamic x) { x.Baz(); }

  static void Main(string[] args) {
    dynamic a = new Program();
    dynamic b = new { Baz = new Action(() => Console.WriteLine("Baz2")) };

    CallBaz(a); // ok
    CallBaz(b); // ok
    CallBaz(a); // Unhandled Exception:
    // Microsoft.CSharp.RuntimeBinder.RuntimeBinderException:
    // The name 'Baz' is bound to a method and cannot be used like a property
  }
}

I'm using the Visual Studio 2010 Release Candidate.

Is this a bug? If it's true, will it be fixed in the Release?

+2  A: 

Same thing happens for me,i suggest you report it here.

Paul Creasey
+7  A: 

This looks like a serious bug...

Note that it works fine if you use an ExpandoObject instead of an anonymous type :

using System;
using System.Dynamic;

class Program {
  public void Baz() { Console.WriteLine("Baz1"); }
  static void CallBaz(dynamic x) { x.Baz(); }

  static void Main(string[] args) {
    dynamic a = new Program();
    dynamic b = new ExpandoObject();
    b.Baz = new Action(() => Console.WriteLine("Baz2"));

    CallBaz(a); // ok
    CallBaz(b); // ok
    CallBaz(a); // ok
  }
}

So the issue seems specific to anonymous objects...

Apparently, in the second call to CallBaz(a), the DLR still tries to access Baz as a property, because it was a property on the anonymous type. I suspect that the C# binder does some caching of call resolution for better performance, but in that case it's clearly broken...

Thomas Levesque
Yeah, I think so too, it's an caching problem...
ControlFlow
+11  A: 

Looks suspicious. I'll send it off to testing and we'll see what they say.

Just to set expectations: if this is a bug, and it hasn't been found and fixed already, odds are good a fix will not get into the final release.

Thanks for bringing this to our attention!

Eric Lippert
+23  A: 

I can confirm that this is indeed a bug. The quick description of what's going wrong here is as follows: In CallBaz, there is a single callsite that is invoked three times. That callsite is an InvokeMember, because that's the best guess the compiler can make given the C# syntax, despite that it could, in actuality, resolve to a GetMember followed by an Invoke.

During the second execution of the callsite, this is indeed the binding that the runtime finds. And so it produces a deferral to a GetMember followed by an invoke. The bug is that this deferral does not properly restrict itself to the case where the argument is the anonymous type. Therefore, in the third execution the deferral kicks in and the GetMember tries to bind to Program, which of course fails.

Thanks for finding this. As Eric points out, we're in a very late stage here, and it's becoming difficult to fix issues before we ship. But we also want to ship the right product. I'm going to do what I can to get this resolved, though I may not succeed. If you come up with anything else, please feel free to contact me. =)

UPDATE:

Although I can make no guarantee what the final version of VS 2010 and C# 4 will look like when it ships, I can say that I was successful in pushing this fix through. Today's release escrow build behaves correctly for your code. Barring some catastrophe, you will see this fixed at release. Thanks again. I owe you a beer.

Chris Burrows
Nice, thank you, Chris!
ControlFlow
Wow, that was quick... well done, that's very good news !
Thomas Levesque