Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can use IList but not List in generic method

I'm trying to create a method which returns a list of whichever type the user wants. To do this I'm using generics, which I'm not too familiar with so this question may be obvious. The problem is that this code doesn't work and throws the error message Cannot convert type Systems.Collections.Generic.List<CatalogueLibrary.Categories.Brand> to Systems.Collection.Generic.List<T>

private List<T> ConvertToList<T>(Category cat)
{            
     switch (cat)
     {
         case Category.Brands:
             return (List<T>)collection.Brands.ToList<Brand>();

     }
    ...
}

But if I use IList instead, there are no errors.

private IList<T> ConvertToList<T>(Category cat)
{            
     switch (cat)
     {
         case Category.Brands:
             return (IList<T>)collection.Brands.ToList<Brand>();

     }
     ...
} 

Why can I use IList but not List in this case? collection.Brands returns a BrandCollection type from a third party library so I don't know how that's created. Could it be that BrandCollection may derive from IList (just guessing that it does) and so it can be converted to it but not to a normal List?

like image 572
XSL Avatar asked Mar 24 '13 20:03

XSL


1 Answers

Since there are no constraints on T, it can only be converted to object at compile time. Casts to interface types aren't checked by the compiler since there could theoretically be a new class created which implements IList<object> and inherits List<Brand>. However, the cast to List<T> will fail since it is known that there cannot be a class created which inherits both List<object> and List<Brand>. However, in your case, you know what the type T is through your switch statement and wish to force the cast. To do this, cast through object first as follows:

private List<T> ConvertToList<T>(Category cat)
{            
    switch (cat)
    {
        case Category.Brands:
            return (List<T>)(object)collection.Brands.ToList<Brand>();
    }
}

The bigger design problem here, though, is that generics are not the best choice when you have a discrete list of known types for T. Generics are better when T can either be anything, or be constrained to a base type or interface. Here, you'd be better off just writing a separate method for each branch of the switch statement:

private List<Brand> ConvertToBrandList()
{
    return collection.Brands.ToList<Brand>();
}

Without this, you have very little type safety. What if someone calls your method with ConvertToList<int>(Category.Brands)?

like image 168
jam40jeff Avatar answered Oct 12 '22 05:10

jam40jeff