Вызов LINQ с выражением предиката через ILGenerator

Swift 4:

let x:Int = 45
let str:String = String(describing: x)

Developer.Apple.com> String> init (описание:)

1
задан Haus 16 January 2019 в 14:55
поделиться

1 ответ

Я не совсем понимаю, чего вы пытаетесь достичь с помощью прямого поколения IL; OrderByDescending принимает параметр Func<TSource, TKey> с именем «keySelector». Таким образом, единственным IL, который вы можете сгенерировать, все еще используя этот метод, будет просто обычный вызов метода, который передает аргумент «keySelector» методу OrderByDescending, если только вы не собираетесь повторно реализовать OrderByDescending в IL.

Есть ли причина, по которой вам нужно полностью перейти к 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>));
}
0
ответ дан Joshua Webb 16 January 2019 в 14:55
поделиться
Другие вопросы по тегам:

Похожие вопросы: