web-dev-qa-db-fra.com

L'expression de type 'System.Int32' ne peut pas être utilisée pour le type de retour 'System.Object'

J'essaie de produire un système de script simple qui sera utilisé pour imprimer des étiquettes. J'ai fait cela dans le passé avec réflexion sans problème, mais j'essaie maintenant de le faire avec des fonctions Lambda afin de pouvoir mettre en cache les fonctions pour les réutiliser.

Le code que j'ai jusqu'à présent est le suivant ...

public static string GetValue<T>(T source, string propertyPath) {

    try {

        Func<T, Object> func;

        Type type = typeof(T);
        ParameterExpression parameterExpression = Expression.Parameter(type, @"source");
        Expression expression = parameterExpression;
        foreach (string property in propertyPath.Split('.')) {
            PropertyInfo propertyInfo = type.GetProperty(property);
            expression = Expression.Property(expression, propertyInfo);
            type = propertyInfo.PropertyType;
        }

        func = Expression.Lambda<Func<T, Object>>(expression, parameterExpression).Compile();

        object value = func.Invoke(source);
        if (value == null)
            return string.Empty;
        return value.ToString();

    }
    catch {

        return propertyPath;

    }

}

Cela semble fonctionner dans certains cas, mais dans d'autres, cela échoue. Le problème semble résider dans ma tentative de renvoyer les valeurs en tant qu'objets, quels que soient les types de données réels. J'essaie de le faire parce que je ne sais pas au moment de la compilation quel sera le type de données mais à long terme, je n'ai besoin que d'une chaîne.

J'obtiens l'exception indiquée dans le titre de ce message chaque fois que j'essaie d'accéder à une propriété de type Int32 - mais je l'obtiens également pour les types Nullable et autres. L'exception est levée lorsque j'essaie de compiler l'expression dans la fonction.

Quelqu'un peut-il suggérer comment je pourrais procéder différemment tout en conservant la fonctionnalité Lambda afin de pouvoir mettre en cache les accesseurs?

56
Martin Robins

Avez-vous essayé d'utiliser Expression.Convert ? Cela ajoutera la conversion boxe/levage/etc.

Expression conversion = Expression.Convert(expression, typeof(object));
func = Expression.Lambda<Func<T, Object>>(conversion, parameterExpression).Compile();
102
Jon Skeet

J'espère que ce code vous aidera

using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace Student
{
    class Program
    {
        static void Main(string[] args)
        {
            var a = new Student();
            PrintProperty(a, "Name");
            PrintProperty(a, "Age");
            Console.ReadKey();

        }
        private static void PrintProperty<T>(T a, string propName)
        {
            PrintProperty<T, object>(a, propName);
        }
        private static void PrintProperty<T, TProperty>(T a, string propName)
        {
            ParameterExpression ep = Expression.Parameter(typeof(T), "x");
            MemberExpression em = Expression.Property(ep, typeof(T).GetProperty(propName));
            var el = Expression.Lambda<Func<T, TProperty>>(Expression.Convert(em, typeof(object)), ep);
            Console.WriteLine(GetValue(a, el));
        }

        private static TPorperty GetValue<T, TPorperty>(T v, Expression<Func<T, TPorperty>> expression)
        {
            return expression.Compile().Invoke(v);
        }

        public class Student
        {
            public Student()
            {
                Name = "Albert Einstein";
                Age = 15;
            }
            public string Name { get; set; }
            public int Age { get; set; }
        }
    }
}

0
Zekaee Esmaeel