using System.Collections.Generic;
public static void Main() {
var serviceCall = SomeServiceMethod();
if (!serviceCall.IsSuccessful)
Console.WriteLine(serviceCall.GetRootError().Message);
Console.WriteLine(serviceCall.Data);
public static Result<string> SomeServiceMethod() {
var random = new Random();
if (random.NextDouble() > 0.5F) {
return Result.Fail<string>("Oh no it failed", "general-failure");
return "Great it worked!";
public class EmptyResponse
Items = Array.Empty<string>();
public string[] Items { get; set; }
public string? Message { get; init; }
public string? ErrorType { get; init; }
public List<Error> Reasons { get; init; } = new List<Error>();
public Error(string message) => Message = message;
public static Error FromError(Error error)
List<Error> reasonsList = error.Reasons.Select(FromError).ToList();
ErrorType = error.ErrorType
public abstract record Result
public static Result<EmptyResponse> Ok()
=> new() { Data = new EmptyResponse() };
public static Result<TData> Ok<TData>(TData data)
=> new() { Data = data };
public static Result<EmptyResponse> Fail(string errorMessage, string? errorType = null)
=> Fail<EmptyResponse>(errorMessage, errorType);
public static Result<EmptyResponse> Fail(Error error)
=> Fail<EmptyResponse>(error);
public static Result<TReturnType> Fail<TReturnType>(string errorMessage, string? errorType = null)
var error = new Error(errorMessage);
if (!string.IsNullOrEmpty(errorType))
error = error with { ErrorType = errorType };
return new Result<TReturnType>
Errors = new List<Error>() { error }
public static Result<TReturnType> Fail<TReturnType>(Error error) =>
Errors = new List<Error>() { error }
public record Result<TData>
public bool IsSuccessful => !Errors.Any();
public List<Error> Errors { get; init; } = new();
public TData Data { get; init; }
public Error? GetRootError() => Errors.FirstOrDefault();
public static implicit operator TData?(Result<TData> result)
if (!result.IsSuccessful)
throw new NullReferenceException("Unable to read data from a failed result");
public static implicit operator Result<TData>(TData data)
=> new() { Data = data };