¿Reducir el código de manejo de error duplicado en C #?

votos
32

Nunca he estado completamente satisfecho con la forma en que funciona el manejo de excepciones, hay muchas excepciones y trae try / catch a la mesa (desenrollado de la pila, etc.), pero parece que rompe gran parte del modelo de OO en el proceso.

De todos modos, aquí está el problema:

Digamos que tiene alguna clase que envuelve o incluye operaciones de IO de archivos en red (por ejemplo, leer y escribir en algún archivo en alguna ruta particular de UNC en alguna parte). Por diversas razones, no desea que esas operaciones de IO fallen, por lo que si detecta que fallan, las vuelve a intentar y las vuelve a intentar hasta que tengan éxito o llegue a un tiempo de espera excedido. Ya tengo una clase RetryTimer conveniente que puedo instanciar y usar para dormir el hilo actual entre reintentos y determinar cuándo ha transcurrido el tiempo de espera, etc.

El problema es que tiene varias operaciones de E / S en varios métodos de esta clase, y necesita ajustar cada uno de ellos en la lógica de intentar capturar / reintentar.

Aquí hay un fragmento de código de ejemplo:

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();
    }
}

Entonces, ¿cómo evita duplicar la mayor parte de este código para cada operación de IO de archivos a lo largo de la clase? Mi solución fue usar bloques de delegados anónimos y un único método en la clase que ejecutó el bloque de delegados que se le pasó. Esto me permitió hacer cosas como esta en otros métodos:

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

Me gusta algo así, pero deja mucho que desear. Me gustaría escuchar cómo otras personas resolverían este tipo de problema.

Publicado el 04/08/2008 a las 20:21
fuente por usuario
En otros idiomas...                            


4 respuestas

votos
13

Esto parece una excelente oportunidad para echar un vistazo a la Programación Orientada a Aspectos. Aquí hay un buen artículo sobre AOP en .NET . La idea general es extraer la preocupación interfuncional (es decir, volver a intentar por x horas) en una clase separada y luego anotar cualquier método que necesite modificar su comportamiento de esa manera. Así es como se verá (con un buen método de extensión en Int32)

[RetryFor( 10.Hours() )]
public void DeleteArchive()
{
  //.. code to just delete the archive
}
Respondida el 05/08/2008 a las 10:43
fuente por usuario

votos
4

Solo me pregunto, ¿qué sientes que tu método deja que desear? Podría reemplazar al delegado anónimo con un .. named? delegar, algo así 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" );
    }
Respondida el 04/08/2008 a las 21:07
fuente por usuario

votos
2

Esto es lo que hice recientemente. Probablemente se ha hecho en otros lugares mejor, pero parece bastante limpio y reutilizable.

Tengo un método de utilidad que tiene este aspecto:

    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);
    }

Luego, en mi solicitud, yo uso la sintaxis de expresiones Lamda C # 's para mantener las cosas ordenadas:

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

Esta llama mi método y vuelve a intentar hasta 5 veces. En el quinto intento, la excepción original se relanza en el interior de una excepción de reintento.

Respondida el 13/09/2010 a las 03:25
fuente por usuario

votos
2

También podría usar un enfoque más OO:

  • Cree una clase base que maneje el error y llame a un método abstracto para realizar el trabajo concreto. (Patrón de método de plantilla)
  • Crea clases concretas para cada operación.

Esto tiene la ventaja de nombrar cada tipo de operación que realiza y le proporciona un patrón de comando: las operaciones se han representado como objetos.

Respondida el 07/08/2008 a las 12:30
fuente por usuario

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