C#: What's the right way to 'dynamically' call functions

So this mostly is an exercise in making more work for myself than is necessary, but.

I have a TreeView of checkboxes.
The idea is to loop through the TreeView, and for each checked checkbox, execute the function corresponding to it.

I realize I could do this with a big long Switch statement. But in trying to simplify the code, i’m trying to figure out if theres a good way to dynamically call a function; it feels stupid to create a switch statement that reads:

switch(e.Node.Name) 
{
case x:
   x();
break;
case y:
   y();
break;
....

(Note that because it’s looping through a defined object, the idea of “what happens if the method doesnt exist” is moot; I control the names of the checkboxes.)

Sorry, but what is your question?

If IRemeber right (luckily it’s many years ago I need to use c# last time) You can call methods by name if you use getMethod() with Invoke()

I think that is too vague for me to be able to help. Do you still need help? My guess is that you can use a delegate. You do not however say if the function corresponding to it is an existing member of the TreeView. Does the TreeView documentation say the function is an event?

Since you use the term function and C# programmers tend to use the term method I get the impression you are more familiar with C and/or C++; delegates in .Net are much like function pointers in C.

Invoke() is for calling a method in another task; does that sound correct?

So I have been able to get it to work with Invoke; it still feels a bit… convoluted? Not the right word. But it feels like there should be a better way to do it, even if there isnt actually one.

This is where the code stands at the moment, for those curious:

private async Task<int> ExecuteNode(TreeNode node)
        {
            MethodInfo? method = this.GetType().GetMethod(node.Name);
            if (method != null)
            {
                stepcolor = Color.Green;
                node.ForeColor = Color.Blue;
                AppendText("Invoking " + node.Name);
                currentMethod = node.Name;
                Task? result = (Task?) method.Invoke(this, new object[] { node });
                if (result != null) { await result; }
                node.ForeColor = stepcolor;
                currentMethod = "Main";
            }
            if (!continuesteps) { AppendText("FATAL ERROR: Abort signal received.", Color.Red); node.ForeColor = Color.Red; return -1; }
            foreach (TreeNode child in node.Nodes)
            {
                if (!child.Checked) { continue; }
                await ExecuteNode(child);
                if (!continuesteps) { return -1; }
            }
            return 0;
        }

So a few notes:

  1. Several of the invocations are async due to the nature of their interactions (reaching out to github and web API’s), so i’ve had to cast all of them as async returning Tasks. In order to await the functions (and thus enforce some synch to async steps), the executing function must also be async.
  2. The currentMethod string is a cludge, because of the way C# handles async functions (it chunks them down into processor code at runtime), meaning that getting the method name is (as far as I can find) impossible from inside the call, as its stack is independent and the compiled function doesnt retain its name.
  3. AppendText() is a logger, and can be ignored.
  4. The nullable nature of the invocation is due to Visual Studio moaning at me nonstop about nullable things.

Okay, you are ignoring me. I won’t waste my time.

In my previous reply I was thinking of InvokeRequired. Invoke is different. I don’t understand why the object must use GetMethod to get a method of itself.

Your argument is for TreeNode you probably need to cast that to the class derived from TreeNode.

I dont see how I did that. You said it was too vague, I gave the code. Cant get more specific than that.

The method here is a method of a Form, because it accesses methods of the Form, which need to access elements of the Form.

I have no idea why GetType.GetMethod is required, other than that Invoke must be called from a MethodInfo (or at least, something derived from the MethodBase class) object.

The TreeNode is a default TreeNode that is created as part of a TreeView control’s Nodes property.

This is the first mention of a Form. Is this a Windows Forms application?

Is ExecuteNode an event handler for the form? If it is then what event? What method is it that you are trying to call from it? Is it in the same class as ExecuteNode?

I tried creating a TreeView in a WPF application. It worked except I was unfamiliar with the ObservableCollection class and had problems getting EntityFramework to work with ObservableCollection for a TreeView and got frustrated.

Ah okay, I see where you’re saying its vague now. Apologies. Not entirely sure it’s relevant, but I will elaborate for clarity.

This is a WinForms app. All my functions/methods/whatever are under the MainForm class (inherit from Form; yes, yes, whine at me about putting functional code in the form class etc etc. It’s there, and its staying there.)

ExecuteNode is called by goButton_Click (which is what it sounds like - a button control click handler) that is currently running the same foreach loop across the top level nodes in the tree (foreach (TreeNode node in treeView1.Nodes)…)

I am less likely to tell someone to do it my way than others. For me, the important thing is for others to be aware of and understand the alternatives. And I understand priorities; usually it is better to get the current problem solved. For this I do not see the problem.

Yes, it helps to know that ExecuteNode is a button click handler. And now I see you are calling it recursively. I still do not understand what method.Invoke is invoking.

So i’ll give an example.

treeView1.Nodes[0] is a TreeNode, whose Name is “getCVR”, and whose Checked property is true.
I have a method under MainForm:
public async Task getCVR(TreeNode node) {

so the loop enters treeView1’s Nodes collection, checks that Checked is true (the user has indicated to run this step), and then calls ExecuteNode (because it was checked), passing that Node.
ExecuteNode, having been given a node to execute, reads the Node’s name, and invokes the getCVR method of the MainForm. It awaits that method so that it can synchronize its steps, then walks depth-first into the tree from that Node.

I did work with this some but it has been a while since I have worked with a TreeView and need to refresh my understanding. Then did not have a chance to get back to it.

In your sample code there is extra code not relevant to the problem. If you are able to create a sample with just the minimum code to recreate the problem then that would help.