我的目标是创建一个方法SortRecords
,该方法接受IEnumerable<T>
和PropertyInfo
作为参数。 IEnumerable<T>
是记录列表。 PropertyInfo
是T
的属性。调用时,SortRecords
应使用Enumerable.SortBy<T, typeof Property>
调用x => x.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);
}
结束第一个代码段
方法定义遵循
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
答案 0 :(得分:0)
我没有像这样使用DynamicMethod
,但我怀疑你只需MakeGenericMethod
EnumerableOrderBy
,就像你已经为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
并构建表达式树而不是委托,但这是另一回事。)
答案 1 :(得分:0)
我更喜欢使用表达式来解决这类问题。这是一个适合你的例子。
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;
}