4

I have a struct-array that contains details of different reports that can be run. Each report calls a different method and currently the program has to manually check the selected report value to specifically call the appropriate method.

I would like to store the method name in the struct-array and then have program invoke that method when there is match. Is this possible?

Currently:

if (this.cboSelectReport.Text == "Daily_Unload")
{
   reportDailyUnload();
 }

Ideally:

if(this.cboSelectReport.Text == MyArray[i].Name)
{
   something(MyArray[i].MethodName);
}

UPDATE

I tired a number of the suggestions below and none of them worked. They didn't work probably due to how I have my program structured.

John M
  • 14,338
  • 29
  • 91
  • 143

7 Answers7

6

You can do it using reflection, but IMO it is too fragile: it introduces an invisible dependency on the name of the method that you call.

// Assuming that the method is static, you can access it like this:
var namedReportMethod = "MyReport1";
var reportMethod = typeof(ReporterClass).GetMethod(namedReportMethod);
var res = reportMethod.Invoke(null, new object[] {reportArg1, reportArg2});

A better approach would be to define a delegate based on your method, and store it in the struct/class instead of the method name.

delegate void ReportDelegate(int param1, string param2);

class Runner {
    public static void RunReport(ReportDelegate rd) {
        rd(1, "hello");
    }
}

class Test {
    static void TestReport(int a, string b) {
        // ....
    }
    public static void Main(string[] args) {
        Runner.RunReport(TestReport);
    }
}

Instead of defining your own delegate types, you can use pre-defined ones based on Action<T1,T2,...> or Func<T1,T2,R>, depending on your need to return values from the reports.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
4

Rather than storing the method name, you could store a delegate:

struct ReportInfo
{
    public string Name { get; set; }
    public Action Method { get; set; }
}

//...

MyArray[0] = new ReportInfo { Name = "Daily_Unload", Action = this.reportDailyUnload };

//...

if(this.cboSelectReport.Text == MyArray[i].Name)
{
    MyArray[i].Method.Invoke();
}

Most people seem to prefer the alternative syntax where you can invoke a delegate as if it were a method, using a list of arguments in parentheses. I tend to avoid this, because it can be ambiguous whether the thing being invoked is a method or a delegate:

MyArray[i].Method();

In this case, we're invoking the delegate that is referred to by the Method property, but this code could also represent a call to a method called "Method". Confusing.

phoog
  • 42,068
  • 6
  • 79
  • 117
2

If all the methods share the same signature, one way would be to cache a delegate:

// initialize, maybe in a constructor
Dictionary<string, Action> nameDelegateMapping = new Dictionary<string, Action>();
// setup the delegates
nameDelegateMapping.Add("Daily_Unload", reportDailyUnload);
// ... add more methods here.

// later
string methodName = this.cboSelectReport.Text;
Action action;
if (nameDelegateMapping.TryGetValue(methodName, out action))
{
  action();
}
else
{
  // tell user the method does not exist.
}
Mike Zboray
  • 39,828
  • 3
  • 90
  • 122
2

As for me, supporting simple switch is much easier than dealing with reflection, array of method names or delegates, and invoking that stuff:

switch (reportType)
{
    case "Daily_Unload":
        ReportDailyUnload();
        break;
    // ...
}
Sergey Berezovskiy
  • 232,247
  • 41
  • 429
  • 459
1

Yes, what you are talking about is reflection. Here is an article on how to invoke a method. There is a lot you can find on reflection using google.

Justin Pihony
  • 66,056
  • 18
  • 147
  • 180
1

Add a delegate property to your struct (e.g. of type Action) then just invoke this delegate when you need it. Just set this property to the method you want to call when instantiating the struct instances.

Dave S
  • 775
  • 5
  • 7
0

Using a delegate and dictionary<string, delegate>

void Main()
{
    var reports = new Dictionary<string, Report>
    {
        {"Daily_Unload", ReportDailyUnLoad}
    };

    var report = "Daily_Unload";
    reports[report]();
}

delegate string Report();

string ReportDailyUnLoad()
{
    return "daily unload report";
}
skyfoot
  • 20,629
  • 8
  • 49
  • 71