Executando verificação de segurança...
7

Callback em C#

Bom dia, pessoal.

Meu nome é João Paulo França e sou desenvolvedor .NET.
Hoje no meu primeiro post gostaria de compartilhar como podemos ter o comportamento de callback no C#. Muito comum e facilmente implementável/conhecido em Javascript, temos algumas alternativas em C# porém neste post vou abordar sobre o tipo Delegate
Mas antes... Vamos a algumas definições.

CALLBACK
Em poucas palavras, é um argumento de uma função/método que referencia um código executável (outra função/método).

Exemplo

Função que imprime um número formatadinho:

function imprimirNumeroFormatado(numero){
    console.log("Número: "+numero+".");
}

Função que calcula dois números e usa um CALLBACK para imprimir o resultado

function calcular(valorA, valorB, callbackImprimir){
    var resultadoCalculo = (valorA + valorB);
    callbackImprimir(numero);
}

Exemplo de uso das funções

calcular(4, 4, imprimirNumeroFormatado);

O resultado seria

"Número: 8"

Agora que sabemos um pouco sobre a definição do que é callback, vamos falar da alternativa escolhida: o Delegate.

Delegates
O delegate é um tipo em C#. Assim como podemos criar classes, interfaces, structs, etc. Em C# também podemos contar com os tipos Delegates. Esses tipos nada mais são do que representações de códigos executáveis em um tipo. Te remeteu a deinição de callback também? Massa, né?
Então, diferente de uma função Javascript, os delegates do C# precisam ter as definições de tipos de retorno.

Exemplo de declaração de um delegate:

public delegate int Calcular(int numeroA, int numeroB);

Se tu já és familiarizado com as assinaturas do C#, vai perceber que o que muda da definição de contratos de uma interface é a adição da palavra reservada 'delegate' depois do modificador de acesso do método.

Seguindo o mesmo exemplo que o dado em Javascript, vamos fazer as mesmas operações só que agora com delegates.

Definicão do Delegate:

public delegate void FormatadorNumero(decimal numero);

Função que imprime um número formatadinho:

public static void ImprimirNumeroFormatado(decimal numero)
{
    Console.WriteLine($"Número:{numero}.");
}

Função que calcula dois números e usa um CALLBACK para imprimir o resultado

static void Calcular(decimal numeroA, decimal numeroB, FormatadorNumero callback)
{
    decimal resultadoCalculo = numeroA + numeroB;
    callback(resultadoCalculo);
}

Exemplo de uso das funções

FormatadorNumero formatador = ImprimirNumeroFormatado;
Calcular(4, 4, formatador);

O resultado será o mesmo do Javascript

"Número: 8"

Em C# temos outras variações do uso do tipo Delegate assim como outros tipos mais específicos para podermos implementar um callback em nossa lógica.
Action, Func e EventHandlers são outras alternativas conhecidas que utilizam o delegate como base. Fica o convite para estudar sobre essas outras alternativas!

Espero que tenha contribuido com o conhecimento. Feliz 2023!

Fontes
Callback:
https://en.wikipedia.org/wiki/Callback_(computer_programming)
https://www.w3schools.com/js/js_callback.asp

Delegate C#:
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/delegates/

Contato:
https://jpdotfranca.github.io/

Carregando publicação patrocinada...
1
1

sei que isso poderia render uma postagem específica, mas pelo menos já para deixar anotado, também é possível fazer a aplicação de callbacks para o Java, a partir de classes como o Consumer.

Consumer

Como indica a documentação, o consumer funcionaria como um callback de void e estruturado por lambda, no qual para executar seria algo como:


public static void main(String[] args)
{
    long countVar = 0;
    receivesCallback(countVar, num -> System.out.println(num += 1));
    receivesCallback(countVar, num -> System.out.println(num += 2));
}

private static void receivesCallback(long count, Consumer<Long> callback)
{
    callback.accept(count);
}

Também possuindo classes com mais parâmetros como o BiConsumer e TriConsumer:

Exemplo de BiConsumer:


public static void main(String[] args)
{
    long countVar = 0;
    long counterVar = 1;
    receivesCallback(countVar, counterVar, (num, counter) -> System.out.println(num += counter));
    receivesCallback(countVar, counterVar, (num, counter) -> System.out.println(num += (counter + 1)));
}

private static void receivesCallback(long count, long counter, BiConsumer<Long, Long> callback)
{
    callback.accept(count, counter);
}

AInda teriam outras classes que funcionariam como um callback, como as classes Function ou Stream (com essa ultima funcionando para forEaches, filtros, etc), mas esses acredito que seria mais interessante dedicar a uma postagem mais completa.

1
1

Muito interessante eu programo com .net mas foram poucas as vezes que usei delegate, eu sempre achei confuso para usar, eu sempre fico pensando em que cenario eu usuário o delegate? kkk

Você tem algum exemplo mais real?

1

Buenas, agomes!

Então. Especificamente com delegate não tenho um exemplo real fora as variações que podem ser feitas usando EventHandlers, porém com Func e Action constantemente já tive que usar os callbacks.

Digamos que tu tenhas uma situação onde tu dependa do resultado de uma chamada externa de API para serviços externos.
Nesse nosso cenário hipotético, temos 3 integrações diferentes que fornecem o mesmo serviço que é consumido através da chamada de API, blza?
Numa situação como essa eu dependendo do retorno dessa chamada a API que está integrada para atualizar o banco de dados.

Pegando esse cenário, já tive que implementar callbacks com Action para não ter que replicar o código que salve no meu banco de dados nas 3 chamadas a APIs externas.

void AtualizarBancoDados(int idOperacao, int novoStatus){
    // lógica de atualização no banco de dados
}

``` c#
int ChamarAPI_1(int idOperacao, Action<int, int> atualizacaoBancoDadosCallback){
    // lógica de chamada para API 1 
    int resultado = ChamadaHttpExemplo();
    
    atualizacaoBancoDadosCallback(idOperacao, resultado);
} 
int ChamarAPI_2(int idOperacao, Action<int, int> atualizacaoBancoDadosCallback){
  // lógica de chamada para API 2 
    int resultado = ChamadaHttpExemplo();
    
    atualizacaoBancoDadosCallback(idOperacao, resultado);
} 
int ChamarAPI_3(int idOperacao, Action<int, int> atualizacaoBancoDadosCallback){
  // lógica de chamada para API 3 
    int resultado = ChamadaHttpExemplo();
    
    atualizacaoBancoDadosCallback(idOperacao, resultado);
} 

Exemplo de uso do callback:

int idOperacao = 22;
 ChamarAPI_2(idOperacao, AtualizarBancoDados);

O ganho que temos é não ter que replicar a lógica de atualização do banco de dados dentro das chamadas HTTP. Só respeitar a assinatura do método.

Obviamente temos várias formas de resolver um problemas, porém acho que sabendo do problema e diferentes formas de resolver, é legal ter essas "cartas na manga".

Vlw. Espero ter respondido satisfatóriamente.

1
1

Se me permite um adendo,

Sempre que você utiliza Action e Func você está usando um delegate, ambas as classes são abstrações do delegate para tornar o seu uso mais fácil e simples.

Action => quando você não precisa de algum valor de retorno.
Func => quando você precisa de algum retorno.

Das vezes em que vi/fiz uso de delegate foram em WPF apps, quando você depende de alguma ação para fazer outra. Ex: usuário acompanhando a % de processamento de alguma coisa, um delegate pode ser usado para ir atualizando a tela.

Recomendo a leitura do capitulo sobre delegates/actions/funcs do livro C# In a nutshell.

1