Вызовите общий метод по отражению.System.ArgumentException происходит - PullRequest
2 голосов
/ 18 мая 2019

Я пытаюсь вызвать универсальный метод с помощью отражения. Мне нужно передать объект класса, который реализует интерфейс, который этот метод ожидает для параметра. Я получаю исключение 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 репо: https://github.com/alan994/ReflectionProblem

Ответы [ 3 ]

3 голосов
/ 18 мая 2019

В этой строке:

var generic = method.MakeGenericMethod(type);

Вы создаете универсальный метод типа type, который MyRequest.
Итак, в конечном итоге вы используете MyRequest как TResponse.

Но на самом деле вы хотите передать MyResponse как TResponse.

Вы можете сделать следующее для вызова этого динамического вызова:

  • получить интерфейс IMyRequest<>, который реализует ваш запрос
  • получитьединственный общий аргумент этого интерфейса, который будет 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);
    }
}  
0 голосов
/ 18 мая 2019

Проблема в том, что вы создаете общий метод, используя MyRequest вместо MyResponse, посмотрите на подпись Send<TResponse>. Попробуйте:

var generic = method.MakeGenericMethod(typeof(MyReponse));
0 голосов
/ 18 мая 2019

Вы отправляете неправильный тип для универсального типа метода Send.Его универсальный тип - это тип ответа - TResponse, но вы предоставляете ему тип запроса - эффективно IRequest<TResponse>.

Следующее даст вам правильный универсальный метод для вызова.

var generic = method.MakeGenericMethod(typeof(MyReponse));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...