How do I express a void method call as the result of DynamicMetaObject.BindInvokeMember?

前端 未结 4 1783
栀梦
栀梦 2021-01-31 01:47

I\'m trying to give a short example of IDynamicMetaObjectProvider for the second edition of C# in Depth, and I\'m running into issues.

I want to be able to express a voi

相关标签:
4条回答
  • 2021-01-31 02:13

    The C# binder (in Microsoft.CSharp.dll) knows whether or not the result is used; as x0n (+1) says, it keeps track of it in a flag. Unfortunately, the flag is buried inside a CSharpInvokeMemberBinder instance, which is a private type.

    It looks like the C# binding mechanism uses ICSharpInvokeOrInvokeMemberBinder.ResultDiscarded (a property on an internal interface) to read it out; CSharpInvokeMemberBinder implements the interface (and property). The job appears to be done in Microsoft.CSharp.RuntimeBinder.BinderHelper.ConvertResult(). That method has code that throws if the aforementioned ResultDiscarded property doesn't return true if the type of the expression is void.

    So it doesn't look to me like there's an easy way to tease out the fact that the result of the expression is dropped from the C# binder, in Beta 2 at least.

    0 讨论(0)
  • 2021-01-31 02:16

    This is similar to:

    DLR return type

    You do need to match the return type specified by the ReturnType property. For all of the standard binaries this is fixed to object for almost everything or void (for the deletion operations). If you know you're making a void call I'd suggest wrapping it in:

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

    The DLR used to be quite lax about what it would allow and it would provide some minimal amount of coercion automatically. We got rid of that because we didn't want to provide a set of convensions which may or may not have made sense for each language.

    It sounds like you want to prevent:

    dynamic x = obj.SomeMember();
    

    There's no way to do that, there'll always be a value returned that the user can attempt to continue to interact with dynamically.

    0 讨论(0)
  • 2021-01-31 02:23

    I don't like this, but it seems to work; the real problem seems to be the binder.ReturnType coming in oddly (and not being dropped ("pop") automatically), but:

    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);
    
    0 讨论(0)
  • 2021-01-31 02:28

    Perhaps the callsite expects null to be returned but discards the result - This enum looks interesting, particularly the "ResultDiscarded" flag...

    [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
    }
    

    Food for thought...

    UPDATE:

    More hints can be gleaned from Microsoft / CSharp / RuntimeBinder / DynamicMetaObjectProviderDebugView which is used (I presume) as a visualizer for debuggers. The method TryEvalMethodVarArgs examines the delegate and creates a binder with the result discarded flag (???)

     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);
    

    ... I'm at the end of my Reflector-foo here, but the framing of this code seems a bit odd since the TryEvalMethodVarArgs method itself expects an object as a return type, and the final line returns the result of the dynamic invoke. I'm probably barking up the wrong [expression] tree.

    -Oisin

    0 讨论(0)
提交回复
热议问题