2010-10-13 50 views
2

我有一個給定委託Type參數(不是泛型)的方法,它返回它創建的實現簽名的Delegate。它使用表達式樹在內部創建動態方法。委託類型的格式必須爲Func<SomeParam,IamDerivedObject>,這是一個func,其中IamDerivedObject是IamDerivedObject的繼承者。該方法使用類型信息來確定要實例化哪個對象,因此僅使用該接口進行創建將會是災難性的。調用是從一個返回IamDerivedObject的靜態方法完成的。在.NET 3.5委託中模擬差異

在.NET 4中的代碼,我可以做到這一點:: var myDelegate = MakeMethod(typeArgument) as Func<SomeParam,IamDerivedObject>

在.net 3.5這不起作用。我堅持必須知道調用invoke方法的類型,或者使用另一個表達式生成的委託來調用它並緩存該委託。這意味着更多運行時生成的代碼 不幸的是,我不能使用.NET 4代碼作爲程序集暫時必須是.NET 3.5代碼,因爲它依賴於不能在4.0中工作的第三方DLL。

+0

您確定'var'代碼在.net 3.5中不起作用,但它在.net 4中工作?任何錯誤消息? – 2010-10-13 04:15:38

+0

@Danny:委託類型不支持C#3.0中的協變和反變換。 – 2010-10-13 05:00:55

+0

如果有一種方法可以在C#3.0中進行委託方差,那麼我們不需要在C#4.0中添加*特性;它已經實施了。我認爲你運氣不好。 – 2010-10-13 05:02:06

回答

0

我想出了一種方法,但它基本上需要一些反思魔術來解決它。

/// <summary> 
    /// Converts the delegate to <see cref="TDelegate"/>, provided the types are compatible. Can use 
    /// variance of delegate typing in .NET 4 for a speedy conversion. Otherwise, uses Delegate.CreateDelegate 
    /// to create a new delegate with the appropriate signature. 
    /// </summary> 
    /// <typeparam name="TDelegate">The target delegate type.</typeparam> 
    /// <param name="delegate">The @delegate.</param> 
    /// <returns></returns> 
    public static TDelegate ConvertDelegate<TDelegate>(Delegate @delegate) where TDelegate : class 
    { 
     ArgumentValidator.AssertIsNotNull(() => @delegate); 
     var targetType = typeof(TDelegate); 
     ArgumentValidator.AssertIsDelegateType(() => targetType); 
     var currentType = @delegate.GetType(); 
     if (targetType.IsAssignableFrom(currentType)) 
     { 
      return @delegate as TDelegate; // let's skip as much of this as we can. 
     } 

     var currentMethod = currentType.GetMethod("Invoke"); 
     var targetMethod = targetType.GetMethod("Invoke"); 
     if (!AreDelegateInvokeMethodsCompatible(currentMethod, targetMethod, true)) 
     { 
      throw new ArgumentException(string.Format("{0} is incompatible with {1}.", currentType, targetType), ExpressionHelper.GetMemberName(() => @delegate)); 
     } 
     var invocationList = @delegate.GetInvocationList(); 
     return DelegateHelper.Combine(@delegate.GetInvocationList() 
      .Select(d => IsMethodRunTimeGenerated(d.Method) ? 
         GetDynamicMethodFromMethodInfo(d.Method).CreateDelegate<TDelegate>(d.Target) : 
         DelegateHelper.CreateDelegate<TDelegate>(d.Target, d.Method)).ToArray()); 
    } 
    #region Private Static Variables  
    private static Type s_RTDynamicMethodType = Type.GetType("System.Reflection.Emit.DynamicMethod+RTDynamicMethod",false,true); 
    private static Func<MethodInfo, DynamicMethod> s_GetDynamicMethodDelegate = CreateGetDynamicMethodDelegate(); 
    #endregion Private Static Variables  

    private static Func<MethodInfo, DynamicMethod> CreateGetDynamicMethodDelegate() 
    { 
     var param = Expression.Parameter(
         typeof(MethodInfo), 
         typeof(MethodInfo).Name 
     ); 
     var expression = Expression.Lambda<Func<MethodInfo, DynamicMethod>>(
      Expression.Field(
       Expression.Convert(
        param, 
        s_RTDynamicMethodType 
       ), 
       s_RTDynamicMethodType.GetField("m_owner", BindingFlags.NonPublic | BindingFlags.Instance) 
      ), 
      param 
      ); 
     return expression.Compile(); 
    } 

我不會離開我的委託幫助類,除非別人真的需要它。但重要的是它是可能的,而且非常快。轉換時間通常小於1毫秒,幾乎不像瞬時方法組轉換那麼快,但您知道這是一些東西。