Comment puis-je exprimer un appel de méthode vide à la suite de DynamicMetaObject.BindInvokeMember?

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

Question

Je suis en train de donner un court exemple IDynamicMetaObjectProvider pour la deuxième édition de C # en profondeur, et je suis en cours d'exécution sur les questions.

Je veux être en mesure d'exprimer un appel vide, et je ne. Je suis sûr qu'il est possible, parce que si j'appelle dynamiquement une méthode vide en utilisant le liant de réflexion, tout va bien. Voici un exemple bref mais complet:

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

lance une exception:

  

Exception non gérée:   System.InvalidCastException: Le   type de résultat « System.Void » du   liaison dynamique produit par l'objet   de type « DynamicDemo » pour le liant   'Microsoft.CSharp.RuntimeBinder.CSharpInvokeMemberBinder'   est pas compatible avec le type de résultat « System.Object » attendu par le   le site appel.

Si je change la méthode de retourner l'objet et retourner null, il fonctionne très bien ... mais je ne veux pas que le résultat soit nul, je veux que ce soit vide. Cela fonctionne très bien pour le liant de réflexion (voir le premier appel en principal) mais il échoue pour mon objet dynamique. Je veux que cela fonctionne comme le liant de réflexion -. Il est bien d'appeler la méthode, tant que vous ne cherchez pas à utiliser le résultat

Ai-je manqué un genre particulier d'expression que je peux utiliser comme cible?

Était-ce utile?

La solution

Ceci est similaire à:

DLR type de retour

Vous avez besoin de correspondre au type de rendement spécifié par la propriété ReturnType. Pour tous les binaires standards ceci est fixé à l'objet pour presque tout ou vide (pour les opérations de suppression). Si vous savez que vous faites un appel vide que je vous suggère de l'envelopper dans:

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

Le DLR utilisé pour être assez laxiste sur ce qu'il permettrait et fournirait une certaine quantité minimum de coercition automatiquement. Nous nous sommes débarrassés de cela parce que nous ne voulions pas fournir un ensemble de convensions qui peut ou non avoir un sens pour chaque langue.

On dirait que vous voulez éviter:

dynamic x = obj.SomeMember();

Il n'y a aucun moyen de le faire, il y aura toujours une valeur retournée que l'utilisateur peut tenter de continuer à interagir avec dynamiquement.

Autres conseils

Je ne l'aime pas, mais il semble fonctionner; le vrai problème semble être le binder.ReturnType venir dans curieusement (et ne pas être abandonné ( « pop ») automatiquement), mais:

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

Peut-être le callsite attend nulle à retourner, mais le résultat défausse - Ce ENUM semble intéressant, en particulier le « ResultDiscarded » drapeau ...

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

Mise à jour:

Plus de conseils peuvent être tirées de Microsoft / CSharp / RuntimeBinder / DynamicMetaObjectProviderDebugView qui est utilisé (je suppose) comme Visualiseur pour débogueurs. Le procédé TryEvalMethodVarArgs examine le délégué et crée un liant avec le résultat drapeau mis au rebut (???)

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

... Je suis à la fin de mon réflecteur-foo, mais le cadrage de ce code semble un peu bizarre car la méthode elle-même TryEvalMethodVarArgs attend un objet comme un type de retour, et la dernière ligne renvoie le résultat de l'invocation dynamique. J'aboie sans doute le mal [expression] arbre.

-Oisin

Le liant C # (en Microsoft.CSharp.dll) sait si le résultat est utilisé ou non; comme x0n (+1) dit, il garde la trace dans un drapeau. Malheureusement, le drapeau est enterré dans une instance de CSharpInvokeMemberBinder, qui est un type privé.

Il semble que le mécanisme de liaison C # utilise ICSharpInvokeOrInvokeMemberBinder.ResultDiscarded (une propriété sur une interface interne) pour la lire; CSharpInvokeMemberBinder implémente l'interface (et la propriété). Le travail semble être fait dans Microsoft.CSharp.RuntimeBinder.BinderHelper.ConvertResult(). Cette méthode a un code qui jette si la propriété ResultDiscarded mentionnée ci-dessus ne retourne pas vrai si le type de l'expression est nulle.

Il ne semble pas à moi comme il y a un moyen facile de démêler le fait que le résultat de l'expression est tombé du liant C #, dans la version bêta 2 au moins.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top