Delegates

Delegates são a possibilidade que temos de delegar funções para métodos externos a execução atual.

Delegates

Sendo mais teórico, delegates ou delegados, são ponteiros que apontam para funções, normalmente externas ao método em execução.

Podemos assemelhar os delegates aos callbacks que temos em outras linguagens como JavaScript por exemplo, e embora fora do mundo C# ele tenha um forte uso, particularmente não vejo muito sua aplicabilidade no C#.

Talvez isto se deva ao mal entendimento do assunto ou falta de exemplos mais práticos, como o que veremos aqui.

Contexto

Antes de começar, quero contextualizar o uso dos delegates neste cenário, e frisar que existem várias formas de resolver este problema, mas focaremos aqui no tema em pauta.

Quando começamos a segmentar nossas aplicações em pacotes, as coisas começam a ficar complicadas. Não é fácil pensar em como vamos expor o sucesso ou fracasso da execução de um serviço.

Vamos tomar como base os serviços de pagamento. Hoje por exemplo, no balta temos suporte a PayPal e PagarMe, além de alguns testes que já fiz no Stripe.

Desta forma, são três serviços distintos para gerenciar, cada um tem seu retorno, seu objeto de sucesso e seu objeto de erro.

Do outro lado, nossa API tem uma padronização, por exemplo, TODA E QUALQUER resposta vem no seguinte formato.

public class Response<T> : Notifiable<Notification>
{
    public Response()
    {
    }

    public Response(T data)
    {
        Status = 200;
        Data = data;
    }

    public string Message { get; set; }
    public int Status { get; set; }
    public T Data { get; set; }
}

A resposta é composta pelo Notiiable que vem do Flunt, seguido da mensagem, status e dados genéricos. Tem bem mais coisas, mas reduzi a isto apenas para nosso entendimento.

Em resumo, qualquer comunicação entre serviços do balta ou qualquer resposta da API segue este padrão.

Desafio

O uso dos delegates neste cenário surgiu justamente para gerenciar os erros ocorridos em APIs externas. No caso, tanto para o PayPal quanto para o PagarMe, fazemos um POST e esperamos apenas uma string que é o ID da assinatura criada no serviço.

O mais difícil é a parte de gerenciar erros. Ambos serviços são chamados via RestSharp, então você já imagina que podemos ter erros de:

  • URL inválida
  • Serialização para JSON
  • Serviço fora
  • Deserialização do JSON
  • E por ai vai

ENTÃO JOGA TUDO DENTRO DE UM TRY/CATCH! Brincadeira, ai não rolaria por não sabermos onde o erro aconteceu nem qual o motivo. Então fazemos vários blocos try por que precisamos ser precisos sobre o erro que está acontecendo.

Em pouco tempos chegamos em algo parecido com isto, mas claro, que queríamos muito reaproveitar estas chamadas.

var response = new Response<IEnumerable<PagarMeSubscriptionModel>>();
var client = new RestClient("URL");
var httpRequest = new RestRequest("URL", DataFormat.Json);
httpRequest.AddParameter("count", pageSize);

IRestResponse<IEnumerable<PagarMeSubscriptionModel>> httpResponse;
try
{
    httpResponse = client.Get<IEnumerable<PagarMeSubscriptionModel>>(httpRequest);
}
catch (Exception ex)
{
    response.ToInternalServerError(ex);
    return response;
}

if (httpResponse.IsSuccessful)
{
    response.ToSuccess(httpResponse.Data);
    return response;
}

try
{
    var error = JsonConvert.DeserializeObject<PagarMeErrorJson>(httpResponse.Content);
    response.Message = "Não foi possível listar as assinaturas";
    foreach (var item in error.Errors)
        response.AddNotification(item.ParameterName, item.Message);

    return response;
}
catch (Exception ex)
{
    response.ToInternalServerError(ex);
    return response;
}

Se você notar, boa parte do código pode ser reutilizado, podemos utilizar T como parâmetro na requisição e tornar o método todo genérico.

Exemplo real

O que acontece no exemplo acima é a tratativa de sucesso e diversas falhas diferentes dentros do mesmo método, e todas acopladas a um modelo apenas.

Para o caso do PayPal, que o código seria bem parecido, o mesmo precisaria ser reescrito, e terminaríamos com dois serviços fazendo a mesma coisa.

IMPORTANTE: Não vale criar flag isPayPal ou isPagarMe e chavear dentro do método hein. A ideia aqui é reduzir complexidade e código.

RestSharp

O RestSharp faz requisições HTTP em APIs e podemos já serializar o resultado em um objeto, o que nos facilita muito a vida.

Tudo o que ele precisa é de um Request, um Client e irá retornar um Response, que pode ser tipado, inclusive utilizando genérics.

Nosso maior problema então é, caso a requisição seja executada com sucesso, tratar os dados, e caso haja algo errado, tratar os erros, porém, fora do método que faz a requisição.

Por que fora? Porque a maior parte do código acim é só para fazer isto, e ele que está engessando nossas mudanças.

Iniciando o projeto

Para este exemplo você só precisa criar um Console App e adicionar as duas classes abaixo nele.

dotnet new -o DelegatesSample
cd DelegatesSample
code .

Com o VS Code aberto, crie dois arquivos chamado ArticleModel e ErrorModel na pasta Models.

Criando Delegates

Nosso primeiro passo aqui é criar os delegates, eles ficam acima do método main no Program.cs.

private delegate void OnSuccessDelegate(IEnumerable<ArticleModel> data);
private delegate void OnErrorDelegate(ErrorModel data);

Com oa "ponteiros" definidos, podemos utiliza-los como parâmetros nos métodos, e se você observou bem, eles remetem a sucesso e erro.

Nosso objetivo agora é colocar estes delegates como parâmetros em nosso serviço e gerenciarmos sucesso ou erro externo a ele. É importante notar também que estamos enviando parâmetros para os delegates.

private static void GetArticles(
    OnSuccessDelegate onSuccess, 
    OnErrorDelegate onError)
{
    ...
}

Com tudo declarado, agora temos dois métodos que podem ser chamados na execução do nosso código, o onError e o onSuccess, que podem ser invocados da seguinte forma.

onSuccess(data);
onError(new ErrorModel($"Ocorreu um erro: {ex.Message}"));

Você já consegue ter ideia do que vai acontecer neste ponto? Se sim, paarabéns ❤️

Continuando, como resultado final do nosso serviços temos os seguinte código.

private static void GetArticles(
    OnSuccessDelegate onSuccess, 
    OnErrorDelegate onError)
{
    try
    {
        var data = new List<ArticleModel>();
        data.Add(new ArticleModel(1, "Orientação a objetos"));
        data.Add(new ArticleModel(2, "Fundamentos do C#"));
        //throw new Exception("Deu ruim");
        onSuccess(data);
    }
    catch (Exception ex)
    {
        onError(new ErrorModel($"Ocorreu um erro: {ex.Message}"));
    }
}

Note que deixei um throw new Exception... para forçarmos um erro e vermos os dois scenários.

Gerenciando os callbacks

Neste caso, vamos ter dois métodos, um para sucesso e outro para erro, e como eu comentei nas aulas de delegates do curso de Fundamentos do C#, as assinaturas precisam ser a mesmas.

private static void OnSuccess(IEnumerable<ArticleModel> data)
{
    foreach (var item in data)
    {
        Console.WriteLine(item.Title);
    }
}

private static void OnError(ErrorModel data)
{
    Console.WriteLine($"ERRO: {data.Message}");
}

Ligando tudo

Até o momento temos as declarações dos delegates, o uso destes como parâmetro no método GetArticles e internamente no mesmo método.

Precisamos agora conectar tudo, e a chave para isto são os Handlers que vão manipular estas interações entre nossos códigos.

O primeiro passo a ser realizado é ligar os delegados com os métodos que criamos, e que irão manipular os resultados.

OnSuccessDelegate onSuccessHandler = OnSuccess;
OnErrorDelegate onErrorHandler = OnError;

E depois precisamos chamar o método GetArticles que vai esperar os dois manipuladores que criamos acima.

 GetArticles(onSuccessHandler, onErrorHandler);

Resultado

Execute o programa com dotnet run e verá uma lista com dois artigos. Comente a linha onde forçamos um Exception e rode novamente para ver o erro na tela,

Em suma o que está acontecendo é que os delegates são declarados no topo, ele ficam reporesentando literamente ponteiros neste momento, e assim que criamos uma função que atende os requisitos (Assinatura) eles já podem ser ligados.

Esta ligação é feita através de um handler, cujo pode invocar o método e que também podemos passar para outras funções.

Por fim, não temos a menor preocupação em relação a execução da função pois os casos estão sendo tratados externos a ela.

As funções ficam menores, mais expressivas e evitamos ficar utilizando Try/Catch em tudo ou mesmo retornar um objeto de sucesso que representa um erro.

Outras dicas

Fizemos um exemplo inicial utilizando conceitos básicos, mas já pensou no uso dos delegates com generics? Algo como:

private delegate void OnSuccessDelegate<T>(T data) where T : Entity;

Dado este delegate, podemos ter vários handlers para manipular um ou vários callbacks, cada um de uma forma diferente!

Conclusão

Muitas vezes subestimamos o uso de funcionalidades. Normalmente focamos em propriedades e métodos, mas esquecemos de delegates, event e vários outros recursos legais que o C# nos fornece.

Fontes

Populares

Priority Queue

Priority Queue ou fila prioritária é um tipo de lista inclusa no C# 10 que permite que seus itens...


Implicit Operators no C#

Implicit Operators permitem adicionar comportamentos de conversão a objetos Built In ou complexos...


ASP.NET 5 – Autenticação e Autorização com Bearer e JWT

Este artigo atualmente utiliza a versão 5.0.0-rc.1 do ASP.NET/.NET, o que significa que ainda não...


Clean Code - Guia e Exemplos

Saiba como manter seu código limpo (Clean Code) seguindo algumas práticas sugeridas pelo Robert C...


Git e GitHub - Instalação, Configuração e Primeiros Passos

Git é um sistema de controle de versões distribuídas, enquanto GitHub é uma plataforma que tem o ...


Compartilhe este artigo



Conheça o autor

Me dedico ao desenvolvimento de software desde 2003, sendo minha maior especialidade o Desenvolvimento Web. Durante esta jornada pude trabalhar presencialmente aqui no Brasil e Estados Unidos, atender remotamente times da ?ndia, Inglaterra e Holanda, receber 8x Microsoft MVP e realizar diversas consultorias em empresas e projetos de todos os tamanhos.





2.651

Aulas disponíveis

249

horas de conteúdo

64.156

Alunos matriculados

45.399

Certificados emitidos





Comece de graça agora mesmo!

Temos mais de 19 cursos totalmente de graça e todos com certificado de conclusão.

Começar


Prefere algo mais Premium?

Conheça nossos planos



Premium semestral

Compra única, parcelada em até
12x no cartão de crédito


12x R$

49

,78

=R$ 597,36
  • 6 meses de acesso
  • Acesso à todo conteúdo
  • Emissão de Certificado
  • Tira Dúvidas Online
  • 59 cursos disponíveis
  • 10 carreiras disponíveis
  • 161 temas de tecnologia
  • Conteúdo novo todo mês
  • Encontros Premium

Começar agora

Política de privacidade

Premium anual

Compra única, parcelada em até
12x no cartão de crédito


12x R$

84

,78

=R$ 1.017,36
  • 1 ano de acesso
  • Acesso à todo conteúdo
  • Emissão de Certificado
  • Tira Dúvidas Online
  • 59 cursos disponíveis
  • 10 carreiras disponíveis
  • 161 temas de tecnologia
  • Conteúdo novo todo mês
  • Encontros Premium

Começar agora

Política de privacidade



Precisa de ajuda?

Dúvidas frequentes



  • Posso começar de graça?

    Sim! Basta criar sua conta gratuita no balta.io e começar seus estudos. Nós contamos com diversos cursos TOTALMENTE gratuitos e com certificado de conclusão.

  • Vou ter que pagar algo?

    Nós temos cursos gratuitos e pagos, porém você não precisa informar nenhum dado de pagamento para começar seus estudos gratuitamente conosco. Os cursos gratuitos são completos e com certificado de conclusão, você não paga nada por eles.

    Porém, caso queira algo mais Premium , você terá acesso à diversos benefícios que vão te ajudar ainda mais em sua carreira.

  • Por onde devo começar?

    Siga SEMPRE as nossas Carreiras , elas vão te orientar em todos os sentidos. Os cursos já estão organizados em categorias e carreiras para facilitar seu aprendizado.
    Nossa sugestão para aprendizado é começar pelo Backend e seguindo para Frontend e Mobile.

    • Backend
    • Frontend
    • Mobile

  • Os cursos ensinam tudo que preciso?

    Nenhum curso no mundo vai te ensinar tudo, desculpa ser sincero! Os cursos são uma base, eles fornecem por volta de 30% do que você precisa aprender, o resto é com você, com dedicação e MUITA prática.

  • O que eu devo estudar?

    Java ou .NET? Angular ou React? Xamarin ou Flutter? A resposta é simples e direta: "Você já sabe o básico?"

    Se você ainda não sabe BEM o básico, ou seja, os fundamentos, OOP, SOLID, Clean Code, está perdendo tempo estudando Frameworks ou até coisas mais avançadas como Docker. Foque nos seus objetivos primeiro.
    Agora se você está indeciso sobre qual Framework estudar, a boa notícia é que o mercado neste momento está bem aquecido e você tem várias oportunidade. Desta forma o que levaríamos em conta para tomar esta decisão seria:

    • Já sei o básico
    • O Framework/Tecnologia tem mercado onde eu estou (região)
    • O Framework/Tecnologia é utilizado em uma empresa onde quero atual
    • O Framework/Tecnologia resolve meu problema
    • Eu gosto de utilizar o Framework/Tecnologia

  • Estou pronto para estudar no balta.io?

    Com certeza! O primeiro passo é começar e você pode fazer isto agora mesmo!

    Começar de graça

Ainda tem dúvidas?





Assine nosso Newsletter

Receba em primeira mão todas as nossas novidades.

Cadastrar


balta.io