Delegates são a possibilidade que temos de delegar funções para métodos externos a execução atual.
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.
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.
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:
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.
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.
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.
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
.
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.
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}");
}
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);
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.
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!
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.
Este artigo atualmente utiliza a versão 5.0.0-rc.1 do ASP.NET/.NET, o que significa que ainda não...
Saiba como manter seu código limpo (Clean Code) seguindo algumas práticas sugeridas pelo Robert C...
Git é um sistema de controle de versões distribuídas, enquanto GitHub é uma plataforma que tem o ...
O Visual Studio Code é um editor de código criado pela Microsoft e que tem uma grande adoção pela...
O Angular nos fornece um esquema de rotas e navegação completo, simples e fácil de utilizar.
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.
Aulas disponíveis
horas de conteúdo
Alunos matriculados
Certificados emitidos
Temos mais de 16 cursos totalmente de graça e todos com certificado de conclusão.
Prefere algo mais Premium?
Compra única, parcelada em até
12x no cartão de crédito
Cobrado mensalmente via
cartão de crédito
Precisa de ajuda?
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.
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.
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.
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.
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:
Com certeza! O primeiro passo é começar e você pode fazer isto agora mesmo!
Começar de
graça
Receba em primeira mão todas as nossas novidades.