Как создать собственное лямбда-выражение Select во время выполнения для работы с подклассами - PullRequest
2 голосов
/ 25 февраля 2011

Если у меня следующая иерархия типов:

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

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

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

и многие другие TicketTypes : TicketBase

и я хочу создать функцию, которая выбирает любое свойство, например PropertyA, из любого типа заявки, напримерTicketTypeA

Я написал эту функцию:

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

и вызову ее на List<TicketBase> Tickets примерно так:

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

Я получаю следующее ArgumentException:

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

Кто-нибудь знает, как это исправить?

1 Ответ

2 голосов
/ 25 февраля 2011

Ну, вариант one - включить приведение, например,

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

Так что вызов CreateSelect(typeof(TicketTypeA), "PropertyA") эквивалентен:

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

произойдет сбой, если вы примените его к значению TicketBase, которое относится, скажем, к TicketTypeB, но этого трудно избежать, если у вас есть List<TicketBase> или что-то подобное.

...