Desafio C#
Relatório de vendas com execução adiada
Enunciado
Crie um programa que demonstre a execução adiada do LINQ. Dada uma lista de vendas, monte uma consulta que filtre vendas acima de um valor mínimo e projete o nome do vendedor e o valor. Antes de iterar, adicione uma nova venda que atenda ao filtro. Exiba o resultado e comprove que a nova venda aparece. Em seguida, use GroupBy para agrupar por vendedor e mostrar o total, ordenado pelo total decrescente.
Requisitos
- Monte a consulta LINQ sem executá-la imediatamente (sem
ToListantes da modificação). - Adicione uma nova venda à lista original após definir a consulta.
- Exiba os resultados da consulta incluindo a nova venda.
- Use
GroupBypara agrupar por vendedor e exibir total ordenado decrescentemente.
Código inicial
using System;
using System.Linq;
using System.Collections.Generic;
class Venda
{
public string Vendedor { get; set; }
public decimal Valor { get; set; }
}
class Program
{
static void Main()
{
var vendas = new List<Venda>
{
new Venda { Vendedor = "Ana", Valor = 100 },
new Venda { Vendedor = "Bruno", Valor = 200 },
new Venda { Vendedor = "Ana", Valor = 150 },
new Venda { Vendedor = "Carlos", Valor = 300 }
};
// 1. Crie a consulta para vendas > 120 sem executar
// 2. Adicione uma nova venda (ex.: "Diana", 250)
// 3. Exiba os resultados da consulta
// 4. Agrupe por vendedor e exiba total ordenado decrescentemente
}
}
Saída esperada
Vendas acima de R$ 120,00:
Bruno: R$ 200,00
Ana: R$ 150,00
Carlos: R$ 300,00
Diana: R$ 250,00
Total por vendedor (decrescente):
Carlos: R$ 300,00
Diana: R$ 250,00
Bruno: R$ 200,00
Ana: R$ 250,00
Ver dica
A consulta Where é adiada; se você não chamar ToList() antes de adicionar a venda, o novo item será incluído ao iterar. Para o agrupamento, use GroupBy e Sum.
Mostrar solução
using System;
using System.Linq;
using System.Collections.Generic;
class Venda
{
public string Vendedor { get; set; }
public decimal Valor { get; set; }
}
class Program
{
static void Main()
{
var vendas = new List<Venda>
{
new Venda { Vendedor = "Ana", Valor = 100 },
new Venda { Vendedor = "Bruno", Valor = 200 },
new Venda { Vendedor = "Ana", Valor = 150 },
new Venda { Vendedor = "Carlos", Valor = 300 }
};
// 1. Consulta adiada: ainda não executa
var consulta = vendas
.Where(v => v.Valor > 120)
.Select(v => $"{v.Vendedor}: R$ {v.Valor:F2}");
// 2. Adiciona nova venda após a definição da consulta
vendas.Add(new Venda { Vendedor = "Diana", Valor = 250 });
// 3. Executa a consulta (inclui Diana)
Console.WriteLine("Vendas acima de R$ 120,00:");
foreach (var item in consulta)
Console.WriteLine(item);
// 4. Agrupamento por vendedor com total
var totais = vendas
.GroupBy(v => v.Vendedor)
.Select(g => new { Vendedor = g.Key, Total = g.Sum(v => v.Valor) })
.OrderByDescending(x => x.Total);
Console.WriteLine("\nTotal por vendedor (decrescente):");
foreach (var t in totais)
Console.WriteLine($"{t.Vendedor}: R$ {t.Total:F2}");
}
}
Passo a passo
- Criamos a lista de vendas com quatro registros.
- Definimos
consultausandoWhereeSelect, mas sem chamarToList— a execução fica adiada. - Adicionamos uma nova venda (
Diana, 250) à lista original. - Ao iterar com
foreach, a consulta é executada e inclui a nova venda, comprovando a execução adiada. - Para o agrupamento, usamos
GroupBypor vendedor, projetamos comSelecte somamos os valores. - Ordenamos os totais com
OrderByDescendinge exibimos.
Por que funciona
A execução adiada é possível porque os operadores LINQ retornam um IEnumerable<T> que armazena a lógica da consulta, não os dados. Quando o foreach chama GetEnumerator(), a consulta é avaliada sobre a fonte no estado atual — por isso a venda adicionada depois aparece. Já GroupBy também é adiado, mas ao iterar sobre os grupos, todos os dados são processados.
Erros comuns
- Chamar
ToList()antes de adicionar a venda: a consulta seria materializada e a nova venda não apareceria. - Modificar a lista durante a iteração (ex.: adicionar dentro do
foreach): lançaInvalidOperationException. - Esquecer
using System.Linq;: os métodos de extensão não são reconhecidos.
Outra forma de resolver
Usar sintaxe de consulta para o agrupamento:
var totais = from v in vendas
group v by v.Vendedor into g
orderby g.Sum(x => x.Valor) descending
select new { Vendedor = g.Key, Total = g.Sum(x => x.Valor) };
É preferível quando a consulta tem muitas cláusulas e fica mais próxima de SQL.
Saída esperada
Vendas acima de R$ 120,00:
Bruno: R$ 200,00
Ana: R$ 150,00
Carlos: R$ 300,00
Diana: R$ 250,00
Total por vendedor (decrescente):
Carlos: R$ 300,00
Diana: R$ 250,00
Bruno: R$ 200,00
Ana: R$ 250,00