Conversion explicite des paramètres dans les arbres d'expression créés à partir de MethodInfo

c# expression-trees

Question

J'ai la méthode ci-dessous qui convertit un MethodInfo (non statique) en une expression compilée ( Func ) que je peux ensuite appeler.

Cela fonctionne très bien: je peux l'appeler avec une méthode qui attend à la fois des objets de référence et des types de valeur.

MAIS contrairement à la méthode originale où je pouvais appeler une méthode qui avait un paramètre qui attend un double et qui lui passait un int cette expression compilée ne le supporte pas et lève une InvalidCastException .

Comment modifier cela pour prendre en charge le même type de diffusion implicite que lors d'un appel de méthode normal?

Question bonus: l'instanceExp doit-il utiliser le DeclaringType ou le ReflectedType partir du MethodInfo ?

public Func<object, object[], object> Create(MethodInfo methodInfo)
{
    var methodParams = methodInfo.GetParameters();
    var arrayParameter = Expression.Parameter(typeof(object[]), "array");

    var arguments =
        methodParams.Select((p, i) => Expression.Convert(
            Expression.ArrayAccess(arrayParameter, Expression.Constant(i)), p.ParameterType))
            .Cast<Expression>()
            .ToList();

    var instanceParameter = Expression.Parameter(typeof(object), "controller");

    var instanceExp = Expression.Convert(instanceParameter, methodInfo.DeclaringType);
    var callExpression = Expression.Call(instanceExp, methodInfo, arguments);

    var bodyExpression = Expression.Convert(callExpression, typeof(object));

    return Expression.Lambda<Func<object, object[], object>>(
        bodyExpression, instanceParameter, arrayParameter)
        .Compile();
}

--- MODIFIER

La solution de travail est:

var changeTypeMethod = typeof(Convert).GetMethod("ChangeType", new Type[] { typeof(object), typeof(TypeCode) });
var arguments =
     methodParams.Select((p, i) =>
         !typeof(IConvertible).IsAssignableFrom(p.ParameterType)
             // If NOT IConvertible, don't try to convert it
             ? (Expression)Expression.Convert(
                 Expression.ArrayAccess(arrayParameter, Expression.Constant(i)), p.ParameterType)
            :
             // Otherwise add an explicit conversion to the correct type to handle int <--> double etc.
            (Expression)Expression.Convert(
                Expression.Call(changeTypeMethod,
                    Expression.ArrayAccess(arrayParameter, Expression.Constant(i)),
                    Expression.Constant(Type.GetTypeCode(p.ParameterType))),
                p.ParameterType)
        )
        .ToList();

Réponse acceptée

Le problème est le même que dans ce morceau de code C #:

object a = 123;
double b = (double)a; // InvalidCastException

La raison en est que a est un object , donc pour en faire un double le casting doit le dérouler, puis transformer un int en double . La langue ne permet au casting que de faire une chose - il va se dérouler ou se transformer, mais pas les deux. Vous devez dire au compilateur comment faire cette distribution de manière explicite en lui disant qu'il y a un int intégré dans l' object :

double b = (double)((int)a); // Works

Si vous pouviez faire la même chose dans votre expression LINQ, votre expression compilée fonctionnerait également. Cependant, il est possible que vous ne connaissiez pas le type réel du paramètre au moment où vous générez votre expression. Vous souhaiterez donc peut-être opter pour une stratégie différente: Convert.ChangeType à un appel à la méthode Convert.ChangeType , qui peut être déplié et diffusé simultanément .




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