c# – IEnumerable的异常处理问题,它依赖于懒惰

我曾经用IEnumerable< T>创建接口.作为返回类型,每当我想指定特定输出是只读时.我喜欢它,因为它是简约的,隐藏实现细节并将被调用者与调用者分离.

但是最近我的一位同事认为IEnumerable< T>应该保留仅涉及延迟评估的场景,否则它不清楚调用方法,异常处理应该采取它的位置 – 围绕方法调用或围绕迭代.然后,对于具有只读输出的急切评估案例,我应该使用ReadOnlyCollection.

对我来说听起来很合理,但你会推荐什么?你同意IEnumerable的那个约定吗?或者IEnumerable有更好的异常处理方法吗?

如果我的问题不清楚,我做了一个样本课来说明问题.这里的两个方法具有完全相同的签名,但它们需要不同的异常处理:

public class EvilEnumerable
{
    IEnumerable<int> Throw()
    {
        throw new ArgumentException();
    }

    IEnumerable<int> LazyThrow()
    {
        foreach (var item in Throw())
        {
            yield return item;
        }
    }

    public void Run()
    {
        try
        {
            Throw();
        }
        catch (ArgumentException)
        {
            Console.WriteLine("immediate throw");
        }

        try
        {
            LazyThrow();
        }
        catch (ArgumentException)
        {
            Console.WriteLine("No exception is thrown.");
        }

        try
        {
            foreach (var item in LazyThrow())
            {
                //do smth
            }
        }
        catch (ArgumentException)
        {
            Console.WriteLine("lazy throw");
        }
    }
}

更新1.问题不仅限于ArgumentException.它是关于创建友好类接口的最佳实践,它告诉您它们是否返回惰性求值结果,因为这会影响异常处理方法.

最佳答案
这里真正的问题是延迟执行.在参数检查的情况下,您可以通过添加第二个方法来完成此操作:

IEnumerable<int> LazyThrow() {
     // TODO: check args, throwing exception
     return LazyThrowImpl();
}
IEnumerable<int> LazyThrowImpl() {
    // TODO: lazy code using yield
}

例外发生;即使对于非延迟结果(例如,列表< T>),您也可以获得错误(可能在您进行迭代时,如果另一个线程调整列表).上述方法允许您提前尽可能地减少产量和延迟执行的意外副作用.

转载注明原文:c# – IEnumerable的异常处理问题,它依赖于懒惰 - 代码日志