2

If I have following type hierarchy:

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

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

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

and many more TicketTypes : TicketBase

and want to create a function which selects any property e.g. PropertyA from any ticket type e.g. TicketTypeA

I wrote this function:

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

and call it on a List<TicketBase> Tickets like so:

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

I get the following ArgumentException:

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

Anyone know how to fix this?

cjroebuck
  • 2,273
  • 4
  • 30
  • 46

1 Answers1

2

Well, one option is to include a cast, e.g.

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

So that calling CreateSelect(typeof(TicketTypeA), "PropertyA") is equivalent to:

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

Obviously that's going to fail if you apply it to a TicketBase value which refers to (say) a TicketTypeB, but it's hard to avoid that, if you've got a List<TicketBase> or something similar.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Great, this works - thanks Jon. But I'm intrigued by your emphasis on this being _one_ option.. are there any better options? – cjroebuck Feb 25 '11 at 11:06
  • @cjroebuck: Well, you could potentially make it a generic method to return a `Func` - but it's not clear whether you *always* know the type that you're going to call it with at compile time (as you do here). – Jon Skeet Feb 25 '11 at 11:07
  • Well, I know that it is _always_ going to be a type `T : TicketBase` but I dont know _which_ `T` until run-time – cjroebuck Feb 25 '11 at 11:10
  • @cjroebuck: Sure, but if you don't know the type itself at compile time, you can't easily call it as a generic method. – Jon Skeet Feb 25 '11 at 11:11