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

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!

About Peter Kellner

Peter is a software professional specializing in mobile and web technologies. He has also been a Microsoft MVP since 2007. In addition, he's a multi-course video author at Pluralsight. To read more about Peter Kellner and his experience click here. For information about how Peter Kellner might be able to help you with your project click here.

Follow me:


Comments

  1. When invokind a void to the driver class, do i use the object or the class name?

Follow

Get every new post delivered to your Inbox

Join other followers: