Case question

  • Thread starter Thread starter HardySpicer
  • Start date Start date
H

HardySpicer

Normally I have a simple problem like this

Case Is = "case_1"

sub_1()

Case Is ="case_2"

sub_2()


etc up to case_ n



Now I need to extend this so that "case_1" is in an array (ie so I can
load the value from a data-base)

for i= 0 to number_cases

Case Is = case(i)

' call the sub for the ith case
sub_i ()

next

Now how to I call the sub_i ?? Of course sub_1,sub_2 etc will exist.
Can the sub call be part of a string array of some sort? The reason I
need this is that in future I may need to change things so that case_1
calls sub_2 and so on - ie it needs to be re-configurable.

Thanks

Hardy
 
HardySpicer said:
Normally I have a simple problem like this

Case Is = "case_1"
sub_1()
Case Is ="case_2"
sub_2()
...etc up to... case_ n
case_ n

Now I need to extend this so that "case_1" is in an array (ie so I can
load the value from a data-base)

I think you'll have to give up with the Select and go back to good
old-fashioned If..Then's.

Dim caseOnes as New List(Of Integer)
caseOnes.Add( ...

Dim caseTwos as New List(Of Integer)
caseTwos.Add( ...

If caseOnes.Contains( selectVariable ) Then
sub_1()
ElseIf caseTwos.Contains( selectVariable ) Then
sub_2()
End If

The reason I need this is that in future I may need to change things
so that case_1calls sub_2 and so on - ie it needs to be re-configurable.

For that, you'll need some way of "mapping" the starting values to the
[name of the] method that needs to be called and then invoke the right
one at run-time.

Class DynamicLocalCalling

Public Sub New()

m_methods.Add( "sub_1" _
, Me.GetType().GetMethod( "sub_1" ) )
m_methods.Add( "sub_2" _
, Me.GetType().GetMethod( "sub_2" ) )

End Sub

Private m_methods as New Dictionary(Of String, MethodBase)

' Both "dynamic" sub's have to have the /same/ signature
Sub sub_1()
End Sub

Sub sub_2()
End Sub

End Class

To execute one of them, start with the [select] case variable from your
original code, use your "mapping" to get the required method name,
extract that from the list of available methods, and invoke it.
Sound horrible? Here you go:

Dim sValue as string = "1"

Dim sMethodName as String = GetMethodFromValue( sValue )
' sMethodName now contains, say, "sub_2"

methods( sMethodName ).Invoke( Me, Nothing )

This invokes the method "sub_2" on the current object ("Me.").
If you need arguments to these methods, replace the "Nothing" with an
Object array, something like:

methods( sMethodName ).Invoke( Me, New Object() { 1, 2, 3 } )

HTH,
Phill W.
 
As a side note, you're testing the patience of your coworkers with "Case Is
=" - just use "Case":
Case "case_1"
--
http://www.tangiblesoftwaresolutions.com
C++ to C#
C++ to VB
C++ to Java
VB & C# to Java
Java to VB & C#
Instant C#: VB to C#
Instant VB: C# to VB
Instant C++: VB, C#, or Java to C++
 
Normally I have a simple problem like this

Case Is = "case_1"

sub_1()

Case Is ="case_2"

sub_2()


etc up to case_ n



Now I need to extend this so that "case_1" is in an array (ie so I can
load the value from a data-base)

for i= 0 to number_cases

Case Is = case(i)

' call the sub for the ith case
sub_i ()

next

Now how to I call the sub_i ?? Of course sub_1,sub_2 etc will exist.
Can the sub call be part of a string array of some sort? The reason I
need this is that in future I may need to change things so that case_1
calls sub_2 and so on - ie it needs to be re-configurable.

Thanks

Hardy

In scenarios like yours I tend to implement this in terms of a command
pattern. This looks something like:

ption Strict On
Option Explicit On

Imports System
Imports System.Reflection

Module Module1
Private CommandNames() As String = _
New String() {"Sub_1", "Sub_2", "Sub_3"}
Private rand As New Random()

Sub Main()
For i = 1 To 20
Dim command As BaseCommand = _
DirectCast( _
Assembly.GetExecutingAssembly().CreateInstance(GetCommand(), _
True), BaseCommand)

command.Execute()
Next
End Sub

MustInherit Class BaseCommand
Public MustOverride Sub Execute()
End Class

Class Sub_1
Inherits BaseCommand

Public Overrides Sub Execute()
Console.WriteLine("Sub_1")
End Sub
End Class

Class Sub_2
Inherits BaseCommand

Public Overrides Sub Execute()
Console.WriteLine("Sub_2")
End Sub
End Class

Class Sub_3
Inherits BaseCommand

Public Overrides Sub Execute()
Console.WriteLine("Sub_3")
End Sub
End Class

Function GetCommand() As String
Return String.Format( _
"ConsoleApplication39.Module1+{0}", _
CommandNames(rand.Next(0, CommandNames.Length)))
End Function
End Module


You'll need to adjust the GetCommand() function to change the object
namespaces to be the correct namespaces of course :)
 
In scenarios like yours I tend to implement this in terms of a command
pattern.  This looks something like:

ption Strict On
Option Explicit On

Imports System
Imports System.Reflection

Module Module1
    Private CommandNames() As String = _
                New String() {"Sub_1", "Sub_2", "Sub_3"}
    Private rand As New Random()

    Sub Main()
        For i = 1 To 20
            Dim command As BaseCommand = _
                DirectCast( _
                        Assembly.GetExecutingAssembly().CreateInstance(GetCommand(), _
                        True), BaseCommand)

            command.Execute()
        Next
    End Sub

    MustInherit Class BaseCommand
        Public MustOverride Sub Execute()
    End Class

    Class Sub_1
        Inherits BaseCommand

        Public Overrides Sub Execute()
            Console.WriteLine("Sub_1")
        End Sub
    End Class

    Class Sub_2
        Inherits BaseCommand

        Public Overrides Sub Execute()
            Console.WriteLine("Sub_2")
        End Sub
    End Class

    Class Sub_3
        Inherits BaseCommand

        Public Overrides Sub Execute()
            Console.WriteLine("Sub_3")
        End Sub
    End Class

    Function GetCommand() As String
        Return String.Format( _
                "ConsoleApplication39.Module1+{0}", _
                CommandNames(rand.Next(0, CommandNames.Length)))
    End Function
End Module

You'll need to adjust the GetCommand() function to change the object
namespaces to be the correct namespaces of course :)

Looks good Tom but when I run it I get

Object reference not set to an instance of an object.

pointing at the line command.Execute()

I ran it by running Main() - I assume that's the way you do it?

Hardy
 
Looks good Tom but when I run it I get

Object reference not set to an instance of an object.

pointing at the line command.Execute()

I ran it by running Main() - I assume that's the way you do it?

Hardy

Well, the most likely reason is the namespace reference. Look at the
GetCommand function...

Function GetCommand() As String
Return String.Format( _
"ConsoleApplication39.Module1+{0}", _
CommandNames(rand.Next(0, CommandNames.Length)))
End Function

See how I'm forming the object name? Most likely, unless you named the
application ConsoleApplication39 then you are going to get that error :)

If everything else is the same, then chang the ConsoleApplication39 part of
that to the name of your app. This is not how I would do this in production
code though... This was only meant as a simple example :)
 
Well, the most likely reason is the namespace reference. Look at the
GetCommand function...

Function GetCommand() As String
Return String.Format( _
"ConsoleApplication39.Module1+{0}", _
CommandNames(rand.Next(0, CommandNames.Length)))
End Function

See how I'm forming the object name? Most likely, unless you named the
application ConsoleApplication39 then you are going to get that error :)

If everything else is the same, then chang the ConsoleApplication39 part of
that to the name of your app. This is not how I would do this in production
code though... This was only meant as a simple example :)

Just to make this a little more clear - here is more along the lines of what I
would do (because sometimes I've had these in separate assemblies, etc.). I
put the full names of the types in a dictionary and then create them that
way... Again, here is the simple example, without a hardcoded namespace...

Option Strict On
Option Explicit On

Imports System
Imports System.Reflection

Module Module1
Private _commandNames() As String = New String() {"Sub_1", "Sub_2", "Sub_3"}
Private _typeLookup As New Dictionary(Of String, String)()
Private _rand As New Random()

Sub Main()
' init lookup stuff for example
Dim types() As Type = Assembly.GetExecutingAssembly().GetTypes()
For Each t As Type In types
If t.BaseType.Equals(GetType(BaseCommand)) Then
_typeLookup.Add(t.Name, t.FullName)
End If
Next

For i = 1 To 20
Dim command As BaseCommand = _
DirectCast(Assembly.GetExecutingAssembly().CreateInstance(GetCommand(), True), BaseCommand)

command.Execute()
Next
End Sub

MustInherit Class BaseCommand
Public MustOverride Sub Execute()
End Class

Class Sub_1
Inherits BaseCommand

Public Overrides Sub Execute()
Console.WriteLine("Sub_1")
End Sub
End Class

Class Sub_2
Inherits BaseCommand

Public Overrides Sub Execute()
Console.WriteLine("Sub_2")
End Sub
End Class

Class Sub_3
Inherits BaseCommand

Public Overrides Sub Execute()
Console.WriteLine("Sub_3")
End Sub
End Class

Function GetCommand() As String
Return _typeLookup(_commandNames(_rand.Next(0, _commandNames.Length)))
End Function
End Module


Simple console app... In one app, where I used this, was in a server
application. The client would send a command and it's parameters in a
delimited sting something like:

DO_COOL_STUFF:param1,param2,param3,param4

The server would get the command name, look up the class associated with that
command in the dictionary and then execute the sub by passing in the param
string. Worked great - and no big supper switch statement :)

Anyway, you can be creative :) I have some dynamic validation logic that does
a similar thing, but looks up the class names in a database...
 
Back
Top