2

LINQ para objetos PARTE I

#C#
Willams Sousa
Willams Sousa

LINQ para objetos PARTE I


Introdução

Todo programa precisa eventualmente atuar sobre uma coleção de dados. Os dados geralmente são armazenados na memória, no disco rígido ou no banco de dados, ele pode estar armazenado em arquivos txt, xml ou json ou podem ser recuperados diretamente do banco de dados. Para trabalhar com esses dados temos que usar bibliotecas diferentes para cada situação. A partir do .NET 3.5 a tecnologia LINQ está disponível para uso na qual fornece uma maneira unificada para trabalhar com dados. LINQ é um acrônimo para Language INtegrated Query (linguagem integrada de consulta numa tradução livre) e, como o próprio nome diz, está integrado na linguagem C#.

Neste texto vamos ver o que é LINQ entendendo seus benefícios e aprendendo sua sintaxe em diferentes situações. Na primeira parte faremos uma revisão de alguns tópicos importantes que são usados como base pelo LINQ e teremos uma visão geral do LINQ com alguns exemplos aplicados em um array. Na segunda parte veremos os operadores LINQ com muitos exemplos de como usá-los.

Conceitos Básicos

Antes de começarmos com LINQ é importante revisar alguns conceitos básicos dos quais são usados intensivamente pela tecnologia do LINQ. Esses conceitos são:

·        Tipos implícitos usando a palavra reservada var

·        Sintaxe para inicialização de objetos ou coleções

·        Expressões lambda

·        Métodos de extensão

·        Tipos anônimos

Tipos implícitos usando a palavra reservada var

var myInt = 10;

// string myString = "Hello";

var myString = "Hello";




// Func<int, string, ISomeThingCool, OtherClass> myVar = Foo();

var myVar = Foo();




// função retornando tipo ‘verboso’ de digitar

Func<int, string, ISomeThingCool, OtherClass> Foo() 

{

    return new Fun<int, string, ISomethingCool, WebHostBuilderContext>();

}


Este exemplo mostra quão útil pode ser o uso de var, principalmente no último exemplo. A expressão a direita é avaliada em tempo de compilação e o compilador converte automaticamente para o tipo contido na expressão. Tipos implícitos são muito usados quando obtemos o resultado de uma query no LINQ e em alguns casos é obrigatório seu uso.


Inicialização de objetos ou coleções

Podemos iniciar objetos na mesma linha que instanciamos ele como no seguinte exemplo:

var usuarios = new List<Usuario>
{

    new Usuario {Nome = "João", Email= "j@j.com", new Endereco { Rua="7 de setembro", Numero = "191" },

    new Usuario {Nome = "Fernanda", Email= "f@f.com", new Endereco { Rua="Cairo", Numero = "654" },

    new Usuario {Nome = "Pedro", Email= "p@p.com", new Endereco { Rua="General Osório", Numero = "1232" },

    new Usuario {Nome = "Aline", Email= "a@a.com", new Endereco { Rua="St. Amaro", Numero = "27" },

    new Usuario {Nome = "Marcos", Email= "m@m.com", new Endereco { Rua="Alamedas", Numero = "321" }

};


Expressões lambda

O Formato de uma expressão lambda é o seguinte:

(paramentros) => (expressão)

Por exemplo:

// cria uma lista de inteiros
List<int> list = new List<int>();

list.AddRange(new int[] { 20, 1, 4, 8, 9, 44 });

// uso de expressão lambda

List<int> evenNumbers = list.FindAll(i => (i % 2) == 0);


O método:

public System.Collections.Generic.List<T> FindAll (Predicate<T> match);

Sem expressões lambda, seria usado de maneira mais trabalhosa, acompanhe:

Predicate<T> predicate = IsEven;

List<int> evenNumbers = list.FindAll(predicate);


static bool IsEven(T obj)
{

    return ((int)obj % 2) == 0;

}


Métodos de extensão

Os métodos de extensão (Extension Methods) é um recurso curioso que permite adicionar novos métodos para uma classe sem precisar ter acesso direto ao código. Por exemplo:

namespace MyExtensionMethods
{
    static class MyExtensions
    {
        public static void DisplayHello(this object obj)
        {
            Console.WriteLine("Hello!");
        }

        public static int ReverseDigits(this int i)
        {
            char[] digits = i.ToString().ToCharArray();

            Array.Reverse(digits);

            string newDigits = new string(digits);

            return int.Parse(newDigits);
        }
    }
}


Agora todos os int e todos os object (e seus derivados) possuem os métodos DisplayHello e ReverseDigits. Podemos usar os novos métodos como se eles fossem originais desses tipos:

namespace ExtensionMethods
{
    class Program
    {
        static void Main(string[] args)
        {
            int myInt = 0123456789;

            myInt.DisplayHello(); // int é também um objeto

            Console.WriteLine($"{myInt.ReverseDigits()}");
        }
    }

}


Este é um recurso que pode se tornar confuso, pois é possível fazer isso com qualquer classe.

Tipos Anônimos

Os tipos anônimos são classes não nomeadas, um exemplo vai ajudar entender melhor:

var item = new {

   DataDaCompra = DateTime.Now,

   ItemComprado = new 
   {
     Cor = "Azul", Fabricante = "Cool Company", Tamanho = 55
   }, Preco = 34.000

};



Temos o tipo anônimo em Item cujo qual também possui uma propriedade anônima ItemComprado. Sem o recurso de tipos anônimos seria necessário criar a classe Item e a classe ItemComprado descrevendo suas propriedades antes poder instanciar esses objetos. Este é um recurso muito usado com LINQ.

Visão geral de LINQ

A maior parte de nosso tempo como programadores é gasto manipulando dados. Quando pensamos em dados a primeira vista nos lembramos de banco de dados, mas não é apenas em banco de dados. Lidamos com dados em xml, em arquitetos textos comuns e também em objetos como array genérico List<T>. Muitas vezes precisamos trabalhar com dados em um array como, por exemplo, selecionar apenas os números pares ou primos em uma lista de inteiros. Antes do .NET 3.5 era necessário usar APIs diferentes para cada tipo de dados. Para dados em bancos relacionais usávamos System.Data.dll e System.Data.SqlClient.dll, etc.. Para dados em arquivos XML usávamos System.Xml.dll, para coleções de objetos usávamos System.Array e System.Collections/System.Collections.Generic. Apesar de não ter nada de errado com estas APIs, LINQ permite maior consistência quando operando nesses dados, pois fornece uma uma maneira simétrica de lidar com todos estes tipos de dados permitindo a construção de consultas (query expressions) diretamente na linguagem C#. Os operadores LINQ lembra a linguagem SQL, mas não são a mesma coisa. LINQ é uma maneira genérica para descrever LINQ to Object, LINQ to XML, LINQ to Entities Parallel LINQ ou PLINQ. Usamos LINQ a partir do namespace System.Linq.

Usando LINQ em Arrays

using System;
using System.Collections.Generic;
using System.Linq;

string[] filmes = {"Matrix", "Sherek 2", "Alien 3", "Zumbilândia", "Exterminador do Futuro 2"};

IEnumerable<string> subset = from f in filmes where f.Contains(" ") orderby f select f;
      

foreach (string s in subset) 

    Console.WriteLine("Item: {0}", s);


OBS: As novas versões do C# (acredito que a partir da 7.1) permite compilar e executar programas sem declarar uma classe. O trecho acima pode ser compilador e executado do jeito que está com css linq-teste.cs

No código acima criamos um subset com todos os filmes que contém espaço no nome. As palavras from, in, where, orderby e select são operadores do LINQ. O tipo retornado implmenta a interface IEnumable<T>. Podemos usar métodos de extensão também e usar tipos implícitos, p.ex.:

var s = filmes.Where(f=>f.Contains(" ")).OrderBy(f=>f).Select(f=>f);

Para percebermos como LINQ é útil, basta ver a versão sem ele do mesmo exemplo:

using System;
using System.Collections.Generic;
using System.Linq;

string[] filmes = {"Matrix", "Sherek 2", "Alien 3", "Zumbilândia", "Exterminador do Futuro 2"};
          

//var subset = from f in filmes where f.Contains(" ") orderby f select f;
string[] subset = new string[5];


for (int i = 0; i < filmes.Length; i++)
{
    if (filmes[i].Contains(" "))
    {
        subset[i] = filmes[i];
    }
}

foreach (string s in subset) 

    if(s != null) Console.WriteLine("Item: {0}", s);


Impressionante a economia de código que temos usando LINQ.

A expressão LINQ só é executada quando iteramos sobre o resultado. Se quisermos ter a expressão avaliada no exato momento que ela é declara temos que usar ToArray<T> ou ToList<T> no resultado. Por exemplo:

using System;
using System.Collections.Generic;
using System.Linq;

int[] numbers = { 10, 20, 30, 40, 1, 2, 3, 8 };

// a expressão ainda não é avaliada
var subset = from i in numbers where i < 10 select i;


// modifica antes de avaliar
numbers[0] = 45;


// é avaliado agora, com numbers[0] == 45;
foreach (var i in subset) Console.WriteLine(i);


// avalia agora mesmo a expressão e obtém como Array<int>
int[] subsetAsIntArray = (from i in numbers where i < 10 select i).ToArray<int>();


// avalia agora mesmo a expressão e obtém como List<int>

List<int> subsetAsListOfInts = (from i in numbers where i < 10 select i).ToList<int>();


É isso, na parte II daremos foco nos operadores e teremos muitos exemplos de uso...


1
53

Comentários (1)

0
Afonso Simão

Afonso Simão

30/01/2021 17:06

Excelente artigo Williams.

Parabéns!

Tento ser um sujeito legal e gosto de aprender linguagens de programação.

Brasil