tags:

views:

116

answers:

5

I have a project that has mainly two objects, both inheriting from a base. Like this:

Public Class Vehicle
    Property Model As String
    Property Make As String
End Class

Public Class Truck
    Inherits Vehicle
    Property IsFlatbed As Boolean
End Class

Public Class Car
    Inherits Vehicle
    Property LeatherSeats As Boolean
End Class

Simple enough, eh? Because I don't know if a user will choose car or truck, what I would like to do is just pass around Vehicle.

So, something like this:

Public v As Vehicle
Sub WhichVehicle()
    Select Case cmbVehicle.SelectedItem
        Case Truck
            v = New Truck
        Case Car
            v = New Car
    End Select
    SetFlat (v)
End Sub

This all works, but now I just want to pass v around and use it's properties. Like:

Sub SetFlat (myVehicle As Vehicle)
    myVehicle.IsFlatbed = True
End Sub

The above function doesn't work because myVehicle is a Vehicle, not a Truck.

Is there a way I can pass around a Vehicle type and have the IDE know which type to use? Or am I completely missing a better way to do this?

+2  A: 

Basically when you call SetFlat you know your vehicle has a property named IsFlatbed, right?

Then you should declare an interface Flattable which includes this property. The class Truck would implement that interface, and the SetFlat sub would have a Flattable object as a parameter instead of a vehicle.

Edit:

What about this:

Public Interface IFlattable
    Property IsFlatbed() As Boolean
End Interface

Public Class Truck
    Inherits Vehicle
    Implements IFlattable

    Private _isFlatBed as Boolean
    Public Property IsFlatbed() as Boolean Implements IFlattable.IsFlatbed
        Get
            Return _isFlatbed
        End Get
        Set(ByVal value as Boolean)
            _isFlatbed = value
        End Set
End Class


Public v As Vehicle
Sub WhichVehicle()
    Select Case cmbVehicle.SelectedItem
        Case Truck
            v = New Truck
            SetFlat (DirectCast(v, IFlattable))
        Case Car
            v = New Car
    End Select
End Sub

Sub SetFlat (myVehicle As Flattable)
    myVehicle.IsFlatbed = True
End Sub
GôTô
I'm not sure what this means, the interface `Flattable` thing. The reason I'm wanting to use `v` to pass around is that I have other objects that I pass it too. for example, a `Warranty` class. I may need the `.IsFlatbed` in that class, but only if `v` has it (else it equals nothing). If I have to pass `Truck` and `Car` seperately, I'll need two different `Warranty` classes...I think.
WinnerWinnerChickenDinner
With the particular case of boolean properties (like IsFlatbed), you could always have a base class (Vehicle) that implements all of these properties as overridable (virtual) methods, but returns false. Then, you would override those properties in derived classes where they might need to return a different value, such as in the derived class Truck. This may of course only work in this contrived example, but it's worth a thought, and it'd make it very easy to work with the Vehicle base class.
Cody Gray
A: 

I think you should give some more details about what you try to acomplish... Anyways, in VB you have an operator to determine if an object is of a type, that is the typeof operator. You could test if v is a Truck and use a cast operator (DirectCast, CType) to cast v to a Truck, like

Dim v as Vehicle
'...
If typeof v is Truck then SetFlat(DirectCast(v, Truck))

EDIT: SetFlat should take a Truck as parameter, only that makes sense.

jaraics
i already gave plenty of details above and in the other answers' comments. directly casting v to Truck still doesn't give me Truck properties when using v.
WinnerWinnerChickenDinner
DirectCast(v,Truck) will give you all the properties of Truck
jaraics
The better solution in your case might be to continue to have SetFlat accept a Vehicle as its parameter, but then perform the type-casting itself. For example, you pass in a Vehicle, it checks to see if "TypeOf V Is Truck", and if so, casts v to a Truck (Dim t as Truck = DirectCast(v, Truck)). Then the method calls in SetFlat can work with the t object as if it were a Truck, because it is. If "TypeOf V IsNot Truck", you can either throw an exception, or always return False, or whatever is appropriate.
Cody Gray
+2  A: 

Hi,
I found two approaches which may help you.

The more elegant one is to force your base to subclass by usage of a generic method without type inference. It could look something like (I'm not a VB.Net programmer so there may be some errors):

Sub SetFlat(of T) (myVehicle As T)
    T.IsFlatbed = True
End Sub
// later you can just call
SetFlat(Of Truct)(myVehicle)  

Of course this implies that you need to know the exact type of myVehicle object before calling SetFlat function. Also SetFlat can only be called with classes which do have the IsFlatbed property. Some more details about generics in VB.Net:
http://www.15seconds.com/issue/040526.htm
http://msdn.microsoft.com/en-us/library/w256ka79%28VS.80%29.aspx
http://stackoverflow.com/questions/206982/generic-functions-in-vb-net

A second (dirty) solution is to use .Net reflection to detect if the myVehicle object contains the IsFlatbed property. You may find more details on:
http://msdn.microsoft.com/en-us/magazine/cc163750.aspx
http://visualbasic.about.com/od/usingvbnet/a/proginfo.htm
http://www.eggheadcafe.com/community/aspnet/14/14989/reflection.aspx

Marcin
+2  A: 

I decided to fire up Visual Studio and do some testing because my comments to the other answers probably won't make much sense. Assuming that you have the following classes:

Public Class Vehicle
   Public Property Model As String
   Public Property Make As String
End Class

Public Class Truck : Inherits Vehicle
   Public Property IsFlatbed As Boolean
End Class

Public Class Car : Inherits Vehicle
   Public Property LeatherSeats As Boolean
End Class

You could also have the following methods in another class:

Private Sub WhichVehicle()
    Select Case cmbVehicle.SelectedItem
        Case Truck
            v = New Truck
        Case Car
            v = New Car
    End Select

    SetFlat(v)
End Sub

Private Sub SetFlat(ByVal myVehicle As Vehicle)
   If TypeOf myVehicle Is Car Then
      Debug.WriteLine("This is a car")

      Dim c As Car = DirectCast(myVehicle, Car)
      c.LeatherSeats = False
   ElseIf TypeOf myVehicle is Truck Then
      Debug.WriteLine("This is a truck")

      Dim t As Truck = DirectCast(myVehicle, Truck)
      t.IsFlatbed = True
   End If
End Sub

So, this allows you to pass around a vehicle object as you were wanting, because you won't know until run-time what specific type of vehicle that you are dealing with (either a Car or a Truck). The SetFlat method can determine during run-time what specific sub-class of vehicle that it was passed, and act accordingly. You just have to make sure that you cast the generic vehicle object (v) to a new object of the more specific sub-class after you've determined which type of sub-class that it is (either c for Car or t for Truck), otherwise the code will not compile because you would be trying to call a method that does not exist on the generic vehicle type, but only on the specific sub-types of vehicles.

The biggest downside that I see to this approach is that it may start to get tedious if you have a lot of code. This is because each method that you want to call will have to check to see which type of specific vehicle object that it was passed, and run a different set of routines depending on that specific type.

Cody Gray
+1  A: 

This is one way you could achieve this:

Imports System.Reflection
Module main_
    Sub Main()
        Dim t As New Truck
        Dim c As New Car
        Dim v As Vehicle
        v = t
        SetFlat(v)
        v = c
        SetFlat(v)
    End Sub
    Sub SetFlat(ByVal v As Vehicle)

        Dim vehicletype As Type
        Dim members() As PropertyInfo

        vehicletype = v.GetType
        members = vehicletype.GetProperties

        Console.Write("v is a " & vehicletype.ToString)
        For Each m As PropertyInfo In members
            If m.Name = "IsFlatbed" Then
                m.SetValue(v, True, Nothing)
                Console.WriteLine(" and now it's a flatbed")
                Exit Sub
            End If
        Next
        Console.WriteLine(" so flatbed doesn't apply")
    End Sub
End Module

Output:

v is a Vehicles.Truck and now it's a flatbed
v is a Vehicles.Car so flatbed doesn't apply
smirkingman
All of the others had great suggestions, but this is the one that will most suit my needs. Thank you!
WinnerWinnerChickenDinner
Glad to have been of assistance and thank you for accepting my answer. As you might imagine, this is the tip of the iceberg as far as System.Reflection is concerned; please do feel free to get back to me if you would like to explore this avenue in more depth.
smirkingman