Did you ever have a method that returned a generic list of some type but you actually needed a generic list of one of its interfaces or base class? You probably solved this by cloning the method or by creating a new list while looping through all items. In the article below I will show you a nice and simple generic solution for this problem.
Let’s assume you have a class named Car that has an interface named IVehicle and you also have a class named Plane that also has the interface IVehicle. We also have a data repository that has a method for each of them for retrieving a list of items. Now we want to execute the IVehicle method named Move on all Car objects and Plane objects. We will start by retrieving both lists from our data repository object named MyRepository:
var cars = MyRepository.GetCars()
var planes = MyRepository.GetPlains()
Wouldn’t it be nice if we could just concatenate those 2 lists so that we could call the IVehicle method for all of them in one simple loop?
var vehicles = cars.Concat(planes);
foreach(var vehicle in vehicles)
{
vehicle.Move();
}
Unfortunately trying this will give you this error:
Error 1 The type arguments for method ‘System.Linq.Enumerable.Concat
If we do want to Concat the two collections together, then we first have to cast both collections to th IVehicle interface. At this moment C# 3.0 does not have a simple option to convert a generic list to one of its interfaces or base classes. The .ConvertAll is not easy to use. Fortunately Microsoft recognized this missing feature and in C# 4.0 you will be able to perform this cast by using Covariance and Contravariance. For now you have to use the difficult .ConvertAll method. The simple helper class below will make this easy for you.
public static class GenericListConverter
{
public static T2 AsType<T1, T2>(this T1 model) where T1 : T2
{
return model;
}
public static IList<T2> ToListOfType<T1, T2>(this IEnumerable<T1> myList) where T1 : T2
{
return (new List<T1>(myList.ToList())).ConvertAll<T2>(AsType<T1, T2>);
}
public static IList<T2> ToListOfType<T1, T2>(this IList<T1> myList) where T1 : T2
{
return (new List<T1>(myList)).ConvertAll<T2>(AsType<T1, T2>);
}
}
Using this class is simple. You can just perform a ToListOfType on any generic IEnumerable or IList.
var cars = myRepository.GetCars().ToListOfType<Car, IVehicle>();
var planes = myRepository.GetPlains().ToListOfType<Plain, IVehicle>();
The result of this is that you have 2 generic lists of IVehicle and the Concat will now work.
In this case there is an alternative to the above helper class. It is possible to create a helper method that accept anything that implements the IVehicle interface. The code would then look something like:
...
Move((List<Car>)cars);
Move((List<Plane>)planes);
...
public static void Move<TItem>(List<TItem> vehicles) where TItem : IVehicle
{
foreach (var vehicle in vehicles)
{
vehicle.Move();
}
}
