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

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

问题描述

我有一堆类来进程()对象,然后返回自己的对象:

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

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



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

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;
    }
}



但是,这并不工作,由于<一个HREF =htt​​p://msdn.microsoft.com/en-us/library/cs1x15az%28VS.90%29.aspx> CS1626:不能在try块体产生一个值与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?

推荐答案

如果你想要做的是一个枚举的结果的处理过程中处理异常,然后尝试逻辑只需要直接进入你的内部/ 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;
}



可以理解,当我们看看下面这个例子很明显,抛出的异常会导致这整个区块退出,而第三和第四yield语句永远不会被处理。其实一开始通常的无法访问的代码检测到编译三号产量声明警告

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.

所以,当枚举是一个比较复杂的,同样的规则:

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发现不进行枚举中的下一个项目,他们都的在第一个例外。

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.

因此,要的跳过恩在枚举异常,你将需要提供方便吧。这是唯一可能确实,如果你的编码提供者,或者您也可以联系谁做开发人员,下面是你如何能做到这一点的过于简单化的例子:

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天全站免登陆