Frage

Ich versuche, ein kurzes Beispiel zu geben, IDynamicMetaObjectProvider für die zweite Ausgabe von C # in der Tiefe, und ich laufe in Probleme.

Ich möchte in der Lage, einen Leerruf zum Ausdruck bringen, und ich versagt. Ich bin sicher, es ist möglich, denn wenn ich dynamisch eine void-Methode unter Verwendung des Reflexions Bindemittel nennt, ist alles in Ordnung. Hier ist ein kurzes, aber vollständiges Beispiel:

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

Dies löst eine Ausnahme:

  

Unbehandelte Ausnahme:   System.InvalidCastException: Die   result type 'System.Void' der   dynamisches Binden durch das Objekt erzeugten   mit Typ ‚DynamicDemo‘ für das Bindemittel   'Microsoft.CSharp.RuntimeBinder.CSharpInvokeMemberBinder'   ist mit dem Ergebnistyp ‚System.Object‘ durch die erwarteten nicht kompatibel   Aufrufort.

Wenn ich die Methode ändern Objekt zurückzukehren und null zurück, es funktioniert gut ... aber ich will nicht das Ergebnis null sein, ich will es nichtig. Das funktioniert gut für die Reflexion Bindemittel (siehe den ersten Anruf in Main) aber es funktioniert nicht für mein dynamisches Objekt. Ich will es wie die Reflexion Bindemittel arbeiten -. Es ist in Ordnung, die Methode zu nennen, solange Sie nicht versuchen, das Ergebnis zu verwenden

Habe ich eine bestimmte Art von Ausdruck vermisste ich als Ziel verwenden kann?

War es hilfreich?

Lösung

Dies ist ähnlich:

DLR Rückgabetyp

Sie müssen den Rückgabetyp der ReturnType Eigenschaft angegebenen übereinstimmen. Für alle Standard-Binärdateien dies festgelegt ist für fast alles oder void (für die Löschvorgänge) zu widersprechen. Wenn Sie wissen, Sie machen einen Leerruf ich würde vorschlagen, Einwickeln es in:

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

Das DLR verwendete ziemlich lax zu sein, was es erlauben würde, und es würde eine minimale Menge an Zwang automatisch bereitzustellen. Wir haben uns davon befreien, weil wir eine Reihe von convensions bieten wollte nicht, dass die für jede Sprache Sinn haben kann oder auch nicht gemacht.

Es klingt wie Sie verhindern wollen:

dynamic x = obj.SomeMember();

Es gibt keine Möglichkeit, das zu tun, gibt es immer einen Wert zurückgeführt sein wird, dass der Benutzer mit dynamisch zu interagieren, um weiterhin versuchen kann.

Andere Tipps

Ich mag das nicht, aber es scheint zu funktionieren; das eigentliche Problem scheint seltsam das binder.ReturnType kommt in zu sein (und nicht fallen gelassen werden ( „Pop“) automatisch), aber:

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

Vielleicht erwartet die callsite null zurückgegeben werden, sondern verwirft das Ergebnis - diese Enum sieht interessant, vor allem der „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
}

Zum Nachdenken ...

UPDATE:

Weitere Hinweise können von Microsoft / CSharp / RuntimeBinder / DynamicMetaObjectProviderDebugView entnommen werden, die verwendet wird (nehme ich an) als Visualisierer für Debugger. Das Verfahren untersucht die TryEvalMethodVarArgs delegieren und erzeugt ein Bindemittel, mit dem Ergebnis verworfen 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);

... Ich bin am Ende meiner Reflector-foo hier, aber die Gestaltung dieses Code scheint ein wenig seltsam, da die TryEvalMethodVarArgs Methode selbst ein Objekt als Rückgabetyp erwartet, und die letzte Zeile gibt das Ergebnis der die dynamische invoke. Ich bin wahrscheinlich den falschen [Ausdruck] Holzweg.

-Oisin

Die C # Bindemittel (in Microsoft.CSharp.dll) weiß, ob oder nicht das Ergebnis verwendet wird; als X0n (+1) sagt, verfolgt es sich in einer Flagge. Leider wird die Flagge in einer CSharpInvokeMemberBinder Instanz begraben, die ein privater Typ ist.

Es sieht aus wie die C # Bindungsmechanismus verwendet ICSharpInvokeOrInvokeMemberBinder.ResultDiscarded (eine Eigenschaft auf eine interne Schnittstelle) es auszulesen; CSharpInvokeMemberBinder implementiert die Schnittstelle (und Eigentum). Der Job wird in Microsoft.CSharp.RuntimeBinder.BinderHelper.ConvertResult() getan zu werden. Diese Methode hat Code, wenn die vorgenannte ResultDiscarded Eigenschaft wirft nicht wahr zurück, wenn der Typ des Ausdrucks ist nichtig.

So ist es mir nicht aussehen, als gäbe es eine einfache Möglichkeit, um die Tatsache zu necken, dass das Ergebnis des Ausdrucks aus dem C # Bindemittel fallen gelassen wird, in Beta 2 zumindest.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top