【问题标题】:Call generic method by reflection. System.ArgumentException happens通过反射调用泛型方法。 System.ArgumentException 发生
【发布时间】:2019-05-18 17:44:47
【问题描述】:

我正在尝试通过反射调用泛型方法。我需要传递实现此方法期望参数的接口的类的对象。我收到 System.ArgumentException 告诉我“'ReflectionTest.MyRequest' 类型的对象无法转换为 'ReflectionTest.IRequest`1[ReflectionTest.MyRequest]' 类型。”

class Program
{
    static void Main(string[] args)
    {
        var request = new MyRequest();
        IMediator mediator = new Mediator();

        //This works (of course), but I need to call this by reflection. I don't know the type at design time.
        //var r = mediator.Send(request);

        //I tried this, but it doesn't work
        var type = request.GetType();
        var method = mediator.GetType().GetMethod("Send");
        var generic = method.MakeGenericMethod(type);
        //Exception
        var response = generic.Invoke(mediator, new object[] { request });

    }
}

public interface IRequest<out TResponse>
{

}

public interface IMediator
{
    TResponse Send<TResponse>(IRequest<TResponse> requests);
}

public class MyRequest : IRequest<MyResponse>
{
}

public class MyResponse
{
}

public class Mediator : IMediator
{
    public TResponse Send<TResponse>(IRequest<TResponse> requests)
    {
        Console.WriteLine("Processing...");
        return default(TResponse);
    }
}

有没有人建议我做错了什么?不幸的是,我不擅长反思,所以欢迎任何帮助。

git repo 示例:https://github.com/alan994/ReflectionProblem

【问题讨论】:

    标签: c# .net generics reflection


    【解决方案1】:

    在这一行:

    var generic = method.MakeGenericMethod(type);
    

    您正在创建type 类型的泛型方法,即MyRequest
    所以,最终你使用MyRequest 作为TResponse

    但你真正想做的是你想将MyResponse 传递为TResponse

    您可以执行以下操作以便能够动态调用它:

    • 获取接口IMyRequest&lt;&gt;,即您的请求正在实现
    • 获取此接口的唯一通用参数,即TResponse

    这可能是代码的样子,但您需要在此处添加一些对类型和有效错误处理的检查:

    var type = request.GetType();
    
    var responseType = type.GetInterfaces() // [IRequest<MyResponse>]
        .Single(i => i.GetGenericTypeDefinition() == typeof(IRequest<>)) // IRequest<MyResponse>
        .GetGenericArguments() // [MyResponse]
        .Single(); // MyResponse
    
    var method = mediator.GetType().GetMethod("Send");
    var generic = method.MakeGenericMethod(responseType); // note that responseType is used here
    
    var response = generic.Invoke(mediator, new object[] { request });
    

    毕竟,您确定要将响应绑定到请求的特定定义吗?我不知道您的架构以及您想要实现的目标,但也许,这可能是一个更灵活的解决方案:

    public interface IRequest
    {
    
    }
    
    public interface IMediator
    {
        TResponse Send<TRequest, TResponse>(IRequest request);
    }
    
    public class MyRequest : IRequest
    {
    }
    
    public class MyResponse
    {
    }
    
    public class Mediator : IMediator
    {
        public TResponse Send<TRequest, TResponse>(IRequest request)
        {
            Console.WriteLine("Processing...");
            return default(TResponse);
        }
    }  
    

    【讨论】:

    • 感谢详细的解释和代码示例。我现在明白了,我能说什么......我是白痴 :) 这只是更大代码库的小例子。接口 IMediator 是外部 nuget 包的一部分。感谢您的回答!
    【解决方案2】:

    您为Send 方法的泛型类型发送了错误的类型。它的通用类型是响应类型——TResponse,但你给它的是请求类型——实际上是IRequest&lt;TResponse&gt;

    以下将为您提供正确的通用方法来调用。

    var generic = method.MakeGenericMethod(typeof(MyReponse));
    

    【讨论】:

      【解决方案3】:

      问题是您正在使用MyRequest 而不是MyResponse 制作通用方法,请查看签名Send&lt;TResponse&gt;。试试:

      var generic = method.MakeGenericMethod(typeof(MyReponse));
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多