C#4.0中的dynamic关键字有问题吗?

con*_*low 50 c# caching dynamic

C#4.0动态使用有一些奇怪的行为:

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
  }
}
Run Code Online (Sandbox Code Playgroud)

我正在使用Visual Studio 2010 Release Candidate.

这是一个错误吗?如果是真的,它会在Release中修复吗?

Chr*_*ows 32

我可以确认这确实是一个错误.这里出现问题的快速描述如下:在CallBaz中,有一个调用三次的调用点.该callsite是一个InvokeMember,因为这是编译器在给定C#语法时可以做出的最佳猜测,尽管它实际上可以解析为GetMember,然后是Invoke.

在第二次执行调用站点期间,这确实是运行时找到的绑定.因此它会产生一个GetMember的延迟,然后是一个调用.问题是这种推迟不能正确地将自己限制在参数是匿名类型的情况下.因此,在第三次执行中,推迟启动并且GetMember尝试绑定到Program,这当然会失败.

谢谢你找到了这个.正如埃里克指出的那样,我们现在处于非常晚期阶段,在我们出货之前解决问题变得越来越困难.但我们也想发运正确的产品.尽管我可能没有成功,但我会尽我所能来解决这个问题.如果您想出其他任何事情,请随时与我联系.=)

更新:

虽然我无法保证VS 2010和C#4的最终版本在发布时会是什么样子,但我可以说我成功地推动了这个修复.今天的发布托管版本对您的代码运行正常.除非发生一些灾难,否则你会在发布时看到这个问题.再次感谢.我欠你一杯啤酒.


Eri*_*ert 11

看起来很可疑.我会发送它进行测试,我们会看到他们说的话.

只是为了设定期望:如果这是一个错误,并且它还没有找到并修复,那么赔率很高,修复不会进入最终版本.

谢谢让我们注意到这个!


Tho*_*que 8

这看起来像一个严重的错误......

请注意,如果您使用的ExpandoObject是匿名类型,它可以正常工作:

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
  }
}
Run Code Online (Sandbox Code Playgroud)

所以问题似乎特定于匿名对象......

显然,在第二次调用中CallBaz(a),DLR仍然尝试Baz作为属性进行访问,因为它是匿名类型的属性.我怀疑C#binder对呼叫解决方案进行了一些缓存,以获得更好的性能,但在这种情况下,它显然已被打破......