Arbre de création créer un dictionnaire avec les valeurs de propriété pour la classe

c# expression expression-trees reflection

Question

Essentiellement, j'essaie de faire cela en utilisant des arbres d'expression

var properties = new Dictionary<string, object>();

foreach (var propInfo in objType.GetTypeInfo().GetProperties(BindingFlags.Public))
{
    var name = propInfo.Name;
    var value = propInfo.GetValue(objInstance);

    properties.Add(name, value);
}

return properties;

Ie crée un dictionnaire de paires nom / valeur où nom est le nom d'une propriété pour objType et valeur est la valeur de la propriété pour l'instance objInstance de objType

Maintenant, convertir ceci en une expression doit être compilé en un délégué qui ne fait que

Func<T, Dictionary<string, object>> func = i =>
{
    var properties = new Dictionary<string, object>();

    properties.Add("Prop1", (object)i.Prop1);
    properties.Add("Prop2", (object)i.Prop2);
    properties.Add("Prop3", (object)i.Prop3);
    // depending upon the number of properties of T, Add will continue

    return properties;
};

Je sais comment effectuer certaines de ces opérations, mais je ne suis pas sûr de savoir comment créer une instance locale d'un dictionnaire, puis l'utiliser (et la renvoyer) dans les expressions suivantes.

Réponse acceptée

Cela devrait être quelque chose comme (commentaires en ligne):

public static Func<T, Dictionary<string, object>> GetValuesFunc<T>()
{
    Type objType = typeof(T);

    var dict = Expression.Variable(typeof(Dictionary<string, object>));
    var par = Expression.Parameter(typeof(T), "obj");

    var add = typeof(Dictionary<string, object>).GetMethod("Add", BindingFlags.Public | BindingFlags.Instance, null, new[] { typeof(string), typeof(object) }, null);

    var body = new List<Expression>();
    body.Add(Expression.Assign(dict, Expression.New(typeof(Dictionary<string, object>))));

    var properties = objType.GetTypeInfo().GetProperties(BindingFlags.Public | BindingFlags.Instance);

    for (int i = 0; i < properties.Length; i++)
    {
        // Skip write only or indexers
        if (!properties[i].CanRead || properties[i].GetIndexParameters().Length != 0)
        {
            continue;
        }

        var key = Expression.Constant(properties[i].Name);
        var value = Expression.Property(par, properties[i]);
        // Boxing must be done manually... For reference type it isn't a problem casting to object
        var valueAsObject = Expression.Convert(value, typeof(object));
        body.Add(Expression.Call(dict, add, key, valueAsObject));
    }

    // Return value
    body.Add(dict);

    var block = Expression.Block(new[] { dict }, body);

    var lambda = Expression.Lambda<Func<T, Dictionary<string, object>>>(block, par);
    return lambda.Compile();
}

utilisez-le comme:

public class Test
{
    public int A { get; set; }
    public string B { get; set; }
}

et

Func<Test, Dictionary<string, object>> fn = GetValuesFunc<Test>();

var obj = new Test
{
    A = 5,
    B = "Foo"
};

var res = fn(obj);

Réponse populaire

Vous n'avez besoin que d'Expressions pour mettre en cache les accesseurs, à moins que vous ne connaissiez un moyen plus simple (par exemple, peut-être dynamique?)

public static Func<T, Dictionary<string, object>> GetToDict<T>(){
    var getters = typeof(T)
        .GetProperties(BindingFlags.Public | BindingFlags.Instance)
        .Select(p => {
            var param = Expression.Parameter(typeof(T));
            return new KeyValuePair<PropertyInfo, Func<T, object>>(p, 
                Expression.Lambda<Func<T, object>>(Expression.Convert(Expression.MakeMemberAccess(param, p), typeof(object)), param).Compile());
        })
        .ToList();
    return i => getters.ToDictionary(x => x.Key.Name, x => x.Value(i));
}


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