Versões do C#: o que está por trás dos números e como funciona por baixo dos panos
\n\nSe você já deu uma olhada nas discussões do Stack Overflow, certamente viu uma pergunta que reuniu milhares de visualizações: "What are the correct version numbers for C#?". Parece simples, não? Bastaria abrir a documentação e pronto. Mas na prática, tudo é mais complicado. Especialmente quando se descobre que o C# 7.0 foi lançado com o .NET Framework 4.7, e o C# 8.0 já veio com o .NET Core 3.0. E por que o C# 5.0 não está vinculado a uma versão específica do .NET? Bem-vindo ao mundo onde os números enganam.
\n\nNeste artigo, não vamos apenas listar as versões. Vamos olhar por baixo dos panos do compilador Roslyn, entender como os números de versão se relacionam com as plataformas e mostrar, com exemplos reais de código, quais recursos surgiram em cada versão. Vamos nessa!
\n\nPor que a pergunta sobre versões do C# é tão popular?
\n\nA resposta é simples: confusão entre as versões da linguagem C# e as versões do .NET. Muitos desenvolvedores pensam que C# 7.0 é a mesma coisa que .NET Framework 4.7. Mas não é. A linguagem C# segue seu próprio caminho, e a plataforma .NET, o seu. O compilador Roslyn (também conhecido como csc.exe) é a ponte entre eles. Ele pega o código em C# e o transforma em código IL (Intermediate Language), que é então executado pelo CLR (Common Language Runtime).
Aqui está o ponto-chave: uma mesma versão do compilador pode suportar diferentes versões da linguagem. Por exemplo, o compilador do Visual Studio 2019 pode compilar código C# 7.0, 7.1, 7.2, 7.3, 8.0 — dependendo das configurações do projeto. É por isso que há tanta confusão no Stack Overflow.
\n\nBreve história das versões do C#
\n\nVamos percorrer os principais marcos. Não vou listar todas as 12 versões em sequência — prefiro destacar aquelas que realmente mudaram o jogo.
\n\nC# 1.0 (2002) — O início
\nA primeira versão da linguagem foi lançada junto com o .NET Framework 1.0. Era uma linguagem gerenciada com sintaxe semelhante ao Java, mas com recursos únicos como propriedades (properties) e indexadores. Nada de LINQ, async/await ou generics. Simples, confiável, monótono.
\n\nC# 2.0 (2005) — Generics mudam tudo
\nCom esta versão vieram os genéricos (generics), métodos anônimos, iteradores (yield return) e classes parciais. Foi um avanço: em vez de escrever ArrayList com boxing/unboxing, você podia usar List<T>. O desempenho disparou.
// C# 2.0 — generics\nList<int> numbers = new List<int> { 1, 2, 3 };\nnumbers.ForEach(n => Console.WriteLine(n * 2)); // Método anônimo\n\n\nC# 3.0 (2007) — LINQ e lambdas
\nA versão mais revolucionária. O LINQ (Language Integrated Query) mudou a abordagem de trabalho com dados. Expressões lambda, métodos de extensão, tipos anônimos, inicializadores de objetos — tudo isso surgiu aqui. O compilador Roslyn ainda não havia nascido, mas foi no C# 3.0 que as bases para o futuro foram lançadas.
\n\n// C# 3.0 — LINQ e lambdas\nvar evenNumbers = Enumerable.Range(1, 10)\n .Where(x => x % 2 == 0)\n .Select(x => x * x);\nConsole.WriteLine(string.Join(", ", evenNumbers)); // 4, 16, 36, 64, 100\n\n\nC# 5.0 (2012) — Async/await
\nA programação assíncrona tornou-se mais simples do que nunca. async e await não são apenas açúcar sintático. O compilador gera uma complexa máquina de estados que gerencia as threads. Se você programa em C# 5.0 ou superior, pode esquecer o gerenciamento manual de BeginInvoke e EndInvoke.
// C# 5.0 — async/await\nasync Task<string> FetchDataAsync(string url)\n{\n using (var client = new HttpClient())\n {\n return await client.GetStringAsync(url);\n }\n}\n\n\nC# 6.0 (2015) — Açúcar para o dia a dia
\nEsta versão não trouxe novos paradigmas, mas tornou o código mais limpo: string interpolation