Search code examples
c#genericsabstract

Implement generic abstract method with properties not present in abstract class


I have the following setup:

abstract class Vehicle with the following simplified structure:

public abstract class Vehicle
{
    public string Brand { get; set; }
    public string Model { get; set; }
    public string GetBasicInfo(Vehicle v)
    {
        return string.Format("This car was produced by {0}. The Model is {1}.", Brand, Model);
    }
    abstract public string GetVehicleData<T>(T vehicle) where T : Vehicle;
}

I then have a class Car inheriting from Vehicle with some properties of its own:

public class Car : Vehicle
{
    public bool Is4x4 {get;set;}
    public override string GetVehicleData<Car>(Car vehicle)
    {
        return String.Format("Brand: {0}, Model: {1}, Is4x4: {2}", vehicle.Brand, vehicle.Model, vehicle.Is4x4);
    }
}

When I try to access vehicle.Is4x4 I get an error, namely that Car does not contain a definition for Is4x4. I research on SO and found out, that since the property Is4x4 is not defined in the abstract parent class, it cannot be called upon in the overriden method.

To circumvent this problem I changed the 2 classes as follows:

public abstract class Vehicle
{
    public string Brand { get; set; }
    public string Model { get; set; }
    public string GetBasicInfo(Vehicle v)
    {
        return string.Format("This car was produced by {0}. The Model is {1}.", Brand, Model);
    }
    abstract public string GetVehicleData(Vehicle vehicle);
}

And the car class:

public class Car : Vehicle
{
    public bool Is4x4 {get;set;}
    public override string GetVehicleData(Vehicle vehicle)
    {
        Car vehicleCast = (Car)vehicle;
        return String.Format("Brand: {0}, Model: {1}, Is4x4: {2}", vehicleCast.Brand, vehicleCast.Model, vehicleCast.Is4x4);
    }
}

This code compiles and allows me to add the child class specific properties in the GetVehicleData Method. However I feel like there should be a better way to solve this common problem.


Solution

  • The problem is this line:

    public override string GetVehicleData<Car>(Car vehicle)
    

    This is the same as writing:

    public override string GetVehicleData<T>(T vehicle)
    

    ... except that you've used Car as the name of the type parameter, rather than the more conventional T. Car there does not refer to the class Car, instead it refers to a new generic type parameter.

    When you override a generic method, the language doesn't force you to use the same type parameter names: you could write

    public abstract class Vehicle
    {
        public abstract string GetVehicleData<T>(T vehicle) where T : Vehicle;
    }
    
    public class Car : Vehicle
    {
        public abstract string GetVehicleData<T2>(T2 vehicle) { ... }
    }
    

    ... and you've chosen to use Car instead of T2.


    To address the underlying problem, you can't define the base method Vehicle.GetVehicleData(Vehicle vehicle), and then override that with a more specific Car.GetVehicleData(Car car). What would be called if someone did new Car().GetVehicleData(new Truck())?

    Car's GetVehicleData method needs to be able to accept any type of Vehicle.

    That said, it's unclear why your GetVehicleData is taking a second vehicle, rather than just operating on the instance it's defined on.

    It would be more normal to write something like:

    public abstract class Vehicle
    {
        public string Brand { get; set; }
        public string Model { get; set; }
        public string GetBasicInfo()
        {
            return string.Format("This car was produced by {0}. The Model is {1}.", Brand, Model);
        }
        public abstract string GetVehicleData();
    }
    
    public class Car : Vehicle
    {
        public bool Is4x4 {get;set;}
        public override string GetVehicleData()
        {
            return String.Format("Brand: {0}, Model: {1}, Is4x4: {2}", Brand, Model, Is4x4);
        }
    }