Como você parece estar ciente, colocar duas strings em minúsculas e compará-las não é o mesmo que fazer uma comparação de maiúsculas e minúsculas. Existem muitas razões para isso. Por exemplo, o padrão Unicode permite que o texto com diacríticos seja codificado de várias maneiras. Alguns caracteres incluem o caractere base e o diacrítico em um único ponto de código. Esses caracteres também podem ser representados como o caractere base seguido por um caractere diacrítico de combinação. Essas duas representações são iguais para todos os fins, e as comparações de strings de reconhecimento de cultura no .NET Framework irão identificá-los corretamente como iguais, com CurrentCulture ou InvariantCulture (com ou sem IgnoreCase). Uma comparação ordinal, por outro lado, os considerará incorretamente como desiguais.
Infelizmente, switch
não faz nada além de uma comparação ordinal. Uma comparação ordinal é adequada para certos tipos de aplicativos, como analisar um arquivo ASCII com códigos rigidamente definidos, mas a comparação de strings ordinais é incorreta para a maioria dos outros usos.
O que fiz no passado para obter o comportamento correto é apenas simular minha própria instrução switch. Existem muitas maneiras de fazer isso. Uma maneira seria criar um List<T>
par de strings case e delegados. A lista pode ser pesquisada usando a comparação de string adequada. Quando a correspondência é encontrada, o delegado associado pode ser chamado.
Outra opção é fazer a cadeia óbvia de if
afirmações. Geralmente, isso não é tão ruim quanto parece, já que a estrutura é muito regular.
A melhor coisa sobre isso é que não há realmente nenhuma penalidade de desempenho em simular a funcionalidade de seu próprio switch ao comparar com strings. O sistema não vai fazer uma tabela de pulos O (1) da maneira que faz com inteiros, então vai comparar cada string uma por vez de qualquer maneira.
Se houver muitos casos a serem comparados e o desempenho for um problema, a List<T>
opção descrita acima pode ser substituída por um dicionário classificado ou tabela de hash. Então, o desempenho pode corresponder ou exceder a opção de instrução switch.
Aqui está um exemplo da lista de delegados:
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();
}
Claro, você provavelmente desejará adicionar alguns parâmetros padrão e, possivelmente, um tipo de retorno ao delegado CustomSwitchDestination. E você vai querer fazer nomes melhores!
Se o comportamento de cada um dos seus casos não puder ser delegado à invocação dessa maneira, como se parâmetros diferentes forem necessários, você estará preso a if
instruções encadeadas . Eu também fiz isso algumas vezes.
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()
? Além disso, ele não está comparando duas strings desconhecidas , ele está comparando uma string desconhecida a uma string conhecida. Portanto, contanto que ele saiba como codificar a representação adequada em maiúsculas ou minúsculas, o bloco de comutação deve funcionar bem.ToLower()
ouToLowerInvariant()
retornará false.Equals
comStringComparison.InvariantCultureIgnoreCase
retornará verdadeiro. Como as duas sequências parecem idênticas quando exibidas, aToLower()
versão é um bug desagradável de rastrear. É por isso que é sempre melhor fazer comparações de strings adequadas, mesmo se você não for turco.Uma abordagem mais simples é apenas colocar sua string em minúsculas antes que ela vá para a instrução switch e ter as maiúsculas e minúsculas.
Na verdade, a parte superior é um pouco melhor do ponto de vista de desempenho de nanossegundos extremos, mas menos natural de se olhar.
Por exemplo:
string s = "house"; switch (s.ToLower()) { case "house": s = "window"; break; }
fonte
ToUpper(Invariant)
não é apenas mais rápido, mas também mais confiável: stackoverflow.com/a/2801521/67824Desculpe por este novo post para uma pergunta antiga, mas há uma nova opção para resolver esse problema usando C # 7 (VS 2017).
C # 7 agora oferece "correspondência de padrões" e pode ser usado para resolver esse problema da seguinte maneira:
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; }
Esta solução também lida com o problema mencionado na resposta de @Jeffrey L Whitledge de que a comparação de strings sem distinção entre maiúsculas e minúsculas não é o mesmo que comparar duas strings com caixa baixa.
A propósito, houve um artigo interessante em fevereiro de 2017 na Visual Studio Magazine descrevendo a correspondência de padrões e como ela pode ser usada em blocos de caso. Por favor, dê uma olhada: Correspondência de padrões em blocos de caso C # 7.0
EDITAR
À luz da resposta de @LewisM, é importante ressaltar que a
switch
declaração tem um comportamento novo e interessante. Ou seja, se suacase
instrução contiver uma declaração de variável, o valor especificado naswitch
parte será copiado para a variável declarada emcase
. No exemplo a seguir, o valortrue
é copiado para a variável localb
. Além disso, a variávelb
não é utilizada e existe apenas para que awhen
cláusula dacase
instrução possa existir:switch(true) { case bool b when houseName.Equals("X", StringComparison.InvariantCultureIgnoreCase): windowName = "X-Window";): break; }
Como @LewisM aponta, isso pode ser usado para beneficiar - esse benefício é que a coisa que está sendo comparada está na verdade na
switch
instrução, como acontece com o uso clássico daswitch
instrução. Além disso, os valores temporários declarados nacase
instrução podem evitar alterações indesejadas ou inadvertidas no valor original:switch(houseName) { case string hn when hn.Equals("X", StringComparison.InvariantCultureIgnoreCase): windowName = "X-Window"; break; }
fonte
switch (houseName)
fazer a comparação semelhante à maneira como você fez, ou sejacase var name when name.Equals("MyHouse", ...
switch
valores de argumento acase
variáveis temporárias.case { } when
assim não precisa se preocupar com o tipo e o nome da variável.Em alguns casos, pode ser uma boa ideia usar um enum. Portanto, primeiro analise o enum (com o sinalizador ignoreCase true) e, em seguida, tenha uma opção no enum.
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; } }
fonte
Uma extensão da resposta de @STLDeveloperA. Uma nova maneira de fazer a avaliação de instruções sem várias instruções if a partir de c # 7 é usar a instrução Switch de correspondência de padrões, semelhante à maneira como @STLDeveloper, embora desta forma, ligue a variável que está sendo trocada
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; }
A revista visual studio tem um bom artigo sobre blocos de caixas de correspondência de padrões que pode valer a pena dar uma olhada.
fonte
switch
declaração.case var name when "Bungalow".Equals(name, StringComparison.InvariantCultureIgnoreCase):
pois isso pode evitar uma exceção de referência nula (em que houseName é nula) ou, alternativamente, adicionar um caso para a string ser nula primeiro.Uma maneira possível seria usar um dicionário de caso para ignorar com um delegado de ação.
string s = null; var dic = new Dictionary<string, Action>(StringComparer.CurrentCultureIgnoreCase) { {"house", () => s = "window"}, {"house2", () => s = "window2"} }; dic["HouSe"]();
// Observe que a chamada não retorna texto, mas apenas preenche a variável local s.
// Se você deseja retornar o texto real, substitua os valores
Action
paraFunc<string>
e no dicionário por algo como() => "window2"
fonte
CurrentCultureIgnoreCase
,OrdinalIgnoreCase
é o preferido.Aqui está uma solução que envolve a solução de @Magnus em uma 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(); } }
Aqui está um exemplo de como usá-lo em um aplicativo simples do Windows Form:
var mySwitch = new SwitchCaseIndependent { {"hello", () => MessageBox.Show("hello")}, {"Goodbye", () => MessageBox.Show("Goodbye")}, {"SoLong", () => MessageBox.Show("SoLong")}, }; mySwitch["HELLO"]();
Se você usar lambdas (como o exemplo), obterá fechamentos que capturarão suas variáveis locais (muito parecido com a sensação que você obtém de uma instrução switch).
Como ele usa um Dicionário nos bastidores, ele obtém comportamento O (1) e não depende de percorrer a lista de strings. Claro, você precisa construir esse dicionário, e isso provavelmente custa mais.
Provavelmente faria sentido adicionar um
bool ContainsCase(string aCase)
método simples que simplesmente chamasse oContainsKey
método do dicionário .fonte
Espero que isso ajude a tentar converter toda a string em letras minúsculas ou maiúsculas e usar a string em letras minúsculas para comparação:
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(); } }
fonte
Deve ser suficiente fazer isso:
string s = "houSe"; switch (s.ToLowerInvariant()) { case "house": s = "window"; break; }
A comparação do switch é, portanto, invariante de cultura. Pelo que posso ver, isso deve atingir o mesmo resultado que as soluções de correspondência de padrões C # 7, mas de forma mais sucinta.
fonte
10 anos depois, com a correspondência de padrões C #, você pode fazer algo como:
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 };
fonte