me gustaría decir¿Puedo capturar una variable local en una expresión LINQ como una constante en lugar de una referencia de cierre?
int x = magic(), y = moremagic();
return i => i + (x/y);
y tienen el x ser capturado como una constante en lugar de una referencia variable. La idea es que x nunca cambiará y entonces cuando la expresión se compila más adelante, el compilador puede hacer un plegado constante y producir un código más eficiente, es decir, calculando x/y
una vez en vez de cada llamada, mediante desreferencias de puntero en un registro de cierre.
No hay manera de marcar x como readonly dentro de un método, y el compilador no es lo suficientemente inteligente como para detectar que no cambia después de la creación de la expresión.
No me gustaría tener que construir la expresión a mano. ¿Alguna idea brillante?
ACTUALIZACIÓN: Terminé usando la maravillosa LinqKit para construir un evaluador parcial que va a hacer las sustituciones que quiero. La transformación solo es segura si sabes que las referencias relevantes no cambiarán, pero funcionó para mis propósitos. Es posible restringir la evaluación parcial solo a los miembros directos de su cierre, que usted controla, agregando un cheque adicional o dos, lo que es bastante obvio al inspeccionar el código de muestra proporcionado en el 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);
}
}
}
justo lo que estaba buscando! – jeroenh
Sé que ha pasado un tiempo, pero esto me ha ahorrado mucho tiempo. Gracias. – Stargazer