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.