Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can't cast derived type to base abstract class with type parameter

I have a simple factory method which provides a concrete implementation instance based on a generic type parameter provided. If the concrete classes inherit from a common abstract base class with a type parameter I cannot cast them. The compiler tells me Error 2 Cannot convert type 'Car' to 'VehicleBase<T>'. It works fine if I substitute the abstract class for an interface with the same type parameter, or if I remove the generic type parameter from the abstract class.

interface IWheel
{
}

class CarWheel : IWheel
{
}

abstract class VehicleBase<T>
{
}

class Car : VehicleBase<CarWheel>
{
}

class VehicleFactory
{
    public static VehicleBase<T> GetNew<T>()
    {
        if (typeof(T) == typeof(CarWheel))
        {
            return (VehicleBase<T>)new Car();
        }
        else
        {
            throw new NotSupportedException();
        }
    }
}

This fails to compile on (VehicleBase<T>)new Car(). Is this a compiler defect, or could this be a deliberate design decision to treat abstract classes and interfaces with type parameters differently?

As a workaround I can always make the abstract class implement an interface and use this as the return value for my factory method, but I'd still like to know why this behavior is happening.

like image 634
dahvyd Avatar asked Jun 26 '12 06:06

dahvyd


1 Answers

That is not provable, because generic code needs to work (with the same IL) for every possible T, and there is nothing to say that Car : VehicleBase<float>, for example. The compiler does not over-analyse the fact that the if check sows that T is CarWheel - the static checker treats each statement separately, it doesn't try to understand the cause-and-effect of conditions.

To force it, cast to object in the middle:

return (VehicleBase<T>)(object)new Car();

However! Your approach isn't really "generic" as such.

like image 86
Marc Gravell Avatar answered Nov 15 '22 05:11

Marc Gravell