如何使用运行时生成的类型的已编译lambda函数进行分组

时间:2018-09-04 14:48:41

标签: c# lambda

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

下面是一些示例代码:

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]”。

能否请您提示我我在做什么错以及如何解决?

1 个答案:

答案 0 :(得分:3)

Func<T, TResult>委托的第一个通用参数声明为contravariantin),这意味着您可以将派生参数较少的委托分配给派生参数较多的委托,但是反之亦然(换句话说,您可以将Func<IDynamicGeneratedModelClass,Object>强制转换为Func<DynamicGeneratedModelClass,Object>,但是不能将Func<DynamicGeneratedModelClass,Object>强制强制转换为Func<IDynamicGeneratedModelClass,Object>)。

为避免此问题,您现在生成的不是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);