Сокращение дублирования кода обработки ошибок в C #?

Я никогда не был полностью доволен тем, как работает обработка исключений, есть много исключений, и try / catch приводит к таблице (разматывание стека и т. Д.), Но кажется, что в процессе нарушается большая часть модели OO.

Во всяком случае, вот проблема:

Допустим, у вас есть некоторый класс, который оборачивает или включает в себя сетевые операции ввода-вывода файлов (например, чтение и запись в некоторый файл по определенному пути UNC). По разным причинам вы не хотите, чтобы эти операции ввода-вывода потерпели неудачу, поэтому, если вы обнаружите, что они терпят неудачу, вы повторяете их, и вы продолжаете повторять их, пока они не будут выполнены успешно или пока не истечет время ожидания. У меня уже есть удобный класс RetryTimer, который я могу создать и использовать для ожидания текущего потока между повторными попытками и определения истечения времени ожидания и т. Д.

Проблема в том, что у вас есть несколько операций ввода-вывода в нескольких методах этого класса, и вам нужно обернуть каждую из них в логику try-catch / retry.

Вот пример кода:

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

Итак, как избежать дублирования большей части этого кода для каждой операции ввода-вывода файла во всем классе? Мое решение состояло в том, чтобы использовать анонимные блоки делегатов и единственный метод в классе, который выполнил переданный ему блок делегатов. Это позволило мне делать такие вещи другими способами:

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

Мне это немного нравится, но это оставляет желать лучшего. Я хотел бы услышать, как другие люди решат такую ​​проблему.

4.08.2008 19:21:48
Просто общее замечание: почти всегда лучше просто throw;вместоthrow e;
Dan Tao 13.09.2010 03:34:47
Мне интересно: когда был задан этот вопрос, C # 3 был самым новым. Что-нибудь изменилось до C # 8 (текущая версия от 16.01.20)?
DudeWhoWantsToLearn 16.01.2020 14:47:08
4 ОТВЕТА
РЕШЕНИЕ

Это похоже на прекрасную возможность взглянуть на Аспектно-ориентированное программирование. Вот хорошая статья об АОП в .NET . Общая идея заключается в том, что вы извлекаете межфункциональную задачу (т. Е. Повторите попытку в течение x часов) в отдельный класс, а затем аннотируете любые методы, которым необходимо изменить их поведение таким образом. Вот как это может выглядеть (с хорошим методом расширения на Int32)

[RetryFor( 10.Hours() )]
public void DeleteArchive()
{
  //.. code to just delete the archive
}
14
5.08.2008 09:43:55

Просто интересно, что, по вашему мнению, ваш метод оставляет желать лучшего? Вы можете заменить анонимного делегата на ... с именем? делегат, что-то вроде

    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" );
    }
4
4.08.2008 20:07:41

Вы также можете использовать более ОО подход:

  • Создайте базовый класс, который выполняет обработку ошибок и вызывает абстрактный метод для выполнения конкретной работы. (Шаблон Метод шаблона)
  • Создайте конкретные классы для каждой операции.

Это дает преимущество в названии каждого типа выполняемой вами операции и дает вам шаблон Command - операции представлены в виде объектов.

2
7.08.2008 11:30:38

Вот что я недавно сделал. Вероятно, это было сделано в другом месте лучше, но кажется довольно чистым и многоразовым.

У меня есть служебный метод, который выглядит следующим образом:

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

Затем в своем приложении я использую синтаксис выражения lamda в c #, чтобы поддерживать порядок:

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

Это вызывает мой метод и повторяется до 5 раз. При пятой попытке исходное исключение перебрасывается внутри исключения повторной попытки.

2
13.09.2010 02:25:02
Но почему пользовательский WorkMethodделегат вместо Action?
Dan Tao 13.09.2010 03:32:26