37 votes

Création d'un délégué de définition de propriété

J'ai créé des méthodes pour convertir une propriété lambda en délégué:

 public static Delegate MakeGetter<T>(Expression<Func<T>> propertyLambda)
{
    var result = Expression.Lambda(propertyLambda.Body).Compile();
    return result;
}

public static Delegate MakeSetter<T>(Expression<Action<T>> propertyLambda)
{
    var result = Expression.Lambda(propertyLambda.Body).Compile();
    return result;
}
 

Ces travaux:

 Delegate getter = MakeGetter(() => SomeClass.SomeProperty);
object o = getter.DynamicInvoke();

Delegate getter = MakeGetter(() => someObject.SomeProperty);
object o = getter.DynamicInvoke();
 

mais ceux-ci ne compileront pas:

 Delegate setter = MakeSetter(() => SomeClass.SomeProperty);
setter.DynamicInvoke(new object[]{propValue});

Delegate setter = MakeSetter(() => someObject.SomeProperty);
setter.DynamicInvoke(new object[]{propValue});
 

Les lignes MakeSetter échouent avec "Les arguments de type ne peuvent pas être déduits de l'utilisation. Essayez de spécifier les arguments de type explicitement."

Ce que j'essaie de faire est-il possible? Merci d'avance.

58voto

Marc Gravell Points 482669

L' Expression API prend en charge cette dans .NET 4.0, mais, malheureusement, le compilateur C# ne change en rien les bonbons à l'appui. Mais la bonne nouvelle est que vous pouvez trivialement prendre un "get" expression (qui le compilateur C# peut écrire) et le ré-écrire comme un "ensemble" de l'expression.

Et mieux encore, si vous n'avez pas .NET 4.0, il y a encore au moins deux autres façons d'effectuer un "set" par une expression écrite comme un "get".

Ici, ils le sont tous, pour info:

using System;
using System.Linq.Expressions;
using System.Reflection;
class Foo {
    public string Bar { get; set; }
    static void Main() {
        // take a "get" from C#
        Expression<Func<Foo, string>> get = foo => foo.Bar;

        // re-write in .NET 4.0 as a "set"
        var member = (MemberExpression)get.Body;
        var param = Expression.Parameter(typeof(string), "value");
        var set = Expression.Lambda<Action<Foo, string>>(
            Expression.Assign(member, param), get.Parameters[0], param);

        // compile it
        var action = set.Compile();
        var inst = new Foo();
        action(inst, "abc");
        Console.WriteLine(inst.Bar); // show it working

        //==== reflection
        MethodInfo setMethod = ((PropertyInfo)member.Member).GetSetMethod();
        setMethod.Invoke(inst, new object[] { "def" });
        Console.WriteLine(inst.Bar); // show it working

        //==== Delegate.CreateDelegate
        action = (Action<Foo, string>)
            Delegate.CreateDelegate(typeof(Action<Foo, string>), setMethod);
        action(inst, "ghi");
        Console.WriteLine(inst.Bar); // show it working
    }
}

4voto

Jason Punyon Points 21244

Action<T> représente un délégué qui prend un paramètre de type T et ne renvoie rien. Les lambda expressions vous fournir d' MakeSetter représentent les délégués qui ne prennent pas de paramètre et retourner en SomeClass.SomeProperty ou someObject.SomeProperty.

Les messages d'erreur que vous obtenez sont dues au fait que le compilateur ne peut pas déduire les types de les lambda expressions vous êtes de passage dans l' MakeSetter méthode parce que ce que vous avez réussi et que la méthode est enceinte ne sont pas synchronisés.

4voto

Igor Zevaka Points 32586

Votre MakeSetter s'attend à une Action<T> et vous êtes de passage à un Func<T> (() => someObject.SomeProperty). Essayez les solutions suivantes:

Delegate setter = MakeSetter((prop) => {someObject.SomeProperty = prop;});
setter.DynamicInvoke(new object[]{propValue});

EDIT N'a pas l'air comme vous pouvez convertir déclaration lambdas dans les expressions. C'est un peu un tour sur moyen de le faire sans expressions - droit pour les délégués:

class Test2 {
    delegate void Setter<T>(T value);

    public static void Test() {
        var someObject = new SomeObject();
        Setter<string> setter = (v) => { t.SomeProperty = v; };
        setter.DynamicInvoke(new object[]{propValue});
    }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X