Saltar al contenido principal

switch expression

Anteriormente, aprendimos sobre la estructura switch case que nos ayuda a asignar o ejecutar una lógica específica de acuerdo al resultado de la comparación evaluada.

Dart 3 introdujo la expresión switch que es una mejora al clásico switch case. Esta versión mejorada permite utilizar el switch como una expresión, lo que significa que puede producir un valor y asignarse a una variable o usarse en cualquier lugar donde se espere una expresión

Veamos un sencillo ejemplo, suponiendo que el primer día de la semana es lunes y es el día uno, martes es el día dos y así sucesivamente hasta llegar al domingo. Dado un número entero, queremos imprimir a que día corresponde. Usando el clásico switch case, el código se verá así:

// Ejemplo usando el clásico switch case
void main() {
final diaActual = obtenerDia(5);
print('Hoy es $diaActual'); // Hoy es Viernes
}

String obtenerDia(int dia) {
switch (dia) {
case 1:
return 'Lunes';
case 2:
return 'Martes';
case 3:
return 'Miércoles';
case 4:
return 'Jueves';
case 5:
return 'Viernes';
case 6:
return 'Sábado';
case 7:
return 'Domingo';
default:
return 'El dia no existe';
}
}

En el código anterior, hemos definido múltiples case con todos los valores de los días de la semana posibles y además incluimos un default para los valores que no coincidan con alguno de los días de la semana.

Utilizando la expresión switch, podemos hacer el código más compacto y fácil de leer:

// Ejemplo usando la nueva expresión switch
void main() {
final diaActual = obtenerDia(5);
print('Hoy es $diaActual'); // Hoy es Viernes
}

String obtenerDia(int dia) {
return switch (dia) {
1 => 'Lunes',
2 => 'Martes',
3 => 'Miércoles',
4 => 'Jueves',
5 => 'Viernes',
6 => 'Sábado',
7 => 'Domingo',
_ => 'El dia no existe',
};
}

En este ejemplo, la expresión dia se evalúa y se compara con distintos casos utilizando la sintaxis =>. Si el valor de dia es 1, el resultado es 'Lunes'. El guion bajo _ se utiliza como un caso por defecto para cualquier otro valor que no coincida con los casos anteriores.

Coincidencia de patrones ( pattern matching)

La coincidencia de patrones es una técnica que permite verificar si un valor cumple con un patrón específico y realizar acciones en función de ello. Se utiliza para realizar comparaciones más complejas y flexibles que las simples igualdades o desigualdades.

Si queremos saber si el día es "entre semana" o es un "fin de semana" podemos escribir el siguiente código:

void main() {
final diaActual = obtenerDia(4);
print('Hoy es $diaActual'); // Entre semana
}

String obtenerDia(int dia) {
return switch (dia) {
>= 1 && <= 5 => 'Entre semana',
6 || 7 => 'Fin de semana',
_ => 'El dia no existe',
};
}

También podemos asignar el valor producido por la expresión switch a una variable. El código anterior quedaría así:

void main() {
final dia = 5;

final diaActual = switch (dia) {
>= 1 && <= 5 => 'Entre semana',
6 || 7 => 'Fin de semana',
_ => 'El dia no existe',
};

print('Hoy es $diaActual'); // Entre semana
}

Clases selladas (sealed class)

Las clases selladas en Dart se utilizan a menudo en combinación con las expresiones switch. La estructura switch puede hacer uso exhaustivo de las subclases de una clase sellada, asegurando que se manejen todos los casos posibles.

void main() {
print('El area del circulo es: ${calcularArea(Circulo(5))}');
print('El area del cuadrado es: ${calcularArea(Cuadrado(10))}');
}

double calcularArea(Figura figura) {
return switch (figura) {
Cuadrado(lado: final lado) => lado * lado,
Circulo(radio: final radio) => 3.14 * radio * radio,
};
}

sealed class Figura {}

class Cuadrado extends Figura {
Cuadrado(this.lado);
final double lado;
}

class Circulo extends Figura {
Circulo(this.radio);
final double radio;
}

El ejemplo anterior funciona porque la clase Figura es sealed. Si no lo fuera tendríamos un error non_exhaustive_switch_expression

Cláusulas de guardia (Guard clauses)

Podemos usar la palabra reservada when en conjunto con el switch para especificar una cláusula de guardia. Por ejemplo, si queremos deserializar un JSON:

void main() {
var animal1Json = {
'nombre': 'Max',
'tipo': 'Gato',
};

var animal2Json = {
'nombre': 'Coco',
'tipo': 'Perro',
};

var otroJson = {
'nombre': 'Yayo',
'edad': '23',
};

print(parseJson(animal1Json)); // El gato es: Max
print(parseJson(animal2Json)); // El perro es: Coco
print(parseJson(otroJson)); // Exception: Error al Deserializar el JSON
}

Animal parseJson(Map<String, dynamic> map) {
return switch (map) {
{
'nombre': String nombre,
'tipo': String tipo,
}
when tipo == 'Gato' => Gato(nombre),
{
'nombre': String nombre,
'tipo': String tipo,
}
when tipo == 'Perro' => Perro(nombre),
_ => throw Exception('Error al Deserializar el JSON')
};
}

class Animal {
Animal(this.nombre);
final String nombre;
}

class Gato extends Animal {
Gato(String nombre) : super(nombre);


String toString() => 'El gato es: $nombre';
}

class Perro extends Animal {
Perro(String nombre) : super(nombre);


String toString() => 'El perro es: $nombre';
}

En el fragmento de código anterior tenemos tres JSON, uno es del tipo perro, otro es de tipo gato y el ultimo no tiene ningun tipo. En el switch usamos la palabra when tipo == 'Perro' o when tipo == 'Gato' para evaluar si el JSON es de tipo perro o gato y crear una instancia de sus respectivos clases Perro o Gato. Y en caso de que el JSON tenga un formato no soportado, lanzamos una excepción.

Conclusión

En conclusión, las expresiones switch en Dart 3 ofrecen una mejora significativa sobre las estructuras de switch case tradicionales. Esta nueva versión permite utilizar el switch como una expresión, lo que simplifica el código y lo hace más legible.

También se introduce la capacidad de realizar coincidencia de patrones, lo que brinda mayor flexibilidad en las comparaciones. Además, podemos hacer uso de las clases selladas en combinación con el switch, asegurando que todos los casos posibles sean manejados.

Por último, las cláusulas de guardia, utilizando la palabra reservada when, nos permiten realizar comprobaciones adicionales para deserializar JSON o manejar casos especiales.

En general, estas mejoras en las expresiones switch en Dart 3 facilitan el desarrollo de aplicaciones más concisas y robustas.