2012-06-22 21 views
9

Considerando questo codice:Sostituire parametro espressione lambda

public class Foo 
{ 
    public int a { get; set; } 
    public int b { get; set; } 
} 

private void Test() 
{ 
    List<Foo> foos = new List<Foo>(); 
    foos.Add(new Foo()); 
    foos.Add(new Foo()); 
    Expression<Func<Foo, int>> exp0 = f => f.a * f.b; 
    Expression<Func<int>> exp1 =() => foos[0].a * foos[0].b; 
    Expression<Func<int>> exp2 =() => foos[1].a * foos[1].b; 
} 

Come si può prendere exp0 e di trasformarlo in due espressioni identiche a exp1 e exp2. Si noti che non voglio valutare solo exp0 per ogni Foo in foos, ma invece ottenere due nuove espressioni.

[Aggiornamento]:

Fondamentalmente, voglio essere in grado di espandere o "appiattire" espressione passato a un metodo Linq estensione come Sum in un'espressione per ogni elemento nell'enumerazione poiché queste enumerazioni volontà essere statico, e perché ho già un codice che legge espressioni che non prendono parametri (e poi li trasforma in un'altra lingua).

sto usando il MetadataToken come riferimenti a proprietà che hanno un determinato attributo (in questo caso a e b avrebbe questo attributo) e usarlo con un dizionario che correla C# oggetti da variabili di un'altra lingua:

Foo foo = new Foo(); 
Expression<Func<int>> exp =() => foo.a * foo.a + foo.b; 
string result1 = GetResult(exp); // gets "v_001 * v_001 + v_002" 

List<Foo> foes = new List<Foo>(); 
foes.Add(new Foo()); 
foes.Add(new Foo()); 
Expression<Func<int>> exp2 =() => foes.Sum(f => f.a * f.a + f.b); 
string result2 = GetResult(exp2); // should get "(v_001 * v_001 + v_002) + (v_003 * v_003 + v_004)" 
+0

Puoi darci un esempio di come useresti questo e quale sarebbe l'output? –

+0

Vedere anche http://stackoverflow.com/questions/5631070/currying-expressions-in-c-sharp – AakashM

risposta

15

vorrei fare in questo modo:

Scrivi un'espressione-visitatore parametro-sostituto che manipola l'espressione originale come segue:

  1. Elimina il parametro che non vuoi interamente dalla firma lambda.
  2. Sostituisce tutti gli usi del parametro con l'espressione dell'indicizzatore desiderata.

Ecco un esempio rapido e sporco ho scatenato in base alla mia earlier answer su una questione diversa:

public static class ParameterReplacer 
{ 
    // Produces an expression identical to 'expression' 
    // except with 'source' parameter replaced with 'target' expression.  
    public static Expression<TOutput> Replace<TInput, TOutput> 
        (Expression<TInput> expression, 
        ParameterExpression source, 
        Expression target) 
    { 
     return new ParameterReplacerVisitor<TOutput>(source, target) 
        .VisitAndConvert(expression); 
    } 

    private class ParameterReplacerVisitor<TOutput> : ExpressionVisitor 
    { 
     private ParameterExpression _source; 
     private Expression _target; 

     public ParameterReplacerVisitor 
       (ParameterExpression source, Expression target) 
     { 
      _source = source; 
      _target = target; 
     } 

     internal Expression<TOutput> VisitAndConvert<T>(Expression<T> root) 
     { 
      return (Expression<TOutput>)VisitLambda(root); 
     } 

     protected override Expression VisitLambda<T>(Expression<T> node) 
     { 
      // Leave all parameters alone except the one we want to replace. 
      var parameters = node.Parameters 
           .Where(p => p != _source); 

      return Expression.Lambda<TOutput>(Visit(node.Body), parameters); 
     } 

     protected override Expression VisitParameter(ParameterExpression node) 
     { 
      // Replace the source with the target, visit other params as usual. 
      return node == _source ? _target : base.VisitParameter(node); 
     } 
    } 
} 

Uso per lo scenario (testato):

var zeroIndexIndexer = Expression.MakeIndex 
     (Expression.Constant(foos), 
     typeof(List<Foo>).GetProperty("Item"), 
     new[] { Expression.Constant(0) }); 


// .ToString() of the below looks like the following: 
// () => (value(System.Collections.Generic.List`1[App.Foo]).Item[0].a 
//   * value(System.Collections.Generic.List`1[App.Foo]).Item[0].b) 
var exp1Clone = ParameterReplacer.Replace<Func<Foo, int>, Func<int>> 
        (exp0, exp0.Parameters.Single(), zeroIndexIndexer); 
+1

Per i lambda nidificati, ho trovato che devo usare Expression.Lambda (...) invece di Expression.Lambda (. ..). La versione del parametro di tipo sembra in ogni caso non necessaria. – HappyNomad