0

I'm trying to make a generic type for retrieving data from API, however I can't cast explicit type to T in Get method:

Classes are arranged as follows: PClass, MClass and SClass doesn't inherit over common parent (in fact they are just data model classes)

VMClass <- PagedResponse<T>, where T can be P-, M- or SClass

API sends very similar responses for all 3 Gets: common string name, etc, and P-, M- or SClass result[]

I have few extension methods for casting explicit types:

internal static List<VMClass> ToVMElement(this List<MClass> ms) { ... }
internal static List<VMClass> ToVMElement(this List<pClass> ps) { ... }
internal static List<VMClass> ToVMElement(this List<sClass> ss) { ... }

When I'm trying to use switch-case for deciding which operation I want to perform, I'm returning explicit type of PagedResponse<SClass> (or P- M-) – experiencing CS0029 – Cannot implicitly convert classes

public static async Task<PagedResponse<T>> RetrieveApiData<T>(...)
{
    switch (typeof(T))
    {
        case Type m when m == typeof(MClass):
            PagedResponse<T> r = await GetMClasses(...);  // await returns PagedResponse<MClass>
            return r;
        case Type S when s == typeof(SClass):
            //...
    }
    //...
}

I understand that generic types are resolved at compilation, but I don't know how to accomplish that casting.

Florian
  • 1,019
  • 6
  • 22
ikarmus
  • 35
  • 7
  • Have you considered using `dynamic` type, and checking `if (data is PClass\MClass\SClass)`? – Lior v May 05 '23 at 13:18
  • I didn't even consider it, afaik `dynamic` casting is not a good practice, if it's not the only way to acomplish task, I would rather not use it. If I'm wrong, please correct me – ikarmus May 05 '23 at 13:26
  • When using `dynamic`, you don't have to cast the data, after checking `if (data is PClass)` you can use `data.MethodInPClass();` (You had to perform casting if you used `object` instead of `dynamic`) – Lior v May 05 '23 at 13:32
  • Like in this solution https://stackoverflow.com/a/9842624/14345698 ? – ikarmus May 05 '23 at 13:33
  • Yes, you can try doing something like that. – Lior v May 05 '23 at 13:42
  • Sorry for leaving topic for so long, I encountered some compile time issues. Anyway I used `dynamic` as you suggested, so far I have control over code flow, so risking some runtime issues for now is not a case. Thank you for help – ikarmus May 05 '23 at 15:56

1 Answers1

0

If you only have a small set of known classes, then generics is most likely the wrong solution. You would most likely be better of just specifying the type in the method name

Task<PagedResponse<MClass>> RetrieveApiMClas(...)
Task<PagedResponse<pClass>> RetrieveApipClass(...)
Task<PagedResponse<sClass>> RetrieveApisClass(...)

This should not be more code than using generics with a switch, and it removes the possibility of runtime errors.

If you do not know what type an API call will return you might be able to use a discriminated union. Something like a OneOf<MClass, pClass, sClass>. That way the caller can handle each type in different ways. There is apparently a proposal open to include this in the language, but it was not included in c# 11.

JonasH
  • 28,608
  • 2
  • 10
  • 23
  • Actually I plan to use more endpoints, which will make use of about 20-30 classes, those are just my first approach. – ikarmus May 05 '23 at 15:15
  • @ikarmus so? I do not see any reason why `RetrieveApiData` is preferable to `RetrieveApiDataMClass`, not if you need type specific code inside the generic method. I'm not really sure what the actual problem is, but if you need to do type checks in a generic method, there is a good chance you should be using some other pattern. – JonasH May 05 '23 at 15:27
  • That's a fair point, I need to rethink my approach. Thanks for your help – ikarmus May 05 '23 at 15:54