2010-10-01 16 views
10

Próbuję znaleźć rozwiązanie tego problemu. To jest mój przykładowy kod:za pomocą kolekcji ciągów w oświadczeniu przełącznika

class Program 
{ 
    private string Command; 

    private static string[] Commands = { "ComandOne", "CommandTwo", "CommandThree", "CommandFour" }; 


    static void Main(string[] args) 
    { 
    Command = args[0]; 
    switch(Command) 
    { 
     case Commands[0]: //do something 
     break; 
     case Commands[1]: //do something else 
     break; 
     case Commands[2]: //do something totally different 
     break; 
     case Commands[3]: //do something boring 
     break; 
     default: //do your default stuff 
     break; 
    } 
    } 

    void DifferentMethod() 
    { 
    foreach(string c in Commands) 
    { 
     //do something funny 
    } 
    } 
} 

Ten kod nie działa, ponieważ wartości ciągu w przełączniku nie są stałe. Chcę napisać łatwy do utrzymania kod.
Lubię używać czegoś w rodzaju tablicy, ponieważ potrzebuję użyć tych samych wartości gdzie indziej w pętli.
Przy wartościach int enum byłoby idealne, ale nie znalazłem małego rozwiązania dla tej samej rzeczy ze stringami.

+1

Większość rozwiązań sugerują wyliczeń, ale nazwy wyliczenie mają specjalne wymagania nazewnictwa. Jeśli powoduje to problemy, możesz powiązać atrybut "DescriptionAttribute" z każdym elementem wyliczającym, aby zawierał przyjazne nazwy (które mogą zawierać spacje lub cokolwiek innego) i może wyszukiwać te nazwy podczas iteracji nad wyliczeniem w ramach metody "DifferentMethod". – Brian

+0

@Brian, dobry punkt, i aby uzyskać ten atrybut, będziesz potrzebował pola: 'FieldInfo enumField = typeof (Commands) .GetField (enumValue.ToString());' –

+0

@Kirk Woll: Następne dwie linijki po tym : 'DescriptionAttribute da = (DescriptionAttribute) Attribute.GetCustomAttribute (enumField, typeof (DescriptionAttribute)); if (da.Description! = null) description = da.Description; ' – Brian

Odpowiedz

19

Konwersja Commands do wyliczenia:

enum Commands { ComandOne, CommandTwo, CommandThree, CommandFour } 

switch powinien wyglądać następująco:

static void Main(string[] args) 
{ 
    Command = (Commands)Enum.Parse(typeof(Commands), args[0]); 
    switch(Command) 
    { 
     case Commands.CommandOne: 
      //do something 
      break; 
     case Commands.CommandTwo: 
      //do something else 
      break; 
     ... 
     default: 
      // default stuff 
    } 
} 

I ostatnia metoda powinna wyglądać następująco:

void DifferentMethod() 
{ 
    foreach(var c in Enum.GetValues(typeof(Commands))) 
    { 
     string s = c.ToString(); 
     //do something funny 
    } 
} 
+1

Polecam, aby wstępnie obliczyć 'Enum.GetValues ​​(typeof (Commands)) i przechowywać gdzieś. to nie jest tania operacja. – Andrey

+1

@Andrey: Nie zgadzam się. DifferentMethod() zajmuje tylko około tuzin mikrosekund każde wywołanie na moim komputerze. Wstępna kalkulacja wyliczenia jest warta zrobienia, jeśli profilowanie uzna to za wąskie gardło, które wymaga naprawy, a ja byłbym podejrzany, gdyby właściwym rozwiązaniem było posiadanie statycznej, wstępnie obliczonej kopii wyliczenia. Wolę zapisywać takie optymalizacje, dopóki nie będzie to konieczne (np. Profilowałeś i to było wąskie gardło); sprawia, że ​​kod jest nieco bardziej skomplikowany, ponieważ polecenia są przechowywane zarówno jako wyliczenie, jak i zbiór łańcuchów. – Brian

+0

Czy nie jest Enum.Parse() wewnętrznie robi przełącznik na ciąg? Pod względem wydajności, wywołanie tej samej metody byłoby równie kosztowne jak oryginalna metoda. – manixrock

2

Tak jak powiedziałeś, tylko stałe wyrażenia są dozwolone w przełączniku. Zwykle robisz to, definiując enum i używasz tego w swoim przełączniku.

class Program 
{ 
    private enum Command 
    { 
    CommandOne = 1, 
    CommandTwo = 2, 
    CommandThree = 3 
    } 

    static void Main(string[] args) 
    { 
    var command = Enum.Parse(typeof(Commands), args[0]); 
    switch(command) 
    { 
     case Command.CommandOne: //do something 
     break; 
     case Command.CommandTwo: //do something else 
     break; 
     case Command.CommandThree: //do something totally different 
     break; 
     default: //do your default stuff 
     break; 
    } 
    } 
} 

Zastosowanie Enum.GetValues wyliczyć z wartości enum w DifferentMethod.

+0

+1 Enums to z pewnością sposób na kolekcjonowanie podobnie ustawionych wartości. – BoltClock

+0

'DifferentMethod' nie działa już po zmianie ... – Heinzi

+0

@Heinzi: Tak jak Kirk wskazuje, właśnie do tego służy' Enum.GetValues'. – Brian

5

Zaledwie wczoraj stworzyłem dla niego rozwiązanie. W twoim przypadku enum s są lepsze, ale tutaj jest moje rozwiązanie dla ogólnej sytuacji niestabilności.

zwyczaje:

static string DigitToStr(int i) 
    { 
     return i 
      .Case(1, "one") 
      .Case(2, "two") 
      .Case(3, "three") 
      .Case(4, "four") 
      .Case(5, "five") 
      .Case(6, "six") 
      .Case(7, "seven") 
      .Case(8, "eight") 
      .Case(9, "nine") 
      .Default(""); 
    } 

     int a = 1, b = 2, c = 3; 
     int d = (4 * a * c - b * 2); 
     string res = true 
      .Case(d < 0, "No roots") 
      .Case(d == 0, "One root") 
      .Case(d > 0, "Two roots") 
      .Default(_ => { throw new Exception("Impossible!"); }); 

     string res2 = d 
      .Case(x => x < 0, "No roots") 
      .Case(x => x == 0, "One root") 
      .Case(x => x > 0, "Two roots") 
      .Default(_ => { throw new Exception("Impossible!"); }); 

     string ranges = 11 
      .Case(1, "one") 
      .Case(2, "two") 
      .Case(3, "three") 
      .Case(x => x >= 4 && x < 10, "small") 
      .Case(10, "ten") 
      .Default("big"); 

definicja:

class Res<O, R> 
{ 
    public O value; 
    public bool succ; 
    public R result; 

    public Res() 
    { 

    } 

    static public implicit operator R(Res<O, R> v) 
    { 
     if (!v.succ) 
      throw new ArgumentException("No case condition is true and there is no default block"); 
     return v.result; 
    } 
} 

static class Op 
{ 
    static public Res<O, R> Case<O, V, R>(this Res<O, R> o, V v, R r) 
    { 
     if (!o.succ && Equals(o.value, v)) 
     { 
      o.result = r; 
      o.succ = true; 
     } 
     return o; 
    } 

    static public Res<O, R> Case<O, V, R>(this O o, V v, R r) 
    { 
     return new Res<O, R>() 
     { 
      value = o, 
      result = r, 
      succ = Equals(o, v), 
     }; 
    } 

    static public Res<O, R> Case<O, R>(this Res<O, R> o, Predicate<O> cond, R r) 
    { 
     if (!o.succ && cond(o.value)) 
     { 
      o.result = r; 
      o.succ = true; 
     } 
     return o; 
    } 

    static public Res<O, R> Case<O, R>(this O o, Predicate<O> cond, R r) 
    { 
     return new Res<O, R>() 
     { 
      value = o, 
      result = r, 
      succ = cond(o), 
     }; 
    } 

    private static bool Equals<O, V>(O o, V v) 
    { 
     return o == null ? v == null : o.Equals(v); 
    } 

    static public R Default<O, R>(this Res<O, R> o, R r) 
    { 
     return o.succ 
      ? o.result 
      : r; 
    } 

    static public R Default<O, R>(this Res<O, R> o, Func<O, R> def) 
    { 
     return o.succ ? o.result : def(o.value); 
    } 
} 
2

Zdefiniuj Dictionary<string, enum> i przed wejściem przełącznik map polecenie wejścia do odpowiedniej wartości. Jeśli dopasowanie nie zostanie znalezione, nastąpi przetwarzanie domyślne.

8

łatwo naprawić w twojej specyficzny przykład:

switch(Array.IndexOf(Commands, Command)) 
{ 
    case 0: ... 
    case 1: ... 

    default: //unknown command. Technically, this is case -1 
} 

Inne alternatywy:

  1. Inline sznurki.

    przełącznik (Command) { przypadek "CommandOne": ... przypadek "CommandTwo": ... }

  2. Zastosowanie wyliczenie zamiast, jak mówi KirkWoll. To prawdopodobnie najczystsze rozwiązanie.

  3. W bardziej złożonych sytuacjach użycie wyszukiwania, takiego jak Dictionary<string, Action> lub Dictionary<string, Func<Foo>>, może zapewnić lepszą wyrazistość.

  4. Jeśli sprawy są złożone, można utworzyć interfejs ICommand. Będzie to wymagało odwzorowania ciągu poleceń na właściwą implementację betonu, dla której użyjesz prostych konstrukcji (switch/dictionaries) lub fantazyjnej refleksji (znajdź implementacje ICommand z tą nazwą lub z pewną dekoracją atrybutów).

+3

+1. OP jest chyba lepiej z rozwiązaniem Kirka, ale jest bliżej pokazania, jak zrobić dokładnie to, o co prosi. – Brian

1

Wielkie odpowiedzi tu i prawdopodobnie odpowiedzieć na to pytanie lepiej niż to, co mam zamiar wspomnieć ...

zależności od tego, jak skomplikowana logika opiera się, można rozważyć użycie wzorca strategii tak:

Refactoring a Switch statement

lub

The Strategy Template Pattern

Ponownie, najprawdopodobniej bardziej skomplikowany niż rozwiązanie zapytał tylko rzucanie go tam ...

5

Można wyeliminować oświadczenie switch całkowicie tworząc IYourCommand przedmiotów i umieszczania ich w Dictionary<string, IYourCommand>.

class Program 
{ 
    private Dictionary<string, IYourCommand> Command = new Dictionary<string, IYourCommand> 
    { 
     { "CommandOne", new CommandOne() }, 
     { "CommandTwo", new CommandTwo() }, 
     { "CommandThree", new CommandThree() }, 
     { "CommandFour", new CommandFour() }, 
    }; 

    public static void Main(string[] args) 
    { 
    if (Command.ContainsKey(args[0])) 
    { 
     Command[args[0]].DoSomething(); 
    } 
    } 
} 

public interface IYourCommand 
{ 
    void DoSomething(); 
} 
+0

+1, Nie wiedziałem wcześniej o ICommand. Dzięki! – FrustratedWithFormsDesigner

+0

@FrustratedWithFormsDesigner: Nie, nie. 'ICommand' w tym kontekście jest mitycznym interfejsem. Będziesz musiał stworzyć ten interfejs i nadać mu dowolną nazwę. –

+0

@FrustratedWithFormsDesigner: Edytowałem swoją odpowiedź, aby było bardziej zrozumiałe. –

3

ja generalnie lubię sznurki do tego rodzaju rzeczy - to jest zbyt łatwo dostać się do kłopotów z pisowni, różnych obudowach i tym podobne - ale przypuszczalnie dlatego chcesz użyć zmiennej zamiast napisu dosłownym. Jeśli rozwiązania enum nie są odpowiednie, użycie const powinno osiągnąć twój cel.

EDIT: 28 października 2013, aby naprawić Błędne przyporządkowanie

class Program 
{ 
    private string Command; 

    const string command1 = "CommandOne"; 
    const string command2 = "CommandTwo"; 
    const string command3 = "CommandThree"; 
    const string command4 = "CommandFour"; 

    private static string[] Commands = { command1, command2, command3, command4 }; 

    static void Main(string[] args) 
    { 
     string Command = args[0]; 
     switch (Command) 
     { 
      case command1: //do something 
       break; 
      case command2: //do something else 
       break; 
      case command3: //do something totally different 
       break; 
      case command4: //do something boring 
       break; 
      default: //do your default stuff 
       break; 
     } 
    } 

    void DifferentMethod() 
    { 
     foreach (string c in Commands) 
     { 
      //do something funny 
     } 
    } 
} 
Powiązane problemy