La sentencia switch en C# 7.0

“Si te quiero es porque sos
Mi amor, mi cómplice y todo,
Y en la calle, codo a codo,
Somos mucho más que dos…”
(Poema de Mario Benedetti)

En una entrada anterior reciente presentamos una de las novedades importantes que introdujo C# 7.0, los llamados patrones (patterns), que ahora pueden utilizarse en el contexto de dos construcciones del lenguaje:

  • El operador is de comprobación de tipos, que ahora acepta también en su parte derecha un patrón, además de un tipo.
  • Las cláusulas case de la sentencia switch, que ahora pueden especificar un patrón de emparejamiento, y no únicamente una constante.

En aquella ocasión mostramos cómo hacer uso de los tres tipos de patrones que incorpora C# 7.0 conjuntamente con el operador is; hoy veremos cómo utilizarlos en el marco de una sentencia switch, algo que en la práctica será mucho más común. Para ello, utilizaremos un sencillo ejemplo que contabiliza e imprime los invitados a una fiesta de empresa a la que se admiten también mascotas:

class MainClass
{
    static void Main(string[] args)
    {
        Process(
            new Employee("Octavio"55"Software Eng."),
            new Person("Diana"21),
            new Pet("Shelly"PetType.DOG11)
        );
    }

    static void Process(params object[] data)
    {
        int people = 0pets = 0;
        foreach (var obj in data)
        {
            switch (obj)
            {
                case null:
                    break;
                case Pet pet:
                    Console.WriteLine($"{pet.Name} [{pet.Type}]");
                    pets++;
                    break;
                case Employee e:
                    Console.WriteLine($"{e.Name} ({e.Position})");
                    people++;
                    break;
                case Person p:
                    Console.WriteLine(p.Name);
                    people++;
                    break;
                default:
                    Console.WriteLine("** Invalid input!");
                    break;
            }
        }
        Console.WriteLine($"Total: {people} people, {pets} pets.");
    }
}

Nótese en primer lugar que la expresión de control de la sentencia switch ya no está limitada a los tipos básicos y las cadenas: la expresión puede ser ahora de cualquier tipo, valor o referencia. La primera cláusula case ejemplifica el primer tipo de patrones que estudiamos la vez anterior, los patrones constantes, que es de hecho lo que estábamos acostumbrados a utilizar en cláusulas case en versiones anteriores de C#; solo que ahora null es una constante tan válida como otra cualquiera.

Las tres cláusulas case a continuación ejemplifican el segundo tipo de patrones, los patrones de tipo, que tienen la forma T x, donde T es un tipo y x un identificador de variable; mediante ellos se comprueba si el valor de la expresión es de tipo T, y en caso afirmativo, se asigna dicho valor a la variable x de tipo T. Téngase en cuenta aquí que el valor null nunca “casa” en estos patrones, por lo que es generalmente necesario codificar explícitamente ese caso, tal y como hemos hecho en el ejemplo.

Dejaremos el uso del tercer tipo de patrones (los patrones var) en la sentencia switch para una próxima entrega, para verla en combinación con otra interesante novedad añadida a esta sentencia en C# 7.0: la cláusula when.

Hay que destacar que con la llegada de C# 7.0 y estas nuevas posibilidades, la sentencia switch se convierte en una bestia MUY DIFERENTE de lo que era hasta la versión anterior de C#:

a) En primer lugar, la sentencia se hace mucho más dinámica de lo que fue nunca antes. Ahora los patrones de las cláusulas case se emparejan en tiempo de ejecución. Anteriormente, las cláusulas case solo especificaban constantes (diferentes, añadiría yo), y todo estaba claro ya en tiempo de compilación.

b) En segundo lugar, y estrechamente relacionado con lo anterior, ahora el ORDEN de las cláusulas case en el código fuente es importante; antes era irrelevante.

La semántica de switch ahora consiste, en el caso general, en evaluar secuencialmente (en cascada) cada una de las cláusulas case y ejecutar el bloque de código asociado a la primera cláusula que se satisfaga; cediendo el control al bloque default solamente en caso de que ninguna cláusula case tenga éxito. Dado que en una sentencia switch de versiones anteriores de C# solo había constantes mutuamente excluyentes en las cláusulas case, el compilador tenía múltiples opciones a la hora de traducirla a código de máquina, y la única implementación posible no era una cascada de condicionales, sino varios otros métodos tales como tabla de saltos, búsqueda binaria y otros. Por supuesto, el compilador podría continuar utilizando todas esas técnicas cuando encuentre sentencias switch que no utilicen ninguna de las novedades de C# 7.0.

Por último, y ya para concluir por hoy, señalaremos que en C# 7.0 el compilador sigue haciendo, no obstante lo dicho anteriormente, todo lo que puede por nosotros. Por ejemplo, asumiendo como es de suponer que la clase Employee hereda de Person, nuestro programa de ejemplo no compilará si se intercambian de orden los casos correspondientes en la sentencia switch. Esto es similar a lo que hace el compilador toda la vida en relación con los tipos de excepciones que se especifican en cláusulas catch de un mismo try.


Referencia cultural: Como esta entrada se escribe durante el Día de San Valentín, aprovecho para insertar un fragmento de uno de los muchos lindos poemas de amor de ese gigante de la poesía iberoamericana que es el uruguayo Mario Benedetti.

Octavio Hernandez

Desarrollador y consultor en tecnologías .NET. Microsoft C# MVP entre 2004 y 2010.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *