La réduction du code de gestion des erreurs en double en C #?

voix
32

Je ne suis jamais complètement satisfait des travaux façon de gestion des exceptions, il y a des exceptions beaucoup et try / catch apporte à la table (pile de déroulement, etc.), mais il semble casser beaucoup du modèle OO dans le processus.

Quoi qu'il en soit, voici le problème:

Disons que vous avez une classe qui enveloppe ou comprend fichiers en réseau des opérations d'entrées-sorties (par exemple, la lecture et l'écriture à un certain fichier à un chemin UNC particulier quelque part). Pour diverses raisons que vous ne voulez pas que ces opérations d'entrées-sorties à l'échec, donc si vous détectez qu'ils vous ne les Réessayez et vous gardez les réessayant jusqu'à ce qu'ils réussissent ou que vous atteignez un délai d'attente. J'ai déjà une classe RetryTimer pratique que je peux instancier et utiliser pour dormir le fil en cours entre les tentatives et déterminer le moment où le délai d'attente est écoulé, etc.

Le problème est que vous avez un tas d'opérations d'entrées-sorties dans plusieurs méthodes de cette classe, et vous avez besoin d'envelopper chacun d'eux dans try-catch / logique de nouvelle tentative.

Voici un exemple extrait de code:

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

Alors, comment voulez-vous éviter de dupliquer la plupart de ce code pour chaque fichier IO opération tout au long de la classe? Ma solution a été d'utiliser des blocs de délégués anonymes et une seule méthode dans la classe qui a exécuté le bloc délégué qui lui est passé. Cela m'a permis de faire des choses comme ça dans d'autres méthodes:

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

J'aime cela un peu, mais il laisse beaucoup à désirer. Je voudrais savoir comment d'autres gens résoudre ce genre de problème.

Créé 04/08/2008 à 20:21
source utilisateur
Dans d'autres langues...                            


4 réponses

voix
13

Cela ressemble à une excellente occasion de jeter un oeil à la programmation orientée aspect. Voici un bon article sur AOP dans .NET . L'idée générale est que vous souhaitez extraire la préoccupation interfonctionnelle (c. -à- Retry pendant x heures) dans une catégorie distincte et vous souhaitez annoter toutes les méthodes qui ont besoin de modifier leur comportement de cette façon. Voici comment cela peut paraître (avec une méthode d'extension agréable sur Int32)

[RetryFor( 10.Hours() )]
public void DeleteArchive()
{
  //.. code to just delete the archive
}
Créé 05/08/2008 à 10:43
source utilisateur

voix
4

Je me demandais, qu'est-ce que vous sentez que votre méthode laisse à désirer? Vous pouvez remplacer le délégué anonyme avec un .. nommé? délégué, quelque chose comme

    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" );
    }
Créé 04/08/2008 à 21:07
source utilisateur

voix
2

Voici ce que je l'ai fait récemment. Il a probablement été fait ailleurs mieux, mais il semble assez propre et réutilisable.

J'ai une méthode utilitaire qui ressemble à ceci:

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

Puis dans mon application, j'utilise la syntaxe d'expression Lamda s 'c # de garder les choses en ordre:

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

Cela appelle ma méthode et réessaie jusqu'à 5 fois. À la cinquième tentative, l'exception d'origine est relancée à l'intérieur d'une exception de nouvelle tentative.

Créé 13/09/2010 à 03:25
source utilisateur

voix
2

Vous pouvez également utiliser une approche plus orientée objet:

  • Créer une classe de base qui fait la gestion des erreurs et appelle une méthode abstraite pour effectuer le travail concret. (Pattern Template Method)
  • Créer des classes concrètes pour chaque opération.

Ceci a l'avantage de nommer chaque type d'opération que vous effectuez et vous donne un modèle de commande - opérations ont été représentés comme des objets.

Créé 07/08/2008 à 12:30
source utilisateur

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