Generic advanced Delegate.CreateDelegate using expression trees

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!

-
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)? – Snowbear Mar 3 '11 at 9:00
@Snowbear: This is reflection, method is a MethodInfo. – Steven Jeuris Mar 3 '11 at 14:48
Do you mean that entrance point to your code will be a reflection? So you won't know method you need at compile-time? – Snowbear Mar 3 '11 at 14:53
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! – Steven Jeuris Mar 3 '11 at 15:01

3 Answers

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)

-
You are actually really right about that. :O I just copy pasted it from the info on CreateDelegate from msdn for consistency. :) – Steven Jeuris Mar 4 '11 at 11:41

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

-
"which allows to create a delegate by using reflection" ... I only have MethodInfo. Thanks for the input though. ;p – Steven Jeuris Apr 3 '11 at 3:09
@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? – smartcaveman Apr 3 '11 at 3:12
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! – Steven Jeuris Apr 3 '11 at 3:57
@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 – smartcaveman Apr 3 '11 at 4:01
@Steven, I don't have edit permissions on this site, but you clearly meant bleh.GetType().GetMethod() – smartcaveman Apr 3 '11 at 6:28

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

-