La cláusula where,
representa el elemento de filtrado dentro del mundo de LinQ, al igual que en el lenguaje SQL. Esta es una de las más usadas y de las más útiles de todas
las que componen su librería de métodos
extensores (System.Linq).
La parte más importante dentro de la cláusula Where, es el llamado ‘Predicate’, este es un Parámetro de
tipo Func<TSource, bool>
que viene a indicar la condición del filtrado. Os facilito la entrada
del blog donde se explican en detalle los delegados
anónimos Func y donde podéis ampliar información sobre estos tipos.
Recuerda que aquí tienes el indice de todos los posts del Curso de LinQ.
Predicate
(predicado), es simplemente una expresión que toma un elemento del mismo tipo
de la colección que estamos filtrando y devuelve un true o un false,
según el criterio del mismo.
Para entenderlo mejor, vamos a ver cómo sería el método
extensor Where. Sobra decir
que esto no tiene nada que ver con el código utilizado por el equipo de Microsoft para crear el original.
Como podemos observar, el código es realmente sencillo,
simplemente recorremos la colección (leída implícitamente mediante la inferencia de tipos desde IEnumerable<T> sobre el que se
aplica el método. Más info aquí
sobre los métodos extensores y aquí
de la inferencia de tipos)
y evaluamos cada uno de sus elementos para construir un iterador con cada una de las piezas que cumplan el criterio
solicitado.
Como veis, estoy haciendo uso de un nuevo método extensor
para no repetir código, ToConsole:
Resultado del ejemplo:
Usando un método externo de evaluación
Una de las ventajas que nos ofrece el uso de las clases de LinQ en general, es que podemos
utilizar el 100% de nuestro código y del código disponible dentro del CLR. Cuando
indicamos un delegado Func<TSource,
bool> no debemos ceñirnos únicamente a indicar condiciones ‘en línea’, podemos llamar a
funciones (tanto nuestras como del Framework) o a la combinación de ambas. Esto
quiere decir que podemos utilizar cualquier tipo de función que reciba como
parámetro un tipo T, no tiene por
qué ser un método genérico, puede estar limitado para un solo tipo de datos, significando
que solo nos serviría para utilizar como condición del método Where con colecciones (IEnumerable) de ese tipo y no para
la totalidad. Por supuesto el método tendrá que devolver un bool.
Un método genérico válido sería el siguiente:
Y así podríamos consumirlo, considerando el ejemplo
anterior:
Este ejemplo lo podríamos utilizar con cualquier tipo de
colección, pero si nos limitamos al ejemplo anterior también podríamos utilizar
un método que recibiera un objeto de tipo string,
ya que nuestra colección es de este tipo de datos, aunque evidentemente solo
nos valdría para IEnumerables<string>
o tipos derivados:
Y así lo consumiríamos:
Podríamos utilizar un mix de todas sin problemas:
Filtrando por índice de posición
Normalmente no hay ninguna diferencia en cuanto a
posibilidades si optamos por realizar una consulta de LinQ mediante el uso de los operadores
de consulta o las expresiones lambda,
pero en esta ocasión, tenemos un caso que no cubre la opción del azúcar sintáctico y que está
reservada solo para el uso de la las lambdas y del índice de posición.
El índice de posición consiste en el añadido de un parámetro
int adicional para nuestro delegado genérico o Predicate, quedando así Func<T, int, bool>. Traducido,
quiere decir que almacenará un método con 2 parámetros, uno de tipo T referente al objeto que está
recorriendo y otro de tipo int
referente al índice o posición de este objeto, siendo obligatorio que la
función devuelva un true
o un false.
Esta sería la firma del método extensor Where con índice de
posición:
Esto sería útil, para por ejemplo según nuestro primer
ejemplo pintar los nombres que se encuentran en una posición par:
Resultado:
En este caso para formar la lambda ( n, i ) => , al tener más de un parámetro hemos tenido que añadir los paréntesis, ya que en este caso son obligatorios. Par más
información os dejo la entrada
donde explico las lambdas.
A tener en cuenta
Es importante recalcar, que cuando utilizamos el método extensor Where, el resultado siempre
va a ser un conjunto de datos (IEnumerable<T>),
por mucho que nosotros como programadores sepamos que el resultado de la
consulta va a ser un único elemento, nuestro resultado será un (IEnumerable<T>, List<T>, T[] … ) compuesto de un objeto.
Estas consultas serían erróneas:
Esto lo solucionaremos más adelante con los métodos
extensores: First, FirstOrDefault, Single y SingleOrDefault.
Frikada
Como frikada os dejo el código real del método Where:
Como podemos apreciar, ellos ya tienen en cuenta el tipo de
datos para mantenerlo y no realizar conversiones innecesarias. El código de las
clases internas WhereArayIterator,
etc, ya no lo pongo porque si no, no acabaríamos nunca.
Pues hasta aquí el método extensor Where, con el que ya
hemos empezado la chicha de verdad, ahora a continuar con ello.
No hay comentarios :
Publicar un comentario