Как оптимизировать выражения LINQ? - PullRequest
3 голосов
/ 18 мая 2011

В проекте, созданном на .NET 3.5, я использую выражения LINQ для динамического создания кода во время выполнения. Выражения LINQ компилируются с использованием метода Compile и сохраняются для последующего использования в качестве предикатов с LINQ для объектов.

Выражения иногда довольно сложны и трудны для отладки.

Ниже приведен пример выражения, просматриваемого с помощью визуализатора отладчика в Visual Studio.

{запрос => (Invoke (workEnvelopeHead => (workEnvelopeHead.Method = значение (Wombl.Scenarios.CannedResponses + <> c_ DisplayClass58) .pipeline), request.WorkEnvelope.Head) И вызывать (body => Invoke (значение (Wombl.Scenarios.CannedResponses + <> с _DisplayClass78) .isMatch, body.SingleOrDefault ()), конвертировать (request.WorkEnvelope.Body.Any)))}

Я хотел бы иметь возможность оптимизировать выражения, подобные приведенным выше, чтобы выражение value(Wombl.Scenarios.CannedResponses+<>c__DisplayClass58).pipeline заменялось константой, которая является значением переменной.

В данном конкретном случае value(Wombl.Scenarios.CannedResponses+<>c__DisplayClass58).pipeline является ссылкой в ​​лямбда-выражении на переменную в родительской области видимости. Что-то вроде:

var pipeline = "[My variable's value here]";
// My lambda expression here, which references pipeline
// Func<RequestType, bool> predicate = request => ........ workEnvelopeHead.Method == pipeline ..........

Исходное оптимизированное выражение должно выглядеть так:

{request => (Invoke (workEnvelopeHead => (workEnvelopeHead.Method = "[Здесь значение моей переменной]", request.WorkEnvelope.Head) и Invoke (body =>> Invoke (значение (Wombl.Scenarios.CannedResponses + <> c__DisplayClass78) .isMatch, body.SingleOrDefault ()), конвертировать (request.WorkEnvelope.Body.Any)))}

Как я могу выполнить такую ​​оптимизацию во время выполнения для выражения LINQ перед компиляцией?

1 Ответ

1 голос
/ 18 мая 2011

Итак, я написал выражение посетителя, которое заменяет ссылки на переменные фактическим значением.В конце концов это было не так сложно.

Использование:

var simplifiedExpression = ExpressionOptimizer.Simplify(complexExpression);

Класс:

Он наследуется от ExpressionVisitor, который поступил из примеров кода на эта страница потому что в .NET 3.0 она внутренняя.В .NET 4.0 класс общедоступен, но может потребовать некоторых изменений в этом классе.

public sealed class ExpressionOptimizer : ExpressionVisitor
{
    private ExpressionOptimizer()
    {
    }

    #region Methods

    public static Expression<TDelegate> Simplify<TDelegate>(Expression<TDelegate> expression)
    {
        return expression == null
                   ? null
                   : (Expression<TDelegate>) new ExpressionOptimizer().Visit(expression);
    }

    private static bool IsPrimitive(Type type)
    {
        return type.IsPrimitive
               || type.IsEnum
               || type == typeof (string)
               || type == typeof (DateTime)
               || type == typeof (TimeSpan)
               || type == typeof (DateTimeOffset)
               || type == typeof (Decimal)
               || typeof(Delegate).IsAssignableFrom(type);
    }

    protected override Expression VisitMemberAccess(MemberExpression memberExpression)
    {
        var constantExpression = memberExpression.Expression as ConstantExpression;

        if (constantExpression == null || !IsPrimitive(memberExpression.Type))
            return base.VisitMemberAccess(memberExpression);

        // Replace the MemberExpression with a ConstantExpression
        var constantValue = constantExpression.Value;
        var propertyInfo = memberExpression.Member as PropertyInfo;
        var value = propertyInfo == null
                        ? ((FieldInfo) memberExpression.Member).GetValue(constantValue)
                        : propertyInfo.GetValue(constantValue, null);

        return Expression.Constant(value);
    }

    #endregion
}
...