Introdução: quando o código começa a "entortar"
\n\nImagine: você está escrevendo em C#, precisa exibir uma mensagem como Valor: {42}. Você usa String.Format("Valor: {0}", 42) — e obtém o resultado esperado. Agora tente exibir apenas {42} sem substituição. Ingenuamente escrevendo:
string result = String.Format("{{42}}");\n\nVocê espera ver {42}, mas em vez disso — um erro de formato ou uma string vazia. Familiar? Esta pergunta é uma das mais populares no Stack Overflow com milhares de visualizações. E hoje não vamos apenas dar a resposta, mas vamos olhar sob o capô do .NET para entender como funciona o escape de chaves em C#. Vamos lá!
Problema: por que {{ não funciona como em outras linguagens?
\n\nEm C#, as chaves {} são caracteres especiais em strings de formatação. Elas indicam onde começa e termina um placeholder para substituição de valor. Por exemplo, {0} significa "insira aqui o primeiro argumento".
Mas e se precisarmos exibir as próprias chaves como texto? Na maioria das linguagens (Python, Java), o escape funciona através da duplicação: {{ resulta em {. Em C# — a mesma lógica, mas com um detalhe: duas chaves de abertura {{ se transformam em uma {, mas apenas se não fizerem parte de um placeholder válido.
Exemplo da vida real: você está escrevendo um gerador de código em C# e precisa exibir a string if (x > 0) { return x; }. Se você simplesmente escrever:
string template = "if (x > 0) { return x; }";\n\nO compilador ou String.Format interpretará { return x; } como uma tentativa de formatação e lançará uma exceção FormatException.
Solução: escapando chaves corretamente em C#
\n\nMétodo clássico: String.Format e StringBuilder
\n\nA regra é simples: para exibir uma chave, escreva duas seguidas.
\n\n// Exibirá: {Hello World}\nstring result = String.Format("{{Hello World}}");\nConsole.WriteLine(result); // {Hello World}\n\nMas há uma nuance: se você precisar usar placeholders e chaves escapadas simultaneamente, a ordem é importante. Primeiro o escape, depois a substituição:
\n\nint value = 42;\n// Exibirá: Valor {42} entre chaves\nstring result = String.Format("Valor {{{0}}} entre chaves", value);\nConsole.WriteLine(result); // Valor {42} entre chaves\n\nVamos detalhar: {{{0}}} é {{ (uma chave) + {0} (placeholder) + }} (uma chave). Resultado: {42}.
Interpolação de strings ($"...") — abordagem moderna
\n\nA partir do C# 6, temos a interpolação de strings. Aqui o escape funciona de forma diferente — através da duplicação de chaves, mas sem placeholders internos:
\n\nint value = 42;\n// Exibirá: Valor {42} entre chaves\nstring result = $"Valor {{{value}}} entre chaves";\nConsole.WriteLine(result); // Valor {42} entre chaves\n\nObserve: na interpolação {{ resulta em {, e {value} é a substituição. Tudo funciona intuitivamente, se você lembrar da regra: duas chaves — uma chave.
Caso avançado: StringBuilder.AppendFormat
\n\nAo trabalhar com StringBuilder, use a mesma sintaxe:
var sb = new StringBuilder();\nsb.AppendFormat("{{0}} = {0}", 123);\nConsole.WriteLine(sb.ToString()); // {0} = 123\n\nComo funciona internamente: análise do mecanismo de formatação
\n\nVamos dar uma olhada nos fontes do .NET (eles são abertos no GitHub!). Quando você chama String.Format, internamente é executado o método FormatHelper, que itera sobre os caracteres da string de formato.