📝
C#でResult型を作ってみた
前書き
C#を使っているときにいわゆるResult型を書けないかなと思ったので書いてみました.
Code
GitHubで配布してます.
Usage
- 以下をimport
- Utils.ResultType;
- Utils.ResultType.Data;
- Utils.ResultType.Fail;
- static functionを以下のように定義する
- 簡単!
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)
- 冒頭の関数で用いたstatic function, Ok, Failを定義している
- valueはData定義時に型を決める関係上, Failも
<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
- 単純なEnum
namespace Utils.ResultType.Status
{
public enum ResultStatus
{
Ok,
Fail
}
}
Fail
- 単純なエラーコード
namespace Utils.ResultType.Fail
{
public enum ErrorCode
{
None,
BadRequest,
Unauthorized,
Forbidden,
NotFound
}
}
- ErrorMessageを表示するためのstatic関数
- Enumの引数を受け取って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
- GetValue関数は以下を念頭に作成
- Failならデータを入れない
- Okでdefault, つまり,
OK();
なら呼び出し元でこの関数を使うことはない
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