Reduzir duplicado código de tratamento de erros em C #?

votos
32

Eu nunca fui completamente feliz com a maneira como as obras de tratamento de exceções, há muito exceções e try / catch traz para a mesa (pilha desenrolamento, etc.), mas parece quebrar um monte do modelo OO no processo.

De qualquer forma, aqui está o problema:

Vamos dizer que você tem alguma classe que envolve ou inclui operações de IO de arquivo em rede (por exemplo, leitura e escrita para algum arquivo em algum caminho UNC em particular em algum lugar). Por várias razões que você não quer essas operações de IO a falhar, por isso, se você detectar que eles falham você repeti-los e você continuar a tentar-los até que eles conseguem ou chegar a um timeout. Eu já tenho uma classe RetryTimer conveniente que eu posso instanciar e usar para dormir o segmento atual entre tentativas e determinar quando o período de tempo limite esgotou-se, etc.

O problema é que você tem um monte de operações de IO em vários métodos dessa classe, e você precisa para embrulhar cada um deles em try-catch lógica / repetição.

Aqui está um trecho de exemplo de código:

RetryTimer fileIORetryTimer = new RetryTimer(TimeSpan.FromHours(10));
bool success = false;
while (!success)
{
    try
    {
        // do some file IO which may succeed or fail
        success = true;
    }
    catch (IOException e)
    {
        if (fileIORetryTimer.HasExceededRetryTimeout)
        {
            throw e;
        }
        fileIORetryTimer.SleepUntilNextRetry();
    }
}

Então, como você evitar a duplicação da maior parte deste código para cada operação IO arquivo ao longo da classe? Minha solução foi usar blocos delegado anônimo e um único método na classe que executou o bloco delegado passado para ele. Isso me permitiu fazer coisas como esta em outros métodos:

this.RetryFileIO( delegate()
    {
        // some code block
    } );

Eu gosto deste um pouco, mas deixa muito a desejar. Eu gostaria de saber como outras pessoas iria resolver este tipo de problema.

Publicado 04/08/2008 em 20:21
fonte usuário
Em outras línguas...                            


4 respostas

votos
13

Este parece ser uma excelente oportunidade para ter um olhar para Aspect Oriented Programming. Aqui está um bom artigo sobre AOP em .NET . A idéia geral é que você extrair a preocupação cross-funcional (ie Retry para x horas) em uma classe separada e, em seguida, você iria anotar quaisquer métodos que precisam modificar o seu comportamento nesse sentido. Veja como pode parecer (com um método de extensão agradável em Int32)

[RetryFor( 10.Hours() )]
public void DeleteArchive()
{
  //.. code to just delete the archive
}
Respondeu 05/08/2008 em 10:43
fonte usuário

votos
4

Basta saber, o que você sente deixa o seu método a ser desejado? Você poderia substituir o delegado anônimo com uma .. chamado? delegado, algo como

    public delegate void IoOperation(params string[] parameters);

    public void FileDeleteOperation(params string[] fileName)
    {
        File.Delete(fileName[0]);
    }

    public void FileCopyOperation(params string[] fileNames)
    {
        File.Copy(fileNames[0], fileNames[1]);
    }

    public void RetryFileIO(IoOperation operation, params string[] parameters)
    {
        RetryTimer fileIORetryTimer = new RetryTimer(TimeSpan.FromHours(10));
        bool success = false;
        while (!success)
        {
            try
            {
                operation(parameters);
                success = true;
            }
            catch (IOException e)
            {
                if (fileIORetryTimer.HasExceededRetryTimeout)
                {
                    throw;
                }
                fileIORetryTimer.SleepUntilNextRetry();
            }
        }
    }

    public void Foo()
    {
        this.RetryFileIO(FileDeleteOperation, "L:\file.to.delete" );
        this.RetryFileIO(FileCopyOperation, "L:\file.to.copy.source", "L:\file.to.copy.destination" );
    }
Respondeu 04/08/2008 em 21:07
fonte usuário

votos
2

Aqui está o que eu fiz recentemente. Ele provavelmente foi feito em outro lugar melhor, mas parece bastante limpo e reutilizável.

Eu tenho um método utilitário que se parece com isso:

    public delegate void WorkMethod();

    static public void DoAndRetry(WorkMethod wm, int maxRetries)
    {
        int curRetries = 0;
        do
        {
            try
            {
                wm.Invoke();
                return;
            }
            catch (Exception e)
            {
                curRetries++;
                if (curRetries > maxRetries)
                {
                    throw new Exception("Maximum retries reached", e);
                }
            }
        } while (true);
    }

Então no meu aplicativo, eu uso Lamda sintaxe de expressão C # 's para manter as coisas arrumadas:

Utility.DoAndRetry( () => ie.GoTo(url), 5);

Esta chama meu método e repete até 5 vezes. Na quinta tentativa, a exceção original é relançada dentro de uma exceção de repetição.

Respondeu 13/09/2010 em 03:25
fonte usuário

votos
2

Você também pode usar uma abordagem mais OO:

  • Criar uma classe base que faz o tratamento de erros e chama um método abstrato para realizar o trabalho concreto. (Padrão Template Method)
  • Criar classes concretas para cada operação.

Isto tem a vantagem de nomear cada tipo de operação que você executar e dá-lhe um padrão de Comando - operações foram representados como objetos.

Respondeu 07/08/2008 em 12:30
fonte usuário

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more