代码之家  ›  专栏  ›  技术社区  ›  Sebastian Good

我可以将局部变量作为常量而不是闭包引用捕获到LINQ表达式中吗?

  •  18
  • Sebastian Good  · 技术社区  · 14 年前

    我想说

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

    x/y 一次而不是每次调用,通过指针取消对闭包记录的引用。

    我不想用手来表达。有什么好主意吗?

    :我最终使用了 LinqKit

    /// <summary>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.</summary>
    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);
            }
        }
    }
    
    5 回复  |  直到 14 年前
        1
  •  4
  •   JaredPar    14 年前

    不,在C#没有办法。编译器不支持按值/常量捕获变量。也不能在运行时以这种方式将非常量值转换为常量值。

    此外,C编译器只在初始编译期间对已知的常量值进行常量折叠。如果可以在运行时将一个值冻结为一个常量,那么它就不会参与编译器常量的折叠,因为它是在运行时发生的。

        2
  •  2
  •   Lucero    14 年前

    编译器不执行这种类型的“值缓存”。常量折叠只在编译时对常量执行,而不是对只读字段执行,当然也不是对编译时没有已知值的局部变量执行。

    int x = magic(), y = moremagic();
    int xy = x/y;
    return i => i + xy;
    
        3
  •  0
  •   StriplingWarrior    14 年前

    x 不能是常量,因为您正在执行运行时魔术来确定它是什么。但是,如果你知道 y 不要改变,尝试:

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

    i => i + (x/y) 将显示分区,JIT编译器几乎肯定会优化这一点。

        4
  •  0
  •   supercat    14 年前

    FunctionOf.NewInv()
    

    是一个静态函数,它接受函数(稍后描述)、T3和T4作为参数。传入函数应接受T2、T3和T4类型的参数,并返回T1。NewInv返回的函数将接受一个T2类型的参数,并用该参数调用传入的函数以及给NewInv的参数。

    调用将类似于:

    return FunctionOf.NewInv((i,x,y) => i+x/y, x, y)
    
        5
  •  -1
  •   xumix    8 年前

    如果您(像我一样)正在为SQL查询创建一些表达式生成器,可以考虑以下问题:首先创建一个类变量,使其成为常量,然后按如下方式访问它:

    var constant= Expression.Constant(values);
    var start = Expression.MakeMemberAccess(constant, values.GetMemberInfo(f => f.Start));
    var end = Expression.MakeMemberAccess(constant, values.GetMemberInfo(f => f.End));
    
    var more = Expression.GreaterThanOrEqual(memberBody, start);
    var less = Expression.LessThanOrEqual(memberBody, end);