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!
Action<object> execute = new Action(_owner.SomeMethod)
? \$\endgroup\$method
is aMethodInfo
. \$\endgroup\$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 theMethodInfo
. 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\$