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 开发者_C百科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?
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
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
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
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
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.
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.
精彩评论