Проблема с обработкой исключений, ошибок и возвращаемых значений

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

Например, у меня есть класс «DatabaseProvider», где есть методы для связи с БД. Мне приходится иметь дело со стандартными исключениями, неверными данными из базы данных (например, с неправильным форматом электронной почты), ошибками, возвращаемыми из базы данных. Все это мне нужно вернуть функции, которая вызвала метод DatabaseProvider, чтобы этот метод регистрировал это исключение или ошибку и продолжал работать. Поэтому я не могу бросить исключение.

И если этого было недостаточно, я должен вернуть (из DatabaseProvider) значение в большинстве случаев или объект или список объектов.

Вот я и подумал, может быть, создать класс "Ответ". И объект этого класса будет возвращен из каждого метода в моем приложении.

Вот так:

public class Response
{
    public bool Error { get; set; }
    public string ErrorMessage { get; set; }        
    public bool DatabaseError { get; set; }
    public string DatabaseMessage { get; set; }

    public Exception ex { get; set; }
}

Но как вернуть (например) список объектов собственного дизайна за то же время?

Использование «выходных» параметров является решением? Каков наилучший способ (лучшая практика)? Я искал об этом, но все, что я нашел, это то, как бороться с исключениями (только)..


person Marshall    schedule 30.07.2011    source источник
comment
Есть ли причина, по которой вы не хотите реализовывать собственное исключение? Ваша цель всегда возвращать эту информацию, а не только тогда, когда что-то пойдет не так? Из вашего примера это выглядит очень ошибочным   -  person Paul Bellora    schedule 30.07.2011


Ответы (2)


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

Однако, если вы всегда хотите прикреплять определенную метаинформацию к возвращаемым результатам, вы можете реализовать небольшой класс транспортного средства, чтобы обернуть результаты, неся метаинформацию:

abstract class ResultWrapper
{
    //meta info, such as error references/properties/flags/statistics

    public ResultWrapper([meta info])
    {
        //set meta info
    }
}

class ResultList<T> : ResultWrapper
{
    public readonly List<T> resultList;

    //constructor for resultList, plus meta information for parent
    public ResultList(List<T> resultList, [additional args for meta info]) : base([meta info])
    {
        this.resultList = resultList;
    }
}

class SingleResult<T> : ResultWrapper
{
    public readonly T result;

    //constructor similar to above
}

Редактировать: Основываясь на ваших комментариях, вам определенно следует использовать пользовательские исключения, которые вы можете создавать (возможные исключения из-за переноса). Как сказал Петар, их можно поймать в цикле и обработать там, не останавливая выполнение последующих задач. Язык поддерживает обработку исключений по уважительным причинам — я бы воспользовался этим.

Кроме того, к вашему сведению в вашем шаблоне:

public bool Error { get; set; }
public string ErrorMessage { get; set; }

нет необходимости в bool. Просто проверьте наличие ненулевого значения в поле сообщения об ошибке/ссылке.

person Paul Bellora    schedule 30.07.2011
comment
Ну, не то чтобы я не бросал исключений. Да, но бывают ситуации, когда возникает ошибка, которую нужно записать, но все равно вернуть значение. И вторая причина - проблема с циклом таймера (я объяснил это в комментарии к ответу Петра Иванова - person Marshall; 30.07.2011
comment
В любом случае, является ли хорошей практикой программирования возвращать значение с метаинформацией? Это популярно? Выглядит очень хорошо, но если его использовать очень редко и это не очень хорошее решение.. Я должен подумать об этом снова. - person Marshall; 30.07.2011

Я бы реализовал собственное исключение со всеми данными, которые вам нужно передать в случае исключения. Что-то типа

public class MySpecificException: Exception
{
    public bool Error { get; set; }
    public string ErrorMessage { get; set; }        
    public bool DatabaseError { get; set; }
    public string DatabaseMessage { get; set; }

    public Exception ex { get; set; }
}

Вы выбрасываете исключение и в вызывающих функциях используете блок try catch для вашего конкретного исключения.

public void CallingFunction()
{
    try {
        //call your function
    }
    catch (MySpecificException ex) 
    {
        //log the data from the exception
    }

Это именно то, для чего нужны пользовательские исключения.

Таким образом, вы все еще можете вернуть из функции то, что хотите вернуть (список или что-то еще).

person Petar Ivanov    schedule 30.07.2011
comment
Я знаю, но пока не уверен. У меня есть цикл таймера, который получает несколько задач и выполняет их одну за другой. Теперь, когда случайная задача завершится ошибкой -> я выдаю исключение. Исключение прервет отсчет таймера, и остальные задачи в очереди не будут выполняться. Когда одна задача завершается сбоем, я хочу записать это в журнал, но продолжить с остальными. Я хочу записать все мысли о том, что произошло.. - person Marshall; 30.07.2011
comment
ну ловишь исключение в вызывающей функции - ничего не прервется :) - person Petar Ivanov; 30.07.2011
comment
Таким образом, весь цикл таймера будет в try catch (Exception ex). Внутри будет еще один блок try catch(MyException myex), но внутри этого второго будет только функция Execute. В catch(MyException myex) - я буду логировать все в случае неудачи. Я должен подумать об этом еще раз, но звучит хорошо. - person Marshall; 30.07.2011
comment
может не быть необходимости во внешнем try/catch, если только вы не предвидите проскальзывание определенных «исключительных» исключений. - person Paul Bellora; 30.07.2011
comment
вам не нужны два блока try для перехвата двух разных исключений — у вас может быть два блока catch для одной и той же попытки: try { } catch(Exception1 ex) { } catch(Exception2 ex) { }. - person Petar Ivanov; 30.07.2011
comment
Я пойду с исключениями. Спасибо за помощь. Я хотел бы выбрать оба ответа, но я должен выбрать первый - person Marshall; 30.07.2011