10
\$\begingroup\$

As far as I know the standard Delegate.CreateDelegate() which allows to create a delegate by using reflection, doesn't allow doing something as follows when the first parameter of method isn't exactly of type object.

public class Bleh
{
    public void SomeMethod( string s ) { }
}
...
object bleh = new Bleh(); 
MethodInfo method = bleh.GetType().GetMethod("SomeMethod");    
// Starting from here, all knowledge about Bleh is unknown. 
// Only bleh and method are available.
Action<object> a = (Action<object>)Delegate.CreateDelegate(
    typeof( Action<object> ), bleh, method );

Which is why I now have an implementation which allows the following:

Action<object> compatibleExecute =
    DelegateHelper.CreateCompatibleDelegate<Action<object>>( bleh, method );

method can be any method, with or without return type, and with as many parameters as desired. I need to create an Action<T>, since other code is dependant on this strong typed delegate.

In this example, I know the method is at least an Action<object>.

The following code improves on the original CreateDelegate in two ways:

  • It is generic now, instead of passing type as a first parameter.
  • It does conversion to suitable lower types when possible.

This is the first time for me working with expression trees, so I don't know whether what I'm doing is correct or the best approach. I based this implementation on an article by Jon Skeet (without fully understanding it :)), but the code seems to be working!

/// <summary>
///   A generic helper class to do common
///   <see cref = "System.Delegate">Delegate</see> operations.
/// </summary>
/// <author>Steven Jeuris</author>
public static class DelegateHelper
{
    /// <summary>
    ///   The name of the Invoke method of a Delegate.
    /// </summary>
    const string InvokeMethod = "Invoke";

    /// <summary>
    ///   Get method info for a specified delegate type.
    /// </summary>
    /// <param name = "delegateType">The delegate type to get info for.</param>
    /// <returns>The method info for the given delegate type.</returns>
    public static MethodInfo MethodInfoFromDelegateType( Type delegateType )
    {
        Contract.Requires<ArgumentException>(
            delegateType.IsSubclassOf( typeof( MulticastDelegate ) ),
            "Given type should be a delegate." );

        return delegateType.GetMethod( InvokeMethod );
    }

    /// <summary>
    ///   Creates a delegate of a specified type that represents the specified
    ///   static or instance method, with the specified first argument.
    ///   Conversions are done when possible.
    /// </summary>
    /// <typeparam name = "T">The type for the delegate.</typeparam>
    /// <param name = "firstArgument">
    ///   The object to which the delegate is bound,
    ///   or null to treat method as static
    /// </param>
    /// <param name = "method">
    ///   The MethodInfo describing the static or
    ///   instance method the delegate is to represent.
    /// </param>
    public static T CreateCompatibleDelegate<T>(
       object firstArgument,
       MethodInfo method )
    {
        MethodInfo delegateInfo = MethodInfoFromDelegateType( typeof( T ) );

        ParameterInfo[] methodParameters = method.GetParameters();
        ParameterInfo[] delegateParameters = delegateInfo.GetParameters();

        // Convert the arguments from the delegate argument type
        // to the method argument type when necessary.
        ParameterExpression[] arguments =
            (from delegateParameter in delegateParameters
             select Expression.Parameter( delegateParameter.ParameterType ))
             .ToArray();
        Expression[] convertedArguments =
            new Expression[methodParameters.Length];
        for ( int i = 0; i < methodParameters.Length; ++i )
        {
            Type methodType = methodParameters[ i ].ParameterType;
            Type delegateType = delegateParameters[ i ].ParameterType;
            if ( methodType != delegateType )
            {
                convertedArguments[ i ] =
                    Expression.Convert( arguments[ i ], methodType );
            }
            else
            {
                convertedArguments[ i ] = arguments[ i ];
            }
        }

        // Create method call.
        ConstantExpression instance = firstArgument == null
            ? null
            : Expression.Constant( firstArgument );
        MethodCallExpression methodCall = Expression.Call(
            instance,
            method,
            convertedArguments
            );

        // Convert return type when necessary.
        Expression convertedMethodCall = 
            delegateInfo.ReturnType == method.ReturnType
                ? (Expression)methodCall
                : Expression.Convert( methodCall, delegateInfo.ReturnType );

        return Expression.Lambda<T>(
            convertedMethodCall,
            arguments
            ).Compile();
    }
}

So, did I do this the easiest/best way? Am I doing something completely unnecessary? Thanks!

\$\endgroup\$
4
  • \$\begingroup\$ Why would you use such delegates? You're mentioning that it is generic but couldn't you simply use delegates constructor like so: Action<object> execute = new Action(_owner.SomeMethod)? \$\endgroup\$
    – Snowbear
    Commented Mar 3, 2011 at 9:00
  • \$\begingroup\$ @Snowbear: This is reflection, method is a MethodInfo. \$\endgroup\$ Commented Mar 3, 2011 at 14:48
  • \$\begingroup\$ Do you mean that entrance point to your code will be a reflection? So you won't know method you need at compile-time? \$\endgroup\$
    – Snowbear
    Commented Mar 3, 2011 at 14:53
  • \$\begingroup\$ The MethodInfo is known, the instance (in this scenario) is known but the type of it unkown. By using applied attributes to a class I known which method is desired, and can retrieve the MethodInfo. That's the specific scenario, but in the general case, I convert a MethodInfo into a delegate. Read the intro of Jon Skeet's article for more information. Thanks for the interest! \$\endgroup\$ Commented Mar 3, 2011 at 15:01

3 Answers 3

4
\$\begingroup\$

I feel like naming your first argument firstArgument will cause more confusion than it's worth. I would offer 2 overloads for this method.

// This overload will simply pass null to the main overload 
// for the sake of convenience.
public static T CreateCompatibleDelegate<T>(MethodInfo method)

// I feel like the name 'instance' really makes it clear
// what the parameter means.
public static T CreateCompatibleDelegate<T>(object instance, MethodInfo method)
\$\endgroup\$
1
3
\$\begingroup\$

I don't know if I'm missing something, but this doesn't look like it adds anything to doing

 Expression<Action<T,TArg1,TArg2>> lambda = 
      (instance,arg1,arg2) => instance.CallSomeMethod(arg1,arg2);
 var delegate = lambda.Compile();

If the only problem is getting the delegate to accept something without knowing the type then all you have to do is

 Action<object> ConvertToUntyped<T>(Action<T> action){
      return o => action((T)o);
 }

If you need to get the MethodInfo for some other reason you can always get the (MethodCallExpression)Expression<TDelegate>.Expression. I don't know your use case, but from my similar experience it looks like you are trying to rebuild some functionality that is already there for you.

I'd also like to add that, as a general rule, if you are setting out to optimize Jon Skeet's C# code, you are probably wasting your time.

EDIT Using the non-generic lambda expression you have to write less code, and you could probably event cut this down. I wrote an example for you below. However, I'm still not sure I understand why you are trying to do this.

        var instanceParameterExpression = Expression.Parameter(method.DeclaringType, "Instance");
        var argumentParameterExpressions = method.GetParameters().Select(x => Expression.Parameter(x.ParameterType, x.Name));
        var delegateParameters = new List<ParameterExpression>();
        delegateParameters.Add(instanceParameterExpression);
        delegateParameters.AddRange(argumentParameterExpressions);
        var lambda = Expression.Lambda(
            Expression.Call(instanceParameterExpression, method, argumentParameterExpressions.Cast<Expression>()),
            delegateParameters.ToArray()); 

UPDATE

the method below will make a delegate with all object parameters. I do see what you are getting at now, but I don't think it is a good idea at all. You have a minimal performance benefit over just using Action<object> action = arg => bleh.GetType().GetMethod("SomeMethod").Invoke(new[]{arg});, and a huge amount of excess complexity. Furthermore, you are not even using the benefit of strong typing, and you will lose some of the performance benefit whenever you use this on methods with ValueType parameters, (boxing/unboxing). In theory, this could make something faster, but I think the real world scenarios you will find to apply it this way will be very rare, and won't be worth it.

    public Delegate CreateDelegateWithObjectParameters(object instance, MethodInfo methodInfo)
    {
        var parameters = methodInfo.GetParameters()
            .Select(parameterInfo => new
            {
                MethodParameterType = parameterInfo.ParameterType,
                DelegateParameter = Expression.Parameter(typeof(object), parameterInfo.Name)
            })
            .Select(x => new
            {
                x.DelegateParameter,
                MethodParameter = Expression.Convert(x.DelegateParameter, x.MethodParameterType)
            });
        MethodCallExpression methodCallExpression = instance == null
                                                        ? Expression.Call(methodInfo, parameters.Select(x => x.MethodParameter).ToArray())
                                                        : Expression.Call(Expression.Constant(instance), methodInfo, parameters.Select(x => x.MethodParameter).ToArray());
        return Expression.Lambda(methodCallExpression, parameters.Select(x => x.DelegateParameter).ToArray()).Compile();
    }
\$\endgroup\$
32
  • \$\begingroup\$ "which allows to create a delegate by using reflection" ... I only have MethodInfo. Thanks for the input though. ;p \$\endgroup\$ Commented Apr 3, 2011 at 3:09
  • \$\begingroup\$ @Steven, do you have an instance of the object or just the MethodInfo? How did you get the MethodInfo? From where/why are you making this call? \$\endgroup\$ Commented Apr 3, 2011 at 3:12
  • \$\begingroup\$ The MethodInfo is known, the instance (in this scenario) is known but the type of it unkown. By using applied attributes to a class I known which method is desired, and can retrieve the MethodInfo. That's the specific scenario, but in the general case, I convert a MethodInfo into a delegate. Read the intro of Jon Skeet's article for more information. Thanks for the interest! \$\endgroup\$ Commented Apr 3, 2011 at 3:57
  • \$\begingroup\$ @Steven, (1) You can get the type from MethodInfo.DeclaringType, (2) Is the method a static method or an instance method? Because, if you want to get a delegate to a method on an instance, then you also need to construct the instance, (3) I've read the article several times and I just reviewed the intro - I'm still not clear on what you are trying to accomplish \$\endgroup\$ Commented Apr 3, 2011 at 4:01
  • 1
    \$\begingroup\$ @Steven, I don't have edit permissions on this site, but you clearly meant bleh.GetType().GetMethod() \$\endgroup\$ Commented Apr 3, 2011 at 6:28
1
\$\begingroup\$

By applying some extra LINQ 4.0 Zip magic, I was able to reduce the code to the following. Anyone sees any more improvements? IMHO this is already a bit clearer.

public static T CreateCompatibleDelegate<T>( object instance, MethodInfo method )
{
    MethodInfo delegateInfo = MethodInfoFromDelegateType( typeof( T ) );

    var methodTypes = method.GetParameters().Select( m => m.ParameterType );
    var delegateTypes = delegateInfo.GetParameters().Select( d => d.ParameterType );

    // Convert the arguments from the delegate argument type
    // to the method argument type when necessary.
    var arguments = methodTypes.Zip( delegateTypes, ( methodType, delegateType ) =>
    {
        ParameterExpression delegateArgument = Expression.Parameter( delegateType );
        return new
        {
            DelegateArgument = delegateArgument,
            ConvertedArgument = methodType != delegateType
                            ? (Expression)Expression.Convert( delegateArgument, methodType )
                            : delegateArgument
        };
    } ).ToArray();

    // Create method call.;
    MethodCallExpression methodCall = Expression.Call(
        instance == null ? null : Expression.Constant( instance ),
        method,
        arguments.Select( a => a.ConvertedArgument )
        );

    // Convert return type when necessary.
    Expression convertedMethodCall = delegateInfo.ReturnType == method.ReturnType
                                ? (Expression)methodCall
                                : Expression.Convert( methodCall, delegateInfo.ReturnType );

    return Expression.Lambda<T>(
        convertedMethodCall,
        arguments.Select( a => a.DelegateArgument )
        ).Compile();
}

At first I got the dreaded "variable .. of type ... referenced from scope '' but it is not defined." exception, but after some pondering I realized I had to add ToArray() after the Zip() statement to make sure the delegate arguments would already be defined. Powerful stuff this deferred execution, but apparently also a source for errors. I got the same exception when running smartcaveman's latest update, which is perhaps due to a similar mistake.


Writing a custom Zip which takes three arguments removes the need of the anonymous type. Writing the Zip is really easy, as documented by Jon Skeet.

public static T CreateCompatibleDelegate<T>( object instance, MethodInfo method )
{
    MethodInfo delegateInfo = MethodInfoFromDelegateType( typeof( T ) );

    var methodTypes = method.GetParameters().Select( m => m.ParameterType );
    var delegateTypes = delegateInfo.GetParameters().Select( d => d.ParameterType );
    var delegateArguments = delegateTypes.Select( Expression.Parameter ).ToArray();

    // Convert the arguments from the delegate argument type
    // to the method argument type when necessary.
    var convertedArguments = methodTypes.Zip(
        delegateTypes, delegateArguments,
        ( methodType, delegateType, delegateArgument ) =>
            methodType != delegateType
                ? (Expression)Expression.Convert( delegateArgument, methodType )
                : delegateArgument );

    // Create method call.
    MethodCallExpression methodCall = Expression.Call(
        instance == null ? null : Expression.Constant( instance ),
        method,
        convertedArguments
        );

    // Convert return type when necessary.
    Expression convertedMethodCall = delegateInfo.ReturnType == method.ReturnType
                                ? (Expression)methodCall
                                : Expression.Convert( methodCall, delegateInfo.ReturnType );

    return Expression.Lambda<T>(
        convertedMethodCall,
        delegateArguments
        ).Compile();
}
\$\endgroup\$
7
  • \$\begingroup\$ If I understood this correctly, I would need to know in advance, the signature of the delegate (that matches the target method(s) signature). What if I don't know it before hand? In my use case, I am reading a file with "method calls" in each line. For example, DoThis(1,2); DoThat(3,4,"test"). I am able to execute them now using Method.Invoke, in fact caching then into a dictionary <String,Action> where the method invoke call, is wrapped further by an Action (e.g. Action act = ()=> methodInfo.Invoke({1,2}). I'm wondering if I could expand this delegate idea further for my use-case. \$\endgroup\$ Commented Mar 15, 2019 at 14:40
  • \$\begingroup\$ I wrote more info on this use case here: "Creating delegates during reflection for unknown types", "Creating delegates during reflection with unbound instances", and "Casting to less generic types". \$\endgroup\$ Commented Mar 15, 2019 at 15:27
  • \$\begingroup\$ However, I doubt what I describe here (and in the blog posts) is relevant to you. If all you are interested in is calling the methods and their full type definition is known (no casting is needed), then you can just use Delegate.CreateDelegate I start the post with. The result of that function would be the Delegate you store in your dictionary. The example you state does not make clear why the method signature would be unknown; you have the MethodInfo, no? \$\endgroup\$ Commented Mar 15, 2019 at 15:36
  • \$\begingroup\$ Yes, after reflection, the method signature is known, and in fact, I am able to generate a delegate of Action<T1,T2,...Tn> type matching the method signature, by calling Expression.GetActionType(types.ToArray()), where types is from methodInfo.GetParameters() . In theory, this would be the type I would use in your method above (CreateCompatibleDelegate). Where I'm lost next, is that how I then "feed" my string method call into this delegate, so I can cache that specific method call (cache this: Action act = () => newlycreatedDelegate("DoThis(1,2)")) \$\endgroup\$ Commented Mar 15, 2019 at 18:25
  • \$\begingroup\$ Another way (or my other problem), is that when I make the call to the generic method CreateCompatibleDelegate<T>( object instance, MethodInfo method ), when I don't know T until runtime. I am able to programmatically obtain it, but I can't dynamically supply it to this method. T must be known at compile time. \$\endgroup\$ Commented Mar 15, 2019 at 18:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.