2010-10-13 52 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毫秒,几乎不像瞬时方法组转换那么快,但您知道这是一些东西。