Parse e TryParse no C#

Parse e TryParse são dois métodos que o C# oferece para converter, ou tentar converter, uma string para outro tipo de forma explícita. Neste artigo, você vai entender como cada um funciona, como lidar com culturas e formatos e quando escolher um ou outro.

O que são Parse e TryParse no C#

Os métodos Parse e TryParse permitem converter uma string para outro tipo. Na prática, todos os tipos built-in do .NET, como int, bool, double, decimal e DateTime, possuem esses métodos.

Esse tipo de conversão aparece o tempo todo. Por exemplo, um valor digitado no console, um campo de formulário ou uma coluna de um arquivo CSV chegam como texto e precisam virar número ou data.

A diferença central entre Parse e TryParse está no comportamento diante de um valor inválido. O Parse lança uma exceção, enquanto o TryParse devolve false e segue a execução normalmente.

Se você ainda tem dúvidas sobre os tipos numéricos do .NET, vale ler antes o artigo sobre números no C# e .NET.

Como usar o Parse

O Parse é um método estático disponível no próprio tipo de destino. Assim, você invoca o método informando uma string, como em int.Parse("123").

Abaixo temos três exemplos de conversão de string para outros tipos built-in. O terceiro converte para DateTime, que é uma estrutura mais complexa.

var number = int.Parse("123");
var flag = bool.Parse("true");
var date = DateTime.Parse("01/01/2022");

Console.WriteLine(number);
Console.WriteLine(flag);
Console.WriteLine(date);

Como resultado da execução, temos o seguinte texto impresso na tela:

123
True
1/1/2022 12:00:00 AM

Repare que a data aparece no formato americano. Isso acontece porque a saída usa a cultura da máquina, que nesse exemplo era en-US.

Exceções lançadas pelo Parse

O Parse assume que o texto é válido. Quando isso não acontece, ele lança uma exceção, e você precisa estar preparado para tratá-la.

Segundo a documentação do Int32.Parse, três exceções são as mais comuns. A ArgumentNullException aparece quando a string é null.

Já a FormatException surge quando o texto não está em um formato válido, como "abc". Por fim, a OverflowException indica um número fora do intervalo do tipo, como "9999999999" para um int.

try
{
    var value = int.Parse("9999999999");
}
catch (OverflowException)
{
    Console.WriteLine("Value out of range for int.");
}
catch (FormatException)
{
    Console.WriteLine("Invalid format.");
}

Parse e TryParse em outros tipos built-in

O mesmo padrão se repete em vários outros tipos da plataforma. Por isso, depois que você entende Parse e TryParse no int, consegue aplicar o conhecimento em double, Guid, TimeSpan e enums.

var ratio = double.Parse("0.75", CultureInfo.InvariantCulture); // 0.75
var id = Guid.Parse("3f2504e0-4f89-11d3-9a0c-0305e82c3301");
var duration = TimeSpan.Parse("01:30:00");                      // 1 hora e 30 minutos

var isValid = Enum.TryParse<DayOfWeek>("friday", ignoreCase: true, out var day);
Console.WriteLine(day); // Friday

O bool.Parse também merece atenção. Ele aceita "true" e "false" sem diferenciar maiúsculas e minúsculas, mas rejeita valores como "1" ou "sim".

Já no caso dos enums, o Enum.TryParse aceita textos numéricos mesmo quando o número não corresponde a nenhum membro. Portanto, combine o resultado com Enum.IsDefined quando precisar garantir um valor válido.

Cultura e IFormatProvider

Voltando ao exemplo anterior, passamos o valor 01/01/2022 para o DateTime. Esse valor foi proposital, porque funciona tanto no formato brasileiro quanto no americano.

Agora vamos considerar uma nova data, 28/10/2021, no formato dd/MM/yyyy. Esse é o formato usado no Brasil, mas os Estados Unidos usam MM/dd/yyyy.

Unhandled exception. System.FormatException: String '28/10/2021' was not recognized as a valid DateTime.
   at System.DateTime.Parse(String s)
   at Program.<Main>$(String[] args) in C:\dev\CsharpTryParse\Program.cs:line 8

Nesse caso, a conversão falha porque 28/10/2021 não é uma data válida nos Estados Unidos. O correto lá seria 10/28/2021.

O Parse sem cultura usa a cultura atual da thread, que normalmente vem do sistema operacional. Por isso, o mesmo código pode funcionar na sua máquina e falhar no servidor.

Convertendo datas com CultureInfo

Felizmente, conseguimos contornar esse problema informando uma cultura. A classe CultureInfo implementa a interface IFormatProvider, que o Parse aceita como parâmetro.

using System.Globalization;

var culture = new CultureInfo("pt-BR");
var date = DateTime.Parse("28/10/2021", culture);

Note que agora temos um objeto do tipo CultureInfo, que define a cultura como pt-BR, ou seja, português do Brasil. Quando executamos o Parse com essa cultura, a conversão funciona.

Essa mesma sobrecarga com IFormatProvider existe em Parse e TryParse. Dessa forma, você escolhe a cultura certa independentemente do método usado.

Para aprofundar o assunto, o artigo sobre DateTime no C# e .NET explica fusos, formatos e armadilhas comuns com datas.

Cultura atual da aplicação

Quando você não informa a cultura, Parse e TryParse usam o valor de CultureInfo.CurrentCulture. Esse valor vem das configurações regionais do sistema operacional ou do contêiner onde a aplicação roda.

Em servidores Linux e imagens Docker, a cultura costuma ser diferente da sua máquina de desenvolvimento. Assim, uma conversão que funciona no seu computador pode falhar em produção.

// Exibe a cultura usada nas conversões sem IFormatProvider
Console.WriteLine(CultureInfo.CurrentCulture.Name);

// Define a cultura padrão para todas as threads da aplicação
CultureInfo.DefaultThreadCurrentCulture = new CultureInfo("pt-BR");

Alterar a cultura padrão resolve casos simples, mas esconde a dependência. Por isso, informar a cultura em cada chamada continua sendo a opção mais clara.

InvariantCulture para dados técnicos

Quando o texto vem de um arquivo, de uma API ou de um banco de dados, o ideal é não depender da cultura da máquina. Nesse caso, use CultureInfo.InvariantCulture.

A cultura invariante usa ponto como separador decimal e vírgula como separador de milhar. Além disso, ela nunca muda entre ambientes, o que torna a conversão previsível.

var price = decimal.Parse("1234.56", CultureInfo.InvariantCulture); // 1234.56

NumberStyles

Outro modificador que podemos usar é o NumberStyles. Ele define quais elementos o texto pode conter, como ponto decimal, separador de milhar, sinal ou espaços.

Console.WriteLine(int.Parse("1234", NumberStyles.None));
// 1234

Console.WriteLine(int.Parse("12123.0", NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture));
// 12123

Console.WriteLine(int.Parse("123,456", NumberStyles.AllowThousands, CultureInfo.InvariantCulture));
// 123456

O segundo exemplo funciona porque a parte decimal contém apenas zeros. Segundo a documentação, se houver qualquer outro dígito depois do ponto, como em "12123.5", o int.Parse lança uma OverflowException.

Também é possível combinar a cultura com o NumberStyles para trabalhar com a relação entre ponto e vírgula dos valores. No formato brasileiro, o ponto separa milhares e a vírgula separa decimais.

var ptBr = new CultureInfo("pt-BR");

var total = int.Parse("123.456", NumberStyles.AllowThousands, ptBr);  // 123456
var amount = decimal.Parse("1.234,56", NumberStyles.Number, ptBr);    // 1234.56

Como usar o TryParse

Como vimos nos exemplos anteriores, o Parse faz uma tentativa de conversão que nem sempre termina com sucesso. No caso do DateTime, tivemos uma FormatException porque a data não estava no formato esperado.

Para evitar esse tipo de situação, temos o TryParse. Ele devolve um booleano que informa se a conversão foi possível e, em caso positivo, entrega o valor convertido.

var success = int.TryParse("45689", out var value);

Console.WriteLine(success);
Console.WriteLine(value);

Note que o TryParse usa o out var value. Portanto, o retorno direto do método é um bool, e o valor convertido fica na variável declarada no out.

Como resultado de uma conversão de sucesso, temos o seguinte texto no terminal:

True
45689

O que acontece quando a conversão falha

Agora vamos ao segundo cenário, onde o TryParse mais se destaca. Aqui temos uma tentativa de conversão que vai falhar, afinal estamos tentando converter "asdaasd" para um int.

var success = int.TryParse("asdaasd", out var value);

Console.WriteLine(success);
Console.WriteLine(value);

Nesse caso, não temos exceção, e sim os valores false para o resultado e 0 para o int. Isso ocorre porque o método atribui o valor padrão do tipo à variável quando a conversão falha.

False
0

Para um int, o valor padrão é 0. Já para um DateTime, o resultado de uma falha é DateTime.MinValue.

O TryParse também lida bem com null. Em vez de lançar ArgumentNullException, ele simplesmente devolve false.

Um padrão muito comum combina o TryParse com um if. Assim, o código só usa o valor quando a conversão deu certo.

Console.Write("Enter your age: ");
var input = Console.ReadLine();

if (int.TryParse(input, out var age))
    Console.WriteLine($"Next year you will be {age + 1}.");
else
    Console.WriteLine("Please enter a valid number.");

Validando regras depois da conversão

Um detalhe importante: o TryParse verifica apenas se o texto representa um valor do tipo. Ele não sabe nada sobre as regras do seu negócio.

Por exemplo, "-5" é um int válido, mas não faz sentido como idade. Da mesma forma, "200" converte sem problemas, embora dificilmente represente a idade real de alguém.

if (!int.TryParse(input, out var age))
{
    Console.WriteLine("Please enter a number.");
}
else if (age is < 0 or > 130)
{
    // A conversão funcionou, mas o valor viola a regra de negócio
    Console.WriteLine("Please enter a valid age.");
}
else
{
    Console.WriteLine($"Age accepted: {age}");
}

Separar essas duas etapas deixa as mensagens de erro mais claras. Assim, o usuário sabe se digitou um texto inválido ou um número fora da faixa permitida.

Esse cuidado vale tanto para Parse e TryParse em aplicações de console quanto em APIs e formulários web.

Parse e TryParse com cultura e NumberStyles

Tudo o que vimos sobre cultura vale para os dois métodos. Ou seja, Parse e TryParse aceitam NumberStyles e IFormatProvider nas mesmas sobrecargas.

var ptBr = new CultureInfo("pt-BR");

if (decimal.TryParse("1.234,56", NumberStyles.Number, ptBr, out var amount))
    Console.WriteLine(amount); // 1234,56 em uma máquina pt-BR

if (DateTime.TryParse("28/10/2021", ptBr, DateTimeStyles.None, out var date))
    Console.WriteLine(date.ToString("yyyy-MM-dd")); // 2021-10-28

Dessa forma, você mantém o controle do formato e ainda evita exceções no caminho.

Parse e TryParse com ReadOnlySpan

Além das sobrecargas com string, os tipos numéricos oferecem Parse e TryParse que recebem um ReadOnlySpan<char>. Com isso, você converte um trecho de texto sem criar uma nova string.

Imagine uma linha com o formato id;quantidade. Normalmente, você usaria Split ou Substring, que alocam novas strings na memória.

var line = "1050;42";
var separator = line.IndexOf(';');

// Converte cada parte sem alocar novas strings
var idOk = int.TryParse(line.AsSpan(0, separator), out var productId);
var qtyOk = int.TryParse(line.AsSpan(separator + 1), out var quantity);

Console.WriteLine($"{productId}: {quantity}"); // 1050: 42

Em um processamento pontual, a diferença é pequena. No entanto, em importações com milhões de linhas, evitar essas alocações reduz o trabalho do garbage collector.

ParseExact e TryParseExact para datas

Em alguns cenários, o formato da data é fixo e conhecido. Nesse caso, o ParseExact e o TryParseExact são ainda mais seguros, porque aceitam apenas o formato informado.

var ok = DateTime.TryParseExact(
    "28/10/2021",
    "dd/MM/yyyy",
    CultureInfo.InvariantCulture,
    DateTimeStyles.None,
    out var date);

Console.WriteLine(ok);   // True
Console.WriteLine(date.Month); // 10

Com o formato explícito, o método rejeita textos como 10/28/2021. Por isso, essa é a melhor opção para importar arquivos com layout definido.

Quando o arquivo pode trazer mais de um formato, informe uma lista de formatos aceitos. O método testa cada um deles e só falha se nenhum combinar com o texto.

string[] formats = ["dd/MM/yyyy", "yyyy-MM-dd"];

var parsed = DateTime.TryParseExact(
    "2021-10-28", formats, CultureInfo.InvariantCulture, DateTimeStyles.None, out var result);

Console.WriteLine(parsed); // True

Dessa forma, você aceita variações conhecidas sem abrir mão do controle sobre o formato.

Diferenças entre Parse e TryParse

Para resumir, a tabela abaixo compara Parse e TryParse lado a lado.

Característica Parse TryParse
Retorno O valor convertido Um bool indicando sucesso
Valor inválido Lança FormatException Devolve false
Valor nulo Lança ArgumentNullException Devolve false
Fora do intervalo Lança OverflowException Devolve false
Valor em caso de falha Não se aplica Valor padrão do tipo
Suporte a cultura Sim, via IFormatProvider Sim, via IFormatProvider

Além do comportamento, existe uma diferença de custo. Lançar e capturar exceções é uma operação cara, então o TryParse tende a ser mais eficiente quando falhas são frequentes.

Vale citar também o Convert.ToInt32. Ele chama o Parse por baixo dos panos, mas devolve 0 quando recebe uma string null, em vez de lançar exceção.

Quando usar Parse e TryParse

A escolha entre Parse e TryParse depende de quanto você confia na origem do texto.

Use o Parse quando o valor deveria ser sempre válido e uma falha representa um erro de programação ou de configuração. Nesse caso, a exceção ajuda a encontrar o problema rapidamente.

Por outro lado, use o TryParse quando o texto vem do usuário, de arquivos externos ou de integrações. Entradas inválidas são esperadas nesses cenários, então não faz sentido tratá-las como exceção.

Um bom exemplo é a leitura de um arquivo CSV com milhares de linhas. Com o TryParse, você registra as linhas inválidas e continua processando o restante.

var lines = new[] { "10", "20", "abc", "40" };
var total = 0;

foreach (var line in lines)
{
    if (int.TryParse(line, NumberStyles.Integer, CultureInfo.InvariantCulture, out var number))
        total += number;
    else
        Console.WriteLine($"Invalid line ignored: {line}");
}

Console.WriteLine(total); // 70

Parse e TryParse em tipos genéricos

A partir do .NET 7, Parse e TryParse ganharam um contrato comum. A interface IParsable<TSelf> define os dois métodos, e os tipos numéricos e de data a implementam.

Com isso, você consegue escrever um único método genérico que converte para qualquer tipo compatível.

static T? ParseOrDefault<T>(string? input) where T : IParsable<T>
{
    // Usa o TryParse do tipo T com a cultura invariante
    return T.TryParse(input, CultureInfo.InvariantCulture, out var result)
        ? result
        : default;
}

var number = ParseOrDefault<int>("42");          // 42
var price = ParseOrDefault<decimal>("19.90");    // 19.90
var invalid = ParseOrDefault<int>("not a number"); // 0

Seus próprios tipos também podem implementar IParsable<TSelf>. Assim, eles passam a funcionar com o mesmo padrão dos tipos built-in.

Boas práticas com Parse e TryParse

Depois de conhecer os detalhes, vale reunir algumas boas práticas para usar Parse e TryParse no dia a dia.

Primeiro, informe sempre a cultura quando o formato importa. Sem ela, o resultado depende da máquina onde o código roda.

Em seguida, prefira CultureInfo.InvariantCulture para dados técnicos, como arquivos, APIs e configurações. Já para textos digitados pelo usuário, use a cultura dele.

Também prefira o TryParse para entradas externas e deixe o Parse para valores que deveriam ser sempre válidos. Por fim, use ParseExact ou TryParseExact quando o formato da data for fixo.

Se o próximo passo for validar ou transformar o texto antes da conversão, o guia de manipulação de strings em C# e .NET ajuda nessa etapa.

Conclusão

Fazer conversões de valores no C# é uma tarefa simples, e a plataforma oferece vários recursos para isso. Entre eles, Parse e TryParse são os mais usados no dia a dia.

O Parse é direto e lança exceções quando algo dá errado. Já o TryParse devolve um booleano e mantém o fluxo do programa sob controle.

Combinados com CultureInfo e NumberStyles, Parse e TryParse convertem números e datas de qualquer formato com segurança. Portanto, escolha o método de acordo com a origem do dado e sempre deixe a cultura explícita.

Agora, experimente revisar as conversões do seu projeto e verifique quais delas dependem da cultura da máquina sem que você perceba.

FAQ

Qual a diferença entre Parse e TryParse?

O Parse devolve o valor convertido e lança exceção se o texto for inválido. O TryParse devolve um bool e entrega o valor em um parâmetro out, sem lançar exceção.

O que o TryParse devolve quando a conversão falha?

Nesse caso, o método devolve false e atribui o valor padrão do tipo à variável out. Para um int, esse valor é 0.

Por que o DateTime.Parse funciona na minha máquina e falha no servidor?

Sem uma cultura explícita, o Parse usa a cultura do sistema operacional. Informe um CultureInfo ou use ParseExact para ter o mesmo resultado em qualquer ambiente.

O TryParse é mais rápido que o Parse?

Com valores válidos, o desempenho é parecido. Com muitos valores inválidos, o TryParse é mais eficiente porque não precisa lançar e capturar exceções.

Posso usar Parse e TryParse com meus próprios tipos?

Sim. A partir do .NET 7, basta implementar a interface IParsable<TSelf> no seu tipo.