DynamicmetaObject.bindinVokemember의 결과로 무효 메소드 호출을 어떻게 표현합니까?
-
11-09-2019 - |
문제
나는 짧은 예를 제시하려고합니다 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이되기를 원하지 않습니다. 나는 그것이 무효가되기를 원합니다. 그것은 반사 바인더에 적합하지만 (메인의 첫 번째 호출 참조) 내 동적 객체에는 실패합니다. 나는 그것이 반사 바인더처럼 작동하기를 원합니다. 결과를 사용하지 않는 한 방법을 호출하는 것은 괜찮습니다.
목표로 사용할 수있는 특정한 종류의 표현을 놓쳤습니까?
해결책
이것은 다음과 유사합니다.
당신은 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# 바인더에서 삭제된다는 사실을 어지럽히는 쉬운 방법이있는 것처럼 보이지 않습니다.