使用泛型方法将Interface实例转换为Action

时间:2016-11-04 16:58:18

标签: c# generics reflection delegates action

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

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

我尝试过使用Delegate.CreateDelegate,但我似乎无法通过泛型返回Action。

示例

我创建了以下界面:

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

然后我继承了类中的接口:

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帖子(但没有一个完全涵盖我的案例),最终得到了类似的答案:

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

但是这不起作用,因为“无法绑定到目标方法,因为它的签名或安全透明度与委托类型的不兼容。”

如何使用指定的type作为输入类型获取Action,同时仍保留对象引用(不显示新实例)?

1 个答案:

答案 0 :(得分:1)

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

由于您需要的是返回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);

但是如果你需要返回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)