2

I'm trying to call generic method via reflection. I need to pass object of class that implements interface this method expects for parameter. I'm getting System.ArgumentException telling me 'Object of type 'ReflectionTest.MyRequest' cannot be converted to type '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);
    }
}

Has anyone have some suggestion what I'm doing wrong? Unfortunately, I'm not that skilled with reflection so any help is welcome.

Sample git repo: https://github.com/alan994/ReflectionProblem

3 Answers 3

3

In this line:

var generic = method.MakeGenericMethod(type);

You are creating a generic method of type type, which is MyRequest.
So, eventually you are using MyRequest as TResponse.

But what you actually want to do is you want to pass MyResponse as TResponse.

You can do the following in order to be able to call this dynamically:

  • get the interface IMyRequest<> which is your request is implementing
  • get the only generic argument of this interface, which will be TResponse

This is how the code might look like, but you need to add some checks on types and valid error handling here:

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

After all, are you sure that you want to bind your response to a specific definition of a request? I don't know your architecture and what you are trying to achieve, but, perhaps, this can be a more agile solution:

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);
    }
}  
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for detailed explanation and code example. I see now and what can I say...I'm idiot :) This is just small example of larger code base. Interface IMediator is part of external nuget package. Thanks for answer!
0

You are sending the wrong type for the generic type of the Send method. Its generic type is the response type - TResponse, but you are giving it the request type - effectively IRequest<TResponse>.

The following will give you correct generic method to invoke.

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

Comments

0

The problem is that you are making a generic method using MyRequest instead of MyResponse, take a look at the signature Send<TResponse>. Try:

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.