Универсальный расширенный делегата.Вызове, используя выражение деревьев


Насколько я знаю стандартный делегат.Вызове() , которая позволяет создать делегат с помощью отражения, не позволяет что-то делаю следующим образом, когда первый параметр метода не точно типа объекта.

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

Поэтому теперь у меня есть реализация, которая позволяет:

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

способ может быть любой способ, С или без возвращаемого типа, и с таким количеством параметров, как хотелось бы. Мне нужно создать действиес другой код зависит от этого сильный типизированный делегат.

В этом примере, я знаю метод-это минимум действий.

Следующий код улучшает исходный вызове двумя способами:

  • Это теперь универсальный, вместо передачи типа в качестве первого параметра.
  • Это делает преобразование в соответствующий нижней видах, когда это возможно.

Это первый раз для меня работе с деревьев выражений, поэтому я не знаю, что я делаю правильно или лучший подход. Я на основе этой реализации на статью Джона Скита (без полного понимания его :)), но код, кажется, работает!

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

Итак, я сделал это самый простой/лучший способ? Я делаю что-то совершенно ненужное? Спасибо!



20248
8
задан 2 марта 2011 в 02:03 Источник Поделиться
Комментарии
1 ответ

Я чувствую, что называя свой первый аргумент заданный вызывает больше путаницы, чем пользы. Я бы предложил 2 перегрузки этого метода.

// 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)

4
ответ дан 3 марта 2011 в 10:03 Источник Поделиться