包装一个IEnumerable并捕获异常 [英] Wrap an IEnumerable and catch exceptions

查看:170
本文介绍了包装一个IEnumerable并捕获异常的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一堆可以 Process()对象的类,并返回自己的对象:

I've got a bunch of classes that can Process() objects, and return their own objects:

public override IEnumerable<T> Process(IEnumerable<T> incoming) { ... }

我想编写一个处理器类可以包装这些处理​​器之一,并记录包装的 Process()方法可能抛出的任何未捕获的异常。我的第一个想法是这样的:

I want to write a processor class that can wrap one of these processors, and log any uncaught exceptions that the wrapped Process() method might throw. My first idea was something like this:

public override IEnumerable<T> Process(IEnumerable<T> incoming) {
    try {
        foreach (var x in this.processor.Process(incoming)) {
            yield return x;
        }
    } catch (Exception e) {
        WriteToLog(e);
        throw;
    }
}

但这不起作用,因为 CS1626:无法在catch块的正文中产生一个值条款

所以我想写一些在概念上等同于但是编译的东西。 :-)我有这个:

So I want to write something that's conceptually equivalent but compiles. :-) I've got this:

public override IEnumerable<T> Process(IEnumerable<T> incoming) {
    IEnumerator<T> walker;
    try {
        walker = this.processor.Process(incoming).GetEnumerator();
    } catch (Exception e) {
        WriteToLog(e);
        throw;
    }

    while (true) {
        T value;
        try {
            if (!walker.MoveNext()) {
                break;
            }
            value = walker.Current;
        } catch (Exception e) {
            WriteToLog(e);
            throw;
        }
        yield return value;
    }
}

但这比我想象的要复杂得多,我不完全确定其正确性,或者没有一个简单的方法。

but that's more complex than I'd hoped, and I'm not entirely certain of either its correctness or that there isn't a much simpler way.

我在正确的轨道上吗?有一个更简单的方法吗?

Am I on the right track here? Is there an easier way?

推荐答案

如果你想要做的是在处理枚举结果的过程中处理一个异常然后你尝试逻辑只需要直接进入你的for / while循环。

If what you want to do is handle an exception during the processing of the result of an enumeration, then you try logic simply needs to go directly inside your for/while loop.

但是你的例子看起来好像你试图抓住并跳过由枚举提供者。

But your example reads as if you are trying to catch and skip over exceptions raised by the enumeration provider.

据我所知,C#中没有办法迭代枚举器,并且在枚举器本身中跳过和异常。
如果枚举器引发异常,那么以后所有的MoveNext()调用将导致错误的输出。

As far as I can ascertain, there is no way in C# to iterate over an enumerator and skip and exception that occurs within the enumerator itself. If the enumerator raises an exception, then all future calls to MoveNext() will result in false output.

解释为什么会发生这种情况的最简单的方法是这个非常简单的枚举:

The easiest way to explain why this happens is with this very simple enumerable:

IEnumerable<int> TestCases()
{
    yield return 1;
    yield return 2;
    throw new ApplicationException("fail eunmeration");
    yield return 3;
    yield return 4;
}

可以理解,当我们看这个例子,很明显,抛出的异常会导致这个整个块要退出,第3和第4个收益表将不会被处理。实际上,在第3个yield语句中得到通常的Unreachable code detected编译器警告。

Understandably when we look at this example it is obvious that the thrown exception will cause this whole block to exit, and the 3rd and 4th yield statement will not ever be processed. In fact the get the usual 'Unreachable code detected' compiler warning on the 3rd yield statement.

所以当可枚举更复杂时,同样的规则适用: p>

So when the enumerable is a more complex, the same rules apply:

IEnumerable<int> TestCases2()
{
    foreach (var item in Enumerable.Range(0,10))
    {
        switch(item)
        {
            case 2:
            case 5:
                throw new ApplicationException("This bit failed");
            default:
                yield return item;
                break;
        }
    }
}

当引发异常时,该块的处理停止并将调用堆栈备份到最近的异常处理程序。

When the exception is raised, the processing of this block ceases and passes back up the call stack to the nearest exception handler.

所有可行的示例来解决我在SO上发现的这个问题不要继续查询枚举中的下一个项目,所有这些都在第一个例外 break

ALL of the workable examples to get around this issue that I have found on SO do not proceed to the next item in the enumeration, they all break at the first exception.

因此,要跳过 en在枚举中你将需要提供者来方便它。这仅仅是可能的,如果您编码的提供商,或者您可以联系开发人员,以下是一个过度简化的例子,您可以如何实现:

Therefore to skip en exception in the enumeration you will need the provider to facilitate it. This is only possible really if your coded the provider, or you can contact the developer who did, the following is an over-simplified example of how you could achieve this:

IEnumerable<int> TestCases3(Action<int, Exception> exceptionHandler)
{
    foreach (var item in Enumerable.Range(0, 10))
    {
        int value = default(int);
        try
        {
            switch (item)
            {
                case 2:
                case 5:
                    throw new ApplicationException("This bit failed");
                default:
                    value = item;
                    break;
            }
        }
        catch(Exception e)
        {
            if (exceptionHandler != null)
            {
                exceptionHandler(item, e);
                continue;
            }
            else
                throw;
        }
        yield return value;
    }
}

...

foreach (var item in TestCases3(
    (int item, Exception ex) 
    => 
    Console.Out.WriteLine("Error on item: {0}, Exception: {1}", item, ex.Message)))
{
    Console.Out.WriteLine(item);
}

这将产生以下输出:

0
1
Error on item: 2, Exception: This bit failed
3
4
Error on item: 5, Exception: This bit failed
6
7
8
9

我希望将来会为其他开发人员解决问题,因为一旦我们开始深入Linq和枚举是一个很常见的想法。有力的东西,但有一些逻辑上的限制。

I hope this clears up the issue for other developers in the future as it is a pretty common idea that we all get once we start getting deep into Linq and enumerations. Powerful stuff but there are some logical limitations.

这篇关于包装一个IEnumerable并捕获异常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆