【问题标题】:How do I create a custom Select lambda expression at runtime to work with sub classes如何在运行时创建自定义 Select lambda 表达式以使用子类
【发布时间】:2011-02-25 10:36:37
【问题描述】:

如果我有以下类型层次结构:

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&lt;TicketBase&gt; 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'

有人知道如何解决这个问题吗?

【问题讨论】:

    标签: linq lambda runtime compilation expression-trees


    【解决方案1】:

    嗯,一个选项是包含演员表,例如

    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;
    

    很明显,如果你将它应用到引用(比如)TicketTypeBTicketBase 值,那将会失败,但如果你有 List&lt;TicketBase&gt; 或类似的东西,就很难避免这种情况。

    【讨论】:

    • 太棒了,这很有效 - 谢谢乔恩。但我对您强调这是 one 选项的强调很感兴趣。还有更好的选择吗?
    • @cjroebuck:嗯,你可以将它作为一个通用方法来返回 Func&lt;T, string&gt; - 但不清楚你是否总是知道你要使用的类型在编译时调用它(就像你在这里做的那样)。
    • 好吧,我知道它总是会是一个类型T : TicketBase,但直到运行时我才知道哪个 T
    • @cjroebuck:当然可以,但是如果您在编译时不知道类型本身,就不能轻易将其称为泛型方法。
    • 是的,确实如此。感谢您的帮助。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-03-19
    • 2017-01-12
    • 2019-02-20
    • 1970-01-01
    • 2011-10-10
    • 2020-05-28
    • 1970-01-01
    相关资源
    最近更新 更多