.NET 6 - Novas extensões da LINQ
![]() |
Hoje vou apresentar as novas extensões da LINQ incluídas no .NET 6. |
A LINQ recebeu alguma atenção durante o desenvolvimento do .NET 6 e várias novas extensões foram adicionadas.
![]() ![]() |
Esses recursos foram incluídos no .NET 6 preview 4, portanto, para usar essas novas extensões, você precisa instalar esta versão do .NET 6 ou mais recente. E para ter uma melhor experiência do desenvolvedor, você também precisa da versão mais recente do Visual Studio 2022.
1- Suporte de índice com ElementAt
O C# 8 introduziu índices que podem ser usados para indexar em listas e matrizes a partir do final, como:
var lista = new string[] { "Net6", "LINQ", "2021", "Novidades", "VS 2022"};
Console.WriteLine(lista[^1]); //Exibe 'VS 2022'
|
Veja o meu artigo - C# 8.0 - Apresentando Indices e Ranges para mais detalhes.
Os índices eram exclusivos para coleções
como arrays e listas e isso faz sentido, pois é fácil encontrar o enésimo
elemento no final quando você sabe o tamanho de uma coleção.
Mas agora, também podemos fazer isso para qualquer coleção
IEnumerable usando ElementAt(Index index).
Para isso é feita uma verificação se o tipo subjacente tem um tamanho fixo, e se disponível, ele será usado. Internamente é usado o método TryGetNonEnumeratedCount para tentar obter a contagem sem enumerar.
Assim para obter algo do final de uma coleção, antes você tinha que calcular o comprimento da coleção e, em seguida, obter o item por esse índice, subtraindo conforme necessário:
var ultimoElemento = lista.ElementAt(lista.Count() - 1);
|
Com o .NET 6, a LINQ agora permite que você use uma sobrecarga de índice para o método ElementAt da seguinte maneira:
var ultimoElemento = lista.ElementAt(^1);
|
Assim estamos usando os recursos já existente no C# para capturar elementos a partir do final de uma coleção.
2- Take com suporte a Range
Com base na ideia anterior de usar índices para melhorar o código, o método Take foi expandido para funcionar com um parâmetro Range:
var
intervalo1 = lista.Take(2..3);
|
Este código pula até o 2º elemento e, em seguida, pega as 3 entradas seguintes.
Outras expressões de intervalo, como
..3 ou 2.. também funcionam, o que expande
significativamente sua capacidade de capturar elementos de coleções por índices
específicos.
3- Sobrecarga do
parâmetro Zip
Anteriormente, o LINQ oferecia um método Zip que permitia enumerar por meio de duas coleções em paralelo:
string[] titulos = { "Missão Impossivel", "O quarto da morte", "Orgulho e preconceito" }; string[] generos = { "Drama", "Horror", "Romance" };
foreach ((string titulo, string genero) in titulos.Zip(generos)) |
Resultado:
Esse recurso é fantástico pois evita que
tenhamos que criar tipos anônimos ou nomeados adicionais para percorrer várias
coleções.
No entanto, existem alguns casos em que você pode desejar enumerar por meio de
três coleções juntas.
Para atender a essa necessidade, a LINQ agora tem uma sobrecarga adicional que permite que três coleções trabalhem em conjunto:
string[] titulos = { "Missão Impossivel", "O quarto da morte", "Orgulho e preconceito" }; string[] generos = { "Drama", "Horror", "Romance" }; float[] avaliacoes = { 5f, 3.5f, 4.5f };
foreach ((string titulo, string genero, float avaliacao) in titulos.Zip(generos, avaliacoes)) Console.Read(); |
Resultado:
Aqui temos que um é normal , dois é bom
e três é melhor...
4- Parâmetros padrão para métodos comuns
Quem nunca usou os métodos FirstOrDefault, SingleOrDefault e LastOrDefault da LINQ ? Eles são como pilares da LINQ.
Esses métodos examinarão uma coleção e retornarão uma correspondência se uma condição for atendida.
Se uma condição não for atendida, o valor padrão para esse tipo será usado.
Por exemplo, digamos que vamos tentar obter o primeiro filme que incluiu um autor em seu elenco:
Filme filme = filmes.FirstOrDefault(m => m.Elenco.Includes("Macoratti")); |
Como eu nunca estive em um filme, FirstOrDefault iria retornar o seu valor padrão e o filme seria definido como null.
No entanto, no .NET 6, a LINQ agora permite que você especifique um parâmetro personalizado a ser usado caso nada corresponda à condição.
Isso evita ter que lidar com valores nulos e, em vez disso, especifica uma alternativa segura, conforme ilustra o código a seguir:
Filme filme = filmes.FirstOrDefault(m => m.Elenco.Includes("Macoratti"), valorPadrao); |
Nesse caso, quando FirstOrDefault não
atingir uma correspondência, ele usará o parâmetro
valorPadrao.
Essa mudança também se aplica a SingleOrDefault e
LastOrDefault de maneiras semelhantes, onde agora você pode especificar
um parâmetro valorPadrao personalizado.
A seguir temos um exemplo bem simples onde temos uma lista de números e vamos retornar -1 caso FirstOrDefault não encontre a correspondência pretendida:
List<int> numeros = new List<int> { 1, 2, 3, 4, 5 };
var resultado = numeros.FirstOrDefault(x => x > 5, -1);
|
5- MaxBy and MinBy
Finalmente, o .NET 6 oferece os métodos de extensão MinBy e MaxBy na LINQ.
Esses dois métodos permitem que você
percorra a sua coleção e encontre o maior ou o menor de algo, com base em
uma expressão lambda fornecida por você.
Antes do .NET 6, para obter a entidade que tinha o maior ou o menor de algo,
você teria que usar Max ou Min para encontrar o
valor e, em seguida, consultar novamente a coleção para encontrar a entidade relacionada:
int numBatalhas = filmes.Max(m => m.BatalhasEspaciais); Filme ficcao = filmes.First(m => m.BatalhasEspaciais == numBatalhas); |
Este código funciona, mas é preciso 2 linhas
de código em vez de 1, e enumera a coleção várias vezes.
Com os métodos de extensão LINQ MinBy e MaxBy do
.NET 6, agora podemos fazer isso mais rapidamente em uma única linha de código:
Filme ficcao = filmes.MaxBy(m => m.BatalhasEspaciais);
|
A mesma lógica se aplica ao método de extensão MinBy.
Assim, com essas melhorias , codar com
LINQ ficou mais fácil e simples.
"Sei estar abatido,
e sei também ter abundância; em toda a maneira, e em todas as coisas estou
instruído, tanto a ter fartura, como a ter fome; tanto a ter abundância,
como a padecer necessidade.
Posso todas as coisas em Cristo que me fortalece."
Filipenses 4:12,13
Referências: