No post anterior eu comentei sobre algumas das possíveis funcionalidades da próxima versão do C#.
No evento Build 2014 (que ocorreu algumas semanas atras) a MS confirmou algumas destas funcionalidades bem como apresentou algumas que eu não tinha conhecimento (e que realmente gostei). Abaixo segue uma discussão das mesmas, começando por aquela que mais me animou:
Projeto Roslyn
A MS não apensas confirmou que a próxima versão dos compiladores C#/VB.Net serão baseados neste projeto, como surpreendeu muita gente ao anunciar que o mesmo agora é um projeto open source! Isso mesmo, você ouviu correto. Você já pode baixar o código fonte e ver com seus próprios olhos (inclusive muitas das funcionalidades anunciadas para a próxima versão do C# já funcionam com o compilador implementado baseado neste projeto).Com esta abertura, subtamente, um monte de informação se tornou disponível. Por exemplo, você pode ver aqui a lista completa de funcionalidades para a próxima vesão do C# / VB.NET.
Apesar de, na minha opnião, baixar o código fonte e estudar o mesmo seja o caminho para aprender sobre o Roslyn, caso você não tenha tido contato com o mesmo anteriormente eu recomendo baixar/instalar o End user preview (na minha opnião, um caminho muito mais simples).
Propriedades automaticas (somente leitura)
Implementado; a medida que eu uso mais estes novos recursos eu gosto cada vez mais dos mesmos! :) Veja o exemplo abaixo (especial atenção à linha 3):class Test(string name) { public string Name { get; } = name; public static void Main(string[] args) { System.Console.WriteLine(new Test(args[0]).Name); } }
“Primary Constructors”
Melhor do que eu esperava. Não apenas o comportamento dos parâmetros definidos em primary constructors obedecem as regras de escopo atuais como também é possível obter o resultado que eu propus no post anterior, ou seja, o conceito de “campos automáticos”. Veja o exemplo abaixo:class Example (private int value, string n) { private string name = n; public static void Main(string[] args) { new Example(42, "foo").Print(); } private void Print() { System.Console.WriteLine("{0} = {1}", name, value); } }Observe que o campo value (usado na linha 12) nunca foi declarado explicitamente (pelo menos não usando-se a sintaxe tradicional)
“using” para membros estáticos (ou static using)
Lembra-se que eu comentei que não fiquei muito empolgado com esta funcionalidade? Pois é, depois de usá-lo bastante (ao escrever os exemplos deste post) estou começando a mudar de idéia ;)Declaração inline para parâmetros out
Mais um uau! Acontece que meu entendimento da funcionalidade estava incorreta! Na realidade o nome correto da mesma é “declaration expressions” (desculpa, não sei uma boa tradução sem entrar em detalhes sobre conceitos de compiladores, o que não é meu objetivo neste post - além de não conhecer o assunto o suficiente).Na prática, nesta nova versão, podemos declarar variáveis, por exemplo, na expressão de testes de if s, for s, etc.
Veja o exemplo abaixo:
using System.IO; using System.Console; class Test { public static void Main(string[] args) { if (args.Length == 0) { WriteLine("arquivo?"); return; } using(var arq = File.OpenText(args[0])) { while ( (var line = arq.ReadLine()) != null ) { WriteLine("> {0}", line); } } } }Note que na linha 16 declaramos a variável line na expressão de teste do bloco while!
Como sempre, quando bem utilizado, este recurso tem o potencial de eliminar “ruido” do código.
Funcionalidades que eu não estava ciente (e não comentei no post anterior)
Separadores em literais e constantes binárias
Ok, essa é mais uma “me too” copiado do Java ;)Confesso que torci o nariz quando vi a funcionalidade implementada no Java, mas após usá-la algumas vezes acabei me convencendo da conveniência da mesma (infelizmente não esta implementado nesta preview ainda).
Basicamente, agora podemos usar o separador _ em valores de constantes além de declaras constantes binárias. Veja o exemplo:
class Test { public static void Main(string[] args) { var value = 0xDEAD_BEEF; var bin = 0b00000100; } }
Exception Filters
Na versão corrente do C# (5.0), cenários que demandam tratamento específico de exceções baseados em dados da mesma acabam sendo um tanto quanto difíceis. Imagine o exemplo abaixo:class Example { public void M() { try { } catch(MyException me) { if (me.SomeProp == some_value) { // trata a exceção aqui } else { throw; } } } }O if dentro do bloco catch (linha 10) tem como único objetivo filtrar exceções baseado no valor de uma propriedade. A partir da versão 6.0 do C# a seguinte sitaxe é válida:
class Example { public static void Main() { try { } catch(MyException me) if (CheckException(me)) { // trata a exceção aqui } } private static bool CheckException(MyException ex) { return ex.SomeProp == 42; } } class MyException : System.Exception { public int SomeProp; }Eu, particularmente gostaria de poder usar uma sintaxe mais parecida com a declaração de uma expressão lambda:
class Example { public static void Main() { try { } catch(MyException me => me.SomeProp == 42) { // trata a exceção aqui } } } class MyException : System.Exception { public int SomeProp; }
Membros indexados
Esta funcionalidade tem como objetivo simplificar o processo de inicialização de tipos que sobrecarregam o operador de indexação [] introduzindo uma sintaxe alternativa, mais natural em alguns cenários:using System.Collections.Generic; using System.Console; class Example { private IDictionary<string, int> data = new Dictionary<string, int>(); public int this[string i] { get { return data[i]; } set { data[i] = value ; } } public static void Main(string[] args) { var dic2 = new Dictionary<int, string>() { {0, "nada" }, {1, "uno" } }; var dic = new Dictionary<int, string>() { [0] = "nada", [1] = "uno" }; var inst = new Example { $zero = 0, $one = 1, $two = 2 }; WriteLine("Hello World from roslyn! {0} {1}", inst.$zero, inst["zero"]); } }Outro ponto positivo é que esta sintaxe é válida também na inicialização de objetos.
Note que, além da sintaxe simplificada quando o operador de indexação utiliza strings, também pode-se utilizar a sintaxe de indexadores (quando a chave não é string).
Recurso muito bem vindo em minha opinião.
Extension “Add” Methods em inicializadores de coleções
Caso você nunca tenha usado “collection initializers” em seus programas veja o exemplo abaixo:
using System.Collections; class Coll : IEnumerable { public void Add(string s) { System.Console.WriteLine(s); } public IEnumerator GetEnumerator() { return null; } } class Test { public static void Main(string[] args) { var x = new Coll() { "foo", "bar", "baz" }; } }A existência do método Add() (linhas 5 ~ 8) (bem como implementar IEnumerable) é uma condição indispensável para que a syntaxe de “Collection Initializers” (linhas 17 ~ 22) possa ser utilizada. Até a versão 5.0 (inclusive) do C# este método tinha que, obrigatóriamente, ser um método na hierarquia da classe.
A partir da versão 6.0 do C# Extension Methods também são suportados:
using System.Collections; static class Extensoes { public static void Add(this Coll coll, string s) { System.Console.WriteLine(s); } } class Coll : IEnumerable { public IEnumerator GetEnumerator() { return null; } } class Test { public static void Main(string[] args) { var x = new Coll() { "foo", "bar", "baz" }; } }
Outras funcionalidades planejadas
Dentre as funcionalides comentadas no post anterior que ainda não foram implementadas mas que estão planejadas podemos citar:- Property Expressions / “Method expressions”: Na documentação do projeto aparecem com o nome de Expression-bodied members.
- Enumerables como parâmetros do tipo “params”
- Inferência de tipos a partir de parêmtros de construtores
Você pode ver a lista completa das funcionalidades planejadas / já implementadas para as próximas versões do C#/VB.NET nestes links.
O que você acha? (eu, particularmente, estou entusiasmado com o futuro da plataforma .Net!)