Normalmente cuando queremos verificar que un objeto cumple con ciertas propiedades utilizamos un condicional escribiendo una y otra vez el objeto para poder hacer la comprobación.
Por ejemplo, en este código hemos definido una clase simple llamada Person
e instanciamos un objeto de la misma:
class Person
{
public string Name { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
}
List<Person> personitas = new List<Person>();
personitas.Add(new Person { Name = "Juan", LastName = "Perez", Age = 20 });
personitas.Add(new Person { Name = "Elvira", LastName = "García", Age = 24});
personitas.Add(new Person { Name = "Pedro", LastName = "González", Age = 30});
personitas.Add(new Person { Name = "Maria", LastName = "", Age = 40});
personitas.Add(new Person { Name = "José Antonio", LastName = "Español", Age = 50 });
Si ahora quisiéramos sacar una lista con las personas que cumplan con ciertas condiciones, por ejemplo que tengan entre 20 y 40 años y que tengan apellidos haríamos un condicional parecido a este:
List<Person> personitas1 = new List<Person>();
foreach (Person p in personitas)
{
if (p.Age >= 20 && p.Age <= 40 && p.LastName.Length > 0)
personitas1.Add(p);
}
Console.WriteLine(personitas1);
No está mal, pero es un poco tedioso escribir todo eso.
Podemos simplificarlo si usamos el método extensor FindAll
de Linq aplicándolo a la lista, que toma una expresión lambda para decidir qué elementos encuentra y filtra:
List<Person> personitas1 = personitas.FindAll(p => p.Age >= 20 && p.Age <= 40 && p.LastName.Length > 0);
Aún así demasiado largo.
Podemos simplificarlo gracias a la coincidencia de patrones de C#, que está disponible a partir dela versión 7 del lenguaje y que usa el operador is
. Estas expresiones nos permiten comparar un objeto con un patrón para el mismo, siendo más cortos, más claros y ahorrándonos escribir innecesariamente.
Se trata de definir cómo queremos que sea el objeto con el que queremos compararnos, por ejemplo así:
List<Person> personitas1 = personitas.FindAll(p => p is {
Age: >= 20,
Age: <= 40,
LastName.Length: >0
});
De hecho, lo podemos simplificar usando operadores simples como and
, or
o not
en la expresión de cada propiedad:
List<Person> personitas1 = personitas.FindAll(p => p is
{
Age: >= 20 and <= 40,
LastName.Length: >0
});
Esto está muy bien, pero tiene algunas limitaciones, y es que solo podemos emplear constantes para los valores y, además, sólo podemos usar las propiedades y las propiedades de éstas, pero no sus métodos. Así, por ejemplo, si quisiésemos filtrar de este modo a aquellas personas cuyo apellido termine en "ez", nos daría un error:
List<Person> personitas1 = personitas.FindAll(p => p is
{
Age: >=20 and <=40,
LastName.Length: > 0,
p.LastName.EndsWith("ez"): true
});
¡Esto no es válido!
Tendríamos que combinar la coincidencia de patrones con una condición convencional:
List<Person> personitas1 = personitas.FindAll(p => p is {
//Age: >= 20,
//Age: <= 40,
Age: >= 20 and <= 40,
LastName.Length: >0
}
&&
p.LastName.EndsWith("ez"));
También podemos utilizar propiedades complejas en la coincidencia de patrones. Por ejemplo, supongamos que le añadimos una propiedad Manager
de tipo Person
para saber de quién dependen en una organización:
class Person
{
public string Name { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
public Person Manager;
}
List<Person> personitas = new List<Person>();
personitas.Add(new Person { Name = "Juan", LastName = "Perez", Age = 20 });
personitas.Add(new Person { Name = "Elvira", LastName = "García", Age = 24, Manager = personitas[0] });
personitas.Add(new Person { Name = "Pedro", LastName = "González", Age = 30, Manager = personitas[0] });
personitas.Add(new Person { Name = "Maria", LastName = "", Age = 40, Manager = personitas[1] });
personitas.Add(new Person { Name = "José Antonio", LastName = "Español", Age = 50 });
Ahora podríamos hacer filtros como estos:
List<Person> personitas3 = personitas.FindAll(p => p is {
Manager.LastName: "Perez"
});
Console.WriteLine(personitas3);
Pero mucho cuidado con esto ya que tiene sus limitaciones:
- Como ya he dicho antes, solo se permiten constantes o literales en el patrón, así que esto daría un error de sintaxis:
string lnManager = "Perez"
List<Person> personitas3 = personitas.FindAll(p => p is {
Manager.LastName: lnManager
});
Obteniendo un error ya en tiempo de edición:
- Lo de usar propiedades de las propiedades en la expresión sólo funciona con C# 10 o posterior, o sea, con .NET 6 o posterior. Si lo intentamos con una versión más antigua nos dará un error, pero podemos saltarnos la limitación anidando la coincidencia de patrones, así:
List<Person> personitas3 = personitas.FindAll(p => p is {
Manager: {LastName: "Perez"}
});
Console.WriteLine(personitas3);
En resumen
La coincidencia de patrones para propiedades de objetos es una técnica muy útil para ciertos casos que nos permite ahorrar tiempo y mejorar la claridad de lo que estamos haciendo. Aprende a sacarles partido y a utilizarlos en tu código a partir de ahora.
Y si quieres aprenden C# y .NET en serio y no "con alfileres" para no perderte cosas importantes como estas y ser un buen profesional, deberías estudiar con nuestro curso de la Desarrollo con la plataforma .NET y C#
¡No te arrepentirás!