iTranslated by AI
The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
📝
Implementing a Result Type in C#
Preface
While using C#, I wondered if I could implement a so-called Result type, so I gave it a try.
Code
It is available on GitHub.
Usage
- Import the following:
- Utils.ResultType;
- Utils.ResultType.Data;
- Utils.ResultType.Fail;
- Define a static function as follows:
- Simple!
static ResultData<decimal> Divide(int a, int b)
{
if (b == 0)
{
return Result.Fail<decimal>(ErrorCode.BadRequest);
}
return Result.Ok<decimal>((decimal) a / b);
}
Implementation
Directory Architecture
.
├── Program.cs
├── ResultType
│ ├── Data
│ │ └── ResultData.cs
│ ├── Fail
│ │ ├── ErrorCode.cs
│ │ └── ErrorMessage.cs
│ ├── Result.cs
│ └── Status
│ └── ResultStatus.cs
└── Utils.csproj
Result.cs (Static Class)
- Defines the static functions
OkandFailused in the function mentioned at the beginning. - Since the type of
valueis determined whenDatais defined,Failalso uses<T>.
using Utils.ResultType.Data;
using Utils.ResultType.Fail;
using Utils.ResultType.Status;
namespace Utils.ResultType
{
public static class Result
{
public static ResultData<T> Ok<T>(T value)
{
return new ResultData<T>(
status: ResultStatus.Ok,
value: value
);
}
public static ResultData<T> Fail<T>(ErrorCode errorCode)
{
return new ResultData<T>(
status: ResultStatus.Fail,
errorCode: errorCode,
value: default(T)
);
}
}
}
Status
- A simple Enum
namespace Utils.ResultType.Status
{
public enum ResultStatus
{
Ok,
Fail
}
}
Fail
- Simple error codes
namespace Utils.ResultType.Fail
{
public enum ErrorCode
{
None,
BadRequest,
Unauthorized,
Forbidden,
NotFound
}
}
- A static function for displaying
ErrorMessage - Takes an Enum as an argument and returns a string
namespace Utils.ResultType.Fail
{
public static class ErrorMessage
{
public static string Show(ErrorCode code)
{
return code switch
{
ErrorCode.BadRequest => "Failed!: Bad Request",
ErrorCode.Unauthorized => "Failed!: Unauthorized",
ErrorCode.Forbidden => "Failed!: Forbidden",
ErrorCode.NotFound => "Failed!: Not Found",
ErrorCode.None => throw new InvalidOperationException(message: "ErrorCode is None"),
_ => throw new ArgumentException(message: "Unknown ErrorCode")
};
}
}
}
Data
- The
GetValuefunction was created with the following in mind:- If it is
Fail, do not include data. - If it is
Okwith the default value (i.e.,Ok();), the caller will not use this function.
- If it is
using Utils.ResultType.Fail;
using Utils.ResultType.Status;
namespace Utils.ResultType.Data
{
public class ResultData<T>
{
public readonly ResultStatus Status;
public readonly ErrorCode ErrorCode;
private readonly T? _value;
public ResultData(ResultStatus status, ErrorCode? errorCode = null, T? value = default)
{
Status = status;
ErrorCode = errorCode == null ? ErrorCode.None : errorCode.Value;
_value = value;
}
public T GetValue()
{
if (Status == ResultStatus.Fail)
{
throw new InvalidOperationException("No Data with Status == Fail");
}
if (_value == null)
{
throw new InvalidOperationException(message: "Data is Null");
}
return _value!;
}
}
}
Discussion