Skip to content

Using Method.Invoke to avoid a lengthy Case Statement in C# (Using Reflection)

Updated: at 11:17 PM

So, I asked the following question in at http://forums.asp.net/t/1408631.aspx

image

The problem is, I’ve got 30 methods that all have the same signature, but have different implementations.  Mike Banavige (the lead moderator at the forums) suggested I look at the following article on reflection:  http://www.csharphelp.com/archives/archive200.html.  Turns out, that helped my solve my problem exactly.  Rather than go through a lengthy line by line explanation, I’m going to post some of my before and after code.  It’s pretty clear what is happening, but I thought it would be nice to show a real example.

Before Code:

 private static void AddTemplateCode(string templateNumber, int indent, ICollection<string> newLines,FullMeta fullMetal)
        {
            int iTemplateNumber = Convert.ToInt32(templateNumber);
            var sb = new StringBuilder(indent);
            sb.Append(' ', indent);
            string indentString = sb.ToString();
 
 
             // be nice to get rid of this ugly case statement.  Using it because I want to have a way to separate
             // methods associated with groups of methods.  that is, put Template11-15 in a separate file later.
            switch (iTemplateNumber)
            {
                case 11:
                    {
                        Template11(newLines, indentString,fullMetal);
                        break;
                    }
                case 12:
                    {
                        Template12(newLines, indentString,fullMetal);
                        break;
                    }
                case 13:
                    {
                        Template13(newLines, indentString, fullMetal);
                        break;
                    }
                case 14:
<This goes on and on and on and on>

Then, After, following the sample above:

 private static void AddTemplateCode(string templateNumber, int indent, ICollection<string> newLines,FullMeta fullMetal)
        {
             
            int iTemplateNumber = Convert.ToInt32(templateNumber);
            var sb = new StringBuilder(indent);
            sb.Append(' ', indent);
            string indentString = sb.ToString();
 
 
 
            var processTemplate = new ProcessTemplateDynamic();
            processTemplate.DoIt(iTemplateNumber, newLines, indentString, fullMetal);
 
<Then the class with DoIt in it...>
 
using System;
using System.Collections.Generic;
using System.Reflection;
 
namespace ThreePLogicAccessCodeGen.Code
{
    public class ProcessTemplateDynamic
    {
        internal void DoIt(int iTemplateNumber, ICollection<string> newLines, string indentString, FullMeta fullMetal)
        {
            // Template11(newLines, indentString, fullMetal);
            string templateMethodName = String.Format("Template{0}", iTemplateNumber);
            var userParameters = new object[3];
            userParameters[0] = newLines;
            userParameters[1] = indentString;
            userParameters[2] = fullMetal;
 
            try
            {
                Type thisType = GetType();
                MethodInfo theMethod = thisType.GetMethod(templateMethodName);
 
                // that we have in this class that we don't want called.
                if (theMethod == null || (!CheckMethod(theMethod)))
                {
                    throw new ApplicationException(string.Format("[e] Command <{0}> not supported.", templateMethodName));
                }
                // Invoke the Method!
                theMethod.Invoke(this, userParameters);
            }
            catch (ArgumentNullException e)
            {
                // This exception is from the user entering in a null string on the command line
                throw new ApplicationException("[e] Please enter in a non-null string. (" + e.Message + ")");
            }
            catch (TargetParameterCountException e)
            {
                // This exception is thrown when the method is not passed the right number of parameters
                throw new ApplicationException(string.Format("[e] Command <{0}> requires parameters. ({1})",
                                                             templateMethodName, e.Message));
            }
            catch (Exception e)
            {
                // All other exceptions!
                throw new ApplicationException(string.Format("[e] General Exception:n{0}", e));
            }
        }
 

Well, looking at it, seems maybe the Case statement is better, but the nice thing now is that each time I add a new template, I don’t have to make another case statement.  That combined with it’s easier to make typos when you have a case statement so I’m sticking with my new solution here.

Hope this Helps!