【问题标题】:How to groupBy with compiled lambda function with type generated during runtime如何使用在运行时生成的已编译 lambda 函数进行分组
【发布时间】:2018-09-04 14:48:41
【问题描述】:

我有一个针对接口运行的已编译 Lambda 函数。不幸的是,该接口只是一个标记接口,实际类型是在运行时动态生成的,并且具有我想要对其进行分组的属性。

这里有一些示例代码:

class Program
{
    static void Main(string[] args)
    {
        // Just an example assignment: In the real life scenario the dynamic generated class is created during runtime. 
        IEnumerable<IDynamicGeneratedModelClass> list = GetDataFromService();

        // get the 'real' type from the list
        LambdaExpression lambdaExpression = DynamicExpression.ParseLambda(list.First().GetType(), typeof(object), "SomeProperty");
        Func<IDynamicGeneratedModelClass, object> compiledLambdaFunction = (Func<IDynamicGeneratedModelClass, object>)lambdaExpression.Compile();

        // Expected result: Group list on "SomeProp"
        var result = list.GroupBy(compiledLambdaFunction);
    }

    private static IList<IDynamicGeneratedModelClass> GetDataFromService()
    {
        return new List<IDynamicGeneratedModelClass> {  
            new DynamicGeneratedModelClass("Class1"),
            new DynamicGeneratedModelClass("Class2")
        };
    }
}

public interface IDynamicGeneratedModelClass
{}

public class DynamicGeneratedModelClass : IDynamicGeneratedModelClass
{
    public DynamicGeneratedModelClass(string someProperty)
    {
        SomeProperty = someProperty;
    }

    public string SomeProperty { get; }
}

当 lambda 表达式被编译时,它会抛出以下异常:

System.InvalidCastException: '无法转换类型的对象 'System.Func`2[ConsoleApp12.DynamicGeneratedModelClass,System.Object]' 输入 'System.Func`2[ConsoleApp12.IDynamicGeneratedModelClass,System.Object]'。'

您能否给我一个提示我做错了什么以及如何解决它?

【问题讨论】:

    标签: c# lambda


    【解决方案1】:

    Func&lt;T, TResult&gt; 委托的第一个泛型参数声明为contravariant (in),这意味着您可以将具有较少派生参数的委托分配给具有更多派生参数的委托,但反之则不行(在另一个也就是说,你可以将Func&lt;IDynamicGeneratedModelClass,Object&gt; 转换为Func&lt;DynamicGeneratedModelClass,Object&gt;,但不能将Func&lt;DynamicGeneratedModelClass,Object&gt; 转换为Func&lt;IDynamicGeneratedModelClass,Object&gt;

    为了避免这个问题,你现在生成的不是 lambda 表达式:

    // lambda has "wrong" type Func<DynamicGeneratedModelClass, object>
    (DynamicGeneratedModelClass item) => item.SomeProperty
    

    生成与此等效的 lambda:

    // lambda now has "correct" type Func<IDynamicGeneratedModelClass, object>
    (IDynamicGeneratedModelClass item) => ((DynamicGeneratedModelClass)item).SomeProperty
    

    我不熟悉您用来生成 lambda 的 DynamicExpression 库,但只需使用 System.Linq.Expression 类即可轻松完成:

    var itemType = list.First().GetType();
    var propertyName = "SomeProperty";
    var parameterExpr = Expression.Parameter(typeof(IDynamicGeneratedModelClass));
    var castExpr = Expression.Convert(parameterExpr, itemType);
    var propExpr = Expression.Property(castExpr, propertyName);
    var lambdaExpr = Expression.Lambda(propExpr, parameterExpr);
    
    // Compiled lambda is now of type Func<IDynamicGeneratedModelClass, object>
    Func<IDynamicGeneratedModelClass, object> compiledLambdaFunction = (Func<IDynamicGeneratedModelClass, object>)lambdaExpr.Compile();
    
    var result = list.GroupBy(compiledLambdaFunction);
    

    【讨论】:

    • 非常感谢您的详尽解释和您的代码 sn-p。您的解决方案就像一个魅力,我只需要添加对对象的转换,以便在“SomeProperty”不是字符串或对象时使其工作。 var castExprOut = Expression.Convert(propExpr, typeof(object)); var lambdaExpr = Expression.Lambda(castExprOut, parameterExpr);再次感谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-16
    • 1970-01-01
    • 1970-01-01
    • 2020-07-23
    相关资源
    最近更新 更多