Comme vous semblez le savoir, mettre deux chaînes en minuscules et les comparer n'est pas la même chose que de faire une comparaison sans casse. Il y a de nombreuses raisons à cela. Par exemple, la norme Unicode permet de coder du texte avec des signes diacritiques de plusieurs manières. Certains caractères incluent à la fois le caractère de base et le signe diacritique dans un seul point de code. Ces caractères peuvent également être représentés comme le caractère de base suivi d'un caractère diacritique de combinaison. Ces deux représentations sont égales à toutes fins, et les comparaisons de chaînes tenant compte de la culture dans le .NET Framework les identifieront correctement comme étant égales, avec CurrentCulture ou InvariantCulture (avec ou sans IgnoreCase). Une comparaison ordinale, en revanche, les considérera à tort comme inégales.
Malheureusement, switch
ne fait rien d'autre qu'une comparaison ordinale. Une comparaison ordinale convient à certains types d'applications, comme l'analyse d'un fichier ASCII avec des codes définis de manière rigide, mais la comparaison de chaînes ordinales est incorrecte pour la plupart des autres utilisations.
Ce que j'ai fait dans le passé pour obtenir le bon comportement, c'est juste une maquette de ma propre instruction switch. Il y a plusieurs manières de faire ça. Une façon serait de créer une List<T>
paire de chaînes de cas et de délégués. La liste peut être recherchée en utilisant la comparaison de chaînes appropriée. Lorsque la correspondance est trouvée, le délégué associé peut être appelé.
Une autre option est de faire la chaîne évidente d' if
instructions. Cela s'avère généralement moins grave qu'il n'y paraît, car la structure est très régulière.
La grande chose à ce sujet est qu'il n'y a pas vraiment de pénalité en termes de performances à simuler votre propre fonctionnalité de commutateur lors de la comparaison avec des chaînes. Le système ne va pas créer une table de saut O (1) comme il le peut avec des entiers, il va donc comparer chaque chaîne une à la fois de toute façon.
S'il y a de nombreux cas à comparer et que les performances sont un problème, l' List<T>
option décrite ci-dessus peut être remplacée par un dictionnaire trié ou une table de hachage. Ensuite, les performances peuvent potentiellement correspondre ou dépasser l'option d'instruction switch.
Voici un exemple de la liste des délégués:
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();
}
Bien sûr, vous souhaiterez probablement ajouter des paramètres standard et éventuellement un type de retour au délégué CustomSwitchDestination. Et vous voudrez vous faire de meilleurs noms!
Si le comportement de chacun de vos cas ne permet pas de déléguer l'invocation de cette manière, par exemple si des paramètres différents sont nécessaires, alors vous êtes coincé avec des if
instructions chaînées . J'ai également fait cela plusieurs fois.
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()
ouToLowerInvariant()
? De plus, il ne compare pas deux chaînes inconnues , il compare une chaîne inconnue à une chaîne connue. Ainsi, tant qu'il sait comment coder en dur la représentation en majuscules ou minuscules appropriée, le bloc de commutation devrait fonctionner correctement.ToLower()
ouToLowerInvariant()
renverra false.Equals
avecStringComparison.InvariantCultureIgnoreCase
retournera vrai. Étant donné que les deux séquences semblent identiques lorsqu'elles sont affichées, laToLower()
version est un bogue méchant à traquer. C'est pourquoi il est toujours préférable de faire des comparaisons de chaînes appropriées, même si vous n'êtes pas turc.Une approche plus simple consiste simplement à mettre votre chaîne en minuscules avant qu'elle n'entre dans l'instruction switch et à réduire la casse.
En fait, la partie supérieure est un peu meilleure du point de vue des performances de la nanoseconde extrême pure, mais moins naturelle à regarder.
Par exemple:
string s = "house"; switch (s.ToLower()) { case "house": s = "window"; break; }
la source
ToUpper(Invariant)
être non seulement plus rapide, mais plus fiable: stackoverflow.com/a/2801521/67824Désolé pour ce nouveau message à une ancienne question, mais il existe une nouvelle option pour résoudre ce problème en utilisant C # 7 (VS 2017).
C # 7 propose désormais la «correspondance de modèles», et il peut être utilisé pour résoudre ce problème ainsi:
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; }
Cette solution traite également le problème mentionné dans la réponse de @Jeffrey L Whitledge selon laquelle la comparaison insensible à la casse des chaînes n'est pas la même chose que la comparaison de deux chaînes minuscules.
À propos, il y avait un article intéressant en février 2017 dans Visual Studio Magazine décrivant la correspondance de modèles et comment elle peut être utilisée dans les blocs de cas. Veuillez jeter un œil: Correspondance de motifs dans les blocs de cas C # 7.0
ÉDITER
À la lumière de la réponse de @ LewisM, il est important de souligner que la
switch
déclaration a un nouveau comportement intéressant. Autrement dit, si votrecase
instruction contient une déclaration de variable, la valeur spécifiée dans laswitch
partie est copiée dans la variable déclarée dans lecase
. Dans l'exemple suivant, la valeurtrue
est copiée dans la variable localeb
. De plus, la variableb
est inutilisée et n'existe que pour que lawhen
clause de l'case
instruction puisse exister:switch(true) { case bool b when houseName.Equals("X", StringComparison.InvariantCultureIgnoreCase): windowName = "X-Window";): break; }
Comme le souligne @LewisM, cela peut être utilisé pour en bénéficier - cet avantage étant que la chose comparée est en fait dans la
switch
déclaration, comme c'est le cas avec l'utilisation classique de laswitch
déclaration. En outre, les valeurs temporaires déclarées dans l'case
instruction peuvent empêcher des modifications indésirables ou involontaires de la valeur d'origine:switch(houseName) { case string hn when hn.Equals("X", StringComparison.InvariantCultureIgnoreCase): windowName = "X-Window"; break; }
la source
switch (houseName)
alors faire la comparaison de la même manière que vous l'avez fait, c'estcase var name when name.Equals("MyHouse", ...
switch
valeurs d'argument auxcase
variables temporaires.case { } when
pour ne pas avoir à vous soucier du type et du nom de la variable.Dans certains cas, il peut être judicieux d'utiliser une énumération. Alors commencez par analyser l'énumération (avec l'indicateur ignoreCase true) et ensuite avoir un commutateur sur l'énumération.
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; } }
la source
Une extension à la réponse de @STLDeveloperA. Une nouvelle façon de faire une évaluation d'instruction sans plusieurs instructions if à partir de c # 7 consiste à utiliser l'instruction Switch de correspondance de modèle, similaire à la façon dont @STLDeveloper, de cette manière, active la variable en cours de commutation.
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; }
Le magazine Visual Studio a un bel article sur les blocs de cas de correspondance de motifs qui pourraient valoir le coup d'œil.
la source
switch
déclaration.case var name when "Bungalow".Equals(name, StringComparison.InvariantCultureIgnoreCase):
coderais comme ceci: car cela peut empêcher une exception de référence nulle (où houseName est nul), ou encore ajouter un cas pour que la chaîne soit nulle en premier.Une manière possible serait d'utiliser un dictionnaire d'ignorer les cas avec un délégué d'action.
string s = null; var dic = new Dictionary<string, Action>(StringComparer.CurrentCultureIgnoreCase) { {"house", () => s = "window"}, {"house2", () => s = "window2"} }; dic["HouSe"]();
// Notez que l'appel ne renvoie pas de texte, mais ne remplit que la variable locale s.
// Si vous souhaitez renvoyer le texte réel, remplacez
Action
parFunc<string>
et les valeurs dans le dictionnaire par quelque chose comme() => "window2"
la source
CurrentCultureIgnoreCase
,OrdinalIgnoreCase
est préféré.Voici une solution qui encapsule la solution de @Magnus dans une classe:
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(); } }
Voici un exemple d'utilisation dans une application Windows Form simple:
var mySwitch = new SwitchCaseIndependent { {"hello", () => MessageBox.Show("hello")}, {"Goodbye", () => MessageBox.Show("Goodbye")}, {"SoLong", () => MessageBox.Show("SoLong")}, }; mySwitch["HELLO"]();
Si vous utilisez des lambdas (comme dans l'exemple), vous obtenez des fermetures qui captureront vos variables locales (assez proches du sentiment que vous obtenez d'une instruction switch).
Puisqu'il utilise un dictionnaire sous les couvertures, il obtient le comportement O (1) et ne se fie pas à parcourir la liste de chaînes. Bien sûr, vous devez construire ce dictionnaire, et cela coûte probablement plus cher.
Il serait probablement judicieux d'ajouter une
bool ContainsCase(string aCase)
méthode simple qui appelle simplement laContainsKey
méthode du dictionnaire .la source
J'espère que cela aide à essayer de convertir toute la chaîne en cas particulier en minuscules ou en majuscules et à utiliser la chaîne en minuscules à des fins de comparaison:
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(); } }
la source
Cela devrait suffire:
string s = "houSe"; switch (s.ToLowerInvariant()) { case "house": s = "window"; break; }
La comparaison des commutateurs est donc invariante de culture. Pour autant que je sache, cela devrait aboutir au même résultat que les solutions de correspondance de motifs C # 7, mais de manière plus succincte.
la source