Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to document exceptions of async methods?

A sample method with XML documentation:

// summary and param tags are here when you're not looking.
/// <exception cref="ArgumentNullException>
///    <paramref name="text" /> is null.
/// </exception>
public void Write(string text)
{
    if (text == null)
        throw new ArgumentNullException("text", "Text must not be null.");

    // sync stuff...
}

Write(null) throws an exception as expected. Here is an asynchronous method:

public async Task WriteAsync(string text)
{
    if (text == null)
        throw new ArgumentNullException("text", "Text must not be null.");

    // async stuff...
}

WriteAsync(null), won't throw an exception until awaited. Should I specify the ArgumentNullException in an exception tag anyway? I think it would make the consumer think that calling WriteAsync may throw an ArgumentNullException and write something like this:

Task t;
try
{
    t = foo.WriteAsync(text);
}
catch (ArgumentNullException)
{
    // handling stuff.
}

What is the best practice for documenting exceptions in asynchronous methods?

like image 921
Şafak Gür Avatar asked Apr 09 '13 13:04

Şafak Gür


People also ask

How do I get async exception?

To catch an exception that an async task throws, place the await expression in a try block, and catch the exception in a catch block. Uncomment the throw new Exception line in the example to demonstrate exception handling. The task's IsFaulted property is set to True , the task's Exception.

What happens if an exception is thrown within an asynchronous method?

As we know, in asynchronous programming, control does not wait for the function's result and it executes the next line. So when the function throws an exception, at that moment the program control is out of the try-catch block.

Does await throw exception?

When you await a Task, the first exception is re-thrown, so you can catch the specific exception type (such as InvalidOperationException). However, when you synchronously block on a Task using Task. Wait or Task. Result, all of the exceptions are wrapped in an AggregateException and thrown.


2 Answers

Not a direct answer, but personally I'd advise leaning towards fast-fail here; this might mean writing 2 methods:

public Task WriteAsync(string text) // no "async"
{
    // validation
    if (text == null)
        throw new ArgumentNullException("text", "Text must not be null.");

    return WriteAsyncImpl(text);
}
private async Task WriteAsyncImpl(string text)
{
    // async stuff...
}

This pattern is also an ideal place to add "fast path" code, for example:

public Task WriteAsync(string text) // no "async"
{
    // validation
    if (text == null)
        throw new ArgumentNullException("text", "Text must not be null.");

    if (some condition)
        return Task.FromResult(0); // or similar; also returning a pre-existing
                                   // Task instance can be useful

    return WriteAsyncImpl(text);
}
like image 112
Marc Gravell Avatar answered Oct 04 '22 05:10

Marc Gravell


Microsoft doesn't seem to differentiate between the async method throwing an exception and the returned Task having an exception stored in its Exception property. E.g.:

WebClient.DownloadFileTaskAsync(string, string)

Personally, I would choose to document the exceptions as part of the documentation for the return value (i.e. the returned Task), since the distinction may be important for clients.

like image 44
Matt Smith Avatar answered Oct 04 '22 03:10

Matt Smith