Zohar Peled
Posted on March 5, 2021
(First published on What the # do I know?)
tl;dr; – Check out the code on GitHub.
Microsoft’s recommendations for handling errors in dot net are published in a page entitled Best practices for exceptions.
In this page there’s a paragraph entitled "Design classes so that exceptions can be avoided", which is a very good advice – If you can design your code to avoid exceptions, you really should.
For instance, if your code contains the mathematical operation of division, you should check the values before hand to avoid having to deal with a
DivideByZeroException thrown at the you by at run time.
However, this page also contains the following recommendation:
Another way to avoid exceptions is to return null (or default) for extremely common error cases instead of throwing an exception. An extremely common error case can be considered normal flow of control. By returning null (or default) in these cases, you minimize the performance impact to an app.
For value types, whether to use Nullable or default as your error indicator is something to consider for your particular app. By using Nullable, default becomes null instead of Guid.Empty. Some times, adding Nullable can make it clearer when a value is present or absent. Other times, adding Nullable can create extra cases to check that aren’t necessary, and only serve to create potential sources of errors.
This is not a bad advice, but what if your writing a void method? What if your method might fail in more than one non exceptional way? returning null doesn’t help you in these situations.
They also suggest, in that page to
Throw exceptions instead of returning an error code.
Exceptions ensure that failures do not go unnoticed because calling code didn’t check a return code.
This is also a good advice, but then again, shouldn’t exceptions be used only in exceptional circumstances? you know, things that you can’t control in your code?
So I’ve decided to solve this problem by using the Result
class for void methods, and Result<T>
for non-void methods.
Here’s a scenario in which I found it useful to return an instance of Result instead of using a void method:
I needed to take a parameter, get some data based on that parameter from another method, and then send that data to another service – Seems like a fairly simple and common task, right? something like this should do it:
void SendReport(int id)
{
var report = GetReport(id);
if (ValidateReport(report))
{
SendReport(report);
}
}
Except that… The SendReport
method had several points in which it could fail – for instance, if there was no data corresponding to the parameter, if there was data but it was invalid, if the communication between services was down and so on.
I didn’t need this method to return anything, except I needed to know if it succeeded or failed and if it failed, why.
In this particular case, I couldn’t introduce a logger as a dependency to this method, so I’ve decided to write a class that would help me get that information back from the method, so that I can use it in the code calling this method.
Enter the Result
class. This is a very simple class that contains only two properties – A bool indicating success, and a string specifying the error message. The simplified code looks like this:
public class Result
{
public static Result Success() => new Result();
public static Result Fail(string errorDescription) => new Result(errorDescription);
protected Result() => Succeeded = true;
protected Result(string errorDescription)
{
Succeeded = false;
ErrorDescription = errorDescription;
}
public bool Succeeded { get; }
public string ErrorDescription { get; }
}
Note the result class have no public constructors, only protected ones – you can only initialize it using the static Success or Fail methods. This serves two purposes: One, when you initialize an instance of the Result class, you have a very small chance of initializing it wrong, and two, when you read the code, it’s very clear and unambiguous.
So now, the code from before looks like this:
Result SendReport(int id)
{
var report = GetReport(id);
var isValid = ValidateReport(report);
(isValid.Succeeded)
{
return SendReport(report);
}
return isValid;
}
Now the calling code can handle whatever went wrong in this method, but there’s just too much code here for such a simple method, right?
I know I can do better – so let me introduce the Result<T>
class as well.
This class inherits the Result
class and adds a property of type T which is called Value. Like it’s base class, its constructors are also protected and not public, and can only be initialized from static methods.
This class enables me to enhance the value I would return from a non-void method to also include the succeeded indicator and textual error description – so now my code can be simplified to this:
private Result SendReport(int id)
{
var report = GetReport(id);
var validationResult = ValidateReport(report);
return validationResult
? SendReport(validationResult.Value)
: validationResult;
}
The minimalists can see we don’t even need the report variable any more – so the first couple of code lines can be combined into one line (though I wouldn’t recommend that for the sake of readability).
Now let’s take a look on how the ValidateReport
method might look like: We know it returns a Result<T>
, takes in an instance of T as a parameter, and runs some tests on it. Assume the following code, for example:
private Result<Report> ValidateReport(Report report)
{
if (report is null) return Result<Report>.Fail("report is null.");
if (report.SubReports.Count == 0) return Result<Report>.Fail("report has no sub reports.");
if (string.IsNullOrEmpty(report.Author)) return Result<Report>.Fail("report has no author.");
if (report.PublishDate <= DateTime.Now.AddYears(-1)) return Result<Report>.Fail("report is too old.");
return Result<Report>.Success(report);
}
Pretty self explanatory, right? I like that in a code, it’s readable, simple and maintainable.
On the other hand, however, this code is also kind of repetitive, and I’m a big fan of DRY.
So to reduce the amount of repeating in my code, I’ve also overloaded the | and & operators, and to enable short-circuit conditions, I’ve also overloaded the true and false operators.
With this cool new feature I’ve added I can write the code in a more concise manner:
private Result<Report> ValidateReport(Report report)
{
return IsValid("report is null.", r => r is null)
&& IsValid("report has no sub reports.", r => r.SubReports.Count == 0)
&& IsValid("report has no author.", r => string.IsNullOrEmpty(r.Author))
&& IsValid("report is too old.", r => r.PublishDate <= DateTime.Now.AddYears(-1));
Result<Report> IsValid(string errorMessage, Predicate<Report> failPredicate)
=> failPredicate(report)
? Result<Report>.Fail(errorMessage)
: Result<Report>.Success(report);
}
I’ve uploaded the Result
and Result<T>
classes as a GitHub repository called ResultOf, under MIT licence which basically means everyone can use it for is free.
Both classes are completely documented and the README.md file contains usage examples.
Posted on March 5, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
November 26, 2024