Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# Generics and using the non-generic version from typed method

I'm using the RestSharp library to access a REST API.

I want all the API requests to go through the same method, so I can add headers, handle errors and do other stuff in a central place.

So I made a method that accepts a generic Func<> and that solves most of my problems, but I don't know how to handle the case where I don't have a return type.

private T PerformApiCall<T>(RestRequest restRequest, Func<RestRequest, IRestResponse<T>> restMethod)
{
    var response = restMethod.Invoke(restRequest);
    //handle errors
    ....

    return response.Data;
}

I call it like this:

var apples = PerformApiCall(new RestRequest('/api/apples'), req => Client.Execute<List<Apple>>(req));

But I came across a problem, a bunch of API calls don't have a return type because they don't return data. So I used Client.Execute(req) and I get the error saying the type arguments cannot be inferred, I tried to pass , but that failed because it couldn't convert the non-generic IRestResponse to the typed one.

Any ideas on how to tackle this in a nice way?

like image 832
Madd0g Avatar asked Oct 31 '22 15:10

Madd0g


1 Answers

One thing you could try is to add an overload to your PerformApiCall function that takes a Func with a non-generic result type, and returns nothing:

// Notice the `Func` has `IRestResponse`, not `IRestResponse<T>`
public void PerformApiCall(RestRequest restRequest,
                           Func<RestRequest, IRestResponse> restMethod)
    ...

Then, depending on how complex your error checking/logic is, you could move it out to a separate method (which returns the response), and call it from both overloads of PerformApiCall:

private T PerformRequestWithChecks<T>(RestRequest restRequest,
                                      Func<RestRequest, T> restMethod)
    where T : IRestResponse
{
    var response = restMethod.Invoke(restRequest);
    // Handle errors...
    return response;
}

// You can use it from both versions of `PerformApiCall` like so:
//
//     // From non-generic version
//     var response =
//         PerformRequestWithChecks<IRestResponse>(restRequest, restMethod);
//
//     // From generic version
//     var response =
//         PerformRequestWithChecks<IRestResponse<T>>(restRequest, restMethod);
//     return response.Data;

You were getting a compiler error because it is sound to treat a subtype as if it was an instance of its supertype, but it is not sound to do it in the other direction (which is what was happening when you changed your calling code to just Client.Execute(req), returning a non-generic).

Here's an ideone paste illustrating this: http://ideone.com/T2mQfl

like image 177
voithos Avatar answered Nov 12 '22 22:11

voithos