DynamicmetaObject.bindinVokemember의 결과로 무효 메소드 호출을 어떻게 표현합니까?

StackOverflow https://stackoverflow.com/questions/1835912

문제

나는 짧은 예를 제시하려고합니다 IDynamicMetaObjectProvider C#의 두 번째 판의 경우 깊이 있는데 문제가 발생합니다.

공허 전화를 표현할 수 있고 실패합니다. 반사 바인더를 사용하여 빈 공간 메소드를 동적으로 호출하면 괜찮습니다. 짧지 만 완전한 예는 다음과 같습니다.

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'Binder 'microsoft.csharp.runtimebinder.csharpinvokememberBinder'에 대한 유형의 'DynamicDemo'가있는 객체가 생성 한 동적 바인딩의 'System.Void'는 결과 유형 '시스템과 호환되지 않습니다. 객체 '통화 사이트에 의해 예상됩니다.

객체를 반환하고 null을 반환하도록 메소드를 변경하면 잘 작동합니다 ... 결과가 null이되기를 원하지 않습니다. 나는 그것이 무효가되기를 원합니다. 그것은 반사 바인더에 적합하지만 (메인의 첫 번째 호출 참조) 내 동적 객체에는 실패합니다. 나는 그것이 반사 바인더처럼 작동하기를 원합니다. 결과를 사용하지 않는 한 방법을 호출하는 것은 괜찮습니다.

목표로 사용할 수있는 특정한 종류의 표현을 놓쳤습니까?

도움이 되었습니까?

해결책

이것은 다음과 유사합니다.

DLR 리턴 유형

당신은 ReturnType 재산. 모든 표준 바이너리의 경우 이것은 거의 모든 것 또는 무효 (삭제 연산의 경우)에 대한 객체에 고정됩니다. 당신이 공허 전화를하고 있다는 것을 알고 있다면 다음을 마무리하는 것이 좋습니다.

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

DLR은 허용 할 내용에 대해 상당히 느슨했으며 자동으로 최소한의 강요를 제공 할 것입니다. 우리는 각 언어에 대해 의미가 있거나 그렇지 않을 수도있는 일련의 컨벤션을 제공하고 싶지 않았기 때문에 그것을 제거했습니다.

예방하고 싶은 것 같습니다.

dynamic x = obj.SomeMember();

그렇게 할 방법이 없습니다. 사용자가 동적으로 계속 상호 작용하려고 시도 할 수 있다는 값이 항상 반환됩니다.

다른 팁

나는 이것을 좋아하지 않지만 효과가있는 것 같습니다. 실제 문제는 인 것 같습니다 binder.ReturnType 이상하게 오는 것 (그리고 삭제되지 않음 ( "pop") 자동으로).

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

아마도 Callite는 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
}

생각할 거리...

업데이트:

더 많은 힌트는 Debugger를위한 시각화로 사용되는 (I Posume)에서 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);

... 나는 여기에 반사판이 끝나지만이 코드의 프레임은 tryevalmethodvarargs 자체가 객체를 반환 유형으로 기대하고 최종 줄은 동적 호출 결과를 반환하기 때문에이 코드의 프레임이 약간 이상하게 보입니다. . 나는 아마도 잘못된 [표현] 나무를 짖을 것입니다.

-이신

C# 바인더 (Microsoft.csharp.dll)는 결과가 사용되는지 여부를 알고 있습니다. X0N (+1)이 말했듯이 플래그로 추적합니다. 불행히도, 깃발은 a 안에 묻혀 있습니다 CSharpInvokeMemberBinder 개인 유형입니다.

C# 바인딩 메커니즘이 사용하는 것처럼 보입니다 ICSharpInvokeOrInvokeMemberBinder.ResultDiscarded (내부 인터페이스의 속성)를 읽으려면; CSharpInvokeMemberBinder 인터페이스 (및 속성)를 구현합니다. 작업이 완료된 것 같습니다 Microsoft.CSharp.RuntimeBinder.BinderHelper.ConvertResult(). 이 방법에는 앞서 언급 한 경우 던지는 코드가 있습니다 ResultDiscarded 표현식 유형이 무효 인 경우 속성이 True를 반환하지 않습니다.

따라서 표현식의 결과가 적어도 베타 2에서 C# 바인더에서 삭제된다는 사실을 어지럽히는 쉬운 방법이있는 것처럼 보이지 않습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top