2014-09-11 1 views
1

Je suis en train de créer une application de console pour aider à expier certaines choses que je fais régulièrement. J'ai un menu avec 4 options de procédures, qui se traduisent par différentes méthodes dans ma classe.En C#, quel est le meilleur moyen de contrôler le flux basé sur l'analyse d'une chaîne d'entrée?

son genre Fondamentalement de la manière suivante:

Que voulez-vous faire?

1 Cette chose

2 That Thing

3 Quelques trucs

4 Cool Stuff

0 Tous les trucs.

chaîne de commande d'entrée: _

Actuellement Je vérifie une entrée valide avec:

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

et le contrôle puis flux avec:

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(); 
} 

L'objectif est de fournir entrer et exécuter un ou plusieurs processus comme indiqué:

1: thisThing()

13: thisThing() et someStuff();

42: thatThing() et coolStuff();

0: exécuter tous les processus que j'ai définis.

Y a-t-il un moyen de faire quelque chose comme ça avec de meilleures pratiques?

+1

Eh bien, honnêtement, je créerais un gui dans les formes de gain ou WPF et juste mapper les clics des boutons aux processus. En outre, vous ne pouvez pas, avec cette méthode, exécuter une commande deux fois dans le cas où vous devez effectuer thisThing() => someStuff() => thisThing(). –

+0

Oui, j'ai pensé à utiliser une interface graphique avec des boutons, mais je suppose que j'ai décidé de la console pour plus de simplicité. Je suis vraiment nouveau dans ce genre de choses, donc les choses de l'interface graphique m'intimident un peu. Actuellement, ce n'est qu'une petite partie de quelque chose qui pourrait être divisé en un module d'une plus grande chose - à quel point quelque chose comme ça devrait probablement être fait. En ce moment, c'est juste un fichier .exe qui m'aide à configurer un nouveau client dans une base de données, avec plusieurs tâches qui peuvent ou ne doivent pas être faites. Merci pour votre temps! –

Répondre

1

Je créerais un 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)); 

Puis, après validation d'entrée

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

Le dictionnaire contient, comme les clés, les caractères autorisés et, en tant que valeurs, le délégué à une fonction de vide retour et pas d'arguments. Maintenant, il suffit de faire une boucle sur le char en entrée par char et d'appeler la méthode associée.

Gardez présent que cette approche, avec si peu choiches, ne vaut pas mieux qu'un simple, if/else if/else ou switch/case. De cette manière, votre utilisateur peut également taper 24 ou 42 en inversant l'ordre d'exécution des méthodes et cela ne peut pas être autorisé dans votre contexte réel.

+0

Cela semble être une façon assez cool de faire cela. Je suppose que cela finit par être un peu similaire à la façon dont je commençais à analyser la chaîne (une instruction if (string.contains (")) pour chaque fonction) mais j'aime qu'il y ait un endroit solide et évident où les" commandes " le menu est décrit. J'ai besoin d'en savoir un peu plus sur les délégués. Merci de me pointer dans une direction! –

1

Je créerais un dictionnaire des délégués. La clé du dictionnaire serait la valeur d'entrée et le délégué exécuterait le code.

La syntaxe est couverte ici C# Store functions in a Dictionary. De cette façon, vous pouvez parcourir la chaîne d'entrée dans une boucle et effectuer une recherche dans un dictionnaire; si la valeur existe, exécutez le délégué, sinon ignorez.

Ce n'est pas spécifiquement mieux qu'un If if si set endif, même si je le trouverais plus attrayant et extensible.

+0

Merci pour votre réponse PhillipH! Avec l'exemple de Steve, et votre lien, j'ai une meilleure idée sur d'autres façons de gérer cela. Merci! –

0

Je créerais une interface à partir de laquelle toutes les commandes dériveraient et une instruction switch déterminerait quelle action exécuter. Quelque chose le long des lignes de ...

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; 
      } 
    } 
} 

L'avantage ici est que chaque fois qu'une nouvelle commande est ajoutée alors il a juste besoin de mettre en œuvre la méthode DoSomething(), après qu'il est juste la tâche triviale de créer une instance de la classe var someStuff = new SomeStuff(); puis ajouté à la liste des commandes dans le case "0":

plus l'intention de l'OMI est plus claire à lire que la gestion d'un dictionnaire, etc.

+0

Salut Carl, merci pour votre temps. J'aurais dû préciser que toutes les méthodes sont de nature très procédurale et sont actuellement figées. Donc, dans votre exemple, vous les prendriez et transformeriez les différentes procédures en objets, avec une méthode de .DoSomething()? Désolé, je suis vraiment nouveau dans l'architecture de programmation et C#, donc je ne suis pas totalement en train de suivre. –

+0

Oui c'est exactement ça. Dans mon exemple, vous créez une instance de chacune des commandes que vous avez/besoin/voulez et en fonction de ce que l'utilisateur a saisi, vous appelez la méthode __that__ commandes '.DoSomething()'.Dans le cas où vous souhaitez appeler * toutes * les commandes, car elles implémentent toutes la même interface, vous n'avez pas besoin de savoir ou de savoir quels sont les types d'objet (ThisThing, ThatThing, SomeStuff) et vous n'avez qu'une liste de le type d'interface (dans mon exemple c'est 'ISystem') C'est pourquoi vous pouvez juste faire le one-liner' commands.ForEach (system => system.DoSomething()); 'J'espère que cela répond à votre question? –

1

J'ai trouvé une très belle façon de le faire sur la base des expressions rationnelles en un Ayende Rahien project w Ici, il analyse le contenu des fichiers de données Freedb.org

Fondamentalement, les parser ensembles une liste de Tuple<Regex, Action<contextual class>> avec le changement de classe contextuelle en fonction de l'endroit où l'analyseur est à l'époque. L'analyse devient alors essayer de faire correspondre chaque élément (dans ce cas la ligne) avec une regex et d'exécuter l'action correspondante si elle correspond.

Dans votre cas, vous pourriez être en mesure de lier vos commandes à plusieurs entrées, par exemple

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; 
      } 
     } 
    } 
} 

qui vous permet d'exécuter les méthodes que vous voulez avec différents intrants.

+0

Je ne sais pas pourquoi celui-ci n'est pas mieux classé. Est-ce parce que c'est sur-conçu pour le problème spécifique? J'ai été extrêmement chanceux de le rencontrer, car c'est exactement ce dont j'ai besoin! –

+0

Merci @JesseSmith. Il peut être surengagé dans ce cas, mais c'est un moyen très simple et réutilisable d'analyser les fichiers de données. Heureux que cela puisse aider, et n'hésitez pas à étendre son utilisation – samy

Questions connexes