1 回答

TA貢獻(xiàn)1906條經(jīng)驗(yàn) 獲得超10個(gè)贊
我不完全理解你想通過(guò)直接 IL 生成來(lái)實(shí)現(xiàn)什么;OrderByDescending接受一個(gè)Func<TSource, TKey>名為“keySelector”的參數(shù)。因此,您在仍然使用此方法時(shí)唯一可以生成的 IL 只是一個(gè)常規(guī)方法調(diào)用,它將“keySelector”參數(shù)傳遞給該方法,除非您打算在 ILOrderByDescending中重新實(shí)現(xiàn)。OrderByDescending
你有什么理由需要一直下到 IL 嗎?
如果這是用于用戶級(jí)代碼,您可以“編譯”expression將傳遞給此方法并OrderByDescending()正常調(diào)用的代碼,例如
var expression = /* Select Field/Property Expression*/;
array.OrderByDescending(expression.Compile()).ToArray();
如果這是框架/實(shí)用程序級(jí)別的代碼,您可能會(huì)使用“表達(dá)式樹(shù)”而無(wú)需一直使用手動(dòng) IL。例如
public static FilterDelegate<T> CreateDelegate<T>(Expression<Func<T, double>> expression)
{
var parameter = Expression.Parameter(typeof(IEnumerable<T>), "source");
// Your `GetMethod` for OrderByDescending did not work for me,
// so I'll just hand wave about this.
var orderByDescMethod = typeof(Enumerable)
.GetMethods()
.Single(m => m.Name == nameof(Enumerable.OrderByDescending) &&
m.GetParameters().Length == 2)
.MakeGenericMethod(typeof(T), typeof(double));
var toArrayMethod = typeof(Enumerable)
.GetMethod(nameof(Enumerable.ToArray))
.MakeGenericMethod(typeof(T));
var orderByExpression = Expression.Call(orderByDescMethod, parameter, expression);
var lambdaBody = Expression.Call(toArrayMethod, orderByExpression);
var lambdaExpression = Expression.Lambda<FilterDelegate<T>>(lambdaBody, parameter);
return lambdaExpression.Compile();
}
但是,如果由于某種原因您仍然需要直接通過(guò) IL 發(fā)出它,那么類(lèi)似下面的東西可以工作。
public static FilterDelegate<T> CreateDelegate<T>(Expression<Func<T, double>> expression)
{
// Your `GetMethod` for OrderByDescending did not work for me,
// so I'll just hand wave about this.
var orderByDescMethod = typeof(Enumerable)
.GetMethods()
.Single(m => m.Name == nameof(Enumerable.OrderByDescending) &&
m.GetParameters().Length == 2)
.MakeGenericMethod(typeof(T), typeof(double));
var toArrayMethod = typeof(Enumerable)
.GetMethod(nameof(Enumerable.ToArray))
.MakeGenericMethod(typeof(T));
// TODO: if you don't already have one of these
// you'll probably want to pull this out and re-use it
// rather than making a new one for every delegate
// TODO: if you do share a module builder I don't think it's thread-safe
// so this method will need sufficient locking/synchronization
var dynamicAssemblyName = new AssemblyName { Name = $"{Guid.NewGuid()}" };
var asm = AppDomain.CurrentDomain.DefineDynamicAssembly(dynamicAssemblyName, AssemblyBuilderAccess.Run);
var module = asm.DefineDynamicModule(dynamicAssemblyName.Name);
// Create a class with a static field to hold our compiled expression
var typeBuilder = module.DefineType(
$"{Guid.NewGuid()}",
TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.Sealed | TypeAttributes.Serializable);
var compiledExpressionField = typeBuilder.DefineField(
"CompiledExpression",
typeof(Func<T, double>),
FieldAttributes.Static | FieldAttributes.Private);
var holderType = typeBuilder.CreateType();
var compiledExpression = expression.Compile();
// Get the actual field after we've compiled the type
var compiledExpressionFieldInfo = holderType.GetField(
compiledExpressionField.Name,
BindingFlags.Static | BindingFlags.NonPublic);
// Store the compiled expression in the static field
compiledExpressionFieldInfo.SetValue(null, compiledExpression);
var newDelegate = new DynamicMethod($"{Guid.NewGuid()}",
typeof(IOrderedEnumerable<T>),
new[] { typeof(IEnumerable<T>) },
typeof(ILGen), true);
var il = newDelegate.GetILGenerator();
// Load the array passed into the Delegate (T[])
il.Emit(OpCodes.Ldarg_0);
// Load the compiled expression from a static field
il.Emit(OpCodes.Ldsfld, compiledExpressionFieldInfo);
// Call .OrderByDescending()
il.Emit(OpCodes.Call, orderByDescMethod);
// Call .ToArray()
il.Emit(OpCodes.Call, toArrayMethod);
il.Emit(OpCodes.Ret); // Stores the sorted array
return (FilterDelegate<T>)newDelegate.CreateDelegate(typeof(FilterDelegate<T>));
}
- 1 回答
- 0 關(guān)注
- 129 瀏覽
添加回答
舉報(bào)