Как указать исключения, которые должны быть выбраны разработчиком интерфейса?

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

Если приложение-хост использует несколько таких интерфейсов, оно ожидает обработки определенных исключений, которые могут возникнуть, например, интерфейс IDataRetriever имеет метод SomeDataType GetData(int timeout);, и хост может обрабатывать некоторые пользовательские исключения, такие как DataRetrievalTimeoutException или NetworkConnectionException.

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

На данный момент я только что добавил теги xml исключений в метод xml comment - достаточно ли этого?

Ответы

Ответ 1

XML-теги (и любая другая документация, которую вы хотите записать) в основном ближе всего к "vanilla".NET на данный момент.

Возможно, вы захотите посмотреть "Контракты кода" , который позволяет вам аннотировать ваш интерфейс контрактами, который может включать исключения, предварительные условия и т.д.

Ответ 2

В интерфейсе вы не можете. Вы можете в базовом классе.

public interface IFoo
{    
  /// <summary>
  /// Lol
  /// </summary>
  /// <exception cref="FubarException">Thrown when <paramref name="lol"> 
  /// is <c>null</c></exception>
  /// <remarks>Implementors, pretty please throw FE on lol 
  /// being null kthx</remarks>
  void Bar(object lol);
}

против.

public abstract BaseFoo
{    
  /// <summary>
  /// Lol
  /// </summary>
  /// <exception cref="FubarException">Thrown when <paramref name="lol"> 
  /// is <c>null</c></exception>
  public void Bar(object lol)
  {
    if(lol == null)
      throw new FubarException();
    InnerBar(lol);
  }

  /// <summary>
  /// Handles execution of <see cref="Bar" />.
  /// </summary>
  /// <remarks><paramref name="lol"> is guaranteed non-<c>null</c>.</remarks>
  protected abstract void InnerBar(object lol);
}

Ответ 3

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

Я рекомендую Microsoft рекомендовать избегать определения настраиваемых типов исключений, чтобы быть неудачным, поскольку это означает, что нет чистого способа отличить, будет ли IEnumerator<T>.MoveNext() вызывать InvalidOperationException, потому что базовая коллекция была изменена во время перечисления или была ли внутренняя обработка из IEnumerator<T>.MoveNext() встречается с InvalidOperationException и просто пропускает его до вызывающего. Если вместо этого IEnumerator<T>.MoveNext() выбрал IEnumerator.InvalidatedEnumeratorException в первом случае, то любой InvalidOperationException, который сбежал, должен был бы представить последний случай.

Ответ 4

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

Ответ 5

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

public static void ThisMethodShouldThrow(this Iinterface obj)
{
     if(obj.ConditionToThrowIsMet) throw new...
}

Это позволяет вам не требовать цепочки наследования.

Ответ 6

Если это не соответствует любому классу, реализующему интерфейс?

Например, если я возьму свой интерфейс и реализую свой собственный класс, используя метод GetData, я мог бы "получать" данные из любого места. Допустим, что это веб-служба, тогда типы исключений, которые могут быть выбраны, могут отличаться от тех, которые я получал от локальной файловой системы.

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