Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 

Repository files navigation

Exceptionable<T>

A class containing either a value or an exception. A bit like Nullable<T>, but for exceptions.

EXAMPLES

Method with return type Exceptionable<T> can return a T or an exception:

public Exceptionable<string> MethodThatReturnsExceptionable(int positiveNumber)
{
    if (positiveNumber > 0)
    {
        return positiveNumber + " is a great number.";
    }
    else
    {
        return new ArgumentException();
    }
}
var result = MethodThatReturnsExceptionable(positiveNumber: 42);

Assert(result.HasValue == true);
Assert(result == "42 is a great number.");
var result = MethodThatReturnsExceptionable(positiveNumber: -1);

Assert(result.HasException == true);
Assert(result.Exception.GetType() == typeof(ArgumentException));

Exceptionable<T> can be passed to a method expecting a T:

var text = new Exceptionable<string>("A piece of text");

var result = MethodThatTakesAString(text);

Assert(result == "A piece of text was passed.");

Exceptionable<T> can be assigned a T:

Exceptionable<int> value = 99;

Assert(value == 99);

Exceptionable<T> can be compared to T for equality:

var value = new Exceptionable<int>(3);

Assert(value == 3);
Assert(3 == value);

Web Service Example

Traditional exception-based approach:

public class PaymentServiceExample_WithExceptions
{
    public PaymentResultEnum TakePayment()
    {
        try
        {
            var account = this.GetAccount(12345);

            var paymentResult = this.CallWebService();

            // other code

            this.log.Info("Payment taken successfully.");

            return PaymentResultEnum.Success;
        }
        catch (WebException ex)
        {
            // Web service exception handling is a long way from code which threw the exception.
            
            // Catch blocks are in a different order to the code that caused the exception, as 
            // more-specific exceptions have to be caught first.

            // account.Name is not available in this context:

            this.log.LogError($"Payment failed.  Name = {account.Name}.", ex); // Oops!

            return PaymentResultEnum.WebServiceCallFailed;
        }
        catch (Exception ex)
        {
            // Again, the exception handling is a long way from where the exception arose.

            this.log.Error($"Payment failed.  Account ID = {accountId}.", ex);

            return PaymentResultEnum.NoSuchAccount;
        }
    }

    private Account GetAccount(int accountId)
    {
        if (no_such_account)
        {
            throw new ArgumentException("No account with ID " + accountId);
        }
        else
        {
            return new Account(accountId, "Ed Smale");
        }
    }

    private string CallWebService()
    {
        if (web_request_fails)
        {
            throw new WebException("The web request failed :(");
        }
        else
        {
            return "OK";
        }
    }
}

Exceptionable<T> approach:

public class PaymentService
{
    public PaymentResultEnum TakePayment()
    {
        var account = this.GetAccount(12345);

        if (account.HasException)
        {
            // Code for handling account exception is near the method call which generated the exception.
            // We know which bit of code generated the exception, so we can take the appropriate action.

            this.log.LogError($"Payment failed.  Account ID = {accountId}.", account.Exception);

            return PaymentResultEnum.NoSuchAccount;
        }

        var paymentResult = this.CallWebService();

        if (paymentResult.HasException)
        {
            // The 'account' variable is available, so we can log the account name.

            this.log.LogError($"Payment failed.  Account ID = {accountId}, Name = {account.Value.Name}.", paymentResult.Exception);

            return PaymentResultEnum.WebServiceCallFailed;
        }

        // other code

        this.log.Info("Payment taken successfully.");

        return PaymentResultEnum.Success;
    }

    // Returns an account, or an Exception in the event of failure:

    private Exceptionable<Account> GetAccount(int accountId)
    {
        if (no_such_account)
        {
            return new ArgumentException("No account with ID " + accountId);
        }
        else
        {
            return new Account(accountId, "Ed Smale");
        }
    }

    // Returns a string, or an Exception in the event of failure:

    private Exceptionable<string> CallWebService()
    {
        if (web_request_fails)
        {
            return new WebException("The web request failed :(");
        }
        else
        {
            return "OK";
        }
    }
}

About

Class that can contain either a value or an exception.

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages