При компиляции LambdaExpression с вложенными LambdaExpressions будут ли они компилироваться? - PullRequest
0 голосов
/ 07 февраля 2019

При построении LambdaExpression во время выполнения, если я использую LambdaExpression в качестве параметра для выражения вызова (например, при использовании Linq), затем компилируется основная лямбда, компилируется ли вложенная лямбда или она нужна?

Код работает так же, если я использую LambdaExpression в качестве параметра для метода, принимающего Func<T, T2>, или если я скомпилирую его и использую Expression.Constant вместо скомпилированного Func.

Не скомпилировано:

    var selectParam = Expression.Parameter(propType, "selectParam");
    var selectExp = Expression.Call(typeof(System.Linq.Enumerable).GetMethods().First(a => a.Name == "Select" && /*Func<TSource,TResult>*/ a.GetParameters().Last().ParameterType.GenericTypeArguments.Length == 2).MakeGenericMethod(propType, typeof(int)),
                                    whereExp,
                                    Expression.Lambda(Expression.Property(selectParam, "Length"), selectParam));

Скомпилировано:

    var selectParam = Expression.Parameter(propType, "selectParam");
    var selectExp = Expression.Call(typeof(System.Linq.Enumerable).GetMethods().First(a => a.Name == "Select" && /*Func<TSource,TResult>*/ a.GetParameters().Last().ParameterType.GenericTypeArguments.Length == 2).MakeGenericMethod(propType, typeof(int)),
                                    whereExp,
                                    Expression.Constant(Expression.Lambda(Expression.Property(selectParam, "Length"), selectParam).Compile())); //compile

Выражения, которые я создаю, вызываются в цикле миллионы раз, поэтому я хотел бы знать, компилирует ли внутренняя лямбда компиляцию внешней лямбдыправильно.

Поскольку это не легко объяснить, см. мою скрипку здесь .

Я почти уверен, что они не будут бытьскомпилировано так, что вызываемые методы могут захотеть, чтобы они были выражениями для их анализа.В этом случае, есть ли прирост производительности во время выполнения, чтобы скомпилировать их, когда используется так?

Мышление на более высоком уровне, при стандартном использовании в цикле - это вообще оптимизировано?Разумеется, они не компилируются при каждом вызове при выполнении linq над массивом или чем-то подобным?

1 Ответ

0 голосов
/ 07 февраля 2019

Краткий ответ: да, каждая внутренняя лямбда будет скомпилирована.

Я немного изменил ваш первый метод (но он генерирует то же выражение):

private static Expression<Func<int>> ActuallyInnerAlsoCompile()
{
    var strType = typeof(string);
    var intType = typeof(int);
    var enumearbleType = typeof(Enumerable);

    var array = Expression.NewArrayInit(strType, Expression.Constant("test"), Expression.Constant("test2"));

    var x = Expression.Parameter(strType, "whereParam");
    var whereExp = Expression.Call(enumearbleType,
        "Where",
        new[] {strType},
        array,
        Expression.Lambda(Expression.NotEqual(Expression.PropertyOrField(x, "Length"), Expression.Constant(4)), x));

    var selectExp = Expression.Call(enumearbleType,
        "Select",
        new[] {strType, intType},
        whereExp,
        Expression.Lambda(Expression.PropertyOrField(x, "Length"), x));

    var firstOrDefault = Expression.Call(enumearbleType,
        "FirstOrDefault",
        new[] {intType},
        selectExp);

    return Expression.Lambda<Func<int>>(firstOrDefault);
}

Теперь вы можете обратиться к этому ответу и скомпилировать ваше выражение в новую сборку:

var lambda = ActuallyInnerAlsoCompile();

var dynamicAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(
    new AssemblyName("dynamicAssembly"),
    AssemblyBuilderAccess.Save);

var dm = dynamicAssembly.DefineDynamicModule("dynamicModule", "dynamic.dll");
var dt = dm.DefineType("dynamicType");
var m1 = dt.DefineMethod(
    "dynamicMethod",
    MethodAttributes.Public | MethodAttributes.Static);

lambda.CompileToMethod(m1);
dt.CreateType();

dynamicAssembly.Save("dynamic.dll");

Если вы откроете dynamic.dll с помощью какого-либо инструмента IL (например, dotPeek), вы увидите что-то вроде:

// Decompiled with JetBrains decompiler
// Type: dynamicType
// Assembly: dynamicAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
// MVID: 94346EDD-3BCD-4EB8-BA4E-C25343918535

using System;
using System.Collections.Generic;
using System.Linq;

internal class dynamicType
{
  public static int dynamicMethod()
  {
    return ((IEnumerable<string>) new string[2]
    {
      "test",
      "test2"
    }).Where<string>(new Func<string, bool>(dynamicType.\u003CExpressionCompilerImplementationDetails\u003E\u007B1\u007Dlambda_method)).Select<string, int>(new Func<string, int>(dynamicType.\u003CExpressionCompilerImplementationDetails\u003E\u007B2\u007Dlambda_method)).FirstOrDefault<int>();
  }

  private static bool \u003CExpressionCompilerImplementationDetails\u003E\u007B1\u007Dlambda_method(string whereParam)
  {
    return whereParam.Length != 4;
  }

  private static int \u003CExpressionCompilerImplementationDetails\u003E\u007B2\u007Dlambda_method(string whereParam)
  {
    return whereParam.Length;
  }
}

Или (без некрасивых escape-последовательностей Unicode)

...