gpt4 book ai didi

c# - 通过 ILGenerator 调用带谓词表达式的 LINQ

转载 作者:太空宇宙 更新时间:2023-11-03 22:37:37 25 4
gpt4 key购买 nike

我正在尝试通过在运行时发出 IL 来编译 DynamicMethod。我希望它执行以下操作:

array.OrderByDesc( /* Select Field/Property Expression*/ ).ToArray();

编译 DynamicMethod 的方法有一个 FieldInfo 变量,我想将其用于 OrderByDesc 需要的表达式。

这是我目前所拥有的:

public static FilterDelegate<T> CreateDelegate<T>( Expression<Func<T, double>> expression )
{
var field = expression.GetFieldInfo();// Extension, gets FieldInfo from expression
...
il.Emit( OpCodes.Ldloc_1 ); // Loads an array (T[])
il.Emit( OpCodes.Call, typeof( Enumerable ).GetMethod( nameof( Enumerable.OrderByDescending ), new Type[0]).MakeGenericMethod( typeof( T ) ) );
il.Emit( OpCodes.Call, typeof( Enumerable ).GetMethod( nameof( Enumerable.ToArray ) ).MakeGenericMethod( typeof( T ) ) );
il.Emit( OpCodes.Stloc_1 ); // Stores the sorted array
}

一些注意事项:

  • 提供的表达式是一个选择器,指定在整个编译方法中使用哪个字段(或属性支持值)。
  • 此方法不仅仅调用 OrderByDescending(),还包含大量低级优化。排除排序,预计大部分情况下运行时间在40ns以内。

如何使用传递给编译方法的表达式或 FieldInfo 来正确调用 OrderByDescending()

最佳答案

我不完全理解您试图通过直接 IL 生成实现什么; OrderByDescending需要 Func<TSource, TKey>名为“keySelector”的参数。因此,您在仍然使用此方法的同时可以生成的唯一 IL 只是一个常规方法调用,它将“keySelector”参数传递给 OrderByDescending方法,除非您打算重新实现 OrderByDescending在伊利诺伊州。

有什么理由让您需要一直下降到 IL?

如果这是用户级代码,您可以“编译”expression那会被传递给这个方法并调用OrderByDescending()通常例如

var expression = /* Select Field/Property Expression*/;
array.OrderByDescending(expression.Compile()).ToArray();

如果这是框架/实用程序级别的代码,您可能会使用“表达式树”而无需一直使用手动 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();
}

但是,如果出于某种原因您仍然需要通过 IL 直接发出它,那么可以使用类似以下的方法。

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

关于c# - 通过 ILGenerator 调用带谓词表达式的 LINQ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54210315/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com