Los Quantifier
Operators, son un grupo bastante especial de métodos extensores que
devuelven un objeto de tipo bool.
Este tipo de métodos son en ocasiones obviados, normalmente por su
desconocimiento y sustituidos por la unión del uso del método Where() + Count().
Este suele ser un error común de uso, ya que los Quantifier Operators están
optimizados para este fin, y la ganancia de rendimiento con su uso es más que
evidente.
Más adelante haremos hincapié en ello con unos ejemplos que expongan
este manifiesto.
Recuerda que aquí tienes el indice de todos los posts del Curso de LinQ.
Operador All
El operador All,
comprueba que todos los elementos de una secuencia, cumplan un determinado
criterio. Este criterio se especifica mediante el mismo tipo que el operador Where, y no es otro que Func<TSource, bool>. Así quedaría la firma:
public static bool All<TSource>(this IEnumerable<T> source, Func<TSource, bool> predicate)
Vamos a por un ejemplo completo:
static void Main(string[] args)
{
string[] nombres = { "Luis", "Pablo", "Susana", "María" };
Console.WriteLine("¿ Empiezan todos por a ?{0}", Environment.NewLine);
bool empiezanTodosPorA = nombres.All(n => n.StartsWith("a"));
string respuesta = empiezanTodosPorA ? "SI" : "NO";
Console.WriteLine(" - {0}", respuesta);
Console.Read();
}
Vamos a ver el resultado:
Operador Any
El operador Any,
a diferencia del operador All,
contiene 2 sobrecargas. La primera de ellas, en la cual no pasaremos ningún parámetro, simplemente comprueba que
una secuencia no esté vacía, ósea que contenga algún elemento. La segunda
sobrecarga es muy similar a la de All,
y recibe el mismo tipo de parámetro Func<TSource,
bool> , y verifica que algún elemento de la secuencia cumpla con
este criterio:
public static bool Any<TSource>(this IEnumerable<TSource> source)
public static bool Any<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
Vamos con un ejemplo completo:
static void Main(string[] args)
{
string[] nombres = { "Luis", "Pablo", "Susana", "María" };
Console.WriteLine("¿ Está vacía ?{0}", Environment.NewLine);
bool estaVacia = !nombres.Any();
string respuestaVacia = estaVacia ? "SI" : "NO";
Console.WriteLine(" - {0}", respuestaVacia);
Console.WriteLine("{0}", Environment.NewLine);
Console.WriteLine("¿ Empieza alguno por P ?{0}", Environment.NewLine);
bool empiezaAlgunoPorA = nombres.Any(n => n.ToUpper().StartsWith("P"));
string respuestaEmpieza = empiezaAlgunoPorA ? "SI" : "NO";
Console.WriteLine(" - {0}", respuestaEmpieza);
Console.Read();
}
Con el siguiente resultado:
Operador Contains
El operador
Contains,
sirve para comprobar si la secuencia contiene un elemento
equivalente al objeto facilitado por parámetros. Utilizo la
palabra
equivalente y no
igual, porque este operador como
otros que hemos estudiado anteriormente tiene una sobrecarga en la que acepta un
IEqualityComparer<T>
en el que podemos describir esta equivalencia.
Vamos con las firmas de los métodos extensores:
public static bool Comparer<TSource>(this IEnumerable<TSource> source, TSource value)
public static bool Comparer<TSource>(this IEnumerable<TSource> source, TSource value, IEqualityComparer<TSource> comparer)
Ejemplo de la primera sobrecarga:
static void Main(string[] args)
{
string[] nombres = { "Luis", "Pablo", "Susana", "María" };
Console.WriteLine("¿ Está Susana en 'nombres' ?{0}", Environment.NewLine);
bool estaVacia = nombres.Contains("Susana");
string respuestaVacia = estaVacia ? "SI" : "NO";
Console.WriteLine(" - {0}", respuestaVacia);
Console.Read();
}
Resultado :
La segunda sobrecarga del operador
Contais, acepta un parámetro adicional de tipo
IEqualityComparer<T>.
El funcionamiento es exactamente el mismo, al de las entregas anteriores de
GroupBy
y
Join,
y no es otro que el diseñar una política de igualdad para un tipo determinado.
Para hacer el ejemplo utilizaremos una clase de las que ya
hemos utilizado en otras entradas, la clase Automovil:
public class Automovil
{
public string Marca { get; set; }
public string Modelo { get; set; }
public TipoCarburante Carburante { get; set; }
public Color Color { get; set; }
}
static void Main(string[] args)
{
var coches = new List<Automovil>
{
new Automovil { Marca = "Renault", Modelo = "Clio" , Carburante = TipoCarburante.Gasolina, Color = Colors.Green },
new Automovil { Marca = "Citroen", Modelo = "C3" , Carburante = TipoCarburante.Diesel , Color = Colors.Gray },
new Automovil { Marca = "Renault", Modelo = "Laguna", Carburante = TipoCarburante.Diesel , Color = Colors.Black },
new Automovil { Marca = "Renault", Modelo = "Megane", Carburante = TipoCarburante.Gasolina, Color = Colors.Blue },
new Automovil { Marca = "Citroen", Modelo = "C4" , Carburante = TipoCarburante.Gasolina, Color = Colors.Black }
};
var cocheAComparar = new Automovil
{
Marca = "Renault",
Modelo = "Laguna",
Carburante = TipoCarburante.Diesel,
Color = Colors.BlueViolet
};
Console.WriteLine("¿ Está 'cocheAComparar' dentro de coches ?{0}", Environment.NewLine);
bool resultado = coches.Contains(cocheAComparar, new AutomovilEqualityComparer());
string respuesta = resultado ? "SI" : "NO";
Console.WriteLine(" - {0}", respuesta);
Console.Read();
}
Resultado:
Resaltar, que de no haber indicado nuestro
IEqualityComparer<Automovil>,
el resultado hubiera sido negativo, ya que hubiera utilizado la estrategia por
defecto, y en el Framework para las clases no es otra que comparar por
referencias, y nuestro
cocheAComparar no apunta a la misma
dirección de memoria que ninguno de los elementos que forman la colección
coches.
static void Main(string[] args)
{
var coches = new List<Automovil>
{
new Automovil { Marca = "Renault", Modelo = "Clio" , Carburante = TipoCarburante.Gasolina, Color = Colors.Green },
new Automovil { Marca = "Citroen", Modelo = "C3" , Carburante = TipoCarburante.Diesel , Color = Colors.Gray },
new Automovil { Marca = "Renault", Modelo = "Laguna", Carburante = TipoCarburante.Diesel , Color = Colors.Black },
new Automovil { Marca = "Renault", Modelo = "Megane", Carburante = TipoCarburante.Gasolina, Color = Colors.Blue },
new Automovil { Marca = "Citroen", Modelo = "C4" , Carburante = TipoCarburante.Gasolina, Color = Colors.Black }
};
var cocheAComparar = new Automovil
{
Marca = "Renault",
Modelo = "Laguna",
Carburante = TipoCarburante.Diesel,
Color = Colors.BlueViolet
};
Console.WriteLine("¿ Está 'cocheAComparar' dentro de coches ?{0}", Environment.NewLine);
bool resultado = coches.Contains(cocheAComparar);
string respuesta = resultado ? "SI" : "NO";
Console.WriteLine(" - {0}", respuesta);
Console.Read();
}
Exactamente igual que el ejemplo anterior, pero sin el
parámetro comparador dentro de la llamada a la cláusula Contais:
Resultado:
Para rizar un poco más el rizo, y demostrar el tema de las
referencias, indicar que si hubiéramos instanciado nuestro cocheAComparar de la manera siguiente, la comparación si
hubiera resultado positiva:
var cocheAComparar = coches.First();
Pongo mucho ímpetu en el uso de
IEqualityComparer<T>
ya que puede ser verdaderamente práctico dentro de todo el universo
LinQ.
Uso de Where() + Count()
Tengo que reconocer que durante mucho tiempo he realizado
prácticas mediante la combinación de estos dos operadores para suplir el uso de
los que hoy nos afectan. Esta práctica la realizaba principalmente por
desconocimiento de los mismos. Con lo que hacía cosas como estas:
static void Main(string[] args)
{
var coches = new List<Automovil>
{
new Automovil { Marca = "Renault", Modelo = "Clio" , Carburante = TipoCarburante.Gasolina, Color = Colors.Green },
new Automovil { Marca = "Citroen", Modelo = "C3" , Carburante = TipoCarburante.Diesel , Color = Colors.Gray },
new Automovil { Marca = "Renault", Modelo = "Laguna", Carburante = TipoCarburante.Diesel , Color = Colors.Black },
new Automovil { Marca = "Renault", Modelo = "Megane", Carburante = TipoCarburante.Gasolina, Color = Colors.Blue },
new Automovil { Marca = "Citroen", Modelo = "C4" , Carburante = TipoCarburante.Gasolina, Color = Colors.Black }
};
var cochesNegros = coches.Where(c => c.Color == Colors.Black).ToList();
if (cochesNegros.Count > 0)
{
Console.WriteLine("Los siguientes coches son negros :{0}", Environment.NewLine);
foreach (var cocheNegro in cochesNegros)
{
Console.WriteLine("Marca: {0} - Modelo: {1} ...", cocheNegro.Marca, cocheNegro.Modelo);
}
}
var cochesCitroen = coches.Where(c => c.Marca.Equals("Citroen", StringComparison.CurrentCultureIgnoreCase)).ToList();
if (cochesCitroen.Count == coches.Count)
{
Console.WriteLine("Todos los coches son de la marca Citroen");
}
else
{
Console.WriteLine("Hay coches que NO son de la marca Citroen");
}
Console.Read();
}
Apuntar de nuevo, que este procedimiento no está
recomendado, ya que los operadores Any
y All, están optimizados para este
tipo de casuísticas.
No hay comentarios :
Publicar un comentario