Validar, gerar e formatar CPF em C#
Classe estática em C# 12 (.NET 8) para validar, gerar e formatar CPF com o módulo 11, testes com xUnit [Theory] e chamada à API com HttpClient.
Validar CPF em C# costuma aparecer em APIs ASP.NET Core, em validadores do FluentValidation e em rotinas de importação. A classe abaixo é estática, não guarda estado e usa só a biblioteca base do .NET 8, com a sintaxe do C# 12. Ela supõe um projeto com ImplicitUsings habilitado, o padrão dos templates atuais, que já importa System e System.Linq. A lógica é a mesma das ferramentas do site; o guia do algoritmo do módulo 11 detalha a conta.
Validar CPF em C#
using System.Security.Cryptography;
using System.Text.RegularExpressions;
namespace CpfDev;
public static class Cpf
{
private static readonly Regex Separadores = new(@"[.\-\s]", RegexOptions.Compiled);
private static int CalcularDigito(string digitos, int pesoInicial)
{
var soma = 0;
for (var i = 0; i < digitos.Length; i++)
{
soma += (digitos[i] - '0') * (pesoInicial - i);
}
var resto = soma % 11;
return resto < 2 ? 0 : 11 - resto;
}
public static bool Validar(string? valor)
{
if (valor is null) return false;
var cpf = Separadores.Replace(valor, "");
if (cpf.Length != 11 || !cpf.All(char.IsAsciiDigit)) return false;
if (cpf.All(c => c == cpf[0])) return false;
var dv1 = CalcularDigito(cpf[..9], 10);
var dv2 = CalcularDigito(cpf[..10], 11);
return cpf[9] - '0' == dv1 && cpf[10] - '0' == dv2;
}
}Detalhes da implementação:
char.IsAsciiDigit(.NET 7 em diante) aceita só 0 a 9.char.IsDigitaceitaria dígitos de outros alfabetos, que quebrariam a conta com- '0'.cpf.All(c => c == cpf[0])recusa 111.111.111-11 e as outras sequências repetidas, que fecham o módulo 11, mas não são consideradas válidas.cpf[..9]usa o operador de intervalo para pegar a base semSubstring.string?deixa explícito, com nullable reference types, que a função aceitanulle devolvefalse.
Com 529.982.247-25, a primeira soma é 295 (resto 9, dígito 2) e a segunda é 347 (resto 6, dígito 5); as comparações batem e Validar devolve true. Com 529.982.247-26, cpf[10] - '0' vale 6 e o dígito calculado é 5, então o resultado é false.
No FluentValidation, a regra fica RuleFor(x => x.Cpf).Must(Cpf.Validar).WithMessage("CPF inválido").
Gerar CPF válido em C#
Os métodos a seguir ficam dentro da mesma classe. RandomNumberGenerator.GetInt32 usa o gerador criptográfico do sistema.
public static string Gerar(bool formatado = false)
{
string base9;
do
{
base9 = string.Concat(Enumerable.Range(0, 9).Select(_ => RandomNumberGenerator.GetInt32(10)));
} while (base9.All(c => c == base9[0]));
var dv1 = CalcularDigito(base9, 10);
var dv2 = CalcularDigito(base9 + dv1, 11);
var cpf = $"{base9}{dv1}{dv2}";
return formatado ? Formatar(cpf) : cpf;
}A variável se chama base9 porque base é palavra reservada em C#. O CPF gerado é válido, mas pode coincidir com um CPF real por acaso; use-o em testes, seeds do Entity Framework e ambientes de homologação. O guia de massa de teste e LGPD explica os cuidados.
Formatar e limpar CPF em C#
public static string Limpar(string? valor) =>
valor is null ? "" : new string(valor.Where(char.IsAsciiDigit).ToArray());
public static string Formatar(string? valor)
{
var d = Limpar(valor);
if (d.Length != 11)
throw new ArgumentException($"CPF precisa de 11 dígitos, recebeu {d.Length}", nameof(valor));
return $"{d[..3]}.{d[3..6]}.{d[6..9]}-{d[9..]}";
}Cpf.Formatar("52998224725") devolve "529.982.247-25", e Cpf.Limpar("529.982.247-25") devolve "52998224725". Grave a versão limpa em uma coluna char(11); o guia sobre validar CPF no banco de dados explica por que tipos numéricos perdem o zero à esquerda.
Testes
No xUnit, [Theory] com um [InlineData] por caso gera um teste para cada linha.
using CpfDev;
using Xunit;
public class CpfTests
{
[Theory]
[InlineData("529.982.247-25", true)]
[InlineData("111.111.111-11", false)]
[InlineData("529.982.247-26", false)]
public void Validar_DevolveOResultadoEsperado(string cpf, bool esperado)
{
Assert.Equal(esperado, Cpf.Validar(cpf));
}
}Rode com dotnet test. Para mais entradas problemáticas, como CPF com letras ou com 10 dígitos, veja os erros comuns ao validar CPF.
Usar a API em vez de reimplementar
A API do cpf.dev.br valida e gera CPFs por HTTP, sem chave, com limite de 60 requisições por minuto por IP. GetFromJsonAsync desserializa a resposta direto para um record.
using System.Net.Http.Json;
using var http = new HttpClient { BaseAddress = new Uri("https://www.cpf.dev.br/") };
var r = await http.GetFromJsonAsync<RespostaValidacao>("api/v1/cpf/validar/529.982.247-25");
Console.WriteLine(r);
// RespostaValidacao { Valido = True, Cpf = 52998224725, Formatado = 529.982.247-25, Uf = System.String[] }
var lote = await http.GetFromJsonAsync<RespostaLote>("api/v1/cpf/gerar?quantidade=5&uf=SP&formatado=true");
Console.WriteLine(string.Join(", ", lote!.Cpfs));
public record RespostaValidacao(bool Valido, string Cpf, string Formatado, string[] Uf);
public record RespostaLote(string[] Cpfs);O exemplo cria um HttpClient direto para ficar curto; em ASP.NET Core, registre-o com IHttpClientFactory. A resposta da validação traz Uf igual a ["ES", "RJ"] para esse CPF. Para formulários, a classe local continua sendo a melhor opção. Para conferir um número à mão, use o validador de CPF.