C # costruisce lambda usando Expression.Call non piacciono certi tipi come params?

c# expression-trees lambda

Domanda

Per vari motivi sto costruendo un lambda C # in modo dinamico usando le strutture degli alberi delle espressioni. ad esempio, posso creare Func <string, bool> in runtime come mostrato nel seguente frammento.

   public static bool myMethod( object obj ) {  … }

    // Construct a Func<string,bool>
    var myMethod = GetType().GetMethod("myMethod");
    var lambdaParams = new ParameterExpression[] {Expression.Parameter(typeof (string))};
    var callMyMethod = Expression.Call(myMethod, lambdaParams);
    var lambda = Expression.Lambda(typeof(Func<string,bool>), callMyMethod, lambdaParams);
    var del = (Func<string,bool>)lambda.Compile();
    del("foo"); // works

Comunque se uso lo stesso codice per provare a fare un Func <int, bool> o un Func <DateTime, bool> esplode dove indicato con la seguente strana eccezione:

    // Construct a Func<DateTime,bool> or perhaps a struct type fails... why?
    var myMethod = GetType().GetMethod("myMethod");
    var lambdaParams = new ParameterExpression[] {Expression.Parameter(typeof (DateTime))};
    var callMyMethod = Expression.Call(myMethod, lambdaParams);  // Blows up here…

System.ArgumentException: Expression of type 'System.DateTime' cannot be used for parameter of type 'System.Object' of method 'Boolean myMethod(System.Object)'

Quindi, string funziona e List <string> funziona ma int32 non funziona né DataTime. Cosa sta succedendo? Non so come funzionino gli interni profondi di C #, ma suppongo che sia dovuto a un vero manipolo come primitivo e forse DateTime (che è anche una struttura) ...

Qualsiasi aiuto con questo sarebbe molto apprezzato.

grazie, Pat

Risposta accettata

A quanto ho capito, Expression.Call non esegue il auto-boxing degli argomenti di tipo value. Non riesco a trovare alcuna documentazione in tal senso, ma è menzionata su questa pagina del forum .

Una soluzione alternativa sarebbe esplicitamente fare la conversione di boxe nell'espressione con Expression.TypeAs .

Crea un'espressione unaria che rappresenta un riferimento esplicito o una conversione di boxing in cui viene fornito un valore null se la conversione ha esito negativo.

Nel tuo caso, questo dovrebbe funzionare:

var boxedParams = lambdaParams.Select(p => Expression.TypeAs(p, typeof(object)))
                              .ToArray();

var callMyMethod = Expression.Call(myMethod, boxedParams);

(Non hai bisogno dei fantasiosi lambda se c'è un solo parametro)

A seconda dell'utilizzo effettivo, potrebbe essere necessario verificare se la conversione di boxing è necessaria a seconda che il / i tipo / i in questione sia (sono) di tipo / i di valore.


Risposta popolare

Controlla questo: devi inserire il DateTime, dato che DateTime non è un tipo di riferimento!

// Construct a Func<DateTime,bool>
var myMethod = typeof(Program).GetMethod("myMethod");
var param = Expression.Parameter(typeof(DateTime));
var boxy = Expression.TypeAs(param, typeof(object));

var callMyMethod = Expression.Call(myMethod, boxy);
var lambda = Expression.Lambda(typeof(Func<DateTime, bool>), callMyMethod, new ParameterExpression[] { param });
var del = (Func<DateTime,bool>)lambda.Compile();
del(DateTime.Now); // works


Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché
Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché