Manolis Manolis - 3 months ago 14
Vb.net Question

Avoiding long case statement on argument class

There is a class hierarchy of tasks that can be performed by the class A.

Inside the class A there is a long case statement checking for the task class and calling the correspondent handler method.

Class Task
End Class

Class TaskX
Inherits Task
End Class

Class TaskY
Inherits Task
End Class

...


Class A
Public Sub PerformTask(Task As Task)
Select Case Task.GetType
Case GetType(TaskX)
PerformTaskX(CType(Task, TaskX))
Case GetType(TaskY)
PerformTaskY(CType(Task, TaskY))
End Select
End Sub


Private Sub PerformTaskX(Task As TaskX)
End Sub

Private Sub PerformTaskY(Task As TaskY)
End Sub

...
End Class


The tasks have their individual parameters, validation logic, etc. The example is simplified.

I want to avoid the case statement and I am looking for alternative solutions.

The general idea is mapping a class type to a Handler(Of class) that could work somehow like this:

Delegate Sub Handler(Task As Task)

Private TypeToHandlerDict As New Dictionary(Of System.Type, Handler) From { _
{GetType(TaskX), AddressOf PerformTaskX}, _
{GetType(TaskY), AddressOf PerformTaskY} _
}


so that I can replace the PerformTask method contents with the much more elegant:

Public Sub PerformTask(Task As Task)
Dim handler As Handler = Nothing
If Not TypeToHandlerDict.TryGetValue(Task.GetType, handler) Then Exit Sub
handler(Task)
End Sub


But it doesn't work.

The delegate binds to type Task, but the handler methods accept a task subclass parameter. I am using Option Strict.
I could rewrite all handler methods to accept a Task object and perform an explicit cast, but this is equally inelegant.

Any suggestions?
Thank you!

Update: Here is the solution thanks to the answer from @Sehnsucht.

Class Task
Public MustOverride Sub Perform(a As A)
End Class

Class TaskX
Inherits Task
Public Overrides Sub Perform(a As A)
a.DoThis()
End Sub
End Class

Class TaskY
Inherits Task
Public Overrides Sub Perform(a As A)
a.DoThat()
End Sub
End Class

...


Class A
Public Sub PerformTask(Task As Task)
Task.Perform(Me)
End Sub


Public Sub DoThis()
End Sub

Public Sub DoThat()
End Sub

...
End Class

Answer

All the PerformTask... have the same signature and that's enforced by the delegate Handler you showed in your post.
So you should make an Overridable method in your Task class with a default implementation (or mark the method as MustOverride if the Task class is MustInherit), method which will be overrided by it's inheritors to do the job according to their specific case.

Class Task
    Public Overridable Sub Perform()
        ' implementation specific to Task ("default" case)
    End Sub
End Class

Class TaskX
    Inherits Task

    Public Overrides Sub Perform()
        ' implementation specific to TaskX
        ' you can call the parent implementation if needed with
        MyBase.Perform()
    End Sub
End Class

'Usage
Class A
    Public Sub PerformTask(task As Task)
        task.Perform() ' will call the "right one" according to it's runtime type
    End Sub
End Class
Comments