简体   繁体   中英

How to specify exceptions to be thrown by an implementor of an interface?

I'm currently developing a solution and have designed it in a way such that it strongly implements the strategy/provider pattern. As such the solution exposes a number of interfaces and contains default implementations of these interfaces which can be replaced via a DI type methodology.

Where the host application uses a number of these interfaces it is expecting to handle certain exceptions that may occur, for example IDataRetriever interface has a method SomeDataType GetData(int timeout); and the host can handle some custom exceptions such as DataRetrievalTimeoutException or NetworkConnectionException .

My question is, what is the best way to mark up the interface class such that when a developer implements it they would know that certain exceptions should be thrown and would be handled by the host?

At the moment I have just added the exception xml tags to the methods xml comment - does this suffice?

The XML tags (and any other documentation you want to write) are basically the closest you've got in "vanilla" .NET at the moment.

You might want to look at Code Contracts which lets you annotate your interface with contracts, which can include exceptions, preconditions etc.

You cannot in an interface. You CAN in a base class.

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

vs.

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

I would suggest defining exception classes within the interface, and specifying that no exceptions which do not derive from those should be allowed to escape unless the CPU is on fire or other such drastic situation exists (even then, it might not be a bad idea to have an explicitly-defined IWoozle.SystemCorruptionException so that if a Pokemon handler simply catches, logs, and throws out the exception, the log will reflect that at least someone thought the exception was important).

I consider Microsoft's recommendation to avoid defining custom exception types to be unfortunate, since it means that there's no clean way to distinguish whether IEnumerator<T>.MoveNext() throws an InvalidOperationException because the underlying collection was altered during enumeration, or whether the internal processing of IEnumerator<T>.MoveNext() encounters an InvalidOperationException and simply lets it bubble up to the caller. If instead, IEnumerator<T>.MoveNext() threw an IEnumerator.InvalidatedEnumeratorException in the former case, then any InvalidOperationException which escaped would have to represent the latter case.

Should this not be up to whatever class implements the Interface?

Eg if I take your Interface and implement my own class, with a method GetData, I could be 'getting' data from anywhere. Lets say it was a web service then the types of exceptions that could be thrown could be different to those if I was 'getting' data from local file system.

So in my implementation I would implement (and document) those exceptions specific to the implmentation so that whatever code is using the implementation could handle them. How those exception are handled may be very specific to the implementation, say I am consuming them in a web app as opposed to a desktop app.

I think the best way to provide this information is in the XML documentation that you would provide for each interface. There you can specify what Exception is thrown by the method so that the host can handle that error.

Another strategy if you can't support a generic base class, is extension method implementations.

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

This has the benefit of allowing you to not require an inheritance chain.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM