Apr 23, 2014

C# 6.0: Open Source!

Olá
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!)