在C#中,基于解析输入字符串来控制流的最佳方法是什么

时间:2014-09-11 14:49:03

标签: c# console string-parsing flow-control

我正在构建一个控制台应用程序,以帮助解决我经常做的一些事情。我有一个包含4个程序选项的菜单,可以转换为我班级的不同方法。

基本上它有点像这样:

  

你想做什么?

     

1这件事

     

2那件事

     

3一些东西

     

4 Cool Stuff

     

0所有的东西。

     

输入命令字符串:_

目前我正在检查有效输入:

while (command.IndexOfAny("12340".ToCharArray()) == -1)
{
  //Display the menu and accept input
}

然后用:

控制流程
if (command.IndexOf("1") > 0 )
{
  thisThing();
}

if (command.IndexOf("2") > 0 )
{
  thatThing();
}

if (command.IndexOf("3") > 0 )
{
  someStuff();
}

if (command.IndexOf("4") > 0 )
{
  coolStuff();
}

if (command.IndexOf("0") > 0 )
{
  thisThing();
  thatThing();
  someStuff();
  coolStuff();
}

目标是提供输入并按指示运行一个或多个流程:

1:thisThing()

13:thisThing()和someStuff();

42:thatThing()和coolStuff();

0:运行我定义的所有进程。

有没有办法用更好的做法做这样的事情?

4 个答案:

答案 0 :(得分:1)

我会创建一个委托词典。字典键将是输入值,委托将执行代码。

这里涵盖了语法C# Store functions in a Dictionary

这样你可以在循环中逐步输入字符串并进行字典查找;如果值存在则执行委托,否则跳过。

虽然我发现它更具吸引力和可扩展性,但它并不比if else if endif更好。

答案 1 :(得分:1)

我会创建一个Dictionary<char, DoSomething>

public delegate void DoSomething();

Dictionary<char, DoSomething> commands = new Dictionary<char, DoThing>();
commands.Add('0', new DoSomething(DoAll));
commands.Add('1', new DoSomething(ThisThing));
commands.Add('2', new DoSomething(ThatThing));
commands.Add('3', new DoSomething(SomeStuff));
commands.Add('4', new DoSomething(CoolStuff));

然后,在输入验证后

foreach(char c in command.ToCharArray()) 
{  
   // Better check if the input is valid
   if(commands.ContainsKey(c))
       commands[c].Invoke();
}

字典包含允许的字符作为键,并且作为值包含具有void返回且没有参数的函数的委托。现在只需要通过char循环输入char并调用关联的方法。

请注意,这种方法很少,只需要一个简单的 if / else if / else switch / case 。同样通过这种方式,您的用户可以键入2442来反转方法的执行顺序,这在您的真实环境中是不允许的。

答案 2 :(得分:1)

基于Ayende Rahien project中的正则表达式,我找到了一种非常好的方法来解析Freedb.org数据文件的内容

基本上parser设置了Tuple<Regex, Action<contextual class>>的列表,其中上下文类会根据解析器当时的位置而更改。解析然后尝试将每个元素(在本例中为行)与正则表达式匹配,并在匹配时执行相应的操作。

在您的情况下,您可以将命令链接到多个输入,例如

public class Parser
{
    readonly List<Tuple<Regex, Action>> actions = new List<Tuple<Regex, Action>>();
    public Parser()
    {
        Add(@"^0$", () => { DoSomething(); });
        Add(@"^DoSomething$", () => { DoSomething(); });

        Add(@"^1$", () => { DoSomethingElse() });
        Add(@"^DoSomethingElse$", () => { DoSomethingElse() });

        // etc
    }
    private void Add(string regex, Action action)
    {
        var key = new Regex(regex, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace | RegexOptions.Multiline);
        actions.Add(Tuple.Create(key, action));
    }

    public void Parse(string text)
    {
        foreach (var action in actions)
        {
            var collection = action.Item1.Matches(text);
            try
            {
                action.Item2();
            }
            catch (Exception e)
            {
                // log?
                throw;
            }
        }
    }
}

允许您使用不同的输入运行所需的方法。

答案 3 :(得分:0)

我创建了一个接口,所有命令都是从这个接口派生出来的,并且有一个switch语句决定要执行哪个动作。 有些东西......

public abstract class ISystem
{
    public abstract void DoSomething();
}

public class ThisThing : ISystem
{
    public override void DoSomething()
    {
        Console.WriteLine("Do This Thing");
    }
}

public class ThatThing : ISystem
{
    public override void DoSomething()
    {
        Console.WriteLine("Do That Thing");
    }
}

static void Main(string[] args)
{
    var input = "-1";
    while(/*input is invalid*/)
    {
        // get input from user
    }

    var thisThing = new ThisThing();
    var thatThing = new ThatThing();

    switch(input)
    {
        case "1":
           {
               thisThing.DoSomething();
               break;
           }
        case "2":
           {
               thatThing.DoSomething();
               break;
           }
        case "0":
           {
               var commands = new List<ISystem>();
               commands.Add(thisThing);
               commands.Add(thatThing);

               // Execute all commands
               commands.ForEach(system => system.DoSomething());
               break;
           }
    }
}

这里的优点是每次添加一个新命令时,它只需要实现DoSomething()方法,之后它只是创建类{{1}的实例的简单任务然后添加到var someStuff = new SomeStuff();

中的命令列表中

加上IMO的意图比管理词典等更清楚。