2

I have a method which calls other method based on input parameter using switch statement as shown below:

    switch (reportName)
            {
                case A:
                    return GenerateReportA(reportName, model, language);
                case B:
                    return GenerateReportB(reportName, model, language);
                case C:
                    return GenerateReportC(reportName, model, language);
                case D:
                    return GenerateReportD(reportName, model, language);
                case E:
                    return GenerateReportE(reportName, model, language);

                and so on...                    
            }

I know this switch statement will keep on increasing as more and more reports are added to system. Is there an alternate way to achieve this? ...Delegate? ...Lambda? All I know is all my GenerateReport methods will have same signature.

SKS
  • 220
  • 3
  • 12
  • 2
    You can make a `Dictionary>`. – SLaks Aug 05 '19 at 17:54
  • You can use reflection to find the method by name i.e. `var method = this.GetType().GetMethod($"GenerateReport{reportName}", null, BindingFlags.Instance | BindingFlags.NonPublic)` and then invoke it: `return (Report)method.Invoke(new object[] { reportName, model, language });`. If this is easier to write it does not mean it works faster. – Suiden Aug 05 '19 at 18:18

2 Answers2

8

As SLaks said, you could refactor your switch into a dictionary. I assumed your three input parameters were strings. Adjust as you need it:

// the dictionary value is a function delegate with three string input
// parameters and a string result
var _generateReport = new Dictionary<string, Func<string, string, string, string>>()
    { 
        {"A", GenerateReportA}, 
        {"B", GenerateReportB}, 
        {"C", GenerateReportC}, 
        {"D", GenerateReportD}, 
        {"E", GenerateReportE} 
        ...
    };

Then you can rewrite your switch statement as

if (_generateReport.ContainsKey(reportName)) 
     _generateReport[reportName].Invoke(reportName, model, language); 
div
  • 905
  • 1
  • 7
  • 20
1

This might be overkill for you but you could do something like this. I learned this approach when researching the O part of S.O.L.I.D (design principles). The nice thing about this method is that when a new report is required you just create a class that derives from BaseReportGenerator for it. I've demonstrated the method using assembly scanning to get the relevant classes but another method would be to have them all injected as dependencies.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace AltToSwitch
{
    class Program
    {
        static void Main(string[] args)
        {
            var myReportType = "A";
            var model = "myModel";
            var language = "myLanguage";

            var reportGenerators = new List<BaseReportGenerator>();

            //use assembly scanning to get all report generator implementations
            foreach (Type type in
                Assembly.GetAssembly(typeof(BaseReportGenerator)).GetTypes()
                .Where(myType => myType.IsClass && !myType.IsAbstract && myType.IsSubclassOf(typeof(BaseReportGenerator))))
            {
                reportGenerators.Add((BaseReportGenerator)Activator.CreateInstance(type));
            }

            var reportGeneratorToUse = reportGenerators.SingleOrDefault(x => x.ReportName == myReportType);
            Console.WriteLine(reportGeneratorToUse.GenerateReport(model, language));
        }
    }

    public abstract class BaseReportGenerator
    {
        public abstract string ReportName
        {
            get;
        }
        public abstract string GenerateReport(string model, string language);

        //common report functionality could go here
    }
    public class TheAReportGenerator : BaseReportGenerator
    {
        public override string ReportName => "A";

        public override string GenerateReport(string model, string language)
        {
            return "The A report";
        }
    }
}
Dave Barnett
  • 2,045
  • 2
  • 16
  • 32