C# 当foreach将中断时执行一些操作

C# 当foreach将中断时执行一些操作,c#,ienumerable,C#,Ienumerable,我正在尝试为我的一个类实现IEnumerable接口: public class NdbScanTableTuple<TKey,TRow> : NdbTableTuple<TKey,TRow>,IEnumerable<TRow> { public IEnumerator<TRow> GetEnumerator() { yieldTransaction = StartTransaction(); NdbS

我正在尝试为我的一个类实现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();
    }

}
这是因为我必须在退出循环时关闭事务。 现在我的问题是:当我提前终止foreach循环时,有一种方法可以执行
yieldTransaction.Close()
,而无需显式调用
closeyieldtransaction()

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

然后,该类可以包含事务对象,并从迭代器的
Dispose
方法中关闭它,该方法在
foreach
终止时(正常或异常)自动调用

这也将使您的设计更简洁,因为目前,您似乎将事务存储为类的一个成员,这使得您当前的类对于多个线程进行枚举(甚至对于单个线程获取多个枚举数)是不安全的。

您应该实际创建一个(可能是嵌套的)类,该类实现接口并包含执行迭代所需的资源(例如事务对象)

然后,该类可以包含事务对象,并从迭代器的
Dispose
方法中关闭它,该方法在
foreach
终止时(正常或异常)自动调用


这也将使您的设计更简洁,因为目前,您似乎将事务存储为类的一个成员,这使得您当前的类对于多个线程进行枚举(甚至对于单个线程获取多个枚举器)是不安全的.

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

确保事务关闭的一种方法是在
using
语句中使用
IDisposable
,并在本地资源所在的位置使用适当的终结器(最有可能是套接字-您不必处理它,它已经被处理)是的。棘手的部分是你真的不能依赖任何托管资源仍然存在于终结器中,所以你有一个问题

当您使用
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();
  }
}
public IEnumerator GetEnumerator()
{
var yieldTransaction=StartTransaction();
尝试
{
NdbScanOperation scanPub=yieldTransaction.ScanTable
(m_scanRow.NdbRecordValue、ndbooperation.LockMode.LM_Read);
//在中间和循环中进行一些错误检查。
TROWTMPRES;
while(scanPub.nextResult(ref m_scanRow.ManagedRow)==0)
{
tmpRes=m_scanRow.GetValue();
收益率;
}           
}
最后
{
yieldTransaction.Close();
}
}

学习使用
finally
-您正在执行的每个托管终结都应该在
finally
中,这对于正确的深入异常处理非常关键。

您不知道枚举已停止。不再调用该方法,但您不知道这一点

确保事务关闭的一种方法是在
using
语句中使用
IDisposable
,并在本地资源所在的位置使用适当的终结器(最有可能是套接字-您不必处理它,它已经被处理)是的。棘手的部分是你真的不能依赖任何托管资源仍然存在于终结器中,所以你有一个问题

当您使用
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();
  }
}
public IEnumerator GetEnumerator()
{
var yieldTransaction=StartTransaction();
尝试
{
NdbScanOperation scanPub=yieldTransaction.ScanTable
(m_scanRow.NdbRecordValue、ndbooperation.LockMode.LM_Read);
//在中间和循环中进行一些错误检查。
TROWTMPRES;
while(scanPub.nextResult(ref m_scanRow.ManagedRow)==0)
{
tmpRes=m_scanRow.GetValue();
收益率;
}           
}
最后
{
yieldTransaction.Close();
}
}

学习使用
finally
-您正在执行的每个托管终结都应该在
finally
中,这对于正确的深入异常处理非常关键。

您需要实现
IEnumerator
,如下所示:

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()
        {
        }
    }
}
public类NdbScanTableTuple:IEnumerable
{
IEnumerator IEnumerable.GetEnumerator()
{
返回此.GetEnumerator();
}
公共IEnumerator GetEnumerator()
{
返回新的枚举数();
}
私有类枚举器:IEnumerator
{
对象IEnumerator.Current{get{返回this.Current;}
公共T当前{get{return tmpRes;}
公共空间处置()
{
yieldTransaction.Close();
}
公共图书馆
{
}
公共无效重置()
{
}
}
}
(我给你们留下了一些细节。)

这就是它不起作用的原因

您会注意到
Di