Search code examples
c#genericscasting

Converting explicit type to T in generic method


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.


Solution

  • 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.