2008-12-06 10 views

Odpowiedz

10

Oto przykład z obu podejść. Jeśli coś przeoczyłem lub chcesz uzyskać więcej informacji, daj mi znać.

static void Main() 
{ 
    // try to do "x + (3 * x)" 

    var single = BuildSingle<decimal>(); 
    var composite = BuildComposite<decimal>(); 

    Console.WriteLine("{0} vs {1}", single(13.2M), composite(13.2M)); 
} 
// utility method to get the 3 as the correct type, since there is not always a "int x T" 
static Expression ConvertConstant<TSource, TDestination>(TSource value) 
{ 
    return Expression.Convert(Expression.Constant(value, typeof(TSource)), typeof(TDestination)); 
} 
// option 1: a single expression tree; this is the most efficient 
static Func<T,T> BuildSingle<T>() 
{   
    var param = Expression.Parameter(typeof(T), "x"); 
    Expression body = Expression.Add(param, Expression.Multiply(
     ConvertConstant<int, T>(3), param)); 
    var lambda = Expression.Lambda<Func<T, T>>(body, param); 
    return lambda.Compile(); 
} 
// option 2: nested expression trees: 
static Func<T, T> BuildComposite<T>() 
{ 

    // step 1: do the multiply: 
    var paramInner = Expression.Parameter(typeof(T), "inner"); 
    Expression bodyInner = Expression.Multiply(
     ConvertConstant<int, T>(3), paramInner); 
    var lambdaInner = Expression.Lambda(bodyInner, paramInner); 

    // step 2: do the add, invoking the existing tree 
    var paramOuter = Expression.Parameter(typeof(T), "outer"); 
    Expression bodyOuter = Expression.Add(paramOuter, Expression.Invoke(lambdaInner, paramOuter)); 
    var lambdaOuter = Expression.Lambda<Func<T, T>>(bodyOuter, paramOuter); 

    return lambdaOuter.Compile(); 
} 

Osobiście zmierzałem w kierunku pierwszej metody; jest prostszy i bardziej wydajny. Może to obejmować przekazywanie oryginalnego parametru przez cały stos kodu zagnieżdżonego, ale niech tak będzie. Mam gdzieś kod, który przyjmuje podejście "Invoke" (kompozyt) i ponownie zapisuje drzewo jako pierwsze podejście (pojedyncze) - ale jest dość skomplikowany i długi. Ale bardzo przydatne dla Entity Framework (która nie obsługuje Expression.Invoke).

+0

To załatwiło sprawę ... Wielkie dzięki. –

12

Trzeba stworzyć lambda - tj

var lambda = Expression.Lambda<Func<float,int>>(body, param); 
Func<float,int> method = lambda.Compile(); 
int v = method(1.0); // test 

gdzie "ciało" jest drzewo wyrażenie (biorąc pływaka, wracając int) obejmujące ParameterExpression param.

Można również znaleźć this i this pomocne.

+0

Problem dotyczy ((x + 2) + y)/z Jeśli w drzewie znajduje się więcej niż jedna część parametrów pod wyrażeń, co mam zrobić? –

+0

Możliwe są różne parametry (jest to tablica parametrów ParameterExpression); dla wyrażeń podrzędnych trzeba wywołać wyrażenie wewnętrzne (Wyrażenie.Zgoda?) –

+0

Można również zbudować całe drzewo w pojedynczym wyrażeniu; jest to nieco bardziej wydajne, ale jest bardziej skomplikowane. –