0

I'm trying to create a program that inputs a command from the user e.g. "add 2 4" and then calls a method based on that command. So I type in the command string "add 2 4", then a parser reads the first word "add" and outputs it as the name of the routine, sends that routine name to another sub with the parameters, and then calls the relevant sub.

Essentially I'm trying to avoid this:

Select Case RoutineName.ToUpper
    Case "ADD"
        Add(Param1, Param2)
    Case "SUBTRACT"
        Subtract(Param1, Param2)
    Case "MULTIPLY"
        Multiply(Param1, Param2)
    ...

Because I want to be able to easily add references to a new sub without having to continually update a Selection statement.

I saw someone else having the same problem: Create a method call in .NET based on a string value but I couldn't make sense of any of the answers to his question, or they weren't in VB. One person suggested using a dictionary of delegates, with a string as a key. This sounds like a great idea, but delegates confuse me a great deal: would anyone be able to show me how this solution might be implemented?

Community
  • 1
  • 1
Lou
  • 2,200
  • 2
  • 33
  • 66
  • "or they weren't in VB": translate them to VB, it's easy since you are actually using the same infrastructure as c# – Felice Pollano Jun 13 '13 at 06:06
  • Also, take have a look at using Func. http://msdn.microsoft.com/en-us/library/bb534647.aspx – Adriaan Stander Jun 13 '13 at 06:07
  • @FelicePollano I tried translating one of the answers to C#, but it didn't work and I didn't understand what the original code was doing so I didn't know how to make it work - then I felt rude butting in on someone else's question to ask :P – Lou Jun 13 '13 at 21:30

2 Answers2

1

You have to look at Reflection. Search on the NET there are plenty of examples in VB.NET too. The strategy I can suggest is having a class implementing the functions Add Subtract etc and use the reflection GetMethod() and Invoke() to call them according to the command string.

an example could be:

Type type = calcobject.GetType();
MethodInfo method = type.GetMethod(RoutineName);
if (method != null)
{
    method.Invoke(this, ...parameters...);
}

Using a dictionary of delegates would be probably better but it does not satisfy you request of automatic binding: you need to manually add the new function in the dictionary when you decide to extend the function your interpreter understand. You can even fill the dictionary automatically; but in this case reflection come up again.

Felice Pollano
  • 32,832
  • 9
  • 75
  • 115
  • Hey, thank you, I've got your example working now, but there's one issue: the second argument for Invoke wants a reference type, not a value type. So even if the method uses integers I'd still have to store the parameters using something else, and the obvious thing to use is Object... but that doesn't seem like a very elegant solution. Is there any way around using a reference type with this method? – Lou Jun 17 '13 at 06:15
  • the second parameter is an array of objects.Each item of the array is one parameter – Felice Pollano Jun 17 '13 at 07:00
  • Okay. One more problem - this works within a class but I can't work out how to do it outside of a class. I want to just have a generic sub or function and refer to it using reflection – Lou Jun 17 '13 at 16:07
  • Maybe the @Ceres answer is more interesting for you in this case. Otherwise reflection works also with static classes – Felice Pollano Jun 17 '13 at 16:12
  • I liked his answer and I managed to get that implementation working, but while the dictionary method is neat I'd like to know how to use reflection so that all I have to do to extend the functionality is add new subroutines and functions, rather than adding in a new member to the dictionary for every routine. – Lou Jun 17 '13 at 17:11
  • Also I don't understand what you mean by static classes. Apparently VB.net doesn't have them. – Lou Jun 17 '13 at 17:12
  • @LeoKing Module == static class – Felice Pollano Jun 18 '13 at 05:16
1

I used arrays in case you want to pass a variable amount of parameters to the functions. You don't have to though

Dim methods As New Dictionary(Of String, Func(Of Double(), Double))
Dim results as double

methods.Add("Add", Function(values() As Double) values(0) + values(1))
methods.Add("Sub", Function(values() As Double) values(0) - values(1))
methods.Add("Mul", Function(values() As Double) values(0) * values(1))
methods.Add("Div", Function(values() As Double) values(0) / values(1))

results = methods("Add")(New Double() {1, 2})
results = methods("Sub")(New Double() {5, 3})
results = methods("Mul")(New Double() {4, 8})
results = methods("Div")(New Double() {1, 2})

Without the arrays...

Dim methods As New Dictionary(Of String, Func(Of Double, Double, Double))

methods.Add("Add", Function(a As Double, b As Double)
                       Return a + b
                   End Function)

methods.Add("Sub", Function(a As Double, b As Double)
                       Return a - b
                   End Function)

methods.Add("Mul", Function(a As Double, b As Double)
                       Return a * b
                   End Function)

methods.Add("Div", Function(a As Double, b As Double)
                       Return a / b
                   End Function)

results = methods("Add")(1, 2)
results = methods("Sub")(5, 3)
results = methods("Mul")(4, 8)
results = methods("Div")(1, 2)
Ceres
  • 3,524
  • 3
  • 18
  • 25
  • Ooh thank you, I hadn't thought about using Lambda expressions! I suppose I could just as easily use a Sub instead of a Function with a call to a separate routine included :3. Thanks, this looks like just what I needed! – Lou Jun 13 '13 at 21:33