【问题标题】:Yield return from a try/catch block [duplicate]来自 try/catch 块的收益回报 [重复]
【发布时间】:2025-12-12 00:50:02
【问题描述】:

正如 Eric Lippert 在 this article 中所述,yield return 不允许在 try/catch 子句中使用。

有没有一种很好的方法可以让我得到这样的东西,而无需手动编写自己的IEnumerator

public IEnumerable<Data> GetData()
{
    var transaction = Session.BeginTransaction());
    try 
    {
        IQuery q = CreateQuery(session);

        foreach (var result in q.Enumerable())
            yield return ProjectResult(result);  // <-- doesn't work

        session.Commit();
    }
    catch (Exception ex)
    {
        transaction.Rollback();
        throw;
    }
    finally
    {
        transaction.Dispose();
    }
}

【问题讨论】:

    标签: c# transactions idisposable try-catch-finally yield-return


    【解决方案1】:

    我只需像这样更改事务处理逻辑:

    public IEnumerable<Data> GetData()
    {
        var transaction = Session.BeginTransaction();
        bool rollback = true;
        try 
        {
            IQuery q = CreateQuery(session);
    
            foreach (var result in q.Enumerable())
            {
                yield return ProjectResult(result);
            }
    
            rollback = false;
            session.Commit();
        }
        finally
        {
            if (rollback)
            {
                transaction.Rollback();
            }
            transaction.Dispose();
        }
    }
    

    或者,如果您的事务支持“除非已提交,否则处置意味着回滚”的想法:

    public IEnumerable<Data> GetData()
    {
        using (var transaction = Session.BeginTransaction();
        {
            IQuery q = CreateQuery(session);
    
            foreach (var result in q.Enumerable())
            {
                yield return ProjectResult(result);
            }
    
            // Commits the tnrasaction, so disposing it won't roll it back.
            session.Commit();
        }
    }
    

    【讨论】:

    • OP 不是说 yield return 在 try/catch 块中是不允许的吗?
    • @Stripling,这不是 try/catch
    • @StriplingWarrior:只是为了扩展 Anthony 的评论:在 try/finally 中允许 yield return,只是在 try/catch 中不允许。
    • 啊,引用的文章说“3)允许在具有 finally 块的 try 块中返回 yield,但如果它们有 catch 块则不允许。”我明白了。
    • 谢谢,第一个例子是一个聪明的解决方法。第二个例子更好,因为 NHibernate 确实执行implicit rollbacks when disposed without prior commit
    【解决方案2】:

    重构

    foreach (var result in q.Enumerable()) 
      yield return ProjectResult(result);
    

    到一个单独的方法中并简单地返回它的结果。

    【讨论】:

    • 您能添加一个显示 try/catch 块的示例吗?我的猜测是,由于懒惰的评估,您的建议将无法正常工作;如果没有yield return,则finally 子句将在结果迭代之前执行。