csharp06a00

Neste quinto e último artigo da série sobre C# 6.0 serão apresentados os seguintes recursos:

  • Chamadas assíncronas em blocos catch e finally;
  • Null-conditional operator;
  • Null-conditional operator e delegates.

OBSERVAÇÃO: todos os exemplos aqui descritos foram implementados a partir do Visual Studio 2015 CTP 5.

Chamadas assíncronas em blocos catch e finally

A palavra-chave await foi introduzida no C# 5.0, sendo utilizada na invocação de métodos assíncronos (os quais estariam marcados com a palavra-chave async em suas respectivas assinaturas).

No que se refere ao tratamento de exceções relacionadas à chamada de operações assíncronas, este tipo de ação poderia ser executado apenas dentro de blocos try. Entretanto, a tentativa de codificar uma instrução envolvendo a palavra-chave await nas seções catch e finally resultaria em erro, conforme indicado na Imagem 1.


Imagem 1. Erro ao tentar utilizar chamadas assíncronas nos blocos catch e finally

Com o C# 6.0 esta limitação foi superada, sendo possível agora a invocação de métodos assíncronos a partir de blocos catch e finally como especificado no exemplo da Listagem 1.

using System;

namespace TesteExceptionAwait
{
    public static class ClienteAsync
    {
        public static async void TesteAsyncAwaitException()
        {
            ClasseExemploAsync objTeste = new ClasseExemploAsync();

            try
            {
                await objTeste.ExecutarAsync();
            }
            catch (Exception ex)
            {
                await objTeste.TratarErroAsync(ex);
            }
            finally
            {
                await objTeste.FinalizarAsync();
            }
        }
    }
}

Listagem 1: Exemplo de uso de chamadas assíncronas nos blocos catch e finally

Null-conditional operator

A ocorrência da exceção NullReferenceException está associada a tentativas de acesso a referências nulas, sendo normalmente causada por falhas dos desenvolvedores em realizar uma verificação mais apropriada destas situações. A Listagem 2 apresenta um exemplo no qual uma série de condições são checadas, a fim de evitar erros na manipulação de objetos do tipo DirectoryInfo (namespace System.IO).

...

private static void TesteInformacoesDiretorio(
    DirectoryInfo diretorio)
{
    Console.WriteLine("Diretório: " +
        (diretorio != null ? diretorio.FullName : "Não definido"));

    string primeiroSubDiretorio = null;
    if (diretorio != null)
    {
        DirectoryInfo subDir = diretorio.GetDirectories().FirstOrDefault();
        if (subDir != null)
            primeiroSubDiretorio = subDir.FullName;
    }

    Console.WriteLine("Primeiro subdiretório: " +
        (primeiroSubDiretorio ?? "Não encontrado"));
}

...

Listagem 2: Exemplo de código com checagens para evitar exceções do tipo NullReferenceException

Conforme foi possível observar neste primeiro exemplo, a navegação ao longo de toda uma cadeia de objetos acaba por implicar em um esforço extra com o intuito de inibir a geração de exceções. Inúmeras verificações condicionais são executadas, empregando para isto cláusulas “if” ou, mesmo, o operador condicional “?” (um substituto de instruções “if-else”). Na última linha do método TesteInformacoesDiretorio nota-se ainda o uso do operador “??” (conhecido como null-coalesce, disponível desde as primeiras versões do .NET Framework), o qual retorna o segundo elemento de uma expressão caso o primeiro item resulte no valor null.

Procurando simplificar a realização destas verificações, o C# 6.0 permite o uso do símbolo “?” (neste caso chamado de “null-conditional operator”) a referências. O intuito deste novo recurso é dispensar os programadores da necessidade de elaboração de numerosas instruções condicionais, visando com isto a obtenção de um código mais enxuto e de fácil visualização/compreensão.

A Listagem 3 demonstra a utilização deste novo mecanismo, através de uma versão refatorada do método TesteInformacoesDiretorio. A maioria das checagens anteriores foi substituída pelo uso do operador “?” no final das referências, sendo que estas últimas podem vir representadas sob a forma de variáveis, propriedades ou ainda, pelo retorno de métodos que foram invocados. A execução deste bloco de código produzirá como resultado uma tela similar à que consta na Imagem 2.

using System;
using System.Linq;
using System.IO;

namespace TesteNullPropagation
{
    class Program
    {
        private static void TesteInformacoesDiretorio(
            DirectoryInfo diretorio)
        {
            Console.WriteLine("Diretório: " +
                (diretorio?.FullName ?? "Não definido"));

            Console.WriteLine("Primeiro subdiretório: " +
                (diretorio?.GetDirectories().FirstOrDefault()?.FullName ??
                 "Não encontrado"));
        }

        static void Main(string[] args)
        {
            TesteInformacoesDiretorio(
                new DirectoryInfo(@"C:\Windows"));

            Console.WriteLine(Environment.NewLine);
            TesteInformacoesDiretorio(
                new DirectoryInfo(@"C:\Program Files\IIS Express\pt-BR"));

            Console.WriteLine(Environment.NewLine);
            TesteInformacoesDiretorio(null);

            Console.ReadKey();
        }
    }
}

Listagem 3: Exemplo de utilização do recurso null-conditional operator


Imagem 2. Resultado dos testes demonstrando o uso do operador null-conditional operator

Null-conditional operator e delegates

A seção anterior demonstrou como o recurso conhecido como “Null-conditional operator” pode simplificar a manipulação de referências em códigos escritos em C#. No caso específico de delegates, o uso do operador “?” associado a tais estruturas apresenta algumas peculiaridades.

Normalmente empregados na implementação de eventos, os delegates costumam ser utilizados em conjunto com classes derivadas do tipo EventArgs (esta última pertence ao namespace System, representando dados que podem ser úteis aos métodos que implementam um evento específico).

Na Listagem 4 é possível observar:

  • A implementação da classe AtualizacaoCotacaoEventArgs, a qual conterá informações descrevendo a atualização de uma cotação de moeda estrangeira num determinado horário;
  • O delegate AtualizacaoCotacaoHandler, que também depende do tipo AtualizacaoCotacaoEventArgs. Essa estrutura corresponde à representação de um evento, sendo disparada quando o valor de cotação de uma moeda estrangeira for modificado em uma outra classe apresentada mais adiante.

Estas construções servirão de base para a discussão a respeito do uso do operador “?” junto a delegates.

using System;

namespace TesteNullPropagationDelegates
{
    public class AtualizacaoCotacaoEventArgs : EventArgs
    {
        public string Simbolo { get; }
        public DateTime UltimaAtualizacao { get; }
        public double ValorCotacao { get; }

        public AtualizacaoCotacaoEventArgs(string simbolo,
            DateTime ultimaAtualizacao, double valorCotacao)
        {
            this.Simbolo = simbolo;
            this.UltimaAtualizacao = ultimaAtualizacao;
            this.ValorCotacao = valorCotacao;
        }
    }

    public delegate void AtualizacaoCotacaoHandler(
        object sender, AtualizacaoCotacaoEventArgs e);
}

Já na Listagem 5 está a definição da classe CotacaoMoeda. Este tipo contém dados como a descrição, o símbolo de uma moeda estrangeira, a data de referência, o horário de última atualização, além do valor da cotação propriamente dita.

No caso específico do valor, a propriedade ValorCotacao é o único elemento deste tipo ao qual podem ser atribuídos diretamente novos valores. Se um método com a assinatura do delegate AtualizacaoCotacaoHandler estiver associado ao evento AtualizacaoCotacao (uma verificação com “if” é então realizada), o mesmo será acionado automaticamente como resposta a uma modificação na instância do tipo CotacaoMoeda.

using System;

namespace TesteNullPropagationDelegates
{
    public class CotacaoMoeda
    {
        public string Moeda { get; }
        public string Simbolo { get; }
        public DateTime DataCotacao { get; }

        private DateTime _ultimaAtualizacao;

        public DateTime UltimaAtualizacao
        {
            get { return this._ultimaAtualizacao; }
        }

        private double? _valorCotacao;

        public double ValorCotacao
        {
            get { return this._valorCotacao ?? 0; }

            set
            {
                this._valorCotacao = value;
                this._ultimaAtualizacao = DateTime.Now;

                if (AtualizacaoCotacao != null)
                {
                    AtualizacaoCotacao(this,
                        new AtualizacaoCotacaoEventArgs(
                            this.Simbolo,
                            this._ultimaAtualizacao,
                            value));
                }
            }
        }

        public event AtualizacaoCotacaoHandler AtualizacaoCotacao;

        public CotacaoMoeda(
            string moeda, string simbolo)
        {
            this.Moeda = moeda;
            this.Simbolo = simbolo;
            this.DataCotacao = DateTime.Now.Date;
        }
    }
}

Diante do que foi exposto para a classe CotacaoMoeda, é fácil pressupor que o operador “?” poderia ser usado em conjunto com a referência do tipo AtualizacaoCotacaoHandler (no caso, o evento AtualizacaoCotacao). Contudo, a tentativa em se empregar diretamente o recurso null-conditional operator a este item resultará em um erro, como indicado na Imagem 3.


Imagem 3. Erro ao tentar invocar diretamente um delegate com o operador ?

A solução para este problema passa pelo uso do método Invoke (Listagem 6), com o mesmo seguindo a mesma assinatura do delegate ao qual o evento AtualizacaoCotacao se refere. O operador “?” continuará associado a AtualizacaoCotacao, de forma a possibilitar a execução de Invoke somente se existir uma implementação de AtualizacaoCotacaoHandler ligada à instância do tipo CotacaoMoeda considerada.

...

private double? _valorCotacao;

public double ValorCotacao
{
    get { return this._valorCotacao ?? 0; }

    set
    {
        this._valorCotacao = value;
        this._ultimaAtualizacao = DateTime.Now;

        AtualizacaoCotacao?.Invoke(this,
            new AtualizacaoCotacaoEventArgs(
                this.Simbolo,
                this._ultimaAtualizacao,
                value));
    }
}

...

Na Listagem 7 está um exemplo de utilização das estruturas até aqui descritas. O método processarAtualizacaoCotacao corresponde à implementação do evento AtualizacaoCotacao, sendo acionado a cada vez que um valor for atribuído à propriedade ValorCotacao. O resultado da execução deste programa de testes pode ser visto na Imagem 4.

using System;
using System.Threading;

namespace TesteNullPropagationDelegates
{
    class Program
    {
        private static void processarAtualizacaoCotacao(
            object sender, AtualizacaoCotacaoEventArgs e)
        {
            Console.WriteLine(
                $"Moeda: {e.Simbolo} - " +
                $"Data/Hora da Cotação: {e.UltimaAtualizacao :HH:mm:ss} - " +
                $"Valor da Cotação: {e.ValorCotacao :0.000}");
        }

        static void Main(string[] args)
        {
            CotacaoMoeda cotacao = new CotacaoMoeda(
                "Dólar norte-americano", "US$");
            cotacao.AtualizacaoCotacao += processarAtualizacaoCotacao;

            cotacao.ValorCotacao = 2.858;

            Thread.Sleep(3000);
            cotacao.ValorCotacao = 2.866;

            Console.ReadKey();
        }
    }
}


Imagem 4. Resultado dos testes utilizando o recurso Null Propagation em conjunto com um delegate

Conclusão

Este post foi a última parte da série sobre novidades do C# 6.0 (ao menos até o momento em que escrevo o mesmo). Conforme sempre ressaltei ao longo destes 5 artigos, as mudanças na linguagem não exigem um trabalho drástico de readaptação. Na verdade, os recursos disponibilizados têm como meta principal simplificar a codificação de soluções em .NET, estando inclusas nisto melhorias na própria IDE do Visual Studio 2015 (o debugging de expressões lambdas foi certamente uma das funcionalidades mais esperadas).

Agradeço ainda ao apoio e sugestões da comunidade ao longo de praticamente 2 meses em que trabalhei nesta série.

Até uma próxima oportunidade!

Links

A C# 6.0 Language Preview
http://msdn.microsoft.com/en-us/magazine/dn683793.aspx

Languages features in C# 6 and VB 14
https://roslyn.codeplex.com/wikipage?title=Language%20Feature%20Status&referringTitle=Documentation

New Features in C# 6
http://blogs.msdn.com/b/csharpfaq/archive/2014/11/20/new-features-in-c-6.aspx

The New and Improved C# 6.0
http://msdn.microsoft.com/en-us/magazine/dn802602.aspx

What’s New In C# 6.0
http://channel9.msdn.com/Events/Visual-Studio/Connect-event-2014/116

Renato Groffe

Atua como consultor em atividades voltadas ao desenvolvimento de softwares há mais de 13 anos. Bacharel em Sistemas de Informação, com especialização em Engenharia de Software. Microsoft Certified Technology Specialist (Web, WCF, Distributed Applications, ADO.NET, Windows Forms), Microsoft Specialist (HTML5 with JavaScript and CSS3, Developing ASP.NET MVC 4 Web Applications), Oracle Certified Associate (PL/SQL), Sun Certified (SCJP, SCWCD), ITIL Foundation V2, Cobit 4.1 Foundation.

Facebook Google+ 

Comentários

comentarios