como pareces ser consciente, en minúscula dos cadenas y comparándolos no es lo mismo que hacer una comparación ignore-case. Hay muchas razones para esto. Por ejemplo, el estándar Unicode permite que el texto con signos diacríticos se codifique de múltiples maneras. Algunos caracteres incluyen tanto el carácter base como el signo diacrítico en un único punto de código. Estos caracteres también pueden representarse como el carácter base seguido de un carácter diacrítico de combinación. Estas dos representaciones son iguales para todos los propósitos, y las comparaciones de cadenas con reconocimiento de cultura en .NET Framework las identificará 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 más que una comparación ordinal. Una comparación ordinal es adecuada para ciertos tipos de aplicaciones, como el análisis de un archivo ASCII con códigos rígidamente definidos, pero la comparación de cadenas ordinales es incorrecta para la mayoría de los demás 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>
de pares de cadenas de casos y delegados. La lista se puede buscar 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 instrucciones if
. Esto generalmente no es tan malo como parece, ya que la estructura es muy regular.
Lo bueno de esto es que no hay realmente ninguna penalización en el rendimiento en la imitación de su propia funcionalidad de conmutación cuando se compara con cadenas. El sistema no creará una tabla de salto O (1) de la manera que sea posible con números enteros, por lo que de todos modos se compararán cada cadena de a una.
Si hay muchos casos para comparar y el rendimiento es un problema, la opción List<T>
descrita anteriormente podría reemplazarse con un diccionario ordenado o una tabla hash. Entonces, el rendimiento puede coincidir o superar la opción de declaración de cambio.
Aquí es 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, es probable que desee añadir 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 queda bloqueado con las sentencias if
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";
}
A menos que esté equivocado, los dos solo son diferentes para ciertas culturas (como el turco), y en ese caso no podría usar 'ToUpperInvariant()' o 'ToLowerInvariant()'? 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 correcta de mayúsculas y minúsculas, entonces el bloque de interruptores debería funcionar bien. –
@Seth Petry-Johnson - Tal vez esa optimización podría hacerse, pero la razón por la cual las opciones de comparación de cadenas están integradas en el marco es para que no todos tengamos que convertirnos en expertos en lingüística para escribir software correcto y extensible. –
OK. Daré un ejemplo donde esto es relivante. Supongamos que en lugar de "casa" tuviéramos la palabra (¡inglés!) "Café". Este valor podría representarse igualmente bien (y con la misma probabilidad) por "caf \ u00E9" o "cafetería \ u0301". La igualdad ordinal (como en una sentencia switch) con 'ToLower()' o 'ToLowerInvariant()' devolverá falso. 'Equals' con' StringComparison.InvariantCultureIgnoreCase' devolverá true. Dado que ambas secuencias se ven idénticas cuando se muestran, la versión 'ToLower()' es un error desagradable para rastrear. Es por eso que siempre es mejor hacer comparaciones de cuerdas adecuadas, incluso si no eres turco. –