将递归形式改写为迭代形式.异常处理

本文关键字:迭代 异常处理 递归 | 更新日期: 2023-09-27 18:12:00

考虑这样一个函数:

void RequestThings(List<Things> container, Connection connection, Int32 lastVersion) {
    var version = lastVersion;
    try {
        foreach(var thing in connection.RequestThings(version)) {
            container.Add(thing);
            version = thing.lastVersion;
        }
    }
    catch(Exception ex) {
        RequestThings(container, connection, version + 1);
    }
}

但是这个选择远不是完美的:它涉及到增加递归深度(直到堆栈溢出),以防出现(许多)异常。

我如何用迭代的方式重写这个?

我试过这样做:

var container = new List<Things>();
var version = getLastVersionFromDB();
foreach(var thing in connection.RequestThings(version)) {
    try {
        container.Add(thing);
    }
    catch(Exception ex) {
        continue;
    }
}

exception似乎没有得到处理。我该怎么做呢?

编辑。细节

  1. Connection.RequestThings(Int32 startVersion)从远程服务器请求数据。接受种子版本作为其唯一参数。可能会有阻塞/损坏的文件,您无法请求,尽管它们出现在调用Connection.RequestThings(Int32 startVersion)返回的结果上。这段代码抛出异常
  2. 不知道为什么,但内部try/catch在我的iterative的例子没有捕捉到异常。

将递归形式改写为迭代形式.异常处理

一般来说,为所有异常使用catch子句是一个坏主意。考虑只捕获特定的异常类型,以确保不会出现意外错误。

另外,如果一开始就出现了堆栈溢出,这表明您可能做错了什么。例如,如果将无效的version号传递给此方法,并且没有具有更大版本号的文档可用,会发生什么情况?此方法将永远运行,没有机会优雅地取消它。特别是因为你似乎正在从数据库中获得"最后版本";如果此操作失败,则可以非常确定不存在更高版本。

话虽如此,您可以通过创建一个"无限"循环来简化该方法,然后在成功时使用return退出该方法:

void RequestThings(List<Things> container, Connection conn, int version)
{
    while (true)
    {
        try 
        {
            foreach (var thing in connection.RequestThings(version))
            {
                container.Add(thing);
                version = thing.lastVersion;
            }
            return;
        }
        catch (Exception ex)
        {   
            Log.Error(ex);
            version++;
        }
    }
}

一个稍微好一点的方法可能是确保你真的获得了成功的整个列表,或者什么都没有。如果在迭代过程中发生异常,您现在编写代码的方式可能会导致container被多次填充。

List<Things> RequestThings(Connection conn, int version)
{
    while (true)
    {
        try 
        {
            // this will either create an entire list,
            // or fail completely
            return connection.RequestThings(version).ToList();
        }
        catch (Exception ex)
        {   
            Log.Error(ex);
            version++;
        }
    }
}