Expression > - Obtenir l'instance d'objet appelant de Property sans Compile ()

c# expression-trees lambda

Question

EDIT: Exemple mis à jour pour ce que je suis en train de faire.

EDIT 2: Le pourquoi:
Ma classe "s'empare" d'un créateur de propriétés (ou d'une méthode, anonyme ou non) et le "contrôle" pendant un certain temps. L'utilisateur peut essayer d'attacher une autre instance de ma classe et l'attacher à une propriété déjà "contrôlée". J'ai besoin d'un moyen de détecter ces conflits de manière fiable. Je pourrais renoncer à la méthode () => {propriété}, mais cela oblige l'utilisateur à envelopper la propriété cible dans une méthode et, s'il souhaite la détection de conflit, à transmettre l'objet contenant la propriété. Le faire de cette façon introduit un risque plus élevé de bugs car ils peuvent taper le mauvais objet et la classe n’aurait aucun moyen de les vérifier.

Je ne parviens pas à utiliser Lambda Compile () car je cible AOT en plus de JIT.


J'ai une méthode qui prend un

Expression<Func<T>> 

comme argument. L'expression est toujours évaluée comme une propriété avec un paramètre de type T.

private static TargetInfo GetTargetInfo<T>(Expression<Func<T>> _propertyExpression, out Action<T> _setter)
{
    //Get Property info
    var propInfo = ((MemberExpression)_propertyExpression.Body).Member as PropertyInfo;
    if (propInfo == null)
        throw new ArgumentException("_propertyExpression must be a property.");


    var declType = propInfo.DeclaringType;
    var methodInfo = propInfo.GetSetMethod(true) ?? declType.GetProperty(propInfo.Name).GetSetMethod(true);
    if (methodInfo == null)
        throw new Exception("Could not create setter from property '" + _propertyExpression + "'");
    var isStaticProp = methodInfo.IsStatic;

    //Get Target Object
    object targetObject = null;
    var memberExp = _propertyExpression.Body as MemberExpression;
    var bodyExp = memberExp.Expression;
    if (bodyExp != null)
// PROBLEM LINE BELOW - Can't use Compile()  *********************************
        targetObject = Expression.Lambda<Func<object>>(bodyExp).Compile()();   
// PROBLEM LINE ABOVE  *******************************************************
    else if (isStaticProp)
        targetObject = memberExp.Member.DeclaringType;
    else
        throw new Exception("Could not determine target object.  Use Action<T> overload. (no conflict detection)");


    //Cache setter of property
    if (isStaticProp)
        _setter = (Action<T>) Delegate.CreateDelegate(typeof (Action<T>), methodInfo);
    else
        _setter = (Action<T>)Delegate.CreateDelegate(typeof(Action<T>), targetObject, methodInfo);

    return new TargetInfo(methodInfo, targetObject);
}

L'expression (_propertyExpression) est toujours une propriété. Et la propriété a toujours un passeur.

  • Je dois extraire le setter de la propriété en tant qu'Action (ce qui ne me pose aucun problème)
  • J'ai également besoin de l'instance de l'objet contenant cette propriété sans utiliser Compile () car elle n'est pas disponible sur certaines des plates-formes cibles que je cible.

La raison pour laquelle j'ai besoin de l'instance est de déterminer si une autre instance d'objet de ma classe utilise actuellement cette propriété SETTER sur cet objet. (Pas de filetage, juste une utilisation sur une période de temps) Je stocke l'objet ensemble L'information d'instance et de définition de propriété sous forme de clé. Si deux clés égales existent, il existe un conflit qui est géré en fonction des paramètres de configuration de l'utilisateur. Ma classe.

Je peux obtenir l'instance d'objet pour des expressions simples telles que:

() => MyProperty

ou

() => MyObjectInst.PropertyFoo

ou

() => MyStaticClass.PropertyFooBar

Pas de transpiration!

Mais que faire si l'utilisateur fait ceci:

//Need instance of 'someObject' here.
() => SomeArray[idx].someObject.propertyA

ou sauter mon sommet:

//Need instance of 'someField[4]' here
() => SomeStaticClass.somePropertyList[5].SomeDictionary[objKey].SomeProperty.someFieldArray[4].propertyB

AAAK!

Je regarde cela depuis un moment et je crois que je dois traverser l’arbre RETOUR à l’objet de base (ConstantExpression), puis commencer à évaluer l’arrière de l’arbre en utilisant les informations collectées en marchant vers l’avant. Au vu des exemples ci-dessus, j'ai décidé qu'il me fallait des "armes plus grosses".

  1. Existe-t-il un moyen plus simple d'obtenir l'instance de l'appelant de la propriété, autrement que de parcourir complètement l'arborescence SANS utiliser Compile ()? S'il vous plaît expliquer si oui!

  2. Si non 1. Si quelqu'un pouvait me diriger dans la bonne direction sur la façon de traverser l'arbre et de gérer des choses comme ci-dessus. Ce serait génial. Je ne trouve pas beaucoup d'informations sur la traversée des arbres d'expression, des types d'expression, etc. Même une référence à un livre recommandé spécifique au sujet serait utile.

  3. soupir Devrais-je simplement abandonner et obliger l'utilisateur à utiliser des expressions simples. c'est à dire. Ne laissez pas l'utilisateur taper de longues lignes d'accès à la propriété.

Clarification: le séparateur est mis en cache pour être utilisé par une classe de mécanismes "FOO" qui définira cette valeur comme une plage INCONNU de valeurs de type T sur une période de temps définie. L'instance d'objet est mise en cache et utilisée en tant que clé pour détecter le cas qu'une autre instance de FOO tente d'attacher et de définir des valeurs pour la même propriété sur le même objet. L'instance d'objet est utilisée pour détecter ces conflits et également pour garder une trace de tous les "FOO" attachés à un objet particulier. L'utilisateur peut attacher plusieurs "FOO" à un même objet, à des propriétés différentes. Dans le cas où l'utilisateur attache une instance de FOO à une propriété sur un objet auquel un FOO est déjà attaché pour cette propriété, un événement conflictuel se produit. FOO peut être configuré sur la façon de gérer lorsque cela se produit.

Réponse populaire

Voici les problèmes avec vos approches suggérées:

  1. Existe-t-il un moyen plus simple d'obtenir l'instance de l'appelant de la propriété, autrement que de parcourir complètement l'arborescence SANS utiliser Compile ()? S'il vous plaît expliquer si oui!

Réponse courte, non. Les expressions sont destinées à être compilées. Soit dans des Funcs / Actions .NET ou une autre plateforme (LINQ-to-Entities les compile essentiellement en SQL). Si vous ne voulez pas le compiler, vous utilisez probablement le mauvais outil.

  1. Si non 1. Si quelqu'un pouvait me diriger dans la bonne direction sur la façon de traverser l'arbre et de gérer des choses comme ci-dessus. Ce serait génial. Je ne trouve pas beaucoup d'informations sur la traversée des arbres d'expression, des types d'expression, etc. Même une référence à un livre recommandé spécifique au sujet serait utile.

Le meilleur moyen de parcourir un arbre d'expression consiste à hériter de ExpressionVisitor . Un bon blog pour commencer serait le blog de Matt Warren http://blogs.msdn.com/b/mattwar/ , lisez la série sur la mise en œuvre d' IQueryable . Mais non, ce n'est pas facile.

Théoriquement, on pourrait hériter d' ExpressionVisitor pour parcourir la chaîne vers l'instance d'origine. Mais vous devrez alors remonter dans la chaîne d’une manière ou d’une autre, et le moyen le plus simple de le faire serait de compiler les expressions enfants. En théorie, vous pourriez utiliser Reflection pour revenir en arrière, comme le tente votre réponse, mais j'espère que vous aurez distinctement l'impression que vous avez choisi le mauvais outil si votre position de non-compilation est impeccable.

  1. soupir Devrais-je simplement abandonner et obliger l'utilisateur à utiliser des expressions simples.

Vous n'avez pas décrit les avantages / inconvénients de ceci.




Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi