当foreach断裂时执行一些操作

时间:2014-05-27 07:34:53

标签: c# ienumerable

我试图为我的一个类实现IEnumerable接口:

public class NdbScanTableTuple<TKey,TRow> : NdbTableTuple<TKey,TRow>,IEnumerable<TRow>
{
   public IEnumerator<TRow> GetEnumerator()
    {
        yieldTransaction = StartTransaction();
        NdbScanOperation scanPub = yieldTransaction.ScanTable(m_scanRow.NdbRecordValue,           NdbOperation.LockMode.LM_Read);

        //some error checking in the middle... and than the loop
        TRow tmpRes;
        while (scanPub.nextResult<TRow>(ref m_scanRow.ManagedRow) == 0)
        {
            tmpRes = m_scanRow.GetValue();
            yield return tmpRes;
        }           
        yieldTransaction.Close();

    }

    public void CloseYieldedTransaction() 
    {
        yieldTransaction.Close();
    }

}

我以这种方式使用代码:             NdbScanTableTuple m_protoTable;

        //init and some other things...

        foreach (protoprocRow proto in m_protoTable) 
        {
            if (proto.col_name == protoprocName) 
            {
                m_protoStruct = proto;
                m_protoTable.CloseYieldedTransaction();
                break;
            }
        }

这是因为我必须在退出循环时关闭事务。 现在我的问题是:当我提前终止foreach循环时,有一种方法可以在不调用yieldTransaction.Close()的情况下执行CloseYieldedTransaction()吗?

3 个答案:

答案 0 :(得分:2)

您应该创建一个(可能是嵌套的)类来实现iterator接口并包含执行迭代所需的资源(例如您的事务对象),而不是使用IEnumerator<T>样式的函数。

然后该类可以在迭代器的Close方法中包含事务对象和Dispose,该方法在foreach终止时自动调用(无论是正常还是异常)

这也将使您的设计更清晰,因为此时,您似乎将事务存储为类的成员,这使您的当前类不安全,多个线程枚举(或甚至单个线程获取多个枚举)。

答案 1 :(得分:1)

您不知道枚举已停止。该方法不再被调用,但您无法知道。

确保交易关闭的一种方法是在IDisposable语句中使用using,在本地资源(最有可能是套接字 - 您不喜欢的地方)使用正确的终结器。必须处理它,它已经处理过了)。棘手的部分是你真的不能依赖终结器中仍然存在的任何托管资源,所以你遇到了问题。

当您使用IDisposable时,foreach会在Dispose超出范围时致电foreach,这样会很好。当然,当你不使用yield return时,许多自动化的东西都会崩溃。

现在,IEnumerator使其成为自己的try - finally。但是,它 正确处理处理。你唯一需要做的就是你应该做的事情:使用public IEnumerator<TRow> GetEnumerator() { var yieldTransaction = StartTransaction(); try { NdbScanOperation scanPub = yieldTransaction.ScanTable (m_scanRow.NdbRecordValue, NdbOperation.LockMode.LM_Read); //some error checking in the middle... and than the loop TRow tmpRes; while (scanPub.nextResult<TRow>(ref m_scanRow.ManagedRow) == 0) { tmpRes = m_scanRow.GetValue(); yield return tmpRes; } } finally { yieldTransaction.Close(); } }

finally

学习使用finally - 您正在进行的每个管理完成都应该在{{1}}中,这对于正确的深入异常处理非常关键。

答案 2 :(得分:1)

您需要像这样实施IEnumerator<T>

public class NdbScanTableTuple<TKey,TRow> : IEnumerable<TRow>
{
    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }

    public IEnumerator<TRow> GetEnumerator()
    {
        return new Enumerator<TRow>();
    }

    private class Enumerator<T> : IEnumerator<T>
    {
        object IEnumerator.Current { get { return this.Current; } }
        public T Current { get { return tmpRes; } }

        public void Dispose()
        {
            yieldTransaction.Close();
        }

        public bool MoveNext()
        {
        }

        public void Reset()
        {
        }
    }
}

(我已经为你留下了一些细节。)

这就是为什么它无法正常工作。

您会注意到Dispose会自然地为您清理。