使用DynamicMethod调用具有泛型参数的方法

本文关键字:参数 方法 泛型 DynamicMethod 调用 使用 | 更新日期: 2023-09-27 18:27:44

我的目标是创建一个接受IEnumerable<T>PropertyInfo作为参数的方法SortRecordsIEnumerable<T>是一个记录列表。PropertyInfoT的一个性质。调用时,SortRecords应使用x => x.Property调用Enumerable.SortBy<T, typeof Property>方法。请注意,Enumerable.SortBy有两个通用参数。此外,反射不能在lambda表达式中使用,因为(a)它很慢,(b)它不能与实体框架一起使用。

我已经写了一些代码,但我一直看到错误消息Operation could destabilize the runtime。以下是我的代码看起来像

for (int i = 0; i < NumberOfSorts; i++)
        {
            string propertyName = PropertyNames[ColumnSortOrder[i]];
            PropertyInfo property = typeof(T).GetProperties().Single(p => p.Name == propertyName);              
            Func<IEnumerable<T>, PropertyInfo, IEnumerable<T>> sortingFunction = GetFunctionToSortRecords<T>(filteredRecords, property);
            sortedRecords = GetFunctionToSortRecords<T>(filteredRecords, property)(filteredRecords, property);
         }

结束第一个代码段

方法定义遵循

delegate IEnumerable<T> GetFunctionToSortRecordsDelegate<T>(IEnumerable<T> records, PropertyInfo propertyToSortOn);
public static Func<IEnumerable<T>, PropertyInfo, IEnumerable<T>> GetFunctionToSortRecords<T>(IEnumerable<T> records, PropertyInfo propertyToSortOn)
    {
        Type propertyType = propertyToSortOn.GetType();
        DynamicMethod method = new DynamicMethod("SortRecords", typeof(IEnumerable<T>), new Type[] { typeof(IEnumerable<T>), typeof(PropertyInfo) });            
        ILGenerator generator = method.GetILGenerator();            
        MethodInfo GetPropertyValue = propertyToSortOn.GetGetMethod();
        MethodInfo GetDefaultKeySelectorForProperty = typeof(DataTablesSorting).GetMethod("GetDefaultKeySelectorForProperty")                                                                                                         
            .MakeGenericMethod(new Type[] {typeof(T), propertyToSortOn.PropertyType });            
        MethodInfo EnumerableOrderBy = typeof(Enumerable).GetMethods()
            .Single(m => m.Name == "OrderBy" && m.GetParameters().Count()==3);
        // Get the default key selector for the property passed in.            
        generator.Emit(OpCodes.Ldarg_1); // property
        generator.Emit(OpCodes.Call, GetDefaultKeySelectorForProperty);
        // Save the default key selector at location 0
        generator.Emit(OpCodes.Stloc_0);
        generator.Emit(OpCodes.Ldarg_0); // records
        generator.Emit(OpCodes.Ldloc_0); // default key selector
        generator.Emit(OpCodes.Call, EnumerableOrderBy);
        generator.Emit(OpCodes.Ret);
        return ((GetFunctionToSortRecordsDelegate<T>)(method.CreateDelegate(typeof(GetFunctionToSortRecordsDelegate<T>)))).Invoke;
    }
    delegate TKey GetDefaultKeySelectorForPropertyDelegate<T, TKey>(T t);
    public static Func<T, TKey> GetDefaultKeySelectorForProperty<T, TKey>(PropertyInfo property)
    {
        DynamicMethod method = new DynamicMethod("GetKeySelector", typeof(TKey), new Type[] { typeof(T) });
        ILGenerator generator = method.GetILGenerator();
        MethodInfo GetPropertyValue = property.GetGetMethod();
        generator.Emit(OpCodes.Ldarg_0);
        generator.Emit(OpCodes.Callvirt, GetPropertyValue);
        generator.Emit(OpCodes.Ret);
        return ((GetDefaultKeySelectorForPropertyDelegate<T, TKey>)(method.CreateDelegate(typeof(GetDefaultKeySelectorForPropertyDelegate<T, TKey>)))).Invoke;
    }

我认为这个问题可能与以下内容有关:具有泛型类型参数

使用DynamicMethod调用具有泛型参数的方法

的DynamicMethod

我自己还没有像这样使用DynamicMethod,但我怀疑您只需要在这个EnumerableOrderBy上使用MakeGenericMethod,就像您已经在为GetDefaultKeySelectorForProperty做的那样。目前,您正在尝试在不指定任何类型参数的情况下调用泛型方法。

所以类似于:

MethodInfo EnumerableOrderBy = typeof(Enumerable).GetMethods()
    .Single(m => m.Name == "OrderBy" && m.GetParameters().Count() == 3)
    .MakeGenericMethod(typeof(T), propertyToSortOn.PropertyType);

MakeGenericMethod使用参数数组,因此不需要显式构造Type[]来传入。)

(如果您需要使用实体框架,我认为您应该考虑Queryable而不是Enumerable,并构建表达式树而不是委托,但这是另一回事。)

对于这类问题,我更喜欢使用表达式。下面是一个对你有用的例子。

    public static Func<IEnumerable<T>, PropertyInfo, IEnumerable<T>> GetFunctionToSortRecords<T>(IEnumerable<T> records, PropertyInfo property)
    {
        var propertyExpression = GetExpressionForProperty<T>(property);
        var method = typeof(TheCurrentClass).GetMethod("InternalGetFunctionToSortRecords", BindingFlags.NonPublic | BindingFlags.Static);
        return (Func<IEnumerable<T>, PropertyInfo, IEnumerable<T>>)method.MakeGenericMethod(typeof(T), property.PropertyType).Invoke(null, new object[] { propertyExpression });
    }
    private static Func<IEnumerable<T>, PropertyInfo, IEnumerable<T>> InternalGetFunctionToSortRecords<T, TProp>(Expression propertyExpression)
    {
        var lambdaExpression = propertyExpression as LambdaExpression;
        Func<T, TProp> keySelector = (Func<T, TProp>)lambdaExpression.Compile();
        Func<IEnumerable<T>, PropertyInfo, IEnumerable<T>> sorter = (x, y) => x.OrderBy(keySelector);
        return sorter.Invoke;
    }
    private static Expression GetExpressionForProperty<T>(PropertyInfo property)
    {
        var parameter = Expression.Parameter(typeof(T));
        var propertyExpression = Expression.Property(parameter, property);
        var lambdaExpression = Expression.Lambda(propertyExpression, parameter);
        return lambdaExpression;
    }