Come faccio a creare un'espressione Select lambda personalizzata in runtime per lavorare con le classi secondarie

compilation expression-trees lambda linq runtime

Domanda

Se ho la seguente gerarchia di tipi:

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

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

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

e molti altri TicketTypes : TicketBase

e desidera creare una funzione che seleziona qualsiasi proprietà, ad esempio PropertyA da qualsiasi tipo di ticket, ad esempio TicketTypeA

Ho scritto questa funzione:

    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();
    }

e chiamalo su una List<TicketBase> Tickets come questi:

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

Ottengo la seguente ArgumentException:

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

Qualcuno sa come risolvere questo?

Risposta accettata

Ebbene, una possibilità è quella di includere un cast, ad esempio,

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();
}

Pertanto, la chiamata a CreateSelect(typeof(TicketTypeA), "PropertyA") equivale a:

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

Ovviamente questo fallirà se lo applichi a un valore TicketBase che si riferisce a (ad esempio) un TicketTypeB , ma è difficile evitarlo, se hai una List<TicketBase> o qualcosa di simile.



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é