【问题标题】:IEnumerable of Options to Option of IEnumerableIEnumerable 的选项到 IEnumerable 的选项
【发布时间】:2017-06-15 23:02:30
【问题描述】:

我有一个第 3 方方法,可以获取 T 的 IEnumerable 并对其进行评估。 我想在我的 LINQ 评估语句(select,where...)中引入 Option with exceptional values(either),以获得需要输入方法的值。

对于每个 T 元素,我希望有一个方法可以返回转换后的元素或错误(将其保留为 deferred execution),如果返回错误,评估应该停止。 (从技术上讲,这可以通过抛出异常来实现,但这正是我试图避免使用 Option 的原因)

我想知道这个概念是否可以通过 LINQ 或更一般地在 C# 中实现?

如果无法实现,这会导致我用抛出异常来完全替换代码中的选项,因为使用带有选项的 API 的客户端可能会出现同样的问题。

总而言之,这就是我想要实现的目标:

这可能是一种可能的实现方法:

public Option<IEnumerable<T>,TError> Sequence<T,TError>(IEnumerable<Option<T,TError>> options)

其中:

  • 应该让 T 的 IEnumerable 延迟执行

  • 应该返回发现的第一个错误

  • 发现错误时应停止

** 最终方法需要 T 的 IEnumerable 来评估它(它可能是我无法访问的第 3 方方法) 而且我不想两次评估 IEnumerable

【问题讨论】:

  • 除非我有误解,否则我认为如果您只收到IEnumerable&lt;Option&lt;T&gt;&gt;,您的要求是不可能的。从生成器返回或让步时,您必须立即决定是否返回“无”类型,以防您的 Option&lt;T&gt; 之一出现问题。这意味着在最坏的情况下(可能是最常见的情况)评估输入的每个元素,在你的Option&lt;T&gt;s 中没有“无”。
  • 是否有另一种方法来处理代码中 IEnumerable 元素的错误而不抛出异常(包括以上几点)?
  • 如果您想预先确定IEnumerable 中是否有“错误”元素,那么无论哪种方式,您都必须继续查看元素,直到找到一。抛出异常或仅显式检查 None/null 状态(在任何会消耗您上面编写的函数的情况下)相当于大致相同的解决方案。
  • 是的,但在例外情况下,您可以停止执行,而使用您不能的选项(您可以但您必须控制有时您无权访问的最高执行功能)
  • 确实如此。你必须决定什么最适合你的目的。不过,对于您的问题,我只想指出 LINQ 或 C# 中确实没有任何东西可以将您保存在这里,您将不得不采取不同的方法或更改您的要求。

标签: c# linq monads optional


【解决方案1】:

在尝试了一段时间来解决这个问题之后,我觉得应该可以用任何一个 monad 来模仿异常,我发布了一个 more conceptual version of this question。

谢谢,根据马克的回答,这也证实了我对“与 C#/Java 风格的异常同构”的怀疑。

我能够创建以下实现:

public static class LinqEitherExtension
{
    public static IEnumerable<T> UntilException<T,TException>(
        this IEnumerable<Option<T,TException>> enumerable, 
        Action<TException> errorHandler
        )
    {
        return enumerable
            .TakeWhile(e =>
                           {
                               e.MatchNone(errorHandler);
                               return e.HasValue;
                           })
            .SelectMany(e => e.ToEnumerable());
    }
}


public class Program
{
    static void Main(string[] args)
    {
        var numbers = new List<int> {1, 2, 3, 4, 5};
        ThirdPartyFunction(numbers.Select(CheckNumbers).UntilException(Console.WriteLine));
        //returns: 
        //1     
        //2
        //3 is exceptional number.
    }

    public static Option<int,string> CheckNumbers(int number)
    {
        return number == 3
            ? Option.None<int, string>("3 is exceptional number.")
            : number.Some<int, string>();
    }

    //Cannot be accessed/altered
    public static void ThirdPartyFunction(IEnumerable<int> numbers)
    {
        foreach (var number in numbers) Console.WriteLine(number.ToString());
    }

}

注意:如果ThirdPartyFunction在消费完enumerable后会继续做副作用(如Console.WriteLine),这仍然会在抛出异常和使用错误作为返回类型之间留下空隙:

  • 抛出异常方法:也不会执行任何副作用。
  • 任何一种方式:都会让ThirdPartyFunction完成执行,客户端消费函数的输出可以忽略返回值,但是内部发生的任何副作用仍然会执行。

因为我在这之后会完成工作。

【讨论】:

    猜你喜欢
    • 2022-09-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-21
    • 2013-08-22
    • 1970-01-01
    • 1970-01-01
    • 2011-04-11
    相关资源
    最近更新 更多