2013-05-21 61 views
1

我有下面的代碼是僞代碼。 我想讓這個函數可以接受一個Expresstion類型,編譯這個表達式並用適當的參數調用它。c中的表達式和委託#

public static void f<T>(Expression<T> exp, params dynamic[] d) 
{ 
    Console.WriteLine("begin"); 
    exp.Compile().Invoke(d[0],d[1].....);//this is pseudo-code 

    Console.WriteLine("end"); 
} 

我確定T是一個動作類型。 (T可以是Action,Action<int>等)。參數d是一個動態類型的數組,它被髮送到調用。

但我不知道如何完成代碼。我相信實現它並不容易。也許它不可能是真的在c#

回答

3

你不能使用Invoke,除非你知道確切的簽名。你可以,但是,使用DynamicInvoke,例如:

((Delegate)exp.Compile()).DynamicInvoke(d); 

注意的是,在上述dynamic無濟於事 - d也可以同樣是object[]

其他的,稍微複雜一些,的方法 - 將其編譯爲Func<object[]>,並用p[n],其中p是一個重新寫入表達式(ExpressionVisitor)取代「參數n」(從原始exp)單個​​和nConstantExpressionn。這可能是有利的如果你要存儲並積極地重新使用編譯的lambda。但是在您的特定場景中,您正在編譯每次調用,因此這沒有任何好處。

下面是一個例子,但這主要是爲了後面的讀者使用類似的情況,但編譯後的代表被重用;這個重寫的「優點」是它避免了Delegate.DynamicInvoke的性能影響,同時保留簽名Delegate.DynamicInvoke;但是這隻會在委託多次使用時纔有用。目前(每次調用編譯)這裏的大部分「工作」將在表達式編譯和JIT編譯中進行。

using System; 
using System.Collections.Generic; 
using System.Linq.Expressions; 
static class Program { 
    static void Main() { 
     Expression<Func<int, float, double>> exp = (i, f) => i * f; 
     var func = CompileToBasicType(exp); 

     object[] args = { 3, 2.3F }; 
     object result = func(args); // 6.9 (double) 
    } 

    static Func<object[], object> CompileToBasicType(LambdaExpression exp) { 
     ParameterExpression arg = 
      Expression.Parameter(typeof(object[]), "args"); 
     Dictionary<Expression, Expression> lookup = 
      new Dictionary<Expression, Expression>(); 
     int i = 0; 
     foreach (var p in exp.Parameters) { 
      lookup.Add(p, Expression.Convert(Expression.ArrayIndex(
       arg, Expression.Constant(i++)), p.Type)); 
     } 
     var body = Expression.Convert(
      new ReplaceVisitor(lookup).Visit(exp.Body), typeof(object)); 
     return Expression.Lambda<Func<object[], object>>(body, arg).Compile(); 
    } 
    class ReplaceVisitor : ExpressionVisitor { 
     private readonly Dictionary<Expression, Expression> lookup; 
     public ReplaceVisitor(Dictionary<Expression, Expression> lookup) { 
      if (lookup == null) throw new ArgumentNullException("lookup"); 
      this.lookup= lookup; 
     } 
     public override Expression Visit(Expression node) { 
      Expression found; 
      return lookup.TryGetValue(node, out found) ? found 
       : base.Visit(node); 
     } 
    } 
} 
2
public static void F<T>(Expression<T> exp, params object[] d) 
{ 
    Console.WriteLine("begin"); 

    var del = exp.Compile() as Delegate; 
    del.DynamicInvoke(d); 

    Console.WriteLine("end"); 
} 

然後:

F<Action<int>>(i => Console.WriteLine(i), 5); 

或:

F<Action<string, int>>((s, i) => Console.WriteLine("{0} : {1}", s, i), "Hello", 5);