C# funcional - usando ou devolvendo ações
-
26-09-2019 - |
Pergunta
Navegando pela rede para obter um melhor manuseio de falhas em C#, eu tenho o seguinte as seguintes estratégias de implementação. O primeiro é natural para mim, enquanto a outra implementação não tenho certeza de quais são suas vantagens?
1)
static void Fault(Action protectedBlock, Action faultHandler)
{
try
{
protectedBlock();
}
catch
{
faultHandler();
throw;
}
}
2)
static Action Fault(Action protectedBlock, Action faultHandler)
{
return () =>
{
try
{
protectedBlock();
}
catch
{
faultHandler();
throw;
}
};
}
2) A estratégia preferida ao desenvolver funções de ordem superior em C#?
E, estou me perguntando, se uma abordagem é mais eficiente que a outra.
Solução
O segundo caso é como uma fábrica de ação falha. Onde você passa um delegado do que você quer fazer, protectedBlock
, e um delegado do que fazer quando um Exception
ocorre faultHandler
. As ações são devolvidas embrulhadas em uma estrutura de tentativa/captura como um agregado Action
. Meu problema com esses dois métodos é não Exception
está realmente sendo pego, então quem vai pegar seu arremesso não tem informações sobre as quais agir.
A diferença na execução entre os 2 é quando eles realmente executam. O primeiro será executado quando for chamado. O segundo será executado sempre que o retorno Action
é chamado. Eu não acho que a diferença de eficiência seria significativa.
Outras dicas
(2) pode ser composto ainda mais, enquanto (1) apenas executa. Mas também não são exatamente "funcionais" como Action
não é uma função (compare com Func<A, R>
).
Então, com (2) você pode fazer:
Fault(someAction, Fault(firstTryFaultHandler, lastDitchFaultHandler))();
... e obtenha comportamento esperado. Isso não funciona com (1)
Em C#, a abordagem 2 pode ser confusa. Um chamador pode usar "Falha (A, B);" Esperando que A e possivelmente B sejam chamados. Em vez disso, um lambda é criado, devolvido e descartado. Em outras palavras, nada é feito.
Em relação à eficiência, a abordagem 2 é um pouco desperdiçada se a maioria das suas chamadas for do formulário "Falha (a, b) ();", ou seja, você invoca o lambda imediatamente. Nesta situação, você não precisa de um lambda.
Por esses motivos, eu preferiria a abordagem 1. Se você precisar adiar a execução, poderá apresentar um lambda explicitamente "() => falha (a, b)".