1. Introdução
Filtrar é selecionar da coleção só aqueles elementos que passam por uma condição. Imagina: na sua base tem 10 000 produtos, mas o gerente quer “só” 12 que batem com um critério novo e maluco. Escrever um loop foreach toda vez e um monte de if — ninguém merece, e o código fica horrível. LINQ resolve esse perrengue pra você.
Na vida real, filtrar é a operação mais comum com dados: mostramos pro usuário só os registros que ele quer, “limpamos” a coleção de itens indesejados, fazemos seleções pra relatórios, buscas ou envio de e-mail. Em entrevistas, perguntas sobre filtragem com LINQ aparecem direto. Entender esse assunto é essencial pra quem tá começando com .NET.
2. Conhecendo o método Where
O que é e como funciona
O método Where é um método de extensão do LINQ que recebe uma função (ou expressão lambda) que define a condição de filtragem. Ele retorna uma nova sequência (sem mudar a original!), só com os elementos pra quem a condição retorna true.
Assinatura principal:
public static IEnumerable<TSource> Where<TSource>(
this IEnumerable<TSource> source,
Func<TSource, bool> predicate)
Não se assusta com generics e nomes estranhos. O importante agora é sacar a ideia: Where recebe a coleção original e a condição que você manda. Na prática é assim:
- source — a coleção que vamos filtrar (tipo List<Product>)
- predicate — a função/condição (tipo p => p.Price > 100)
Um exercício simples pra começar
Digamos que temos uma lista de produtos e queremos só os que custam mais de 100. Antigamente, a gente fazia um loop foreach e adicionava manualmente os itens certos numa nova lista. Com LINQ, dá pra fazer isso em uma linha.
3. Filtragem
Nosso class Product e a coleção inicial
No módulo passado, a gente fez uma classe de produtos mais ou menos assim:
class Product
{
public string Name { get; set; }
public double Price { get; set; }
// Pra deixar a saída bonitinha:
public override string ToString()
{
return $"{Name} (preço: {Price})";
}
}
Digamos que temos uma lista de produtos:
var products = new List<Product>
{
new Product { Name = "Pão", Price = 30 },
new Product { Name = "Leite", Price = 87 },
new Product { Name = "Queijo", Price = 250 },
new Product { Name = "Chocolate", Price = 130 }
};
Filtrando produtos acima de 100 com Where
var expensiveProducts = products.Where(p => p.Price > 100);
foreach (var product in expensiveProducts)
{
Console.WriteLine(product);
}
O que rola por trás dos panos:
- Where passa por todos os itens da lista products,
- Pra cada um, chama a função (no nosso caso: p => p.Price > 100),
- Se a função retorna true, o produto entra na nova coleção.
O que aparece na tela:
Queijo (preço: 250)
Chocolate (preço: 130)
Repara: a lista original products não mudou! LINQ não destrói seus dados.
4. Filtragem preguiçosa (Deferred Execution)
Um detalhe importante: o resultado do Where só é calculado quando você realmente acessa os itens. Por exemplo, quando começa o loop foreach, o LINQ “anda” pela coleção original e filtra os itens na hora. Se você não usar o resultado, nada acontece. Isso se chama execução preguiçosa (deferred execution).
Isso traz várias vantagens:
- Dá pra montar cadeias enormes de métodos (tipo filtrar e ordenar), e só quando você realmente pede o resultado é que tudo roda.
- Economiza memória: não cria coleções intermediárias à toa.
- Se precisar, dá pra cancelar a execução ou “parar” depois de achar o tanto de itens que você queria.
Esquema visual
Coleção original —► Where (condição) —► Só passamos pelos itens certos
[ x ] [ x ] [ + ] [ + ] —► p.Price > 100 —► [ + ] [ + ]
(x — item não passa na condição, + — passa)
5. Variações de sintaxe
Sintaxe clássica de método de extensão (Method Syntax)
Esse é o estilo que já estamos usando:
var result = products.Where(p => p.Price > 100);
Alternativa: Query Syntax
O LINQ aceita uma sintaxe parecida com SQL:
var result = from p in products
where p.Price > 100
select p;
O resultado é o mesmo!
Escolher a sintaxe é questão de gosto, mas o method syntax (com ponto) aparece mais, principalmente em projetos reais e quando você faz cadeias longas de métodos.
6. Condições de filtragem mais complexas
Várias condições
Você pode usar operadores lógicos (&&, ||, !):
// Achar todos os produtos caros, menos "Chocolate"
var filtered = products.Where(
p => p.Price > 100 && p.Name != "Chocolate"
);
Filtrando por string (substring, case, Contains)
// Produtos cujo nome tem a letra "l"
var lProducts = products.Where(p => p.Name.Contains("l"));
Atenção: Contains diferencia maiúsculas de minúsculas! Se quiser ignorar o case, faz assim:
var lProducts = products.Where(p => p.Name
.ToLower().Contains("l")); // agora "Leite" e "Chocolate" entram na seleção
Filtrando com várias coleções
Digamos que você tem duas listas — produtos e pagamentos. Dá pra filtrar produtos que aparecem na lista de pagamentos — mas aí é melhor usar os métodos Any e Join, que a gente vai ver depois. Só pra saber: o LINQ filtra de jeitos bem mais avançados também!
7. Dicas úteis
Filtragem aninhada e cadeias de métodos
Você pode juntar várias etapas de filtragem, chamando Where várias vezes:
var filtered = products
.Where(p => p.Price > 100)
.Where(p => p.Name.StartsWith("C"));
Mas é melhor juntar as condições num só Where usando operadores lógicos — fica mais eficiente e o código mais limpo.
Comparadores prontos e funções customizadas
Às vezes os operadores padrão não bastam. Por exemplo, pra filtrar strings ignorando case e cultura. Aí é bom usar métodos de comparação:
var rusProducts = products.Where(
p => p.Name.StartsWith("c", StringComparison.OrdinalIgnoreCase)
);
8. Filtragem e entrada do usuário
Bora fazer uma filtragem simples no console! Por exemplo, perguntar pro usuário o preço mínimo e mostrar os produtos que batem.
Console.Write("Preço mínimo do produto? ");
var minPriceStr = Console.ReadLine();
if (double.TryParse(minPriceStr, out double minPrice))
{
var filtered = products.Where(p => p.Price >= minPrice);
foreach (var product in filtered)
{
Console.WriteLine(product);
}
}
else
{
Console.WriteLine("Erro: não foi digitado um número.");
}
Agora nosso "mini-app" já tá quase parecendo uma lojinha!
9. Erros comuns e armadilhas ao usar Where
Na programação, como na vida, tem umas pegadinhas. Olha só o que evitar.
Esqueceu de chamar ToList() ou ToArray()
O resultado do Where não é uma lista nem um array! É um objeto IEnumerable<Product>. Na maioria das vezes, funciona de boa no foreach, mas se você precisa de uma coleção de verdade (tipo pra acessar por índice), não esquece de chamar .ToList() ou .ToArray():
var filteredList = products.Where(p => p.Price > 100).ToList();
Se esquecer disso, pode dar erro tipo "Collection was modified during enumeration" — principalmente se você tentar mudar a coleção original durante o loop.
Mudando a coleção original
Como a filtragem do LINQ é preguiçosa, se você apagar itens da lista original no meio do loop, pode rolar exceção. Isso é ainda mais perigoso em cenários com várias threads ou quando a coleção muda durante a filtragem.
Valores null e predicados
Se a coleção tem null e você tenta acessar um campo do objeto sem checar, vai tomar um NullReferenceException. Tipo:
var filtered = products.Where(p => p.Name.StartsWith("A"));
Se algum item em products for null, o código quebra.
Dica: sempre coloca uma checagem de null se tiver chance de aparecer:
var filtered = products.Where(p => p != null && p.Name.StartsWith("A"));
GO TO FULL VERSION