开发者

need to create a summary of a large switch statement in C#

开发者 https://www.devze.com 2022-12-10 15:07 出处:网络
Alright, i dont know how to explain it well.. but i have a switch statement, string mystring = \"hello\";

Alright, i dont know how to explain it well.. but i have a switch statement,

string mystring = "hello";
switch(mystring)
{
case "hello":
break;
case "goodbye":
break;
case "example":
break;
}

of course this is an example, and in the real situation, there will be different things happening for each case. ok, hope you get the point, now, doing this manually is impossible, because of the sheer number of different case's. i need to respectively create a list, of all the cases, so for instance.. for the above switch statement开发者_开发百科, i would need

string[] list = { "hello", "goodbye", "example" };

maybe could be done with a foreach some how i dont know, any help would be greatly appreciated.

also, any working codes provided would be awesome!

edit: people are asking for more detail, so here is how it works. the user of the program, inputs a series of strings. based on the string(s) they entered, it will do a few if's and else if's and throw back the new strings basically. i need to be able to be able to create a list, through the program, of all the options available to use. and i cant just make a list and hard code it in, because im always adding more case's to the statement, and i cant be going back and keeping a list up to date.


FOR VISUAL STUDIO:

if mystring is an enum instead of a string, in visual studio, if you type "switch" [TAB] "mystring" [ENTER] it'll build the long switch for you with all the cases.


It depends on how clever you want to get... You could create a custom attribute that attaches to a method with the string that method should handle. Then, instead of a switch statement, you would just find the attribute with your desired value and execute it.

using System;
using System.Reflection;

namespace ConsoleApplication1 {
    [AttributeUsage(AttributeTargets.Method)]
    internal class ProvidesAttribute : Attribute {
        private String[] _strings;
        public ProvidesAttribute(params String[] strings) {
            _strings = strings;
        }
        public bool Contains(String str) {
            foreach (String test in _strings) {
                if (test.Equals(str)) {
                    return true;
                }
            }
            return false;
        }
    }

    internal class Program {
        [Provides("hello", "goodbye")]
        public void HandleSomeStuff(String str) {
            Console.WriteLine("some stuff: {0}", str);
        }

        [Provides("this")]
        public void HandleMoreStuff(String str) {
            Console.WriteLine("more stuff: {0}", str);
        }

        public void HandleString(String str) {
            // we could loop through each Type in the assembly here instead of just looking at the
            // methods of Program; this would allow us to push our "providers" out to other classes
            MethodInfo[] methods = typeof(Program).GetMethods();
            foreach (MethodInfo method in methods) {
                Attribute attr = Attribute.GetCustomAttribute(method, typeof(ProvidesAttribute));
                ProvidesAttribute prov = attr as ProvidesAttribute;
                if ((prov != null) && (prov.Contains(str))) {
                    method.Invoke(this, new Object[] { str } );
                    break;  // removing this enables multiple "providers"
                }
            }
        }

        internal static void Main(String[] args) {
            Program prog = new Program();
            foreach (String str in args) {
                prog.HandleString(str);
            }
        }
    }
}

Once you have the framework, you wouldn't need to alter the HandleString() code, just add the methods you want to take care of and set the Provides attribute on them. If you wanted to extend the idea a little further, you could create multiple classes to handle a wide variety of strings, then loop through each type in your assembly looking for the Provides attribute.

EDIT this has the added benefit that you can define multiple methods that act on the same string (by removing the break in the loop logic).


I'm note sure what you are trying to do, but you might be able to use a dictionary.

    Dictionary<string, int> lookupTable = new Dictionary<string, int>();

    lookupTable.Add("hello", 1);
    lookupTable.Add("goodbye", 2);
    lookupTable.Add("example", 3);


    int output = lookupTable["hello"];

You wouldn't need to have code to add each individual entry. You could read in the keys and values from a file, loop though them and populate the dictionary.

If you explain more about what you are trying to do, we could give you more specific advice.


By proper refactoring (your hypothetical example) you can make sure that out of your sheer number of cases, there will be a lot of them that can call the same sub routine with their string parameter.

In many of these scenarios, you may not even need a huge switch statement, but just parameterize one sub routine that can handle them.

Without a concrete example of what you want to do in the case statements, it is hard to come up with a concrete answer.


You appear to be trying to extract "command strings" from your code, so that you can automatically update the list of available commands in your user documentation. I think this will not gain you much, as you will still need to manually document what each command does.

That being said, the following powershell command will extract the data you want from test.cs:

type test.cs|select-string 'case "(.*)"'|foreach {$_.Matches[0].Groups[1].Value}


Switch statements evaluate on constants, so the case statements won't work with variables. Perhaps you should consider using a Dictionary<> and branching based on that. But without any more insight into the problem you're solving, there's little point in saying anything more.


Create an abstract class, call it something like StringHandler. Give it 2 abstract methods, 1 to check whether the handler can handle the string, then the other to do the processing. Something like:

  public abstract class StringHandler
  {
    public abstract bool CanProcess(string input);
    public abstract void Process();
  }

  public class HelloStringHandler : StringHandler
  {
    public override bool CanProcess(string input)
    {
      return input.Equals("hello");
    }

    public override void Process()
    {
      Console.WriteLine("HELLO WORLD");
    }
  }

Then in your main class you can do a simple loop with a list of all known handlers, like

  List<StringHandler> handlers = new List<StringHandler>();
  handlers.Add(new HelloStringHandler());
  string myString = "hello";

  foreach (StringHandler handler in handlers)
  {
    if (handler.CanProcess(myString))
    {
      handler.Process();
      break;
    }
  }

All this can be optimised/improved obviously, but I hope you get the picture?


I am very rusty at c#, but this was a fun little exercise. The following code is not very clean, but will do what you asked. You will want to add more checks, use the variables better and add more logic, but this should help you get going in the right direction.

    var newfile = System.IO.File.CreateText("newcode.txt");
    newfile.Write("string[] list = { ");
    using (var file = System.IO.File.OpenText("code.txt"))
    {
        bool bFirst = true;
        while (!file.EndOfStream)
        {
            String line = file.ReadLine();

            if (line.Contains("case ") && line.EndsWith(":"))
            {
                line = line.Replace("case", " ");
                line = line.Replace(":", " ");
                line = line.Trim();
                if (bFirst == false)
                {
                    newfile.Write(", ");
                }
                bFirst = false;
                newfile.Write(line);
            }
        }
    }
    newfile.WriteLine(" };");
    newfile.Close();

Good luck!


Inspired by @Jheddings answer, I came up with this. Maybe it's over the top, but at least I had fun figuring it out:

Main benefits over jheddings solution:

  • Uses extension methods, no utility class instance needed.
  • Reflection lookup of all candidate methods is done only once, right before the first string is evaluated. Afterwards, it is a simple lookup and invoke.
  • Even simpler usage

    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Linq;
    using System.Reflection;
    
    namespace StringSwitcher
    { 
    class Program
    {
        static void Main(string[] args)
        {
            "noAction".Execute(); //No action, since no corresponding method defined
            "Hello".Execute();    //Calls Greet method
            "world".Execute();    //Calls Shout method
            "example".Execute();  //Calls Shout method
            Console.ReadKey();
        }
    
        //Handles only one keyword
        [Keywords("Hello")]
        static public void Greet(string s)
        {
            Console.WriteLine(s + " world!");
        }
    
        //Handles multiple keywords
        [Keywords("world", "example")]
        static public void Shout(string s)
        {
            Console.WriteLine(s + "!!");
        }
    }
    
    internal static class ActionBrokerExtensions
    {
        static Dictionary<string, MethodInfo> actions;
    
        static ActionBrokerExtensions()
        {
            //Initialize lookup mechanism once upon first Execute() call
            actions = new Dictionary<string, MethodInfo>();
            //Find out which class is using this extension
            Type type = new StackTrace(2).GetFrame(0).GetMethod().DeclaringType;
            //Get all methods with proper attribute and signature
            var methods = type.GetMethods().Where(
            method => Attribute.GetCustomAttribute(method, typeof(KeywordsAttribute)) is KeywordsAttribute &&
                      method.GetParameters().Length == 1 &&
                      method.GetParameters()[0].ParameterType.Equals(typeof(string)));
            //Fill the dictionary
            foreach (var m in methods)
            {
                var att = (Attribute.GetCustomAttribute(m, typeof(KeywordsAttribute)) as KeywordsAttribute);
                foreach (string str in att.Keywords)
                {
                    actions.Add(str, m);
                }
            }
        }
    
        public static void Execute(this string input)
        {
            //Invoke method registered with keyword 
            MethodInfo mi;
            if (actions.TryGetValue(input, out mi))
            {
                mi.Invoke(null, new[] { input });
            }
        }
    }
    
    [AttributeUsage(AttributeTargets.Method)]
    internal class KeywordsAttribute : Attribute
    {
        private ICollection<string> keywords;
        public KeywordsAttribute(params String[] strings)
        {
            keywords = new List<string>(strings);
        }
    
        public ICollection<string> Keywords
        {
            get { return keywords; }
        }
    }
    

    }

Apologies for any strange rendering, for some reason the syntax highlighting chokes on the code :-(

0

精彩评论

暂无评论...
验证码 换一张
取 消