5
\$\begingroup\$

What do you use for refactoring away from exceptions while programming functional in C#? I defined this class to hold function outcome:

public class Result 
{
    public static readonly Result OK = new Result();
    public static implicit operator Result(Exception error) => new Result(error);

    protected Result(Exception error = null)
    {
        Error = error;
    }

    public Exception Error { get; }
    public bool Succeeded => Error == null;
    public bool Failed => !Succeeded;

    public override string ToString() =>
        Succeeded ? "OK" : Error.Message;
}

And its subclass to hold return value where necessary:

public class Result<T> : Result, IEnumerable<T>
{
    public static implicit operator Result<T>(Exception error) => new Result<T>(error);
    public static implicit operator Result<T>(T value) => new Result<T>(value);
    public static implicit operator T(Result<T> result)
    {
        if (result.Failed)
            throw result.Error;

        return result.Value;
    }

    protected Result(T value)
        : base(error: null)
    {
        Value = value;
    }

    protected Result(Exception error)
        : base(error)
    {
    }

    T Value { get; }

    IEnumerable<T> Values =>
        Failed ? new T[0] : new[] { Value };

    public IEnumerator<T> GetEnumerator() =>
        Values.GetEnumerator();

    IEnumerator IEnumerable.GetEnumerator() => 
        GetEnumerator();
}  

It is enumerable, exposing 0..1 elements, so amount of if-statements can be reduced.

Usage:

        var r1 = Div(
            Div(100, 10),
            Div(10, 2));

        var r2 = Div(
            Div(100, 10),
            Div(0, 0));

        Console.WriteLine(r1.Succeeded); // True
        Console.WriteLine(r1.Count()); // 1
        Console.WriteLine(r1); // 2

        Console.WriteLine(r2.Succeeded); // False
        Console.WriteLine(r2.Count()); // 0
        // Console.WriteLine(r2); // throws DivideByZeroException

Where:

    static Result<int> Div(Result<int> a, Result<int> b)
    {            
        try
        {
            return a / b;
        }
        catch (DivideByZeroException ex)
        {
            return ex;
        }
    }

Idea comes from: Refactoring away from exceptions

\$\endgroup\$
  • \$\begingroup\$ One thing I'm not sure about... this is about refactoring away from exceptions so why is this throwing if (result.Failed) throw result.Error;. Shouldn't it return a Failed result? \$\endgroup\$ – t3chb0t Mar 14 '17 at 19:58
  • \$\begingroup\$ It's a conversion operator to the Value type - it needs to return something of T or throw... \$\endgroup\$ – Dmitry Nogin Mar 14 '17 at 20:01
  • \$\begingroup\$ Have a look at that Pluralsight video from above - it is a good one :) \$\endgroup\$ – Dmitry Nogin Mar 14 '17 at 20:06
  • \$\begingroup\$ The problem with this is you're not getting away from the Exception, you're just wrapping it, and poorly at that. You will still have all the negative performance implications of throwing and catching exceptions, and none of the benefits. \$\endgroup\$ – Der Kommissar Mar 14 '17 at 22:32
  • \$\begingroup\$ Original FP friendly idea is just to pack ex into a testable result object. There is no need to rethrow if properly tested, so it should be no extra performance penalty. Have you got a chance to see the video? \$\endgroup\$ – Dmitry Nogin Mar 14 '17 at 22:39
3
\$\begingroup\$

I don't know what to think of the entire thing yet but...

public bool Succeeded => Error == null;
public bool Failed => !Succeeded;

I find this should be only one property bool Success.


I've watched video series and generally I think it's a new and interesting idea that I need to try out. Your solution is a good start especially with the implicit operators but I'm missing a few features.

I'm not so fond of using the Result as a parameter. It has another implicit operator for converting it into a value so using actual types for parameters is sufficient and it's easier to work with pure parameters rather than dealing with Results everywhere (but maybe it's just an example). It's also safer because I'm going to throw an exception if a result is in an invalid state so implicit casting it for the parameter would fail when a method is called and not later when a parameter is used. I find this is better because it indicates a bug before the call and not inside the method that uses a result.

public static implicit operator T(Result<T> result)
{
    if (result.Failed)
        throw result.Error;

    return result.Value;
}

I don't think this is a good implementation for this operator and I prefer getting the Value would throw like this

T Value => Success ? _value : throw new InvalidOperationException("Value isn't set because the result is in error state.");

informing me that I cannot use it because the Result is in an invalid state for it. This is the behaviour that I expect from this property and not some default value that I might use in case I forgot to check the Success flag.

After all I think I could also live with two flags for the result that I'd call

public bool Success => string.IsNullOrEmpty(Message) && Exception == null;

and

public bool Failure => !Success;

whether it should be Success or Succeeded or Failure or Failed is probably an entire new question for the English Language community but to me the former sounds better as it tells me whether the operation was a Success or a Failure and not if the Result succeeded. Well, tometo tomato ;-)


The current solution is prepared to work only with exceptions and to be used only in methods that can throw but if we wanted to make our API consistent then it should also be possible to use the Result for other methods that not necessarily throw but cannot continue because of some other reason so implementing factory methods like Ok and Fail and a custom message is not such a bad idea.


To me the perfect solution would go like this:

The non-generic Result can be created in a similar way as an exception this is with a message and an exception or with either one only. In case of a failure it returns either the exception's message or the custom one and could be used for methods that return void. For consistency reasons I turned the OK property into a Ok method.

public class Result
{
    protected Result(string message, Exception exception) { Message = message; Exception = exception; }
    protected Result(string message) : this(message, null) { }
    protected Result(Exception Exception) : this(null, Exception) { }

    public Exception Exception { get; }
    public string Message { get; }

    public bool Success => string.IsNullOrEmpty(Message) && Exception == null;
    public bool Failure => !Success;

    public static Result Ok() => new Result(nameof(Ok));

    public static Result Fail(string message, Exception exception) => new Result(message, exception);
    public static Result Fail(string message) => Fail(message, null);
    public static Result Fail(Exception exception) => Fail(null, exception);

    public override string ToString() => Success ? Message : (Exception?.Message ?? Message);

    public static implicit operator Result(Exception exception) => Fail(exception);
    public static implicit operator Result(string message) => Fail(message);
    public static implicit operator bool(Result result) => result.Success;
}

The generic implementation could provide a value:

public class Result<T> : Result, IEnumerable<T>
{
    private readonly T _value;

    protected Result(T value) : base(Exception: null) => _value = value;
    protected Result(string message, Exception exception) : base(message, exception) { }
    protected Result(string message) : base(message, null) { }
    protected Result(Exception exception) : this(null, exception) { }

    public T Value => Success ? _value : throw new InvalidOperationException("Value isn't set because the result is in error state.");

    public static Result<T> Ok(T value) => new Result<T>(value);

    public static new Result<T> Fail(string message, Exception exception) => new Result<T>(message, exception);
    public static new Result<T> Fail(string message) => Fail(message, null);
    public static new Result<T> Fail(Exception exception) => Fail(null, exception);

    public IEnumerator<T> GetEnumerator() => Enumerable.Repeat(Value, Success ? 1 : 0).GetEnumerator();
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

    public static implicit operator Result<T>(string message) => Fail(message);
    public static implicit operator Result<T>(Exception exception) => Fail(exception);
    public static implicit operator Result<T>(T value) => Ok(value);
    public static implicit operator Result<T>((string Message, Exception Exception) t) => Fail(t.Message, t.Exception);
    public static implicit operator T(Result<T> result) => result.Value;

    public void Deconstruct(out string message, out Exception exception)
    {
        message = Message;
        exception = Exception;
    }
}

and it can also be use with methods that do not have to throw. Consider another version of the Div method:

static Result<int> Div(int a, int b)
{
    if (b == 0) return "Cannot divide by zero" ; else return a / b;
}

I have also added an implicit cast to bool so you can use the result in a boolean expression:

var n = Div(4, 0);
if (n)
{
    Console.WriteLine("Hooray, divided!");
}
else
{
    Console.WriteLine("This time it didn't work.");
}

and with the new anonymous tuples in C# 7 we can even do this:

public static Result<int> Div(int a, int b)
{
    try
    {
        return a / b;
    }
    catch (DivideByZeroException ex)
    {
        return ("Are you nuts?!", ex);
    }
}

or how about deconstructing it?

var result = Div(4, 0);
if (!result)
{
    var (message, exception) = result;
}
\$\endgroup\$
  • 1
    \$\begingroup\$ Yep, Framework Design Guidelines suggests to use positive lexicon only. But so many people were stumbling on ! in !r.Succeeded so many times around the globe, - common, one could think that it is the right moment to start helping ourselves a little bit :) \$\endgroup\$ – Dmitry Nogin Mar 14 '17 at 19:53
  • \$\begingroup\$ @DmitryNogin updated. I hope I got everything right because I didn't test each possible execution path (yet) ;-) \$\endgroup\$ – t3chb0t Mar 25 '17 at 9:19
  • \$\begingroup\$ I left it IEnumerable but actually I can't come up with any use case for it. \$\endgroup\$ – t3chb0t Mar 25 '17 at 13:14
  • \$\begingroup\$ What if T in Result<T> is string? You would have a tough time compiling return "resultOrError". This is why I went with Exception... \$\endgroup\$ – Dmitry Nogin Mar 26 '17 at 23:41
  • \$\begingroup\$ @DmitryNogin ok, didn't see this one ;-] \$\endgroup\$ – t3chb0t Mar 27 '17 at 4:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.