Wie Sie anscheinend wissen, ist das Verringern und Vergleichen von zwei Zeichenfolgen nicht dasselbe wie das Vergleichen von Groß- und Kleinschreibung. Dafür gibt es viele Gründe. Der Unicode-Standard ermöglicht beispielsweise die mehrfache Codierung von Text mit diakritischen Zeichen. Einige Zeichen enthalten sowohl das Basiszeichen als auch das diakritische Zeichen in einem einzelnen Codepunkt. Diese Zeichen können auch als Basiszeichen dargestellt werden, gefolgt von einem kombinierten diakritischen Zeichen. Diese beiden Darstellungen sind für alle Zwecke gleich, und die kulturbewussten Zeichenfolgenvergleiche in .NET Framework identifizieren sie korrekt als gleich, entweder mit der CurrentCulture oder der InvariantCulture (mit oder ohne IgnoreCase). Ein Ordnungsvergleich hingegen betrachtet sie fälschlicherweise als ungleich.
Tut leider switch
nichts anderes als einen ordinalen Vergleich. Ein Ordnungsvergleich ist für bestimmte Arten von Anwendungen in Ordnung, z. B. das Parsen einer ASCII-Datei mit fest definierten Codes, aber der Ordnungszeichenfolgenvergleich ist für die meisten anderen Anwendungen falsch.
Was ich in der Vergangenheit getan habe, um das richtige Verhalten zu erzielen, ist nur meine eigene switch-Anweisung zu verspotten. Es gibt viele Möglichkeiten, dies zu tun. Eine Möglichkeit wäre, ein List<T>
Paar von Fallzeichenfolgen und Delegaten zu erstellen . Die Liste kann mit dem richtigen Zeichenfolgenvergleich durchsucht werden. Wenn die Übereinstimmung gefunden wird, kann der zugeordnete Delegat aufgerufen werden.
Eine andere Möglichkeit besteht darin, die offensichtliche Kette von if
Aussagen zu machen. Dies stellt sich normalerweise als nicht so schlecht heraus, wie es sich anhört, da die Struktur sehr regelmäßig ist.
Das Tolle daran ist, dass es keine wirklichen Leistungseinbußen gibt, wenn Sie Ihre eigene Switch-Funktionalität beim Vergleich mit Strings nachahmen. Das System wird eine O (1) -Sprungtabelle nicht so erstellen, wie es mit ganzen Zahlen möglich ist, daher wird es sowieso jede Zeichenfolge einzeln vergleichen.
Wenn viele Fälle verglichen werden müssen und die Leistung ein Problem darstellt, kann die List<T>
oben beschriebene Option durch ein sortiertes Wörterbuch oder eine Hash-Tabelle ersetzt werden. Dann kann die Leistung möglicherweise mit der Option switch-Anweisung übereinstimmen oder diese überschreiten.
Hier ist ein Beispiel für die Liste der Delegierten:
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();
}
Natürlich möchten Sie dem CustomSwitchDestination-Delegaten wahrscheinlich einige Standardparameter und möglicherweise einen Rückgabetyp hinzufügen. Und du wirst bessere Namen machen wollen!
Wenn das Verhalten jedes Ihrer Fälle nicht dazu geeignet ist, den Aufruf auf diese Weise zu delegieren, z. B. wenn unterschiedliche Parameter erforderlich sind, bleiben Sie bei verketteten if
Anweisungen hängen. Ich habe das auch ein paar Mal gemacht.
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()
oderToLowerInvariant()
? Außerdem vergleicht er nicht zwei unbekannte Zeichenfolgen , sondern eine unbekannte Zeichenfolge mit einer bekannten Zeichenfolge. Solange er weiß, wie man die geeignete Groß- oder Kleinschreibung fest codiert, sollte der Schaltblock einwandfrei funktionieren.ToLower()
oderToLowerInvariant()
wird false zurückgeben.Equals
mitStringComparison.InvariantCultureIgnoreCase
wird true zurückgeben. Da beide Sequenzen bei der Anzeige identisch aussehen, ist dieToLower()
Version ein böser Fehler. Aus diesem Grund ist es immer am besten, korrekte Zeichenfolgenvergleiche durchzuführen, auch wenn Sie kein Türke sind.Ein einfacherer Ansatz besteht darin, die Zeichenfolge nur in Kleinbuchstaben zu schreiben, bevor sie in die switch-Anweisung aufgenommen wird, und die Groß- und Kleinschreibung zu verringern.
Tatsächlich ist das Obermaterial vom Standpunkt der extremen Nanosekundenleistung etwas besser, aber weniger natürlich anzusehen.
Z.B:
string s = "house"; switch (s.ToLower()) { case "house": s = "window"; break; }
quelle
ToUpper(Invariant)
nicht nur schneller, sondern auch zuverlässiger zu sein: stackoverflow.com/a/2801521/67824Entschuldigen Sie diesen neuen Beitrag zu einer alten Frage, aber es gibt eine neue Option zum Lösen dieses Problems mit C # 7 (VS 2017).
C # 7 bietet jetzt "Pattern Matching" und kann verwendet werden, um dieses Problem folgendermaßen zu beheben:
string houseName = "house"; // value to be tested, ignoring case string windowName; // switch block will set value here switch (true) { case bool b when houseName.Equals("MyHouse", StringComparison.InvariantCultureIgnoreCase): windowName = "MyWindow"; break; case bool b when houseName.Equals("YourHouse", StringComparison.InvariantCultureIgnoreCase): windowName = "YourWindow"; break; case bool b when houseName.Equals("House", StringComparison.InvariantCultureIgnoreCase): windowName = "Window"; break; default: windowName = null; break; }
Diese Lösung befasst sich auch mit dem in der Antwort von @Jeffrey L Whitledge erwähnten Problem, dass der Vergleich von Zeichenfolgen ohne Berücksichtigung der Groß- und Kleinschreibung nicht mit dem Vergleich zweier Zeichenfolgen mit niedrigerem Gehäuse identisch ist.
Übrigens gab es im Februar 2017 im Visual Studio Magazine einen interessanten Artikel, in dem der Mustervergleich und seine Verwendung in Fallblöcken beschrieben wurden. Bitte werfen Sie einen Blick darauf: Pattern Matching in C # 7.0-Fallblöcken
BEARBEITEN
In Anbetracht der Antwort von @ LewisM ist es wichtig darauf hinzuweisen, dass die
switch
Aussage ein neues, interessantes Verhalten aufweist. Das heißt, wenn Ihrecase
Anweisung eine Variablendeklaration enthält, wird der imswitch
Teil angegebene Wert in die in der deklarierte Variable kopiertcase
. Im folgenden Beispiel wird der Werttrue
in die lokale Variable kopiertb
. Darüber hinaus wird die Variableb
nicht verwendet und existiert nur, damit diewhen
Klausel zurcase
Anweisung existieren kann:switch(true) { case bool b when houseName.Equals("X", StringComparison.InvariantCultureIgnoreCase): windowName = "X-Window";): break; }
Wie @LewisM hervorhebt, kann dies zum Nutzen genutzt werden - der Vorteil besteht darin, dass das zu vergleichende Objekt tatsächlich in der
switch
Aussage enthalten ist, wie dies bei der klassischen Verwendung derswitch
Aussage der Fall ist. Außerdem können die in dercase
Anweisung deklarierten temporären Werte unerwünschte oder versehentliche Änderungen des ursprünglichen Werts verhindern:switch(houseName) { case string hn when hn.Equals("X", StringComparison.InvariantCultureIgnoreCase): windowName = "X-Window"; break; }
quelle
switch (houseName)
case var name when name.Equals("MyHouse", ...
switch
Argumentwerten zucase
temporären Variablen hinzugefügt .case { } when
damit Sie sich nicht um Variablentyp und -namen kümmern müssen.In einigen Fällen kann es eine gute Idee sein, eine Aufzählung zu verwenden. Analysieren Sie also zuerst die Aufzählung (mit dem Flag ignoreCase true) und schalten Sie dann die Aufzählung ein.
SampleEnum Result; bool Success = SampleEnum.TryParse(inputText, true, out Result); if(!Success){ //value was not in the enum values }else{ switch (Result) { case SampleEnum.Value1: break; case SampleEnum.Value2: break; default: //do default behaviour break; } }
quelle
Eine Erweiterung der Antwort von @STLDeveloperA. Eine neue Methode zur Bewertung von Anweisungen ohne mehrere if-Anweisungen ab c # 7 ist die Verwendung der Switch-Anweisung mit Mustervergleich, ähnlich wie bei @STLDeveloper, obwohl auf diese Weise die zu schaltende Variable eingeschaltet wird
string houseName = "house"; // value to be tested string s; switch (houseName) { case var name when string.Equals(name, "Bungalow", StringComparison.InvariantCultureIgnoreCase): s = "Single glazed"; break; case var name when string.Equals(name, "Church", StringComparison.InvariantCultureIgnoreCase): s = "Stained glass"; break; ... default: s = "No windows (cold or dark)"; break; }
Das Visual Studio Magazin hat einen schönen Artikel über Musterblöcke , die einen Blick wert sein könnten.
quelle
switch
Anweisung hingewiesen haben .case var name when "Bungalow".Equals(name, StringComparison.InvariantCultureIgnoreCase):
Dies kann eine Nullreferenzausnahme verhindern (wobei houseName null ist) oder alternativ einen Fall hinzufügen, in dem die Zeichenfolge zuerst null ist.Eine Möglichkeit wäre die Verwendung eines Wörterbuchs zum Ignorieren von Groß- und Kleinschreibung mit einem Aktionsdelegierten.
string s = null; var dic = new Dictionary<string, Action>(StringComparer.CurrentCultureIgnoreCase) { {"house", () => s = "window"}, {"house2", () => s = "window2"} }; dic["HouSe"]();
// Beachten Sie, dass der Aufruf keinen Text zurückgibt, sondern nur lokale Variablen s auffüllt.
// Wenn Sie den eigentlichen Text zurückzukehren, ersetzen
Action
zuFunc<string>
wie und Werte im Wörterbuch zu etwas() => "window2"
quelle
CurrentCultureIgnoreCase
,OrdinalIgnoreCase
bevorzugt.Hier ist eine Lösung, die die Lösung von @Magnus in eine Klasse einschließt:
public class SwitchCaseIndependent : IEnumerable<KeyValuePair<string, Action>> { private readonly Dictionary<string, Action> _cases = new Dictionary<string, Action>(StringComparer.OrdinalIgnoreCase); public void Add(string theCase, Action theResult) { _cases.Add(theCase, theResult); } public Action this[string whichCase] { get { if (!_cases.ContainsKey(whichCase)) { throw new ArgumentException($"Error in SwitchCaseIndependent, \"{whichCase}\" is not a valid option"); } //otherwise return _cases[whichCase]; } } public IEnumerator<KeyValuePair<string, Action>> GetEnumerator() { return _cases.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return _cases.GetEnumerator(); } }
Hier ist ein Beispiel für die Verwendung in einer einfachen Windows Form-App:
var mySwitch = new SwitchCaseIndependent { {"hello", () => MessageBox.Show("hello")}, {"Goodbye", () => MessageBox.Show("Goodbye")}, {"SoLong", () => MessageBox.Show("SoLong")}, }; mySwitch["HELLO"]();
Wenn Sie Lambdas verwenden (wie im Beispiel), erhalten Sie Abschlüsse, die Ihre lokalen Variablen erfassen (ziemlich nahe an dem Gefühl, das Sie von einer switch-Anweisung erhalten).
Da es ein Wörterbuch unter der Decke verwendet, erhält es O (1) -Verhalten und muss nicht durch die Liste der Zeichenfolgen gehen. Natürlich müssen Sie dieses Wörterbuch erstellen, und das kostet wahrscheinlich mehr.
Es wäre wahrscheinlich sinnvoll, eine einfache
bool ContainsCase(string aCase)
Methode hinzuzufügen , die einfach dieContainsKey
Methode des Wörterbuchs aufruft .quelle
Ich hoffe, dies hilft beim Versuch, die gesamte Zeichenfolge in Kleinbuchstaben oder Großbuchstaben umzuwandeln und die Zeichenfolge in Kleinbuchstaben zum Vergleich zu verwenden:
public string ConvertMeasurements(string unitType, string value) { switch (unitType.ToLower()) { case "mmol/l": return (Double.Parse(value) * 0.0555).ToString(); case "mg/dl": return (double.Parse(value) * 18.0182).ToString(); } }
quelle
Dies sollte ausreichen:
string s = "houSe"; switch (s.ToLowerInvariant()) { case "house": s = "window"; break; }
Der Schaltervergleich ist dabei kulturinvariant. Soweit ich sehen kann, sollte dies das gleiche Ergebnis wie die C # 7-Pattern-Matching-Lösungen erzielen, jedoch prägnanter.
quelle
10 Jahre später können Sie mit dem C # -Musterabgleich Folgendes tun:
private string NormalisePropertyType(string propertyType) => true switch { true when string.IsNullOrWhiteSpace(propertyType) => propertyType, true when "house".Equals(propertyType, StringComparison.OrdinalIgnoreCase) => "house", true when "window".Equals(propertyType, StringComparison.OrdinalIgnoreCase) => "window", true when "door".Equals(propertyType, StringComparison.OrdinalIgnoreCase) => "door", true when "roof".Equals(propertyType, StringComparison.OrdinalIgnoreCase) => "roof", true when "chair".Equals(propertyType, StringComparison.OrdinalIgnoreCase) => "chair", _ => propertyType };
quelle