7
votes

Créer une action Lambda à partir d'une expression de fonction

Il est relativement facile de créer une fonction Lambda qui retournera la valeur d'une propriété à partir d'un objet, y compris des propriétés profondes ... xxx pré>

et cela peut être appelé comme suit. .. p> xxx pré>

mais, donné em> strud> la fonction résultante ci-dessus (ou l'expression initialement utilisée pour créer la fonction) , peut-on fournir un moyen facile de créer l'action opposée ... p>

Action SetCategoryName = nouvelle action ((c, s) => c. Nom = s); code> p>

... qui permettra la définition de la même valeur de propriété comme suit? P> xxx pré>

Notez que je Je cherche un moyen de créer l'action programmatisée à partir de la fonction ou de l'expression - j'espère que j'ai montré que je savais déjà comment le créer manuellement. P>

Je suis ouvert aux réponses qui fonctionnent dans les deux .net 3.5 et 4.0. P>

merci. P>

mise à jour: p>

Peut-être que je ne suis peut-être pas clair dans ma question, alors laissez-moi essayer de démontrer plus clairement ce que je suis essayer faire. P>

J'ai la méthode suivante (que j'ai créée pour les besoins de cette question) ... p>

Category category = *<get object from somewhere>*;
this.DoLambdaStuff(category, c => c.Name);


1 commentaires

Je ne comprends pas vraiment ce que vous entendez en le faisant automatiquement de manière manuelle. Toutefois, si vous souhaitez définir des propriétés et décider de la propriété que vous souhaitez définir au moment de l'exécution, vous devez utiliser la réflexion. De plus, vous pouvez utiliser l'arborescence d'expression pour construire des expressions Lambda à temps d'exécution.


4 Réponses :


0
votes

Cela devrait être possible en utilisant arbres d'expression , qui peut être créé à partir d'expressions Lambda, modifiées, puis compilées dans un délégué.


1 commentaires

C'est le genre de chose que je cherchais, j'espérais juste un exemple!



3
votes

OK, le code que je cherche va faire quelque chose comme ça ... xxx

Ce code fonctionne, mais uniquement pour les propriétés peu profondes (c'est-à-dire des propriétés de l'objet immédiat) mais Ne fonctionne pas pour des propriétés profondes - bien que la fonction getter puisse fonctionner pour des propriétés profondes. Il serait intéressant de savoir si quelqu'un peut m'aider à modifier cela pour travailler avec des propriétés profondes, mais je vais soulever cela comme une question séparée.


1 commentaires

Ce code ne fonctionne que dans .NET 4 car il nécessite l'amélioration de la prise en charge des arbres d'expression introduits dans .NET 4.



2
votes

Comme Martin notait ci-dessus, les propriétés "Deep" brisent sa solution. La raison pour laquelle il ne fonctionne pas est cette expression: xxx pré>

La raison de celle-ci n'est pas immédiatement évidente: la classe dérivée déclare sa propre propriété Getter, qui redirige vers la classe de base, mais ne fait pas Définissez un seigter correspondant. P>

Pour contourner ce problème, vous devez localiser la propriété par réflexion, rechercher son type de déclaration, puis obtenir la propriété correspondante du déclarant. Contrairement à la propriété que vous obtenez sur la classe dérivée, la propriété du déclarant a un getter et un setter, et est donc assignable. (Cela suppose qu'une sécheuse de la propriété est déclarée du tout: si le déclarant ne fournit pas de seigur, personne d'autre ne pourrait la fournir.) P>

Voici une implémentation squelettique qui répond à ce problème. Remplacez l'appel ci-dessus à expression.property code> avec l'appel de getpropertyorfield code> ci-dessous et que la solution de Martin va fonctionner. P>

private static MemberExpression GetPropertyOrField(Expression baseExpr, string name) {
    if (baseExpr == null) {
        throw new ArgumentNullException("baseExpr");
    }
    if (string.IsNullOrWhiteSpace(name)) {
        throw new ArgumentException("name");
    }
    var type = baseExpr.Type;
    var properties = type
        .GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
        .Where(p => p.Name.Equals(name))
        .ToArray();
    if (properties.Length == 1) {
        var res = properties[0];
        if (res.DeclaringType != type) {
            // Here is the core of the fix:
            var tmp = res
                .DeclaringType
                .GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
                .Where(p => p.Name.Equals(name))
                .ToArray();
            if (tmp.Length == 1) {
                return Expression.Property(baseExpr, tmp[0]);
            }
        }
        return Expression.Property(baseExpr, res);
    }
    if (properties.Length != 0) {
        throw new NotSupportedException(name);
    }
    var fields = type
        .GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
        .Where(p => p.Name.Equals(name))
        .ToArray();
    if (fields.Length == 1) {
        return Expression.Field(baseExpr, fields[0]);
    }
    if (fields.Length != 0) {
        throw new NotSupportedException(name);
    }
    throw new ArgumentException(
        string.Format(
            "Type [{0}] does not define property/field called [{1}]"
        ,   type
        ,   name
        )
    );
}


0 commentaires

5
votes
void DoLambdaStuff<TObject, TValue>(TObject obj, Expression<Func<TObject, TValue>> expression) {

    Func<TObject, TValue> getValue = expression.Compile();
    TValue stuff = getValue(obj);

    var p = Expression.Parameter(typeof(TValue), "v");
    Expression<Action<TObject, TValue>> assignmentExpression = 
        Expression.Lambda<Action<TObject, TValue>>(Expression.Assign(expression.Body, p), expression.Parameters[0], p);

    Action<TObject, TValue> setValue = assignmentExpression.Compile();

    setValue(obj, stuff);
}

1 commentaires

+1. Utilisation de ce code, je suis capable de définir des valeurs pour champs privés et propriétés privées imbriquées aussi. Le type de champ / propriété pourrait être un struct aussi et cela fonctionne simplement. Aimerait vraiment une explication de la façon dont cela fonctionne.