Jon*_*eet 56 dynamic-language-runtime dynamic .net-4.0 c#-4.0
我试图IDynamicMetaObjectProvider
在深度中给出第二版C#的简短例子,我遇到了问题.
我希望能够表达一个无效的电话,而我却失败了.我确信这是可能的,因为如果我使用反射活页夹动态调用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
}
}
Run Code Online (Sandbox Code Playgroud)
这引发了一个异常:
未处理的异常:System.InvalidCastException:绑定器"Microsoft.CSharp.RuntimeBinder.CSharpInvokeMemberBinder"类型为"DynamicDemo"的对象生成的动态绑定的结果类型"System.Void"与结果类型"System"不兼容.呼叫站点预期的对象'.
如果我改变方法返回对象并返回null,它工作正常...但我不希望结果为null,我希望它是无效的.这适用于反射绑定器(请参阅Main中的第一个调用)但它对我的动态对象失败.我希望它像反射绑定器一样工作 - 调用方法是好的,只要你不尝试使用结果.
我是否错过了一种我可以用作目标的特定表达方式?
Din*_*and 26
这类似于:
您需要匹配ReturnType
属性指定的返回类型.对于所有标准二进制文件,这几乎被修复为对象或无效(对于删除操作).如果你知道你正在进行无效呼叫,我建议将其包含在:
Expression.Block(
call,
Expression.Default(typeof(object))
);
Run Code Online (Sandbox Code Playgroud)
DLR过去对它允许的内容非常宽松,它会自动提供一些最小量的强制.我们摆脱了这一点,因为我们不想提供一套对每种语言都有意义或可能没有意义的对话.
听起来你想要阻止:
dynamic x = obj.SomeMember();
Run Code Online (Sandbox Code Playgroud)
没有办法做到这一点,总会有一个返回值,用户可以尝试继续动态交互.
Mar*_*ell 11
我不喜欢这样,但似乎有效; 真正的问题似乎是binder.ReturnType
奇怪地进入(而不是自动丢弃("弹出")),但是:
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);
Run Code Online (Sandbox Code Playgroud)
也许callsite期望返回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
}
Run Code Online (Sandbox Code Playgroud)
值得思考的东西......
更新:
可以从Microsoft/CSharp/RuntimeBinder/DynamicMetaObjectProviderDebugView中收集更多提示,使用(我推测)作为调试器的可视化工具.方法TryEvalMethodVarArgs检查委托并创建一个带有结果丢弃标志的绑定器(???)
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);
Run Code Online (Sandbox Code Playgroud)
...我在这里的Reflector-foo的末尾,但是这个代码的框架似乎有点奇怪,因为TryEvalMethodVarArgs方法本身需要一个对象作为返回类型,最后一行返回动态调用的结果.我可能正在咆哮错误的[表达]树.
-Oisin
C#binder(在Microsoft.CSharp.dll中)知道结果是否被使用; 正如x0n(+1)所说,它在一个标志中跟踪它.不幸的是,该标志被隐藏在一个CSharpInvokeMemberBinder
实例中,这是一个私有类型.
看起来C#绑定机制使用ICSharpInvokeOrInvokeMemberBinder.ResultDiscarded
(内部接口上的属性)来读取它; CSharpInvokeMemberBinder
实现接口(和属性).这项工作似乎已经完成了Microsoft.CSharp.RuntimeBinder.BinderHelper.ConvertResult()
.ResultDiscarded
如果表达式的类型为void ,则如果上述属性不返回true,则该方法具有抛出的代码.
所以我不认为有一种简单的方法可以理解表达式的结果至少从Beta 2中的C#binder中删除.
归档时间: |
|
查看次数: |
3823 次 |
最近记录: |