是否存在类似(值OR错误)区分联合的.NET类型?

Jam*_*aix 10 .net c# f# functional-programming exception-handling

在C#中,我希望能够以更"功能"的方式处理错误,而不是总是使用默认的异常抛出模型.在某些情况下,投掷模型很棒,因为它允许您强制代码在发生意外情况时停止执行.但是,投掷模型有几个主要缺点:

  1. 异常抛出基本上是一个颠覆主控制流系统的应用控制流的整个二级系统.当一个方法被调用时,它的主体被执行,然后有效地控制返回给调用者,无论它是由于a return还是throw语句.如果控制从throw语句返回给调用者,如果没有适当的try/ catch在范围内,控件将立即移动到其调用者; 而如果控制权从一个return陈述中返回,则控制权继续正常进行.我知道实现方式比这更复杂和微妙,但我认为这是一个恰当的概念性总结.在设计时和运行时,这种双系统通常会以不同的方式混淆.
  2. 投掷系统在并行或异步场景中变得更加笨拙,正如在错误处理中看到的那样System.Threading.Tasks.Task.Task执行抛出的任何异常都存储在一个并由调用代码AggregateException通过Task.Exception属性访问.因此,当Tasks执行可能被中止时,调用代码必须使用正常的C#控制流来查找存储在对象属性中的错误.
  3. 除了XML注释之外,没有关于方法是否可能抛出异常或者可能抛出异常的元数据.异常表现为方法输出的替代形式,但在类型系统中很大程度上被忽略.例如,该方法int Divide(int x, int y)可能导致整数或a DivideByZeroException,但此方法的签名不建议任何有关错误的内容.相反,我经常听到有关Java检查异常的抱怨,其中一个方法可以抛出的每个特定异常类型必须添加到其签名中,这可能变得非常冗长.对我来说,最简单的中间地点是泛型类型Nullable<T>,包含值或异常.这样的方法Divide会有这个签名:Fallible<int> Divide(int x, int y).然后,使用该结果的任何操作都需要处理错误情况.方法也可以采用Fallible参数来更容易链接.

这是Fallible我概述的一个实现:

public class Fallible<T> : IEquatable<Fallible<T>> {

    #region Constructors

    public Fallible() {
        //value defaults to default(T)
        //exception defaults to null
    }

    public Fallible(T value) : this() {
        this.value = value;
    }

    public Fallible(Exception error) : this() {
        if (error == null) throw new ArgumentNullException(nameof(error));
        Error = error;
    }

    public Fallible(Func<T> getValue) : this() {
        if (error == null) throw new ArgumentNullException(nameof(getValue));
        try {
            this.value = getValue();
        }
        catch(Exception x) {
            Error = x;
        }
    }

    #endregion


    #region Properties

    public T Value {
        get {
            if (!HasValue) throw new InvalidOperationException("Cannot get Value if HasValue is false.");
            return value;
        }
    }
    private T value;

    public Exception Error { get; }

    public bool HasValue => Error == null;

    #endregion


    #region Equality

    public bool Equals(Fallible<T> other) => (other != null)
        && Equals(Error, other.Error)
        && Equals(Value, other.Value);

    public override bool Equals(object obj) => Equals(obj as Fallible<T>);

    public static bool operator ==(Fallible<T> a, Fallible<T> b) {
        if (a == null) return b == null;
        return a.Equals(b);
    }

    public static bool operator !=(Fallible<T> a, Fallible<T> b) {
        if (a == null) return b != null;
        return !a.Equals(b);
    }

    public override int GetHashCode() =>
        HasValue 
            ? Value.GetHashCode() 
            : Error.GetHashCode();

    #endregion


    public override string ToString() =>
        HasValue
            ? $"Fallible{{{Value}}}"
            : $"Fallible{{{Error.GetType()}: {Error.Message}}}";
}
Run Code Online (Sandbox Code Playgroud)

问题是:

  1. 是否有像Fallible<T>.NET 这样的东西?也许是任务并行库,Reactive Extensions或F#核心库中的一个类?
  2. 上述实施中是否存在重大缺陷?
  3. 控制流程是否存在任何我可能忽略的概念问题?

Ree*_*sey 6

尽管FSharp.Core确实包含对选项的支持,但它提供了另一种可以避免异常处理的机制.

语言分机项目的确包含一个Try<T>类型,是非常相似,你描述.此外,它具有C#友好性,Option<T>具有伪模式匹配,可用于选项上的操作.