将具有通用方法的接口实例转换为操作

Converting instance of Interface with generic method to Action

我正在努力使用我可以使用反射检索的 MethodInfo 创建 Action<> 的实例。

使用下面的示例,我可以轻松地在我的对象上调用自定义方法,但我真的很想将它们转换为 Actions。

我已经尝试使用 Delegate.CreateDelegate 来完成它,但我似乎无法将其用于 return 具有通用类型的 Action。

例子

我创建了以下界面:

interface IMyTest { } // Marker interface
interface<TInput> IMyTest : IMyTest {
    void MyMethod(TInput input);
}

然后我继承接口在一个class:

class MyClass : IMyTest<DateTime>, IMyTest<int> {
    void MyMethod(DateTime input) {
        // Do something
    }
    void MyMethod(int input) {
        // Do something else
    }
}

然后我创建了一个使用反射从接口实例中查找和调​​用 "MyMethod" 的方法:

void DoCallMyMethod(object target, object input) {
    IEnumerable<Type> interfaces = target.GetType().GetInterfaces()
        .Where(x => typeof(IMyTest).IsAssignableFrom(x) && x.IsGenericType);

    foreach (Type @interface in interfaces) {
        Type type = @interface.GetGenericArguments()[0];
        MethodInfo method = @interface.GetMethod("MyMethod", new Type[] { type });

        if (method != null) {
            method.Invoke(target, new[] { input });
        }
    }
}

最后我把它们放在一起:

MyClass foo = new MyClass();
DoCallMyMethod(foo, DateTime.Now);
DoCallMyMethod(foo, 47);

我想要什么

DoCallMyMethod 内部,我希望将 MethodInfo method 转换为通用 Action,这样结果将是这样的:

Action<type> myAction = method;

但这显然行不通

我找到了一些类似的 SO 帖子(但 none 完全涵盖了我的情况),最终得到了类似于此的答案:

Action<object> action = 
    (Action<object>)Delegate.CreateDelegate(typeof(Action<object>), target, method);

但这行不通,因为 "Cannot bind to the target method because its signature or security transparency is not compatible with that of the delegate type."

我怎样才能得到指定 type 作为其输入类型的 Action,同时仍然保留对象引用(没有新实例出现)?

您无法从 MyMethod 之一创建 Action<object>,因为该方法需要特定对象的类型(intDateTime),并且如果您将其视为 Action<object> 您可以使用任何类型的对象调用它。

由于您需要的是 return 一个 Action<T>,因此不需要将输入值传递给 DoCallMyMethod 方法。您可以将输入类型作为通用参数传递:

public Action<T> DoCallMyMethod<T>(object target)
{
    var @interface = typeof(IMyTest<T>);
    if (@interface.IsAssignableFrom(target.GetType()))
    {
        var method = @interface.GetMethod("MyMethod");
        if (method != null)
        {
            var action = Delegate.CreateDelegate(typeof(Action<T>), target, method) as Action<T>;                    
            return action;
        }
    }

    return null;
}

然后,像这样使用它:

MyClass foo = new MyClass();
var action1 = DoCallMyMethod<DateTime>(foo);
var action2 = DoCallMyMethod<int>(foo);

action1(DateTime.Now);
action2(47);

如果您在编译时不知道输入的类型,您可以试试这个:

public Delegate DoCallMyMethod(object target, Type inputType)
{
    var @interface = typeof(IMyTest<>).MakeGenericType(inputType);
    if (@interface.IsAssignableFrom(target.GetType()))
    {
        var method = @interface.GetMethod("MyMethod");
        if (method != null)
        {
            var @delegate = Delegate.CreateDelegate(typeof(Action<>).MakeGenericType(inputType), target, method);
            return @delegate;
        }
    }

    return null;
}

并像这样使用它:

MyClass foo = new MyClass();
var action1 = DoCallMyMethod(foo, typeof(DateTime)) as Action<DateTime>;
var action2 = DoCallMyMethod(foo, typeof(int)) as Action<int>;

action1(DateTime.Now);
action2(47);

//or

int input = 47;
var @delegate = DoCallMyMethod(foo, input.GetType());
@delegate.DynamicInvoke(input);

但是,如果您需要 return 一个 Action<object>,您可以执行一个操作来接收对象并在对象的类型有效时调用该方法:

public Action<object> DoCallMyMethod(object target, Type inputType)
{
    var @interface = typeof(IMyTest<>).MakeGenericType(inputType);
    if (@interface.IsAssignableFrom(target.GetType()))
    {
        var method = @interface.GetMethod("MyMethod");
        if (method != null)
        {
            Action<object> action = obj =>
            {
                if (obj.GetType().IsAssignableFrom(inputType))
                    method.Invoke(target, new object[] { obj });
            };

            return action;
        }
    }

    return null;
}

还有...

MyClass foo = new MyClass();
Action<object> action = DoCallMyMethod(foo, typeof(int));
action(47);         // MyMethod(int) is called.
action("...");      // Nothing happens.

现在您有一个 Action<object> 仅在传递整数时才调用 MyMethod(int)