Могу ли я записать локальную переменную в выражение LINQ как константу, а не ссылку на замыкание?

Я хотел бы сказать

int x = magic(), y = moremagic();
return i => i + (x/y);

и записать x как константу вместо ссылки на переменную. Идея состоит в том, что x никогда не изменится, и поэтому, когда выражение будет позже скомпилировано, компилятор может выполнять сворачивание констант и производить более эффективный код, то есть вычислять x / y один раз вместо каждого вызова через разыменование указателя в закрывающую запись.

Невозможно пометить x как доступный только для чтения в методе, и компилятор недостаточно умен, чтобы обнаружить, что он не изменяется после создания выражения.

Мне бы очень не хотелось создавать Выражение от руки. Есть какие-нибудь блестящие идеи?

ОБНОВЛЕНИЕ : В итоге я использовал чудесный LinqKit для создания частичного вычислителя, который будет выполнять нужные мне замены. Преобразование безопасно только в том случае, если вы знаете, что соответствующие ссылки не изменятся, но это сработало для моих целей. Можно ограничить частичную оценку только непосредственными членами вашего замыкания, которые вы контролируете, добавив туда одну или две дополнительные проверки, что довольно очевидно при просмотре примера кода, предоставленного в LinqKit.

/// Walks your expression and eagerly evaluates property/field members and substitutes them with constants.
/// You must be sure this is semantically correct, by ensuring those fields (e.g. references to captured variables in your closure)
/// will never change, but it allows the expression to be compiled more efficiently by turning constant numbers into true constants, 
/// which the compiler can fold.
public class PartiallyEvaluateMemberExpressionsVisitor : ExpressionVisitor
{
    protected override Expression VisitMemberAccess(MemberExpression m)
    {
        Expression exp = this.Visit(m.Expression);

        if (exp == null || exp is ConstantExpression) // null=static member
        {
            object @object = exp == null ? null : ((ConstantExpression)exp).Value;
            object value = null; Type type = null;
            if (m.Member is FieldInfo)
            {
                FieldInfo fi = (FieldInfo)m.Member;
                value = fi.GetValue(@object);
                type = fi.FieldType;
            }
            else if (m.Member is PropertyInfo)
            {
                PropertyInfo pi = (PropertyInfo)m.Member;
                if (pi.GetIndexParameters().Length != 0)
                    throw new ArgumentException("cannot eliminate closure references to indexed properties");
                value = pi.GetValue(@object, null);
                type = pi.PropertyType;
            }
            return Expression.Constant(value, type);
        }
        else // otherwise just pass it through
        {
            return Expression.MakeMemberAccess(exp, m.Member);
        }
    }
}

18
задан Sebastian Good 21 October 2010 в 22:50
поделиться