You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 15:45:04