我想举一个简短的例子 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
    }
}

这会引发异常:

未处理的异常:System.InvalidCastException:对象产生的动态绑定的结果类型“ system.void”,带有类型为“ dynamicdemo”的粘合剂'microsoft.csharp.csharp.csharp.runtimebinder.csharpinvokemembinder.csharpinvokememberd' 。

如果我将方法更改为返回对象并返回 null,它就可以正常工作......但我不希望结果为空,我希望它为空。这对于反射绑定器来说效果很好(请参阅 Main 中的第一个调用),但对于我的动态对象来说却失败了。我希望它像反射绑定器一样工作 - 调用该方法就可以,只要您不尝试使用结果即可。

我是否错过了可以用作目标的特定表达方式?

有帮助吗?

解决方案

这是类似于:

DLR返回类型

您确实需要匹配由ReturnType属性指定的返回类型。对于所有的标准二进制文件,这是固定对象,用于几乎所有的或无效(对于删除操作)。如果你知道你正在做一个空白电话,我建议在它包装:

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

在DLR曾经是相当松懈什么它将允许,它会自动提供强制的一些少量的。我们摆脱了,因为我们不想提供一组可能会或可能并没有意义,各语言convensions的。

这听起来像你想阻止:

dynamic x = obj.SomeMember();

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

其他提示

我不喜欢这一点,但它似乎工作;真正的问题似乎是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);

也许调用点希望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其中使用(I假定)作为调试器可视化工具中收集。该方法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);

...我在我的反射,FOO这里结束,但是这个代码的框架似乎有点奇怪,因为TryEvalMethodVarArgs方法本身期望的对象作为返回类型,并最终行返回的结果动态调用。我可能找错[表达式]树。

-Oisin

在C#粘合剂(在Microsoft.CSharp.dll)知道结果是否被使用;作为x0n(+1)表示,它跟踪它的标志。不幸的是,该标志被埋入一个CSharpInvokeMemberBinder实例,这是一个私有类型的内部。

它看起来像C#结合机构使用ICSharpInvokeOrInvokeMemberBinder.ResultDiscarded(内部接口上的属性)来读取它; CSharpInvokeMemberBinder实现接口(和属性)。该作业将出现在Microsoft.CSharp.RuntimeBinder.BinderHelper.ConvertResult()工作要做。该方法具有将引发如果表达式的类型是空隙上述ResultDiscarded属性不返回真实代码。

因此,它不看我像有梳理出该表达式的结果从C#粘结剂下降的事实,一个简单的方法,在Beta 2中至少

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top