Como parece saber, poner dos cadenas en minúsculas y compararlas no es lo mismo que hacer una comparación de caso omiso. Hay muchas razones para esto. Por ejemplo, el estándar Unicode permite codificar texto con signos diacríticos de varias formas. Algunos caracteres incluyen tanto el carácter base como el diacrítico en un solo punto de código. Estos caracteres también pueden representarse como el carácter base seguido de un carácter diacrítico combinado. Estas dos representaciones son iguales para todos los propósitos, y las comparaciones de cadenas con reconocimiento cultural en .NET Framework las identificarán correctamente como iguales, ya sea con CurrentCulture o InvariantCulture (con o sin IgnoreCase). Una comparación ordinal, por otro lado, los considerará incorrectamente como desiguales.
Desafortunadamente, switch
no hace nada más que una comparación ordinal. Una comparación ordinal está bien para ciertos tipos de aplicaciones, como analizar un archivo ASCII con códigos rígidamente definidos, pero la comparación de cadenas ordinales es incorrecta para la mayoría de los otros usos.
Lo que he hecho en el pasado para obtener el comportamiento correcto es simplemente simular mi propia declaración de cambio. Hay muchas formas de hacer esto. Una forma sería crear un List<T>
par de cadenas de casos y delegados. Se puede buscar en la lista utilizando la comparación de cadenas adecuada. Cuando se encuentra la coincidencia, se puede invocar al delegado asociado.
Otra opción es hacer la cadena obvia de if
declaraciones. Esto generalmente resulta no ser tan malo como parece, ya que la estructura es muy regular.
Lo bueno de esto es que realmente no hay ninguna penalización en el rendimiento al simular la funcionalidad de su propio interruptor cuando se compara con cadenas. El sistema no va a hacer una tabla de salto O (1) como puede hacerlo con números enteros, por lo que comparará cada cadena una a la vez de todos modos.
Si hay muchos casos para comparar y el rendimiento es un problema, entonces la List<T>
opción descrita anteriormente podría reemplazarse con un diccionario ordenado o una tabla hash. Entonces, el rendimiento puede igualar o superar potencialmente la opción de declaración de cambio.
A continuación se muestra un ejemplo de la 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();
}
Por supuesto, probablemente querrá agregar algunos parámetros estándar y posiblemente un tipo de retorno al delegado CustomSwitchDestination. ¡Y querrás hacer mejores nombres!
Si el comportamiento de cada uno de sus casos no es susceptible de delegar la invocación de esta manera, por ejemplo, si son necesarios diferentes parámetros, entonces está atascado con if
declaraciones encadenadas . También he hecho esto algunas veces.
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()
oToLowerInvariant()
? Además, no está comparando dos cadenas desconocidas , está comparando una cadena desconocida con una cadena conocida. Por lo tanto, siempre que sepa cómo codificar la representación adecuada en mayúsculas o minúsculas, el bloque de interruptores debería funcionar bien.ToLower()
oToLowerInvariant()
devolverá falso.Equals
conStringComparison.InvariantCultureIgnoreCase
volverá verdadero. Dado que ambas secuencias se ven idénticas cuando se muestran, laToLower()
versión es un error desagradable de rastrear. Es por eso que siempre es mejor hacer comparaciones de cadenas adecuadas, incluso si no eres turco.Un enfoque más simple es simplemente poner en minúsculas su cadena antes de que entre en la declaración de cambio, y hacer que los casos sean más bajos.
En realidad, la parte superior es un poco mejor desde el punto de vista del rendimiento de nanosegundos extremos, pero menos natural a la vista.
P.ej:
string s = "house"; switch (s.ToLower()) { case "house": s = "window"; break; }
fuente
ToUpper(Invariant)
que no solo es más rápido, sino más confiable: stackoverflow.com/a/2801521/67824Lo siento por esta nueva publicación a una pregunta anterior, pero hay una nueva opción para resolver este problema usando C # 7 (VS 2017).
C # 7 ahora ofrece "coincidencia de patrones" y se puede utilizar para abordar este problema de la siguiente manera:
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 solución también se ocupa del problema mencionado en la respuesta de @Jeffrey L Whitledge de que la comparación de cadenas sin distinción entre mayúsculas y minúsculas no es lo mismo que comparar dos cadenas en minúsculas.
Por cierto, hubo un artículo interesante en febrero de 2017 en Visual Studio Magazine que describe la coincidencia de patrones y cómo se puede usar en bloques de casos. Por favor, eche un vistazo: Coincidencia de patrones en bloques de casos de C # 7.0
EDITAR
A la luz de la respuesta de @ LewisM, es importante señalar que la
switch
declaración tiene un comportamiento nuevo e interesante. Es decir, si sucase
declaración contiene una declaración de variable, entonces el valor especificado en laswitch
parte se copia en la variable declarada encase
. En el siguiente ejemplo, el valortrue
se copia en la variable localb
. Además de eso, la variableb
no se usa y existe solo para que lawhen
cláusula de lacase
declaración pueda existir:switch(true) { case bool b when houseName.Equals("X", StringComparison.InvariantCultureIgnoreCase): windowName = "X-Window";): break; }
Como señala @LewisM, esto se puede usar para beneficiarse; ese beneficio es que lo que se está comparando está realmente en la
switch
declaración, como lo está con el uso clásico de laswitch
declaración. Además, los valores temporales declarados en lacase
declaración pueden evitar cambios no deseados o inadvertidos en el valor original:switch(houseName) { case string hn when hn.Equals("X", StringComparison.InvariantCultureIgnoreCase): windowName = "X-Window"; break; }
fuente
switch (houseName)
hacer la comparación similar a la forma en que lo hizo, es decircase var name when name.Equals("MyHouse", ...
switch
valores de argumentos acase
variables temporales.case { } when
no tener que preocuparse por el tipo y el nombre de la variable.En algunos casos, puede ser una buena idea usar una enumeración. Entonces, primero analice la enumeración (con la bandera ignoreCase verdadera) y luego active la enumeración.
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; } }
fuente
Una extensión de la respuesta de @STLDeveloperA. Una nueva forma de hacer la evaluación de declaraciones sin múltiples declaraciones if a partir de c # 7 es usar la declaración Switch de coincidencia de patrones, similar a la forma en que @STLDeveloper, aunque de esta manera está activando la variable que se está cambiando
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; }
La revista Visual Studio tiene un buen artículo sobre bloques de casos de coincidencia de patrones que podría valer la pena ver.
fuente
switch
declaración.case var name when "Bungalow".Equals(name, StringComparison.InvariantCultureIgnoreCase):
ya que esto puede evitar una excepción de referencia nula (donde houseName es nulo) o, alternativamente, agregar un caso para que la cadena sea nula primero.Una forma posible sería utilizar un diccionario de casos ignorados con un delegado de acción.
string s = null; var dic = new Dictionary<string, Action>(StringComparer.CurrentCultureIgnoreCase) { {"house", () => s = "window"}, {"house2", () => s = "window2"} }; dic["HouSe"]();
// Tenga en cuenta que la llamada no devuelve texto, solo llena las variables locales s.
// Si desea devolver el texto real, reemplace
Action
aFunc<string>
y valores en el diccionario a algo así como() => "window2"
fuente
CurrentCultureIgnoreCase
,OrdinalIgnoreCase
se prefiere.Aquí hay una solución que envuelve la solución de @Magnus en una clase:
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(); } }
Aquí hay un ejemplo de cómo usarlo en una aplicación simple de Windows Form:
var mySwitch = new SwitchCaseIndependent { {"hello", () => MessageBox.Show("hello")}, {"Goodbye", () => MessageBox.Show("Goodbye")}, {"SoLong", () => MessageBox.Show("SoLong")}, }; mySwitch["HELLO"]();
Si usa lambdas (como el ejemplo), obtendrá cierres que capturarán sus variables locales (bastante cerca de la sensación que obtiene de una declaración de cambio).
Dado que utiliza un diccionario oculto, obtiene un comportamiento O (1) y no se basa en recorrer la lista de cadenas. Por supuesto, necesita construir ese diccionario, y eso probablemente cueste más.
Probablemente tenga sentido agregar un
bool ContainsCase(string aCase)
método simple que simplemente llame alContainsKey
método del diccionario .fuente
Espero que esto ayude a tratar de convertir toda la cadena en minúsculas o mayúsculas y usar la cadena en minúsculas para comparar:
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(); } }
fuente
Debería ser suficiente para hacer esto:
string s = "houSe"; switch (s.ToLowerInvariant()) { case "house": s = "window"; break; }
Por tanto, la comparación de conmutadores no varía según la cultura. Por lo que puedo ver, esto debería lograr el mismo resultado que las soluciones C # 7 Pattern-Matching, pero de manera más sucinta.
fuente