2011-10-18 63 views
12

這是使用代碼最好的解釋。我有一個泛型類,它有一個返回整數的方法。下面是一個簡單的版本用於解釋的目的...如何在類型直到運行時才知道時創建Expression.Lambda?

public class Gen<T> 
{ 
    public int DoSomething(T instance) 
    { 
     // Real code does something more interesting! 
     return 1; 
    } 
} 

在運行時我使用反射來發現的一些類型,然後想爲特定類型的創建我的根類的一個實例。這是很容易像這樣做...

Type fieldType = // This is the type I have discovered 
Type genericType = typeof(Gen<>).MakeGenericType(fieldType); 
object genericInstance = Activator.CreateInstance(genericType); 

我現在要創建一個將作爲一個參數的泛型類型的實例表達式,然後調用類型的DoSomething的方法。所以我想表達的有效執行此...

int answer = genericInstance.DoSomething(instance); 

...除了我沒有「實例」,直到某個點以後在運行時和genericInstance是生成的類型,從上面可以看出。我在創建拉姆達這是如下的嘗試......

MethodInfo mi = genericType.GetMethod("DoSomething", 
             BindingFlags.Instance | BindingFlags.Public); 

var p1 = Expression.Parameter(genericType, "generic"); 
var p2 = Expression.Parameter(fieldType, "instance"); 

var x = Expression.Lambda<Func<genericType, fieldType, int>> 
      (Expression.Call(p1, mi, p2), 
      new[] { p1, p2 }).Compile(); 

...因此以後我可以像這樣把它...

int answer = x(genericInstance, instance); 

當然,你無法爲Func提供實例參數,因此我不知道如何參數化Lambda代。有任何想法嗎?

回答

18

我想你只想使用接受委託類型爲類型而不是作爲一個通用的Expression.Lambda,並動態地創建你的函數功能就像你是Gen<>

MethodInfo mi = genericType.GetMethod("DoSomething", 
           BindingFlags.Instance | BindingFlags.Public); 

var p1 = Expression.Parameter(genericType, "generic"); 
var p2 = Expression.Parameter(fieldType, "instance"); 
var func = typeof (Func<,,>); 
var genericFunc = func.MakeGenericType(genericType, fieldType, typeof(int)); 
var x = Expression.Lambda(genericFunc, Expression.Call(p1, mi, p2), 
       new[] { p1, p2 }).Compile(); 

這將返回委託而不是強類型的Func,但是如果需要的話,你當然可以施放它(如果你不知道你在施展什麼,看起來很困難),或者使用DynamicInvoke動態調用它。

int answer = (int) x.DynamicInvoke(genericInstance, instance); 

編輯

一個很好的想法,確實工作。不幸的是,我想使用強類型編譯Lambda的原因是性能。與類型化的Lambda相比,使用DynamicInvoke非常慢。

這似乎工作,而不需要動態調用。

var p1 = Expression.Parameter(genericType, "generic"); 
var p2 = Expression.Parameter(fieldType, "instance"); 
var func = typeof(Func<,,>); 
var genericFunc = func.MakeGenericType(genericType, fieldType, typeof(int)); 
var x = Expression.Lambda(genericFunc, Expression.Call(p1, mi, p2), new[] { p1, p2 }); 
var invoke = Expression.Invoke(x, Expression.Constant(genericInstance), Expression.Constant(instance)); 
var answer = Expression.Lambda<Func<int>>(invoke).Compile()(); 

編輯2

大大簡化版本:

Type fieldType = ;// This is the type I have discovered 
Type genericType = typeof(Gen<>).MakeGenericType(fieldType); 
object genericInstance = Activator.CreateInstance(genericType); 
MethodInfo mi = genericType.GetMethod("DoSomething", 
           BindingFlags.Instance | BindingFlags.Public); 
var value = Expression.Constant(instance, fieldType); 
var lambda = Expression.Lambda<Func<int>>(Expression.Call(Expression.Constant(genericInstance), mi, value)); 
var answer = lambda.Compile()(); 
+0

一個確實有效的好主意。不幸的是,我想使用強類型編譯Lambda的原因是性能。與類型化的Lambda相比,使用DynamicInvoke非常慢。 –

+0

是否可以捕獲表達式樹中的變量?我會幫助捕獲genericInstance,因爲這永遠不會改變。 –

+0

@PhilWright嗯,我明白了。讓我看看我還能想出什麼。 – vcsjones

1

這個答案,如果你使用的是.NET 4.0只適用。

如果您genericInstancedynamic,而不是object,您可以然後直接調用就可以了DoSomething方法,動態語言運行時會照顧一切的爲您服務。

class Type1 { 
    public int DoSomething() { return 1; } 
} 
class Type2 { 
    public int DoSomething() { return 2; } 
} 

static void TestDynamic() { 
    dynamic t1 = Activator.CreateInstance(typeof(Type1)); 
    int answer1 = t1.DoSomething(); // returns 1 

    dynamic t2 = Activator.CreateInstance(typeof(Type2)); 
    int answer2 = t2.DoSomething(); // returns 2 
} 

如果你需要保持這種類結構(Gen<T>),然後我看不出迴避的事實,你不知道在編譯時的類型T一個簡單的方法。如果要調用委託,則必須在編譯時知道其完整類型,或者需要將參數作爲對象傳遞。

使用dynamic可以讓您隱藏獲取MethodInfo等的複雜性,併爲您提供出色的性能。與我看到的DynamicInvoke相比,一個缺點是我相信你會爲每個呼叫站點解決一次動態呼叫的初始開銷。如果您使用相同類型的對象調用綁定,綁定將被緩存,以便從第二次開始運行得非常快。

0

最好接受object並使用convert爲已知類型。

下面是一個例子,如何建立對未知的深度訪問財產名稱:

var model = new { A = new { B = 10L } }; 
string prop = "A.B"; 
var parameter = Expression.Parameter(typeof(object)); 
Func<object, long> expr = (Func<object, long>) Expression.Lambda(prop.Split('.').Aggregate<string, Expression>(Expression.Convert(parameter, model.GetType()), Expression.Property), parameter).Compile(); 
expr(model).Dump(); 

它避免DynamicInvoke的額外費用時,委託類型在編譯時是未知的。

相關問題