tags:

views:

54

answers:

1

I have a method that given a delegate Type argument (not a generic), it returns a Delegate that it created that implements the signature. It internally creates a dynamic method using Expression trees. The delegate type must be of this form Func<SomeParam,IamDerivedObject>,a func where IamDerivedObject is an inheritor of IamDerivedObject. The method uses the type information to figure out which object to instance, so simply using the interface for creation would be disastrous. The calling is done from a static method that returns IamDerivedObject.

In .NET 4 code, I can do this:: var myDelegate = MakeMethod(typeArgument) as Func<SomeParam,IamDerivedObject>

In .Net 3.5 this does not work. And I'm stuck with having to know the type to call the invoke method, or using another expression generated delegate to invoke it and cache that delegate. This means more runtime generated code. Unfortunately, I cannot use the .NET 4 code as the assembly for the time being must be .NET 3.5 code as it relies on a third party dll which does not work in 4.0.

A: 

I figured out an approach, but it basically requires some reflection magic to figure it out.

    /// <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();
    }

I'm not going to leave up my delegatehelper class unless somebody else can really want it. But the point is it is possible, and actually pretty quick. Conversion is usually less than 1ms, not nearly as fast as method group conversion which is near instantaneous, but you know thats something.

Michael B