如何在C# Result类的Fail方法中省略泛型类型指定?
优雅的C# Result类实现方案
背景
我使用Result类已有多年,接触C#时首次了解到John Ottosson的Result类实现,以此为基础定制的版本多年来表现良好,但仍存在不足,我一直寻求更优雅的实现方式。
典型的C# Result类实现
public class Error { public string Message { get; set; } //省略其他属性以简化代码 } public class Result { //无错误则表示调用成功 public bool IsSuccessful => Errors?.Any() ?? true; public IReadOnlyList<Error>? Errors { get; set; } public static Result<T> Success<T>(T data) { return new Result<T> { Data = data, Errors = null }; } public static Result<T> Fail<T>(Error error) { return new Result<T> { Errors = new List<Error> { error } }; } } public class Result<T> : Result { public T Data { get; set; } }
现有实现的痛点
该实现中最让我困扰的是:即便Fail方法最终会使泛型T为null,仍必须为其指定类型。例如需编写Result.Fail<MyDto?>("Some Error Message"),使用繁琐且存在误导性。我曾将该类移植到TypeScript中,使用体验更佳。
基于OneOf库的尝试(第一次更新)
借助OneOf库我取得了一些进展(感谢@Alejandro),但以下代码无法编译:
public class Result { //省略部分代码 public static Result<EmptyResponse> Fail(Error error) { return new Result<EmptyResponse> { Errors = new List<Error> { error }, Data = new EmptyResponse() //此处报错,详见下方错误信息 }; } } public class Result<T> : Result { public OneOf<T, EmptyResponse> Data { get; set; } }
报错信息:
ERROR: Cannot convert source type 'Playground.EmptyResponse' to target type 'OneOf.OneOf<Playground.EmptyResponse,Playground.EmptyResponse>
尽管Data的类型是OneOf<T, EmptyResponse>,仍需提供T的类型信息。如下代码可编译,但又回到了原点——仍需为Fail方法指定无需用到的类型:
public static Result<T> Fail<T>(Error error) { return new Result<T> { Errors = new List<Error> { error }, Data = new EmptyResponse() }; }
可行的OneOf库实现方案(第二次更新)
我找到了基于OneOf库的可行方案,现分享给感兴趣的开发者:
public class Result<TResult> : OneOfBase<TResult, IReadOnlyList<Error>> { protected Result(OneOf<TResult, IReadOnlyList<Error>> _) : base(_) { } public bool IsSuccessful => IsT0; public TResult Data => AsT0; public IReadOnlyList<Error> Errors => AsT1; public static implicit operator Result<TResult> (TResult _) => new(_); public static implicit operator Result<TResult>(List<Error> _) => new Result<TResult>(_); public static implicit operator Result<TResult>(Error _) => new Result<TResult>(new List<Error>() { _ }); }
通过该方案,方法可直接返回成功或失败结果,无需指定额外类型信息:
public Result<string> TestSuccessful() { return "Yay it worked!"; } public Result<string> TestFail() { return new Error() { Message = "Ahh idk what happened" }; }
使用示例如下:
var failResult = controller.TestFail(); var successResult = controller.TestSuccessful(); if (failResult.IsSuccessful) Console.WriteLine(failResult.Data); else Console.WriteLine(failResult.Errors.FirstOrDefault()?.Message); if (successResult.IsSuccessful) Console.WriteLine(successResult.Data); else Console.WriteLine(successResult.Errors.FirstOrDefault()?.Message);
该方案可通过Result对象直接获取成功数据或错误信息,同时保留类型安全,完全满足需求。
内容的提问来源于stack exchange,提问作者ArcSpark76
相关产品推荐
相关产品推荐

