Jak zdajesz się być świadomy, zmniejszanie dwóch liter i porównywanie ich nie jest tym samym, co porównywanie wielkości liter. Jest ku temu wiele powodów. Na przykład standard Unicode umożliwia kodowanie tekstu ze znakami diakrytycznymi na wiele sposobów. Niektóre znaki zawierają zarówno znak podstawowy, jak i znak diakrytyczny w jednym punkcie kodowym. Znaki te mogą być również przedstawiane jako znak podstawowy, po którym następuje łączący znak diakrytyczny. Te dwie reprezentacje są równe we wszystkich celach, a porównania ciągów uwzględniające kulturę w .NET Framework poprawnie zidentyfikują 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 porównaniem porządkowym. Porównanie porządkowe jest dobre w przypadku niektórych rodzajów aplikacji, takich jak analiza pliku ASCII ze sztywno zdefiniowanymi kodami, ale w większości innych zastosowań porównanie ciągów porządkowych jest niewłaściwe.
To, co zrobiłem w przeszłości, aby uzyskać prawidłowe zachowanie, to po prostu makieta własnej instrukcji przełącznika. Można to zrobić na wiele sposobów. Jednym ze sposobów byłoby utworzenie List<T>
par ciągów znaków i delegatów. Listę można przeszukiwać za pomocą odpowiedniego porównania ciągów. Po znalezieniu dopasowania można wywołać skojarzonego delegata.
Inną opcją jest wykonanie oczywistego łańcucha if
instrukcji. Zwykle okazuje się, że nie jest tak źle, jak się wydaje, ponieważ struktura jest bardzo regularna.
Wspaniałą rzeczą w tym jest to, że tak naprawdę nie ma żadnego spadku wydajności w tworzeniu własnych funkcji przełącznika podczas porównywania z ciągami. System nie utworzy tablicy skoków O (1) w taki sposób, w jaki może to zrobić z liczbami całkowitymi, więc i tak będzie porównywał każdy łańcuch pojedynczo.
Jeśli istnieje wiele przypadków do porównania, a wydajność jest problemem, wówczas List<T>
opisaną powyżej opcję można zastąpić posortowanym słownikiem lub tabelą skrótów. Wtedy wydajność może potencjalnie odpowiadać lub przekraczać opcję instrukcji switch.
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 będziesz chciał dodać kilka standardowych parametrów i prawdopodobnie typ zwracany do delegata CustomSwitchDestination. I będziesz chciał tworzyć lepsze nazwy!
Jeśli zachowanie każdego z twoich przypadków nie pozwala na delegowanie wywołania w ten sposób, na przykład jeśli potrzebne są różne parametry, to utkniesz z połączonymi if
instrukcjami. 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";
}
ToUpperInvariant()
lubToLowerInvariant()
? Poza tym nie porównuje dwóch nieznanych ciągów , porównuje jeden nieznany ciąg z jednym znanym. Tak więc, o ile wie, jak zakodować na stałe odpowiednią reprezentację wielkich lub małych liter, blok przełącznika powinien działać dobrze.