Para deixar a primeira letra de uma string maiúscula em C#, converta o primeiro caractere em maiúscula e copie o restante sem alterações. string.Create() faz isso com uma única alocação: string.Create(input.Length, input, static (chars, str) => { chars[0] = char.ToUpperInvariant(str[0]); str.AsSpan(1).CopyTo(chars[1..]); }).
Todo o resto desta página é uma variação dessa ideia, com um custo diferente. Substrings, arrays de caracteres, LINQ e uma expressão regular funcionam, e todos alocam mais do que precisam.
Uma das abordagens não deveria estar em código de produção de jeito nenhum. Fixar a string na memória e sobrescrever o primeiro caractere por meio de um ponteiro é a linha mais rápida do benchmark, mas corrompe todas as outras referências à mesma string. Mantemos essa abordagem aqui, com um aviso, porque ela circula bastante.
Técnica com Substring
Para converter corretamente a primeira letra de uma string em maiúscula, precisamos extrair a primeira letra da string e usar o método nativo ToUpper() para converter o primeiro caractere em maiúscula.
Vamos implementar um método para ilustrar esse conceito:
public string FirstCharSubstring(string input)
{
if (string.IsNullOrEmpty(input))
{
return string.Empty;
}
return $"{input[0].ToString().ToUpper()}{input.Substring(1)}";
}
Primeiro, verificamos se a string é null ou vazia antes de extrair o primeiro caractere e chamar o método ToUpper() para converter a primeira letra em maiúscula. Em seguida, concatenamos o primeiro caractere com o restante da substring e retornamos o resultado ao usuário.
Em todos os testes unitários que vamos implementar hoje, usaremos o mesmo campo:
private readonly FirstLetterToUpperMethods _upperCase = new();
Em seguida, podemos verificar se o método cumpre o seu objetivo:
var testString = "this is a test string";
var returnedString = _upperCase.FirstCharSubstring(testString);
Assert.AreEqual("This is a test string", returnedString);
Uso do método AsSpan()
Podemos usar o método nativo AsSpan() para converter a primeira letra de uma string em maiúscula. A chamada AsSpan(1) cria um novo span somente leitura sobre a string, do segundo caractere até o último, o que evita alocações de memória adicionais e melhora o desempenho do método:
public string FirstCharToUpperAsSpan(string input)
{
if (string.IsNullOrEmpty(input))
{
return string.Empty;
}
Span<char> destination = stackalloc char[1];
input.AsSpan(0, 1).ToUpperInvariant(destination);
return $"{destination}{input.AsSpan(1)}";
}
O método FirstCharToUpperAsSpan() recebe uma string, verifica se ela é null ou vazia e retorna uma string vazia se true.
Caso contrário, usamos stackalloc para criar um bloco de memória na pilha para um caractere. Depois, convertemos a primeira letra da string em maiúscula chamando o método AsSpan() junto com o método ToUpperInvariant().
Por fim, criamos um span somente leitura sobre o restante da substring, concatenamos esse span com a primeira letra da string chamando o método AsSpan() e retornamos o resultado ao usuário.
Vamos verificar com o teste se o método cumpre o seu objetivo:
var testString = "this is a test string";
var returnedString = _upperCase.FirstCharToUpperAsSpan(testString);
Assert.AreEqual("This is a test string", returnedString);
Uso de string.Create com AsSpan
Se você quiser melhorar a implementação com Span e ganhar ainda mais desempenho, pode usar o método string.Create:
public string FirstCharToUpperStringCreate(string input)
{
if (string.IsNullOrEmpty(input))
{
return string.Empty;
}
return string.Create(input.Length, input, static (Span<char> chars, string str) =>
{
chars[0] = char.ToUpperInvariant(str[0]);
str.AsSpan(1).CopyTo(chars[1..]);
});
}
Este é o método seguro mais rápido da página, e o benchmark mais adiante o compara com os outros sete. Agradecemos ao nosso leitor Joel por ter deixado essa sugestão na seção de comentários.
Uso do método Char.ToUpper()
Esta técnica faz o mesmo trabalho que a técnica com Substring, com uma diferença: ela converte o primeiro caractere em maiúscula como um char, em vez de transformá-lo antes em uma string de um caractere:
public string FirstCharToUpper(string input)
{
if (string.IsNullOrEmpty(input))
{
return string.Empty;
}
return $"{char.ToUpper(input[0])}{input[1..]}";
}
O método FirstCharToUpper() pega o primeiro caractere input[0] e chama o método char.ToUpper() para convertê-lo em maiúscula. Concluímos o processo concatenando esse caractere com o restante da string e retornando o resultado como string.
Agora podemos verificar se a implementação converte o primeiro caractere em maiúscula:
var testString = "this is a test string";
var returnedString = _upperCase.FirstCharToUpper(testString);
Assert.AreEqual("This is a test string", returnedString);
Uso de arrays de caracteres
Já que podemos enxergar uma string como um array de caracteres, é possível convertê-la em um array e chamar o método char.ToUpper() para converter a primeira letra do array em maiúscula antes de retorná-lo como string:
public string FirstCharToCharArray(string input)
{
if (string.IsNullOrEmpty(input))
{
return string.Empty;
}
var stringArray = input.ToCharArray();
if (char.IsLower(stringArray[0]))
{
stringArray[0] = char.ToUpper(stringArray[0]);
}
return new string(stringArray);
}
Depois de verificar se input é null ou vazia, convertemos a string em um array de caracteres chamando o método string.ToCharArray(). Em seguida, convertemos o primeiro elemento do array em maiúscula chamando o método char.ToUpper(), depois de verificar com o método char.IsLower() se ele está em minúscula. Retornamos um novo objeto string depois de converter a primeira letra da string em maiúscula.
Podemos verificar se o método converte a primeira letra de uma string em maiúscula:
var testString = "this is a test string";
var returnedString = _upperCase.FirstCharToCharArray(testString);
Assert.AreEqual("This is a test string", returnedString);
Expressões regulares
O C# oferece suporte a expressões regulares, que ficam no namespace System.Text.RegularExpressions. Podemos usar o método Regex.Replace() para converter a primeira letra de uma string em maiúscula e deixar que o gerador de código-fonte de RegEx crie o mecanismo em tempo de compilação:
[GeneratedRegex("^\\p{Ll}")]
private static partial Regex FirstLowerLetter();
public string FirstCharToUpperRegex(string input)
{
if (string.IsNullOrEmpty(input))
{
return string.Empty;
}
return FirstLowerLetter().Replace(input, c => c.Value.ToUpper());
}
Aqui, input é a string em que procuramos caracteres minúsculos. "^\p{Ll}" é o padrão da expressão regular que corresponde ao primeiro caractere da string input. Quando o método Replace() encontra um caractere minúsculo no início de uma string, de acordo com o nosso padrão, ele o substitui pelo equivalente maiúsculo chamando o método ToUpper() na última parte, c => c.Value.ToUpper().
O padrão importa mais do que parece. A forma óbvia de escrevê-lo, "^[a-z]", corresponde apenas às vinte e seis letras ASCII minúsculas, então uma string que começa com é ou ñ volta inalterada, enquanto todas as outras técnicas desta página convertem essa letra em maiúscula. A categoria Unicode \p{Ll} significa “qualquer letra minúscula” e fecha essa lacuna sem adicionar dependência de cultura.

E-book gratuito
Sua Web API está pronta para produção?
33 itens para verificar antes de implantá-la, com a correção de cada um. Um PDF gratuito de 76 páginas para .NET 10.
O e-book está em inglês.
Baixe o checklist gratuitoPDF gratuito. Um único e-mail para enviá-lo. Cancele a inscrição quando quiser.
Podemos verificar com um teste se a solução converte a primeira letra de uma string em maiúscula:
var testString = "this is a test string";
var returnedString = _upperCase.FirstCharToUpperRegex(testString);
Assert.AreEqual("This is a test string", returnedString);
Conversão da primeira letra em maiúscula com LINQ
Outra forma de converter a primeira letra de uma string em maiúscula é usar LINQ. Para manter o exemplo simples, vamos usar o método FirstOrDefault() para extrair o primeiro caractere da string de entrada e chamar o método ToUpper() para convertê-lo em maiúscula:
public string FirstCharToUpperLinq(string input)
{
if (string.IsNullOrEmpty(input))
{
return string.Empty;
}
return $"{input.FirstOrDefault().ToString().ToUpper()}{input.Substring(1)}";
}
Extraímos a primeira letra da string, convertemos essa letra em maiúscula e concatenamos o resultado com o restante da substring antes de retorná-lo ao usuário.
Agora, vamos testar essa lógica:
var testString = "this is a test string";
var returnedString = _upperCase.FirstCharToUpperLinq(testString);
Assert.AreEqual("This is a test string", returnedString);
Por que não usar código unsafe para deixar a primeira letra maiúscula?
A versão unsafe nunca cria uma nova string. Ela fixa na memória a string recebida, sobrescreve o primeiro caractere no próprio lugar e retorna a mesma instância.
As strings no .NET são imutáveis, e o runtime depende disso. Literais de string idênticos em um assembly normalmente compartilham uma única instância internada, então sobrescrever o primeiro caractere de uma variável altera todas as outras variáveis, campos e chaves de dicionário que apontam para o mesmo literal.
Executamos isso no .NET 10. Duas variáveis que recebem o mesmo literal são a mesma instância e, depois de uma chamada feita por qualquer uma delas, as duas leem This is a test string.
Um Dictionary<string, int> montado com essa string antes da chamada deixa de encontrar uma chave igual depois dela, porque o bucket foi escolhido a partir do conteúdo antigo.
O benchmark favorece esse método pelo mesmo motivo. Ele não aloca nem copia nada, então não está fazendo o trabalho que os outros sete métodos fazem.
A imagem resume todo o problema: a e b nunca foram strings separadas.

Não use isso em código de produção. Esse código está aqui porque circula como dica de desempenho, e os parágrafos acima explicam o que ele realmente faz. Podemos ver como ele funciona com código unsafe:
public string FirstCharToUpperUnsafeCode(string input)
{
if (string.IsNullOrEmpty(input))
{
return string.Empty;
}
unsafe
{
fixed (char* p = input)
{
*p = char.ToUpper(*p);
}
}
return input;
}
A conversão de uma letra em maiúscula depende da cultura?
Sim, e as duas chamadas que parecem intercambiáveis não são.
char.ToUpper(c) aplica as regras de maiúsculas e minúsculas da cultura atual. A referência da Microsoft para o método resume isso em uma linha: “As regras de maiúsculas e minúsculas são obtidas da cultura atual.” Já char.ToUpperInvariant(c) aplica a cultura invariável, seja qual for a configuração da máquina.
Para a maioria das letras, as duas concordam. É no turco que elas divergem. A mesma referência mostra o i minúsculo (U+0069) virando I (U+0049) em en-US e na cultura invariável, e İ (U+0130) em tr-TR.

E-book gratuito
Sua Web API está pronta para produção?
33 itens para verificar antes de implantá-la, com a correção de cada um. Um PDF gratuito de 76 páginas para .NET 10.
O e-book está em inglês.
Baixe o checklist gratuitoPDF gratuito. Um único e-mail para enviá-lo. Cancele a inscrição quando quiser.
Reproduzimos o comportamento. Com a cultura atual definida como tr-TR, char.ToUpper('i') retornou U+0130 e char.ToUpperInvariant('i') retornou U+0049.
Então a pergunta é: para que serve a string? Um texto que uma pessoa lê deve seguir a cultura dela. Um identificador, um nome de arquivo, um token de protocolo ou uma chave de banco de dados deve usar ToUpperInvariant(), porque o resultado não pode mudar quando a localidade da máquina muda.
As duas regras de maiúsculas e minúsculas vêm da referência do método Char.ToUpper, cuja tabela de exemplo mostra lado a lado os resultados de en-US, da cultura invariável e de tr-TR para i.
Como converter uma string inteira em maiúsculas em C#?
ToUpper() aplicado a uma string converte todos os caracteres e retorna uma nova string quando algo muda, sem alterar a original. ToUpperInvariant() faz o mesmo trabalho com a cultura invariável, então escolher entre os dois é exatamente a escolha de cultura descrita acima.
Nenhum dos dois converte apenas a primeira letra em maiúscula, e é por isso que o resto deste artigo existe. "code maze".ToUpper() retorna CODE MAZE, e não Code maze.
Converter em maiúscula a primeira letra de cada palavra é ainda outra tarefa, e TextInfo.ToTitleCase() é o método criado para ela. Ele também converte o restante de cada palavra em minúsculas, a menos que a palavra inteira esteja em maiúsculas.
Nenhum desses métodos altera nada. Uma string no .NET é imutável, então, sempre que um caractere precisa mudar, cada um deles aloca uma string nova com o mesmo comprimento da original. Em um loop executado com frequência, essa alocação é o custo que vale a pena medir, e é por causa dela que existem as abordagens baseadas em span mostradas mais acima nesta página.
Testes de desempenho
Vamos avaliar o desempenho dessas técnicas com benchmarks que você mesmo pode executar. Primeiro, vamos implementar um método que gera uma string aleatória, que passaremos aos exemplos:
private string GenerateRandomString(int size)
{
var random = new Random();
var charOptions = "abcdefghijklmnopqrstuvwxyz";
return new string(Enumerable.Repeat(charOptions, size).Select(s => s[random.Next(s.Length)]).ToArray()).ToLower();
}
Primeiro, definimos uma string charOptions que contém todas as letras do alfabeto. Vamos usá-la para gerar strings aleatórias. Em seguida, usamos a classe Random nativa, LINQ e o Length da string para retornar uma nova string aleatória.
Em seguida, vamos criar um novo objeto que passa strings aleatórias aos exemplos, cada uma com comprimento de 2000:
public IEnumerable<object[]> SampleStrings()
{
yield return new object[] { GenerateRandomString(2000)};
}
Vamos avaliar o desempenho das diferentes formas de converter a primeira letra de uma string em maiúscula:
BenchmarkDotNet v0.15.8, Windows 10 (10.0.19045.6466/22H2/2022Update) AMD Ryzen 5 3600 3.60GHz, 1 CPU, 12 logical and 6 physical cores .NET SDK 10.0.302 [Host] : .NET 10.0.10 (10.0.10, 10.0.1026.32716), X64 RyuJIT x86-64-v3 DefaultJob : .NET 10.0.10 (10.0.10, 10.0.1026.32716), X64 RyuJIT x86-64-v3 | Method | input | Mean | Error | StdDev | Median | Rank | Gen0 | Gen1 | Allocated | |----------------------------- |--------------------- |-----------:|-----------:|-----------:|-----------:|-----:|-------:|-------:|----------:| | FirstCharToUpperUnsafeCode | fowj(...)jwgs [2000] | 3.912 ns | 0.1390 ns | 0.3277 ns | 3.825 ns | 1 | - | - | - | | FirstCharToUpperStringCreate | kbbm(...)nhyr [2000] | 181.430 ns | 8.7778 ns | 24.0291 ns | 173.651 ns | 2 | 0.4809 | - | 4024 B | | FirstCharToUpperAsSpan | zhay(...)mbzp [2000] | 344.204 ns | 20.6009 ns | 59.7669 ns | 323.639 ns | 3 | 0.4807 | - | 4024 B | | FirstCharToCharArray | glhj(...)kvvv [2000] | 365.260 ns | 12.7927 ns | 36.0820 ns | 352.843 ns | 3 | 0.9618 | 0.0072 | 8048 B | | FirstCharToUpperLinq | gkkq(...)aiqu [2000] | 390.932 ns | 22.4573 ns | 64.0720 ns | 362.186 ns | 3 | 0.9675 | 0.0072 | 8096 B | | FirstCharSubstring | vmla(...)mybs [2000] | 407.755 ns | 11.0983 ns | 31.6641 ns | 401.588 ns | 4 | 0.9675 | 0.0072 | 8096 B | | FirstCharToUpper | ovap(...)dkyy [2000] | 476.392 ns | 13.8828 ns | 39.3832 ns | 469.819 ns | 5 | 0.9618 | - | 8048 B | | FirstCharToUpperRegex | tpvo(...)tijn [2000] | 561.932 ns | 17.1487 ns | 50.2942 ns | 549.487 ns | 6 | 0.5112 | - | 4280 B |
A técnica com código unsafe é a linha mais rápida, com 3,912 ns, e é a linha que devemos ignorar: ela não aloca nem copia nada porque não cria nenhuma string nova. Dos sete métodos que fazem o trabalho, string.Create() vence com 181,430 ns e a expressão regular é a mais lenta, com 561,932 ns. A coluna “Allocated” separa os métodos ainda mais: string.Create() e AsSpan() alocam uma string cada, enquanto as técnicas com substring, LINQ e char.ToUpper() também alocam strings intermediárias, e a técnica com array de caracteres aloca um array intermediário.
| Abordagem | Chamada | Alocações por chamada | Cultura | Quando usar |
|---|---|---|---|---|
string.Create com um span | string.Create(len, input, …) + char.ToUpperInvariant() | Uma string | Invariável, explícita | Escolha padrão; código crítico para o desempenho |
AsSpan() com stackalloc | input.AsSpan(0, 1).ToUpperInvariant(dest) | Uma string, via interpolação | Invariável, explícita | Você já tem um span em mãos |
| Array de caracteres | input.ToCharArray() e depois char.ToUpper() | Array mais string | Cultura atual, implícita | O array é necessário de qualquer forma |
| Substring | input[0].ToString().ToUpper() + input.Substring(1) | Quatro strings | Cultura atual, implícita | Legibilidade acima do custo |
char.ToUpper com um intervalo | char.ToUpper(input[0]) + input[1..] | Duas strings | Cultura atual, implícita | Legibilidade acima do custo |
| LINQ | FirstOrDefault().ToString() + .ToUpper() | Quatro strings | Cultura atual, implícita | Nunca; é a versão com substring disfarçada |
| Expressão regular | [GeneratedRegex("^\\p{Ll}")] e depois Replace(input, …) | Três strings mais o maquinário de correspondência | Cultura atual, implícita | Há um padrão de verdade envolvido, não apenas um caractere |
| Escrita por ponteiro com código unsafe | fixed (char* p = input) *p = … | Nenhuma, porque altera a string no próprio lugar | Cultura atual, implícita | Nunca. Veja a seção sobre código unsafe |
Conclusão
Neste artigo, vimos as diferentes técnicas que podemos usar para converter a primeira letra de uma string em maiúscula.
string.Create() é a técnica a escolher: ela produz o resultado com uma única alocação e é a mais rápida das técnicas seguras, com 181,430 ns. A técnica com ponteiro é ainda mais rápida no papel, com 3,912 ns, e é justamente a que devemos evitar, porque altera uma string que outras partes do programa também estão usando.
Converter em maiúscula a primeira letra de cada palavra, e não só a primeira letra da string, é outra tarefa, e o nosso artigo sobre como converter uma string em title case trata desse assunto.
Testado com .NET 10.0.10 e BenchmarkDotNet 0.15.8.