2

I did this little console application to illustrate my problem.

I wonder if there is a way to access the properties on the class which is the current implementation of the interface you are working on. In this example below I would like to set the vehicle interface property Engine if it is a car. I found a way to do this using the brackets when you are doing a new instance of car, but I would like to do this later by doing vehicle.Engine = "something";

namespace ConsoleApplication1
{
class Program
{
    static void Main(string[] args)
    {
        var car = (Car)GetVehicle(VehicleType.Car);

        var bike = (Bike)GetVehicle(VehicleType.Bike);

        Console.ReadKey();
    }



    static IVehicle GetVehicle(VehicleType type)
    {
        IVehicle vehicle = null;

        switch (type)
        {
            case VehicleType.Car:
                vehicle = new Car() { Engine = "V8", Name = "Car Name" };//Works
                vehicle.Engine = "cannot do this"; //This does not work
                break;
            case VehicleType.Bike:
                vehicle = new Bike() { Mountainbike = true, Name = "Bike name" }; // Works
                vehicle.Mountaikbike = true; //This does not work either, same problem
                break;
            default:
                break;
        }

        return vehicle;
    }


}


public enum VehicleType
{
    Car, Bike
}


public class Car : IVehicle
{
    public string Name { get; set; }
    public string Engine { get; set; }

}

public class Bike : IVehicle
{
    public string Name { get; set; }
    public bool Mountainbike { get; set; }
}

public interface IVehicle
{
    string Name { get; set; }
}

}

1
  • Look at Casting. Commented Jun 3, 2015 at 8:58

5 Answers 5

3

Even though your GetVehicle() method returns an IVechile, it doesn't mean that internally it has to refer to the returned object as IVechile as well. It can definitely refer to it as the actual (concrete) type and then having the ability to access its public properties as usual.

So you can simply do this:

static IVehicle GetVehicle(VehicleType type)
{
    IVehicle ret = null;

    switch (type)
    {
        case VehicleType.Car:
            var car = new Car() { Name = "Car Name" };
            car.Engine = "V8";
            ret = car;
            break;
        case VehicleType.Bike:
            var bike = new Bike() { Name = "Bike name" }; 
            bike.Mountainbike = true;
            ret = bike;
            break;
    }

    return ret;
}

Or even shorter:

static IVehicle GetVehicle(VehicleType type)
{
    switch (type)
    {
        case VehicleType.Car:
            return new Car() { Engine = "V8", Name = "Car Name" };
        case VehicleType.Bike:
            return new Bike() { Mountainbike = true, Name = "Bike name" }; 
    }

    return null;
}
Sign up to request clarification or add additional context in comments.

Comments

3

This is known as 'leaky abstraction' and shouldn't be done: ((Car)vehicle).Engine = "...". Your code should deal in contracts (interfaces) not in concrete implementations (classes).

In this instance you should create an ICar interface that extends IVehicle and use it to expose the Engine property and test if your IVehicle exposes ICar. This way many things can be treated as IVehicle and ICar is a specialization that can be handled more specifically.

9 Comments

True. Casting interface to the concrete type is usually a sign of a code smell.
Your answer started so promising... and then you just suggest adding another interface. That does nothing to solve the OPs problem, and it doesn't really help the proper abstractions in the code either.
Your GetVehicle method is ok for now I think as you are dealing in concrete classes internally and exposing IVehicle publically. Although you may need to separate into various factories as you develop this further. If you have a list of vehicles you can do use some linq: vehicles.OfType<ICar>(). If you have a single instance just use 'var car = vehicle as ICar' and if not null then proceed.
By the way; the boolean Motorbike property on Bike is not very OO. Bike is a base class and shouldn't know about any specific sub class type. You should create a Motorbike sub class instead and remove this property.
He's got a factory - it's perfectly fine to work with class specific fields and features in the code that actually creates the specific class instance. Your approach would be great if he was working with a method that accepts IVehicle, but that's not what he's doing. As for Motorbike, well, it really shows how class-OOP isn't really all that good of an abstraction. It's still powerful when used well and on the right kind of problem, but the abstraction breaks way too easily. Programmers love trees, but there's many problems that simply aren't tree-like :D
|
2

To answer the question of how to do it later, if you have code like

IVehicle vehicle= GetVehicle(VehicleType.Car);

// cast car 
Car car = vehicle as Car;

if (car != null)
{
 car.Engine = "V8";
}

basically IVehicle does not have 'Engine' as a property. it is something a Car type added. Hence you need to cast your IVehicle to Car and check if it is really a car. If it is, then you set it.

And if you think that a lot of your vehicles would need Engine, then you can do:

public interface IVehicleWithEngine :IVehicle
{
 string Engine { get; set; }
}

and make Car and other 'Engine' based vehciles implement it.

Comments

1

The problem is caused by where you are initiating the vehicle, you can just return from the switch since that is all you are doing in the method.

switch (type)
{
    case VehicleType.Car:
        return new Car() { Engine = "V8", Name = "Car Name" };
        break;
    case VehicleType.Bike:
        return new Bike() { Mountainbike = true, Name = "Bike name" }; 
        break;
    default:
        break;
}
return null;

Comments

-2

you can write this:

((Car)vehicle).Engine = "what you want";

simply you are casting the object when needed.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.