Más cosas curiosas sobre el C#
Voy a comentar más cosas curiosas que estoy descubriéndole al C# y que quizás los expertos del lenguaje vean como obvias, pero que personalmente considero son bastante interesantes. Vamos allá.
La sentencia switch
Esta sentencia es muy curiosa (y muy potente, quizás más que su equivalente en C++). Lo más curioso de todo es que se puede utilizar una cadena (del tipo string como valor comparativo. Algo así:
switch(miCad)
{
case "Hola":
break;
case "Adiós":
break;
default:
break;
}
Y no es una característica pobre el poder hacer esto, que tradicionalmente se suele hacer mediante la concatenación de condicionales.
Otra cosa curiosa es la imposibilidad de "caer a través" de un case, lo que si en principio puede parecer una limitación, realmente no lo es, porque existe una forma correcta de "caer" y que, al contrario que con otros lenguajes, no es propenso a cometer errores. Porque amigos, el error más común en una sentencia swtich es que se te olvide un break, y dicho olvido puede causar interesantes efectos laterales no deseados que pueden causar una buena tanda de problemas. Veamos un ejemplo:
switch(f)
{
case 1:
CasoUno(); //Error, falta break
case 2:
CasoDos();
goto default;
case 3:
CasoTres();
break;
case 4:
CasoCuatro();
goto case 2;
case 5:
default:
break;
}
Menuda máquina de estados, está más liada que el pelo de un indigente. En el caso 1 tenemos un error, pues estamos "cayendo" sobre el caso 2. En el 2, tras ejecutar el método, saltamos al caso por defecto. Y finalmente en el caso 4, tras ejecutar el método, volvemos al caso 2. Como cosa curiosa decir que la forma del caso 5 está permitida para agrupar casos con el mismo comportamiento. Muy bonito y muy potente.
Además, los casos no tienen por qué estar ordenados, el compilador ya sabe por cuáles ha pasado y por cuáles no. Menuda gozada.
Alias para espacios de nombres
Con esta característica lo mismo sorprendo a alguien. Seguro. Podemos definir espacios de nombre (namespace) que son alias de otros espacios de nombres. Es un tema curioso y que creo que ni el C++ ni el C++/CLI tienen. Veamos un ejemplo:
namespace N1.N2
{
class A{}
}
namespace N3
{
using R=N1.N2;
class B:R.A{}
}
Observamos que la clase B hereda de la clase A del espacio de nombres N1.N2, pero lo hemos hecho utilizando un alias.
Las reglas de manejo de todo esto están explicadas en las páginas 266 y siguientes del The C# Programming Language que he citado por aquí en otros momentos. Son unas reglas un tanto pejigueras, puesto que no permiten anidación y están limitadas dentro de un espacio de nombres, pero bueno, ahí están para ser utilizadas.
Bloqueos y chequeos
Tenemos dos sentencias que permiten establecer la verificación de los cálculos matemáticos, lo que en ciertas aplicaciones científicas o comerciales pueden ser de gran ayuda. Si colocamos una serie de sentencias dentro de un bloque checked su interior será verificado por el compilador y el entorno de ejecución, generando excepciones en los casos que sea necesario o incluso impidiendo la compilación en los casos en los que la verificación puede comprobarse en tiempo de compilación. Con unchecked hacemos justo lo contrario. Y tenemos estas dos opciones porque podemos decir que nuestro código sea chequeado y tener partes no chequeadas y viceversa en las opciones del proyecto. También hay que tener en cuenta que los bloques verificados se ejecutan más lentos.
Podemos bloquear una variable y ejecutar un bloque de código con dicha variable bloqueada. Es una característica interesante aunque peligrosa si estamos utilizando hilos, porque podríamos bloquear la aplicación indefinidamente si dos hilos bloquean simultáneamente la misma variable, así que hay que andarse con ojo con esta característica, y mejor es utilizar otras opciones de sincronización ofrecidas por el sistema operativo o las bibliotecas de clases.
Me voy
El C# como todo lenguaje que se precie tiene la instrucción goto, aunque aparte del uso ya explicado poco sentido le veo; yo suelo usar goto cuando programo hardware (micros sin sistema operativo, interrupciones físicas, etc.), pero en un lenguaje de alto nivel (o con C/C++ pero en programas de alto nivel) la verdad es que dicha instrucción se desvirtúa.
Usando el uso
Esta característica me gusta poco aunque la considero útil. En mis programas en C++/CLI uso la destrucción determinista, pero en C# no se puede hacer. Para forzar la destrucción de recursos no manejados debemos utilizar la sentencia using, aunque personalmente nunca la he usado… pero no me olvido de cerrar el recurso abierto.
Un ejemplo de libro sobre este uso sería:
Using(TextReader r=File.OpenText("hola.txt")) {
String s;
While((s=r.ReadLine())!=null) {
Console.WriteLine(s);
}
}
Lo curioso de este ejemplo es que la sentencia while genera un warning en la asignación de s, pero no tiene nada que ver con lo que estamos explicando.