How to implement the TryDoSomething pattern with async

async-await, asynchronous, c#, c#-5.0, design-patterns

Solution

So far I've used a little class called `Attempt<T>`:

public sealed class Attempt<T>
{
    /// <summary>
    /// Initializes a new instance of the <see cref="Attempt{T}"/> class.
    /// </summary>
    public Attempt() { }

    /// <summary>
    /// Initializes a new instance of the <see cref="Attempt{T}"/> class.
    /// </summary>
    public Attempt(Exception exception)
    {
        this.Exception = exception;
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="Attempt{T}"/> class.
    /// </summary>
    /// <param name="result">The result.</param>
    public Attempt(T result)
    {
        this.Result = result;
        this.HasResult = true;
    }

    /// <summary>
    /// Gets the result.
    /// </summary>
    /// <value>The result.</value>
    public T Result { get; private set; }

    /// <summary>
    /// Determines whether this instance has result.
    /// </summary>
    /// <returns><c>true</c> if this instance has result; otherwise, <c>false</c>.</returns>
    public bool HasResult { get; private set; }

    /// <summary>
    /// Returns the result with a true or false depending on whether its empty, but throws if there is an exception in the attempt.
    /// </summary>
    /// <param name="result">The result, which may be null.</param>
    /// <returns><c>true</c> if the specified result has a value; otherwise, <c>false</c>.</returns>
    /// <exception cref="System.AggregateException">The attempt resulted in an exception. See InnerExceptions.</exception>
    public bool TryResult(out T result)
    {
        if (this.HasResult)
        {
            result = this.Result;
            return true;
        }
        else
        {
            if (this.Exception != null)
            {
                throw new AggregateException("The attempt resulted in an exception. See InnerExceptions.", this.Exception);
            }
            else
            {
                result = default(T);
                return false;
            }
        }
    }

    /// <summary>
    /// Gets or sets the exception.
    /// </summary>
    /// <value>The exception.</value>
    public Exception Exception { get; private set; }
}

Which is used within a Try method like this:

internal async Task<Attempt<T>> TryReadObjectAsync<T>(string folderName, string fileName)
{
    if (String.IsNullOrWhiteSpace(folderName))
        throw new ArgumentException("The string argument is null or whitespace.", "folderName");

    if (String.IsNullOrWhiteSpace(fileName))
        throw new ArgumentException("The string argument is null or whitespace.", "fileName");

    try
    {
        StorageFolder folder = this.StorageFolder;                
        if (folderName != @"\")
            folder = await StorageFolder.GetFolderAsync(folderName);

        var file = await folder.GetFileAsync(fileName);
        var buffy = await FileIO.ReadBufferAsync(file);

        string xml = await buffy.ReadUTF8Async();

        T obj = await Evoq.Serialization.DataContractSerializerHelper.DeserializeAsync<T>(xml);

        return new Attempt<T>(obj);
    }
    catch (FileNotFoundException fnfe)
    {
        // Only catch and wrap expected exceptions.

        return new Attempt<T>(fnfe);
    }
}

And is consumed like so:

DataContractStorageSerializer xmlStorage = new DataContractStorageSerializer(this.StorageFolder);

var readAttempt = await xmlStorage.TryReadObjectAsync<UserProfile>(userName, UserProfileFilename);

try
{
    UserProfile user;
    if (readAttempt.TryResult(out user))
    {
        // Do something with user.
    }
    else
    {
        // No user in the persisted file.
    }
}
catch (Exception ex)
{
    // Some unexpected, unhandled exception happened.
}

Or, ignoring errors.

...
var readAttempt = await xmlStorage.TryReadObjectAsync<UserProfile>(userName, UserProfileFilename);

if (readAttempt.HasResult)
{
    // Continue.

    this.DoSomethingWith(readAttempt.Result);
}
else
{
    // Create new user.
}

Problem

I often use the `TryDoSomething` pattern like this totally made up example: ``` GameContext gameContext; if (gamesRepository.TryLoadLastGame(out gameContext)) { // Perform actions with gameContext instance. } else { // Create a new game or go to home screen or whatever. } ``` This allows a nice readable flow but it also allows a success status `true` but a null return value which is useful sometimes for communicating "I was able to get you your thing but its actually null". With async-await, the asynchronous lower APIs "force" calling APIs to do the right thing and work asynchronously. However, without `out` parameters, this pattern doesn't work. How can it be achieved? I have an answer, and am answering Q/A style to see how other people think about it.

Original source

Related problems