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.

Para baixar o código-fonte deste artigo, você pode acessar nosso repositório no GitHub.

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.

The Web API Production Checklist, e-book gratuito em inglês

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 gratuito

PDF 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.

Duas variáveis que referenciam uma única string internada; uma escrita por ponteiro feita por uma delas altera o valor que as duas variáveis leem.

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.

The Web API Production Checklist, e-book gratuito em inglês

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 gratuito

PDF 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.

AbordagemChamadaAlocações por chamadaCulturaQuando usar
string.Create com um spanstring.Create(len, input, …) + char.ToUpperInvariant()Uma stringInvariável, explícitaEscolha padrão; código crítico para o desempenho
AsSpan() com stackallocinput.AsSpan(0, 1).ToUpperInvariant(dest)Uma string, via interpolaçãoInvariável, explícitaVocê já tem um span em mãos
Array de caracteresinput.ToCharArray() e depois char.ToUpper()Array mais stringCultura atual, implícitaO array é necessário de qualquer forma
Substringinput[0].ToString().ToUpper() + input.Substring(1)Quatro stringsCultura atual, implícitaLegibilidade acima do custo
char.ToUpper com um intervalochar.ToUpper(input[0]) + input[1..]Duas stringsCultura atual, implícitaLegibilidade acima do custo
LINQFirstOrDefault().ToString() + .ToUpper()Quatro stringsCultura atual, implícitaNunca; é 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ênciaCultura atual, implícitaHá um padrão de verdade envolvido, não apenas um caractere
Escrita por ponteiro com código unsafefixed (char* p = input) *p = …Nenhuma, porque altera a string no próprio lugarCultura atual, implícitaNunca. 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.