Programação Funcional em C#: de A a Z
Programação Funcional (PF) é um paradigma que trata a computação como a avaliação de funções matemáticas e evita estado mutável e efeitos colaterais. Apesar de C# ser uma linguagem multiparadigma com forte suporte à Programação Orientada a Objetos (POO), suas versões modernas (a partir de C# 3.0 em diante) integram ativamente elementos do estilo funcional. Isso permite escrever código mais conciso, previsível e facilmente testável.
Neste artigo, percorreremos o caminho desde os fundamentos até as técnicas avançadas de programação funcional em C# no contexto do .NET. Você aprenderá como usar expressões lambda, LINQ, tipos de dados imutáveis e padrões para criar aplicações robustas.
1. Fundamentos: Expressões Lambda e Delegados
O coração da abordagem funcional em C# são as expressões lambda — funções anônimas que podem ser usadas para criar delegados ou árvores de expressão. Elas permitem passar comportamento como dados.
Considere o exemplo clássico de ordenação de uma lista. Em vez de criar um método de comparação separado, usamos uma lambda:
using System;using System.Collections.Generic;
public class LambdaExample{ public static void Main() { var numbers = new List<int> { 5, 3, 8, 1, 9 }; // Ordenação decrescente com expressão lambda numbers.Sort((x, y) => y.CompareTo(x)); Console.WriteLine(string.Join(", ", numbers)); // Saída: 9, 8, 5, 3, 1 }}Conceitos-chave que atuam aqui:
- Delegados
Func<>eAction<>:Func<T, TResult>retorna um valor,Action<T>não. Lambdas são implicitamente convertidas para esses tipos. - Closures (Fechamentos): Uma lambda pode capturar variáveis do escopo externo, tornando-a uma ferramenta poderosa, mas que requer cuidado devido a possíveis efeitos colaterais.
2. LINQ: Estilo Declarativo de Trabalhar com Dados
Language Integrated Query (LINQ) é, talvez, o exemplo mais marcante da aplicação de PF em C#. Ele permite escrever consultas declarativas para coleções, bancos de dados e XML, usando encadeamento de métodos.
Em vez de loops imperativos foreach, usamos operações funcionais:
using System;using System.Linq;using System.Collections.Generic;
public class LinqExample{ public static void Main() { var people = new List<Person> { new Person { Name = "Alice", Age = 30 }, new Person { Name = "Bob", Age = 17 }, new Person { Name = "Charlie", Age = 25 } };
// Consulta funcional: filtragem, projeção, ordenação var result = people .Where(p => p.Age >= 18) // Filtragem (predicado) .OrderBy(p => p.Name) // Ordenação .Select(p => new { p.Name, IsAdult = true }) // Projeção (tipo anônimo) .ToList();
foreach (var item in result) { Console.WriteLine($"{item.Name} - Adult: {item.IsAdult}"); } // Saída: Alice - Adult: True, Charlie - Adult: True }
public class Person { public string Name { get; set; } public int Age { get; set; } }}Operadores LINQ principais:
Where— filtragem (análogo afilter).Select— transformação (análogo amap).Aggregate— redução (análogo areduce).Any,All— verificação de condições.
É importante lembrar sobre a execução