【问题标题】:Using DynamicMethod to invoke a method with generic parameters?使用 DynamicMethod 调用具有泛型参数的方法?
【发布时间】:2013-08-17 19:01:11
【问题描述】:

我的目标是创建一个方法SortRecords,它接受IEnumerable<T>PropertyInfo 作为参数。 IEnumerable<T> 是记录列表。 PropertyInfoT 的属性。当被调用时,SortRecords 应该使用x => x.Property 调用Enumerable.SortBy<T, typeof Property> 方法。请注意,Enumerable.SortBy 有两个通用参数。此外,反射不能在 lambda 表达式中使用,因为 (a) 它很慢,并且 (b) 它不适用于 Entity Framework。

我已经编写了一些代码,但我一直看到错误消息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);
         }

结束第一个代码sn-p

方法定义如下

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 with generic type parameters

【问题讨论】:

    标签: c# .net sorting dynamicmethod dynamic-method


    【解决方案1】:

    我自己没有像这样使用过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[]即可传入。)

    (如果您需要使用 Entity Framework,我认为您会关注 Queryable 而不是 Enumerable 并构建表达式树而不是委托,但这是另一回事。)

    【讨论】:

    • 毫无疑问,您已经做出了重要的观察,但即使我应用了您建议的更改,我仍然收到 Operation could destabilize the runtime 错误。
    • @DanielAllenLangdon:在什么时候?一个简短但完整的示例在这里会有所帮助 - 如果您可以使用更短的 IL 块(例如传递排序委托)来重现它,那也会有所帮助。
    • 我尝试使用反射重写代码,发现需要双参数OrderBy方法。我认为这绝对是一个问题,但是在尝试使用动态方法时我仍然收到相同的错误消息。我认为也许解决方案可能是将通用 OrderBy 方法干净地包装在一个使用 Dynamic 方法并调用它的方法中......
    • @DanielAllenLangdon:你应该能够让它工作。我会将问题简化为一个动态方法,just 充当OrderBy 的代理,然后从那里开始。 (很抱歉没有发现您需要两个参数 OrderBy 调用......我应该已经看到了。)
    【解决方案2】:

    我更喜欢使用表达式来解决这类问题。这是一个适合您的示例。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-03-26
      • 1970-01-01
      • 1970-01-01
      • 2016-02-06
      • 1970-01-01
      • 2022-01-03
      相关资源
      最近更新 更多