Обработка ошибок я должен выдать исключение? Или дескриптор в источнике?

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

public interface IFoo<T> where T : new()
{
  void SomeMethod();
}

public class Foo : IFoo<Foo>
{
  // This will not compile
  public Foo(int x)
  {

  }

  #region ITest<Test> Members

  public void SomeMethod()
  {
    throw new NotImplementedException();
  }

  #endregion
}

, С другой стороны, если Вы хотите протестировать, если тип имеет paramerterless конструктора, можно сделать то отражение использования:

public static class TypeHelper
{
  public static bool HasParameterlessConstructor(Object o)
  {
    return HasParameterlessConstructor(o.GetType());
  }

  public static bool HasParameterlessConstructor(Type t)
  {
    // Usage: HasParameterlessConstructor(typeof(SomeType))
    return t.GetConstructor(new Type[0]) != null;
  }
}

Hope это помогает.

13
задан John Saunders 29 August 2009 в 21:25
поделиться

4 ответа

Я всегда использую эмпирическое правило:

  • На низких уровнях бросать, когда операция не может быть завершена из-за исключительных обстоятельств.
  • На средних уровнях перехватывает несколько типов исключений и заново оборачивает в единственном типе исключения.
  • Обработка исключений в последний ответственный момент.
  • ДОКУМЕНТ!

Вот пример в псевдокоде для многоуровневого приложения ASP.NET MVC (пользовательский интерфейс, контроллер, логика, безопасность, репозиторий):

  1. Пользователь нажимает кнопку отправки.
  2. Действие контроллера выполняется и вызывает логический (бизнес) уровень.
  3. Логический метод вызывает безопасность с текущими учетными данными пользователя
    • Пользователь недействителен
      • Уровень безопасности генерирует SecurityException
      • Уровень логики перехватывает, обертывает в LogicException более общее сообщение об ошибке
      • Контроллер перехватывает LogicException, перенаправляет на страницу ошибок.
    • Пользователь действителен, а безопасность возвращает
  4. Вызовы уровня логики в Репозиторий для завершения действия
    • Сбой репозитория
      • Репозиторий выдает RepositoryException
      • Логический уровень перехватывает, оборачивает в LogicException с более общим сообщением об ошибке
      • Контроллер перехватывает LogicException, перенаправляет на страницу ошибки.
    • Репозиторий завершается успешно
  5. Логический уровень возвращает
  6. Контроллер перенаправляет в представление «Успех».

Обратите внимание, что уровень логики выдает только один тип исключения - LogicException. Любые всплывающие исключения нижнего уровня перехватываются и заключаются в новый экземпляр LogicException, который генерируется. Это дает нам много преимуществ.

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


Между прочим, ничто не говорит о том, что вы не можете этого сделать:

try
{
  var result = DoSomethingOhMyWhatIsTheReturnType();
}
catch(LogicException e)
{
  if(e.InnerException is SqlException)
  {
    // handle sql exceptions
  }else if(e.InnerException is InvalidCastException)
  {
    // handle cast exceptions
  }
  // blah blah blah
}
22
ответ дан 1 December 2019 в 21:38
поделиться

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

Рассмотрим эти два API:

int int.Parse(string integerValue); // In this case, the method will return int
                                    // or it will die! That means your data must be
                                    // valid for this method to function.

bool int.TryParse(string integerValue, out number); // In this case, we expect the data
                                                    // we passed in might not be fully
                                                    // valid, hence a boolean.
1
ответ дан 1 December 2019 в 21:38
поделиться

Во-первых, не существует единого пути, и определенно не существует идеального, так что не переусердствуйте.

В общем, вы хотите использовать исключения в исключительных случаях (исключения влекут за собой накладные расходы на производительность, поэтому их чрезмерное использование, особенно в «зацикленных» ситуациях, может повлиять на производительность). Допустим, репозиторий по какой-то причине не может подключиться к серверу базы данных. Тогда вы должны использовать исключение. Но если в репозитории выполняется поиск некоторого объекта по идентификатору, а объект не найден, вы захотите вернуть значение null вместо выдачи исключения, в котором говорится, что объект с идентификатором x не существует.

То же самое и с логикой проверки. Поскольку он проверяет, предполагается, что иногда ввод выигрывал ' t validate, поэтому в этом случае было бы хорошо вернуть false из службы проверки (или, возможно, более сложный тип, включающий некоторую дополнительную информацию о том, почему он не прошел проверку). Но если логика проверки включает в себя проверку, взято ли имя пользователя или нет, и он не может этого сделать по какой-то причине, вы вызовете исключение.

Итак, скажем, если обновление не удастся, я должен попробуйте / поймайте в моем репозитории, что выдает ошибку, тогда мой сервис слой ловит это и делает elmah сигнализирует и возвращает ложь?

Почему обновление не удалось? Есть ли у этого совершенно веская причина, которая является частью нормального процесса? Тогда не генерируйте исключение, если это произошло по странной причине (допустим, что-то удалило обновляемую запись до ее обновления), тогда исключение выглядит логичным. На самом деле выхода из этой ситуации нет.

0
ответ дан 1 December 2019 в 21:38
поделиться

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

Не злоупотребляйте исключениями для простого управления потоком - это было бы глупейшим поступком.

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

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

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

Что касается обработки исключений: обрабатывайте те, с которыми вы действительно можете справиться. Например, если вы попытаетесь сохранить файл и получите исключение безопасности, покажите пользователю диалоговое окно с запросом о другом месте для сохранения (поскольку у него может не быть разрешений на сохранение в том месте, где он хотел).

Однако вы можете исключить не может действительно справиться (что вы хотите сделать с "исключением OutOfMemory", правда?), следует оставить нетронутым - может быть, вызывающий абонент дальше по стеку вызовов сможет обработать их - или нет.

Что касается обработки исключений: обработайте те, с которыми вы действительно можете справиться. Например, если вы пытаетесь сохранить файл и получаете исключение безопасности, покажите пользователю диалоговое окно с запросом о другом месте для сохранения (поскольку у него может не быть разрешений на сохранение в том месте, где он хотел).

Однако вы можете исключить не может действительно справиться (что вы хотите сделать с "исключением OutOfMemory", правда?), следует оставить нетронутым - может быть, вызывающий абонент дальше по стеку вызовов сможет обработать их - или нет.

Что касается обработки исключений: обработайте те, с которыми вы действительно можете справиться. Например, если вы попытаетесь сохранить файл и получите исключение безопасности, покажите пользователю диалоговое окно с запросом о другом месте для сохранения (поскольку у него может не быть разрешений на сохранение в том месте, где он хотел).

Однако вы можете исключить не может действительно справиться (что вы хотите сделать с "исключением OutOfMemory", правда?), следует оставить нетронутым - может быть, вызывающий абонент дальше по стеку вызовов сможет обработать их - или нет. Марк

2
ответ дан 1 December 2019 в 21:38
поделиться
Другие вопросы по тегам:

Похожие вопросы: