Carl Franklin

.NET Wonk

Calling a function from a variable (using delegates)

I wanted to share this code with my readers. I recently answered a post on vbcity.net where Cindi asked:

I have a class called HardDate that will include 70 different functions.

Each function is a symbol that will match the data that I will be iterating through..
For example.. SP, HO & HU are the symbols.

So I will be able to call the following 3 functions

Code:

Dim Value1, Value2, Value3 as int16
Dim HD as HardDate

Value1=HD.SP
Value2=HD.HO
Value3=HD.HU

Is there a way for me to pass a variable as the function name?


Code:

Dim Value1 as int16
Dim HD as HardDate
Dim iSym as string = “SP”
 
Value1=HD.iSym 'which will simply call HD.SP


Or am I going to have to find another way around this that will need to list out all 70 functions as in a "Select Case" or "ElseIF"?

Can I create an object array that holds all 70 functions can call it by its index number??

I don't know what my options are.. but there must be a better way of doing it then listing out all 70 functions..

My Response:

You need a delegate. Think of a Delegate as an object that represents a method call and can be passed around like any other variable.

Create a form and put two buttons on it. Here is some sample code:

Public Class Form1
    Inherits System.Windows.Forms.Form

'#Region " Windows Form Designer generated code " REMOVED

    '-- This is really a data type that represents a function call having a single string argument.
    ' Create others for subs that have different argument lists (or signatures)
    Private Delegate Sub dlgSingleString(ByVal Arg As String)

    '-- Create a new object that has the methods you want to call
    Dim tc As New TestClass

    '-- These variables represent the function calls
    Dim dlg1 As dlgSingleString
    Dim dlg2 As dlgSingleString

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        '-- The dlgSingleString objects are created with the address of a method (could be local, global, or in any object)
        dlg1 = New dlgSingleString(AddressOf tc.Sub1)
        dlg2 = New dlgSingleString(AddressOf tc.Sub2)
    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        '-- Call the first sub
        dlg1.Invoke("Hello Sub 1")
    End Sub

    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        '-- Call the second sub
        dlg2.Invoke("Hello Sub 2")
    End Sub

End Class

Public Class TestClass
    '-- Simple class with two subs

    Public Sub Sub1(ByVal Arg As String)
        MsgBox("Sub1: " & Arg)
    End Sub

    Public Sub Sub2(ByVal Arg As String)
        MsgBox("Sub2: " & Arg)
    End Sub

End Class

 

Comments

Maxim V. Karpov said:

Carl,
This is very good use of Delegates. I agree with you. I had a post on this topic on my blog as well let me know what you think:

http://faithinteractive.com/simpleblog/PermLink.aspx?entryid=13

There is a clean demo code for all senerios of using delegates and events :)

Thanks, Maxim
# November 22, 2003 12:11 PM

Jim Cheseborough said:

Delegates. So was Cindi satified with you ans.?
Still seems a bit confusing to me.
Thanks.
Jim
# November 24, 2003 12:03 AM

Jim Cheseborough said:

Ok, I seem to have learn how to declare and use a delegate, but please explain to me how this woman problem was solved by using delegates. In other words, I don't yet see how your detailed explanation solved her problem. Of course it's my ignorance that's the problem here.

deleg = new xxx(address of XYZ)

But isn't Cindi saying that XYZ is a variable.
So, how does your explanation work with this.

She has methods:
obj.aa
obj.bb
obj.cc
etc...

So, if I understand this correctly, she needs to do something like:
deleg = new xxx(address of obj.aa)

But...the fact that we need "aa" this time (say it was passed into a Querystring - ...aspx?meth=aa) means that we need something like:

'--- Bad syntax - just making a point
deleg = new xxx(address of obj. & meth)

So...please set me straight on this. I'm lost.

Thanks a lot.
Jim
jim@cheseborough.com
# November 24, 2003 3:45 PM

Carl Franklin said:

Jim,

The question I answered for her is "Is there a way for me to pass a variable as the function name"

IOW she wants to represent a function call with a variable.

I did not address her issue of "I don't want to have to type 70 function calls separately" but I did see it as an opportunity to show her how she could represent the methods of a class as variables.

If you understand the fundamental of how to represent functions as delegates then it's an easy logical step to figure out how to return an array of delegates that represent the function calls.

# November 24, 2003 4:00 PM

Jim Cheseborough said:

Carl,
Thanks a bunch for your reply!
I feel really stupid asking again, but you teachers always say there's "no such thing as a stupid question". So, I'll keep asking till I understand.

In YOUR example code, can you tell me how Sub1 could possibly be used from a STRING variable having a value of "Sub1". Assume *somehow* we have a string, string1 = "Sub1".

Using delegates in your example, how can I get at "Public Sub Sub1(ByVal Arg As String)" (in your TestClass) using this value in string1?

I thought THAT was what the woman was asking, Or (most likely) I'm all confused and brain dead as far as delegates go.

By the way, love your show. Listen to all of them as soon as they appear. I have the Archos mp3 player too. Keep up the good work.

Thanks again,
Jim
# November 24, 2003 7:25 PM

Carl Franklin said:

Absolutely no such thing as a stupid question!

I see know why you're confused. I did not show her how to call the function by name with a STRING variable. I showed her how to access the function as a DELEGATE variable.

But, since you asked... and so did she apparently, here is some code that you can add to ANY CLASS to call a member by name with a STRING variable.

Public Class Foo
'-- Add this function to any class to call a method by name
Public Function InvokeMethodByName(ByVal MethodName As String, Optional ByVal Args() As Object = Nothing) As Object
'-- Get a type obj for my instance
Dim mytype As Type = Me.GetType

Try
'-- Get a MethodInfo object for the given methodname
Dim mi As MethodInfo = mytype.GetMethod(MethodName)
'-- Invoke the method and return the result
Return mi.Invoke(Me, Args)
Catch ex As Exception
'-- Error handling just passes exception up. Modify if necessary
Throw ex
End Try
End Function

'-- Sample method 1: Does nothing
Public Sub Sub1()
MsgBox("Sub1")
End Sub

'-- Sample method 2: Returns a value
Public Function Sub2() As Int32
Return 100
End Function

'-- Sample method 3: Accepts an argument
Public Sub Sub3(ByVal Arg As String)
MsgBox("Sub3: " & Arg)
End Sub

End Class

' Now, here is some code you can use to test it:

Dim MyFoo As New Foo

'-- Call Sub1
MyFoo.InvokeMethodByName("Sub1")

'-- Call Sub2 and display return value
Dim retval As Int32 = CInt(MyFoo.InvokeMethodByName("Sub2"))
MsgBox("Sub2 returns: " & retval.ToString)

'-- Call Sub3 passing a string argument
Dim args() As Object = {"Hello!"}
MyFoo.InvokeMethodByName("Sub3", args)

Hope this helps.
# November 24, 2003 10:20 PM
Leave a Comment

(required) 

(required) 

(optional)

(required)