背景
大家好。我有一个名为BaseRecordFetcher<TEntity>
的抽象类,它有一个方法从子类中获取read / sort / translate / move-next方法,yield将结果作为模型实体返回。
当我读取多个数据行时,它为每个实体正确执行yield return
,然后在do...while
之后到达跟踪消息,没有任何问题。
问题
但我注意到,当我在集合上使用IEnumerable.FirstOrDefault()
时,系统假定不再需要返回yield,并且 没有完成此方法的执行 < /强>!
除了返回了多少记录的跟踪输出之外,我对这种行为没有太大的问题,但它让我想到:“如果我确实需要有一些......我们怎么称呼它finally
代码?“
问题
是否有办法始终确保系统在yield return
之后运行一些后处理代码?
代码
/// <summary>
/// Retrieve translated entities from the database. The methods used to do
/// this are specified from the child class as parameters (i.e. Action or
/// Func delegates).
/// </summary>
/// <param name="loadSubsetFunc">
/// Specify how to load a set of database records. Return boolean
/// confirmation that records were found.
/// </param>
/// <param name="preIterationAction">
/// Specify what should happen to sort the results.
/// </param>
/// <param name="translateRowFunc">
/// Specify how a database record should translate to a model entity.
/// Return the new entity.
/// </param>
/// <param name="moveNextFunc">
/// Specify how the database row pointer should move on. Return FALSE on a
/// call to the final row.
/// </param>
/// <returns>
/// A set of translated entities from the database.
/// </returns>
/// <example><code>
///
/// return base.FetchRecords(
/// _dOOdad.LoadFacilitySites,
/// () => _dOOdad.Sort = _dOOdad.GetAutoKeyColumn(),
/// () =>
/// {
/// var entity = new FacilitySite();
/// return entity.PopulateLookupEntity(
/// _dOOdad.CurrentRow.ItemArray);
/// },
/// _dOOdad.MoveNext);
///
/// </code></example>
protected virtual IEnumerable<TEntity> FetchRecords(
Func<bool> loadSubsetFunc, Action preIterationAction,
Func<TEntity> translateRowFunc, Func<bool> moveNextFunc)
{
// If records are found, sort them and return set of entities
if (loadSubsetFunc())
{
Trace.WriteLine(string.Format(
"# FOUND one or more records: Returning {0}(s) as a set.",
typeof(TEntity).Name));
int recordCount = 0;
preIterationAction();
do
{
recordCount++;
var entity = translateRowFunc();
yield return entity;
}
while (moveNextFunc());
// This code never gets reached if FirstOrDefault() is used on the set,
// because the system will assume no more enities need to be returned
Trace.WriteLine(string.Format(
"# FINISHED returning records: {0} {1}(s) returned as a set.",
recordCount, typeof(TEntity).Name));
}
else
{
Trace.WriteLine(string.Format(
"# ZERO records found: Returning an empty set of {0}.",
typeof(TEntity).Name));
}
}
编辑(添加解决方案;谢谢@Servy和@BenRobinson):
try
{
do
{
recordCount++;
var entity = translateRowFunc();
yield return entity;
}
while (moveNextFunc());
}
finally
{
// This code always executes, even when you use FirstOrDefault() on the set.
Trace.WriteLine(string.Format(
"# FINISHED returning records: {0} {1}(s) returned as a set.",
recordCount, typeof(TEntity).Name));
}
答案 0 :(得分:4)
是的,您可以在迭代器块中提供finally
块,是的,它们旨在处理这种情况。
IEnumerator<T>
实施IDisposable
。当编译器将迭代器块转换为实现时,枚举器的Dispose
方法将执行任何finally块,这些块应根据枚举器处理时的当前位置执行。
这意味着只要迭代IEnumerator
的任何人确保始终处置其枚举器,您就可以确保您的finally
块将会运行。
另请注意,using
块将转换为try/finally
块,因此基本上可以在迭代器块中实现。如果需要,它们将在处理迭代器时清理给定的资源。