如何将Dynamic方法调用作为DynamicMetaObject.BindInvokeMember的结果来表示?

我正在尝试深入介绍C#的第二版IDynamicMetaObjectProvider一个简短的例子,我遇到了问题。

我想能够expression一个无效的电话,而我失败了。 我敢肯定,这是可能的,因为如果我dynamic调用使用reflection粘合剂的void方法,一切都很好。 这里有一个简短而完整的例子:

 using System; using System.Dynamic; using System.Linq.Expressions; class DynamicDemo : IDynamicMetaObjectProvider { public DynamicMetaObject GetMetaObject(Expression expression) { return new MetaDemo(expression, this); } public void TestMethod(string name) { Console.WriteLine(name); } } class MetaDemo : DynamicMetaObject { internal MetaDemo(Expression expression, DynamicDemo demo) : base(expression, BindingRestrictions.Empty, demo) { } public override DynamicMetaObject BindInvokeMember (InvokeMemberBinder binder, DynamicMetaObject[] args) { Expression self = this.Expression; Expression target = Expression.Call (Expression.Convert(self, typeof(DynamicDemo)), typeof(DynamicDemo).GetMethod("TestMethod"), Expression.Constant(binder.Name)); var restrictions = BindingRestrictions.GetTypeRestriction (self, typeof(DynamicDemo)); return new DynamicMetaObject(target, restrictions); } } class Test { public void Foo() { } static void Main() { dynamic x = new Test(); x.Foo(); // Works fine! x = new DynamicDemo(); x.Foo(); // Throws } } 

这引发了一个exception:

未处理的exception:System.InvalidCastException:types为“DynamicDemo”的对象生成的用于绑定器“Microsoft.CSharp.RuntimeBinder.CSharpInvokeMemberBinder”的dynamic绑定的结果types“System.Void”与结果types“System”不兼容。对象“的呼叫网站预期。

如果我改变方法返回对象并返回null,它工作正常…但我不希望结果为空,我希望它是无效的。 对于reflection联编程序(对于Main中的第一个调用)来说工作正常,但是对于我的dynamic对象却失败了。 我希望它像reflection联编程序一样工作 – 只要您不尝试使用结果就可以调用方法。

我错过了一个我可以用作目标的特殊expression方式吗?

这类似于:

DLR返回types

您需要匹配由ReturnType属性指定的返回types。 对于所有的标准二进制文件,这个对象几乎全部或者无效(对于删除操作)。 如果你知道你正在做一个无效的电话,我build议把它包装在:

 Expression.Block( call, Expression.Default(typeof(object)) ); 

DLR过去一直很松懈,它会自动提供一些最低限度的强制。 我们摆脱了这一点,因为我们不想提供一套对每种语言都有意义或可能没有意义的对照。

这听起来像你想要阻止:

 dynamic x = obj.SomeMember(); 

没有办法做到这一点,总会有一个值返回,用户可以尝试继续dynamic交互。

我不喜欢这个,但似乎有效; 真正的问题似乎是binder.ReturnType来奇怪(而不是被自动删除(“stream行”)),但:

 if (target.Type != binder.ReturnType) { if (target.Type == typeof(void)) { target = Expression.Block(target, Expression.Default(binder.ReturnType)); } else if (binder.ReturnType == typeof(void)) { target = Expression.Block(target, Expression.Empty()); } else { target = Expression.Convert(target, binder.ReturnType); } } return new DynamicMetaObject(target, restrictions); 

也许该调用网站期望返回null,但放弃结果 – 这枚举看起来很有趣,尤其是“ResultDiscarded”标志…

 [Flags, EditorBrowsable(EditorBrowsableState.Never)] public enum CSharpBinderFlags { BinaryOperationLogical = 8, CheckedContext = 1, ConvertArrayIndex = 0x20, ConvertExplicit = 0x10, InvokeSimpleName = 2, InvokeSpecialName = 4, None = 0, ResultDiscarded = 0x100, ResultIndexed = 0x40, ValueFromCompoundAssignment = 0x80 } 

食物的思想…

更新:

从Microsoft / CSharp / RuntimeBinder / DynamicMetaObjectProviderDebugView可以收集到更多的提示,这些提示可以用作debugging器的可视化工具。 TryEvalMethodVarArgs方法检查委托并创build一个结果放弃标志(???)

  Type delegateType = Expression.GetDelegateType(list.ToArray()); if (string.IsNullOrEmpty(name)) { binder = new CSharpInvokeBinder(CSharpCallFlags.ResultDiscarded, AccessibilityContext, list2.ToArray()); } else { binder = new CSharpInvokeMemberBinder(CSharpCallFlags.ResultDiscarded, name, AccessibilityContext, types, list2.ToArray()); } CallSite site = CallSite.Create(delegateType, binder); 

…我在这里的Reflector-foo的末尾,但是这个代码的框架似乎有点奇怪,因为TryEvalMethodVarArgs方法本身期望一个对象作为返回types,最后一行返回dynamic调用的结果。 我可能会叫出错误的表情树。

-Oisin

C#联编程序(Microsoft.CSharp.dll中)知道是否使用结果; 正如x0n(+1)所说的那样,它会在一个标志中跟踪它。 不幸的是,这个标志被隐藏在一个私有types的CSharpInvokeMemberBinder实例中。

它看起来像C#绑定机制使用ICSharpInvokeOrInvokeMemberBinder.ResultDiscarded (内部接口上的属性)来读取它; CSharpInvokeMemberBinder实现接口(和属性)。 这个工作似乎是在Microsoft.CSharp.RuntimeBinder.BinderHelper.ConvertResult() 。 如果expression式的types为void,那么如果前面提到的ResultDiscarded属性没有返回true,那么该方法就会抛出代码。

因此,在我看来,有一种简单的方法可以清除expression式的结果至less在Beta 2中从C#活页夹中删除的事实。