Comment créer une expression lambda personnalisée lors de l'exécution pour qu'elle fonctionne avec les sous-classes

compilation expression-trees lambda linq runtime

Question

Si j'ai la hiérarchie de types suivante:

abstract class TicketBase
{
    public DateTime PublishedDate { get; set; }
}

class TicketTypeA:TicketBase
{
     public string PropertyA { get; set; }
}   

class TicketTypeB:TicketBase
{
     public string PropertyB { get; set; }
}

et beaucoup d'autres TicketTypes : TicketBase

et voulez créer une fonction qui sélectionne n'importe quelle propriété, par exemple PropertyA partir de n'importe quel type de ticket, par exemple TicketTypeA

J'ai écrit cette fonction:

    private Func<TicketBase, String> CreateSelect(Type t, String FieldName)
    {
        var parameterExp = Expression.Parameter(t, "sel");
        var fieldProp = Expression.PropertyOrField(parameterExp, FieldName);
        var lambda = Expression.Lambda<Func<TicketBase, String>>(fieldProp, parameterExp);
        return lambda.Compile();
    }

et appelez-le sur une List<TicketBase> Tickets comme List<TicketBase> Tickets :

Type typeToSelectFrom = typeof(TicketTypeA);
String propertyToSelect = "PropertyA";
Tickets.Select(CreateSelect(typeToSelectFrom, propertyToSelect));

Je reçois la ArgumentException suivante:

ParameterExpression of type 'TicketTypes.TicketTypeA' cannot be used for delegate parameter of type 'Types.TicketBase'

Quelqu'un sait comment réparer ceci?

Réponse acceptée

Eh bien, une option consiste à inclure un casting, par exemple

private Func<TicketBase, String> CreateSelect(Type t, String FieldName)
{
    var parameterExp = Expression.Parameter(typeof(TicketBase), "sel");
    var cast = Expression.Convert(parameterExp, t);
    var fieldProp = Expression.PropertyOrField(cast, FieldName);
    var lambda = Expression.Lambda<Func<TicketBase, String>>(fieldProp,
                                                             parameterExp);
    return lambda.Compile();
}

Ainsi, appeler CreateSelect(typeof(TicketTypeA), "PropertyA") équivaut à:

Func<TicketBase, string> func = tb => ((TicketTypeA)tb).PropertyA;

Évidemment, cela va échouer si vous l'appliquez à une valeur TicketBase qui fait référence (par exemple) à un TicketTypeB , mais il est difficile de l'éviter si vous avez une List<TicketBase> ou quelque chose de similaire.



Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow