2008-12-06 14 views

cevap

10

Her iki yaklaşımın bir örneği. Bir şeyi özlediysem veya daha fazla bilgi istersen, bana haber ver.

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(); 
} 

Şahsen, ilk yönteme yöneliktir; hem daha basit hem de daha verimli. Bu, orijinal parametrenin iç içe geçmiş bir kod yığını boyunca geçirilmesini içerebilir, ancak öyle olsun. "Invoke" yaklaşımını (kompozit) alan ve ağacı ilk yaklaşım olarak (tek) yeniden yazan bir kod var ama oldukça karmaşık ve uzun. Ancak Entity Framework (Expression.Invoke'yi desteklemeyen) için çok kullanışlıdır.

+0

Bu hile yaptı ... Çok teşekkürler. –

12

Bir lambda oluşturmanız gerekir - yani

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

burada "beden" ifadeniz ağaç ParameterExpression param içeren (bir int döndüren bir şamandıra alarak) 'dir.

this ve this yardımcı olabilir.

+0

Sorun şu ki ((x + 2) + y)/z Ağaçta farklı alt ifadelerin bir bölümü birden fazla parametresi olduğunda, ne yapmalıyım? –

+0

Çoklu parametreler mümkündür (ParametreExpression'un params dizisidir); Alt ifadeler için iç ifadeyi çağırmanız gerekir (Expression.Invoke?) –

+0

Ayrıca tüm ağacı tek bir ifadeye de oluşturabilirsiniz; Bu biraz daha verimli, ancak daha karmaşık. –

İlgili konular