Programação Funcional em C#: uma abordagem moderna para o desenvolvimento
Programação Funcional (PF) é um paradigma no qual os cálculos são tratados como avaliação de funções matemáticas e evita-se a mudança de estado e dados mutáveis. Embora C# tenha sido originalmente criado como uma linguagem orientada a objetos, a partir da versão 3.0 ele tem incorporado ativamente elementos da abordagem funcional. Hoje, a programação funcional em C# não é apenas uma tendência da moda, mas uma ferramenta poderosa para escrever código mais previsível, testável e seguro para paralelismo.
Neste artigo, vamos explorar os conceitos-chave da PF aplicados ao C#: funções puras, imutabilidade de dados, LINQ, closures e expressões lambda. Você verá como essas técnicas ajudam a escrever código conciso e confiável no ecossistema .NET.
1. Funções Puras e Ausência de Efeitos Colaterais
Uma função pura é uma função que:
- Sempre retorna o mesmo resultado para os mesmos argumentos (transparência referencial).
- Não possui efeitos colaterais (não altera variáveis globais, não realiza entrada/saída, não modifica os argumentos recebidos).
Em C#, uma função pura pode ser assim:
// Função purapublic int AddPure(int a, int b){ return a + b;}
// Função NÃO pura (efeito colateral — saída no console e alteração de variável global)private int _state = 0;public int AddImpure(int a, int b){ _state++; Console.WriteLine("Chamada de função"); return a + b + _state;}As vantagens das funções puras são óbvias: são fáceis de testar, previsíveis e podem ser chamadas com segurança a partir de threads paralelas. Tente extrair toda a lógica não relacionada a I/O para funções puras.
2. Imutabilidade de Dados (Immutability)
A programação funcional incentiva o trabalho com dados imutáveis. Em vez de modificar um objeto existente, você cria um novo objeto com as propriedades alteradas. Em C#, os seguintes recursos são excelentes para isso:
- record (a partir do C# 9) — tipo referencial com igualdade baseada em valor e imutabilidade por padrão.
- struct — para pequenas estruturas imutáveis (por exemplo, coordenadas, dinheiro).
- IEnumerable<T> — para coleções imutáveis via LINQ.
Exemplo com record:
public record Person(string Name, int Age);
// Uso da expressão with para criar uma cópia com alteraçõesvar original = new Person("Alexey", 30);var updated = original with { Age = 31 };
Console.WriteLine(original); // Person { Name = Alexey, Age = 30 }Console.WriteLine(updated); // Person { Name = Alexey, Age = 31 }Essa abordagem elimina a alteração acidental de dados em um local que afeta outras partes do programa.
3. LINQ — Consultas Funcionais a Dados
LINQ (Language Integrated Query) é, talvez, o exemplo mais marcante da abordagem funcional em C#. Ele permite processar coleções de forma declarativa, usando encadeamento de métodos.
Métodos funcionais chave do LINQ:
Select— transformação de cada elemento (análogo ao map).Where— filtragem (análogo ao filter).Aggregate— redução (análogo ao reduce).SelectMany— desdobramento de coleções aninhadas (flatMap).
Exemplo: encontrar a soma dos quadrados dos números pares.
int[] numbers = { 1, 2, 3, 4, 5, 6 };
// Estilo imperativoint sumImperative = 0;foreach (var num in numbers){ if (num % 2 == 0) { sumImperative += num * num; }}
// Estilo funcional com LINQint sumFunctional = numbers .Where(n => n % 2 == 0) // filtragem .Select(n => n * n) // transformação .Sum(); // agregação
C