2013-12-22 45 views
1

我試圖創建一個擴展方法,該方法生成並存儲已編譯的Lambda表達式,並返回表單類型的泛型類型類的實例,其中Class和T只在運行時才知道(由用戶選擇驅動)。爲僅在運行時已知的類型創建編譯後的Expession.Lambda

由於我是表達式樹新手,我正在努力研究出正確的語法。下面

的「makeGenericExpression」給運行時出現以下錯誤

「的靜態方法需要空實例,非靜態方法需要 非空實例。」

但它是完全可能的,makeGenericLambda也有缺陷(我只是還沒有得到它尚未)

這裏是我到目前爲止所(我使用C#4.0)

public static class TypeXtensions 
{ 
    public static object GetGenericInstance<TArg>(this Type type, TArg argument) 
    { 
     return InstanceCreationFactory<TArg> 
      .CreateGenericInstanceOf(type, argument); 
    } 

    private static class InstanceCreationFactory<TArg> 
    { 
     private static readonly Dictionary<Type, Func<TArg, object>> GenericInstanceCreationMethods = 
      new Dictionary<Type, Func<TArg, object>>(); 

     public static object CreateGenericInstanceOf(Type type, TArg arg) 
     { 
      CacheGenericInstanceCreationMethodIfRequired(type); 

      return GenericInstanceCreationMethods[type].Invoke(arg); 
     } 

     private static void CacheGenericInstanceCreationMethodIfRequired(Type type) 
     { 
      if (GenericInstanceCreationMethods.ContainsKey(type)) return; 

      var makeGenericMI = type.GetType().GetMethod("MakeGenericType"); 
      var paramExpression = Expression.Parameter(typeof (TArg), "argument"); 

      var makeGenericExpression = Expression.Call(makeGenericMI, paramExpression); 

      // Compile the Expression into a Func which takes the type argument and returns the constructed object: 
      var makeGenericLambda = Expression 
       .Lambda<Func<TArg, object>>(makeGenericExpression) 
       .Compile(); 


      GenericInstanceCreationMethods[type] = makeGenericLambda; 

     } 

    } 
} 

爲了完整,呼叫我使用這樣的

private void InputDataChanged(object sender, InputConnector<IndicatorStrategy>.InputDataChangedEventArgs e) 
    { 
     var baseType = e.Payload.GetType().BaseType; 
     if (baseType == null) return; 
     var arg = baseType.GetGenericArguments()[0]; 
     var test = typeof (ComplexIndicatorDataGenerator<>).GetGenericInstance(arg); 
    } 
+0

我很困惑,'arg'總是會成爲'Type',那麼爲什麼'TArg'是通用的? – svick

+0

@svick因爲TArg可以是10個不同的類中的任何一個,這取決於用戶在運行時的選擇,並且擴展方法中的「this」可以是直到運行時再次未知的4種類型中的任何一種。在我的例子中,我試圖創建一個ComplexIndicatorDataGenerator 。 –

+0

這不是你的代碼所做的。 'TArg'總是會成爲'Type'。 – svick

回答

2

看起來如果我理解正確的話,你想要什麼,你有兩個Type秒,我們稱它們爲T1<>T2,並且您想要創建T1<T2>類型的實例。

對於這一點,你不需要任何表達式樹,只需撥打MakeGenericType()直接再使用Activator.CreateInstance()

public static object CreateGenericInstance(
    Type genericTypeDefinition, Type genericParameter) 
{ 
    var genericType = genericTypeDefinition.MakeGenericType(genericParameter); 

    return Activator.CreateInstance(genericType); 
} 

如果Activator.CreateInstance()是你太慢了(你應該衡量,它實際上是太慢了),那麼你可以使用緩存lambda表達式替換它:

Dictionary<Tuple<Type, Type>, Func<object>> instanceCreatorCache 
    = new Dictionary<Tuple<Type, Type>, Func<object>>(); 

object CreateGenericInstance(Type genericTypeDefinition, Type genericParameter) 
{ 
    Func<object> instanceCreator; 

    var cacheKey = Tuple.Create(genericTypeDefinition, genericParameter); 

    if (!instanceCreatorCache.TryGetValue(cacheKey, out instanceCreator)) 
    { 
     var genericType = genericTypeDefinition.MakeGenericType(genericParameter); 

     instanceCreator = Expression.Lambda<Func<object>>(
      Expression.New(genericType)).Compile(); 

     instanceCreatorCache[cacheKey] = instanceCreator; 
    } 

    return instanceCreator(); 
} 

我決定也避免調用MakeGenericType()當類型爲已在緩存中,但我不知道這是否會真正使p erformance。

+0

這正是我所期待的。謝謝!我會檢查你建議的創建時間。目前只需要少量的實例。你的緩存lambda方法實際上包含了困擾我的語法。我正在努力瞭解如何將通用組件傳遞給Expression.New。現在我明白了這一點。再次感謝您爲此提供快速幫助。問候 –

相關問題