Jak wydaje się mieć świadomość, lowercasing dwa ciągi i porównywanie ich nie jest takie samo jak robi ignorowanych sprawę porównanie. Istnieje wiele powodów takiego stanu rzeczy. Na przykład standard Unicode umożliwia kodowanie znaków diakrytycznych na wiele sposobów. Niektóre znaki zawierają zarówno znak bazowy, jak i znak diakrytyczny w jednym punkcie kodowym. Znaki te można również przedstawić jako znak bazowy, a następnie łączący znak diakrytyczny. Te dwie reprezentacje są równe dla wszystkich celów, a porównania łańcuchów z właściwymi dla kultury w .NET Framework poprawnie identyfikują je jako równe, z CurrentCulture lub InvariantCulture (z lub bez IgnoreCase). Z drugiej strony porównanie porządkowe błędnie uzna je za nierówne.
Niestety, switch
nie robi nic poza porządkowym porównaniem. Porównanie porządkowe jest dobre dla niektórych rodzajów aplikacji, takich jak przetwarzanie pliku ASCII ze sztywno zdefiniowanymi kodami, ale porządkowe porównywanie ciągów jest nieprawidłowe dla większości innych zastosowań.
To, co zrobiłem w przeszłości, aby uzyskać prawidłowe zachowanie, to po prostu wyśmiewać moją własną instrukcję przełączania. Jest na to wiele sposobów. Jednym ze sposobów byłoby utworzenie List<T>
par ciągów znaków i delegatów. Lista może być przeszukiwana przy użyciu odpowiedniego porównania łańcuchów. Po znalezieniu powiązania może zostać wywołany powiązany delegat.
Inną opcją jest wykonanie oczywistego łańcucha oświadczeń if
. Zwykle okazuje się, że nie jest tak źle, jak się wydaje, ponieważ struktura jest bardzo regularna.
Wspaniałą rzeczą jest to, że w porównaniu z ciągami nie ma żadnej kary za wydajność w kpieniu z własnej funkcji przełącznika. System nie wykona tabeli przeskoku O (1) tak, jak to możliwe z liczbami całkowitymi, więc i tak będzie porównywać każdy ciąg na raz.
Jeśli istnieje wiele przypadków do porównania, a wydajność jest problemem, opisana powyżej opcja List<T>
może zostać zastąpiona posortowaną tablicą słownika lub tabeli mieszania. Wtedy wydajność może potencjalnie odpowiadać opcji instrukcji switch lub ją przekraczać.
Oto przykład listy delegatów:
delegate void CustomSwitchDestination();
List<KeyValuePair<string, CustomSwitchDestination>> customSwitchList;
CustomSwitchDestination defaultSwitchDestination = new CustomSwitchDestination(NoMatchFound);
void CustomSwitch(string value)
{
foreach (var switchOption in customSwitchList)
if (switchOption.Key.Equals(value, StringComparison.InvariantCultureIgnoreCase))
{
switchOption.Value.Invoke();
return;
}
defaultSwitchDestination.Invoke();
}
Oczywiście, prawdopodobnie zechcesz dodać kilka standardowych parametrów i ewentualnie typ zwracany do delegata CustomSwitchDestination. I będziesz chciał tworzyć lepsze nazwy!
Jeśli zachowanie każdego z twoich przypadków nie jest możliwe do delegowania wywołania w ten sposób, na przykład jeśli konieczne są różne parametry, utkniesz w przyklejonych if
statkach. Zrobiłem to również kilka razy.
if (s.Equals("house", StringComparison.InvariantCultureIgnoreCase))
{
s = "window";
}
else if (s.Equals("business", StringComparison.InvariantCultureIgnoreCase))
{
s = "really big window";
}
else if (s.Equals("school", StringComparison.InvariantCultureIgnoreCase))
{
s = "broken window";
}
O ile się nie mylę, te dwie rzeczy różnią się tylko w niektórych kulturach (np. po turecku) iw takim przypadku nie mógł użyć "ToUpperInvariant()" lub "ToLowerInvariant()"? Poza tym nie porównuje dwóch nieznanych ciągów, porównuje jeden nieznany ciąg do jednego znanego ciągu. Tak więc, o ile wie, jak kodować odpowiednią reprezentację górnej lub małej litery, blok przełącznika powinien działać poprawnie. –
@Seth Petry-Johnson - Być może ta optymalizacja mogłaby być wykonana, ale powód, dla którego opcje porównywania ciągów są wypalane w ramach, jest tak, że nie wszyscy musimy stać się ekspertami lingwistycznymi, aby pisać poprawne, rozszerzalne oprogramowanie. –
OK. Podam przykład, w którym jest on odpowiedni. Przypuśćmy, że zamiast "domu" mamy (angielski!) Słowo "kawiarnia". Ta wartość może być reprezentowana równie dobrze (i równie prawdopodobna) przez "caf \ u00E9" lub "cafe \ u0301". Równość porządkowa (jak w instrukcji switch) z 'ToLower()' lub 'ToLowerInvariant()' zwróci false. 'Equals' z' StringComparison.InvariantCultureIgnoreCase' zwróci wartość true. Ponieważ obie sekwencje wyglądają identycznie po wyświetleniu, wersja 'ToLower()' jest nieprzyjemnym błędem do wyśledzenia. Dlatego zawsze najlepiej jest robić odpowiednie porównania łańcuchów, nawet jeśli nie jesteś Turkiem. –