我试图为我的一个类实现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()
吗?
答案 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
会自然地为您清理。